Skip to content

Commit

Permalink
[media] Add proper audio support for Nova-S Plus with wm8775 ADC
Browse files Browse the repository at this point in the history
This patch adds audio DMA capture and ALSA mixer elements for the line
input jack of the Hauppauge Nova-S-plus DVB-S PCI card.  The Nova-S-plus
has a WM8775 ADC that is currently not detected.  This patch enables
this chip and exports volume, balance mute and ALC elements for ALSA
mixer controls.

[mchehab@redhat.com: Changed the patch to only talk with wm8775 if board
info says so. Also, added platform_data support, to avoid changing the
behaviour for other boards, and fixed CodingStyle]

[awalls@md.metrocast.net: Changed patch to make the WM8775_GID defintion
bridge driver private and let the bridge driver set the value of
v4l2_subdev.grp_id.]

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
  • Loading branch information
Lawrence Rust authored and Mauro Carvalho Chehab committed Mar 21, 2011
1 parent 724dcbf commit 6951803
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 62 deletions.
118 changes: 103 additions & 15 deletions drivers/media/video/cx88/cx88-alsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <sound/control.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <media/wm8775.h>

#include "cx88.h"
#include "cx88-reg.h"
Expand Down Expand Up @@ -577,6 +578,35 @@ static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol,
return 0;
}

static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
struct v4l2_control client_ctl;
int left = value->value.integer.value[0];
int right = value->value.integer.value[1];
int v, b;

memset(&client_ctl, 0, sizeof(client_ctl));

/* Pass volume & balance onto any WM8775 */
if (left >= right) {
v = left << 10;
b = left ? (0x8000 * right) / left : 0x8000;
} else {
v = right << 10;
b = right ? 0xffff - (0x8000 * left) / right : 0x8000;
}
client_ctl.value = v;
client_ctl.id = V4L2_CID_AUDIO_VOLUME;
call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);

client_ctl.value = b;
client_ctl.id = V4L2_CID_AUDIO_BALANCE;
call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
}

/* OK - TODO: test it */
static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
Expand All @@ -587,25 +617,28 @@ static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
int changed = 0;
u32 old;

if (core->board.audio_chip == V4L2_IDENT_WM8775)
snd_cx88_wm8775_volume_put(kcontrol, value);

left = value->value.integer.value[0] & 0x3f;
right = value->value.integer.value[1] & 0x3f;
b = right - left;
if (b < 0) {
v = 0x3f - left;
b = (-b) | 0x40;
v = 0x3f - left;
b = (-b) | 0x40;
} else {
v = 0x3f - right;
v = 0x3f - right;
}
/* Do we really know this will always be called with IRQs on? */
spin_lock_irq(&chip->reg_lock);
old = cx_read(AUD_VOL_CTL);
if (v != (old & 0x3f)) {
cx_write(AUD_VOL_CTL, (old & ~0x3f) | v);
changed = 1;
cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v);
changed = 1;
}
if (cx_read(AUD_BAL_CTL) != b) {
cx_write(AUD_BAL_CTL, b);
changed = 1;
if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) {
cx_write(AUD_BAL_CTL, b);
changed = 1;
}
spin_unlock_irq(&chip->reg_lock);

Expand All @@ -618,7 +651,7 @@ static const struct snd_kcontrol_new snd_cx88_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.name = "Playback Volume",
.name = "Analog-TV Volume",
.info = snd_cx88_volume_info,
.get = snd_cx88_volume_get,
.put = snd_cx88_volume_put,
Expand Down Expand Up @@ -649,7 +682,17 @@ static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,
vol = cx_read(AUD_VOL_CTL);
if (value->value.integer.value[0] != !(vol & bit)) {
vol ^= bit;
cx_write(AUD_VOL_CTL, vol);
cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol);
/* Pass mute onto any WM8775 */
if ((core->board.audio_chip == V4L2_IDENT_WM8775) &&
((1<<6) == bit)) {
struct v4l2_control client_ctl;

memset(&client_ctl, 0, sizeof(client_ctl));
client_ctl.value = 0 != (vol & bit);
client_ctl.id = V4L2_CID_AUDIO_MUTE;
call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
}
ret = 1;
}
spin_unlock_irq(&chip->reg_lock);
Expand All @@ -658,7 +701,7 @@ static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,

