+/* ----------------------------------------------------------------------- */
+/* msp34xxG + (simpler no-thread) */
+/* this one uses both automatic standard detection and automatic sound */
+/* select which are available in the newer G versions */
+/* struct msp: only norm, acb and source are really used in this mode */
+
+static void msp34xxg_set_source(struct i2c_client *client, int source);
+
+/* (re-)initialize the msp34xxg, according to the current norm in msp->norm
+ * return 0 if it worked, -1 if it failed
+ */
+static int msp34xxg_init(struct i2c_client *client)
+{
+ struct msp3400c *msp = i2c_get_clientdata(client);
+ int modus,std;
+
+ if (msp3400c_reset(client))
+ return -1;
+
+ /* make sure that input/output is muted (paranoid mode) */
+ if (msp3400c_write(client,
+ I2C_MSP3400C_DFP,
+ 0x13, /* ACB */
+ 0x0f20 /* mute DSP input, mute SCART 1 */))
+ return -1;
+
+ /* step-by-step initialisation, as described in the manual */
+ modus = msp34xx_modus(msp->norm);
+ std = msp34xx_standard(msp->norm);
+ modus &= ~0x03; /* STATUS_CHANGE=0 */
+ modus |= 0x01; /* AUTOMATIC_SOUND_DETECTION=1 */
+ if (msp3400c_write(client,
+ I2C_MSP3400C_DEM,
+ 0x30/*MODUS*/,
+ modus))
+ return -1;
+ if (msp3400c_write(client,
+ I2C_MSP3400C_DEM,
+ 0x20/*stanard*/,
+ std))
+ return -1;
+
+ /* write the dfps that may have an influence on
+ standard/audio autodetection right now */
+ msp34xxg_set_source(client, msp->source);
+
+ if (msp3400c_write(client, I2C_MSP3400C_DFP,
+ 0x0e, /* AM/FM Prescale */
+ 0x3000 /* default: [15:8] 75khz deviation */))
+ return -1;
+
+ if (msp3400c_write(client, I2C_MSP3400C_DFP,
+ 0x10, /* NICAM Prescale */
+ 0x5a00 /* default: 9db gain (as recommended) */))
+ return -1;
+
+ if (msp3400c_write(client,
+ I2C_MSP3400C_DEM,
+ 0x20, /* STANDARD SELECT */
+ standard /* default: 0x01 for automatic standard select*/))
+ return -1;
+ return 0;
+}
+
+static int msp34xxg_thread(void *data)
+{
+ struct i2c_client *client = data;
+ struct msp3400c *msp = i2c_get_clientdata(client);
+ int val, std, i;
+
+ printk("msp34xxg: daemon started\n");
+ for (;;) {
+ d2printk(KERN_DEBUG "msp34xxg: thread: sleep\n");
+ msp34xx_sleep(msp,-1);
+ d2printk(KERN_DEBUG "msp34xxg: thread: wakeup\n");
+
+ restart:
+ dprintk("msp34xxg: thread: restart scan\n");
+ msp->restart = 0;
+ if (kthread_should_stop())
+ break;
+
+ /* setup the chip*/
+ msp34xxg_init(client);
+ std = standard;
+ if (std != 0x01)
+ goto unmute;
+
+ /* watch autodetect */
+ dprintk("msp34xxg: triggered autodetect, waiting for result\n");
+ for (i = 0; i < 10; i++) {
+ if (msp34xx_sleep(msp,100))
+ goto restart;
+
+ /* check results */
+ val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e);
+ if (val < 0x07ff) {
+ std = val;
+ break;
+ }
+ dprintk("msp34xxg: detection still in progress\n");
+ }
+ if (0x01 == std) {
+ dprintk("msp34xxg: detection still in progress after 10 tries. giving up.\n");
+ continue;
+ }
+
+ unmute:
+ dprintk("msp34xxg: current mode: %s (0x%04x)\n",
+ msp34xx_standard_mode_name(std), std);
+
+ /* unmute: dispatch sound to scart output, set scart volume */
+ dprintk("msp34xxg: unmute\n");
+
+ msp3400c_setbass(client, msp->bass);
+ msp3400c_settreble(client, msp->treble);
+ msp3400c_setvolume(client, msp->muted, msp->volume, msp->balance);
+
+ /* restore ACB */
+ if (msp3400c_write(client,
+ I2C_MSP3400C_DFP,
+ 0x13, /* ACB */
+ msp->acb))
+ return -1;
+ }
+ dprintk(KERN_DEBUG "msp34xxg: thread: exit\n");
+ return 0;
+}
+
+/* set the same 'source' for the loudspeaker, scart and quasi-peak detector
+ * the value for source is the same as bit 15:8 of DFP registers 0x08,
+ * 0x0a and 0x0c: 0=mono, 1=stereo or A|B, 2=SCART, 3=stereo or A, 4=stereo or B
+ *
+ * this function replaces msp3400c_setstereo
+ */
+static void msp34xxg_set_source(struct i2c_client *client, int source)
+{
+ struct msp3400c *msp = i2c_get_clientdata(client);
+
+ /* fix matrix mode to stereo and let the msp choose what
+ * to output according to 'source', as recommended
+ */
+ int value = (source&0x07)<<8|(source==0 ? 0x00:0x20);
+ dprintk("msp34xxg: set source to %d (0x%x)\n", source, value);
+ msp3400c_write(client,
+ I2C_MSP3400C_DFP,
+ 0x08, /* Loudspeaker Output */
+ value);
+ msp3400c_write(client,
+ I2C_MSP3400C_DFP,
+ 0x0a, /* SCART1 DA Output */
+ value);
+ msp3400c_write(client,
+ I2C_MSP3400C_DFP,
+ 0x0c, /* Quasi-peak detector */
+ 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
+ */
+ msp3400c_write(client,
+ I2C_MSP3400C_DEM,
+ 0x22, /* a2 threshold for stereo/bilingual */
+ source==0 ? 0x7f0:stereo_threshold);
+ msp->source=source;
+}
+
+static void msp34xxg_detect_stereo(struct i2c_client *client)
+{
+ struct msp3400c *msp = i2c_get_clientdata(client);
+
+ int status = msp3400c_read(client,
+ I2C_MSP3400C_DEM,
+ 0x0200 /* STATUS */);
+ int is_bilingual = status&0x100;
+ int is_stereo = status&0x40;
+
+ msp->rxsubchans = 0;
+ if (is_stereo)
+ msp->rxsubchans |= V4L2_TUNER_SUB_STEREO;
+ else
+ msp->rxsubchans |= V4L2_TUNER_SUB_MONO;
+ if (is_bilingual) {
+ msp->rxsubchans |= V4L2_TUNER_SUB_LANG1|V4L2_TUNER_SUB_LANG2;
+ /* I'm supposed to check whether it's SAP or not
+ * and set only LANG2/SAP in this case. Yet, the MSP
+ * does a lot of work to hide this and handle everything
+ * the same way. I don't want to work around it so unless
+ * this is a problem, I'll handle SAP just like lang1/lang2.
+ */
+ }
+ dprintk("msp34xxg: status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n",
+ status, is_stereo, is_bilingual, msp->rxsubchans);
+}
+
+static void msp34xxg_set_audmode(struct i2c_client *client, int audmode)
+{
+ struct msp3400c *msp = i2c_get_clientdata(client);
+ int source = 0;
+
+ switch (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: /* doing nothing: a safe, sane default */
+ audmode = 0;
+ return;
+ }
+ msp->audmode = audmode;
+ msp34xxg_set_source(client, source);
+}
+
+