Path: blob/master/sound/hda/codecs/side-codecs/cirrus_scodec_test.c
26481 views
// SPDX-License-Identifier: GPL-2.0-only1//2// KUnit test for the Cirrus side-codec library.3//4// Copyright (C) 2023 Cirrus Logic, Inc. and5// Cirrus Logic International Semiconductor Ltd.67#include <kunit/platform_device.h>8#include <kunit/resource.h>9#include <kunit/test.h>10#include <linux/device.h>11#include <linux/device/faux.h>12#include <linux/gpio/driver.h>13#include <linux/module.h>14#include <linux/platform_device.h>1516#include "cirrus_scodec.h"1718KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,19struct faux_device *)20KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper,21device_remove_software_node,22struct device *)2324struct cirrus_scodec_test_gpio {25unsigned int pin_state;26struct gpio_chip chip;27};2829struct cirrus_scodec_test_priv {30struct faux_device *amp_dev;31struct platform_device *gpio_pdev;32struct cirrus_scodec_test_gpio *gpio_priv;33};3435static int cirrus_scodec_test_gpio_get_direction(struct gpio_chip *chip,36unsigned int offset)37{38return GPIO_LINE_DIRECTION_IN;39}4041static int cirrus_scodec_test_gpio_direction_in(struct gpio_chip *chip,42unsigned int offset)43{44return 0;45}4647static int cirrus_scodec_test_gpio_get(struct gpio_chip *chip, unsigned int offset)48{49struct cirrus_scodec_test_gpio *gpio_priv = gpiochip_get_data(chip);5051return !!(gpio_priv->pin_state & BIT(offset));52}5354static int cirrus_scodec_test_gpio_direction_out(struct gpio_chip *chip,55unsigned int offset, int value)56{57return -EOPNOTSUPP;58}5960static int cirrus_scodec_test_gpio_set(struct gpio_chip *chip,61unsigned int offset, int value)62{63return -EOPNOTSUPP;64}6566static int cirrus_scodec_test_gpio_set_config(struct gpio_chip *gc,67unsigned int offset,68unsigned long config)69{70switch (pinconf_to_config_param(config)) {71case PIN_CONFIG_OUTPUT:72case PIN_CONFIG_OUTPUT_ENABLE:73return -EOPNOTSUPP;74default:75return 0;76}77}7879static const struct gpio_chip cirrus_scodec_test_gpio_chip = {80.label = "cirrus_scodec_test_gpio",81.owner = THIS_MODULE,82.request = gpiochip_generic_request,83.free = gpiochip_generic_free,84.get_direction = cirrus_scodec_test_gpio_get_direction,85.direction_input = cirrus_scodec_test_gpio_direction_in,86.get = cirrus_scodec_test_gpio_get,87.direction_output = cirrus_scodec_test_gpio_direction_out,88.set = cirrus_scodec_test_gpio_set,89.set_config = cirrus_scodec_test_gpio_set_config,90.base = -1,91.ngpio = 32,92};9394static int cirrus_scodec_test_gpio_probe(struct platform_device *pdev)95{96struct cirrus_scodec_test_gpio *gpio_priv;97int ret;9899gpio_priv = devm_kzalloc(&pdev->dev, sizeof(*gpio_priv), GFP_KERNEL);100if (!gpio_priv)101return -ENOMEM;102103/* GPIO core modifies our struct gpio_chip so use a copy */104gpio_priv->chip = cirrus_scodec_test_gpio_chip;105ret = devm_gpiochip_add_data(&pdev->dev, &gpio_priv->chip, gpio_priv);106if (ret)107return dev_err_probe(&pdev->dev, ret, "Failed to add gpiochip\n");108109dev_set_drvdata(&pdev->dev, gpio_priv);110111return 0;112}113114static struct platform_driver cirrus_scodec_test_gpio_driver = {115.driver.name = "cirrus_scodec_test_gpio_drv",116.driver.owner = THIS_MODULE,117.probe = cirrus_scodec_test_gpio_probe,118};119120/* software_node referencing the gpio driver */121static const struct software_node cirrus_scodec_test_gpio_swnode = {122.name = "cirrus_scodec_test_gpio",123};124125static void cirrus_scodec_test_create_gpio(struct kunit *test)126{127struct cirrus_scodec_test_priv *priv = test->priv;128129KUNIT_ASSERT_EQ(test, 0,130kunit_platform_driver_register(test, &cirrus_scodec_test_gpio_driver));131132priv->gpio_pdev = kunit_platform_device_alloc(test,133cirrus_scodec_test_gpio_driver.driver.name,134PLATFORM_DEVID_NONE);135KUNIT_ASSERT_NOT_NULL(test, priv->gpio_pdev);136137KUNIT_ASSERT_EQ(test, 0, device_add_software_node(&priv->gpio_pdev->dev,138&cirrus_scodec_test_gpio_swnode));139KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test,140device_remove_software_node_wrapper,141&priv->gpio_pdev->dev));142143KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, priv->gpio_pdev));144145priv->gpio_priv = dev_get_drvdata(&priv->gpio_pdev->dev);146KUNIT_ASSERT_NOT_NULL(test, priv->gpio_priv);147}148149static void cirrus_scodec_test_set_gpio_ref_arg(struct software_node_ref_args *arg,150int gpio_num)151{152struct software_node_ref_args template =153SOFTWARE_NODE_REFERENCE(&cirrus_scodec_test_gpio_swnode, gpio_num, 0);154155*arg = template;156}157158static int cirrus_scodec_test_set_spkid_swnode(struct kunit *test,159struct device *dev,160struct software_node_ref_args *args,161int num_args)162{163const struct property_entry props_template[] = {164PROPERTY_ENTRY_REF_ARRAY_LEN("spk-id-gpios", args, num_args),165{ }166};167struct property_entry *props;168struct software_node *node;169170node = kunit_kzalloc(test, sizeof(*node), GFP_KERNEL);171if (!node)172return -ENOMEM;173174props = kunit_kzalloc(test, sizeof(props_template), GFP_KERNEL);175if (!props)176return -ENOMEM;177178memcpy(props, props_template, sizeof(props_template));179node->properties = props;180181return device_add_software_node(dev, node);182}183184struct cirrus_scodec_test_spkid_param {185int num_amps;186int gpios_per_amp;187int num_amps_sharing;188};189190static void cirrus_scodec_test_spkid_parse(struct kunit *test)191{192struct cirrus_scodec_test_priv *priv = test->priv;193const struct cirrus_scodec_test_spkid_param *param = test->param_value;194int num_spk_id_refs = param->num_amps * param->gpios_per_amp;195struct software_node_ref_args *refs;196struct device *dev = &priv->amp_dev->dev;197unsigned int v;198int i, ret;199200refs = kunit_kcalloc(test, num_spk_id_refs, sizeof(*refs), GFP_KERNEL);201KUNIT_ASSERT_NOT_NULL(test, refs);202203for (i = 0, v = 0; i < num_spk_id_refs; ) {204cirrus_scodec_test_set_gpio_ref_arg(&refs[i++], v++);205206/*207* If amps are sharing GPIOs repeat the last set of208* GPIOs until we've done that number of amps.209* We have done all GPIOs for an amp when i is a multiple210* of gpios_per_amp.211* We have done all amps sharing the same GPIOs when i is212* a multiple of (gpios_per_amp * num_amps_sharing).213*/214if (!(i % param->gpios_per_amp) &&215(i % (param->gpios_per_amp * param->num_amps_sharing)))216v -= param->gpios_per_amp;217}218219ret = cirrus_scodec_test_set_spkid_swnode(test, dev, refs, num_spk_id_refs);220KUNIT_EXPECT_EQ_MSG(test, ret, 0, "Failed to add swnode\n");221222for (i = 0; i < param->num_amps; ++i) {223for (v = 0; v < (1 << param->gpios_per_amp); ++v) {224/* Set only the GPIO bits used by this amp */225priv->gpio_priv->pin_state =226v << (param->gpios_per_amp * (i / param->num_amps_sharing));227228ret = cirrus_scodec_get_speaker_id(dev, i, param->num_amps, -1);229KUNIT_EXPECT_EQ_MSG(test, ret, v,230"get_speaker_id failed amp:%d pin_state:%#x\n",231i, priv->gpio_priv->pin_state);232}233}234}235236static void cirrus_scodec_test_no_spkid(struct kunit *test)237{238struct cirrus_scodec_test_priv *priv = test->priv;239struct device *dev = &priv->amp_dev->dev;240int ret;241242ret = cirrus_scodec_get_speaker_id(dev, 0, 4, -1);243KUNIT_EXPECT_EQ(test, ret, -ENOENT);244}245246static int cirrus_scodec_test_case_init(struct kunit *test)247{248struct cirrus_scodec_test_priv *priv;249250priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);251if (!priv)252return -ENOMEM;253254test->priv = priv;255256/* Create dummy GPIO */257cirrus_scodec_test_create_gpio(test);258259/* Create dummy amp driver dev */260priv->amp_dev = faux_device_create("cirrus_scodec_test_amp_drv", NULL, NULL);261KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);262KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test,263faux_device_destroy_wrapper,264priv->amp_dev));265266return 0;267}268269static const struct cirrus_scodec_test_spkid_param cirrus_scodec_test_spkid_param_cases[] = {270{ .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 1 },271{ .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 1 },272{ .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 1 },273{ .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 1 },274{ .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 1 },275{ .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 1 },276{ .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 1 },277{ .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 1 },278{ .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 1 },279{ .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 1 },280{ .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 1 },281{ .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 1 },282283/* Same GPIO shared by all amps */284{ .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 2 },285{ .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 2 },286{ .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 2 },287{ .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 2 },288{ .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 3 },289{ .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 3 },290{ .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 3 },291{ .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 3 },292{ .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 4 },293{ .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 4 },294{ .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 4 },295{ .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 4 },296297/* Two sets of shared GPIOs */298{ .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 2 },299{ .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 2 },300{ .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 2 },301{ .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 2 },302};303304static void cirrus_scodec_test_spkid_param_desc(const struct cirrus_scodec_test_spkid_param *param,305char *desc)306{307snprintf(desc, KUNIT_PARAM_DESC_SIZE, "amps:%d gpios_per_amp:%d num_amps_sharing:%d",308param->num_amps, param->gpios_per_amp, param->num_amps_sharing);309}310311KUNIT_ARRAY_PARAM(cirrus_scodec_test_spkid, cirrus_scodec_test_spkid_param_cases,312cirrus_scodec_test_spkid_param_desc);313314static struct kunit_case cirrus_scodec_test_cases[] = {315KUNIT_CASE_PARAM(cirrus_scodec_test_spkid_parse, cirrus_scodec_test_spkid_gen_params),316KUNIT_CASE(cirrus_scodec_test_no_spkid),317{ } /* terminator */318};319320static struct kunit_suite cirrus_scodec_test_suite = {321.name = "snd-hda-scodec-cs35l56-test",322.init = cirrus_scodec_test_case_init,323.test_cases = cirrus_scodec_test_cases,324};325326kunit_test_suite(cirrus_scodec_test_suite);327328MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC");329MODULE_DESCRIPTION("KUnit test for the Cirrus side-codec library");330MODULE_AUTHOR("Richard Fitzgerald <[email protected]>");331MODULE_LICENSE("GPL");332333334