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