📄 msp3400.c
字号:
msp3400c_print_mode(msp); /* monitor tv audio mode */ while (msp->watch_stereo) { if (msp34xx_sleep(msp,5000)) goto restart; watch_stereo(client); } } dprintk(KERN_DEBUG "msp3400: thread: exit\n"); return 0;}/* ----------------------------------------------------------------------- *//* this one uses the automatic sound standard detection of newer *//* msp34xx chip versions */static struct MODES { int retval; int main, second; char *name;} modelist[] = { { 0x0000, 0, 0, "ERROR" }, { 0x0001, 0, 0, "autodetect start" }, { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" }, { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" }, { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" }, { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" }, { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" }, { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" }, { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" }, { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" }, { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" }, { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" }, { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" }, { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" }, { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" }, { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" }, { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" }, { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" }, { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, { -1, 0, 0, NULL }, /* EOF */};static inline const char *msp34xx_standard_mode_name(int mode){ int i; for (i = 0; modelist[i].name != NULL; i++) if (modelist[i].retval == mode) return modelist[i].name; return "unknown";}static int msp34xx_modus(int norm){ switch (norm) { case VIDEO_MODE_PAL: return 0x1003; case VIDEO_MODE_NTSC: /* BTSC */ return 0x2003; case VIDEO_MODE_SECAM: return 0x0003; case VIDEO_MODE_RADIO: return 0x0003; case VIDEO_MODE_AUTO: return 0x2003; default: return 0x0003; }}static int msp34xx_standard(int norm){ switch (norm) { case VIDEO_MODE_PAL: return 1; case VIDEO_MODE_NTSC: /* BTSC */ return 0x0020; case VIDEO_MODE_SECAM: return 1; case VIDEO_MODE_RADIO: return 0x0040; default: return 1; }}static int msp3410d_thread(void *data){ struct i2c_client *client = data; struct msp3400c *msp = i2c_get_clientdata(client); int mode,val,i,std; printk("msp3410: daemon started\n"); for (;;) { d2printk(KERN_DEBUG "msp3410: thread: sleep\n"); msp34xx_sleep(msp,-1); d2printk(KERN_DEBUG "msp3410: thread: wakeup\n"); restart: dprintk("msp3410: thread: restart scan\n"); msp->restart = 0; if (kthread_should_stop()) break; if (msp->mode == MSP_MODE_EXTERN) { /* no carrier scan needed, just unmute */ dprintk(KERN_DEBUG "msp3410: thread: no carrier scan\n"); msp3400c_setvolume(client, msp->muted, msp->volume, msp->balance); continue; } /* put into sane state (and mute) */ msp3400c_reset(client); /* some time for the tuner to sync */ if (msp34xx_sleep(msp,200)) goto restart; /* start autodetect */ mode = msp34xx_modus(msp->norm); std = msp34xx_standard(msp->norm); msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode); msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std); msp->watch_stereo = 0; if (debug) printk(KERN_DEBUG "msp3410: setting mode: %s (0x%04x)\n", msp34xx_standard_mode_name(std) ,std); if (std != 1) { /* programmed some specific mode */ val = std; } else { /* triggered autodetect */ for (;;) { if (msp34xx_sleep(msp,100)) goto restart; /* check results */ val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); if (val < 0x07ff) break; dprintk(KERN_DEBUG "msp3410: detection still in progress\n"); } } for (i = 0; modelist[i].name != NULL; i++) if (modelist[i].retval == val) break; dprintk(KERN_DEBUG "msp3410: current mode: %s (0x%04x)\n", modelist[i].name ? modelist[i].name : "unknown", val); msp->main = modelist[i].main; msp->second = modelist[i].second; if (amsound && (msp->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) { /* autodetection has failed, let backup */ dprintk(KERN_DEBUG "msp3410: autodetection failed," " switching to backup mode: %s (0x%04x)\n", modelist[8].name ? modelist[8].name : "unknown",val); val = 0x0009; msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val); } /* set various prescales */ msp3400c_write(client, I2C_MSP3400C_DFP, 0x0d, 0x1900); /* scart */ msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */ msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* nicam */ /* set stereo */ switch (val) { case 0x0008: /* B/G NICAM */ case 0x000a: /* I NICAM */ if (val == 0x0008) msp->mode = MSP_MODE_FM_NICAM1; else msp->mode = MSP_MODE_FM_NICAM2; /* just turn on stereo */ msp->rxsubchans = V4L2_TUNER_SUB_STEREO; msp->nicam_on = 1; msp->watch_stereo = 1; msp3400c_set_audmode(client,V4L2_TUNER_MODE_STEREO); break; case 0x0009: msp->mode = MSP_MODE_AM_NICAM; msp->rxsubchans = V4L2_TUNER_SUB_MONO; msp->nicam_on = 1; msp3400c_set_audmode(client,V4L2_TUNER_MODE_MONO); msp->watch_stereo = 1; break; case 0x0020: /* BTSC */ /* just turn on stereo */ msp->mode = MSP_MODE_BTSC; msp->rxsubchans = V4L2_TUNER_SUB_STEREO; msp->nicam_on = 0; msp->watch_stereo = 1; msp3400c_set_audmode(client,V4L2_TUNER_MODE_STEREO); break; case 0x0040: /* FM radio */ msp->mode = MSP_MODE_FM_RADIO; msp->rxsubchans = V4L2_TUNER_SUB_STEREO; msp->audmode = V4L2_TUNER_MODE_STEREO; msp->nicam_on = 0; msp->watch_stereo = 0; /* not needed in theory if HAVE_RADIO(), but short programming enables carrier mute */ msp3400c_setmode(client,MSP_MODE_FM_RADIO); msp3400c_setcarrier(client, MSP_CARRIER(10.7), MSP_CARRIER(10.7)); /* scart routing */ msp3400c_set_scart(client,SCART_IN2,0);#if 0 /* radio from SCART_IN2 */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0220); msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0220); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0220);#else /* msp34xx does radio decoding */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0020); msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0020); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0020);#endif break; case 0x0003: case 0x0004: case 0x0005: msp->mode = MSP_MODE_FM_TERRA; msp->rxsubchans = V4L2_TUNER_SUB_MONO; msp->audmode = V4L2_TUNER_MODE_MONO; msp->nicam_on = 0; msp->watch_stereo = 1; break; } /* unmute, restore misc registers */ msp3400c_setbass(client, msp->bass); msp3400c_settreble(client, msp->treble); msp3400c_setvolume(client, msp->muted, msp->volume, msp->balance); msp3400c_write(client, I2C_MSP3400C_DFP, 0x0013, msp->acb); /* monitor tv audio mode */ while (msp->watch_stereo) { if (msp34xx_sleep(msp,5000)) goto restart; watch_stereo(client); } } dprintk(KERN_DEBUG "msp3410: thread: exit\n"); return 0;}/* ----------------------------------------------------------------------- *//* 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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -