diff options
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); |