Immutable branch between MFD, Pinctrl and soundwire due for the v6.6 merge window

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmTfkKAACgkQUa+KL4f8
 d2FwzxAArqA6/pebr+19TNvv6hFuUxpbR57jr0XM4YzPNvBJs/iEk1wfdGTkOdFG
 NSWVGDIE4aY62mwehq0AEfYQVrTzKcAuMJZgOeg0lzReBYMhn6WuyCc192L7tgRA
 H3bm2oQcSVXiNGXvKoTr9fDckBf5Ta9OcoVmf48YFQXvwv+vuO/kS77JuVkLZxwH
 yycmLxD7PJlvikj8ITsg8pzj/xLhCpHziLsyMjQpgvr7hRPG+f5W0Dq9vNONI5IL
 YU810I2Dp+bU5YGAElGTrSwHnI2FrCUlrcR88ukGPN9rBn84TcmypWsetMR83juo
 zcn6AfUp6WuhaQxs06tTzsNc9cQpKjtjYojTZ1OYYsC7eOhlF3ifQLEiqVJbfY0W
 8BN1e3QDf59ZwXAUJJKrV37yHc9/uuGVerc7dzkng0f2OHosNWzLNyy7LiOb80dD
 Hp/yGxXXyiASGjswSaxqbu67TuEPWKuoceW0wRS9/ExZx+cPvEzdTzdCWYej7cDH
 UmJup/J053htC1k/vos+0SRezqeuXvHXDRuwP9Lnhdct9eeDiGFn920S9d2DVjgR
 PImk+O9dTnOVehNY7+I9tqBK9dM3AolH/jbEQQ6VkIfm2clewBy+1M0SohWIBW/E
 4iQbtPkVXwGHI9Lp+6pP4exhA58FQCDL4KoUkchWsokF9zTM/eM=
 =Sb7O
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmTfnlEACgkQJNaLcl1U
 h9BD2Af/au+JWxg2p6BOe2KLaqsVbWsijBMWaoAz3XRsiTow5MEfo0I4jSsw9wQw
 YYxJ++VFlydheP+VpPC7At6dqz5BGbk/5uG9Vh4AAwgnVL/OCNJqT54jJ5RV4lh6
 zWh4xXa8enKfKGuWRbCbCpYIOEvzwFaHYe4V1axbiAJYunTaRcBvEyJXeaSQGKXr
 uI7kTef/h1j+HWgGVkm8ODTf/YfXpuH9myysDBYcIlb4I6Daphtk70M1IdbyepXo
 khTV9r3Weof2tUajRYUuiegaEW3UFU/tEP87/UVKZ5k6eef7O77evqdWRWcYhMfw
 E8oCacS26cGGGL7ynj+XxmV5po7dJw==
 =xI4n
 -----END PGP SIGNATURE-----

mfd: Immutable branch between MFD, Pinctrl and soundwire due for the v6.6 merge window

Merge tag 'ib-mfd-pinctrl-soundwire-v6.6' of https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd into tmp

Immutable branch between MFD, Pinctrl and soundwire due for the v6.6 merge window
This commit is contained in:
Mark Brown 2023-08-18 17:37:08 +01:00
commit 038e0da7ba
16 changed files with 3856 additions and 0 deletions

View file