static const struct snd_kcontrol_new snd_cx88_dac_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Playback Switch",
.name = "Audio-Out Switch",
.info = snd_ctl_boolean_mono_info,
.get = snd_cx88_switch_get,
.put = snd_cx88_switch_put,
Expand All @@ -667,13 +710,51 @@ static const struct snd_kcontrol_new snd_cx88_dac_switch = {

static const struct snd_kcontrol_new snd_cx88_source_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Switch",
.name = "Analog-TV Switch",
.info = snd_ctl_boolean_mono_info,
.get = snd_cx88_switch_get,
.put = snd_cx88_switch_put,
.private_value = (1<<6),
};

static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
struct v4l2_control client_ctl;

memset(&client_ctl, 0, sizeof(client_ctl));
client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
call_hw(core, WM8775_GID, core, g_ctrl, &client_ctl);
value->value.integer.value[0] = client_ctl.value ? 1 : 0;

return 0;
}

static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
struct v4l2_control client_ctl;

memset(&client_ctl, 0, sizeof(client_ctl));
client_ctl.value = 0 != value->value.integer.value[0];
client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);

return 0;
}

static struct snd_kcontrol_new snd_cx88_alc_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line-In ALC Switch",
.info = snd_ctl_boolean_mono_info,
.get = snd_cx88_alc_get,
.put = snd_cx88_alc_put,
};

/****************************************************************************
Basic Flow for Sound Devices
****************************************************************************/
Expand Down Expand Up @@ -724,7 +805,8 @@ static void snd_cx88_dev_free(struct snd_card * card)
static int devno;
static int __devinit snd_cx88_create(struct snd_card *card,
struct pci_dev *pci,
snd_cx88_card_t **rchip)
snd_cx88_card_t **rchip,
struct cx88_core **core_ptr)
{
snd_cx88_card_t *chip;
struct cx88_core *core;
Expand All @@ -750,7 +832,7 @@ static int __devinit snd_cx88_create(struct snd_card *card,
if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) {
dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name);
err = -EIO;
cx88_core_put(core,pci);
cx88_core_put(core, pci);
return err;
}

Expand Down Expand Up @@ -786,6 +868,7 @@ static int __devinit snd_cx88_create(struct snd_card *card,
snd_card_set_dev(card, &pci->dev);

*rchip = chip;
*core_ptr = core;

return 0;
}
Expand All @@ -795,6 +878,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci,
{
struct snd_card *card;
snd_cx88_card_t *chip;
struct cx88_core *core;
int err;

if (devno >= SNDRV_CARDS)
Expand All @@ -812,7 +896,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci,

card->private_free = snd_cx88_dev_free;

err = snd_cx88_create(card, pci, &chip);
err = snd_cx88_create(card, pci, &chip, &core);
if (err < 0)
goto error;

Expand All @@ -830,6 +914,10 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci,
if (err < 0)
goto error;

/* If there's a wm8775 then add a Line-In ALC switch */
if (core->board.audio_chip == V4L2_IDENT_WM8775)
snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip));

