| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * smdk_spdif.c  --  S/PDIF audio for SMDK | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright 2010 Samsung Electronics Co. Ltd. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License as | 
					
						
							|  |  |  |  * published by the Free Software Foundation; either version 2 of the | 
					
						
							|  |  |  |  * License, or (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/clk.h>
 | 
					
						
							| 
									
										
										
										
											2011-07-15 12:38:28 -04:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <sound/soc.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "spdif.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Audio clock settings are belonged to board specific part. Every
 | 
					
						
							|  |  |  |  * board can set audio source clock setting which is matched with H/W | 
					
						
							|  |  |  |  * like this function-'set_audio_clock_heirachy'. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int set_audio_clock_heirachy(struct platform_device *pdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct clk *fout_epll, *mout_epll, *sclk_audio0, *sclk_spdif; | 
					
						
							| 
									
										
										
										
											2010-12-06 07:56:59 +09:00
										 |  |  | 	int ret = 0; | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	fout_epll = clk_get(NULL, "fout_epll"); | 
					
						
							|  |  |  | 	if (IS_ERR(fout_epll)) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING "%s: Cannot find fout_epll.\n", | 
					
						
							|  |  |  | 				__func__); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mout_epll = clk_get(NULL, "mout_epll"); | 
					
						
							| 
									
										
										
										
											2010-11-21 20:40:21 +03:00
										 |  |  | 	if (IS_ERR(mout_epll)) { | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 		printk(KERN_WARNING "%s: Cannot find mout_epll.\n", | 
					
						
							|  |  |  | 				__func__); | 
					
						
							|  |  |  | 		ret = -EINVAL; | 
					
						
							|  |  |  | 		goto out1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sclk_audio0 = clk_get(&pdev->dev, "sclk_audio"); | 
					
						
							|  |  |  | 	if (IS_ERR(sclk_audio0)) { | 
					
						
							|  |  |  | 		printk(KERN_WARNING "%s: Cannot find sclk_audio.\n", | 
					
						
							|  |  |  | 				__func__); | 
					
						
							|  |  |  | 		ret = -EINVAL; | 
					
						
							|  |  |  | 		goto out2; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sclk_spdif = clk_get(NULL, "sclk_spdif"); | 
					
						
							| 
									
										
										
										
											2010-11-21 20:40:21 +03:00
										 |  |  | 	if (IS_ERR(sclk_spdif)) { | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 		printk(KERN_WARNING "%s: Cannot find sclk_spdif.\n", | 
					
						
							|  |  |  | 				__func__); | 
					
						
							|  |  |  | 		ret = -EINVAL; | 
					
						
							|  |  |  | 		goto out3; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
											
										 
											2010-11-01 15:38:34 -04:00
										 |  |  | 	/* Set audio clock hierarchy for S/PDIF */ | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 	clk_set_parent(mout_epll, fout_epll); | 
					
						
							|  |  |  | 	clk_set_parent(sclk_audio0, mout_epll); | 
					
						
							|  |  |  | 	clk_set_parent(sclk_spdif, sclk_audio0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	clk_put(sclk_spdif); | 
					
						
							|  |  |  | out3: | 
					
						
							|  |  |  | 	clk_put(sclk_audio0); | 
					
						
							|  |  |  | out2: | 
					
						
							|  |  |  | 	clk_put(mout_epll); | 
					
						
							|  |  |  | out1: | 
					
						
							|  |  |  | 	clk_put(fout_epll); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* We should haved to set clock directly on this part because of clock
 | 
					
						
							|  |  |  |  * scheme of Samsudng SoCs did not support to set rates from abstrct | 
					
						
							| 
									
										
										
											
												tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
											
										 
											2010-11-01 15:38:34 -04:00
										 |  |  |  * clock of it's hierarchy. | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  |  */ | 
					
						
							|  |  |  | static int set_audio_clock_rate(unsigned long epll_rate, | 
					
						
							|  |  |  | 				unsigned long audio_rate) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct clk *fout_epll, *sclk_spdif; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fout_epll = clk_get(NULL, "fout_epll"); | 
					
						
							|  |  |  | 	if (IS_ERR(fout_epll)) { | 
					
						
							|  |  |  | 		printk(KERN_ERR "%s: failed to get fout_epll\n", __func__); | 
					
						
							|  |  |  | 		return -ENOENT; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	clk_set_rate(fout_epll, epll_rate); | 
					
						
							|  |  |  | 	clk_put(fout_epll); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sclk_spdif = clk_get(NULL, "sclk_spdif"); | 
					
						
							|  |  |  | 	if (IS_ERR(sclk_spdif)) { | 
					
						
							|  |  |  | 		printk(KERN_ERR "%s: failed to get sclk_spdif\n", __func__); | 
					
						
							|  |  |  | 		return -ENOENT; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	clk_set_rate(sclk_spdif, audio_rate); | 
					
						
							|  |  |  | 	clk_put(sclk_spdif); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int smdk_hw_params(struct snd_pcm_substream *substream, | 
					
						
							|  |  |  | 		struct snd_pcm_hw_params *params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
					
						
							|  |  |  | 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | 
					
						
							|  |  |  | 	unsigned long pll_out, rclk_rate; | 
					
						
							|  |  |  | 	int ret, ratio; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (params_rate(params)) { | 
					
						
							|  |  |  | 	case 44100: | 
					
						
							|  |  |  | 		pll_out = 45158400; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case 32000: | 
					
						
							|  |  |  | 	case 48000: | 
					
						
							|  |  |  | 	case 96000: | 
					
						
							|  |  |  | 		pll_out = 49152000; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Setting ratio to 512fs helps to use S/PDIF with HDMI without
 | 
					
						
							|  |  |  | 	 * modify S/PDIF ASoC machine driver. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	ratio = 512; | 
					
						
							|  |  |  | 	rclk_rate = params_rate(params) * ratio; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set audio source clock rates */ | 
					
						
							|  |  |  | 	ret = set_audio_clock_rate(pll_out, rclk_rate); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set S/PDIF uses internal source clock */ | 
					
						
							|  |  |  | 	ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, | 
					
						
							|  |  |  | 					rclk_rate, SND_SOC_CLOCK_IN); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct snd_soc_ops smdk_spdif_ops = { | 
					
						
							|  |  |  | 	.hw_params = smdk_hw_params, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct snd_soc_dai_link smdk_dai = { | 
					
						
							|  |  |  | 	.name = "S/PDIF", | 
					
						
							|  |  |  | 	.stream_name = "S/PDIF PCM Playback", | 
					
						
							| 
									
										
										
										
											2012-12-07 13:59:21 +05:30
										 |  |  | 	.platform_name = "samsung-spdif", | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 	.cpu_dai_name = "samsung-spdif", | 
					
						
							|  |  |  | 	.codec_dai_name = "dit-hifi", | 
					
						
							|  |  |  | 	.codec_name = "spdif-dit", | 
					
						
							|  |  |  | 	.ops = &smdk_spdif_ops, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct snd_soc_card smdk = { | 
					
						
							|  |  |  | 	.name = "SMDK-S/PDIF", | 
					
						
							| 
									
										
										
										
											2011-12-22 10:53:15 +08:00
										 |  |  | 	.owner = THIS_MODULE, | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 	.dai_link = &smdk_dai, | 
					
						
							|  |  |  | 	.num_links = 1, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct platform_device *smdk_snd_spdif_dit_device; | 
					
						
							|  |  |  | static struct platform_device *smdk_snd_spdif_device; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init smdk_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	smdk_snd_spdif_dit_device = platform_device_alloc("spdif-dit", -1); | 
					
						
							|  |  |  | 	if (!smdk_snd_spdif_dit_device) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = platform_device_add(smdk_snd_spdif_dit_device); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2010-11-26 14:54:42 +08:00
										 |  |  | 		goto err1; | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	smdk_snd_spdif_device = platform_device_alloc("soc-audio", -1); | 
					
						
							|  |  |  | 	if (!smdk_snd_spdif_device) { | 
					
						
							|  |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto err2; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	platform_set_drvdata(smdk_snd_spdif_device, &smdk); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = platform_device_add(smdk_snd_spdif_device); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2010-11-26 14:54:42 +08:00
										 |  |  | 		goto err3; | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
											
										 
											2010-11-01 15:38:34 -04:00
										 |  |  | 	/* Set audio clock hierarchy manually */ | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 	ret = set_audio_clock_heirachy(smdk_snd_spdif_device); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2010-11-26 14:54:42 +08:00
										 |  |  | 		goto err4; | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2010-11-26 14:54:42 +08:00
										 |  |  | err4: | 
					
						
							|  |  |  | 	platform_device_del(smdk_snd_spdif_device); | 
					
						
							|  |  |  | err3: | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 	platform_device_put(smdk_snd_spdif_device); | 
					
						
							|  |  |  | err2: | 
					
						
							| 
									
										
										
										
											2010-11-26 14:54:42 +08:00
										 |  |  | 	platform_device_del(smdk_snd_spdif_dit_device); | 
					
						
							|  |  |  | err1: | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | 	platform_device_put(smdk_snd_spdif_dit_device); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __exit smdk_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	platform_device_unregister(smdk_snd_spdif_device); | 
					
						
							| 
									
										
										
										
											2010-11-26 14:54:42 +08:00
										 |  |  | 	platform_device_unregister(smdk_snd_spdif_dit_device); | 
					
						
							| 
									
										
										
										
											2010-10-12 20:58:52 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_init(smdk_init); | 
					
						
							|  |  |  | module_exit(smdk_exit); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("ALSA SoC SMDK+S/PDIF"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); |