| // SPDX-License-Identifier: GPL-2.0-only | 
 | // Copyright (C) 2022 Cirrus Logic, Inc. and | 
 | //                    Cirrus Logic International Semiconductor Ltd. | 
 |  | 
 | #include <kunit/test.h> | 
 | #include <linux/module.h> | 
 | #include <sound/pcm.h> | 
 | #include <sound/pcm_params.h> | 
 | #include <sound/soc.h> | 
 | #include <uapi/sound/asound.h> | 
 |  | 
 | static const struct { | 
 | 	u32 rate; | 
 | 	snd_pcm_format_t fmt; | 
 | 	u8 channels; | 
 | 	u8 tdm_width; | 
 | 	u8 tdm_slots; | 
 | 	u8 slot_multiple; | 
 | 	u32 bclk; | 
 | } tdm_params_to_bclk_cases[] = { | 
 | 	/* rate		fmt	   channels tdm_width tdm_slots slot_multiple bclk */ | 
 |  | 
 | 	/* From params only */ | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 1,	0,	0,	0,	  128000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 2,	0,	0,	0,	  256000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S24_LE, 1,	0,	0,	0,	  192000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S24_LE, 2,	0,	0,	0,	  384000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 1,	0,	0,	0,	  256000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 2,	0,	0,	0,	  512000 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S16_LE, 1,	0,	0,	0,	  705600 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S16_LE, 2,	0,	0,	0,	 1411200 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S24_LE, 1,	0,	0,	0,	 1058400 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S24_LE, 2,	0,	0,	0,	 2116800 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S32_LE, 1,	0,	0,	0,	 1411200 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S32_LE, 2,	0,	0,	0,	 2822400 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 1,	0,	0,	0,	 6144000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 2,	0,	0,	0,	12288000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S24_LE, 1,	0,	0,	0,	 9216000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S24_LE, 2,	0,	0,	0,	18432000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 1,	0,	0,	0,	12288000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 2,	0,	0,	0,	24576000 }, | 
 |  | 
 | 	/* I2S from params */ | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 1,	0,	0,	2,	  256000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 2,	0,	0,	2,	  256000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S24_LE, 1,	0,	0,	2,	  384000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S24_LE, 2,	0,	0,	2,	  384000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 1,	0,	0,	2,	  512000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 2,	0,	0,	2,	  512000 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S16_LE, 1,	0,	0,	2,	 1411200 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S16_LE, 2,	0,	0,	2,	 1411200 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S24_LE, 1,	0,	0,	2,	 2116800 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S24_LE, 2,	0,	0,	2,	 2116800 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S32_LE, 1,	0,	0,	2,	 2822400 }, | 
 | 	{  44100,  SNDRV_PCM_FORMAT_S32_LE, 2,	0,	0,	2,	 2822400 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 1,	0,	0,	2,	12288000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 2,	0,	0,	2,	12288000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S24_LE, 1,	0,	0,	2,	18432000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S24_LE, 2,	0,	0,	2,	18432000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 1,	0,	0,	2,	24576000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 2,	0,	0,	2,	24576000 }, | 
 |  | 
 | 	/* Fixed 8-slot TDM, other values from params */ | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 1,	0,	8,	0,	 1024000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 2,	0,	8,	0,	 1024000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 3,	0,	8,	0,	 1024000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 4,	0,	8,	0,	 1024000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 1,	0,	8,	0,	 2048000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 2,	0,	8,	0,	 2048000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 3,	0,	8,	0,	 2048000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 4,	0,	8,	0,	 2048000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 1,	0,	8,	0,	49152000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 2,	0,	8,	0,	49152000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 3,	0,	8,	0,	49152000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 4,	0,	8,	0,	49152000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 1,	0,	8,	0,	98304000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 2,	0,	8,	0,	98304000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 3,	0,	8,	0,	98304000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 4,	0,	8,	0,	98304000 }, | 
 |  | 
 | 	/* Fixed 32-bit TDM, other values from params */ | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 1,	32,	0,	0,	  256000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 2,	32,	0,	0,	  512000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 3,	32,	0,	0,	  768000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 4,	32,	0,	0,	 1024000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 1,	32,	0,	0,	  256000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 2,	32,	0,	0,	  512000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 3,	32,	0,	0,	  768000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S32_LE, 4,	32,	0,	0,	 1024000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 1,	32,	0,	0,	12288000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 2,	32,	0,	0,	24576000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 3,	32,	0,	0,	36864000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S16_LE, 4,	32,	0,	0,	49152000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 1,	32,	0,	0,	12288000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 2,	32,	0,	0,	24576000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 3,	32,	0,	0,	36864000 }, | 
 | 	{ 384000,  SNDRV_PCM_FORMAT_S32_LE, 4,	32,	0,	0,	49152000 }, | 
 |  | 
 | 	/* Fixed 6-slot 24-bit TDM, other values from params */ | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 1,	24,	6,	0,	 1152000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 2,	24,	6,	0,	 1152000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 3,	24,	6,	0,	 1152000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S16_LE, 4,	24,	6,	0,	 1152000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S24_LE, 1,	24,	6,	0,	 1152000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S24_LE, 2,	24,	6,	0,	 1152000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S24_LE, 3,	24,	6,	0,	 1152000 }, | 
 | 	{   8000,  SNDRV_PCM_FORMAT_S24_LE, 4,	24,	6,	0,	 1152000 }, | 
 | 	{ 192000,  SNDRV_PCM_FORMAT_S16_LE, 1,	24,	6,	0,	27648000 }, | 
 | 	{ 192000,  SNDRV_PCM_FORMAT_S16_LE, 2,	24,	6,	0,	27648000 }, | 
 | 	{ 192000,  SNDRV_PCM_FORMAT_S16_LE, 3,	24,	6,	0,	27648000 }, | 
 | 	{ 192000,  SNDRV_PCM_FORMAT_S16_LE, 4,	24,	6,	0,	27648000 }, | 
 | 	{ 192000,  SNDRV_PCM_FORMAT_S24_LE, 1,	24,	6,	0,	27648000 }, | 
 | 	{ 192000,  SNDRV_PCM_FORMAT_S24_LE, 2,	24,	6,	0,	27648000 }, | 
 | 	{ 192000,  SNDRV_PCM_FORMAT_S24_LE, 3,	24,	6,	0,	27648000 }, | 
 | 	{ 192000,  SNDRV_PCM_FORMAT_S24_LE, 4,	24,	6,	0,	27648000 }, | 
 | }; | 
 |  | 
 | static void test_tdm_params_to_bclk_one(struct kunit *test, | 
 | 					unsigned int rate, snd_pcm_format_t fmt, | 
 | 					unsigned int channels, | 
 | 					unsigned int tdm_width, unsigned int tdm_slots, | 
 | 					unsigned int slot_multiple, | 
 | 					unsigned int expected_bclk) | 
 | { | 
 | 	struct snd_pcm_hw_params params; | 
 | 	int got_bclk; | 
 |  | 
 | 	_snd_pcm_hw_params_any(¶ms); | 
 | 	snd_mask_none(hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT)); | 
 | 	hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_RATE)->min = rate; | 
 | 	hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_RATE)->max = rate; | 
 | 	hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS)->min = channels; | 
 | 	hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS)->max = channels; | 
 | 	params_set_format(¶ms, fmt); | 
 |  | 
 | 	got_bclk = snd_soc_tdm_params_to_bclk(¶ms, tdm_width, tdm_slots, slot_multiple); | 
 | 	pr_debug("%s: r=%u sb=%u ch=%u tw=%u ts=%u sm=%u expected=%u got=%d\n", | 
 | 		 __func__, | 
 | 		 rate, params_width(¶ms), channels, tdm_width, tdm_slots, slot_multiple, | 
 | 		 expected_bclk, got_bclk); | 
 | 	KUNIT_ASSERT_EQ(test, expected_bclk, (unsigned int)got_bclk); | 
 | } | 
 |  | 
 | static void test_tdm_params_to_bclk(struct kunit *test) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < ARRAY_SIZE(tdm_params_to_bclk_cases); ++i) { | 
 | 		test_tdm_params_to_bclk_one(test, | 
 | 					    tdm_params_to_bclk_cases[i].rate, | 
 | 					    tdm_params_to_bclk_cases[i].fmt, | 
 | 					    tdm_params_to_bclk_cases[i].channels, | 
 | 					    tdm_params_to_bclk_cases[i].tdm_width, | 
 | 					    tdm_params_to_bclk_cases[i].tdm_slots, | 
 | 					    tdm_params_to_bclk_cases[i].slot_multiple, | 
 | 					    tdm_params_to_bclk_cases[i].bclk); | 
 |  | 
 | 		if (tdm_params_to_bclk_cases[i].slot_multiple > 0) | 
 | 			continue; | 
 |  | 
 | 		/* Slot multiple 1 should have the same effect as multiple 0 */ | 
 | 		test_tdm_params_to_bclk_one(test, | 
 | 					    tdm_params_to_bclk_cases[i].rate, | 
 | 					    tdm_params_to_bclk_cases[i].fmt, | 
 | 					    tdm_params_to_bclk_cases[i].channels, | 
 | 					    tdm_params_to_bclk_cases[i].tdm_width, | 
 | 					    tdm_params_to_bclk_cases[i].tdm_slots, | 
 | 					    1, | 
 | 					    tdm_params_to_bclk_cases[i].bclk); | 
 | 	} | 
 | } | 
 |  | 
 | static void test_snd_soc_params_to_bclk_one(struct kunit *test, | 
 | 					    unsigned int rate, snd_pcm_format_t fmt, | 
 | 					    unsigned int channels, | 
 | 					    unsigned int expected_bclk) | 
 | { | 
 | 	struct snd_pcm_hw_params params; | 
 | 	int got_bclk; | 
 |  | 
 | 	_snd_pcm_hw_params_any(¶ms); | 
 | 	snd_mask_none(hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT)); | 
 | 	hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_RATE)->min = rate; | 
 | 	hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_RATE)->max = rate; | 
 | 	hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS)->min = channels; | 
 | 	hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS)->max = channels; | 
 | 	params_set_format(¶ms, fmt); | 
 |  | 
 | 	got_bclk = snd_soc_params_to_bclk(¶ms); | 
 | 	pr_debug("%s: r=%u sb=%u ch=%u expected=%u got=%d\n", | 
 | 		 __func__, | 
 | 		 rate, params_width(¶ms), channels, expected_bclk, got_bclk); | 
 | 	KUNIT_ASSERT_EQ(test, expected_bclk, (unsigned int)got_bclk); | 
 | } | 
 |  | 
 | static void test_snd_soc_params_to_bclk(struct kunit *test) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < ARRAY_SIZE(tdm_params_to_bclk_cases); ++i) { | 
 | 		/* | 
 | 		 * snd_soc_params_to_bclk() is all the test cases where | 
 | 		 * snd_pcm_hw_params values are not overridden. | 
 | 		 */ | 
 | 		if (tdm_params_to_bclk_cases[i].tdm_width | | 
 | 		    tdm_params_to_bclk_cases[i].tdm_slots | | 
 | 		    tdm_params_to_bclk_cases[i].slot_multiple) | 
 | 			continue; | 
 |  | 
 | 		test_snd_soc_params_to_bclk_one(test, | 
 | 						tdm_params_to_bclk_cases[i].rate, | 
 | 						tdm_params_to_bclk_cases[i].fmt, | 
 | 						tdm_params_to_bclk_cases[i].channels, | 
 | 						tdm_params_to_bclk_cases[i].bclk); | 
 | 	} | 
 | } | 
 |  | 
 | static struct kunit_case soc_utils_test_cases[] = { | 
 | 	KUNIT_CASE(test_tdm_params_to_bclk), | 
 | 	KUNIT_CASE(test_snd_soc_params_to_bclk), | 
 | 	{} | 
 | }; | 
 |  | 
 | static struct kunit_suite soc_utils_test_suite = { | 
 | 	.name = "soc-utils", | 
 | 	.test_cases = soc_utils_test_cases, | 
 | }; | 
 |  | 
 | kunit_test_suites(&soc_utils_test_suite); | 
 |  | 
 | MODULE_DESCRIPTION("ASoC soc-utils kunit test"); | 
 | MODULE_LICENSE("GPL"); |