diff options
Diffstat (limited to 'drivers/mfd/twl4030-audio.c')
-rw-r--r-- | drivers/mfd/twl4030-audio.c | 105 |
1 files changed, 70 insertions, 35 deletions
diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c index 77c9acb14583..5c11acf9e0fd 100644 --- a/drivers/mfd/twl4030-audio.c +++ b/drivers/mfd/twl4030-audio.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include <linux/kernel.h> | 28 | #include <linux/kernel.h> |
29 | #include <linux/fs.h> | 29 | #include <linux/fs.h> |
30 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
31 | #include <linux/of.h> | ||
32 | #include <linux/of_platform.h> | ||
31 | #include <linux/i2c/twl.h> | 33 | #include <linux/i2c/twl.h> |
32 | #include <linux/mfd/core.h> | 34 | #include <linux/mfd/core.h> |
33 | #include <linux/mfd/twl4030-audio.h> | 35 | #include <linux/mfd/twl4030-audio.h> |
@@ -156,47 +158,70 @@ unsigned int twl4030_audio_get_mclk(void) | |||
156 | } | 158 | } |
157 | EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk); | 159 | EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk); |
158 | 160 | ||
161 | static bool twl4030_audio_has_codec(struct twl4030_audio_data *pdata, | ||
162 | struct device_node *node) | ||
163 | { | ||
164 | if (pdata && pdata->codec) | ||
165 | return true; | ||
166 | |||
167 | if (of_find_node_by_name(node, "codec")) | ||
168 | return true; | ||
169 | |||
170 | return false; | ||
171 | } | ||
172 | |||
173 | static bool twl4030_audio_has_vibra(struct twl4030_audio_data *pdata, | ||
174 | struct device_node *node) | ||
175 | { | ||
176 | int vibra; | ||
177 | |||
178 | if (pdata && pdata->vibra) | ||
179 | return true; | ||
180 | |||
181 | if (!of_property_read_u32(node, "ti,enable-vibra", &vibra) && vibra) | ||
182 | return true; | ||
183 | |||
184 | return false; | ||
185 | } | ||
186 | |||
159 | static int __devinit twl4030_audio_probe(struct platform_device *pdev) | 187 | static int __devinit twl4030_audio_probe(struct platform_device *pdev) |
160 | { | 188 | { |
161 | struct twl4030_audio *audio; | 189 | struct twl4030_audio *audio; |
162 | struct twl4030_audio_data *pdata = pdev->dev.platform_data; | 190 | struct twl4030_audio_data *pdata = pdev->dev.platform_data; |
191 | struct device_node *node = pdev->dev.of_node; | ||
163 | struct mfd_cell *cell = NULL; | 192 | struct mfd_cell *cell = NULL; |
164 | int ret, childs = 0; | 193 | int ret, childs = 0; |
165 | u8 val; | 194 | u8 val; |
166 | 195 | ||
167 | if (!pdata) { | 196 | if (!pdata && !node) { |
168 | dev_err(&pdev->dev, "Platform data is missing\n"); | 197 | dev_err(&pdev->dev, "Platform data is missing\n"); |
169 | return -EINVAL; | 198 | return -EINVAL; |
170 | } | 199 | } |
171 | 200 | ||
201 | audio = devm_kzalloc(&pdev->dev, sizeof(struct twl4030_audio), | ||
202 | GFP_KERNEL); | ||
203 | if (!audio) | ||
204 | return -ENOMEM; | ||
205 | |||
206 | mutex_init(&audio->mutex); | ||
207 | audio->audio_mclk = twl_get_hfclk_rate(); | ||
208 | |||
172 | /* Configure APLL_INFREQ and disable APLL if enabled */ | 209 | /* Configure APLL_INFREQ and disable APLL if enabled */ |
173 | val = 0; | 210 | switch (audio->audio_mclk) { |
174 | switch (pdata->audio_mclk) { | ||
175 | case 19200000: | 211 | case 19200000: |
176 | val |= TWL4030_APLL_INFREQ_19200KHZ; | 212 | val = TWL4030_APLL_INFREQ_19200KHZ; |
177 | break; | 213 | break; |
178 | case 26000000: | 214 | case 26000000: |
179 | val |= TWL4030_APLL_INFREQ_26000KHZ; | 215 | val = TWL4030_APLL_INFREQ_26000KHZ; |
180 | break; | 216 | break; |
181 | case 38400000: | 217 | case 38400000: |
182 | val |= TWL4030_APLL_INFREQ_38400KHZ; | 218 | val = TWL4030_APLL_INFREQ_38400KHZ; |
183 | break; | 219 | break; |
184 | default: | 220 | default: |
185 | dev_err(&pdev->dev, "Invalid audio_mclk\n"); | 221 | dev_err(&pdev->dev, "Invalid audio_mclk\n"); |
186 | return -EINVAL; | 222 | return -EINVAL; |
187 | } | 223 | } |
188 | twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | 224 | twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, val, TWL4030_REG_APLL_CTL); |
189 | val, TWL4030_REG_APLL_CTL); | ||
190 | |||
191 | audio = kzalloc(sizeof(struct twl4030_audio), GFP_KERNEL); | ||
192 | if (!audio) | ||
193 | return -ENOMEM; | ||
194 | |||
195 | platform_set_drvdata(pdev, audio); | ||
196 | |||
197 | twl4030_audio_dev = pdev; | ||
198 | mutex_init(&audio->mutex); | ||
199 | audio->audio_mclk = pdata->audio_mclk; | ||
200 | 225 | ||
201 | /* Codec power */ | 226 | /* Codec power */ |
202 | audio->resource[TWL4030_AUDIO_RES_POWER].reg = TWL4030_REG_CODEC_MODE; | 227 | audio->resource[TWL4030_AUDIO_RES_POWER].reg = TWL4030_REG_CODEC_MODE; |
@@ -206,21 +231,28 @@ static int __devinit twl4030_audio_probe(struct platform_device *pdev) | |||
206 | audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL; | 231 | audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL; |
207 | audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN; | 232 | audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN; |
208 | 233 | ||
209 | if (pdata->codec) { | 234 | if (twl4030_audio_has_codec(pdata, node)) { |
210 | cell = &audio->cells[childs]; | 235 | cell = &audio->cells[childs]; |
211 | cell->name = "twl4030-codec"; | 236 | cell->name = "twl4030-codec"; |
212 | cell->platform_data = pdata->codec; | 237 | if (pdata) { |
213 | cell->pdata_size = sizeof(*pdata->codec); | 238 | cell->platform_data = pdata->codec; |
239 | cell->pdata_size = sizeof(*pdata->codec); | ||
240 | } | ||
214 | childs++; | 241 | childs++; |
215 | } | 242 | } |
216 | if (pdata->vibra) { | 243 | if (twl4030_audio_has_vibra(pdata, node)) { |
217 | cell = &audio->cells[childs]; | 244 | cell = &audio->cells[childs]; |
218 | cell->name = "twl4030-vibra"; | 245 | cell->name = "twl4030-vibra"; |
219 | cell->platform_data = pdata->vibra; | 246 | if (pdata) { |
220 | cell->pdata_size = sizeof(*pdata->vibra); | 247 | cell->platform_data = pdata->vibra; |
248 | cell->pdata_size = sizeof(*pdata->vibra); | ||
249 | } | ||
221 | childs++; | 250 | childs++; |
222 | } | 251 | } |
223 | 252 | ||
253 | platform_set_drvdata(pdev, audio); | ||
254 | twl4030_audio_dev = pdev; | ||
255 | |||
224 | if (childs) | 256 | if (childs) |
225 | ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells, | 257 | ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells, |
226 | childs, NULL, 0, NULL); | 258 | childs, NULL, 0, NULL); |
@@ -229,39 +261,42 @@ static int __devinit twl4030_audio_probe(struct platform_device *pdev) | |||
229 | ret = -ENODEV; | 261 | ret = -ENODEV; |
230 | } | 262 | } |
231 | 263 | ||
232 | if (!ret) | 264 | if (ret) { |
233 | return 0; | 265 | platform_set_drvdata(pdev, NULL); |
266 | twl4030_audio_dev = NULL; | ||
267 | } | ||
234 | 268 | ||
235 | platform_set_drvdata(pdev, NULL); | ||
236 | kfree(audio); | ||
237 | twl4030_audio_dev = NULL; | ||
238 | return ret; | 269 | return ret; |
239 | } | 270 | } |
240 | 271 | ||
241 | static int __devexit twl4030_audio_remove(struct platform_device *pdev) | 272 | static int __devexit twl4030_audio_remove(struct platform_device *pdev) |
242 | { | 273 | { |
243 | struct twl4030_audio *audio = platform_get_drvdata(pdev); | ||
244 | |||
245 | mfd_remove_devices(&pdev->dev); | 274 | mfd_remove_devices(&pdev->dev); |
246 | platform_set_drvdata(pdev, NULL); | 275 | platform_set_drvdata(pdev, NULL); |
247 | kfree(audio); | ||
248 | twl4030_audio_dev = NULL; | 276 | twl4030_audio_dev = NULL; |
249 | 277 | ||
250 | return 0; | 278 | return 0; |
251 | } | 279 | } |
252 | 280 | ||
253 | MODULE_ALIAS("platform:twl4030-audio"); | 281 | static const struct of_device_id twl4030_audio_of_match[] = { |
282 | {.compatible = "ti,twl4030-audio", }, | ||
283 | { }, | ||
284 | }; | ||
285 | MODULE_DEVICE_TABLE(of, twl4030_audio_of_match); | ||
254 | 286 | ||
255 | static struct platform_driver twl4030_audio_driver = { | 287 | static struct platform_driver twl4030_audio_driver = { |
256 | .probe = twl4030_audio_probe, | ||
257 | .remove = __devexit_p(twl4030_audio_remove), | ||
258 | .driver = { | 288 | .driver = { |
259 | .owner = THIS_MODULE, | 289 | .owner = THIS_MODULE, |
260 | .name = "twl4030-audio", | 290 | .name = "twl4030-audio", |
291 | .of_match_table = twl4030_audio_of_match, | ||
261 | }, | 292 | }, |
293 | .probe = twl4030_audio_probe, | ||
294 | .remove = __devexit_p(twl4030_audio_remove), | ||
262 | }; | 295 | }; |
263 | 296 | ||
264 | module_platform_driver(twl4030_audio_driver); | 297 | module_platform_driver(twl4030_audio_driver); |
265 | 298 | ||
266 | MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); | 299 | MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); |
300 | MODULE_DESCRIPTION("TWL4030 audio block MFD driver"); | ||
267 | MODULE_LICENSE("GPL"); | 301 | MODULE_LICENSE("GPL"); |
302 | MODULE_ALIAS("platform:twl4030-audio"); | ||