aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@nokia.com>2009-10-22 06:26:45 -0400
committerTony Lindgren <tony@atomide.com>2009-11-22 13:09:00 -0500
commit3066eec68d21cf4d468809c0b7b1fe9ee59c8f32 (patch)
tree96fe8780590818b4f87129d7a67b5c9c48ff7b4d /drivers
parenta76df42a675c9936e8bf3607226e74c8a5e2d847 (diff)
MFD: twl4030: add twl4030_codec MFD as a new child to the core
New MFD child to twl4030 MFD device. Reason for the twl4030_codec MFD: the vibra control is actually in the codec part of the twl4030. If both the vibra and the audio functionality is needed from the twl4030 at the same time, than they need to control the codec power and APLL at the same time without breaking the other driver. Also these two has to be able to work without the need for the other driver. This MFD device will be used by the drivers, which needs resources from the twl4030 codec like audio and vibra. The platform specific configuration data is passed along to the child drivers (audio, vibra). Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com> Acked-by: Samuel Ortiz <sameo@linux.intel.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/Kconfig6
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/twl4030-codec.c241
-rw-r--r--drivers/mfd/twl4030-core.c14
4 files changed, 262 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 570be139f9df..08f2d07bf56a 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -121,6 +121,12 @@ config TWL4030_POWER
121 and load scripts controling which resources are switched off/on 121 and load scripts controling which resources are switched off/on
122 or reset when a sleep, wakeup or warm reset event occurs. 122 or reset when a sleep, wakeup or warm reset event occurs.
123 123
124config TWL4030_CODEC
125 bool
126 depends on TWL4030_CORE
127 select MFD_CORE
128 default n
129
124config MFD_TMIO 130config MFD_TMIO
125 bool 131 bool
126 default n 132 default n
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f3b277b90d40..af0fc903cec8 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
26 26
27obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o 27obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o
28obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o 28obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
29obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
29 30
30obj-$(CONFIG_MFD_MC13783) += mc13783-core.o 31obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
31 32
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
new file mode 100644
index 000000000000..97103078dc2a
--- /dev/null
+++ b/drivers/mfd/twl4030-codec.c
@@ -0,0 +1,241 @@
1/*
2 * MFD driver for twl4030 codec submodule
3 *
4 * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
5 *
6 * Copyright: (C) 2009 Nokia Corporation
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 */
23
24#include <linux/module.h>
25#include <linux/types.h>
26#include <linux/kernel.h>
27#include <linux/fs.h>
28#include <linux/platform_device.h>
29#include <linux/i2c/twl4030.h>
30#include <linux/mfd/core.h>
31#include <linux/mfd/twl4030-codec.h>
32
33#define TWL4030_CODEC_CELLS 2
34
35static struct platform_device *twl4030_codec_dev;
36
37struct twl4030_codec_resource {
38 int request_count;
39 u8 reg;
40 u8 mask;
41};
42
43struct twl4030_codec {
44 struct mutex mutex;
45 struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
46 struct mfd_cell cells[TWL4030_CODEC_CELLS];
47};
48
49/*
50 * Modify the resource, the function returns the content of the register
51 * after the modification.
52 */
53static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
54{
55 struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
56 u8 val;
57
58 twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
59 codec->resource[id].reg);
60
61 if (enable)
62 val |= codec->resource[id].mask;
63 else
64 val &= ~codec->resource[id].mask;
65
66 twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
67 val, codec->resource[id].reg);
68
69 return val;
70}
71
72static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
73{
74 struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
75 u8 val;
76
77 twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
78 codec->resource[id].reg);
79
80 return val;
81}
82
83/*
84 * Enable the resource.
85 * The function returns with error or the content of the register
86 */
87int twl4030_codec_enable_resource(enum twl4030_codec_res id)
88{
89 struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
90 int val;
91
92 if (id >= TWL4030_CODEC_RES_MAX) {
93 dev_err(&twl4030_codec_dev->dev,
94 "Invalid resource ID (%u)\n", id);
95 return -EINVAL;
96 }
97
98 mutex_lock(&codec->mutex);
99 if (!codec->resource[id].request_count)
100 /* Resource was disabled, enable it */
101 val = twl4030_codec_set_resource(id, 1);
102 else
103 val = twl4030_codec_get_resource(id);
104
105 codec->resource[id].request_count++;
106 mutex_unlock(&codec->mutex);
107
108 return val;
109}
110EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource);
111
112/*
113 * Disable the resource.
114 * The function returns with error or the content of the register
115 */
116int twl4030_codec_disable_resource(unsigned id)
117{
118 struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
119 int val;
120
121 if (id >= TWL4030_CODEC_RES_MAX) {
122 dev_err(&twl4030_codec_dev->dev,
123 "Invalid resource ID (%u)\n", id);
124 return -EINVAL;
125 }
126
127 mutex_lock(&codec->mutex);
128 if (!codec->resource[id].request_count) {
129 dev_err(&twl4030_codec_dev->dev,
130 "Resource has been disabled already (%u)\n", id);
131 mutex_unlock(&codec->mutex);
132 return -EPERM;
133 }
134 codec->resource[id].request_count--;
135
136 if (!codec->resource[id].request_count)
137 /* Resource can be disabled now */
138 val = twl4030_codec_set_resource(id, 0);
139 else
140 val = twl4030_codec_get_resource(id);
141
142 mutex_unlock(&codec->mutex);
143
144 return val;
145}
146EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource);
147
148static int __devinit twl4030_codec_probe(struct platform_device *pdev)
149{
150 struct twl4030_codec *codec;
151 struct twl4030_codec_data *pdata = pdev->dev.platform_data;
152 struct mfd_cell *cell = NULL;
153 int ret, childs = 0;
154
155 codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
156 if (!codec)
157 return -ENOMEM;
158
159 platform_set_drvdata(pdev, codec);
160
161 twl4030_codec_dev = pdev;
162 mutex_init(&codec->mutex);
163
164 /* Codec power */
165 codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
166 codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ;
167
168 /* PLL */
169 codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL;
170 codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN;
171
172 if (pdata->audio) {
173 cell = &codec->cells[childs];
174 cell->name = "twl4030_codec_audio";
175 cell->platform_data = pdata->audio;
176 cell->data_size = sizeof(*pdata->audio);
177 childs++;
178 }
179 if (pdata->vibra) {
180 cell = &codec->cells[childs];
181 cell->name = "twl4030_codec_vibra";
182 cell->platform_data = pdata->vibra;
183 cell->data_size = sizeof(*pdata->vibra);
184 childs++;
185 }
186
187 if (childs)
188 ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells,
189 childs, NULL, 0);
190 else {
191 dev_err(&pdev->dev, "No platform data found for childs\n");
192 ret = -ENODEV;
193 }
194
195 if (!ret)
196 return 0;
197
198 platform_set_drvdata(pdev, NULL);
199 kfree(codec);
200 twl4030_codec_dev = NULL;
201 return ret;
202}
203
204static int __devexit twl4030_codec_remove(struct platform_device *pdev)
205{
206 struct twl4030_codec *codec = platform_get_drvdata(pdev);
207
208 mfd_remove_devices(&pdev->dev);
209 platform_set_drvdata(pdev, NULL);
210 kfree(codec);
211 twl4030_codec_dev = NULL;
212
213 return 0;
214}
215
216MODULE_ALIAS("platform:twl4030_codec");
217
218static struct platform_driver twl4030_codec_driver = {
219 .probe = twl4030_codec_probe,
220 .remove = __devexit_p(twl4030_codec_remove),
221 .driver = {
222 .owner = THIS_MODULE,
223 .name = "twl4030_codec",
224 },
225};
226
227static int __devinit twl4030_codec_init(void)
228{
229 return platform_driver_register(&twl4030_codec_driver);
230}
231module_init(twl4030_codec_init);
232
233static void __devexit twl4030_codec_exit(void)
234{
235 platform_driver_unregister(&twl4030_codec_driver);
236}
237module_exit(twl4030_codec_exit);
238
239MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
240MODULE_LICENSE("GPL");
241
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c
index 6cb12502e306..cf462b90dee2 100644
--- a/drivers/mfd/twl4030-core.c
+++ b/drivers/mfd/twl4030-core.c
@@ -114,6 +114,12 @@
114#define twl_has_watchdog() false 114#define twl_has_watchdog() false
115#endif 115#endif
116 116
117#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE)
118#define twl_has_codec() true
119#else
120#define twl_has_codec() false
121#endif
122
117/* Triton Core internal information (BEGIN) */ 123/* Triton Core internal information (BEGIN) */
118 124
119/* Last - for index max*/ 125/* Last - for index max*/
@@ -601,6 +607,14 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
601 return PTR_ERR(child); 607 return PTR_ERR(child);
602 } 608 }
603 609
610 if (twl_has_codec() && pdata->codec) {
611 child = add_child(1, "twl4030_codec",
612 pdata->codec, sizeof(*pdata->codec),
613 false, 0, 0);
614 if (IS_ERR(child))
615 return PTR_ERR(child);
616 }
617
604 if (twl_has_regulator()) { 618 if (twl_has_regulator()) {
605 /* 619 /*
606 child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1); 620 child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);