1 // SPDX-License-Identifier: GPL-2.0-only 2 // 3 // KUnit test for the Cirrus side-codec library. 4 // 5 // Copyright (C) 2023 Cirrus Logic, Inc. and 6 // Cirrus Logic International Semiconductor Ltd. 7 8 #include <kunit/platform_device.h> 9 #include <kunit/resource.h> 10 #include <kunit/test.h> 11 #include <linux/device.h> 12 #include <linux/device/faux.h> 13 #include <linux/gpio/driver.h> 14 #include <linux/module.h> 15 #include <linux/platform_device.h> 16 17 #include "cirrus_scodec.h" 18 19 KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy, 20 struct faux_device *) 21 KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper, 22 device_remove_software_node, 23 struct device *) 24 25 struct cirrus_scodec_test_gpio { 26 unsigned int pin_state; 27 struct gpio_chip chip; 28 }; 29 30 struct cirrus_scodec_test_priv { 31 struct faux_device *amp_dev; 32 struct platform_device *gpio_pdev; 33 struct cirrus_scodec_test_gpio *gpio_priv; 34 }; 35 36 static int cirrus_scodec_test_gpio_get_direction(struct gpio_chip *chip, 37 unsigned int offset) 38 { 39 return GPIO_LINE_DIRECTION_IN; 40 } 41 42 static int cirrus_scodec_test_gpio_direction_in(struct gpio_chip *chip, 43 unsigned int offset) 44 { 45 return 0; 46 } 47 48 static int cirrus_scodec_test_gpio_get(struct gpio_chip *chip, unsigned int offset) 49 { 50 struct cirrus_scodec_test_gpio *gpio_priv = gpiochip_get_data(chip); 51 52 return !!(gpio_priv->pin_state & BIT(offset)); 53 } 54 55 static int cirrus_scodec_test_gpio_direction_out(struct gpio_chip *chip, 56 unsigned int offset, int value) 57 { 58 return -EOPNOTSUPP; 59 } 60 61 static int cirrus_scodec_test_gpio_set(struct gpio_chip *chip, 62 unsigned int offset, int value) 63 { 64 return -EOPNOTSUPP; 65 } 66 67 static int cirrus_scodec_test_gpio_set_config(struct gpio_chip *gc, 68 unsigned int offset, 69 unsigned long config) 70 { 71 switch (pinconf_to_config_param(config)) { 72 case PIN_CONFIG_LEVEL: 73 case PIN_CONFIG_OUTPUT_ENABLE: 74 return -EOPNOTSUPP; 75 default: 76 return 0; 77 } 78 } 79 80 static const struct gpio_chip cirrus_scodec_test_gpio_chip = { 81 .label = "cirrus_scodec_test_gpio", 82 .owner = THIS_MODULE, 83 .request = gpiochip_generic_request, 84 .free = gpiochip_generic_free, 85 .get_direction = cirrus_scodec_test_gpio_get_direction, 86 .direction_input = cirrus_scodec_test_gpio_direction_in, 87 .get = cirrus_scodec_test_gpio_get, 88 .direction_output = cirrus_scodec_test_gpio_direction_out, 89 .set = cirrus_scodec_test_gpio_set, 90 .set_config = cirrus_scodec_test_gpio_set_config, 91 .base = -1, 92 .ngpio = 32, 93 }; 94 95 static int cirrus_scodec_test_gpio_probe(struct platform_device *pdev) 96 { 97 struct cirrus_scodec_test_gpio *gpio_priv; 98 int ret; 99 100 gpio_priv = devm_kzalloc(&pdev->dev, sizeof(*gpio_priv), GFP_KERNEL); 101 if (!gpio_priv) 102 return -ENOMEM; 103 104 /* GPIO core modifies our struct gpio_chip so use a copy */ 105 gpio_priv->chip = cirrus_scodec_test_gpio_chip; 106 gpio_priv->chip.parent = &pdev->dev; 107 ret = devm_gpiochip_add_data(&pdev->dev, &gpio_priv->chip, gpio_priv); 108 if (ret) 109 return dev_err_probe(&pdev->dev, ret, "Failed to add gpiochip\n"); 110 111 dev_set_drvdata(&pdev->dev, gpio_priv); 112 113 return 0; 114 } 115 116 static struct platform_driver cirrus_scodec_test_gpio_driver = { 117 .driver.name = "cirrus_scodec_test_gpio_drv", 118 .driver.owner = THIS_MODULE, 119 .probe = cirrus_scodec_test_gpio_probe, 120 }; 121 122 /* software_node referencing the gpio driver */ 123 static const struct software_node cirrus_scodec_test_gpio_swnode = { 124 .name = "cirrus_scodec_test_gpio", 125 }; 126 127 static void cirrus_scodec_test_create_gpio(struct kunit *test) 128 { 129 struct cirrus_scodec_test_priv *priv = test->priv; 130 131 KUNIT_ASSERT_EQ(test, 0, 132 kunit_platform_driver_register(test, &cirrus_scodec_test_gpio_driver)); 133 134 priv->gpio_pdev = kunit_platform_device_alloc(test, 135 cirrus_scodec_test_gpio_driver.driver.name, 136 PLATFORM_DEVID_NONE); 137 KUNIT_ASSERT_NOT_NULL(test, priv->gpio_pdev); 138 139 KUNIT_ASSERT_EQ(test, 0, device_add_software_node(&priv->gpio_pdev->dev, 140 &cirrus_scodec_test_gpio_swnode)); 141 KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, 142 device_remove_software_node_wrapper, 143 &priv->gpio_pdev->dev)); 144 145 KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, priv->gpio_pdev)); 146 147 priv->gpio_priv = dev_get_drvdata(&priv->gpio_pdev->dev); 148 KUNIT_ASSERT_NOT_NULL(test, priv->gpio_priv); 149 } 150 151 static void cirrus_scodec_test_set_gpio_ref_arg(struct software_node_ref_args *arg, 152 int gpio_num) 153 { 154 struct software_node_ref_args template = 155 SOFTWARE_NODE_REFERENCE(&cirrus_scodec_test_gpio_swnode, gpio_num, 0); 156 157 *arg = template; 158 } 159 160 static int cirrus_scodec_test_set_spkid_swnode(struct kunit *test, 161 struct device *dev, 162 struct software_node_ref_args *args, 163 int num_args) 164 { 165 const struct property_entry props_template[] = { 166 PROPERTY_ENTRY_REF_ARRAY_LEN("spk-id-gpios", args, num_args), 167 { } 168 }; 169 struct property_entry *props; 170 struct software_node *node; 171 172 node = kunit_kzalloc(test, sizeof(*node), GFP_KERNEL); 173 if (!node) 174 return -ENOMEM; 175 176 props = kunit_kzalloc(test, sizeof(props_template), GFP_KERNEL); 177 if (!props) 178 return -ENOMEM; 179 180 memcpy(props, props_template, sizeof(props_template)); 181 node->properties = props; 182 183 return device_add_software_node(dev, node); 184 } 185 186 struct cirrus_scodec_test_spkid_param { 187 int num_amps; 188 int gpios_per_amp; 189 int num_amps_sharing; 190 }; 191 192 static void cirrus_scodec_test_spkid_parse(struct kunit *test) 193 { 194 struct cirrus_scodec_test_priv *priv = test->priv; 195 const struct cirrus_scodec_test_spkid_param *param = test->param_value; 196 int num_spk_id_refs = param->num_amps * param->gpios_per_amp; 197 struct software_node_ref_args *refs; 198 struct device *dev = &priv->amp_dev->dev; 199 unsigned int v; 200 int i, ret; 201 202 refs = kunit_kcalloc(test, num_spk_id_refs, sizeof(*refs), GFP_KERNEL); 203 KUNIT_ASSERT_NOT_NULL(test, refs); 204 205 for (i = 0, v = 0; i < num_spk_id_refs; ) { 206 cirrus_scodec_test_set_gpio_ref_arg(&refs[i++], v++); 207 208 /* 209 * If amps are sharing GPIOs repeat the last set of 210 * GPIOs until we've done that number of amps. 211 * We have done all GPIOs for an amp when i is a multiple 212 * of gpios_per_amp. 213 * We have done all amps sharing the same GPIOs when i is 214 * a multiple of (gpios_per_amp * num_amps_sharing). 215 */ 216 if (!(i % param->gpios_per_amp) && 217 (i % (param->gpios_per_amp * param->num_amps_sharing))) 218 v -= param->gpios_per_amp; 219 } 220 221 ret = cirrus_scodec_test_set_spkid_swnode(test, dev, refs, num_spk_id_refs); 222 KUNIT_EXPECT_EQ_MSG(test, ret, 0, "Failed to add swnode\n"); 223 224 for (i = 0; i < param->num_amps; ++i) { 225 for (v = 0; v < (1 << param->gpios_per_amp); ++v) { 226 /* Set only the GPIO bits used by this amp */ 227 priv->gpio_priv->pin_state = 228 v << (param->gpios_per_amp * (i / param->num_amps_sharing)); 229 230 ret = cirrus_scodec_get_speaker_id(dev, i, param->num_amps, -1); 231 KUNIT_EXPECT_EQ_MSG(test, ret, v, 232 "get_speaker_id failed amp:%d pin_state:%#x\n", 233 i, priv->gpio_priv->pin_state); 234 } 235 } 236 } 237 238 static void cirrus_scodec_test_no_spkid(struct kunit *test) 239 { 240 struct cirrus_scodec_test_priv *priv = test->priv; 241 struct device *dev = &priv->amp_dev->dev; 242 int ret; 243 244 ret = cirrus_scodec_get_speaker_id(dev, 0, 4, -1); 245 KUNIT_EXPECT_EQ(test, ret, -ENOENT); 246 } 247 248 static int cirrus_scodec_test_case_init(struct kunit *test) 249 { 250 struct cirrus_scodec_test_priv *priv; 251 252 priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); 253 if (!priv) 254 return -ENOMEM; 255 256 test->priv = priv; 257 258 /* Create dummy GPIO */ 259 cirrus_scodec_test_create_gpio(test); 260 261 /* Create dummy amp driver dev */ 262 priv->amp_dev = faux_device_create("cirrus_scodec_test_amp_drv", NULL, NULL); 263 KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev); 264 KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, 265 faux_device_destroy_wrapper, 266 priv->amp_dev)); 267 268 return 0; 269 } 270 271 static const struct cirrus_scodec_test_spkid_param cirrus_scodec_test_spkid_param_cases[] = { 272 { .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 1 }, 273 { .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 1 }, 274 { .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 1 }, 275 { .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 1 }, 276 { .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 1 }, 277 { .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 1 }, 278 { .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 1 }, 279 { .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 1 }, 280 { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 1 }, 281 { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 1 }, 282 { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 1 }, 283 { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 1 }, 284 285 /* Same GPIO shared by all amps */ 286 { .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 2 }, 287 { .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 2 }, 288 { .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 2 }, 289 { .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 2 }, 290 { .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 3 }, 291 { .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 3 }, 292 { .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 3 }, 293 { .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 3 }, 294 { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 4 }, 295 { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 4 }, 296 { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 4 }, 297 { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 4 }, 298 299 /* Two sets of shared GPIOs */ 300 { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 2 }, 301 { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 2 }, 302 { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 2 }, 303 { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 2 }, 304 }; 305 306 static void cirrus_scodec_test_spkid_param_desc(const struct cirrus_scodec_test_spkid_param *param, 307 char *desc) 308 { 309 snprintf(desc, KUNIT_PARAM_DESC_SIZE, "amps:%d gpios_per_amp:%d num_amps_sharing:%d", 310 param->num_amps, param->gpios_per_amp, param->num_amps_sharing); 311 } 312 313 KUNIT_ARRAY_PARAM(cirrus_scodec_test_spkid, cirrus_scodec_test_spkid_param_cases, 314 cirrus_scodec_test_spkid_param_desc); 315 316 static struct kunit_case cirrus_scodec_test_cases[] = { 317 KUNIT_CASE_PARAM(cirrus_scodec_test_spkid_parse, cirrus_scodec_test_spkid_gen_params), 318 KUNIT_CASE(cirrus_scodec_test_no_spkid), 319 { } /* terminator */ 320 }; 321 322 static struct kunit_suite cirrus_scodec_test_suite = { 323 .name = "snd-hda-cirrus-scodec-test", 324 .init = cirrus_scodec_test_case_init, 325 .test_cases = cirrus_scodec_test_cases, 326 }; 327 328 kunit_test_suite(cirrus_scodec_test_suite); 329 330 MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC"); 331 MODULE_DESCRIPTION("KUnit test for the Cirrus side-codec library"); 332 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); 333 MODULE_LICENSE("GPL"); 334