strcpy (card->driver, "CX88x");
sprintf(card->shortname, "Conexant CX%x", pci->device);
sprintf(card->longname, "%s at %#llx",
Expand Down
3 changes: 2 additions & 1 deletion drivers/media/video/cx88/cx88-cards.c
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,8 @@ static const struct cx88_board cx88_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.audio_chip = V4L2_IDENT_WM8775,
.audio_chip = V4L2_IDENT_WM8775,
.i2sinputcntl = 2,
.input = {{
.type = CX88_VMUX_DVB,
.vmux = 0,
Expand Down
8 changes: 6 additions & 2 deletions drivers/media/video/cx88/cx88-tvaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -786,8 +786,12 @@ void cx88_set_tvaudio(struct cx88_core *core)
break;
case WW_I2SADC:
set_audio_start(core, 0x01);
/* Slave/Philips/Autobaud */
cx_write(AUD_I2SINPUTCNTL, 0);
/*
* Slave/Philips/Autobaud
* NB on Nova-S bit1 NPhilipsSony appears to be inverted:
* 0= Sony, 1=Philips
*/
cx_write(AUD_I2SINPUTCNTL, core->board.i2sinputcntl);
/* Switch to "I2S ADC mode" */
cx_write(AUD_I2SCNTL, 0x1);
set_audio_finish(core, EN_I2SIN_ENABLE);
Expand Down
52 changes: 48 additions & 4 deletions drivers/media/video/cx88/cx88-video.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "cx88.h"
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/wm8775.h>

MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
Expand Down Expand Up @@ -989,6 +990,32 @@ int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl)
ctl->value = c->v.minimum;
if (ctl->value > c->v.maximum)
ctl->value = c->v.maximum;

/* Pass changes onto any WM8775 */
if (core->board.audio_chip == V4L2_IDENT_WM8775) {
struct v4l2_control client_ctl;
memset(&client_ctl, 0, sizeof(client_ctl));
client_ctl.id = ctl->id;

switch (ctl->id) {
case V4L2_CID_AUDIO_MUTE:
client_ctl.value = ctl->value;
break;
case V4L2_CID_AUDIO_VOLUME:
client_ctl.value = (ctl->value) ?
(0x90 + ctl->value) << 8 : 0;
break;
case V4L2_CID_AUDIO_BALANCE:
client_ctl.value = ctl->value << 9;
break;
default:
client_ctl.id = 0;
break;
}
if (client_ctl.id)
call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
}

mask=c->mask;
switch (ctl->id) {
case V4L2_CID_AUDIO_BALANCE:
Expand Down Expand Up @@ -1526,7 +1553,9 @@ static int radio_queryctrl (struct file *file, void *priv,
if (c->id < V4L2_CID_BASE ||
c->id >= V4L2_CID_LASTP1)
return -EINVAL;
if (c->id == V4L2_CID_AUDIO_MUTE) {
if (c->id == V4L2_CID_AUDIO_MUTE ||
c->id == V4L2_CID_AUDIO_VOLUME ||
c->id == V4L2_CID_AUDIO_BALANCE) {
for (i = 0; i < CX8800_CTLS; i++) {
if (cx8800_ctls[i].v.id == c->id)
break;
Expand Down Expand Up @@ -1856,9 +1885,24 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,

/* load and configure helper modules */

if (core->board.audio_chip == V4L2_IDENT_WM8775)
v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
"wm8775", 0x36 >> 1, NULL);
if (core->board.audio_chip == V4L2_IDENT_WM8775) {
struct i2c_board_info wm8775_info = {
.type = "wm8775",
.addr = 0x36 >> 1,
.platform_data = &core->wm8775_data,
};
struct v4l2_subdev *sd;

if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1)
core->wm8775_data.is_nova_s = true;
else
core->wm8775_data.is_nova_s = false;

sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap,
&wm8775_info, NULL);
if (sd != NULL)
sd->grp_id = WM8775_GID;
}

if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
/* This probes for a tda9874 as is used on some
Expand Down
13 changes: 11 additions & 2 deletions drivers/media/video/cx88/cx88.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <media/cx2341x.h>
#include <media/videobuf-dvb.h>
#include <media/ir-kbd-i2c.h>
#include <media/wm8775.h>

#include "btcx-risc.h"
#include "cx88-reg.h"
Expand Down Expand Up @@ -273,6 +274,9 @@ struct cx88_board {
enum cx88_board_type mpeg;
unsigned int audio_chip;
int num_frontends;

/* Used for I2S devices */
int i2sinputcntl;
};

struct cx88_subid {
Expand Down Expand Up @@ -379,6 +383,7 @@ struct cx88_core {

/* I2C remote data */
struct IR_i2c_init_data init_data;
struct wm8775_platform_data wm8775_data;

struct mutex lock;
/* various v4l controls */
Expand All @@ -398,17 +403,21 @@ static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
return container_of(v4l2_dev, struct cx88_core, v4l2_dev);
}

#define call_all(core, o, f, args...) \
#define WM8775_GID (1 << 0)

#define call_hw(core, grpid, o, f, args...) \
do { \
if (!core->i2c_rc) { \
if (core->gate_ctrl) \
core->gate_ctrl(core, 1); \
v4l2_device_call_all(&core->v4l2_dev, 0, o, f, ##args); \
v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \
if (core->gate_ctrl) \
core->gate_ctrl(core, 0); \
} \
} while (0)

#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args)

struct cx8800_dev;
struct cx8802_dev;

Expand Down
Loading

0 comments on commit 6951803

Please sign in to comment.