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_OUTPUT: 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 ret = devm_gpiochip_add_data(&pdev->dev, &gpio_priv->chip, gpio_priv); 107 if (ret) 108 return dev_err_probe(&pdev->dev, ret, "Failed to add gpiochip\n"); 109 110 dev_set_drvdata(&pdev->dev, gpio_priv); 111 112 return 0; 113 } 114 115 static struct platform_driver cirrus_scodec_test_gpio_driver = { 116 .driver.name = "cirrus_scodec_test_gpio_drv", 117 .driver.owner = THIS_MODULE, 118 .probe = cirrus_scodec_test_gpio_probe, 119 }; 120 121 /* software_node referencing the gpio driver */ 122 static const struct software_node cirrus_scodec_test_gpio_swnode = { 123 .name = "cirrus_scodec_test_gpio", 124 }; 125 126 static void cirrus_scodec_test_create_gpio(struct kunit *test) 127 { 128 struct cirrus_scodec_test_priv *priv = test->priv; 129 130 KUNIT_ASSERT_EQ(test, 0, 131 kunit_platform_driver_register(test, &cirrus_scodec_test_gpio_driver)); 132 133 priv->gpio_pdev = kunit_platform_device_alloc(test, 134 cirrus_scodec_test_gpio_driver.driver.name, 135 PLATFORM_DEVID_NONE); 136 KUNIT_ASSERT_NOT_NULL(test, priv->gpio_pdev); 137 138 KUNIT_ASSERT_EQ(test, 0, device_add_software_node(&priv->gpio_pdev->dev, 139 &cirrus_scodec_test_gpio_swnode)); 140 KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, 141 device_remove_software_node_wrapper, 142 &priv->gpio_pdev->dev)); 143 144 KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, priv->gpio_pdev)); 145 146 priv->gpio_priv = dev_get_drvdata(&priv->gpio_pdev->dev); 147 KUNIT_ASSERT_NOT_NULL(test, priv->gpio_priv); 148 } 149 150 static void cirrus_scodec_test_set_gpio_ref_arg(struct software_node_ref_args *arg, 151 int gpio_num) 152 { 153 struct software_node_ref_args template = 154 SOFTWARE_NODE_REFERENCE(&cirrus_scodec_test_gpio_swnode, gpio_num, 0); 155 156 *arg = template; 157 } 158 159 static int cirrus_scodec_test_set_spkid_swnode(struct kunit *test, 160 struct device *dev, 161 struct software_node_ref_args *args, 162 int num_args) 163 { 164 const struct property_entry props_template[] = { 165 PROPERTY_ENTRY_REF_ARRAY_LEN("spk-id-gpios", args, num_args), 166 { } 167 }; 168 struct property_entry *props; 169 struct software_node *node; 170 171 node = kunit_kzalloc(test, sizeof(*node), GFP_KERNEL); 172 if (!node) 173 return -ENOMEM; 174 175 props = kunit_kzalloc(test, sizeof(props_template), GFP_KERNEL); 176 if (!props) 177 return -ENOMEM; 178 179 memcpy(props, props_template, sizeof(props_template)); 180 node->properties = props; 181 182 return device_add_software_node(dev, node); 183 } 184 185 struct cirrus_scodec_test_spkid_param { 186 int num_amps; 187 int gpios_per_amp; 188 int num_amps_sharing; 189 }; 190 191 static void cirrus_scodec_test_spkid_parse(struct kunit *test) 192 { 193 struct cirrus_scodec_test_priv *priv = test->priv; 194 const struct cirrus_scodec_test_spkid_param *param = test->param_value; 195 int num_spk_id_refs = param->num_amps * param->gpios_per_amp; 196 struct software_node_ref_args *refs; 197 struct device *dev = &priv->amp_dev->dev; 198 unsigned int v; 199 int i, ret; 200 201 refs = kunit_kcalloc(test, num_spk_id_refs, sizeof(*refs), GFP_KERNEL); 202 KUNIT_ASSERT_NOT_NULL(test, refs); 203 204 for (i = 0, v = 0; i < num_spk_id_refs; ) { 205 cirrus_scodec_test_set_gpio_ref_arg(&refs[i++], v++); 206 207 /* 208 * If amps are sharing GPIOs repeat the last set of 209 * GPIOs until we've done that number of amps. 210 * We have done all GPIOs for an amp when i is a multiple 211 * of gpios_per_amp. 212 * We have done all amps sharing the same GPIOs when i is 213 * a multiple of (gpios_per_amp * num_amps_sharing). 214 */ 215 if (!(i % param->gpios_per_amp) && 216 (i % (param->gpios_per_amp * param->num_amps_sharing))) 217 v -= param->gpios_per_amp; 218 } 219 220 ret = cirrus_scodec_test_set_spkid_swnode(test, dev, refs, num_spk_id_refs); 221 KUNIT_EXPECT_EQ_MSG(test, ret, 0, "Failed to add swnode\n"); 222 223 for (i = 0; i < param->num_amps; ++i) { 224 for (v = 0; v < (1 << param->gpios_per_amp); ++v) { 225 /* Set only the GPIO bits used by this amp */ 226 priv->gpio_priv->pin_state = 227 v << (param->gpios_per_amp * (i / param->num_amps_sharing)); 228 229 ret = cirrus_scodec_get_speaker_id(dev, i, param->num_amps, -1); 230 KUNIT_EXPECT_EQ_MSG(test, ret, v, 231 "get_speaker_id failed amp:%d pin_state:%#x\n", 232 i, priv->gpio_priv->pin_state); 233 } 234 } 235 } 236 237 static void cirrus_scodec_test_no_spkid(struct kunit *test) 238 { 239 struct cirrus_scodec_test_priv *priv = test->priv; 240 struct device *dev = &priv->amp_dev->dev; 241 int ret; 242 243 ret = cirrus_scodec_get_speaker_id(dev, 0, 4, -1); 244 KUNIT_EXPECT_EQ(test, ret, -ENOENT); 245 } 246 247 static int cirrus_scodec_test_case_init(struct kunit *test) 248 { 249 struct cirrus_scodec_test_priv *priv; 250 251 priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); 252 if (!priv) 253 return -ENOMEM; 254 255 test->priv = priv; 256 257 /* Create dummy GPIO */ 258 cirrus_scodec_test_create_gpio(test); 259 260 /* Create dummy amp driver dev */ 261 priv->amp_dev = faux_device_create("cirrus_scodec_test_amp_drv", NULL, NULL); 262 KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev); 263 KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, 264 faux_device_destroy_wrapper, 265 priv->amp_dev)); 266 267 return 0; 268 } 269 270 static const struct cirrus_scodec_test_spkid_param cirrus_scodec_test_spkid_param_cases[] = { 271 { .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 1 }, 272 { .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 1 }, 273 { .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 1 }, 274 { .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 1 }, 275 { .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 1 }, 276 { .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 1 }, 277 { .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 1 }, 278 { .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 1 }, 279 { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 1 }, 280 { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 1 }, 281 { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 1 }, 282 { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 1 }, 283 284 /* Same GPIO shared by all amps */ 285 { .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 2 }, 286 { .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 2 }, 287 { .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 2 }, 288 { .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 2 }, 289 { .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 3 }, 290 { .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 3 }, 291 { .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 3 }, 292 { .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 3 }, 293 { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 4 }, 294 { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 4 }, 295 { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 4 }, 296 { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 4 }, 297 298 /* Two sets of shared GPIOs */ 299 { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 2 }, 300 { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 2 }, 301 { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 2 }, 302 { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 2 }, 303 }; 304 305 static void cirrus_scodec_test_spkid_param_desc(const struct cirrus_scodec_test_spkid_param *param, 306 char *desc) 307 { 308 snprintf(desc, KUNIT_PARAM_DESC_SIZE, "amps:%d gpios_per_amp:%d num_amps_sharing:%d", 309 param->num_amps, param->gpios_per_amp, param->num_amps_sharing); 310 } 311 312 KUNIT_ARRAY_PARAM(cirrus_scodec_test_spkid, cirrus_scodec_test_spkid_param_cases, 313 cirrus_scodec_test_spkid_param_desc); 314 315 static struct kunit_case cirrus_scodec_test_cases[] = { 316 KUNIT_CASE_PARAM(cirrus_scodec_test_spkid_parse, cirrus_scodec_test_spkid_gen_params), 317 KUNIT_CASE(cirrus_scodec_test_no_spkid), 318 { } /* terminator */ 319 }; 320 321 static struct kunit_suite cirrus_scodec_test_suite = { 322 .name = "snd-hda-scodec-cs35l56-test", 323 .init = cirrus_scodec_test_case_init, 324 .test_cases = cirrus_scodec_test_cases, 325 }; 326 327 kunit_test_suite(cirrus_scodec_test_suite); 328 329 MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC"); 330 MODULE_DESCRIPTION("KUnit test for the Cirrus side-codec library"); 331 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); 332 MODULE_LICENSE("GPL"); 333