diff mbox

[v2,3/3] ASoC: AMD: Enable/Disable auxiliary clock via common clock framework

Message ID 1521447999-24120-3-git-send-email-akshu.agrawal@amd.com (mailing list archive)
State New, archived
Headers show

Commit Message

Akshu Agrawal March 19, 2018, 8:26 a.m. UTC
This enables/disables and sets auxiliary clock at 25Mhz. It uses
common clock framework for proper ref counting. This approach will
save power in comparison to keeping it always On in firmware.

V2: Correcting the pin to OSCOUT1 from OSCOUT2

TEST= aplay -vv <file>
check register to see clock enabled
kill aplay
check register to see clock disabled

Signed-off-by: Akshu Agrawal <akshu.agrawal@amd.com>
---
 sound/soc/amd/acp-da7219-max98357a.c | 133 ++++++++++++++++++++++++++++++++++-
 1 file changed, 131 insertions(+), 2 deletions(-)

Comments

kernel test robot March 20, 2018, 12:32 p.m. UTC | #1
Hi Akshu,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on next-20180309]
[cannot apply to v4.16-rc4 v4.16-rc3 v4.16-rc2 v4.16-rc6]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Akshu-Agrawal/ASoC-AMD-Use-single-dai-for-da7219-playback-and-capture/20180320-032923
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):


sparse warnings: (new ones prefixed by >>)


vim +/acp_clks_hw +52 sound/soc/amd/acp-da7219-max98357a.c

    49	
    50	struct cz_clock {
    51		const char* acp_clks_name;
  > 52		struct clk_hw acp_clks_hw;
    53		struct clk_lookup *acp_clks_lookup;
    54		struct clk *acp_clks;
    55		void __iomem *res_base;
    56	};
    57	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot March 27, 2018, 6:39 p.m. UTC | #2
Hi Akshu,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on next-20180309]
[cannot apply to v4.16-rc4 v4.16-rc3 v4.16-rc2 v4.16-rc7]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Akshu-Agrawal/ASoC-AMD-Use-single-dai-for-da7219-playback-and-capture/20180320-032923
config: i386-allyesconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   sound/soc/amd/acp-da7219-max98357a.c:52:16: error: field 'acp_clks_hw' has incomplete type
     struct clk_hw acp_clks_hw;
                   ^~~~~~~~~~~
   sound/soc/amd/acp-da7219-max98357a.c: In function 'acpd7219_clks_prepare':
>> sound/soc/amd/acp-da7219-max98357a.c:273:95: error: dereferencing pointer to incomplete type 'struct clk_hw'
      container_of(hw, struct cz_clock, acp_clks_hw);
                                                                                                  ^    
   sound/soc/amd/acp-da7219-max98357a.c: At top level:
   sound/soc/amd/acp-da7219-max98357a.c:310:14: error: variable 'acpd7219_clks_ops' has initializer but incomplete type
    const struct clk_ops acpd7219_clks_ops = {
                 ^~~~~~~
   sound/soc/amd/acp-da7219-max98357a.c:311:3: error: 'const struct clk_ops' has no member named 'prepare'
     .prepare = acpd7219_clks_prepare,
      ^~~~~~~
   sound/soc/amd/acp-da7219-max98357a.c:311:13: warning: excess elements in struct initializer
     .prepare = acpd7219_clks_prepare,
                ^~~~~~~~~~~~~~~~~~~~~
   sound/soc/amd/acp-da7219-max98357a.c:311:13: note: (near initialization for 'acpd7219_clks_ops')
   sound/soc/amd/acp-da7219-max98357a.c:312:3: error: 'const struct clk_ops' has no member named 'unprepare'
     .unprepare = acpd7219_clks_unprepare,
      ^~~~~~~~~
   sound/soc/amd/acp-da7219-max98357a.c:312:15: warning: excess elements in struct initializer
     .unprepare = acpd7219_clks_unprepare,
                  ^~~~~~~~~~~~~~~~~~~~~~~
   sound/soc/amd/acp-da7219-max98357a.c:312:15: note: (near initialization for 'acpd7219_clks_ops')
   sound/soc/amd/acp-da7219-max98357a.c:313:3: error: 'const struct clk_ops' has no member named 'is_enabled'
     .is_enabled = acpd7219_clks_is_enabled,
      ^~~~~~~~~~
   sound/soc/amd/acp-da7219-max98357a.c:313:16: warning: excess elements in struct initializer
     .is_enabled = acpd7219_clks_is_enabled,
                   ^~~~~~~~~~~~~~~~~~~~~~~~
   sound/soc/amd/acp-da7219-max98357a.c:313:16: note: (near initialization for 'acpd7219_clks_ops')
   sound/soc/amd/acp-da7219-max98357a.c: In function 'register_acpd7219_clocks':
   sound/soc/amd/acp-da7219-max98357a.c:318:9: error: variable 'init' has initializer but incomplete type
     struct clk_init_data init = {};
            ^~~~~~~~~~~~~
   sound/soc/amd/acp-da7219-max98357a.c:318:23: error: storage size of 'init' isn't known
     struct clk_init_data init = {};
                          ^~~~
   sound/soc/amd/acp-da7219-max98357a.c:337:13: error: implicit declaration of function 'devm_clk_register'; did you mean 'device_register'? [-Werror=implicit-function-declaration]
     acp_clks = devm_clk_register(&dev, &cz_clock_obj->acp_clks_hw);
                ^~~~~~~~~~~~~~~~~
                device_register
>> sound/soc/amd/acp-da7219-max98357a.c:346:20: error: implicit declaration of function 'clkdev_create'; did you mean 'clk_set_rate'? [-Werror=implicit-function-declaration]
     acp_clks_lookup = clkdev_create(acp_clks, cz_clock_obj->acp_clks_name,
                       ^~~~~~~~~~~~~
                       clk_set_rate
   sound/soc/amd/acp-da7219-max98357a.c:346:18: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
     acp_clks_lookup = clkdev_create(acp_clks, cz_clock_obj->acp_clks_name,
                     ^
   sound/soc/amd/acp-da7219-max98357a.c:318:23: warning: unused variable 'init' [-Wunused-variable]
     struct clk_init_data init = {};
                          ^~~~
   sound/soc/amd/acp-da7219-max98357a.c: At top level:
   sound/soc/amd/acp-da7219-max98357a.c:310:22: error: storage size of 'acpd7219_clks_ops' isn't known
    const struct clk_ops acpd7219_clks_ops = {
                         ^~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors

vim +273 sound/soc/amd/acp-da7219-max98357a.c

   268	
   269	static int acpd7219_clks_prepare(struct clk_hw *hw)
   270	{
   271		u32 reg_val;
   272		struct cz_clock *cz_clock_obj =
 > 273			container_of(hw, struct cz_clock, acp_clks_hw);
   274		void __iomem *base = cz_clock_obj->res_base;
   275	
   276		reg_val = readl(base + MISCCLKCNTL1);
   277		reg_val &= ~(0x1 << OSCCLKENB);
   278		writel(reg_val, base + MISCCLKCNTL1);
   279		reg_val = readl(base + CLKDRVSTR2);
   280		reg_val |= (0x1 << OSCOUT1CLK25MHZ);
   281		writel(reg_val, base + CLKDRVSTR2);
   282	
   283		return 0;
   284	}
   285	
   286	static void acpd7219_clks_unprepare(struct clk_hw *hw)
   287	{
   288		u32 reg_val;
   289		struct cz_clock *cz_clock_obj =
   290			container_of(hw, struct cz_clock, acp_clks_hw);
   291		void __iomem *base = cz_clock_obj->res_base;
   292	
   293		reg_val = readl(base + MISCCLKCNTL1);
   294		reg_val |= (0x1 << OSCCLKENB);
   295		writel(reg_val, base + MISCCLKCNTL1);
   296	}
   297	
   298	static int acpd7219_clks_is_enabled(struct clk_hw *hw)
   299	{
   300		u32 reg_val;
   301		struct cz_clock *cz_clock_obj =
   302			container_of(hw, struct cz_clock, acp_clks_hw);
   303		void __iomem *base = cz_clock_obj->res_base;
   304	
   305		reg_val = readl(base + MISCCLKCNTL1);
   306	
   307		return !(reg_val & OSCCLKENB);
   308	}
   309	
   310	const struct clk_ops acpd7219_clks_ops = {
   311		.prepare = acpd7219_clks_prepare,
   312		.unprepare = acpd7219_clks_unprepare,
   313		.is_enabled = acpd7219_clks_is_enabled,
   314	};
   315	
   316	static int register_acpd7219_clocks(struct platform_device *pdev)
   317	{
 > 318		struct clk_init_data init = {};
   319		struct clk *acp_clks;
   320		struct clk_lookup *acp_clks_lookup;
   321		struct cz_clock *cz_clock_obj;
   322		struct resource *res;
   323		struct device dev = pdev->dev;
   324	
   325		cz_clock_obj = kzalloc(sizeof(struct cz_clock), GFP_KERNEL);
   326		if (!cz_clock_obj)
   327			return -ENOMEM;
   328	
   329		cz_clock_obj->acp_clks_name = "acpd7219-clks";
   330	
   331		init.parent_names = NULL;
   332		init.num_parents = 0;
   333		init.name = cz_clock_obj->acp_clks_name;
   334		init.ops = &acpd7219_clks_ops;
   335		cz_clock_obj->acp_clks_hw.init = &init;
   336	
   337		acp_clks = devm_clk_register(&dev, &cz_clock_obj->acp_clks_hw);
   338		if (IS_ERR(acp_clks))
   339		{
   340			dev_err(&dev, "Failed to register DAI clocks: %ld\n",
   341				 PTR_ERR(acp_clks));
   342			return -EINVAL;
   343		}
   344		cz_clock_obj->acp_clks = acp_clks;
   345	
 > 346		acp_clks_lookup = clkdev_create(acp_clks, cz_clock_obj->acp_clks_name,
   347						"%s", dev_name(&dev));
   348		if (!acp_clks_lookup)
   349			dev_warn(&dev, "Failed to create DAI clkdev");
   350		else
   351			cz_clock_obj->acp_clks_lookup = acp_clks_lookup;
   352	
   353		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
   354		if (!res) {
   355			dev_err(&pdev->dev, "Failed to get misc io resource.\n");
   356			return -EINVAL;
   357		}
   358		cz_clock_obj->res_base = devm_ioremap_nocache(&pdev->dev, res->start,
   359						resource_size(res));
   360		if (!cz_clock_obj->res_base)
   361			return -ENOMEM;
   362	
   363		return 0;
   364	}
   365	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox

Patch

diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
index 99c6b5c..f36b4c2 100644
--- a/sound/soc/amd/acp-da7219-max98357a.c
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -34,6 +34,7 @@ 
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/acpi.h>
+#include <linux/types.h>
 
 #include "../codecs/da7219.h"
 #include "../codecs/da7219-aad.h"
@@ -41,6 +42,18 @@ 
 #define CZ_PLAT_CLK 24000000
 #define MCLK_RATE 24576000
 #define DUAL_CHANNEL		2
+#define CLKDRVSTR2	0x28
+#define MISCCLKCNTL1	0x40
+#define OSCCLKENB	2
+#define OSCOUT1CLK25MHZ	16
+
+struct cz_clock {
+	const char* acp_clks_name;
+	struct clk_hw acp_clks_hw;
+	struct clk_lookup *acp_clks_lookup;
+	struct clk *acp_clks;
+	void __iomem *res_base;
+};
 
 static struct snd_soc_jack cz_jack;
 struct clk *da7219_dai_clk;
@@ -91,6 +104,8 @@  static int cz_da7219_hw_params(struct snd_pcm_substream *substream,
 {
 	int ret = 0;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *card = rtd->card;
+	struct clk *acpd7219_clk;
 
 	ret = clk_prepare_enable(da7219_dai_clk);
 	if (ret < 0) {
@@ -98,13 +113,27 @@  static int cz_da7219_hw_params(struct snd_pcm_substream *substream,
 		return ret;
 	}
 
+	acpd7219_clk = clk_get(card->dev, "acpd7219-clks");
+	ret = clk_prepare_enable(acpd7219_clk);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't enable oscillator clock %d\n", ret);
+		return ret;
+	}
+
 	return ret;
 }
 
 static int cz_da7219_hw_free(struct snd_pcm_substream *substream)
 {
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *card = rtd->card;
+	struct clk *acpd7219_clk;
+
 	clk_disable_unprepare(da7219_dai_clk);
 
+	acpd7219_clk = clk_get(card->dev, "acpd7219-clks");
+	clk_disable_unprepare(acpd7219_clk);
+
 	return 0;
 }
 
@@ -237,9 +266,106 @@  static int cz_fe_startup(struct snd_pcm_substream *substream)
 	.num_controls = ARRAY_SIZE(cz_mc_controls),
 };
 
+static int acpd7219_clks_prepare(struct clk_hw *hw)
+{
+	u32 reg_val;
+	struct cz_clock *cz_clock_obj =
+		container_of(hw, struct cz_clock, acp_clks_hw);
+	void __iomem *base = cz_clock_obj->res_base;
+
+	reg_val = readl(base + MISCCLKCNTL1);
+	reg_val &= ~(0x1 << OSCCLKENB);
+	writel(reg_val, base + MISCCLKCNTL1);
+	reg_val = readl(base + CLKDRVSTR2);
+	reg_val |= (0x1 << OSCOUT1CLK25MHZ);
+	writel(reg_val, base + CLKDRVSTR2);
+
+	return 0;
+}
+
+static void acpd7219_clks_unprepare(struct clk_hw *hw)
+{
+	u32 reg_val;
+	struct cz_clock *cz_clock_obj =
+		container_of(hw, struct cz_clock, acp_clks_hw);
+	void __iomem *base = cz_clock_obj->res_base;
+
+	reg_val = readl(base + MISCCLKCNTL1);
+	reg_val |= (0x1 << OSCCLKENB);
+	writel(reg_val, base + MISCCLKCNTL1);
+}
+
+static int acpd7219_clks_is_enabled(struct clk_hw *hw)
+{
+	u32 reg_val;
+	struct cz_clock *cz_clock_obj =
+		container_of(hw, struct cz_clock, acp_clks_hw);
+	void __iomem *base = cz_clock_obj->res_base;
+
+	reg_val = readl(base + MISCCLKCNTL1);
+
+	return !(reg_val & OSCCLKENB);
+}
+
+const struct clk_ops acpd7219_clks_ops = {
+	.prepare = acpd7219_clks_prepare,
+	.unprepare = acpd7219_clks_unprepare,
+	.is_enabled = acpd7219_clks_is_enabled,
+};
+
+static int register_acpd7219_clocks(struct platform_device *pdev)
+{
+	struct clk_init_data init = {};
+	struct clk *acp_clks;
+	struct clk_lookup *acp_clks_lookup;
+	struct cz_clock *cz_clock_obj;
+	struct resource *res;
+	struct device dev = pdev->dev;
+
+	cz_clock_obj = kzalloc(sizeof(struct cz_clock), GFP_KERNEL);
+	if (!cz_clock_obj)
+		return -ENOMEM;
+
+	cz_clock_obj->acp_clks_name = "acpd7219-clks";
+
+	init.parent_names = NULL;
+	init.num_parents = 0;
+	init.name = cz_clock_obj->acp_clks_name;
+	init.ops = &acpd7219_clks_ops;
+	cz_clock_obj->acp_clks_hw.init = &init;
+
+	acp_clks = devm_clk_register(&dev, &cz_clock_obj->acp_clks_hw);
+	if (IS_ERR(acp_clks))
+	{
+		dev_err(&dev, "Failed to register DAI clocks: %ld\n",
+			 PTR_ERR(acp_clks));
+		return -EINVAL;
+	}
+	cz_clock_obj->acp_clks = acp_clks;
+
+	acp_clks_lookup = clkdev_create(acp_clks, cz_clock_obj->acp_clks_name,
+					"%s", dev_name(&dev));
+	if (!acp_clks_lookup)
+		dev_warn(&dev, "Failed to create DAI clkdev");
+	else
+		cz_clock_obj->acp_clks_lookup = acp_clks_lookup;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get misc io resource.\n");
+		return -EINVAL;
+	}
+	cz_clock_obj->res_base = devm_ioremap_nocache(&pdev->dev, res->start,
+					resource_size(res));
+	if (!cz_clock_obj->res_base)
+		return -ENOMEM;
+
+	return 0;
+}
+
 static int cz_probe(struct platform_device *pdev)
 {
-	int ret;
+	int ret = 0;
 	struct snd_soc_card *card;
 
 	card = &cz_card;
@@ -252,7 +378,10 @@  static int cz_probe(struct platform_device *pdev)
 				cz_card.name, ret);
 		return ret;
 	}
-	return 0;
+
+	ret = register_acpd7219_clocks(pdev);
+
+	return ret;
 }
 
 static const struct acpi_device_id cz_audio_acpi_match[] = {