@ -0,0 +1,313 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/cirrus,cs42l43.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Cirrus Logic CS42L43 Audio CODEC
maintainers:
- patches@opensource.cirrus.com
description: |
The CS42L43 is an audio CODEC with integrated MIPI SoundWire interface
(Version 1.2.1 compliant), I2C, SPI, and I2S/TDM interfaces designed
for portable applications. It provides a high dynamic range, stereo
DAC for headphone output, two integrated Class D amplifiers for
loudspeakers, and two ADCs for wired headset microphone input or
stereo line input. PDM inputs are provided for digital microphones.
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- cirrus,cs42l43
reg:
maxItems: 1
vdd-p-supply:
description:
Power supply for the high voltage interface.
vdd-a-supply:
description:
Power supply for internal analog circuits.
vdd-d-supply:
description:
Power supply for internal digital circuits. Can be internally supplied.
vdd-io-supply:
description:
Power supply for external interface and internal digital logic.
vdd-cp-supply:
description:
Power supply for the amplifier 3 and 4 charge pump.
vdd-amp-supply:
description:
Power supply for amplifier 1 and 2.
reset-gpios:
maxItems: 1
interrupt-controller: true
"#interrupt-cells":
const: 2
interrupts:
maxItems: 1
"#sound-dai-cells":
const: 1
clocks:
items:
- description: Synchronous audio clock provided on mclk_in.
clock-names:
const: mclk
cirrus,bias-low:
type: boolean
description:
Select a 1.8V headset micbias rather than 2.8V.
cirrus,bias-sense-microamp:
description:
Current at which the headset micbias sense clamp will engage, 0 to
disable.
enum: [ 0, 14, 23, 41, 50, 60, 68, 86, 95 ]
default: 0
cirrus,bias-ramp-ms:
description:
Time in milliseconds the hardware allows for the headset micbias to
ramp up.
enum: [ 10, 40, 90, 170 ]
default: 170
cirrus,detect-us:
description:
Time in microseconds the type detection will run for. Long values will
cause more audible effects, but give more accurate detection.
enum: [ 20, 100, 1000, 10000, 50000, 75000, 100000, 200000 ]
default: 10000
cirrus,button-automute:
type: boolean
description:
Enable the hardware automuting of decimator 1 when a headset button is
pressed.
cirrus,buttons-ohms:
description:
Impedance in Ohms for each headset button, these should be listed in
ascending order.
minItems: 1
maxItems: 6
cirrus,tip-debounce-ms:
description:
Software debounce on tip sense triggering in milliseconds.
default: 0
cirrus,tip-invert:
type: boolean
description:
Indicates tip detect polarity, inverted implies open-circuit whilst the
jack is inserted.
cirrus,tip-disable-pullup:
type: boolean
description:
Indicates if the internal pullup on the tip detect should be disabled.
cirrus,tip-fall-db-ms:
description:
Time in milliseconds a falling edge on the tip detect should be hardware
debounced for. Note the falling edge is considered after the invert.
enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ]
default: 500
cirrus,tip-rise-db-ms:
description:
Time in milliseconds a rising edge on the tip detect should be hardware
debounced for. Note the rising edge is considered after the invert.
enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ]
default: 500
cirrus,use-ring-sense:
type: boolean
description:
Indicates if the ring sense should be used.
cirrus,ring-invert:
type: boolean
description:
Indicates ring detect polarity, inverted implies open-circuit whilst the
jack is inserted.
cirrus,ring-disable-pullup:
type: boolean
description:
Indicates if the internal pullup on the ring detect should be disabled.
cirrus,ring-fall-db-ms:
description:
Time in milliseconds a falling edge on the ring detect should be hardware
debounced for. Note the falling edge is considered after the invert.
enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ]
default: 500
cirrus,ring-rise-db-ms:
description:
Time in milliseconds a rising edge on the ring detect should be hardware
debounced for. Note the rising edge is considered after the invert.
enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ]
default: 500
pinctrl:
type: object
$ref: /schemas/pinctrl/pinctrl.yaml#
additionalProperties: false
properties:
gpio-controller: true
"#gpio-cells":
const: 2
gpio-ranges:
items:
- description: A phandle to the CODEC pinctrl node
minimum: 0
- const: 0
- const: 0
- const: 3
patternProperties:
"-state$":
oneOf:
- $ref: "#/$defs/cirrus-cs42l43-state"
- patternProperties:
"-pins$":
$ref: "#/$defs/cirrus-cs42l43-state"
additionalProperties: false
spi:
type: object
$ref: /schemas/spi/spi-controller.yaml#
unevaluatedProperties: false
$defs:
cirrus-cs42l43-state:
type: object
allOf:
- $ref: /schemas/pinctrl/pincfg-node.yaml#
- $ref: /schemas/pinctrl/pinmux-node.yaml#
oneOf:
- required: [ groups ]
- required: [ pins ]
additionalProperties: false
properties:
groups:
enum: [ gpio1, gpio2, gpio3, asp, pdmout2, pdmout1, i2c, spi ]
pins:
enum: [ gpio1, gpio2, gpio3,
asp_dout, asp_fsync, asp_bclk,
pdmout2_clk, pdmout2_data, pdmout1_clk, pdmout1_data,
i2c_sda, i2c_scl,
spi_miso, spi_sck, spi_ssb ]
function:
enum: [ gpio, spdif, irq, mic-shutter, spk-shutter ]
drive-strength:
description: Set drive strength in mA
enum: [ 1, 2, 4, 8, 9, 10, 12, 16 ]
input-debounce:
description: Set input debounce in uS
enum: [ 0, 85 ]
required:
- compatible
- reg
- vdd-p-supply
- vdd-a-supply
- vdd-io-supply
- vdd-cp-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
cs42l43: codec@1a {
compatible = "cirrus,cs42l43";
reg = <0x1a>;
vdd-p-supply = <&vdd5v0>;
vdd-a-supply = <&vdd1v8>;
vdd-io-supply = <&vdd1v8>;
vdd-cp-supply = <&vdd1v8>;
vdd-amp-supply = <&vdd5v0>;
reset-gpios = <&gpio 0>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gpio>;
interrupts = <56 IRQ_TYPE_LEVEL_LOW>;
#sound-dai-cells = <1>;
clocks = <&clks 0>;
clock-names = "mclk";
cs42l43_pins: pinctrl {
gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&cs42l43_pins 0 0 3>;
pinctrl-names = "default";
pinctrl-0 = <&pinsettings>;
pinsettings: default-state {
shutter-pins {
groups = "gpio3";
function = "mic-shutter";
};
};
};
spi {
#address-cells = <1>;
#size-cells = <0>;
cs-gpios = <&cs42l43_pins 1 0>;
sensor@0 {
compatible = "bosch,bme680";
reg = <0>;
spi-max-frequency = <1400000>;
};
};
};
};

View file

@ -4886,7 +4886,10 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
L: patches@opensource.cirrus.com
S: Maintained
F: Documentation/devicetree/bindings/sound/cirrus,cs*
F: drivers/mfd/cs42l43*
F: drivers/pinctrl/cirrus/pinctrl-cs42l43*
F: include/dt-bindings/sound/cs*
F: include/linux/mfd/cs42l43*
F: include/sound/cs*
F: sound/pci/hda/cs*
F: sound/pci/hda/hda_cs_dsp_ctl.*

View file

@ -237,6 +237,29 @@ config MFD_CROS_EC_DEV
To compile this driver as a module, choose M here: the module will be
called cros-ec-dev.
config MFD_CS42L43
tristate
select MFD_CORE
select REGMAP
config MFD_CS42L43_I2C
tristate "Cirrus Logic CS42L43 (I2C)"
depends on I2C
select REGMAP_I2C
select MFD_CS42L43
help
Select this to support the Cirrus Logic CS42L43 PC CODEC with
headphone and class D speaker drivers over I2C.
config MFD_CS42L43_SDW
tristate "Cirrus Logic CS42L43 (SoundWire)"
depends on SOUNDWIRE
select REGMAP_SOUNDWIRE
select MFD_CS42L43
help
Select this to support the Cirrus Logic CS42L43 PC CODEC with
headphone and class D speaker drivers over SoundWire.
config MFD_MADERA
tristate "Cirrus Logic Madera codecs"
select MFD_CORE

View file

@ -13,6 +13,9 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o
obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o
obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o
obj-$(CONFIG_MFD_CS42L43) += cs42l43.o
obj-$(CONFIG_MFD_CS42L43_I2C) += cs42l43-i2c.o
obj-$(CONFIG_MFD_CS42L43_SDW) += cs42l43-sdw.o
obj-$(CONFIG_MFD_ENE_KB3930) += ene-kb3930.o
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o

98
drivers/mfd/cs42l43-i2c.c Normal file
View file

