Commit 2474ed44 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

V4L/DVB (3582): Implement correct msp3400 input/output routing

- implement VIDIOC_INT_S_AUDIO_ROUTING for msp3400 and tvaudio
- use the new command in bttv, pvrusb2 and em28xx.
- remove the now obsolete MSP_SET_MATRIX from msp3400 (yeah!)
- remove the obsolete VIDIOC_S_AUDIO from msp3400.
Signed-off-by: default avatarHans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 49965a80
......@@ -37,6 +37,7 @@
#include "bttvp.h"
#include <media/v4l2-common.h>
#include <media/tvaudio.h>
#include <media/msp3400.h>
#include <linux/dma-mapping.h>
......@@ -934,11 +935,9 @@ static int
audio_mux(struct bttv *btv, int input, int mute)
{
int gpio_val, signal;
struct v4l2_audio aud_input;
struct v4l2_control ctrl;
struct i2c_client *c;
memset(&aud_input, 0, sizeof(aud_input));
gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
bttv_tvcards[btv->c.type].gpiomask);
signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
......@@ -953,7 +952,6 @@ audio_mux(struct bttv *btv, int input, int mute)
gpio_val = bttv_tvcards[btv->c.type].gpiomute;
else
gpio_val = bttv_tvcards[btv->c.type].gpiomux[input];
aud_input.index = btv->audio;
gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
if (bttv_gpio)
......@@ -962,15 +960,51 @@ audio_mux(struct bttv *btv, int input, int mute)
return 0;
ctrl.id = V4L2_CID_AUDIO_MUTE;
/* take automute into account, just btv->mute is not enough */
ctrl.value = mute;
ctrl.value = btv->mute;
bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, &ctrl);
c = btv->i2c_msp34xx_client;
if (c)
c->driver->command(c, VIDIOC_S_AUDIO, &aud_input);
if (c) {
struct v4l2_routing route;
/* Note: the inputs tuner/radio/extern/intern are translated
to msp routings. This assumes common behavior for all msp3400
based TV cards. When this assumption fails, then the
specific MSP routing must be added to the card table.
For now this is sufficient. */
switch (input) {
case TVAUDIO_INPUT_RADIO:
route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1,
MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART);
break;
case TVAUDIO_INPUT_EXTERN:
route.input = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1,
MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART);
break;
case TVAUDIO_INPUT_INTERN:
/* Yes, this is the same input as for RADIO. I doubt
if this is ever used. The only board with an INTERN
input is the BTTV_BOARD_AVERMEDIA98. I wonder how
that was tested. My guess is that the whole INTERN
input does not work. */
route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1,
MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART);
break;
case TVAUDIO_INPUT_TUNER:
default:
route.input = MSP_INPUT_DEFAULT;
break;
}
route.output = MSP_OUTPUT_DEFAULT;
c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route);
}
c = btv->i2c_tvaudio_client;
if (c)
c->driver->command(c, VIDIOC_S_AUDIO, &aud_input);
if (c) {
struct v4l2_routing route;
route.input = input;
route.output = 0;
c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route);
}
return 0;
}
......
......@@ -28,8 +28,9 @@
#include <linux/i2c.h>
#include <linux/usb.h>
#include <media/tuner.h>
#include <media/audiochip.h>
#include <media/msp3400.h>
#include <media/tveeprom.h>
#include <media/audiochip.h>
#include <media/v4l2-common.h>
#include "em28xx.h"
......@@ -146,11 +147,12 @@ struct em28xx_board em28xx_boards[] = {
.input = {{
.type = EM28XX_VMUX_TELEVISION,
.vmux = 0,
.amux = 6,
.amux = MSP_INPUT_DEFAULT,
},{
.type = EM28XX_VMUX_SVIDEO,
.vmux = 2,
.amux = 1,
.amux = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1,
MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART),
}},
},
[EM2820_BOARD_MSI_VOX_USB_2] = {
......
......@@ -38,6 +38,7 @@
#include "em28xx.h"
#include <media/tuner.h>
#include <media/v4l2-common.h>
#include <media/msp3400.h>
#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
"Markus Rechberger <mrechberger@gmail.com>, " \
......@@ -216,9 +217,14 @@ static void video_mux(struct em28xx *dev, int index)
em28xx_videodbg("Setting input index=%d, vmux=%d, amux=%d\n",index,input,dev->ctl_ainput);
if (dev->has_msp34xx) {
struct v4l2_routing route;
if (dev->i2s_speed)
em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed);
em28xx_i2c_call_clients(dev, VIDIOC_S_AUDIO, &dev->ctl_ainput);
route.input = dev->ctl_ainput;
route.output = MSP_OUTPUT(MSP_OUT_SCART1_DA);
/* Note: this is msp3400 specific */
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route);
ainput = EM28XX_AUDIO_SRC_TUNER;
em28xx_audio_source(dev, ainput);
} else {
......
......@@ -555,7 +555,6 @@ static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
struct msp_state *state = i2c_get_clientdata(client);
int scart = -1;
if (msp_debug >= 2)
v4l_i2c_print_ioctl(client, cmd);
......@@ -660,15 +659,6 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
break;
}
/* msp34xx specific */
case MSP_SET_MATRIX:
{
struct msp_matrix *mspm = arg;
msp_set_scart(client, mspm->input - 1, mspm->output);
break;
}
/* --- v4l2 ioctls --- */
case VIDIOC_S_STD:
{
......@@ -682,35 +672,37 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
return 0;
}
case VIDIOC_S_AUDIO:
case VIDIOC_INT_G_AUDIO_ROUTING:
{
struct v4l2_audio *sarg = arg;
struct v4l2_routing *rt = arg;
switch (sarg->index) {
case TVAUDIO_INPUT_RADIO:
/* Hauppauge uses IN2 for the radio */
state->mode = MSP_MODE_FM_RADIO;
scart = SCART_IN2;
break;
case TVAUDIO_INPUT_EXTERN:
/* IN1 is often used for external input ... */
state->mode = MSP_MODE_EXTERN;
scart = SCART_IN1;
break;
case TVAUDIO_INPUT_INTERN:
/* ... sometimes it is IN2 through ;) */
state->mode = MSP_MODE_EXTERN;
scart = SCART_IN2;
break;
case TVAUDIO_INPUT_TUNER:
state->mode = -1;
*rt = state->routing;
break;
}
if (scart >= 0) {
state->rxsubchans = V4L2_TUNER_SUB_STEREO;
msp_set_scart(client, scart, 0);
case VIDIOC_INT_S_AUDIO_ROUTING:
{
struct v4l2_routing *rt = arg;
int tuner = (rt->input >> 3) & 1;
int old_tuner = (state->routing.input >> 3) & 1;
int sc_in = rt->input & 0x7;
int sc1_out = rt->output & 0xf;
int sc2_out = (rt->output >> 4) & 0xf;
u16 val;
state->routing = *rt;
if (state->opmode == OPMODE_AUTOSELECT) {
val = msp_read_dem(client, 0x30) & ~0x100;
msp_write_dem(client, 0x30, val | (tuner ? 0x100 : 0));
} else {
val = msp_read_dem(client, 0xbb) & ~0x100;
msp_write_dem(client, 0xbb, val | (tuner ? 0x100 : 0));
}
msp_set_scart(client, sc_in, 0);
msp_set_scart(client, sc1_out, 1);
msp_set_scart(client, sc2_out, 2);
msp_set_audmode(client);
if (tuner != old_tuner)
msp_wake_thread(client);
break;
}
......@@ -941,6 +933,9 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind)
state->muted = 0;
state->i2s_mode = 0;
init_waitqueue_head(&state->wq);
/* These are the reset input/output positions */
state->routing.input = MSP_INPUT_DEFAULT;
state->routing.output = MSP_OUTPUT_DEFAULT;
state->rev1 = msp_read_dsp(client, 0x1e);
if (state->rev1 != -1)
......
......@@ -4,6 +4,8 @@
#ifndef MSP3400_DRIVER_H
#define MSP3400_DRIVER_H
#include <media/msp3400.h>
/* ---------------------------------------------------------------------- */
/* This macro is allowed for *constants* only, gcc must calculate it
......@@ -72,7 +74,7 @@ struct msp_state {
int i2s_mode;
int main, second; /* sound carrier */
int input;
int source; /* see msp34xxg_set_source */
struct v4l2_routing routing;
/* v4l2 */
int audmode;
......
......@@ -187,13 +187,14 @@ void msp3400c_set_mode(struct i2c_client *client, int mode)
{
struct msp_state *state = i2c_get_clientdata(client);
struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode];
int tuner = (state->routing.input >> 3) & 1;
int i;
v4l_dbg(1, msp_debug, client, "set_mode: %d\n", mode);
state->mode = mode;
state->rxsubchans = V4L2_TUNER_SUB_MONO;
msp_write_dem(client, 0x00bb, data->ad_cv);
msp_write_dem(client, 0x00bb, data->ad_cv | (tuner ? 0x100 : 0));
for (i = 5; i >= 0; i--) /* fir 1 */
msp_write_dem(client, 0x0001, data->fir1[i]);
......@@ -783,34 +784,6 @@ int msp3410d_thread(void *data)
* struct msp: only norm, acb and source are really used in this mode
*/
/* set the same 'source' for the loudspeaker, scart and quasi-peak detector
* the value for source is the same as bit 15:8 of DSP registers 0x08,
* 0x0a and 0x0c: 0=mono, 1=stereo or A|B, 2=SCART, 3=stereo or A, 4=stereo or B
*/
static void msp34xxg_set_source(struct i2c_client *client, int source)
{
struct msp_state *state = i2c_get_clientdata(client);
/* fix matrix mode to stereo and let the msp choose what
* to output according to 'source', as recommended
* for MONO (source==0) downmixing set bit[7:0] to 0x30
*/
int value = (source & 0x07) << 8 | (source == 0 ? 0x30 : 0x20);
v4l_dbg(1, msp_debug, client, "set source to %d (0x%x)\n", source, value);
msp_set_source(client, value);
/*
* set identification threshold. Personally, I
* I set it to a higher value that the default
* of 0x190 to ignore noisy stereo signals.
* this needs tuning. (recommended range 0x00a0-0x03c0)
* 0x7f0 = forced mono mode
*/
/* a2 threshold for stereo/bilingual */
msp_write_dem(client, 0x22, msp_stereo_thresh);
state->source = source;
}
static int msp34xxg_modus(struct i2c_client *client)
{
struct msp_state *state = i2c_get_clientdata(client);
......@@ -843,10 +816,65 @@ static int msp34xxg_modus(struct i2c_client *client)
return 0x0001;
}
static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in)
{
struct msp_state *state = i2c_get_clientdata(client);
int source, matrix;
switch (state->audmode) {
case V4L2_TUNER_MODE_MONO:
source = 0; /* mono only */
matrix = 0x30;
break;
case V4L2_TUNER_MODE_LANG1:
source = 3; /* stereo or A */
matrix = 0x00;
break;
case V4L2_TUNER_MODE_LANG2:
source = 4; /* stereo or B */
matrix = 0x10;
break;
case V4L2_TUNER_MODE_STEREO:
default:
source = 1; /* stereo or A|B */
matrix = 0x20;
break;
}
if (in == MSP_DSP_OUT_TUNER)
source = (source << 8) | 0x20;
/* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14
instead of 11, 12, 13. So we add one for that msp version. */
else if (in >= MSP_DSP_OUT_MAIN_AVC && state->has_dolby_pro_logic)
source = ((in + 1) << 8) | matrix;
else
source = (in << 8) | matrix;
v4l_dbg(1, msp_debug, client, "set source to %d (0x%x) for output %02x\n",
in, source, reg);
msp_write_dsp(client, reg, source);
}
static void msp34xxg_set_sources(struct i2c_client *client)
{
struct msp_state *state = i2c_get_clientdata(client);
u32 in = state->routing.input;
msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf);
/* quasi-peak detector is set to same input as the loudspeaker (MAIN) */
msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf);
msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf);
msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf);
if (state->has_scart23_in_scart2_out)
msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf);
msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf);
}
/* (re-)initialize the msp34xxg */
static void msp34xxg_reset(struct i2c_client *client)
{
struct msp_state *state = i2c_get_clientdata(client);
int tuner = (state->routing.input >> 3) & 1;
int modus;
/* initialize std to 1 (autodetect) to signal that no standard is
......@@ -864,11 +892,12 @@ static void msp34xxg_reset(struct i2c_client *client)
/* step-by-step initialisation, as described in the manual */
modus = msp34xxg_modus(client);
modus |= tuner ? 0x100 : 0;
msp_write_dem(client, 0x30, modus);
/* write the dsps that may have an influence on
standard/audio autodetection right now */
msp34xxg_set_source(client, state->source);
msp34xxg_set_sources(client);
msp_write_dsp(client, 0x0d, 0x1900); /* scart */
msp_write_dsp(client, 0x0e, 0x3000); /* FM */
......@@ -896,7 +925,6 @@ int msp34xxg_thread(void *data)
v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n");
state->source = 1; /* default */
for (;;) {
v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n");
msp_sleep(state, -1);
......@@ -993,7 +1021,6 @@ static int msp34xxg_detect_stereo(struct i2c_client *client)
static void msp34xxg_set_audmode(struct i2c_client *client)
{
struct msp_state *state = i2c_get_clientdata(client);
int source;
if (state->std == 0x20) {
if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) &&
......@@ -1005,25 +1032,7 @@ static void msp34xxg_set_audmode(struct i2c_client *client)
}
}
switch (state->audmode) {
case V4L2_TUNER_MODE_MONO:
source = 0; /* mono only */
break;
case V4L2_TUNER_MODE_STEREO:
source = 1; /* stereo or A|B, see comment in msp34xxg_get_v4l2_stereo() */
/* problem: that could also mean 2 (scart input) */
break;
case V4L2_TUNER_MODE_LANG1:
source = 3; /* stereo or A */
break;
case V4L2_TUNER_MODE_LANG2:
source = 4; /* stereo or B */
break;
default:
source = 1;
break;
}
msp34xxg_set_source(client, source);
msp34xxg_set_sources(client);
}
void msp_set_audmode(struct i2c_client *client)
......
......@@ -1682,6 +1682,30 @@ static int chip_command(struct i2c_client *client,
case VIDIOC_S_CTRL:
return tvaudio_set_ctrl(chip, arg);
case VIDIOC_INT_G_AUDIO_ROUTING:
{
struct v4l2_routing *rt = arg;
rt->input = chip->input;
rt->output = 0;
break;
}
case VIDIOC_INT_S_AUDIO_ROUTING:
{
struct v4l2_routing *rt = arg;
if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4)
return -EINVAL;
/* There are four inputs: tuner, radio, extern and intern. */
chip->input = rt->input;
if (chip->muted)
break;
chip_write_masked(chip, desc->inputreg,
desc->inputmap[chip->input], desc->inputmask);
break;
}
case VIDIOC_S_AUDIO:
{
struct v4l2_audio *sarg = arg;
......
......@@ -312,7 +312,6 @@ static const char *v4l2_int_ioctls[] = {
[_IOC_NR(DECODER_DUMP)] = "DECODER_DUMP",
#endif
[_IOC_NR(AUDC_SET_RADIO)] = "AUDC_SET_RADIO",
[_IOC_NR(MSP_SET_MATRIX)] = "MSP_SET_MATRIX",
[_IOC_NR(TUNER_SET_TYPE_ADDR)] = "TUNER_SET_TYPE_ADDR",
[_IOC_NR(TUNER_SET_STANDBY)] = "TUNER_SET_STANDBY",
......@@ -431,12 +430,6 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg)
printk ("%s: value=%d\n", s, *p);
break;
}
case MSP_SET_MATRIX:
{
struct msp_matrix *p=arg;
printk ("%s: input=%d, output=%d\n", s, p->input, p->output);
break;
}
case VIDIOC_G_AUDIO:
case VIDIOC_S_AUDIO:
case VIDIOC_ENUMAUDIO:
......@@ -465,7 +458,7 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg)
struct v4l2_buffer *p=arg;
struct v4l2_timecode *tc=&p->timecode;
printk ("%s: %02ld:%02d:%02d.%08ld index=%d, type=%s, "
"bytesused=%d, flags=0x%08d, "
"bytesused=%d, flags=0x%08x, "
"field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx\n",
s,
(p->timestamp.tv_sec/3600),
......@@ -479,7 +472,7 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg)
prt_names(p->memory,v4l2_memory_names),
p->m.userptr);
printk ("%s: timecode= %02d:%02d:%02d type=%d, "
"flags=0x%08d, frames=%d, userbits=0x%p",
"flags=0x%08x, frames=%d, userbits=0x%08p\n",
s,tc->hours,tc->minutes,tc->seconds,
tc->type, tc->flags, tc->frames, tc->userbits);
break;
......@@ -665,7 +658,7 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg)
case VIDIOC_INT_G_VIDEO_ROUTING:
{
struct v4l2_routing *p=arg;
printk ("%s: input=%d, output=%d\n", s, p->input, p->output);
printk ("%s: input=0x%x, output=0x%x\n", s, p->input, p->output);
break;
}
case VIDIOC_G_SLICED_VBI_CAP:
......
......@@ -145,9 +145,14 @@
MSP_DSP_TO_SCART1(sc_i2s_src) | \
MSP_DSP_TO_SCART2(sc_i2s_src) | \
MSP_DSP_TO_I2S(sc_i2s_src))
#define MSP_INPUT_DEFAULT MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1, \
MSP_DSP_OUT_TUNER, MSP_DSP_OUT_TUNER)
#define MSP_OUTPUT(sc) \
(MSP_OUT_TO_SCART1(sc) | \
MSP_OUT_TO_SCART2(sc))
/* This equals the RESET position of the msp3400 ACB register */
#define MSP_OUTPUT_DEFAULT (MSP_OUT_TO_SCART1(MSP_OUT_SCART3) | \
MSP_OUT_TO_SCART2(MSP_OUT_SCART1_DA))
/* Tuner inputs vs. msp version */
/* Chip TUNER_1 TUNER_2
......
......@@ -123,14 +123,6 @@ enum v4l2_chip_ident {
/* v4l device was opened in Radio mode, to be replaced by VIDIOC_INT_S_TUNER_MODE */
#define AUDC_SET_RADIO _IO('d',88)
/* msp3400 ioctl: will be removed in the near future, to be replaced by
VIDIOC_INT_S_AUDIO_ROUTING. */
struct msp_matrix {
int input;
int output;
};
#define MSP_SET_MATRIX _IOW('m',17,struct msp_matrix)
/* tuner ioctls */
/* Sets tuner type and its I2C addr */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment