Linux 6.7-rc7
[linux-modified.git] / sound / pci / hda / cirrus_scodec_test.c
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_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
370 MODULE_LICENSE("GPL");