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