@ -0,0 +1,98 @@
// SPDX-License-Identifier: GPL-2.0
/*
* CS42L43 I2C driver
*
* Copyright (C) 2022-2023 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/mfd/cs42l43-regs.h>
#include <linux/module.h>
#include "cs42l43.h"
static const struct regmap_config cs42l43_i2c_regmap = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.val_format_endian = REGMAP_ENDIAN_BIG,
.max_register = CS42L43_MCU_RAM_MAX,
.readable_reg = cs42l43_readable_register,
.volatile_reg = cs42l43_volatile_register,
.precious_reg = cs42l43_precious_register,
.cache_type = REGCACHE_MAPLE,
.reg_defaults = cs42l43_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs42l43_reg_default),
};
static int cs42l43_i2c_probe(struct i2c_client *i2c)
{
struct cs42l43 *cs42l43;
int ret;
cs42l43 = devm_kzalloc(&i2c->dev, sizeof(*cs42l43), GFP_KERNEL);
if (!cs42l43)
return -ENOMEM;
cs42l43->dev = &i2c->dev;
cs42l43->irq = i2c->irq;
/* A device on an I2C is always attached by definition. */
cs42l43->attached = true;
cs42l43->regmap = devm_regmap_init_i2c(i2c, &cs42l43_i2c_regmap);
if (IS_ERR(cs42l43->regmap)) {
ret = PTR_ERR(cs42l43->regmap);
dev_err(cs42l43->dev, "Failed to allocate regmap: %d\n", ret);
return ret;
}
return cs42l43_dev_probe(cs42l43);
}
static void cs42l43_i2c_remove(struct i2c_client *i2c)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(&i2c->dev);
cs42l43_dev_remove(cs42l43);
}
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id cs42l43_of_match[] = {
{ .compatible = "cirrus,cs42l43", },
{}
};
MODULE_DEVICE_TABLE(of, cs42l43_of_match);
#endif
#if IS_ENABLED(CONFIG_ACPI)
static const struct acpi_device_id cs42l43_acpi_match[] = {
{ "CSC4243", 0 },
{}
};
MODULE_DEVICE_TABLE(acpi, cs42l43_acpi_match);
#endif
static struct i2c_driver cs42l43_i2c_driver = {
.driver = {
.name = "cs42l43",
.pm = pm_ptr(&cs42l43_pm_ops),
.of_match_table = of_match_ptr(cs42l43_of_match),
.acpi_match_table = ACPI_PTR(cs42l43_acpi_match),
},
.probe = cs42l43_i2c_probe,
.remove = cs42l43_i2c_remove,
};
module_i2c_driver(cs42l43_i2c_driver);
MODULE_IMPORT_NS(MFD_CS42L43);
MODULE_DESCRIPTION("CS42L43 I2C Driver");
MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
MODULE_LICENSE("GPL");

239
drivers/mfd/cs42l43-sdw.c Normal file
View file

@ -0,0 +1,239 @@
// SPDX-License-Identifier: GPL-2.0
/*
* CS42L43 SoundWire driver
*
* Copyright (C) 2022-2023 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/mfd/cs42l43-regs.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw_type.h>
#include "cs42l43.h"
enum cs42l43_sdw_ports {
CS42L43_DMIC_DEC_ASP_PORT = 1,
CS42L43_SPK_TX_PORT,
CS42L43_SPDIF_HP_PORT,
CS42L43_SPK_RX_PORT,
CS42L43_ASP_PORT,
};
static const struct regmap_config cs42l43_sdw_regmap = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
.max_register = CS42L43_MCU_RAM_MAX,
.readable_reg = cs42l43_readable_register,
.volatile_reg = cs42l43_volatile_register,
.precious_reg = cs42l43_precious_register,
.cache_type = REGCACHE_MAPLE,
.reg_defaults = cs42l43_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs42l43_reg_default),
};
static int cs42l43_read_prop(struct sdw_slave *sdw)
{
struct sdw_slave_prop *prop = &sdw->prop;
struct device *dev = &sdw->dev;
struct sdw_dpn_prop *dpn;
unsigned long addr;
int nval;
int i;
u32 bit;
prop->use_domain_irq = true;
prop->paging_support = true;
prop->wake_capable = true;
prop->source_ports = BIT(CS42L43_DMIC_DEC_ASP_PORT) | BIT(CS42L43_SPK_TX_PORT);
prop->sink_ports = BIT(CS42L43_SPDIF_HP_PORT) |
BIT(CS42L43_SPK_RX_PORT) | BIT(CS42L43_ASP_PORT);
prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY |
SDW_SCP_INT1_IMPL_DEF;
nval = hweight32(prop->source_ports);
prop->src_dpn_prop = devm_kcalloc(dev, nval, sizeof(*prop->src_dpn_prop),
GFP_KERNEL);
if (!prop->src_dpn_prop)
return -ENOMEM;
i = 0;
dpn = prop->src_dpn_prop;
addr = prop->source_ports;
for_each_set_bit(bit, &addr, 32) {
dpn[i].num = bit;
dpn[i].max_ch = 2;
dpn[i].type = SDW_DPN_FULL;
dpn[i].max_word = 24;
i++;
}
/*
* All ports are 2 channels max, except the first one,
* CS42L43_DMIC_DEC_ASP_PORT.
*/
dpn[CS42L43_DMIC_DEC_ASP_PORT].max_ch = 4;
nval = hweight32(prop->sink_ports);
prop->sink_dpn_prop = devm_kcalloc(dev, nval, sizeof(*prop->sink_dpn_prop),
GFP_KERNEL);
if (!prop->sink_dpn_prop)
return -ENOMEM;
i = 0;
dpn = prop->sink_dpn_prop;
addr = prop->sink_ports;
for_each_set_bit(bit, &addr, 32) {
dpn[i].num = bit;
dpn[i].max_ch = 2;
dpn[i].type = SDW_DPN_FULL;
dpn[i].max_word = 24;
i++;
}
return 0;
}
static int cs42l43_sdw_update_status(struct sdw_slave *sdw, enum sdw_slave_status status)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev);
switch (status) {
case SDW_SLAVE_ATTACHED:
dev_dbg(cs42l43->dev, "Device attach\n");
sdw_write_no_pm(sdw, CS42L43_GEN_INT_MASK_1,
CS42L43_INT_STAT_GEN1_MASK);
cs42l43->attached = true;
complete(&cs42l43->device_attach);
break;
case SDW_SLAVE_UNATTACHED:
dev_dbg(cs42l43->dev, "Device detach\n");
cs42l43->attached = false;
reinit_completion(&cs42l43->device_attach);
complete(&cs42l43->device_detach);
break;
default:
break;
}
return 0;
}
static int cs42l43_sdw_interrupt(struct sdw_slave *sdw,
struct sdw_slave_intr_status *status)
{
/*
* The IRQ itself was handled through the regmap_irq handler, this is
* just clearing up the additional Cirrus SoundWire registers that are
* not covered by the SoundWire framework or the IRQ handler itself.
* There is only a single bit in GEN_INT_STAT_1 and it doesn't clear if
* IRQs are still pending so doing a read/write here after handling the
* IRQ is fine.
*/
sdw_read_no_pm(sdw, CS42L43_GEN_INT_STAT_1);
sdw_write_no_pm(sdw, CS42L43_GEN_INT_STAT_1, CS42L43_INT_STAT_GEN1_MASK);
return 0;
}
static int cs42l43_sdw_bus_config(struct sdw_slave *sdw,
struct sdw_bus_params *params)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev);
int ret = 0;
mutex_lock(&cs42l43->pll_lock);
if (cs42l43->sdw_freq != params->curr_dr_freq / 2) {
if (cs42l43->sdw_pll_active) {
dev_err(cs42l43->dev,
"PLL active can't change SoundWire bus clock\n");
ret = -EBUSY;
} else {
cs42l43->sdw_freq = params->curr_dr_freq / 2;
}
}
mutex_unlock(&cs42l43->pll_lock);
return ret;
}
static const struct sdw_slave_ops cs42l43_sdw_ops = {
.read_prop = cs42l43_read_prop,
.update_status = cs42l43_sdw_update_status,
.interrupt_callback = cs42l43_sdw_interrupt,
.bus_config = cs42l43_sdw_bus_config,
};
static int cs42l43_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
{
struct cs42l43 *cs42l43;
struct device *dev = &sdw->dev;
int ret;
cs42l43 = devm_kzalloc(dev, sizeof(*cs42l43), GFP_KERNEL);
if (!cs42l43)
return -ENOMEM;
cs42l43->dev = dev;
cs42l43->sdw = sdw;
cs42l43->regmap = devm_regmap_init_sdw(sdw, &cs42l43_sdw_regmap);
if (IS_ERR(cs42l43->regmap)) {
ret = PTR_ERR(cs42l43->regmap);
dev_err(cs42l43->dev, "Failed to allocate regmap: %d\n", ret);
return ret;
}
return cs42l43_dev_probe(cs42l43);
}
static int cs42l43_sdw_remove(struct sdw_slave *sdw)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev);
cs42l43_dev_remove(cs42l43);
return 0;
}
static const struct sdw_device_id cs42l43_sdw_id[] = {
SDW_SLAVE_ENTRY(0x01FA, 0x4243, 0),
{}
};
MODULE_DEVICE_TABLE(sdw, cs42l43_sdw_id);
static struct sdw_driver cs42l43_sdw_driver = {
.driver = {
.name = "cs42l43",
.pm = pm_ptr(&cs42l43_pm_ops),
},
.probe = cs42l43_sdw_probe,
.remove = cs42l43_sdw_remove,
.id_table = cs42l43_sdw_id,
.ops = &cs42l43_sdw_ops,
};
module_sdw_driver(cs42l43_sdw_driver);
MODULE_IMPORT_NS(MFD_CS42L43);
MODULE_DESCRIPTION("CS42L43 SoundWire Driver");
MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
MODULE_LICENSE("GPL");

1188
drivers/mfd/cs42l43.c Normal file

File diff suppressed because it is too large Load diff

28
drivers/mfd/cs42l43.h Normal file
View file

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* CS42L43 core driver internal data
*
* Copyright (C) 2022-2023 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#include <linux/mfd/cs42l43.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#ifndef CS42L43_CORE_INT_H
#define CS42L43_CORE_INT_H
#define CS42L43_N_DEFAULTS 176
extern const struct dev_pm_ops cs42l43_pm_ops;
extern const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS];
bool cs42l43_readable_register(struct device *dev, unsigned int reg);
bool cs42l43_precious_register(struct device *dev, unsigned int reg);
bool cs42l43_volatile_register(struct device *dev, unsigned int reg);
int cs42l43_dev_probe(struct cs42l43 *cs42l43);
void cs42l43_dev_remove(struct cs42l43 *cs42l43);
#endif /* CS42L43_CORE_INT_H */

View file

@ -1,4 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
config PINCTRL_CS42L43
tristate "Cirrus Logic CS42L43 Pinctrl Driver"
depends on MFD_CS42L43
select GPIOLIB
select PINMUX
select PINCONF
select GENERIC_PINCONF
help
Select this to support the GPIO/Pinctrl functions of the Cirrus
Logic CS42L43 PC CODEC.
config PINCTRL_LOCHNAGAR
tristate "Cirrus Logic Lochnagar pinctrl driver"
depends on MFD_LOCHNAGAR

View file

@ -1,5 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
# Cirrus Logic pinctrl drivers
obj-$(CONFIG_PINCTRL_CS42L43) += pinctrl-cs42l43.o
obj-$(CONFIG_PINCTRL_LOCHNAGAR) += pinctrl-lochnagar.o
pinctrl-madera-objs := pinctrl-madera-core.o

View file

@ -0,0 +1,609 @@
// SPDX-License-Identifier: GPL-2.0
//
// CS42L43 Pinctrl and GPIO driver
//
// Copyright (c) 2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
#include <linux/bits.h>
#include <linux/build_bug.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/mfd/cs42l43.h>
#include <linux/mfd/cs42l43-regs.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/string_helpers.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinmux.h>
#include "../pinctrl-utils.h"
#define CS42L43_NUM_GPIOS 3
struct cs42l43_pin {
struct gpio_chip gpio_chip;
struct device *dev;
struct regmap *regmap;
bool shutters_locked;
};
struct cs42l43_pin_data {
unsigned int reg;
unsigned int shift;
unsigned int mask;
};
#define CS42L43_PIN(_number, _name, _reg, _field) { \
.number = _number, .name = _name, \
.drv_data = &((struct cs42l43_pin_data){ \
.reg = CS42L43_##_reg, \
.shift = CS42L43_##_field##_DRV_SHIFT, \
.mask = CS42L43_##_field##_DRV_MASK, \
}), \
}
static const struct pinctrl_pin_desc cs42l43_pin_pins[] = {
CS42L43_PIN(0, "gpio1", DRV_CTRL4, GPIO1),
CS42L43_PIN(1, "gpio2", DRV_CTRL4, GPIO2),
CS42L43_PIN(2, "gpio3", DRV_CTRL4, GPIO3),
CS42L43_PIN(3, "asp_dout", DRV_CTRL1, ASP_DOUT),
CS42L43_PIN(4, "asp_fsync", DRV_CTRL1, ASP_FSYNC),
CS42L43_PIN(5, "asp_bclk", DRV_CTRL1, ASP_BCLK),
CS42L43_PIN(6, "pdmout2_clk", DRV_CTRL3, PDMOUT2_CLK),
CS42L43_PIN(7, "pdmout2_data", DRV_CTRL3, PDMOUT2_DATA),
CS42L43_PIN(8, "pdmout1_clk", DRV_CTRL3, PDMOUT1_CLK),
CS42L43_PIN(9, "pdmout1_data", DRV_CTRL3, PDMOUT1_DATA),
CS42L43_PIN(10, "i2c_sda", DRV_CTRL3, I2C_SDA),
CS42L43_PIN(11, "i2c_scl", DRV_CTRL_5, I2C_SCL),
CS42L43_PIN(12, "spi_miso", DRV_CTRL3, SPI_MISO),
CS42L43_PIN(13, "spi_sck", DRV_CTRL_5, SPI_SCK),
CS42L43_PIN(14, "spi_ssb", DRV_CTRL_5, SPI_SSB),
};
static const unsigned int cs42l43_pin_gpio1_pins[] = { 0 };
static const unsigned int cs42l43_pin_gpio2_pins[] = { 1 };
static const unsigned int cs42l43_pin_gpio3_pins[] = { 2 };
static const unsigned int cs42l43_pin_asp_pins[] = { 3, 4, 5 };
static const unsigned int cs42l43_pin_pdmout2_pins[] = { 6, 7 };
static const unsigned int cs42l43_pin_pdmout1_pins[] = { 8, 9 };
static const unsigned int cs42l43_pin_i2c_pins[] = { 10, 11 };
static const unsigned int cs42l43_pin_spi_pins[] = { 12, 13, 14 };
#define CS42L43_PINGROUP(_name) \
PINCTRL_PINGROUP(#_name, cs42l43_pin_##_name##_pins, \
ARRAY_SIZE(cs42l43_pin_##_name##_pins))
static const struct pingroup cs42l43_pin_groups[] = {
CS42L43_PINGROUP(gpio1),
CS42L43_PINGROUP(gpio2),
CS42L43_PINGROUP(gpio3),
CS42L43_PINGROUP(asp),
CS42L43_PINGROUP(pdmout2),
CS42L43_PINGROUP(pdmout1),
CS42L43_PINGROUP(i2c),
CS42L43_PINGROUP(spi),
};
static int cs42l43_pin_get_groups_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(cs42l43_pin_groups);
}
static const char *cs42l43_pin_get_group_name(struct pinctrl_dev *pctldev,
unsigned int group_idx)
{
return cs42l43_pin_groups[group_idx].name;
}
static int cs42l43_pin_get_group_pins(struct pinctrl_dev *pctldev,
unsigned int group_idx,
const unsigned int **pins,
unsigned int *num_pins)
{
*pins = cs42l43_pin_groups[group_idx].pins;
*num_pins = cs42l43_pin_groups[group_idx].npins;
return 0;
}
static const struct pinctrl_ops cs42l43_pin_group_ops = {
.get_groups_count = cs42l43_pin_get_groups_count,
.get_group_name = cs42l43_pin_get_group_name,
.get_group_pins = cs42l43_pin_get_group_pins,
#if IS_ENABLED(CONFIG_OF)
.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
.dt_free_map = pinconf_generic_dt_free_map,
#endif
};
enum cs42l43_pin_funcs {
CS42L43_FUNC_GPIO,
CS42L43_FUNC_SPDIF,
CS42L43_FUNC_IRQ,
CS42L43_FUNC_MIC_SHT,
CS42L43_FUNC_SPK_SHT,
CS42L43_FUNC_MAX
};
static const char * const cs42l43_pin_funcs[] = {
"gpio", "spdif", "irq", "mic-shutter", "spk-shutter",
};
static const char * const cs42l43_pin_gpio_groups[] = { "gpio1", "gpio3" };
static const char * const cs42l43_pin_spdif_groups[] = { "gpio3" };
static const char * const cs42l43_pin_irq_groups[] = { "gpio1" };
static const char * const cs42l43_pin_shutter_groups[] = { "gpio1", "gpio2", "gpio3" };
static const struct pinfunction cs42l43_pin_func_groups[] = {
PINCTRL_PINFUNCTION("gpio", cs42l43_pin_gpio_groups,
ARRAY_SIZE(cs42l43_pin_gpio_groups)),
PINCTRL_PINFUNCTION("spdif", cs42l43_pin_spdif_groups,
ARRAY_SIZE(cs42l43_pin_spdif_groups)),
PINCTRL_PINFUNCTION("irq", cs42l43_pin_irq_groups,
ARRAY_SIZE(cs42l43_pin_irq_groups)),
PINCTRL_PINFUNCTION("mic-shutter", cs42l43_pin_shutter_groups,
ARRAY_SIZE(cs42l43_pin_shutter_groups)),
PINCTRL_PINFUNCTION("spk-shutter", cs42l43_pin_shutter_groups,
ARRAY_SIZE(cs42l43_pin_shutter_groups)),
};
static_assert(ARRAY_SIZE(cs42l43_pin_funcs) == CS42L43_FUNC_MAX);
static_assert(ARRAY_SIZE(cs42l43_pin_func_groups) == CS42L43_FUNC_MAX);
static int cs42l43_pin_get_func_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(cs42l43_pin_funcs);
}
static const char *cs42l43_pin_get_func_name(struct pinctrl_dev *pctldev,
unsigned int func_idx)
{
return cs42l43_pin_funcs[func_idx];
}
static int cs42l43_pin_get_func_groups(struct pinctrl_dev *pctldev,
unsigned int func_idx,
const char * const **groups,
unsigned int * const num_groups)
{
*groups = cs42l43_pin_func_groups[func_idx].groups;
*num_groups = cs42l43_pin_func_groups[func_idx].ngroups;
return 0;
}
static int cs42l43_pin_set_mux(struct pinctrl_dev *pctldev,
unsigned int func_idx, unsigned int group_idx)
{
struct cs42l43_pin *priv = pinctrl_dev_get_drvdata(pctldev);
unsigned int reg, mask, val;
dev_dbg(priv->dev, "Setting %s to %s\n",
cs42l43_pin_groups[group_idx].name, cs42l43_pin_funcs[func_idx]);
switch (func_idx) {
case CS42L43_FUNC_MIC_SHT:
reg = CS42L43_SHUTTER_CONTROL;
mask = CS42L43_MIC_SHUTTER_CFG_MASK;
val = 0x2 << (group_idx + CS42L43_MIC_SHUTTER_CFG_SHIFT);
break;
case CS42L43_FUNC_SPK_SHT:
reg = CS42L43_SHUTTER_CONTROL;
mask = CS42L43_SPK_SHUTTER_CFG_MASK;
val = 0x2 << (group_idx + CS42L43_SPK_SHUTTER_CFG_SHIFT);
break;
default:
reg = CS42L43_GPIO_FN_SEL;
mask = BIT(group_idx + CS42L43_GPIO1_FN_SEL_SHIFT);
val = (func_idx == CS42L43_FUNC_GPIO) ?
(0x1 << (group_idx + CS42L43_GPIO1_FN_SEL_SHIFT)) : 0;
break;
}
if (priv->shutters_locked && reg == CS42L43_SHUTTER_CONTROL) {
dev_err(priv->dev, "Shutter configuration not available\n");
return -EPERM;
}
return regmap_update_bits(priv->regmap, reg, mask, val);
}
static int cs42l43_gpio_set_direction(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int offset, bool input)
{
struct cs42l43_pin *priv = pinctrl_dev_get_drvdata(pctldev);
unsigned int shift = offset + CS42L43_GPIO1_DIR_SHIFT;
int ret;
dev_dbg(priv->dev, "Setting gpio%d to %s\n",
offset + 1, input ? "input" : "output");
ret = pm_runtime_resume_and_get(priv->dev);
if (ret) {
dev_err(priv->dev, "Failed to resume for direction: %d\n", ret);
return ret;
}
ret = regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL1,
BIT(shift), !!input << shift);
if (ret)
dev_err(priv->dev, "Failed to set gpio%d direction: %d\n",
offset + 1, ret);
pm_runtime_put(priv->dev);
return ret;
}
static int cs42l43_gpio_request_enable(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int offset)
{
return cs42l43_pin_set_mux(pctldev, 0, offset);
}
static void cs42l43_gpio_disable_free(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int offset)
{
cs42l43_gpio_set_direction(pctldev, range, offset, true);
}
static const struct pinmux_ops cs42l43_pin_mux_ops = {
.get_functions_count = cs42l43_pin_get_func_count,
.get_function_name = cs42l43_pin_get_func_name,
.get_function_groups = cs42l43_pin_get_func_groups,
.set_mux = cs42l43_pin_set_mux,
.gpio_request_enable = cs42l43_gpio_request_enable,
.gpio_disable_free = cs42l43_gpio_disable_free,
.gpio_set_direction = cs42l43_gpio_set_direction,
.strict = true,
};
static const unsigned int cs42l43_pin_drv_str_ma[] = { 1, 2, 4, 8, 9, 10, 12, 16 };
static inline int cs42l43_pin_get_drv_str(struct cs42l43_pin *priv, unsigned int pin)
{
const struct cs42l43_pin_data *pdat = cs42l43_pin_pins[pin].drv_data;
unsigned int val;
int ret;
ret = regmap_read(priv->regmap, pdat->reg, &val);
if (ret)
return ret;
return cs42l43_pin_drv_str_ma[(val & pdat->mask) >> pdat->shift];
}
static inline int cs42l43_pin_set_drv_str(struct cs42l43_pin *priv, unsigned int pin,
unsigned int ma)
{
const struct cs42l43_pin_data *pdat = cs42l43_pin_pins[pin].drv_data;
int i;
for (i = 0; i < ARRAY_SIZE(cs42l43_pin_drv_str_ma); i++) {
if (ma == cs42l43_pin_drv_str_ma[i]) {
if ((i << pdat->shift) > pdat->mask)
goto err;
dev_dbg(priv->dev, "Set drive strength for %s to %d mA\n",
cs42l43_pin_pins[pin].name, ma);
return regmap_update_bits(priv->regmap, pdat->reg,
pdat->mask, i << pdat->shift);
}
}
err:
dev_err(priv->dev, "Invalid drive strength for %s: %d mA\n",
cs42l43_pin_pins[pin].name, ma);
return -EINVAL;
}
static inline int cs42l43_pin_get_db(struct cs42l43_pin *priv, unsigned int pin)
{
unsigned int val;
int ret;
if (pin >= CS42L43_NUM_GPIOS)
return -ENOTSUPP;
ret = regmap_read(priv->regmap, CS42L43_GPIO_CTRL2, &val);
if (ret)
return ret;
if (val & (CS42L43_GPIO1_DEGLITCH_BYP_MASK << pin))
return 0;
return 85; // Debounce is roughly 85uS
}
static inline int cs42l43_pin_set_db(struct cs42l43_pin *priv, unsigned int pin,
unsigned int us)
{
if (pin >= CS42L43_NUM_GPIOS)
return -ENOTSUPP;
dev_dbg(priv->dev, "Set debounce %s for %s\n",
str_on_off(us), cs42l43_pin_pins[pin].name);
return regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL2,
CS42L43_GPIO1_DEGLITCH_BYP_MASK << pin,
!!us << pin);
}
static int cs42l43_pin_config_get(struct pinctrl_dev *pctldev,
unsigned int pin, unsigned long *config)
{
struct cs42l43_pin *priv = pinctrl_dev_get_drvdata(pctldev);
unsigned int param = pinconf_to_config_param(*config);
int ret;
switch (param) {
case PIN_CONFIG_DRIVE_STRENGTH:
ret = cs42l43_pin_get_drv_str(priv, pin);
if (ret < 0)
return ret;
break;
case PIN_CONFIG_INPUT_DEBOUNCE:
ret = cs42l43_pin_get_db(priv, pin);
if (ret < 0)
return ret;
break;
default:
return -ENOTSUPP;
}
*config = pinconf_to_config_packed(param, ret);
return 0;
}
static int cs42l43_pin_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *configs, unsigned int num_configs)
{
struct cs42l43_pin *priv = pinctrl_dev_get_drvdata(pctldev);
unsigned int val;
int ret;
while (num_configs) {
val = pinconf_to_config_argument(*configs);
switch (pinconf_to_config_param(*configs)) {
case PIN_CONFIG_DRIVE_STRENGTH:
ret = cs42l43_pin_set_drv_str(priv, pin, val);
if (ret)
return ret;
break;
case PIN_CONFIG_INPUT_DEBOUNCE:
ret = cs42l43_pin_set_db(priv, pin, val);
if (ret)
return ret;
break;
default:
return -ENOTSUPP;
}
configs++;
num_configs--;
}
return 0;
}
static int cs42l43_pin_config_group_get(struct pinctrl_dev *pctldev,
unsigned int selector, unsigned long *config)
{
int i, ret;
for (i = 0; i < cs42l43_pin_groups[selector].npins; ++i) {
ret = cs42l43_pin_config_get(pctldev,
cs42l43_pin_groups[selector].pins[i],
config);
if (ret)
return ret;
}
return 0;
}
static int cs42l43_pin_config_group_set(struct pinctrl_dev *pctldev,
unsigned int selector,
unsigned long *configs,
unsigned int num_configs)
{
int i, ret;
for (i = 0; i < cs42l43_pin_groups[selector].npins; ++i) {
ret = cs42l43_pin_config_set(pctldev,
cs42l43_pin_groups[selector].pins[i],
configs, num_configs);
if (ret)
return ret;
}
return 0;
}
static const struct pinconf_ops cs42l43_pin_conf_ops = {
.is_generic = true,
.pin_config_get = cs42l43_pin_config_get,
.pin_config_set = cs42l43_pin_config_set,
.pin_config_group_get = cs42l43_pin_config_group_get,
.pin_config_group_set = cs42l43_pin_config_group_set,
};
static struct pinctrl_desc cs42l43_pin_desc = {
.name = "cs42l43-pinctrl",
.owner = THIS_MODULE,
.pins = cs42l43_pin_pins,
.npins = ARRAY_SIZE(cs42l43_pin_pins),
.pctlops = &cs42l43_pin_group_ops,
.pmxops = &cs42l43_pin_mux_ops,
.confops = &cs42l43_pin_conf_ops,
};
static int cs42l43_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct cs42l43_pin *priv = gpiochip_get_data(chip);
unsigned int val;
int ret;
ret = pm_runtime_resume_and_get(priv->dev);
if (ret) {
dev_err(priv->dev, "Failed to resume for get: %d\n", ret);
return ret;
}
ret = regmap_read(priv->regmap, CS42L43_GPIO_STS, &val);
if (ret)
dev_err(priv->dev, "Failed to get gpio%d: %d\n", offset + 1, ret);
else
ret = !!(val & BIT(offset + CS42L43_GPIO1_STS_SHIFT));
pm_runtime_put(priv->dev);
return ret;
}
static void cs42l43_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
{
struct cs42l43_pin *priv = gpiochip_get_data(chip);
unsigned int shift = offset + CS42L43_GPIO1_LVL_SHIFT;
int ret;
dev_dbg(priv->dev, "Setting gpio%d to %s\n",
offset + 1, value ? "high" : "low");
ret = pm_runtime_resume_and_get(priv->dev);
if (ret) {
dev_err(priv->dev, "Failed to resume for set: %d\n", ret);
return;
}
ret = regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL1,
BIT(shift), value << shift);
if (ret)
dev_err(priv->dev, "Failed to set gpio%d: %d\n", offset + 1, ret);
pm_runtime_put(priv->dev);
}
static int cs42l43_gpio_direction_in(struct gpio_chip *chip, unsigned int offset)
{
return pinctrl_gpio_direction_input(chip->base + offset);
}
static int cs42l43_gpio_direction_out(struct gpio_chip *chip,
unsigned int offset, int value)
{
cs42l43_gpio_set(chip, offset, value);
return pinctrl_gpio_direction_output(chip->base + offset);
}
static int cs42l43_gpio_add_pin_ranges(struct gpio_chip *chip)
{
struct cs42l43_pin *priv = gpiochip_get_data(chip);
int ret;
ret = gpiochip_add_pin_range(&priv->gpio_chip, priv->gpio_chip.label,
0, 0, CS42L43_NUM_GPIOS);
if (ret)
dev_err(priv->dev, "Failed to add GPIO pin range: %d\n", ret);
return ret;
}
static int cs42l43_pin_probe(struct platform_device *pdev)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
struct cs42l43_pin *priv;
struct pinctrl_dev *pctldev;
struct fwnode_handle *fwnode = dev_fwnode(cs42l43->dev);
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = &pdev->dev;
priv->regmap = cs42l43->regmap;
priv->shutters_locked = cs42l43->hw_lock;
priv->gpio_chip.request = gpiochip_generic_request;
priv->gpio_chip.free = gpiochip_generic_free;
priv->gpio_chip.direction_input = cs42l43_gpio_direction_in;
priv->gpio_chip.direction_output = cs42l43_gpio_direction_out;
priv->gpio_chip.add_pin_ranges = cs42l43_gpio_add_pin_ranges;
priv->gpio_chip.get = cs42l43_gpio_get;
priv->gpio_chip.set = cs42l43_gpio_set;
priv->gpio_chip.label = dev_name(priv->dev);
priv->gpio_chip.parent = priv->dev;
priv->gpio_chip.can_sleep = true;
priv->gpio_chip.base = -1;
priv->gpio_chip.ngpio = CS42L43_NUM_GPIOS;
if (is_of_node(fwnode)) {
fwnode = fwnode_get_named_child_node(fwnode, "pinctrl");
if (fwnode && !fwnode->dev)
fwnode->dev = priv->dev;
}
priv->gpio_chip.fwnode = fwnode;
device_set_node(priv->dev, fwnode);
devm_pm_runtime_enable(priv->dev);
pm_runtime_idle(priv->dev);
pctldev = devm_pinctrl_register(priv->dev, &cs42l43_pin_desc, priv);
if (IS_ERR(pctldev))
return dev_err_probe(priv->dev, PTR_ERR(pctldev),
"Failed to register pinctrl\n");
ret = devm_gpiochip_add_data(priv->dev, &priv->gpio_chip, priv);
if (ret)
return dev_err_probe(priv->dev, ret,
"Failed to register gpiochip\n");
return 0;
}
static const struct platform_device_id cs42l43_pin_id_table[] = {
{ "cs42l43-pinctrl", },
{}
};
MODULE_DEVICE_TABLE(platform, cs42l43_pin_id_table);
static struct platform_driver cs42l43_pin_driver = {
.driver = {
.name = "cs42l43-pinctrl",
},
.probe = cs42l43_pin_probe,
.id_table = cs42l43_pin_id_table,
};
module_platform_driver(cs42l43_pin_driver);
MODULE_DESCRIPTION("CS42L43 Pinctrl Driver");
MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
MODULE_LICENSE("GPL");

