diff -U 3 -H -w -d -r -N -- video.orig/cx88/cx88-alsa.c video/cx88/cx88-alsa.c --- video.orig/cx88/cx88-alsa.c 2008-02-11 06:51:11.000000000 +0100 +++ video/cx88/cx88-alsa.c 2008-12-31 16:41:15.000000000 +0100 @@ -583,6 +583,30 @@ int changed = 0; u32 old; + /* If a WM8775 is used for audio input utilise the audio controls */ + if ( core->board.audio_chip && core->board.audio_chip == AUDIO_CHIP_WM8775) { + struct v4l2_control client_ctl; + + if ( value->value.integer.value[0] >= value->value.integer.value[1]) { + v = value->value.integer.value[0] << 10; + b = value->value.integer.value[0] ? + (0x8000 * value->value.integer.value[1]) / value->value.integer.value[0] : + 0x8000; + } else { + v = value->value.integer.value[1] << 10; + b = value->value.integer.value[1] ? + 0xffff - (0x8000 * value->value.integer.value[0]) / value->value.integer.value[1] : + 0x8000; + } + client_ctl.value = v; + client_ctl.id = V4L2_CID_AUDIO_VOLUME; + cx88_call_i2c_clients(core, VIDIOC_S_CTRL, &client_ctl); + + client_ctl.value = b; + client_ctl.id = V4L2_CID_AUDIO_BALANCE; + cx88_call_i2c_clients(core, VIDIOC_S_CTRL, &client_ctl); + } + b = value->value.integer.value[1] - value->value.integer.value[0]; if (b < 0) { v = 0x3f - value->value.integer.value[0]; @@ -594,10 +618,10 @@ spin_lock_irq(&chip->reg_lock); old = cx_read(AUD_VOL_CTL); if (v != (old & 0x3f)) { - cx_write(AUD_VOL_CTL, (old & ~0x3f) | v); + cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v); changed = 1; } - if (cx_read(AUD_BAL_CTL) != b) { + if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) { cx_write(AUD_BAL_CTL, b); changed = 1; } @@ -612,7 +636,7 @@ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, - .name = "Playback Volume", + .name = "Tuner Volume", .info = snd_cx88_volume_info, .get = snd_cx88_volume_get, .put = snd_cx88_volume_put, @@ -643,7 +667,15 @@ 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); + + /* If a WM8775 is used for audio input utilise the audio controls */ + if ( (1<<6) == bit && core->board.audio_chip && core->board.audio_chip == AUDIO_CHIP_WM8775) { + struct v4l2_control client_ctl; + client_ctl.value = 0 == value->value.integer.value[0]; + client_ctl.id = V4L2_CID_AUDIO_MUTE; + cx88_call_i2c_clients(core, VIDIOC_S_CTRL, &client_ctl); + } ret = 1; } spin_unlock_irq(&chip->reg_lock); @@ -652,7 +684,7 @@ static 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, @@ -661,7 +693,7 @@ static struct snd_kcontrol_new snd_cx88_source_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Switch", + .name = "Tuner Switch", .info = snd_ctl_boolean_mono_info, .get = snd_cx88_switch_get, .put = snd_cx88_switch_put, @@ -819,10 +851,10 @@ err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip)); if (err < 0) goto error; - err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip)); + err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip)); if (err < 0) goto error; - err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip)); + err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip)); if (err < 0) goto error; diff -U 3 -H -w -d -r -N -- video.orig/wm8775.c video/wm8775.c --- video.orig/wm8775.c 2008-02-11 06:51:11.000000000 +0100 +++ video/wm8775.c 2009-01-01 13:35:30.000000000 +0100 @@ -55,6 +55,8 @@ struct wm8775_state { u8 input; /* Last selected input (0-0xf) */ u8 muted; + u16 volume; + u16 balance; }; static int wm8775_write(struct i2c_client *client, int reg, u16 val) @@ -76,6 +78,57 @@ return -1; } +static void wm8775_set_audio(struct i2c_client *client) +{ + struct wm8775_state *state = i2c_get_clientdata(client); + u8 vol_l, vol_r; + + /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */ + vol_l = ((min(65536 - state->balance, 32768) * state->volume) / 32768) >> 8; + vol_r = ((min(state->balance, (u16)32768) * state->volume) / 32768) >> 8; + + /* Mute */ + wm8775_write(client, R21, 0x0c0); + + wm8775_write(client, R14, vol_l); + wm8775_write(client, R15, vol_r); + + /* Un-mute */ + if (!state->muted) + wm8775_write(client, R21, state->input); +} + +static struct v4l2_queryctrl wm8775_qctrl[] = { + { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 0xCF00, /* 0 dB */ + .flags = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, { + .id = V4L2_CID_AUDIO_BALANCE, + .name = "Balance", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .flags = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + static int wm8775_command(struct i2c_client *client, unsigned int cmd, void *arg) { @@ -100,37 +153,65 @@ return -EINVAL; } state->input = route->input; - if (state->muted) - break; - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - wm8775_write(client, R21, 0x100 + state->input); + wm8775_set_audio(client); break; case VIDIOC_G_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: ctrl->value = state->muted; break; - case VIDIOC_S_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = state->volume; + break; + + case V4L2_CID_AUDIO_BALANCE: + ctrl->value = state->balance; + break; + + default: return -EINVAL; + } + break; + + case VIDIOC_S_CTRL: + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: state->muted = ctrl->value; - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - if (!state->muted) - wm8775_write(client, R21, 0x100 + state->input); break; - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8775, 0); + case V4L2_CID_AUDIO_VOLUME: + state->volume = ctrl->value; + break; + + case V4L2_CID_AUDIO_BALANCE: + state->balance = ctrl->value; + break; + + default: + return -EINVAL; + } + wm8775_set_audio(client); + break; + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(wm8775_qctrl); i++) + if (qc->id && qc->id == wm8775_qctrl[i].id) { + memcpy(qc, &wm8775_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; + } case VIDIOC_LOG_STATUS: - v4l_info(client, "Input: %d%s\n", state->input, - state->muted ? " (muted)" : ""); + v4l_info(client, "Volume: %04x%s Balance: %04x Input: %d\n", + state->volume, state->muted ? " (muted)" : "", + state->balance, state->input); break; case VIDIOC_S_FREQUENCY: @@ -138,10 +219,7 @@ sound the first time I tune from static to a valid channel. It's difficult to reproduce and is almost certainly related to the zero cross detect circuit. */ - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - wm8775_write(client, R21, 0x100 + state->input); + wm8775_set_audio(client); break; default: @@ -188,26 +266,24 @@ } state->input = 2; state->muted = 0; + state->volume = 0xCF00; + state->balance = 0x8000; i2c_set_clientdata(client, state); /* initialize wm8775 */ wm8775_write(client, R23, 0x000); /* RESET */ - wm8775_write(client, R7, 0x000); /* Disable zero cross detect timeout */ - wm8775_write(client, R11, 0x021); /* Left justified, 24-bit mode */ + /*wm8775_write(client, R7, 0x000); /* Enable zero cross detect timeout */ + wm8775_write(client, R11, 0x022); /* HPF enable, I2S mode, 24-bit */ wm8775_write(client, R12, 0x102); /* Master mode, clock ratio 256fs */ wm8775_write(client, R13, 0x000); /* Powered up */ - wm8775_write(client, R14, 0x1d4); /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(client, R15, 0x1d4); /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(client, R16, 0x1bf); /* ALC Stereo, ALC target level -1dB FS */ - /* max gain +8dB */ - wm8775_write(client, R17, 0x185); /* Enable gain control, use zero cross */ - /* detection, ALC hold time 42.6 ms */ - wm8775_write(client, R18, 0x0a2); /* ALC gain ramp up delay 34 s, */ - /* ALC gain ramp down delay 33 ms */ + wm8775_set_audio(client); /* set volume/mute */ +#if 0 /* Enable ALC */ + wm8775_write(client, R16, 0x1bb); /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */ + wm8775_write(client, R17, 0x185); /* Enable LC, use zero cross, ALC hold 42.7 ms */ + wm8775_write(client, R18, 0x0a2); /* ALC decay time 34 s, ALC attack time 33 ms */ wm8775_write(client, R19, 0x005); /* Enable noise gate, threshold -72dBfs */ - wm8775_write(client, R20, 0x07a); /* Transient window 4ms, lower PGA gain */ - /* limit -1dB */ - wm8775_write(client, R21, 0x102); /* LRBOTH = 1, use input 2. */ + wm8775_write(client, R20, 0x0fb); /* Transient window 4ms, ALC min gain -5dB */ +#endif i2c_attach_client(client); return 0;