mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 00:22:00 +02:00
ASoC: cs35l56: More support for new Dell laptops
Merge series from Richard Fitzgerald <rf@opensource.cirrus.com>: Some new Dell models use spare pins on the amp as a binary integer value to indicate the speaker type. The driver must use this to select the correct firmware files for the hardware. Patch #1 is the new support. The other patches are for KUnit testing.
This commit is contained in:
commit
5209af4db0
|
|
@ -9,6 +9,7 @@
|
|||
#ifndef __CS35L56_H
|
||||
#define __CS35L56_H
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
|
@ -26,6 +27,9 @@ struct snd_ctl_elem_value;
|
|||
#define CS35L56_GLOBAL_ENABLES 0x0002014
|
||||
#define CS35L56_BLOCK_ENABLES 0x0002018
|
||||
#define CS35L56_BLOCK_ENABLES2 0x000201C
|
||||
#define CS35L56_SYNC_GPIO1_CFG 0x0002410
|
||||
#define CS35L56_ASP2_DIO_GPIO13_CFG 0x0002440
|
||||
#define CS35L56_UPDATE_REGS 0x0002A0C
|
||||
#define CS35L56_REFCLK_INPUT 0x0002C04
|
||||
#define CS35L56_GLOBAL_SAMPLE_RATE 0x0002C0C
|
||||
#define CS35L56_OTP_MEM_53 0x00300D4
|
||||
|
|
@ -65,6 +69,9 @@ struct snd_ctl_elem_value;
|
|||
#define CS35L56_IRQ1_MASK_8 0x000E0AC
|
||||
#define CS35L56_IRQ1_MASK_18 0x000E0D4
|
||||
#define CS35L56_IRQ1_MASK_20 0x000E0DC
|
||||
#define CS35L56_GPIO_STATUS1 0x000F000
|
||||
#define CS35L56_GPIO1_CTRL1 0x000F008
|
||||
#define CS35L56_GPIO13_CTRL1 0x000F038
|
||||
#define CS35L56_MIXER_NGATE_CH1_CFG 0x0010004
|
||||
#define CS35L56_MIXER_NGATE_CH2_CFG 0x0010008
|
||||
#define CS35L56_DSP_MBOX_1_RAW 0x0011000
|
||||
|
|
@ -130,6 +137,17 @@ struct snd_ctl_elem_value;
|
|||
#define CS35L56_MTLREVID_MASK 0x0000000F
|
||||
#define CS35L56_REVID_B0 0x000000B0
|
||||
|
||||
/* PAD_INTF */
|
||||
#define CS35L56_PAD_GPIO_PULL_MASK GENMASK(3, 2)
|
||||
#define CS35L56_PAD_GPIO_IE BIT(0)
|
||||
|
||||
#define CS35L56_PAD_PULL_NONE 0
|
||||
#define CS35L56_PAD_PULL_UP 1
|
||||
#define CS35L56_PAD_PULL_DOWN 2
|
||||
|
||||
/* UPDATE_REGS */
|
||||
#define CS35L56_UPDT_GPIO_PRES BIT(6)
|
||||
|
||||
/* ASP_ENABLES1 */
|
||||
#define CS35L56_ASP_RX2_EN_SHIFT 17
|
||||
#define CS35L56_ASP_RX1_EN_SHIFT 16
|
||||
|
|
@ -185,6 +203,12 @@ struct snd_ctl_elem_value;
|
|||
/* MIXER_NGATE_CHn_CFG */
|
||||
#define CS35L56_AUX_NGATE_CHn_EN 0x00000001
|
||||
|
||||
/* GPIOn_CTRL1 */
|
||||
#define CS35L56_GPIO_DIR_MASK BIT(31)
|
||||
#define CS35L56_GPIO_FN_MASK GENMASK(2, 0)
|
||||
|
||||
#define CS35L56_GPIO_FN_GPIO 0x00000001
|
||||
|
||||
/* Mixer input sources */
|
||||
#define CS35L56_INPUT_SRC_NONE 0x00
|
||||
#define CS35L56_INPUT_SRC_ASP1RX1 0x08
|
||||
|
|
@ -279,6 +303,7 @@ struct snd_ctl_elem_value;
|
|||
#define CS35L56_HALO_STATE_TIMEOUT_US 250000
|
||||
#define CS35L56_RESET_PULSE_MIN_US 1100
|
||||
#define CS35L56_WAKE_HOLD_TIME_US 1000
|
||||
#define CS35L56_PAD_PULL_SETTLE_US 10
|
||||
|
||||
#define CS35L56_CALIBRATION_POLL_US (100 * USEC_PER_MSEC)
|
||||
#define CS35L56_CALIBRATION_TIMEOUT_US (5 * USEC_PER_SEC)
|
||||
|
|
@ -289,6 +314,9 @@ struct snd_ctl_elem_value;
|
|||
#define CS35L56_NUM_BULK_SUPPLIES 3
|
||||
#define CS35L56_NUM_DSP_REGIONS 5
|
||||
|
||||
#define CS35L56_MAX_GPIO 13
|
||||
#define CS35L63_MAX_GPIO 9
|
||||
|
||||
/* Additional margin for SYSTEM_RESET to control port ready on SPI */
|
||||
#define CS35L56_SPI_RESET_TO_PORT_READY_US (CS35L56_CONTROL_PORT_READY_US + 2500)
|
||||
|
||||
|
|
@ -338,6 +366,10 @@ struct cs35l56_base {
|
|||
const struct cirrus_amp_cal_controls *calibration_controls;
|
||||
struct dentry *debugfs;
|
||||
u64 silicon_uid;
|
||||
u8 onchip_spkid_gpios[5];
|
||||
u8 num_onchip_spkid_gpios;
|
||||
u8 onchip_spkid_pulls[5];
|
||||
u8 num_onchip_spkid_pulls;
|
||||
};
|
||||
|
||||
static inline bool cs35l56_is_otp_register(unsigned int reg)
|
||||
|
|
@ -413,6 +445,11 @@ void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base);
|
|||
void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
|
||||
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base);
|
||||
int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base);
|
||||
int cs35l56_check_and_save_onchip_spkid_gpios(struct cs35l56_base *cs35l56_base,
|
||||
const u32 *gpios, int num_gpios,
|
||||
const u32 *pulls, int num_pulls);
|
||||
int cs35l56_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base);
|
||||
int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base);
|
||||
int cs35l56_get_bclk_freq_id(unsigned int freq);
|
||||
void cs35l56_fill_supply_names(struct regulator_bulk_data *data);
|
||||
|
||||
|
|
|
|||
|
|
@ -936,6 +936,20 @@ config SND_SOC_CS35L56_TEST
|
|||
help
|
||||
This builds KUnit tests for the Cirrus Logic cs35l56
|
||||
codec driver.
|
||||
|
||||
For more information on KUnit and unit tests in general,
|
||||
please refer to the KUnit documentation in
|
||||
Documentation/dev-tools/kunit/.
|
||||
If in doubt, say "N".
|
||||
|
||||
config SND_SOC_CS35L56_SHARED_TEST
|
||||
tristate "KUnit test for Cirrus Logic cs35l56-shared" if !KUNIT_ALL_TESTS
|
||||
depends on SND_SOC_CS35L56_SHARED && KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
This builds KUnit tests for the Cirrus Logic cs35l56-shared
|
||||
module.
|
||||
|
||||
For more information on KUnit and unit tests in general,
|
||||
please refer to the KUnit documentation in
|
||||
Documentation/dev-tools/kunit/.
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ snd-soc-cs35l45-spi-y := cs35l45-spi.o
|
|||
snd-soc-cs35l45-i2c-y := cs35l45-i2c.o
|
||||
snd-soc-cs35l56-y := cs35l56.o
|
||||
snd-soc-cs35l56-shared-y := cs35l56-shared.o
|
||||
snd-soc-cs35l56-shared-test-y := cs35l56-shared-test.o
|
||||
snd-soc-cs35l56-i2c-y := cs35l56-i2c.o
|
||||
snd-soc-cs35l56-spi-y := cs35l56-spi.o
|
||||
snd-soc-cs35l56-sdw-y := cs35l56-sdw.o
|
||||
|
|
@ -512,6 +513,7 @@ obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o
|
|||
obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_CS35L56) += snd-soc-cs35l56.o
|
||||
obj-$(CONFIG_SND_SOC_CS35L56_SHARED) += snd-soc-cs35l56-shared.o
|
||||
obj-$(CONFIG_SND_SOC_CS35L56_SHARED_TEST) += snd-soc-cs35l56-shared-test.o
|
||||
obj-$(CONFIG_SND_SOC_CS35L56_I2C) += snd-soc-cs35l56-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_CS35L56_SPI) += snd-soc-cs35l56-spi.o
|
||||
obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o
|
||||
|
|
|
|||
680
sound/soc/codecs/cs35l56-shared-test.c
Normal file
680
sound/soc/codecs/cs35l56-shared-test.c
Normal file
|
|
@ -0,0 +1,680 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// KUnit test for the Cirrus Logic cs35l56-shared module.
|
||||
//
|
||||
// Copyright (C) 2026 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <kunit/resource.h>
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/static_stub.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device/faux.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/seq_buf.h>
|
||||
#include <sound/cs35l56.h>
|
||||
|
||||
struct cs35l56_shared_test_priv {
|
||||
struct kunit *test;
|
||||
struct faux_device *amp_dev;
|
||||
struct regmap *registers;
|
||||
struct cs35l56_base *cs35l56_base;
|
||||
u8 applied_pad_pull_state[CS35L56_MAX_GPIO];
|
||||
};
|
||||
|
||||
struct cs35l56_shared_test_param {
|
||||
int spkid_gpios[4];
|
||||
int spkid_pulls[4];
|
||||
unsigned long gpio_status;
|
||||
int spkid;
|
||||
};
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
|
||||
struct faux_device *)
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_wrapper, regmap_exit, struct regmap *)
|
||||
|
||||
static const struct regmap_config cs35l56_shared_test_mock_registers_regmap = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = CS35L56_DSP1_PMEM_5114,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
static const struct regmap_bus cs35l56_shared_test_mock_registers_regmap_bus = {
|
||||
/* No handlers because it is always in cache-only */
|
||||
};
|
||||
|
||||
static unsigned int cs35l56_shared_test_read_gpio_status(struct cs35l56_shared_test_priv *priv)
|
||||
{
|
||||
const struct cs35l56_shared_test_param *param = priv->test->param_value;
|
||||
unsigned int reg_offs, pad_cfg, val;
|
||||
unsigned int status = 0;
|
||||
unsigned int mask = 1;
|
||||
|
||||
for (reg_offs = 0; reg_offs < CS35L56_MAX_GPIO * sizeof(u32); reg_offs += sizeof(u32)) {
|
||||
regmap_read(priv->registers, CS35L56_SYNC_GPIO1_CFG + reg_offs, &pad_cfg);
|
||||
regmap_read(priv->registers, CS35L56_GPIO1_CTRL1 + reg_offs, &val);
|
||||
|
||||
/* Only read a value if set as an input pin and as a GPIO */
|
||||
val &= (CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_MASK);
|
||||
if ((pad_cfg & CS35L56_PAD_GPIO_IE) &&
|
||||
(val == (CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO)))
|
||||
status |= (param->gpio_status & mask);
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_updt_gpio_pres(struct cs35l56_shared_test_priv *priv,
|
||||
unsigned int reg, unsigned int val)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
ret = regmap_write(priv->registers, reg, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val & CS35L56_UPDT_GPIO_PRES) {
|
||||
/* Simulate transferring register state to internal latches */
|
||||
for (i = 0; i < ARRAY_SIZE(priv->applied_pad_pull_state); i++) {
|
||||
reg = CS35L56_SYNC_GPIO1_CFG + (i * sizeof(u32));
|
||||
regmap_read(priv->registers, reg, &val);
|
||||
val = FIELD_GET(CS35L56_PAD_GPIO_PULL_MASK, val);
|
||||
priv->applied_pad_pull_state[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_reg_read(void *context, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
struct cs35l56_shared_test_priv *priv = context;
|
||||
|
||||
switch (reg) {
|
||||
case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
|
||||
case CS35L56_GPIO1_CTRL1 ... CS35L56_GPIO13_CTRL1:
|
||||
return regmap_read(priv->registers, reg, val);
|
||||
case CS35L56_UPDATE_REGS:
|
||||
*val = 0;
|
||||
return 0;
|
||||
case CS35L56_GPIO_STATUS1:
|
||||
*val = cs35l56_shared_test_read_gpio_status(priv);
|
||||
return 0;
|
||||
default:
|
||||
kunit_fail_current_test("Bad regmap read address %#x\n", reg);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_reg_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct cs35l56_shared_test_priv *priv = context;
|
||||
|
||||
switch (reg) {
|
||||
case CS35L56_UPDATE_REGS:
|
||||
return cs35l56_shared_test_updt_gpio_pres(priv, reg, val);
|
||||
case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
|
||||
case CS35L56_GPIO1_CTRL1 ... CS35L56_GPIO13_CTRL1:
|
||||
return regmap_write(priv->registers, reg, val);
|
||||
default:
|
||||
kunit_fail_current_test("Bad regmap write address %#x\n", reg);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_bus cs35l56_shared_test_regmap_bus = {
|
||||
.reg_read = cs35l56_shared_test_reg_read,
|
||||
.reg_write = cs35l56_shared_test_reg_write,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Self-test that the mock GPIO registers obey the configuration bits.
|
||||
* Other tests rely on the mocked registers only returning a GPIO state
|
||||
* if the pin is correctly set as a GPIO input.
|
||||
*/
|
||||
static void cs35l56_shared_test_mock_gpio_status_selftest(struct kunit *test)
|
||||
{
|
||||
const struct cs35l56_shared_test_param *param = test->param_value;
|
||||
struct cs35l56_shared_test_priv *priv = test->priv;
|
||||
struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
|
||||
unsigned int reg, val;
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(test, param);
|
||||
|
||||
/* Set all pins non-GPIO and output. Mock GPIO_STATUS should read 0 */
|
||||
for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
|
||||
|
||||
/* Set all pads as inputs */
|
||||
for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_PAD_GPIO_IE));
|
||||
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
|
||||
KUNIT_EXPECT_EQ(test, val, 0);
|
||||
|
||||
/* Set all pins as GPIO outputs. Mock GPIO_STATUS should read 0 */
|
||||
for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_GPIO_FN_GPIO));
|
||||
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
|
||||
KUNIT_EXPECT_EQ(test, val, 0);
|
||||
|
||||
/* Set all pins as non-GPIO inputs. Mock GPIO_STATUS should read 0 */
|
||||
for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, CS35L56_GPIO_DIR_MASK));
|
||||
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
|
||||
KUNIT_EXPECT_EQ(test, val, 0);
|
||||
|
||||
/* Set all pins as GPIO inputs. Mock GPIO_STATUS should match param->gpio_status */
|
||||
for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
regmap_write(priv->registers, reg,
|
||||
CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO));
|
||||
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
|
||||
KUNIT_EXPECT_EQ(test, val, param->gpio_status);
|
||||
|
||||
/* Set all pads as outputs. Mock GPIO_STATUS should read 0 */
|
||||
for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
|
||||
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_read(cs35l56_base->regmap, CS35L56_GPIO_STATUS1, &val));
|
||||
KUNIT_EXPECT_EQ(test, val, 0);
|
||||
}
|
||||
|
||||
/* Test that the listed chip pins are assembled into a speaker ID integer. */
|
||||
static void cs35l56_shared_test_get_onchip_speaker_id(struct kunit *test)
|
||||
{
|
||||
const struct cs35l56_shared_test_param *param = test->param_value;
|
||||
struct cs35l56_shared_test_priv *priv = test->priv;
|
||||
struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
|
||||
unsigned int i, reg;
|
||||
|
||||
/* Set all pins non-GPIO and output */
|
||||
for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
|
||||
|
||||
for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
|
||||
|
||||
/* Init GPIO array */
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
|
||||
if (param->spkid_gpios[i] < 0)
|
||||
break;
|
||||
|
||||
cs35l56_base->onchip_spkid_gpios[i] = param->spkid_gpios[i] - 1;
|
||||
cs35l56_base->num_onchip_spkid_gpios++;
|
||||
}
|
||||
|
||||
cs35l56_base->num_onchip_spkid_pulls = 0;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0);
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_read_onchip_spkid(cs35l56_base), param->spkid);
|
||||
}
|
||||
|
||||
/* Test that the listed chip pins and the corresponding pads are configured correctly. */
|
||||
static void cs35l56_shared_test_onchip_speaker_id_pad_config(struct kunit *test)
|
||||
{
|
||||
const struct cs35l56_shared_test_param *param = test->param_value;
|
||||
struct cs35l56_shared_test_priv *priv = test->priv;
|
||||
struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
|
||||
unsigned int i, reg, val;
|
||||
|
||||
/* Init values in all pin registers */
|
||||
for (reg = CS35L56_GPIO1_CTRL1; reg <= CS35L56_GPIO13_CTRL1; reg += sizeof(u32))
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
|
||||
|
||||
for (reg = CS35L56_SYNC_GPIO1_CFG; reg <= CS35L56_ASP2_DIO_GPIO13_CFG; reg += sizeof(u32))
|
||||
KUNIT_ASSERT_EQ(test, 0, regmap_write(priv->registers, reg, 0));
|
||||
|
||||
/* Init GPIO array */
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
|
||||
if (param->spkid_gpios[i] < 0)
|
||||
break;
|
||||
|
||||
cs35l56_base->onchip_spkid_gpios[i] = param->spkid_gpios[i] - 1;
|
||||
cs35l56_base->num_onchip_spkid_gpios++;
|
||||
}
|
||||
|
||||
/* Init pulls array */
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
|
||||
if (param->spkid_pulls[i] < 0)
|
||||
break;
|
||||
|
||||
cs35l56_base->onchip_spkid_pulls[i] = param->spkid_pulls[i];
|
||||
cs35l56_base->num_onchip_spkid_pulls++;
|
||||
}
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
|
||||
if (param->spkid_gpios[i] < 0)
|
||||
break;
|
||||
|
||||
/* Pad should be an input */
|
||||
reg = CS35L56_SYNC_GPIO1_CFG + ((param->spkid_gpios[i] - 1) * sizeof(u32));
|
||||
KUNIT_EXPECT_EQ(test, regmap_read(priv->registers, reg, &val), 0);
|
||||
KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_IE, CS35L56_PAD_GPIO_IE);
|
||||
|
||||
/* Specified pulls should be set, others should be none */
|
||||
if (i < cs35l56_base->num_onchip_spkid_pulls) {
|
||||
KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_PULL_MASK,
|
||||
FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK,
|
||||
param->spkid_pulls[i]));
|
||||
} else {
|
||||
KUNIT_EXPECT_EQ(test, val & CS35L56_PAD_GPIO_PULL_MASK,
|
||||
CS35L56_PAD_PULL_NONE);
|
||||
}
|
||||
|
||||
/* Pulls for all specfied GPIOs should have been transferred to AO latch */
|
||||
if (i < cs35l56_base->num_onchip_spkid_pulls) {
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
priv->applied_pad_pull_state[param->spkid_gpios[i] - 1],
|
||||
param->spkid_pulls[i]);
|
||||
} else {
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
priv->applied_pad_pull_state[param->spkid_gpios[i] - 1],
|
||||
CS35L56_PAD_PULL_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test that the listed chip pins are stashed correctly. */
|
||||
static void cs35l56_shared_test_stash_onchip_spkid_pins(struct kunit *test)
|
||||
{
|
||||
const struct cs35l56_shared_test_param *param = test->param_value;
|
||||
struct cs35l56_shared_test_priv *priv = test->priv;
|
||||
struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
|
||||
u32 gpios[5], pulls[5];
|
||||
int i, num_gpios, num_pulls;
|
||||
|
||||
static_assert(ARRAY_SIZE(gpios) >= ARRAY_SIZE(param->spkid_gpios));
|
||||
static_assert(ARRAY_SIZE(pulls) >= ARRAY_SIZE(param->spkid_pulls));
|
||||
|
||||
num_gpios = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
|
||||
if (param->spkid_gpios[i] < 0)
|
||||
break;
|
||||
|
||||
gpios[i] = (u32)param->spkid_gpios[i];
|
||||
num_gpios++;
|
||||
}
|
||||
|
||||
num_pulls = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
|
||||
if (param->spkid_pulls[i] < 0)
|
||||
break;
|
||||
|
||||
pulls[i] = (u32)param->spkid_pulls[i];
|
||||
num_pulls++;
|
||||
}
|
||||
|
||||
cs35l56_base->num_onchip_spkid_gpios = 0;
|
||||
cs35l56_base->num_onchip_spkid_pulls = 0;
|
||||
|
||||
KUNIT_ASSERT_LE(test, num_gpios, ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
|
||||
KUNIT_ASSERT_LE(test, num_pulls, ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls));
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
|
||||
gpios, num_gpios,
|
||||
pulls, num_pulls),
|
||||
0);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_base->num_onchip_spkid_gpios, num_gpios);
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_base->num_onchip_spkid_pulls, num_pulls);
|
||||
|
||||
/* GPIO numbers are adjusted from 1-based to 0-based */
|
||||
for (i = 0; i < num_gpios; i++)
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_base->onchip_spkid_gpios[i], gpios[i] - 1);
|
||||
|
||||
for (i = 0; i < num_pulls; i++)
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_base->onchip_spkid_pulls[i], pulls[i]);
|
||||
}
|
||||
|
||||
/* Test that illegal GPIO numbers are rejected. */
|
||||
static void cs35l56_shared_test_stash_onchip_spkid_pins_reject_invalid(struct kunit *test)
|
||||
{
|
||||
struct cs35l56_shared_test_priv *priv = test->priv;
|
||||
struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
|
||||
u32 gpios[8] = { }, pulls[8] = { };
|
||||
|
||||
KUNIT_EXPECT_LE(test,
|
||||
cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
|
||||
gpios, 1,
|
||||
pulls, 0),
|
||||
0);
|
||||
|
||||
switch (cs35l56_base->type) {
|
||||
case 0x54:
|
||||
case 0x56:
|
||||
case 0x57:
|
||||
gpios[0] = CS35L56_MAX_GPIO + 1;
|
||||
break;
|
||||
case 0x63:
|
||||
gpios[0] = CS35L63_MAX_GPIO + 1;
|
||||
break;
|
||||
default:
|
||||
kunit_fail_current_test("Unsupported type:%#x\n", cs35l56_base->type);
|
||||
return;
|
||||
}
|
||||
KUNIT_EXPECT_LE(test,
|
||||
cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
|
||||
gpios, 1,
|
||||
pulls, 0),
|
||||
0);
|
||||
|
||||
gpios[0] = 1;
|
||||
pulls[0] = 3;
|
||||
KUNIT_EXPECT_LE(test,
|
||||
cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
|
||||
gpios, 1,
|
||||
pulls, 1),
|
||||
0);
|
||||
|
||||
static_assert(ARRAY_SIZE(gpios) > ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
|
||||
static_assert(ARRAY_SIZE(pulls) > ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls));
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
|
||||
gpios, ARRAY_SIZE(gpios),
|
||||
pulls, 0),
|
||||
-EOVERFLOW);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs35l56_check_and_save_onchip_spkid_gpios(cs35l56_base,
|
||||
gpios, 1,
|
||||
pulls, ARRAY_SIZE(pulls)),
|
||||
-EOVERFLOW);
|
||||
}
|
||||
|
||||
static void cs35l56_shared_test_onchip_speaker_id_not_defined(struct kunit *test)
|
||||
{
|
||||
struct cs35l56_shared_test_priv *priv = test->priv;
|
||||
struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
|
||||
|
||||
memset(cs35l56_base->onchip_spkid_gpios, 0, sizeof(cs35l56_base->onchip_spkid_gpios));
|
||||
memset(cs35l56_base->onchip_spkid_pulls, 0, sizeof(cs35l56_base->onchip_spkid_pulls));
|
||||
cs35l56_base->num_onchip_spkid_gpios = 0;
|
||||
cs35l56_base->num_onchip_spkid_pulls = 0;
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_configure_onchip_spkid_pads(cs35l56_base), 0);
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_read_onchip_spkid(cs35l56_base), -ENOENT);
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_case_regmap_init(struct kunit *test,
|
||||
const struct regmap_config *regmap_config)
|
||||
{
|
||||
struct cs35l56_shared_test_priv *priv = test->priv;
|
||||
struct cs35l56_base *cs35l56_base;
|
||||
|
||||
/*
|
||||
* Create a dummy regmap to simulate a register map by holding the
|
||||
* values of all simulated registers in the regmap cache.
|
||||
*/
|
||||
priv->registers = regmap_init(&priv->amp_dev->dev,
|
||||
&cs35l56_shared_test_mock_registers_regmap_bus,
|
||||
priv,
|
||||
&cs35l56_shared_test_mock_registers_regmap);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->registers);
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
kunit_add_action_or_reset(test, regmap_exit_wrapper,
|
||||
priv->registers));
|
||||
regcache_cache_only(priv->registers, true);
|
||||
|
||||
/* Create dummy regmap for cs35l56 driver */
|
||||
cs35l56_base = priv->cs35l56_base;
|
||||
cs35l56_base->regmap = regmap_init(cs35l56_base->dev,
|
||||
&cs35l56_shared_test_regmap_bus,
|
||||
priv,
|
||||
regmap_config);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cs35l56_base->regmap);
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
kunit_add_action_or_reset(test, regmap_exit_wrapper,
|
||||
cs35l56_base->regmap));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_case_base_init(struct kunit *test, u8 type, u8 rev,
|
||||
const struct regmap_config *regmap_config)
|
||||
{
|
||||
struct cs35l56_shared_test_priv *priv;
|
||||
int ret;
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks);
|
||||
|
||||
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
test->priv = priv;
|
||||
priv->test = test;
|
||||
|
||||
/* Create dummy amp driver dev */
|
||||
priv->amp_dev = faux_device_create("cs35l56_shared_test_drv", NULL, NULL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
kunit_add_action_or_reset(test,
|
||||
faux_device_destroy_wrapper,
|
||||
priv->amp_dev));
|
||||
|
||||
priv->cs35l56_base = kunit_kzalloc(test, sizeof(*priv->cs35l56_base), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, priv->cs35l56_base);
|
||||
priv->cs35l56_base->dev = &priv->amp_dev->dev;
|
||||
priv->cs35l56_base->type = type;
|
||||
priv->cs35l56_base->rev = rev;
|
||||
|
||||
if (regmap_config) {
|
||||
ret = cs35l56_shared_test_case_regmap_init(test, regmap_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_case_regmap_init_L56_B0_sdw(struct kunit *test)
|
||||
{
|
||||
return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_sdw);
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_case_regmap_init_L56_B0_spi(struct kunit *test)
|
||||
{
|
||||
return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_spi);
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_case_regmap_init_L56_B0_i2c(struct kunit *test)
|
||||
{
|
||||
return cs35l56_shared_test_case_base_init(test, 0x56, 0xb0, &cs35l56_regmap_i2c);
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_case_regmap_init_L56_B2_sdw(struct kunit *test)
|
||||
{
|
||||
return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_sdw);
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_case_regmap_init_L56_B2_spi(struct kunit *test)
|
||||
{
|
||||
return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_spi);
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_case_regmap_init_L56_B2_i2c(struct kunit *test)
|
||||
{
|
||||
return cs35l56_shared_test_case_base_init(test, 0x56, 0xb2, &cs35l56_regmap_i2c);
|
||||
}
|
||||
|
||||
static int cs35l56_shared_test_case_regmap_init_L63_A1_sdw(struct kunit *test)
|
||||
{
|
||||
return cs35l56_shared_test_case_base_init(test, 0x63, 0xa1, &cs35l63_regmap_sdw);
|
||||
}
|
||||
|
||||
static void cs35l56_shared_test_gpio_param_desc(const struct cs35l56_shared_test_param *param,
|
||||
char *desc)
|
||||
{
|
||||
DECLARE_SEQ_BUF(gpios, 1 + (2 * ARRAY_SIZE(param->spkid_gpios)));
|
||||
DECLARE_SEQ_BUF(pulls, 1 + (2 * ARRAY_SIZE(param->spkid_pulls)));
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
|
||||
if (param->spkid_gpios[i] < 0)
|
||||
break;
|
||||
|
||||
seq_buf_printf(&gpios, "%s%d", (i == 0) ? "" : ",", param->spkid_gpios[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
|
||||
if (param->spkid_pulls[i] < 0)
|
||||
break;
|
||||
|
||||
seq_buf_printf(&pulls, "%s%d", (i == 0) ? "" : ",", param->spkid_pulls[i]);
|
||||
}
|
||||
|
||||
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gpios:{%s} pulls:{%s} status:%#lx spkid:%d",
|
||||
seq_buf_str(&gpios), seq_buf_str(&pulls), param->gpio_status, param->spkid);
|
||||
}
|
||||
|
||||
static const struct cs35l56_shared_test_param cs35l56_shared_test_gpios_selftest_cases[] = {
|
||||
{ .spkid_gpios = { -1 }, .gpio_status = GENMASK(12, 0) },
|
||||
};
|
||||
KUNIT_ARRAY_PARAM(cs35l56_shared_test_gpios_selftest,
|
||||
cs35l56_shared_test_gpios_selftest_cases,
|
||||
cs35l56_shared_test_gpio_param_desc);
|
||||
|
||||
static const struct cs35l56_shared_test_param cs35l56_shared_test_onchip_spkid_cases[] = {
|
||||
{ .spkid_gpios = { 1, -1 }, .gpio_status = 0, .spkid = 0 },
|
||||
{ .spkid_gpios = { 1, -1 }, .gpio_status = ~BIT(0), .spkid = 0 },
|
||||
{ .spkid_gpios = { 1, -1 }, .gpio_status = BIT(0), .spkid = 1 },
|
||||
|
||||
{ .spkid_gpios = { 7, -1 }, .gpio_status = 0, .spkid = 0 },
|
||||
{ .spkid_gpios = { 7, -1 }, .gpio_status = ~BIT(6), .spkid = 0 },
|
||||
{ .spkid_gpios = { 7, -1 }, .gpio_status = BIT(6), .spkid = 1 },
|
||||
|
||||
{ .spkid_gpios = { 1, 7, -1 }, .gpio_status = 0, .spkid = 0 },
|
||||
{ .spkid_gpios = { 1, 7, -1 }, .gpio_status = ~(BIT(0) | BIT(6)), .spkid = 0 },
|
||||
{ .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(6), .spkid = 1 },
|
||||
{ .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(0), .spkid = 2 },
|
||||
{ .spkid_gpios = { 1, 7, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 },
|
||||
|
||||
{ .spkid_gpios = { 7, 1, -1 }, .gpio_status = 0, .spkid = 0 },
|
||||
{ .spkid_gpios = { 7, 1, -1 }, .gpio_status = ~(BIT(6) | BIT(0)), .spkid = 0 },
|
||||
{ .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(0), .spkid = 1 },
|
||||
{ .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(6), .spkid = 2 },
|
||||
{ .spkid_gpios = { 7, 1, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 },
|
||||
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = 0, .spkid = 0 },
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(0), .spkid = 1 },
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(6), .spkid = 2 },
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(6) | BIT(0), .spkid = 3 },
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2), .spkid = 4 },
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(0), .spkid = 5 },
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(6), .spkid = 6 },
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .gpio_status = BIT(2) | BIT(6) | BIT(0), .spkid = 7 },
|
||||
};
|
||||
KUNIT_ARRAY_PARAM(cs35l56_shared_test_onchip_spkid, cs35l56_shared_test_onchip_spkid_cases,
|
||||
cs35l56_shared_test_gpio_param_desc);
|
||||
|
||||
static const struct cs35l56_shared_test_param cs35l56_shared_test_onchip_spkid_pull_cases[] = {
|
||||
{ .spkid_gpios = { 1, -1 }, .spkid_pulls = { 1, -1 }, },
|
||||
{ .spkid_gpios = { 1, -1 }, .spkid_pulls = { 2, -1 }, },
|
||||
|
||||
{ .spkid_gpios = { 7, -1 }, .spkid_pulls = { 1, -1 }, },
|
||||
{ .spkid_gpios = { 7, -1 }, .spkid_pulls = { 2, -1 }, },
|
||||
|
||||
{ .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 1, 1, -1 }, },
|
||||
{ .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 2, 2, -1 }, },
|
||||
|
||||
{ .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 1, 1, -1 }, },
|
||||
{ .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 2, 2, -1 }, },
|
||||
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 1, 1, 1, -1 }, },
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 2, 2, 2, -1 }, },
|
||||
};
|
||||
KUNIT_ARRAY_PARAM(cs35l56_shared_test_onchip_spkid_pull,
|
||||
cs35l56_shared_test_onchip_spkid_pull_cases,
|
||||
cs35l56_shared_test_gpio_param_desc);
|
||||
|
||||
static struct kunit_case cs35l56_shared_test_cases[] = {
|
||||
/* Tests for speaker id */
|
||||
KUNIT_CASE_PARAM(cs35l56_shared_test_mock_gpio_status_selftest,
|
||||
cs35l56_shared_test_gpios_selftest_gen_params),
|
||||
KUNIT_CASE_PARAM(cs35l56_shared_test_get_onchip_speaker_id,
|
||||
cs35l56_shared_test_onchip_spkid_gen_params),
|
||||
KUNIT_CASE_PARAM(cs35l56_shared_test_onchip_speaker_id_pad_config,
|
||||
cs35l56_shared_test_onchip_spkid_gen_params),
|
||||
KUNIT_CASE_PARAM(cs35l56_shared_test_onchip_speaker_id_pad_config,
|
||||
cs35l56_shared_test_onchip_spkid_pull_gen_params),
|
||||
KUNIT_CASE_PARAM(cs35l56_shared_test_stash_onchip_spkid_pins,
|
||||
cs35l56_shared_test_onchip_spkid_pull_gen_params),
|
||||
KUNIT_CASE(cs35l56_shared_test_stash_onchip_spkid_pins_reject_invalid),
|
||||
KUNIT_CASE(cs35l56_shared_test_onchip_speaker_id_not_defined),
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct kunit_suite cs35l56_shared_test_suite_L56_B0_sdw = {
|
||||
.name = "snd-soc-cs35l56-shared-test_L56_B0_sdw",
|
||||
.init = cs35l56_shared_test_case_regmap_init_L56_B0_sdw,
|
||||
.test_cases = cs35l56_shared_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs35l56_shared_test_suite_L56_B2_sdw = {
|
||||
.name = "snd-soc-cs35l56-shared-test_L56_B2_sdw",
|
||||
.init = cs35l56_shared_test_case_regmap_init_L56_B2_sdw,
|
||||
.test_cases = cs35l56_shared_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs35l56_shared_test_suite_L63_A1_sdw = {
|
||||
.name = "snd-soc-cs35l56-shared-test_L63_A1_sdw",
|
||||
.init = cs35l56_shared_test_case_regmap_init_L63_A1_sdw,
|
||||
.test_cases = cs35l56_shared_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs35l56_shared_test_suite_L56_B0_spi = {
|
||||
.name = "snd-soc-cs35l56-shared-test_L56_B0_spi",
|
||||
.init = cs35l56_shared_test_case_regmap_init_L56_B0_spi,
|
||||
.test_cases = cs35l56_shared_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs35l56_shared_test_suite_L56_B2_spi = {
|
||||
.name = "snd-soc-cs35l56-shared-test_L56_B2_spi",
|
||||
.init = cs35l56_shared_test_case_regmap_init_L56_B2_spi,
|
||||
.test_cases = cs35l56_shared_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs35l56_shared_test_suite_L56_B0_i2c = {
|
||||
.name = "snd-soc-cs35l56-shared-test_L56_B0_i2c",
|
||||
.init = cs35l56_shared_test_case_regmap_init_L56_B0_i2c,
|
||||
.test_cases = cs35l56_shared_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs35l56_shared_test_suite_L56_B2_i2c = {
|
||||
.name = "snd-soc-cs35l56-shared-test_L56_B2_i2c",
|
||||
.init = cs35l56_shared_test_case_regmap_init_L56_B2_i2c,
|
||||
.test_cases = cs35l56_shared_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suites(
|
||||
&cs35l56_shared_test_suite_L56_B0_sdw,
|
||||
&cs35l56_shared_test_suite_L56_B2_sdw,
|
||||
&cs35l56_shared_test_suite_L63_A1_sdw,
|
||||
|
||||
&cs35l56_shared_test_suite_L56_B0_spi,
|
||||
&cs35l56_shared_test_suite_L56_B2_spi,
|
||||
|
||||
&cs35l56_shared_test_suite_L56_B0_i2c,
|
||||
&cs35l56_shared_test_suite_L56_B2_i2c,
|
||||
);
|
||||
|
||||
MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
|
||||
MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
|
||||
MODULE_DESCRIPTION("KUnit test for cs35l56-shared module");
|
||||
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -5,13 +5,16 @@
|
|||
// Copyright (C) 2023 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <kunit/static_stub.h>
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/firmware/cirrus/wmfw.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
|
@ -182,6 +185,8 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
|
|||
case CS35L56_OTP_MEM_53:
|
||||
case CS35L56_OTP_MEM_54:
|
||||
case CS35L56_OTP_MEM_55:
|
||||
case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
|
||||
case CS35L56_UPDATE_REGS:
|
||||
case CS35L56_ASP1_ENABLES1:
|
||||
case CS35L56_ASP1_CONTROL1:
|
||||
case CS35L56_ASP1_CONTROL2:
|
||||
|
|
@ -213,6 +218,7 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
|
|||
case CS35L56_IRQ1_MASK_8:
|
||||
case CS35L56_IRQ1_MASK_18:
|
||||
case CS35L56_IRQ1_MASK_20:
|
||||
case CS35L56_GPIO_STATUS1 ... CS35L56_GPIO13_CTRL1:
|
||||
case CS35L56_MIXER_NGATE_CH1_CFG:
|
||||
case CS35L56_MIXER_NGATE_CH2_CFG:
|
||||
case CS35L56_DSP_VIRTUAL1_MBOX_1:
|
||||
|
|
@ -262,6 +268,8 @@ static bool cs35l56_common_volatile_reg(unsigned int reg)
|
|||
case CS35L56_GLOBAL_ENABLES: /* owned by firmware */
|
||||
case CS35L56_BLOCK_ENABLES: /* owned by firmware */
|
||||
case CS35L56_BLOCK_ENABLES2: /* owned by firmware */
|
||||
case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
|
||||
case CS35L56_UPDATE_REGS:
|
||||
case CS35L56_REFCLK_INPUT: /* owned by firmware */
|
||||
case CS35L56_GLOBAL_SAMPLE_RATE: /* owned by firmware */
|
||||
case CS35L56_DACPCM1_INPUT: /* owned by firmware */
|
||||
|
|
@ -272,6 +280,7 @@ static bool cs35l56_common_volatile_reg(unsigned int reg)
|
|||
case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
|
||||
case CS35L56_IRQ1_EINT_18:
|
||||
case CS35L56_IRQ1_EINT_20:
|
||||
case CS35L56_GPIO_STATUS1 ... CS35L56_GPIO13_CTRL1:
|
||||
case CS35L56_MIXER_NGATE_CH1_CFG:
|
||||
case CS35L56_MIXER_NGATE_CH2_CFG:
|
||||
case CS35L56_DSP_VIRTUAL1_MBOX_1:
|
||||
|
|
@ -1552,6 +1561,169 @@ int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base)
|
|||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_get_speaker_id, "SND_SOC_CS35L56_SHARED");
|
||||
|
||||
int cs35l56_check_and_save_onchip_spkid_gpios(struct cs35l56_base *cs35l56_base,
|
||||
const u32 *gpios, int num_gpios,
|
||||
const u32 *pulls, int num_pulls)
|
||||
{
|
||||
int max_gpio;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if ((num_gpios > ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios)) ||
|
||||
(num_pulls > ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls)))
|
||||
return -EOVERFLOW;
|
||||
|
||||
switch (cs35l56_base->type) {
|
||||
case 0x54:
|
||||
case 0x56:
|
||||
case 0x57:
|
||||
max_gpio = CS35L56_MAX_GPIO;
|
||||
break;
|
||||
default:
|
||||
max_gpio = CS35L63_MAX_GPIO;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_gpios; i++) {
|
||||
if (gpios[i] < 1 || gpios[i] > max_gpio) {
|
||||
dev_err(cs35l56_base->dev, "Invalid spkid GPIO %d\n", gpios[i]);
|
||||
/* Keep going so we log all bad values */
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
/* Change to zero-based */
|
||||
cs35l56_base->onchip_spkid_gpios[i] = gpios[i] - 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_pulls; i++) {
|
||||
switch (pulls[i]) {
|
||||
case 0:
|
||||
cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_NONE;
|
||||
break;
|
||||
case 1:
|
||||
cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_UP;
|
||||
break;
|
||||
case 2:
|
||||
cs35l56_base->onchip_spkid_pulls[i] = CS35L56_PAD_PULL_DOWN;
|
||||
break;
|
||||
default:
|
||||
dev_err(cs35l56_base->dev, "Invalid spkid pull %d\n", pulls[i]);
|
||||
/* Keep going so we log all bad values */
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cs35l56_base->num_onchip_spkid_gpios = num_gpios;
|
||||
cs35l56_base->num_onchip_spkid_pulls = num_pulls;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_check_and_save_onchip_spkid_gpios, "SND_SOC_CS35L56_SHARED");
|
||||
|
||||
/* Caller must pm_runtime resume before calling this function */
|
||||
int cs35l56_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base)
|
||||
{
|
||||
struct regmap *regmap = cs35l56_base->regmap;
|
||||
unsigned int addr_offset, val;
|
||||
int num_gpios, num_pulls;
|
||||
int i, ret;
|
||||
|
||||
KUNIT_STATIC_STUB_REDIRECT(cs35l56_configure_onchip_spkid_pads, cs35l56_base);
|
||||
|
||||
if (cs35l56_base->num_onchip_spkid_gpios == 0)
|
||||
return 0;
|
||||
|
||||
num_gpios = min(cs35l56_base->num_onchip_spkid_gpios,
|
||||
ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
|
||||
num_pulls = min(cs35l56_base->num_onchip_spkid_pulls,
|
||||
ARRAY_SIZE(cs35l56_base->onchip_spkid_pulls));
|
||||
|
||||
for (i = 0; i < num_gpios; i++) {
|
||||
addr_offset = cs35l56_base->onchip_spkid_gpios[i] * sizeof(u32);
|
||||
|
||||
/* Set unspecified pulls to NONE */
|
||||
if (i < num_pulls) {
|
||||
val = FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK,
|
||||
cs35l56_base->onchip_spkid_pulls[i]);
|
||||
} else {
|
||||
val = FIELD_PREP(CS35L56_PAD_GPIO_PULL_MASK, CS35L56_PAD_PULL_NONE);
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(regmap, CS35L56_SYNC_GPIO1_CFG + addr_offset,
|
||||
CS35L56_PAD_GPIO_PULL_MASK | CS35L56_PAD_GPIO_IE,
|
||||
val | CS35L56_PAD_GPIO_IE);
|
||||
if (ret) {
|
||||
dev_err(cs35l56_base->dev, "GPIO%d set pad fail: %d\n",
|
||||
cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_write(regmap, CS35L56_UPDATE_REGS, CS35L56_UPDT_GPIO_PRES);
|
||||
if (ret) {
|
||||
dev_err(cs35l56_base->dev, "UPDT_GPIO_PRES failed:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(CS35L56_PAD_PULL_SETTLE_US, CS35L56_PAD_PULL_SETTLE_US * 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_configure_onchip_spkid_pads, "SND_SOC_CS35L56_SHARED");
|
||||
|
||||
/* Caller must pm_runtime resume before calling this function */
|
||||
int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base)
|
||||
{
|
||||
struct regmap *regmap = cs35l56_base->regmap;
|
||||
unsigned int addr_offset, val;
|
||||
int num_gpios;
|
||||
int speaker_id = 0;
|
||||
int i, ret;
|
||||
|
||||
KUNIT_STATIC_STUB_REDIRECT(cs35l56_read_onchip_spkid, cs35l56_base);
|
||||
|
||||
if (cs35l56_base->num_onchip_spkid_gpios == 0)
|
||||
return -ENOENT;
|
||||
|
||||
num_gpios = min(cs35l56_base->num_onchip_spkid_gpios,
|
||||
ARRAY_SIZE(cs35l56_base->onchip_spkid_gpios));
|
||||
|
||||
for (i = 0; i < num_gpios; i++) {
|
||||
addr_offset = cs35l56_base->onchip_spkid_gpios[i] * sizeof(u32);
|
||||
|
||||
ret = regmap_update_bits(regmap, CS35L56_GPIO1_CTRL1 + addr_offset,
|
||||
CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_MASK,
|
||||
CS35L56_GPIO_DIR_MASK | CS35L56_GPIO_FN_GPIO);
|
||||
if (ret) {
|
||||
dev_err(cs35l56_base->dev, "GPIO%u set func fail: %d\n",
|
||||
cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_read(regmap, CS35L56_GPIO_STATUS1, &val);
|
||||
if (ret) {
|
||||
dev_err(cs35l56_base->dev, "GPIO%d status read failed: %d\n",
|
||||
cs35l56_base->onchip_spkid_gpios[i] + 1, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_gpios; i++) {
|
||||
speaker_id <<= 1;
|
||||
|
||||
if (val & BIT(cs35l56_base->onchip_spkid_gpios[i]))
|
||||
speaker_id |= 1;
|
||||
}
|
||||
|
||||
dev_dbg(cs35l56_base->dev, "Onchip GPIO Speaker ID = %d\n", speaker_id);
|
||||
|
||||
return speaker_id;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_read_onchip_spkid, "SND_SOC_CS35L56_SHARED");
|
||||
|
||||
static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = {
|
||||
[0x0C] = 128000,
|
||||
[0x0F] = 256000,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/seq_buf.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <sound/cs35l56.h>
|
||||
#include <sound/cs-amp-lib.h>
|
||||
|
|
@ -23,16 +25,46 @@
|
|||
KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
|
||||
struct faux_device *)
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_node_group_wrapper,
|
||||
software_node_unregister_node_group,
|
||||
const struct software_node * const *)
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_wrapper,
|
||||
software_node_unregister,
|
||||
const struct software_node *)
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper,
|
||||
device_remove_software_node,
|
||||
struct device *)
|
||||
|
||||
struct cs35l56_test_priv {
|
||||
struct faux_device *amp_dev;
|
||||
struct cs35l56_private *cs35l56_priv;
|
||||
|
||||
const char *ssidexv2;
|
||||
|
||||
bool read_onchip_spkid_called;
|
||||
bool configure_onchip_spkid_pads_called;
|
||||
};
|
||||
|
||||
struct cs35l56_test_param {
|
||||
u8 type;
|
||||
u8 rev;
|
||||
|
||||
s32 spkid_gpios[4];
|
||||
s32 spkid_pulls[4];
|
||||
};
|
||||
|
||||
static const struct software_node cs35l56_test_dev_sw_node =
|
||||
SOFTWARE_NODE("SWD1", NULL, NULL);
|
||||
|
||||
static const struct software_node cs35l56_test_af01_sw_node =
|
||||
SOFTWARE_NODE("AF01", NULL, &cs35l56_test_dev_sw_node);
|
||||
|
||||
static const struct software_node *cs35l56_test_dev_and_af01_node_group[] = {
|
||||
&cs35l56_test_dev_sw_node,
|
||||
&cs35l56_test_af01_sw_node,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *cs35l56_test_devm_get_vendor_specific_variant_id_none(struct device *dev,
|
||||
|
|
@ -232,6 +264,190 @@ static void cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw(struct kunit *test)
|
|||
KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5");
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that cs35l56_process_xu_properties() correctly parses the GPIO and
|
||||
* pull values from properties into the arrays in struct cs35l56_base.
|
||||
*
|
||||
* This test creates the node tree:
|
||||
*
|
||||
* Node("SWD1") { // top-level device node
|
||||
* Node("AF01") {
|
||||
* Node("mipi-sdca-function-expansion-subproperties") {
|
||||
* property: "01fa-spk-id-gpios-onchip"
|
||||
* property: 01fa-spk-id-gpios-onchip-pull
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Note that in ACPI "mipi-sdca-function-expansion-subproperties" is
|
||||
* a special _DSD property that points to a Device(EXT0) node but behaves
|
||||
* as an alias of the EXT0 node. The equivalent in software nodes is to
|
||||
* create a Node named "mipi-sdca-function-expansion-subproperties" with
|
||||
* the properties.
|
||||
*
|
||||
*/
|
||||
static void cs35l56_test_parse_xu_onchip_spkid(struct kunit *test)
|
||||
{
|
||||
const struct cs35l56_test_param *param = test->param_value;
|
||||
struct cs35l56_test_priv *priv = test->priv;
|
||||
struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
|
||||
struct software_node *ext0_node;
|
||||
int num_gpios = 0;
|
||||
int num_pulls = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++, num_gpios++) {
|
||||
if (param->spkid_gpios[i] < 0)
|
||||
break;
|
||||
}
|
||||
KUNIT_ASSERT_LE(test, num_gpios, ARRAY_SIZE(cs35l56->base.onchip_spkid_gpios));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++, num_pulls++) {
|
||||
if (param->spkid_pulls[i] < 0)
|
||||
break;
|
||||
}
|
||||
KUNIT_ASSERT_LE(test, num_pulls, ARRAY_SIZE(cs35l56->base.onchip_spkid_pulls));
|
||||
|
||||
const struct property_entry ext0_props[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY_LEN("01fa-spk-id-gpios-onchip",
|
||||
param->spkid_gpios, num_gpios),
|
||||
PROPERTY_ENTRY_U32_ARRAY_LEN("01fa-spk-id-gpios-onchip-pull",
|
||||
param->spkid_pulls, num_pulls),
|
||||
{ }
|
||||
};
|
||||
|
||||
KUNIT_ASSERT_EQ(test,
|
||||
software_node_register_node_group(cs35l56_test_dev_and_af01_node_group),
|
||||
0);
|
||||
KUNIT_ASSERT_EQ(test,
|
||||
kunit_add_action_or_reset(test,
|
||||
software_node_unregister_node_group_wrapper,
|
||||
cs35l56_test_dev_and_af01_node_group),
|
||||
0);
|
||||
|
||||
ext0_node = kunit_kzalloc(test, sizeof(*ext0_node), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ext0_node);
|
||||
*ext0_node = SOFTWARE_NODE("mipi-sdca-function-expansion-subproperties",
|
||||
ext0_props, &cs35l56_test_af01_sw_node);
|
||||
|
||||
KUNIT_ASSERT_EQ(test, software_node_register(ext0_node), 0);
|
||||
KUNIT_ASSERT_EQ(test,
|
||||
kunit_add_action_or_reset(test,
|
||||
software_node_unregister_wrapper,
|
||||
ext0_node),
|
||||
0);
|
||||
|
||||
KUNIT_ASSERT_EQ(test,
|
||||
device_add_software_node(cs35l56->base.dev, &cs35l56_test_dev_sw_node), 0);
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
kunit_add_action_or_reset(test,
|
||||
device_remove_software_node_wrapper,
|
||||
cs35l56->base.dev));
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_process_xu_properties(cs35l56), 0);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs35l56->base.num_onchip_spkid_gpios, num_gpios);
|
||||
KUNIT_EXPECT_EQ(test, cs35l56->base.num_onchip_spkid_pulls, num_pulls);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
|
||||
if (param->spkid_gpios[i] < 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
* cs35l56_process_xu_properties() stores the GPIO numbers
|
||||
* zero-based, which is one less than the value in the property.
|
||||
*/
|
||||
KUNIT_EXPECT_EQ_MSG(test, cs35l56->base.onchip_spkid_gpios[i],
|
||||
param->spkid_gpios[i] - 1,
|
||||
"i=%d", i);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
|
||||
if (param->spkid_pulls[i] < 0)
|
||||
break;
|
||||
|
||||
KUNIT_EXPECT_EQ_MSG(test, cs35l56->base.onchip_spkid_pulls[i],
|
||||
param->spkid_pulls[i], "i=%d", i);
|
||||
}
|
||||
}
|
||||
|
||||
static int cs35l56_test_dummy_read_onchip_spkid(struct cs35l56_base *cs35l56_base)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs35l56_test_priv *priv = test->priv;
|
||||
|
||||
priv->read_onchip_spkid_called = true;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static int cs35l56_test_dummy_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs35l56_test_priv *priv = test->priv;
|
||||
|
||||
priv->configure_onchip_spkid_pads_called = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cs35l56_test_set_fw_name_reads_onchip_spkid(struct kunit *test)
|
||||
{
|
||||
struct cs35l56_test_priv *priv = test->priv;
|
||||
struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
|
||||
|
||||
/* Provide some on-chip GPIOs for spkid */
|
||||
cs35l56->base.onchip_spkid_gpios[0] = 1;
|
||||
cs35l56->base.num_onchip_spkid_gpios = 1;
|
||||
|
||||
cs35l56->speaker_id = -ENOENT;
|
||||
|
||||
kunit_activate_static_stub(test,
|
||||
cs35l56_configure_onchip_spkid_pads,
|
||||
cs35l56_test_dummy_configure_onchip_spkid_pads);
|
||||
kunit_activate_static_stub(test,
|
||||
cs35l56_read_onchip_spkid,
|
||||
cs35l56_test_dummy_read_onchip_spkid);
|
||||
|
||||
priv->configure_onchip_spkid_pads_called = false;
|
||||
priv->read_onchip_spkid_called = false;
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
|
||||
KUNIT_EXPECT_TRUE(test, priv->configure_onchip_spkid_pads_called);
|
||||
KUNIT_EXPECT_TRUE(test, priv->read_onchip_spkid_called);
|
||||
KUNIT_EXPECT_EQ(test, cs35l56->speaker_id,
|
||||
cs35l56_test_dummy_read_onchip_spkid(&cs35l56->base));
|
||||
}
|
||||
|
||||
static void cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios(struct kunit *test)
|
||||
{
|
||||
struct cs35l56_test_priv *priv = test->priv;
|
||||
struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
|
||||
|
||||
/* Provide some on-chip GPIOs for spkid */
|
||||
cs35l56->base.onchip_spkid_gpios[0] = 1;
|
||||
cs35l56->base.num_onchip_spkid_gpios = 1;
|
||||
|
||||
/* Simulate that the driver already got a spkid from somewhere */
|
||||
cs35l56->speaker_id = 15;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
|
||||
KUNIT_EXPECT_EQ(test, cs35l56->speaker_id, 15);
|
||||
}
|
||||
|
||||
static void cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios(struct kunit *test)
|
||||
{
|
||||
struct cs35l56_test_priv *priv = test->priv;
|
||||
struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
|
||||
|
||||
cs35l56->base.num_onchip_spkid_gpios = 0;
|
||||
|
||||
/* Simulate that the driver already got a spkid from somewhere */
|
||||
cs35l56->speaker_id = 15;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
|
||||
KUNIT_EXPECT_EQ(test, cs35l56->speaker_id, 15);
|
||||
}
|
||||
|
||||
static int cs35l56_test_case_init_common(struct kunit *test)
|
||||
{
|
||||
struct cs35l56_test_priv *priv;
|
||||
|
|
@ -263,6 +479,7 @@ static int cs35l56_test_case_init_common(struct kunit *test)
|
|||
cs35l56->component = kunit_kzalloc(test, sizeof(*cs35l56->component), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, cs35l56->component);
|
||||
cs35l56->component->dev = cs35l56->base.dev;
|
||||
snd_soc_component_set_drvdata(cs35l56->component, cs35l56);
|
||||
|
||||
cs35l56->component->card = kunit_kzalloc(test, sizeof(*cs35l56->component->card),
|
||||
GFP_KERNEL);
|
||||
|
|
@ -299,6 +516,50 @@ static int cs35l56_test_case_init_soundwire(struct kunit *test)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void cs35l56_test_gpio_param_desc(const struct cs35l56_test_param *param, char *desc)
|
||||
{
|
||||
DECLARE_SEQ_BUF(gpios, 1 + (2 * ARRAY_SIZE(param->spkid_gpios)));
|
||||
DECLARE_SEQ_BUF(pulls, 1 + (2 * ARRAY_SIZE(param->spkid_pulls)));
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
|
||||
if (param->spkid_gpios[i] < 0)
|
||||
break;
|
||||
|
||||
seq_buf_printf(&gpios, "%s%d", (i == 0) ? "" : ",", param->spkid_gpios[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
|
||||
if (param->spkid_pulls[i] < 0)
|
||||
break;
|
||||
|
||||
seq_buf_printf(&pulls, "%s%d", (i == 0) ? "" : ",", param->spkid_pulls[i]);
|
||||
}
|
||||
|
||||
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gpios:{%s} pulls:{%s}",
|
||||
seq_buf_str(&gpios), seq_buf_str(&pulls));
|
||||
}
|
||||
|
||||
static const struct cs35l56_test_param cs35l56_test_onchip_spkid_cases[] = {
|
||||
{ .spkid_gpios = { 1, -1 }, .spkid_pulls = { 1, -1 }, },
|
||||
{ .spkid_gpios = { 1, -1 }, .spkid_pulls = { 2, -1 }, },
|
||||
|
||||
{ .spkid_gpios = { 7, -1 }, .spkid_pulls = { 1, -1 }, },
|
||||
{ .spkid_gpios = { 7, -1 }, .spkid_pulls = { 2, -1 }, },
|
||||
|
||||
{ .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 1, 1, -1 }, },
|
||||
{ .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 2, 2, -1 }, },
|
||||
|
||||
{ .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 1, 1, -1 }, },
|
||||
{ .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 2, 2, -1 }, },
|
||||
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 1, 1, 1, -1 }, },
|
||||
{ .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 2, 2, 2, -1 }, },
|
||||
};
|
||||
KUNIT_ARRAY_PARAM(cs35l56_test_onchip_spkid,
|
||||
cs35l56_test_onchip_spkid_cases,
|
||||
cs35l56_test_gpio_param_desc);
|
||||
|
||||
static void cs35l56_test_type_rev_param_desc(const struct cs35l56_test_param *param,
|
||||
char *desc)
|
||||
{
|
||||
|
|
@ -331,6 +592,13 @@ static struct kunit_case cs35l56_test_cases_soundwire[] = {
|
|||
cs35l56_test_type_rev_ex_b0_gen_params),
|
||||
KUNIT_CASE(cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw),
|
||||
|
||||
KUNIT_CASE_PARAM(cs35l56_test_parse_xu_onchip_spkid,
|
||||
cs35l56_test_onchip_spkid_gen_params),
|
||||
|
||||
KUNIT_CASE(cs35l56_test_set_fw_name_reads_onchip_spkid),
|
||||
KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios),
|
||||
KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
|
|
@ -339,6 +607,10 @@ static struct kunit_case cs35l56_test_cases_not_soundwire[] = {
|
|||
KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_i2cspi,
|
||||
cs35l56_test_type_rev_all_gen_params),
|
||||
|
||||
KUNIT_CASE(cs35l56_test_set_fw_name_reads_onchip_spkid),
|
||||
KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios),
|
||||
KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
|
|
@ -360,6 +632,7 @@ kunit_test_suites(
|
|||
);
|
||||
|
||||
MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
|
||||
MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
|
||||
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
|
||||
MODULE_DESCRIPTION("KUnit test for Cirrus Logic cs35l56 codec driver");
|
||||
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
|
||||
|
|
|
|||
|
|
@ -1179,15 +1179,28 @@ VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
|
|||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_suffix);
|
||||
|
||||
static int cs35l56_component_probe(struct snd_soc_component *component)
|
||||
VISIBLE_IF_KUNIT int cs35l56_set_fw_name(struct snd_soc_component *component)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
|
||||
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
|
||||
struct dentry *debugfs_root = component->debugfs_root;
|
||||
unsigned short vendor, device;
|
||||
int ret;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
|
||||
if ((cs35l56->speaker_id < 0) && cs35l56->base.num_onchip_spkid_gpios) {
|
||||
PM_RUNTIME_ACQUIRE(cs35l56->base.dev, pm);
|
||||
ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cs35l56_configure_onchip_spkid_pads(&cs35l56->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cs35l56_read_onchip_spkid(&cs35l56->base);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
cs35l56->speaker_id = ret;
|
||||
}
|
||||
|
||||
if (!cs35l56->dsp.system_name &&
|
||||
(snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) {
|
||||
|
|
@ -1208,6 +1221,19 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_name);
|
||||
|
||||
static int cs35l56_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
|
||||
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
|
||||
struct dentry *debugfs_root = component->debugfs_root;
|
||||
int ret;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
|
||||
|
||||
if (!wait_for_completion_timeout(&cs35l56->init_completion,
|
||||
msecs_to_jiffies(5000))) {
|
||||
dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__);
|
||||
|
|
@ -1219,6 +1245,10 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
|
|||
return -ENOMEM;
|
||||
|
||||
cs35l56->component = component;
|
||||
ret = cs35l56_set_fw_name(component);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cs35l56_set_fw_suffix(cs35l56);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -1532,6 +1562,105 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cs35l56_read_fwnode_u32_array(struct device *dev,
|
||||
struct fwnode_handle *parent_node,
|
||||
const char *prop_name,
|
||||
int max_count,
|
||||
u32 *dest)
|
||||
{
|
||||
int count, ret;
|
||||
|
||||
count = fwnode_property_count_u32(parent_node, prop_name);
|
||||
if ((count == 0) || (count == -EINVAL) || (count == -ENODATA)) {
|
||||
dev_dbg(dev, "%s not found in %s\n", prop_name, fwnode_get_name(parent_node));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count < 0) {
|
||||
dev_err(dev, "Get %s error:%d\n", prop_name, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
if (count > max_count) {
|
||||
dev_err(dev, "%s too many entries (%d)\n", prop_name, count);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
ret = fwnode_property_read_u32_array(parent_node, prop_name, dest, count);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error reading %s: %d\n", prop_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int cs35l56_process_xu_onchip_speaker_id(struct cs35l56_private *cs35l56,
|
||||
struct fwnode_handle *ext_node)
|
||||
{
|
||||
static const char * const gpio_name = "01fa-spk-id-gpios-onchip";
|
||||
static const char * const pull_name = "01fa-spk-id-gpios-onchip-pull";
|
||||
u32 gpios[5], pulls[5];
|
||||
int num_gpios, num_pulls;
|
||||
int ret;
|
||||
|
||||
static_assert(ARRAY_SIZE(gpios) == ARRAY_SIZE(cs35l56->base.onchip_spkid_gpios));
|
||||
static_assert(ARRAY_SIZE(pulls) == ARRAY_SIZE(cs35l56->base.onchip_spkid_pulls));
|
||||
|
||||
num_gpios = cs35l56_read_fwnode_u32_array(cs35l56->base.dev, ext_node, gpio_name,
|
||||
ARRAY_SIZE(gpios), gpios);
|
||||
if (num_gpios < 1)
|
||||
return num_gpios;
|
||||
|
||||
num_pulls = cs35l56_read_fwnode_u32_array(cs35l56->base.dev, ext_node, pull_name,
|
||||
ARRAY_SIZE(pulls), pulls);
|
||||
if (num_pulls < 0)
|
||||
return num_pulls;
|
||||
|
||||
if (num_pulls != num_gpios) {
|
||||
dev_warn(cs35l56->base.dev, "%s count(%d) != %s count(%d)\n",
|
||||
pull_name, num_pulls, gpio_name, num_gpios);
|
||||
}
|
||||
|
||||
ret = cs35l56_check_and_save_onchip_spkid_gpios(&cs35l56->base,
|
||||
gpios, num_gpios,
|
||||
pulls, num_pulls);
|
||||
if (ret) {
|
||||
return dev_err_probe(cs35l56->base.dev, ret, "Error in %s/%s\n",
|
||||
gpio_name, pull_name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VISIBLE_IF_KUNIT int cs35l56_process_xu_properties(struct cs35l56_private *cs35l56)
|
||||
{
|
||||
struct fwnode_handle *ext_node = NULL;
|
||||
struct fwnode_handle *link;
|
||||
int ret;
|
||||
|
||||
if (!cs35l56->sdw_peripheral)
|
||||
return 0;
|
||||
|
||||
fwnode_for_each_child_node(dev_fwnode(cs35l56->base.dev), link) {
|
||||
ext_node = fwnode_get_named_child_node(link,
|
||||
"mipi-sdca-function-expansion-subproperties");
|
||||
if (ext_node) {
|
||||
fwnode_handle_put(link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ext_node)
|
||||
return 0;
|
||||
|
||||
ret = cs35l56_process_xu_onchip_speaker_id(cs35l56, ext_node);
|
||||
fwnode_handle_put(ext_node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(cs35l56_process_xu_properties);
|
||||
|
||||
static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56)
|
||||
{
|
||||
struct device *dev = cs35l56->base.dev;
|
||||
|
|
@ -1712,6 +1841,10 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
|
|||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
ret = cs35l56_process_xu_properties(cs35l56);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = cs35l56_dsp_init(cs35l56);
|
||||
if (ret < 0) {
|
||||
dev_err_probe(cs35l56->base.dev, ret, "DSP init failed\n");
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ void cs35l56_remove(struct cs35l56_private *cs35l56);
|
|||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56);
|
||||
int cs35l56_set_fw_name(struct snd_soc_component *component);
|
||||
int cs35l56_process_xu_properties(struct cs35l56_private *cs35l56);
|
||||
#endif
|
||||
|
||||
#endif /* ifndef CS35L56_H */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user