diff options
Diffstat (limited to 'sound/soc/codecs/cx20442.c')
-rw-r--r-- | sound/soc/codecs/cx20442.c | 173 |
1 files changed, 49 insertions, 124 deletions
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index f07a415c753f..e8d27c8f9ba3 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c | |||
@@ -24,7 +24,8 @@ | |||
24 | 24 | ||
25 | 25 | ||
26 | struct cx20442_priv { | 26 | struct 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) | |||
164 | static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg, | 165 | static 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 */ | ||
213 | static 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() */ |
229 | static int v253_open(struct tty_struct *tty) | 227 | static 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: | |||
253 | static void v253_close(struct tty_struct *tty) | 251 | static 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 | ||
316 | struct snd_soc_dai cx20442_dai = { | 320 | static struct snd_soc_dai_driver cx20442_dai = { |
317 | .name = "CX20442", | 321 | .name = "cx20442-voice", |
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 | }; |
333 | EXPORT_SYMBOL_GPL(cx20442_dai); | ||
334 | 337 | ||
335 | static int cx20442_codec_probe(struct platform_device *pdev) | 338 | static 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 | ||
358 | pcm_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 */ |
363 | static int cx20442_codec_remove(struct platform_device *pdev) | 357 | static 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 | ||
373 | struct snd_soc_codec_device cx20442_codec_dev = { | 370 | static 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 | }; |
377 | EXPORT_SYMBOL_GPL(cx20442_codec_dev); | ||
378 | |||
379 | static 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 | |||
420 | err_codec: | ||
421 | snd_soc_unregister_codec(codec); | ||
422 | err: | ||
423 | cx20442_codec = NULL; | ||
424 | kfree(cx20442); | ||
425 | return ret; | ||
426 | } | ||
427 | |||
428 | static 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 | ||
437 | static int cx20442_platform_probe(struct platform_device *pdev) | 379 | static 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 | ||
458 | static int __exit cx20442_platform_remove(struct platform_device *pdev) | 385 | static 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 | ||
466 | static struct platform_driver cx20442_platform_driver = { | 391 | static 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); | |||
487 | MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver"); | 412 | MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver"); |
488 | MODULE_AUTHOR("Janusz Krzysztofik"); | 413 | MODULE_AUTHOR("Janusz Krzysztofik"); |
489 | MODULE_LICENSE("GPL"); | 414 | MODULE_LICENSE("GPL"); |
490 | MODULE_ALIAS("platform:cx20442"); | 415 | MODULE_ALIAS("platform:cx20442-codec"); |