aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPadmavathi Venna <padma.v@samsung.com>2013-01-18 06:47:01 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2013-01-28 23:47:46 -0500
commit40476f61897933d524b7069a6df65629a469d922 (patch)
treef3f71ce0dc064b11b844a18b0bd36c7ad7747926
parent2d77828d9904494d3c7424189ee38cc07950df5e (diff)
ASoC: samsung: Add DT support for i2s
Add support for device based discovery. Signed-off-by: Padmavathi Venna <padma.v@samsung.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--Documentation/devicetree/bindings/sound/samsung-i2s.txt63
-rw-r--r--sound/soc/samsung/dma.c3
-rw-r--r--sound/soc/samsung/dma.h1
-rw-r--r--sound/soc/samsung/i2s.c209
4 files changed, 230 insertions, 46 deletions
diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt
new file mode 100644
index 000000000000..3070046da2e5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt
@@ -0,0 +1,63 @@
1* Samsung I2S controller
2
3Required SoC Specific Properties:
4
5- compatible : "samsung,i2s-v5"
6- reg: physical base address of the controller and length of memory mapped
7 region.
8- dmas: list of DMA controller phandle and DMA request line ordered pairs.
9- dma-names: identifier string for each DMA request line in the dmas property.
10 These strings correspond 1:1 with the ordered pairs in dmas.
11
12Optional SoC Specific Properties:
13
14- samsung,supports-6ch: If the I2S Primary sound source has 5.1 Channel
15 support, this flag is enabled.
16- samsung,supports-rstclr: This flag should be set if I2S software reset bit
17 control is required. When this flag is set I2S software reset bit will be
18 enabled or disabled based on need.
19- samsung,supports-secdai:If I2S block has a secondary FIFO and internal DMA,
20 then this flag is enabled.
21- samsung,idma-addr: Internal DMA register base address of the audio
22 sub system(used in secondary sound source).
23
24Required Board Specific Properties:
25
26- gpios: The gpio specifier for data out,data in, LRCLK, CDCLK and SCLK
27 interface lines. The format of the gpio specifier depends on the gpio
28 controller.
29 The syntax of samsung gpio specifier is
30 <[phandle of the gpio controller node]
31 [pin number within the gpio controller]
32 [mux function]
33 [flags and pull up/down]
34 [drive strength]>
35
36Example:
37
38- SoC Specific Portion:
39
40i2s@03830000 {
41 compatible = "samsung,i2s-v5";
42 reg = <0x03830000 0x100>;
43 dmas = <&pdma0 10
44 &pdma0 9
45 &pdma0 8>;
46 dma-names = "tx", "rx", "tx-sec";
47 samsung,supports-6ch;
48 samsung,supports-rstclr;
49 samsung,supports-secdai;
50 samsung,idma-addr = <0x03000000>;
51};
52
53- Board Specific Portion:
54
55i2s@03830000 {
56 gpios = <&gpz 0 2 0 0>, /* I2S_0_SCLK */
57 <&gpz 1 2 0 0>, /* I2S_0_CDCLK */
58 <&gpz 2 2 0 0>, /* I2S_0_LRCK */
59 <&gpz 3 2 0 0>, /* I2S_0_SDI */
60 <&gpz 4 2 0 0>, /* I2S_0_SDO[1] */
61 <&gpz 5 2 0 0>, /* I2S_0_SDO[2] */
62 <&gpz 6 2 0 0>; /* I2S_0_SDO[3] */
63};
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c
index db87628d7630..21b79262010e 100644
--- a/sound/soc/samsung/dma.c
+++ b/sound/soc/samsung/dma.c
@@ -174,7 +174,8 @@ static int dma_hw_params(struct snd_pcm_substream *substream,
174 config.width = prtd->params->dma_size; 174 config.width = prtd->params->dma_size;
175 config.fifo = prtd->params->dma_addr; 175 config.fifo = prtd->params->dma_addr;
176 prtd->params->ch = prtd->params->ops->request( 176 prtd->params->ch = prtd->params->ops->request(
177 prtd->params->channel, &req); 177 prtd->params->channel, &req, rtd->cpu_dai->dev,
178 prtd->params->ch_name);
178 prtd->params->ops->config(prtd->params->ch, &config); 179 prtd->params->ops->config(prtd->params->ch, &config);
179 } 180 }
180 181
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
index 73d8c7c8a1e8..189a7a6d5020 100644
--- a/sound/soc/samsung/dma.h
+++ b/sound/soc/samsung/dma.h
@@ -19,6 +19,7 @@ struct s3c_dma_params {
19 int dma_size; /* Size of the DMA transfer */ 19 int dma_size; /* Size of the DMA transfer */
20 unsigned ch; 20 unsigned ch;
21 struct samsung_dma_ops *ops; 21 struct samsung_dma_ops *ops;
22 char *ch_name;
22}; 23};
23 24
24int asoc_dma_platform_register(struct device *dev); 25int asoc_dma_platform_register(struct device *dev);
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 808df74c3248..2fc42f9bf962 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -15,11 +15,15 @@
15#include <linux/clk.h> 15#include <linux/clk.h>
16#include <linux/io.h> 16#include <linux/io.h>
17#include <linux/module.h> 17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/of_gpio.h>
18#include <linux/pm_runtime.h> 20#include <linux/pm_runtime.h>
19 21
20#include <sound/soc.h> 22#include <sound/soc.h>
21#include <sound/pcm_params.h> 23#include <sound/pcm_params.h>
22 24
25#include <mach/dma.h>
26
23#include <linux/platform_data/asoc-s3c.h> 27#include <linux/platform_data/asoc-s3c.h>
24 28
25#include "dma.h" 29#include "dma.h"
@@ -34,6 +38,10 @@ enum samsung_dai_type {
34 TYPE_SEC, 38 TYPE_SEC,
35}; 39};
36 40
41struct samsung_i2s_dai_data {
42 int dai_type;
43};
44
37struct i2s_dai { 45struct i2s_dai {
38 /* Platform device for this DAI */ 46 /* Platform device for this DAI */
39 struct platform_device *pdev; 47 struct platform_device *pdev;
@@ -71,6 +79,7 @@ struct i2s_dai {
71 u32 suspend_i2smod; 79 u32 suspend_i2smod;
72 u32 suspend_i2scon; 80 u32 suspend_i2scon;
73 u32 suspend_i2spsr; 81 u32 suspend_i2spsr;
82 unsigned long gpios[7]; /* i2s gpio line numbers */
74}; 83};
75 84
76/* Lock for cross i/f checks */ 85/* Lock for cross i/f checks */
@@ -1000,19 +1009,76 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
1000 return i2s; 1009 return i2s;
1001} 1010}
1002 1011
1012#ifdef CONFIG_OF
1013static int samsung_i2s_parse_dt_gpio(struct i2s_dai *i2s)
1014{
1015 struct device *dev = &i2s->pdev->dev;
1016 int index, gpio, ret;
1017
1018 for (index = 0; index < 7; index++) {
1019 gpio = of_get_gpio(dev->of_node, index);
1020 if (!gpio_is_valid(gpio)) {
1021 dev_err(dev, "invalid gpio[%d]: %d\n", index, gpio);
1022 goto free_gpio;
1023 }
1024
1025 ret = gpio_request(gpio, dev_name(dev));
1026 if (ret) {
1027 dev_err(dev, "gpio [%d] request failed\n", gpio);
1028 goto free_gpio;
1029 }
1030 i2s->gpios[index] = gpio;
1031 }
1032 return 0;
1033
1034free_gpio:
1035 while (--index >= 0)
1036 gpio_free(i2s->gpios[index]);
1037 return -EINVAL;
1038}
1039
1040static void samsung_i2s_dt_gpio_free(struct i2s_dai *i2s)
1041{
1042 unsigned int index;
1043 for (index = 0; index < 7; index++)
1044 gpio_free(i2s->gpios[index]);
1045}
1046#else
1047static int samsung_i2s_parse_dt_gpio(struct i2s_dai *dai)
1048{
1049 return -EINVAL;
1050}
1051
1052static void samsung_i2s_dt_gpio_free(struct i2s_dai *dai)
1053{
1054}
1055
1056#endif
1057
1058static const struct of_device_id exynos_i2s_match[];
1059
1003static inline int samsung_i2s_get_driver_data(struct platform_device *pdev) 1060static inline int samsung_i2s_get_driver_data(struct platform_device *pdev)
1004{ 1061{
1005 return platform_get_device_id(pdev)->driver_data; 1062#ifdef CONFIG_OF
1063 struct samsung_i2s_dai_data *data;
1064 if (pdev->dev.of_node) {
1065 const struct of_device_id *match;
1066 match = of_match_node(exynos_i2s_match, pdev->dev.of_node);
1067 data = (struct samsung_i2s_dai_data *) match->data;
1068 return data->dai_type;
1069 } else
1070#endif
1071 return platform_get_device_id(pdev)->driver_data;
1006} 1072}
1007 1073
1008static int samsung_i2s_probe(struct platform_device *pdev) 1074static int samsung_i2s_probe(struct platform_device *pdev)
1009{ 1075{
1010 u32 dma_pl_chan, dma_cp_chan, dma_pl_sec_chan;
1011 struct i2s_dai *pri_dai, *sec_dai = NULL; 1076 struct i2s_dai *pri_dai, *sec_dai = NULL;
1012 struct s3c_audio_pdata *i2s_pdata; 1077 struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
1013 struct samsung_i2s *i2s_cfg; 1078 struct samsung_i2s *i2s_cfg = NULL;
1014 struct resource *res; 1079 struct resource *res;
1015 u32 regs_base, quirks; 1080 u32 regs_base, quirks = 0, idma_addr = 0;
1081 struct device_node *np = pdev->dev.of_node;
1016 enum samsung_dai_type samsung_dai_type; 1082 enum samsung_dai_type samsung_dai_type;
1017 int ret = 0; 1083 int ret = 0;
1018 1084
@@ -1027,31 +1093,60 @@ static int samsung_i2s_probe(struct platform_device *pdev)
1027 return 0; 1093 return 0;
1028 } 1094 }
1029 1095
1030 i2s_pdata = pdev->dev.platform_data; 1096 pri_dai = i2s_alloc_dai(pdev, false);
1031 if (i2s_pdata == NULL) { 1097 if (!pri_dai) {
1032 dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); 1098 dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
1033 return -EINVAL; 1099 return -ENOMEM;
1034 } 1100 }
1035 1101
1036 res = platform_get_resource(pdev, IORESOURCE_DMA, 0); 1102 if (!np) {
1037 if (!res) { 1103 res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
1038 dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n"); 1104 if (!res) {
1039 return -ENXIO; 1105 dev_err(&pdev->dev,
1040 } 1106 "Unable to get I2S-TX dma resource\n");
1041 dma_pl_chan = res->start; 1107 return -ENXIO;
1108 }
1109 pri_dai->dma_playback.channel = res->start;
1042 1110
1043 res = platform_get_resource(pdev, IORESOURCE_DMA, 1); 1111 res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
1044 if (!res) { 1112 if (!res) {
1045 dev_err(&pdev->dev, "Unable to get I2S-RX dma resource\n"); 1113 dev_err(&pdev->dev,
1046 return -ENXIO; 1114 "Unable to get I2S-RX dma resource\n");
1047 } 1115 return -ENXIO;
1048 dma_cp_chan = res->start; 1116 }
1117 pri_dai->dma_capture.channel = res->start;
1049 1118
1050 res = platform_get_resource(pdev, IORESOURCE_DMA, 2); 1119 if (i2s_pdata == NULL) {
1051 if (res) 1120 dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n");
1052 dma_pl_sec_chan = res->start; 1121 return -EINVAL;
1053 else 1122 }
1054 dma_pl_sec_chan = 0; 1123
1124 if (&i2s_pdata->type)
1125 i2s_cfg = &i2s_pdata->type.i2s;
1126
1127 if (i2s_cfg) {
1128 quirks = i2s_cfg->quirks;
1129 idma_addr = i2s_cfg->idma_addr;
1130 }
1131 } else {
1132 if (of_find_property(np, "samsung,supports-6ch", NULL))
1133 quirks |= QUIRK_PRI_6CHAN;
1134
1135 if (of_find_property(np, "samsung,supports-secdai", NULL))
1136 quirks |= QUIRK_SEC_DAI;
1137
1138 if (of_find_property(np, "samsung,supports-rstclr", NULL))
1139 quirks |= QUIRK_NEED_RSTCLR;
1140
1141 if (of_property_read_u32(np, "samsung,idma-addr",
1142 &idma_addr)) {
1143 if (quirks & QUIRK_SEC_DAI) {
1144 dev_err(&pdev->dev, "idma address is not"\
1145 "specified");
1146 return -EINVAL;
1147 }
1148 }
1149 }
1055 1150
1056 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1151 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1057 if (!res) { 1152 if (!res) {
@@ -1066,24 +1161,14 @@ static int samsung_i2s_probe(struct platform_device *pdev)
1066 } 1161 }
1067 regs_base = res->start; 1162 regs_base = res->start;
1068 1163
1069 i2s_cfg = &i2s_pdata->type.i2s;
1070 quirks = i2s_cfg->quirks;
1071
1072 pri_dai = i2s_alloc_dai(pdev, false);
1073 if (!pri_dai) {
1074 dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
1075 ret = -ENOMEM;
1076 goto err;
1077 }
1078
1079 pri_dai->dma_playback.dma_addr = regs_base + I2STXD; 1164 pri_dai->dma_playback.dma_addr = regs_base + I2STXD;
1080 pri_dai->dma_capture.dma_addr = regs_base + I2SRXD; 1165 pri_dai->dma_capture.dma_addr = regs_base + I2SRXD;
1081 pri_dai->dma_playback.client = 1166 pri_dai->dma_playback.client =
1082 (struct s3c2410_dma_client *)&pri_dai->dma_playback; 1167 (struct s3c2410_dma_client *)&pri_dai->dma_playback;
1168 pri_dai->dma_playback.ch_name = "tx";
1083 pri_dai->dma_capture.client = 1169 pri_dai->dma_capture.client =
1084 (struct s3c2410_dma_client *)&pri_dai->dma_capture; 1170 (struct s3c2410_dma_client *)&pri_dai->dma_capture;
1085 pri_dai->dma_playback.channel = dma_pl_chan; 1171 pri_dai->dma_capture.ch_name = "rx";
1086 pri_dai->dma_capture.channel = dma_cp_chan;
1087 pri_dai->dma_playback.dma_size = 4; 1172 pri_dai->dma_playback.dma_size = 4;
1088 pri_dai->dma_capture.dma_size = 4; 1173 pri_dai->dma_capture.dma_size = 4;
1089 pri_dai->base = regs_base; 1174 pri_dai->base = regs_base;
@@ -1102,20 +1187,34 @@ static int samsung_i2s_probe(struct platform_device *pdev)
1102 sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; 1187 sec_dai->dma_playback.dma_addr = regs_base + I2STXDS;
1103 sec_dai->dma_playback.client = 1188 sec_dai->dma_playback.client =
1104 (struct s3c2410_dma_client *)&sec_dai->dma_playback; 1189 (struct s3c2410_dma_client *)&sec_dai->dma_playback;
1105 /* Use iDMA always if SysDMA not provided */ 1190 sec_dai->dma_playback.ch_name = "tx-sec";
1106 sec_dai->dma_playback.channel = dma_pl_sec_chan ? : -1; 1191
1192 if (!np) {
1193 res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
1194 if (res)
1195 sec_dai->dma_playback.channel = res->start;
1196 }
1197
1107 sec_dai->dma_playback.dma_size = 4; 1198 sec_dai->dma_playback.dma_size = 4;
1108 sec_dai->base = regs_base; 1199 sec_dai->base = regs_base;
1109 sec_dai->quirks = quirks; 1200 sec_dai->quirks = quirks;
1110 sec_dai->idma_playback.dma_addr = i2s_cfg->idma_addr; 1201 sec_dai->idma_playback.dma_addr = idma_addr;
1111 sec_dai->pri_dai = pri_dai; 1202 sec_dai->pri_dai = pri_dai;
1112 pri_dai->sec_dai = sec_dai; 1203 pri_dai->sec_dai = sec_dai;
1113 } 1204 }
1114 1205
1115 if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { 1206 if (np) {
1116 dev_err(&pdev->dev, "Unable to configure gpio\n"); 1207 if (samsung_i2s_parse_dt_gpio(pri_dai)) {
1117 ret = -EINVAL; 1208 dev_err(&pdev->dev, "Unable to configure gpio\n");
1118 goto err; 1209 ret = -EINVAL;
1210 goto err;
1211 }
1212 } else {
1213 if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
1214 dev_err(&pdev->dev, "Unable to configure gpio\n");
1215 ret = -EINVAL;
1216 goto err;
1217 }
1119 } 1218 }
1120 1219
1121 snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv); 1220 snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv);
@@ -1135,10 +1234,14 @@ static int samsung_i2s_remove(struct platform_device *pdev)
1135{ 1234{
1136 struct i2s_dai *i2s, *other; 1235 struct i2s_dai *i2s, *other;
1137 struct resource *res; 1236 struct resource *res;
1237 struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
1138 1238
1139 i2s = dev_get_drvdata(&pdev->dev); 1239 i2s = dev_get_drvdata(&pdev->dev);
1140 other = i2s->pri_dai ? : i2s->sec_dai; 1240 other = i2s->pri_dai ? : i2s->sec_dai;
1141 1241
1242 if (!i2s_pdata->cfg_gpio && pdev->dev.of_node)
1243 samsung_i2s_dt_gpio_free(i2s->pri_dai);
1244
1142 if (other) { 1245 if (other) {
1143 other->pri_dai = NULL; 1246 other->pri_dai = NULL;
1144 other->sec_dai = NULL; 1247 other->sec_dai = NULL;
@@ -1170,6 +1273,21 @@ static struct platform_device_id samsung_i2s_driver_ids[] = {
1170}; 1273};
1171MODULE_DEVICE_TABLE(platform, samsung-i2s-driver-ids); 1274MODULE_DEVICE_TABLE(platform, samsung-i2s-driver-ids);
1172 1275
1276#ifdef CONFIG_OF
1277static struct samsung_i2s_dai_data samsung_i2s_dai_data_array[] = {
1278 [TYPE_PRI] = { TYPE_PRI },
1279 [TYPE_SEC] = { TYPE_SEC },
1280};
1281
1282static const struct of_device_id exynos_i2s_match[] = {
1283 { .compatible = "samsung,i2s-v5",
1284 .data = &samsung_i2s_dai_data_array[TYPE_PRI],
1285 },
1286 {},
1287};
1288MODULE_DEVICE_TABLE(of, exynos_i2s_match);
1289#endif
1290
1173static struct platform_driver samsung_i2s_driver = { 1291static struct platform_driver samsung_i2s_driver = {
1174 .probe = samsung_i2s_probe, 1292 .probe = samsung_i2s_probe,
1175 .remove = samsung_i2s_remove, 1293 .remove = samsung_i2s_remove,
@@ -1177,6 +1295,7 @@ static struct platform_driver samsung_i2s_driver = {
1177 .driver = { 1295 .driver = {
1178 .name = "samsung-i2s", 1296 .name = "samsung-i2s",
1179 .owner = THIS_MODULE, 1297 .owner = THIS_MODULE,
1298 .of_match_table = of_match_ptr(exynos_i2s_match),
1180 }, 1299 },
1181}; 1300};
1182 1301