View file

@ -3,6 +3,7 @@
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_registers.h>
@ -25,6 +26,23 @@ static int sdw_get_id(struct sdw_bus *bus)
return 0;
}
static int sdw_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct sdw_bus *bus = h->host_data;
irq_set_chip_data(virq, bus);
irq_set_chip(virq, &bus->irq_chip);
irq_set_nested_thread(virq, 1);
irq_set_noprobe(virq);
return 0;
}
static const struct irq_domain_ops sdw_domain_ops = {
.map = sdw_irq_map,
};
/**
* sdw_bus_master_add() - add a bus Master instance
* @bus: bus instance
@ -151,6 +169,14 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
bus->params.curr_bank = SDW_BANK0;
bus->params.next_bank = SDW_BANK1;
bus->irq_chip.name = dev_name(bus->dev);
bus->domain = irq_domain_create_linear(fwnode, SDW_MAX_DEVICES,
&sdw_domain_ops, bus);
if (!bus->domain) {
dev_err(bus->dev, "Failed to add IRQ domain\n");
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL(sdw_bus_master_add);
@ -187,6 +213,9 @@ static int sdw_delete_slave(struct device *dev, void *data)
void sdw_bus_master_delete(struct sdw_bus *bus)
{
device_for_each_child(bus->dev, NULL, sdw_delete_slave);
irq_domain_remove(bus->domain);
sdw_master_device_del(bus);
sdw_bus_debugfs_exit(bus);
@ -1725,6 +1754,9 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
struct device *dev = &slave->dev;
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
if (slave->prop.use_domain_irq && slave->irq)
handle_nested_irq(slave->irq);
if (drv->ops && drv->ops->interrupt_callback) {
slave_intr.sdca_cascade = sdca_cascade;
slave_intr.control_port = clear;

View file

@ -122,6 +122,12 @@ static int sdw_drv_probe(struct device *dev)
if (drv->ops && drv->ops->read_prop)
drv->ops->read_prop(slave);
if (slave->prop.use_domain_irq) {
slave->irq = irq_create_mapping(slave->bus->domain, slave->dev_num);
if (!slave->irq)
dev_warn(dev, "Failed to map IRQ\n");
}
/* init the sysfs as we have properties now */
ret = sdw_slave_sysfs_init(slave);
if (ret < 0)
@ -166,7 +172,13 @@ static int sdw_drv_remove(struct device *dev)
int ret = 0;
mutex_lock(&slave->sdw_dev_lock);
slave->probed = false;
if (slave->prop.use_domain_irq)
irq_dispose_mapping(irq_find_mapping(slave->bus->domain,
slave->dev_num));
mutex_unlock(&slave->sdw_dev_lock);
if (drv->remove)

