aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/em28xx
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2011-06-19 12:06:40 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-07-27 16:53:03 -0400
commit850d24a5a861238f583f59cd39de4dfe5142a4c9 (patch)
treee59192ee359e36dcb42b26a79ba00e804f8ba4a9 /drivers/media/video/em28xx
parent5b89ecf98998911f397fa913b06ee2304a373e54 (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')
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c103
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c2
2 files changed, 105 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 */
440static 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
453static 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
470err:
471 mutex_unlock(&dev->lock);
472 return rc;
473}
474
475static 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
493static const DECLARE_TLV_DB_SCALE(em28xx_db_scale, -3450, 150, 0);
494
495static 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 */
436static struct snd_pcm_ops snd_em28xx_pcm_capture = { 523static 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);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 7bf3a86902b0..752d4ed7f828 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -286,6 +286,7 @@ int em28xx_read_ac97(struct em28xx *dev, u8 reg)
286 return ret; 286 return ret;
287 return le16_to_cpu(val); 287 return le16_to_cpu(val);
288} 288}
289EXPORT_SYMBOL_GPL(em28xx_read_ac97);
289 290
290/* 291/*
291 * em28xx_write_ac97() 292 * em28xx_write_ac97()
@@ -313,6 +314,7 @@ int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val)
313 314
314 return 0; 315 return 0;
315} 316}
317EXPORT_SYMBOL_GPL(em28xx_write_ac97);
316 318
317struct em28xx_vol_itable { 319struct em28xx_vol_itable {
318 enum em28xx_amux mux; 320 enum em28xx_amux mux;