Path: blob/master/sound/hda/codecs/side-codecs/cirrus_scodec_test.c
51705 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/resource.h>8#include <kunit/test.h>9#include <linux/device.h>10#include <linux/device/devres.h>11#include <linux/device/faux.h>12#include <linux/gpio/driver.h>13#include <linux/module.h>1415#include "cirrus_scodec.h"1617KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,18struct faux_device *)19KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper,20device_remove_software_node,21struct device *)2223struct cirrus_scodec_test_gpio {24unsigned int pin_state;25struct gpio_chip chip;26};2728struct cirrus_scodec_test_priv {29struct faux_device *amp_dev;30struct faux_device *gpio_dev;31struct cirrus_scodec_test_gpio *gpio_priv;32};3334static int cirrus_scodec_test_gpio_get_direction(struct gpio_chip *chip,35unsigned int offset)36{37return GPIO_LINE_DIRECTION_IN;38}3940static int cirrus_scodec_test_gpio_direction_in(struct gpio_chip *chip,41unsigned int offset)42{43return 0;44}4546static int cirrus_scodec_test_gpio_get(struct gpio_chip *chip, unsigned int offset)47{48struct cirrus_scodec_test_gpio *gpio_priv = gpiochip_get_data(chip);4950return !!(gpio_priv->pin_state & BIT(offset));51}5253static int cirrus_scodec_test_gpio_direction_out(struct gpio_chip *chip,54unsigned int offset, int value)55{56return -EOPNOTSUPP;57}5859static int cirrus_scodec_test_gpio_set(struct gpio_chip *chip,60unsigned int offset, int value)61{62return -EOPNOTSUPP;63}6465static int cirrus_scodec_test_gpio_set_config(struct gpio_chip *gc,66unsigned int offset,67unsigned long config)68{69switch (pinconf_to_config_param(config)) {70case PIN_CONFIG_LEVEL:71case PIN_CONFIG_OUTPUT_ENABLE:72return -EOPNOTSUPP;73default:74return 0;75}76}7778static const struct gpio_chip cirrus_scodec_test_gpio_chip = {79.label = "cirrus_scodec_test_gpio",80.owner = THIS_MODULE,81.request = gpiochip_generic_request,82.free = gpiochip_generic_free,83.get_direction = cirrus_scodec_test_gpio_get_direction,84.direction_input = cirrus_scodec_test_gpio_direction_in,85.get = cirrus_scodec_test_gpio_get,86.direction_output = cirrus_scodec_test_gpio_direction_out,87.set = cirrus_scodec_test_gpio_set,88.set_config = cirrus_scodec_test_gpio_set_config,89.base = -1,90.ngpio = 32,91};9293/* software_node referencing the gpio driver */94static const struct software_node cirrus_scodec_test_gpio_swnode = {95.name = "cirrus_scodec_test_gpio",96};9798static int cirrus_scodec_test_gpio_probe(struct faux_device *fdev)99{100struct cirrus_scodec_test_gpio *gpio_priv;101int ret;102103gpio_priv = devm_kzalloc(&fdev->dev, sizeof(*gpio_priv), GFP_KERNEL);104if (!gpio_priv)105return -ENOMEM;106107ret = device_add_software_node(&fdev->dev, &cirrus_scodec_test_gpio_swnode);108if (ret)109return ret;110111ret = devm_add_action_or_reset(&fdev->dev, device_remove_software_node_wrapper,112&fdev->dev);113if (ret)114return ret;115116/* GPIO core modifies our struct gpio_chip so use a copy */117gpio_priv->chip = cirrus_scodec_test_gpio_chip;118gpio_priv->chip.parent = &fdev->dev;119ret = devm_gpiochip_add_data(&fdev->dev, &gpio_priv->chip, gpio_priv);120if (ret)121return dev_err_probe(&fdev->dev, ret, "Failed to add gpiochip\n");122123dev_set_drvdata(&fdev->dev, gpio_priv);124125return 0;126}127128static const struct faux_device_ops cirrus_scodec_test_gpio_driver_ops = {129.probe = cirrus_scodec_test_gpio_probe,130};131132static void cirrus_scodec_test_create_gpio(struct kunit *test)133{134struct cirrus_scodec_test_priv *priv = test->priv;135136priv->gpio_dev = faux_device_create("cirrus_scodec_test_gpio_drv", NULL,137&cirrus_scodec_test_gpio_driver_ops);138KUNIT_ASSERT_NOT_NULL(test, priv->gpio_dev);139KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test,140faux_device_destroy_wrapper,141priv->gpio_dev));142143priv->gpio_priv = dev_get_drvdata(&priv->gpio_dev->dev);144KUNIT_ASSERT_NOT_NULL(test, priv->gpio_priv);145}146147static void cirrus_scodec_test_set_gpio_ref_arg(struct software_node_ref_args *arg,148int gpio_num)149{150struct software_node_ref_args template =151SOFTWARE_NODE_REFERENCE(&cirrus_scodec_test_gpio_swnode, gpio_num, 0);152153*arg = template;154}155156static int cirrus_scodec_test_set_spkid_swnode(struct kunit *test,157struct device *dev,158struct software_node_ref_args *args,159int num_args)160{161const struct property_entry props_template[] = {162PROPERTY_ENTRY_REF_ARRAY_LEN("spk-id-gpios", args, num_args),163{ }164};165struct property_entry *props;166struct software_node *node;167168node = kunit_kzalloc(test, sizeof(*node), GFP_KERNEL);169if (!node)170return -ENOMEM;171172props = kunit_kzalloc(test, sizeof(props_template), GFP_KERNEL);173if (!props)174return -ENOMEM;175176memcpy(props, props_template, sizeof(props_template));177node->properties = props;178179return device_add_software_node(dev, node);180}181182struct cirrus_scodec_test_spkid_param {183int num_amps;184int gpios_per_amp;185int num_amps_sharing;186};187188static void cirrus_scodec_test_spkid_parse(struct kunit *test)189{190struct cirrus_scodec_test_priv *priv = test->priv;191const struct cirrus_scodec_test_spkid_param *param = test->param_value;192int num_spk_id_refs = param->num_amps * param->gpios_per_amp;193struct software_node_ref_args *refs;194struct device *dev = &priv->amp_dev->dev;195unsigned int v;196int i, ret;197198refs = kunit_kcalloc(test, num_spk_id_refs, sizeof(*refs), GFP_KERNEL);199KUNIT_ASSERT_NOT_NULL(test, refs);200201for (i = 0, v = 0; i < num_spk_id_refs; ) {202cirrus_scodec_test_set_gpio_ref_arg(&refs[i++], v++);203204/*205* If amps are sharing GPIOs repeat the last set of206* GPIOs until we've done that number of amps.207* We have done all GPIOs for an amp when i is a multiple208* of gpios_per_amp.209* We have done all amps sharing the same GPIOs when i is210* a multiple of (gpios_per_amp * num_amps_sharing).211*/212if (!(i % param->gpios_per_amp) &&213(i % (param->gpios_per_amp * param->num_amps_sharing)))214v -= param->gpios_per_amp;215}216217ret = cirrus_scodec_test_set_spkid_swnode(test, dev, refs, num_spk_id_refs);218KUNIT_EXPECT_EQ_MSG(test, ret, 0, "Failed to add swnode\n");219220for (i = 0; i < param->num_amps; ++i) {221for (v = 0; v < (1 << param->gpios_per_amp); ++v) {222/* Set only the GPIO bits used by this amp */223priv->gpio_priv->pin_state =224v << (param->gpios_per_amp * (i / param->num_amps_sharing));225226ret = cirrus_scodec_get_speaker_id(dev, i, param->num_amps, -1);227KUNIT_EXPECT_EQ_MSG(test, ret, v,228"get_speaker_id failed amp:%d pin_state:%#x\n",229i, priv->gpio_priv->pin_state);230}231}232}233234static void cirrus_scodec_test_no_spkid(struct kunit *test)235{236struct cirrus_scodec_test_priv *priv = test->priv;237struct device *dev = &priv->amp_dev->dev;238int ret;239240ret = cirrus_scodec_get_speaker_id(dev, 0, 4, -1);241KUNIT_EXPECT_EQ(test, ret, -ENOENT);242}243244static int cirrus_scodec_test_case_init(struct kunit *test)245{246struct cirrus_scodec_test_priv *priv;247248priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);249if (!priv)250return -ENOMEM;251252test->priv = priv;253254/* Create dummy GPIO */255cirrus_scodec_test_create_gpio(test);256257/* Create dummy amp driver dev */258priv->amp_dev = faux_device_create("cirrus_scodec_test_amp_drv", NULL, NULL);259KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);260KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test,261faux_device_destroy_wrapper,262priv->amp_dev));263264return 0;265}266267static const struct cirrus_scodec_test_spkid_param cirrus_scodec_test_spkid_param_cases[] = {268{ .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 1 },269{ .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 1 },270{ .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 1 },271{ .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 1 },272{ .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 1 },273{ .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 1 },274{ .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 1 },275{ .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 1 },276{ .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 1 },277{ .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 1 },278{ .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 1 },279{ .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 1 },280281/* Same GPIO shared by all amps */282{ .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 2 },283{ .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 2 },284{ .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 2 },285{ .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 2 },286{ .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 3 },287{ .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 3 },288{ .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 3 },289{ .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 3 },290{ .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 4 },291{ .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 4 },292{ .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 4 },293{ .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 4 },294295/* Two sets of shared GPIOs */296{ .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 2 },297{ .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 2 },298{ .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 2 },299{ .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 2 },300};301302static void cirrus_scodec_test_spkid_param_desc(const struct cirrus_scodec_test_spkid_param *param,303char *desc)304{305snprintf(desc, KUNIT_PARAM_DESC_SIZE, "amps:%d gpios_per_amp:%d num_amps_sharing:%d",306param->num_amps, param->gpios_per_amp, param->num_amps_sharing);307}308309KUNIT_ARRAY_PARAM(cirrus_scodec_test_spkid, cirrus_scodec_test_spkid_param_cases,310cirrus_scodec_test_spkid_param_desc);311312static struct kunit_case cirrus_scodec_test_cases[] = {313KUNIT_CASE_PARAM(cirrus_scodec_test_spkid_parse, cirrus_scodec_test_spkid_gen_params),314KUNIT_CASE(cirrus_scodec_test_no_spkid),315{ } /* terminator */316};317318static struct kunit_suite cirrus_scodec_test_suite = {319.name = "snd-hda-cirrus-scodec-test",320.init = cirrus_scodec_test_case_init,321.test_cases = cirrus_scodec_test_cases,322};323324kunit_test_suite(cirrus_scodec_test_suite);325326MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC");327MODULE_DESCRIPTION("KUnit test for the Cirrus side-codec library");328MODULE_AUTHOR("Richard Fitzgerald <[email protected]>");329MODULE_LICENSE("GPL");330331332