File diff suppressed because it is too large Load diff

102
include/linux/mfd/cs42l43.h Normal file
View file

@ -0,0 +1,102 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* CS42L43 core driver external data
*
* Copyright (C) 2022-2023 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/soundwire/sdw.h>
#include <linux/workqueue.h>
#ifndef CS42L43_CORE_EXT_H
#define CS42L43_CORE_EXT_H
#define CS42L43_N_SUPPLIES 3
enum cs42l43_irq_numbers {
CS42L43_PLL_LOST_LOCK,
CS42L43_PLL_READY,
CS42L43_HP_STARTUP_DONE,
CS42L43_HP_SHUTDOWN_DONE,
CS42L43_HSDET_DONE,
CS42L43_TIPSENSE_UNPLUG_DB,
CS42L43_TIPSENSE_PLUG_DB,
CS42L43_RINGSENSE_UNPLUG_DB,
CS42L43_RINGSENSE_PLUG_DB,
CS42L43_TIPSENSE_UNPLUG_PDET,
CS42L43_TIPSENSE_PLUG_PDET,
CS42L43_RINGSENSE_UNPLUG_PDET,
CS42L43_RINGSENSE_PLUG_PDET,
CS42L43_HS2_BIAS_SENSE,
CS42L43_HS1_BIAS_SENSE,
CS42L43_DC_DETECT1_FALSE,
CS42L43_DC_DETECT1_TRUE,
CS42L43_HSBIAS_CLAMPED,
CS42L43_HS3_4_BIAS_SENSE,
CS42L43_AMP2_CLK_STOP_FAULT,
CS42L43_AMP1_CLK_STOP_FAULT,
CS42L43_AMP2_VDDSPK_FAULT,
CS42L43_AMP1_VDDSPK_FAULT,
CS42L43_AMP2_SHUTDOWN_DONE,
CS42L43_AMP1_SHUTDOWN_DONE,
CS42L43_AMP2_STARTUP_DONE,
CS42L43_AMP1_STARTUP_DONE,
CS42L43_AMP2_THERM_SHDN,
CS42L43_AMP1_THERM_SHDN,
CS42L43_AMP2_THERM_WARN,
CS42L43_AMP1_THERM_WARN,
CS42L43_AMP2_SCDET,
CS42L43_AMP1_SCDET,
CS42L43_GPIO3_FALL,
CS42L43_GPIO3_RISE,
CS42L43_GPIO2_FALL,
CS42L43_GPIO2_RISE,
CS42L43_GPIO1_FALL,
CS42L43_GPIO1_RISE,
CS42L43_HP_ILIMIT,
CS42L43_HP_LOADDET_DONE,
};
struct cs42l43 {
struct device *dev;
struct regmap *regmap;
struct sdw_slave *sdw;
struct regulator *vdd_p;
struct regulator *vdd_d;
struct regulator_bulk_data core_supplies[CS42L43_N_SUPPLIES];
struct gpio_desc *reset;
int irq;
struct regmap_irq_chip irq_chip;
struct regmap_irq_chip_data *irq_data;
struct work_struct boot_work;
struct completion device_attach;
struct completion device_detach;
struct completion firmware_download;
int firmware_error;
unsigned int sdw_freq;
/* Lock to gate control of the PLL and its sources. */
struct mutex pll_lock;
bool sdw_pll_active;
bool attached;
bool hw_lock;
};
#endif /* CS42L43_CORE_EXT_H */

