diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-06-19 12:06:40 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-07-27 16:53:03 -0400 |
commit | 850d24a5a861238f583f59cd39de4dfe5142a4c9 (patch) | |
tree | e59192ee359e36dcb42b26a79ba00e804f8ba4a9 /drivers/media/video/em28xx/em28xx-audio.c | |
parent | 5b89ecf98998911f397fa913b06ee2304a373e54 (diff) |
[media] em28xx-alsa: add mixer support for AC97 volume controls
Export ac97 volume controls via mixer.
Pulseaudio will probably handle it very badly, as it has
no idea about how volumes are wired, and how are they
associated with each TV input. Those wirings are
card model dependent, and we don't have the wiring mappings
for each supported device.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/em28xx/em28xx-audio.c')
-rw-r--r-- | drivers/media/video/em28xx/em28xx-audio.c | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index a24e1770db48..a75c779640df 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <sound/info.h> | 41 | #include <sound/info.h> |
42 | #include <sound/initval.h> | 42 | #include <sound/initval.h> |
43 | #include <sound/control.h> | 43 | #include <sound/control.h> |
44 | #include <sound/tlv.h> | ||
44 | #include <media/v4l2-common.h> | 45 | #include <media/v4l2-common.h> |
45 | #include "em28xx.h" | 46 | #include "em28xx.h" |
46 | 47 | ||
@@ -433,6 +434,92 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, | |||
433 | return vmalloc_to_page(pageptr); | 434 | return vmalloc_to_page(pageptr); |
434 | } | 435 | } |
435 | 436 | ||
437 | /* | ||
438 | * AC97 volume control support | ||
439 | */ | ||
440 | static int em28xx_vol_info(struct snd_kcontrol *kcontrol, | ||
441 | struct snd_ctl_elem_info *info) | ||
442 | { | ||
443 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
444 | info->count = 2; | ||
445 | info->value.integer.min = 0; | ||
446 | info->value.integer.max = 0x1f; | ||
447 | |||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | /* FIXME: should also add mute controls for each */ | ||
452 | |||
453 | static int em28xx_vol_put(struct snd_kcontrol *kcontrol, | ||
454 | struct snd_ctl_elem_value *value) | ||
455 | { | ||
456 | struct em28xx *dev = snd_kcontrol_chip(kcontrol); | ||
457 | u16 val = (value->value.integer.value[0] & 0x1f) | | ||
458 | (value->value.integer.value[1] & 0x1f) << 8; | ||
459 | int rc; | ||
460 | |||
461 | mutex_lock(&dev->lock); | ||
462 | rc = em28xx_read_ac97(dev, kcontrol->private_value); | ||
463 | if (rc < 0) | ||
464 | goto err; | ||
465 | |||
466 | val |= rc & 0x8080; /* Preserve the mute flags */ | ||
467 | |||
468 | rc = em28xx_write_ac97(dev, kcontrol->private_value, val); | ||
469 | |||
470 | err: | ||
471 | mutex_unlock(&dev->lock); | ||
472 | return rc; | ||
473 | } | ||
474 | |||
475 | static int em28xx_vol_get(struct snd_kcontrol *kcontrol, | ||
476 | struct snd_ctl_elem_value *value) | ||
477 | { | ||
478 | struct em28xx *dev = snd_kcontrol_chip(kcontrol); | ||
479 | int val; | ||
480 | |||
481 | mutex_lock(&dev->lock); | ||
482 | val = em28xx_read_ac97(dev, kcontrol->private_value); | ||
483 | mutex_unlock(&dev->lock); | ||
484 | if (val < 0) | ||
485 | return val; | ||
486 | |||
487 | value->value.integer.value[0] = val & 0x1f; | ||
488 | value->value.integer.value[1] = (val << 8) & 0x1f; | ||
489 | |||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | static const DECLARE_TLV_DB_SCALE(em28xx_db_scale, -3450, 150, 0); | ||
494 | |||
495 | static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev, | ||
496 | char *name, int id) | ||
497 | { | ||
498 | int err; | ||
499 | struct snd_kcontrol *kctl; | ||
500 | struct snd_kcontrol_new tmp = { | ||
501 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
502 | .name = name, | ||
503 | .info = em28xx_vol_info, | ||
504 | .get = em28xx_vol_get, | ||
505 | .put = em28xx_vol_put, | ||
506 | .private_value = id, | ||
507 | .tlv.p = em28xx_db_scale, | ||
508 | }; | ||
509 | |||
510 | kctl = snd_ctl_new1(&tmp, dev); | ||
511 | |||
512 | err = snd_ctl_add(card, kctl); | ||
513 | if (err < 0) | ||
514 | return err; | ||
515 | |||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | |||
520 | /* | ||
521 | * register/unregister code and data | ||
522 | */ | ||
436 | static struct snd_pcm_ops snd_em28xx_pcm_capture = { | 523 | static struct snd_pcm_ops snd_em28xx_pcm_capture = { |
437 | .open = snd_em28xx_capture_open, | 524 | .open = snd_em28xx_capture_open, |
438 | .close = snd_em28xx_pcm_close, | 525 | .close = snd_em28xx_pcm_close, |
@@ -489,6 +576,22 @@ static int em28xx_audio_init(struct em28xx *dev) | |||
489 | 576 | ||
490 | INIT_WORK(&dev->wq_trigger, audio_trigger); | 577 | INIT_WORK(&dev->wq_trigger, audio_trigger); |
491 | 578 | ||
579 | if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { | ||
580 | em28xx_cvol_new(card, dev, "Video", AC97_VIDEO_VOL); | ||
581 | em28xx_cvol_new(card, dev, "Line In", AC97_LINEIN_VOL); | ||
582 | em28xx_cvol_new(card, dev, "Phone", AC97_PHONE_VOL); | ||
583 | em28xx_cvol_new(card, dev, "Microphone", AC97_PHONE_VOL); | ||
584 | em28xx_cvol_new(card, dev, "CD", AC97_CD_VOL); | ||
585 | em28xx_cvol_new(card, dev, "AUX", AC97_AUX_VOL); | ||
586 | em28xx_cvol_new(card, dev, "PCM", AC97_PCM_OUT_VOL); | ||
587 | |||
588 | em28xx_cvol_new(card, dev, "Master", AC97_MASTER_VOL); | ||
589 | em28xx_cvol_new(card, dev, "Line", AC97_LINE_LEVEL_VOL); | ||
590 | em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO_VOL); | ||
591 | em28xx_cvol_new(card, dev, "LFE", AC97_LFE_MASTER_VOL); | ||
592 | em28xx_cvol_new(card, dev, "Surround", AC97_SURR_MASTER_VOL); | ||
593 | } | ||
594 | |||
492 | err = snd_card_register(card); | 595 | err = snd_card_register(card); |
493 | if (err < 0) { | 596 | if (err < 0) { |
494 | snd_card_free(card); | 597 | snd_card_free(card); |