aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/cx20442.c
diff options
context:
space:
mode:
authorLiam Girdwood <lrg@slimlogic.co.uk>2010-03-17 16:15:21 -0400
committerLiam Girdwood <lrg@slimlogic.co.uk>2010-08-12 09:00:00 -0400
commitf0fba2ad1b6b53d5360125c41953b7afcd6deff0 (patch)
treef6ad50905f8daa616593c978d7ae992e73241180 /sound/soc/codecs/cx20442.c
parentbda7d2a862e6b788bca2d02d38a07966a9c92e48 (diff)
ASoC: multi-component - ASoC Multi-Component Support
This patch extends the ASoC API to allow sound cards to have more than one CODEC and more than one platform DMA controller. This is achieved by dividing some current ASoC structures that contain both driver data and device data into structures that only either contain device data or driver data. i.e. struct snd_soc_codec ---> struct snd_soc_codec (device data) +-> struct snd_soc_codec_driver (driver data) struct snd_soc_platform ---> struct snd_soc_platform (device data) +-> struct snd_soc_platform_driver (driver data) struct snd_soc_dai ---> struct snd_soc_dai (device data) +-> struct snd_soc_dai_driver (driver data) struct snd_soc_device ---> deleted This now allows ASoC to be more tightly aligned with the Linux driver model and also means that every ASoC codec, platform and (platform) DAI is a kernel device. ASoC component private data is now stored as device private data. The ASoC sound card struct snd_soc_card has also been updated to store lists of it's components rather than a pointer to a codec and platform. The PCM runtime struct soc_pcm_runtime now has pointers to all its components. This patch adds DAPM support for ASoC multi-component and removes struct snd_soc_socdev from DAPM core. All DAPM calls are now made on a card, codec or runtime PCM level basis rather than using snd_soc_socdev. Other notable multi-component changes:- * Stream operations now de-reference less structures. * close_delayed work() now runs on a DAI basis rather than looping all DAIs in a card. * PM suspend()/resume() operations can now handle N CODECs and Platforms per sound card. * Added soc_bind_dai_link() to bind the component devices to the sound card. * Added soc_dai_link_probe() and soc_dai_link_remove() to probe and remove DAI link components. * sysfs entries can now be registered per component per card. * snd_soc_new_pcms() functionailty rolled into dai_link_probe(). * snd_soc_register_codec() now does all the codec list and mutex init. This patch changes the probe() and remove() of the CODEC drivers as follows:- o Make CODEC driver a platform driver o Moved all struct snd_soc_codec list, mutex, etc initialiasation to core. o Removed all static codec pointers (drivers now support > 1 codec dev) o snd_soc_register_pcms() now done by core. o snd_soc_register_dai() folded into snd_soc_register_codec(). CS4270 portions: Acked-by: Timur Tabi <timur@freescale.com> Some TLV320aic23 and Cirrus platform fixes. Signed-off-by: Ryan Mallon <ryan@bluewatersys.com> TI CODEC and OMAP fixes Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com> Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> Signed-off-by: Jarkko Nikula <jhnikula@gmail.com> Samsung platform and misc fixes :- Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Reviewed-by: Jassi Brar <jassi.brar@samsung.com> Signed-off-by: Seungwhan Youn <sw.youn@samsung.com> MPC8610 and PPC fixes. Signed-off-by: Timur Tabi <timur@freescale.com> i.MX fixes and some core fixes. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> J4740 platform fixes:- Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> CC: Tony Lindgren <tony@atomide.com> CC: Nicolas Ferre <nicolas.ferre@atmel.com> CC: Kevin Hilman <khilman@deeprootsystems.com> CC: Sascha Hauer <s.hauer@pengutronix.de> CC: Atsushi Nemoto <anemo@mba.ocn.ne.jp> CC: Kuninori Morimoto <morimoto.kuninori@renesas.com> CC: Daniel Gloeckner <dg@emlix.com> CC: Manuel Lauss <mano@roarinelk.homelinux.net> CC: Mike Frysinger <vapier.adi@gmail.com> CC: Arnaud Patard <apatard@mandriva.com> CC: Wan ZongShun <mcuos.com@gmail.com> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/codecs/cx20442.c')
-rw-r--r--sound/soc/codecs/cx20442.c173
1 files changed, 49 insertions, 124 deletions
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index f07a415c753f..cf4323dbf9c4 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -24,7 +24,8 @@
24 24
25 25
26struct cx20442_priv { 26struct cx20442_priv {
27 struct snd_soc_codec codec; 27 enum snd_soc_control_type control_type;
28 void *control_data;
28 u8 reg_cache[1]; 29 u8 reg_cache[1];
29}; 30};
30 31
@@ -102,7 +103,7 @@ static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
102{ 103{
103 u8 *reg_cache = codec->reg_cache; 104 u8 *reg_cache = codec->reg_cache;
104 105
105 if (reg >= codec->reg_cache_size) 106 if (reg >= codec->driver->reg_cache_size)
106 return -EINVAL; 107 return -EINVAL;
107 108
108 return reg_cache[reg]; 109 return reg_cache[reg];
@@ -164,16 +165,17 @@ static int cx20442_pm_to_v253_vsp(u8 value)
164static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg, 165static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
165 unsigned int value) 166 unsigned int value)
166{ 167{
168 struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
167 u8 *reg_cache = codec->reg_cache; 169 u8 *reg_cache = codec->reg_cache;
168 int vls, vsp, old, len; 170 int vls, vsp, old, len;
169 char buf[18]; 171 char buf[18];
170 172
171 if (reg >= codec->reg_cache_size) 173 if (reg >= codec->driver->reg_cache_size)
172 return -EINVAL; 174 return -EINVAL;
173 175
174 /* hw_write and control_data pointers required for talking to the modem 176 /* hw_write and control_data pointers required for talking to the modem
175 * are expected to be set by the line discipline initialization code */ 177 * are expected to be set by the line discipline initialization code */
176 if (!codec->hw_write || !codec->control_data) 178 if (!codec->hw_write || !cx20442->control_data)
177 return -EIO; 179 return -EIO;
178 180
179 old = reg_cache[reg]; 181 old = reg_cache[reg];
@@ -202,17 +204,13 @@ static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
202 return -ENOMEM; 204 return -ENOMEM;
203 205
204 dev_dbg(codec->dev, "%s: %s\n", __func__, buf); 206 dev_dbg(codec->dev, "%s: %s\n", __func__, buf);
205 if (codec->hw_write(codec->control_data, buf, len) != len) 207 if (codec->hw_write(cx20442->control_data, buf, len) != len)
206 return -EIO; 208 return -EIO;
207 209
208 return 0; 210 return 0;
209} 211}
210 212
211 213
212/* Moved up here as line discipline referres it during initialization */
213static struct snd_soc_codec *cx20442_codec;
214
215
216/* 214/*
217 * Line discpline related code 215 * Line discpline related code
218 * 216 *
@@ -228,15 +226,15 @@ static const char *v253_init = "ate0m0q0+fclass=8\r";
228/* Line discipline .open() */ 226/* Line discipline .open() */
229static int v253_open(struct tty_struct *tty) 227static int v253_open(struct tty_struct *tty)
230{ 228{
231 struct snd_soc_codec *codec = cx20442_codec;
232 int ret, len = strlen(v253_init); 229 int ret, len = strlen(v253_init);
233 230
234 /* Doesn't make sense without write callback */ 231 /* Doesn't make sense without write callback */
235 if (!tty->ops->write) 232 if (!tty->ops->write)
236 return -EINVAL; 233 return -EINVAL;
237 234
238 /* Pass the codec structure address for use by other ldisc callbacks */ 235 /* Won't work if no codec pointer has been passed by a card driver */
239 tty->disc_data = codec; 236 if (!tty->disc_data)
237 return -ENODEV;
240 238
241 if (tty->ops->write(tty, v253_init, len) != len) { 239 if (tty->ops->write(tty, v253_init, len) != len) {
242 ret = -EIO; 240 ret = -EIO;
@@ -253,15 +251,18 @@ err:
253static void v253_close(struct tty_struct *tty) 251static void v253_close(struct tty_struct *tty)
254{ 252{
255 struct snd_soc_codec *codec = tty->disc_data; 253 struct snd_soc_codec *codec = tty->disc_data;
254 struct cx20442_priv *cx20442;
256 255
257 tty->disc_data = NULL; 256 tty->disc_data = NULL;
258 257
259 if (!codec) 258 if (!codec)
260 return; 259 return;
261 260
261 cx20442 = snd_soc_codec_get_drvdata(codec);
262
262 /* Prevent the codec driver from further accessing the modem */ 263 /* Prevent the codec driver from further accessing the modem */
263 codec->hw_write = NULL; 264 codec->hw_write = NULL;
264 codec->control_data = NULL; 265 cx20442->control_data = NULL;
265 codec->pop_time = 0; 266 codec->pop_time = 0;
266} 267}
267 268
@@ -277,15 +278,18 @@ static void v253_receive(struct tty_struct *tty,
277 const unsigned char *cp, char *fp, int count) 278 const unsigned char *cp, char *fp, int count)
278{ 279{
279 struct snd_soc_codec *codec = tty->disc_data; 280 struct snd_soc_codec *codec = tty->disc_data;
281 struct cx20442_priv *cx20442;
280 282
281 if (!codec) 283 if (!codec)
282 return; 284 return;
283 285
284 if (!codec->control_data) { 286 cx20442 = snd_soc_codec_get_drvdata(codec);
287
288 if (!cx20442->control_data) {
285 /* First modem response, complete setup procedure */ 289 /* First modem response, complete setup procedure */
286 290
287 /* Set up codec driver access to modem controls */ 291 /* Set up codec driver access to modem controls */
288 codec->control_data = tty; 292 cx20442->control_data = tty;
289 codec->hw_write = (hw_write_t)tty->ops->write; 293 codec->hw_write = (hw_write_t)tty->ops->write;
290 codec->pop_time = 1; 294 codec->pop_time = 1;
291 } 295 }
@@ -313,8 +317,8 @@ EXPORT_SYMBOL_GPL(v253_ops);
313 * Codec DAI 317 * Codec DAI
314 */ 318 */
315 319
316struct snd_soc_dai cx20442_dai = { 320static struct snd_soc_dai_driver cx20442_dai = {
317 .name = "CX20442", 321 .name = "cx20442-hifi",
318 .playback = { 322 .playback = {
319 .stream_name = "Playback", 323 .stream_name = "Playback",
320 .channels_min = 1, 324 .channels_min = 1,
@@ -330,142 +334,63 @@ struct snd_soc_dai cx20442_dai = {
330 .formats = SNDRV_PCM_FMTBIT_S16_LE, 334 .formats = SNDRV_PCM_FMTBIT_S16_LE,
331 }, 335 },
332}; 336};
333EXPORT_SYMBOL_GPL(cx20442_dai);
334 337
335static int cx20442_codec_probe(struct platform_device *pdev) 338static int cx20442_codec_probe(struct snd_soc_codec *codec)
336{ 339{
337 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 340 struct cx20442_priv *cx20442;
338 struct snd_soc_codec *codec;
339 int ret;
340
341 if (!cx20442_codec) {
342 dev_err(&pdev->dev, "cx20442 not yet discovered\n");
343 return -ENODEV;
344 }
345 codec = cx20442_codec;
346
347 socdev->card->codec = codec;
348 341
349 /* register pcms */ 342 cx20442 = kzalloc(sizeof(struct cx20442_priv), GFP_KERNEL);
350 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); 343 if (cx20442 == NULL)
351 if (ret < 0) { 344 return -ENOMEM;
352 dev_err(&pdev->dev, "failed to create pcms\n"); 345 snd_soc_codec_set_drvdata(codec, cx20442);
353 goto pcm_err;
354 }
355 346
356 cx20442_add_widgets(codec); 347 cx20442_add_widgets(codec);
357 348
358pcm_err: 349 cx20442->control_data = NULL;
359 return ret; 350 codec->hw_write = NULL;
351 codec->pop_time = 0;
352
353 return 0;
360} 354}
361 355
362/* power down chip */ 356/* power down chip */
363static int cx20442_codec_remove(struct platform_device *pdev) 357static int cx20442_codec_remove(struct snd_soc_codec *codec)
364{ 358{
365 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 359 struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
366 360
367 snd_soc_free_pcms(socdev); 361 if (cx20442->control_data) {
368 snd_soc_dapm_free(socdev); 362 struct tty_struct *tty = cx20442->control_data;
363 tty_hangup(tty);
364 }
369 365
366 kfree(cx20442);
370 return 0; 367 return 0;
371} 368}
372 369
373struct snd_soc_codec_device cx20442_codec_dev = { 370static struct snd_soc_codec_driver cx20442_codec_dev = {
374 .probe = cx20442_codec_probe, 371 .probe = cx20442_codec_probe,
375 .remove = cx20442_codec_remove, 372 .remove = cx20442_codec_remove,
373 .reg_cache_size = 1,
374 .reg_word_size = sizeof(u8),
375 .read = cx20442_read_reg_cache,
376 .write = cx20442_write,
376}; 377};
377EXPORT_SYMBOL_GPL(cx20442_codec_dev);
378
379static int cx20442_register(struct cx20442_priv *cx20442)
380{
381 struct snd_soc_codec *codec = &cx20442->codec;
382 int ret;
383
384 mutex_init(&codec->mutex);
385 INIT_LIST_HEAD(&codec->dapm_widgets);
386 INIT_LIST_HEAD(&codec->dapm_paths);
387
388 codec->name = "CX20442";
389 codec->owner = THIS_MODULE;
390 snd_soc_codec_set_drvdata(codec, cx20442);
391
392 codec->dai = &cx20442_dai;
393 codec->num_dai = 1;
394
395 codec->reg_cache = &cx20442->reg_cache;
396 codec->reg_cache_size = ARRAY_SIZE(cx20442->reg_cache);
397 codec->read = cx20442_read_reg_cache;
398 codec->write = cx20442_write;
399
400 codec->bias_level = SND_SOC_BIAS_OFF;
401
402 cx20442_dai.dev = codec->dev;
403
404 cx20442_codec = codec;
405
406 ret = snd_soc_register_codec(codec);
407 if (ret != 0) {
408 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
409 goto err;
410 }
411
412 ret = snd_soc_register_dai(&cx20442_dai);
413 if (ret != 0) {
414 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
415 goto err_codec;
416 }
417
418 return 0;
419
420err_codec:
421 snd_soc_unregister_codec(codec);
422err:
423 cx20442_codec = NULL;
424 kfree(cx20442);
425 return ret;
426}
427
428static void cx20442_unregister(struct cx20442_priv *cx20442)
429{
430 snd_soc_unregister_dai(&cx20442_dai);
431 snd_soc_unregister_codec(&cx20442->codec);
432
433 cx20442_codec = NULL;
434 kfree(cx20442);
435}
436 378
437static int cx20442_platform_probe(struct platform_device *pdev) 379static int cx20442_platform_probe(struct platform_device *pdev)
438{ 380{
439 struct cx20442_priv *cx20442; 381 return snd_soc_register_codec(&pdev->dev,
440 struct snd_soc_codec *codec; 382 &cx20442_codec_dev, &cx20442_dai, 1);
441
442 cx20442 = kzalloc(sizeof(struct cx20442_priv), GFP_KERNEL);
443 if (cx20442 == NULL)
444 return -ENOMEM;
445
446 codec = &cx20442->codec;
447
448 codec->control_data = NULL;
449 codec->hw_write = NULL;
450 codec->pop_time = 0;
451
452 codec->dev = &pdev->dev;
453 platform_set_drvdata(pdev, cx20442);
454
455 return cx20442_register(cx20442);
456} 383}
457 384
458static int __exit cx20442_platform_remove(struct platform_device *pdev) 385static int __exit cx20442_platform_remove(struct platform_device *pdev)
459{ 386{
460 struct cx20442_priv *cx20442 = platform_get_drvdata(pdev); 387 snd_soc_unregister_codec(&pdev->dev);
461
462 cx20442_unregister(cx20442);
463 return 0; 388 return 0;
464} 389}
465 390
466static struct platform_driver cx20442_platform_driver = { 391static struct platform_driver cx20442_platform_driver = {
467 .driver = { 392 .driver = {
468 .name = "cx20442", 393 .name = "cx20442-codec",
469 .owner = THIS_MODULE, 394 .owner = THIS_MODULE,
470 }, 395 },
471 .probe = cx20442_platform_probe, 396 .probe = cx20442_platform_probe,
@@ -487,4 +412,4 @@ module_exit(cx20442_exit);
487MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver"); 412MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver");
488MODULE_AUTHOR("Janusz Krzysztofik"); 413MODULE_AUTHOR("Janusz Krzysztofik");
489MODULE_LICENSE("GPL"); 414MODULE_LICENSE("GPL");
490MODULE_ALIAS("platform:cx20442"); 415MODULE_ALIAS("platform:cx20442-codec");