View file

@ -6,6 +6,8 @@
#include <linux/bug.h>
#include <linux/lockdep_types.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/mod_devicetable.h>
#include <linux/bitfield.h>
@ -370,6 +372,7 @@ struct sdw_dpn_prop {
* @clock_reg_supported: the Peripheral implements the clock base and scale
* registers introduced with the SoundWire 1.2 specification. SDCA devices
* do not need to set this boolean property as the registers are required.
* @use_domain_irq: call actual IRQ handler on slave, as well as callback
*/
struct sdw_slave_prop {
u32 mipi_revision;
@ -394,6 +397,7 @@ struct sdw_slave_prop {
u8 scp_int1_mask;
u32 quirks;
bool clock_reg_supported;
bool use_domain_irq;
};
#define SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY BIT(0)
@ -641,6 +645,7 @@ struct sdw_slave_ops {
* struct sdw_slave - SoundWire Slave
* @id: MIPI device ID
* @dev: Linux device
* @irq: IRQ number
* @status: Status reported by the Slave
* @bus: Bus handle
* @prop: Slave properties
@ -670,6 +675,7 @@ struct sdw_slave_ops {
struct sdw_slave {
struct sdw_slave_id id;
struct device dev;
int irq;
enum sdw_slave_status status;
struct sdw_bus *bus;
struct sdw_slave_prop prop;
@ -885,6 +891,7 @@ struct sdw_master_ops {
* is used to compute and program bus bandwidth, clock, frame shape,
* transport and port parameters
* @debugfs: Bus debugfs
* @domain: IRQ domain
* @defer_msg: Defer message
* @clk_stop_timeout: Clock stop timeout computed
* @bank_switch_timeout: Bank switch timeout computed
@ -920,6 +927,8 @@ struct sdw_bus {
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
struct irq_chip irq_chip;
struct irq_domain *domain;
struct sdw_defer defer_msg;
unsigned int clk_stop_timeout;
u32 bank_switch_timeout;