#include <media/tvaudio.h>
#include <media/msp3400.h>
#include <linux/kthread.h>
-#include <linux/suspend.h>
+#include <linux/freezer.h>
#include "msp3400-driver.h"
/* ---------------------------------------------------------------------- */
}
/* ------------------------------------------------------------------------ */
-
+#ifdef CONFIG_VIDEO_V4L1
static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode)
{
if (rxsubchans == V4L2_TUNER_SUB_MONO)
return V4L2_TUNER_MODE_LANG1;
return V4L2_TUNER_MODE_MONO;
}
-
-static struct v4l2_queryctrl msp_qctrl_std[] = {
- {
- .id = V4L2_CID_AUDIO_VOLUME,
- .name = "Volume",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 58880,
- .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,
- },
-};
-
-static struct v4l2_queryctrl msp_qctrl_sound_processing[] = {
- {
- .id = V4L2_CID_AUDIO_BALANCE,
- .name = "Balance",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 32768,
- .flags = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_AUDIO_BASS,
- .name = "Bass",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 32768,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_AUDIO_TREBLE,
- .name = "Treble",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 32768,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_AUDIO_LOUDNESS,
- .name = "Loudness",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- .flags = 0,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },
-};
-
+#endif
static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
{
/* --- v4l ioctls --- */
/* take care: bttv does userspace copying, we'll get a
kernel pointer here... */
+#ifdef CONFIG_VIDEO_V4L1
case VIDIOCGAUDIO:
{
struct video_audio *va = arg;
}
case VIDIOCSFREQ:
+ {
+ /* new channel -- kick audio carrier scan */
+ msp_wake_thread(client);
+ break;
+ }
+#endif
case VIDIOC_S_FREQUENCY:
{
/* new channel -- kick audio carrier scan */
int sc1_out = rt->output & 0xf;
int sc2_out = (rt->output >> 4) & 0xf;
u16 val, reg;
+ int i;
+ int extern_input = 1;
if (state->routing.input == rt->input &&
state->routing.output == rt->output)
break;
state->routing = *rt;
+ /* check if the tuner input is used */
+ for (i = 0; i < 5; i++) {
+ if (((rt->input >> (4 + i * 4)) & 0xf) == 0)
+ extern_input = 0;
+ }
+ state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT;
+ state->rxsubchans = V4L2_TUNER_SUB_STEREO;
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);
reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
val = msp_read_dem(client, reg);
- if (tuner != ((val >> 8) & 1)) {
- msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
- /* wake thread when a new tuner input is chosen */
- msp_wake_thread(client);
- }
+ msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
+ /* wake thread when a new input is chosen */
+ msp_wake_thread(client);
break;
}
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *qc = arg;
- int i;
- for (i = 0; i < ARRAY_SIZE(msp_qctrl_std); i++)
- if (qc->id && qc->id == msp_qctrl_std[i].id) {
- memcpy(qc, &msp_qctrl_std[i], sizeof(*qc));
- return 0;
- }
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ break;
+ }
if (!state->has_sound_processing)
return -EINVAL;
- for (i = 0; i < ARRAY_SIZE(msp_qctrl_sound_processing); i++)
- if (qc->id && qc->id == msp_qctrl_sound_processing[i].id) {
- memcpy(qc, &msp_qctrl_sound_processing[i], sizeof(*qc));
- return 0;
- }
- return -EINVAL;
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_LOUDNESS:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ return -EINVAL;
+ }
}
case VIDIOC_G_CTRL:
case MSP_MODE_EXTERN: p = "External input"; break;
default: p = "unknown"; break;
}
- if (state->opmode == OPMODE_MANUAL) {
+ if (state->mode == MSP_MODE_EXTERN) {
+ v4l_info(client, "Mode: %s\n", p);
+ } else if (state->opmode == OPMODE_MANUAL) {
v4l_info(client, "Mode: %s (%s%s)\n", p,
(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
break;
}
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg, state->ident, (state->rev1 << 16) | state->rev2);
+
default:
/* unknown */
return -EINVAL;
return 0;
}
-static int msp_suspend(struct device * dev, pm_message_t state)
+static int msp_suspend(struct i2c_client *client, pm_message_t state)
{
- struct i2c_client *client = container_of(dev, struct i2c_client, dev);
v4l_dbg(1, msp_debug, client, "suspend\n");
msp_reset(client);
return 0;
}
-static int msp_resume(struct device * dev)
+static int msp_resume(struct i2c_client *client)
{
- struct i2c_client *client = container_of(dev, struct i2c_client, dev);
v4l_dbg(1, msp_debug, client, "resume\n");
msp_wake_thread(client);
if (msp_reset(client) == -1) {
v4l_dbg(1, msp_debug, client, "msp3400 not found\n");
kfree(client);
- return -1;
+ return 0;
}
state = kmalloc(sizeof(*state), GFP_KERNEL);
v4l_dbg(1, msp_debug, client, "not an msp3400 (cannot read chip version)\n");
kfree(state);
kfree(client);
- return -1;
+ return 0;
}
msp_set_audio(client);
snprintf(client->name, sizeof(client->name), "MSP%d4%02d%c-%c%d",
msp_family, msp_product,
msp_revision, msp_hard, msp_rom);
+ /* Rev B=2, C=3, D=4, G=7 */
+ state->ident = msp_family * 10000 + 4000 + msp_product * 10 + msp_revision - '@';
/* Has NICAM support: all mspx41x and mspx45x products have NICAM */
state->has_nicam = msp_prod_hi == 1 || msp_prod_hi == 5;
state->has_virtual_dolby_surround = msp_revision == 'G' && msp_prod_lo == 1;
/* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */
state->has_dolby_pro_logic = msp_revision == 'G' && msp_prod_lo == 2;
+ /* The msp343xG supports BTSC only and cannot do Automatic Standard Detection. */
+ state->force_btsc = msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3;
state->opmode = opmode;
if (state->opmode == OPMODE_AUTO) {
if (thread_func) {
state->kthread = kthread_run(thread_func, client, "msp34xx");
- if (state->kthread == NULL)
+ if (IS_ERR(state->kthread))
v4l_warn(client, "kernel_thread() failed\n");
msp_wake_thread(client);
}
.id = I2C_DRIVERID_MSP3400,
.attach_adapter = msp_probe,
.detach_client = msp_detach,
+ .suspend = msp_suspend,
+ .resume = msp_resume,
.command = msp_command,
.driver = {
.name = "msp3400",
- .suspend = msp_suspend,
- .resume = msp_resume,
},
};