📄 msp3400.c
字号:
case VIDEO_MODE_PAL: msp3400_dbg("video mode selected to PAL\n");#if 1 /* experimental: not sure this works with all chip versions */ return 0x7003;#else /* previous value, try this if it breaks ... */ return 0x1003;#endif case VIDEO_MODE_NTSC: /* BTSC */ msp3400_dbg("video mode selected to NTSC\n"); return 0x2003; case VIDEO_MODE_SECAM: msp3400_dbg("video mode selected to SECAM\n"); return 0x0003; case VIDEO_MODE_RADIO: msp3400_dbg("video mode selected to Radio\n"); return 0x0003; case VIDEO_MODE_AUTO: msp3400_dbg("video mode selected to Auto\n"); 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; msp3400_info("msp3410 daemon started\n"); for (;;) { msp3400_dbg_mediumvol("msp3410 thread: sleep\n"); msp34xx_sleep(msp,-1); msp3400_dbg_mediumvol("msp3410 thread: wakeup\n"); restart: msp3400_dbg("thread: restart scan\n"); msp->restart = 0; if (kthread_should_stop()) break; if (msp->mode == MSP_MODE_EXTERN) { /* no carrier scan needed, just unmute */ msp3400_dbg("thread: no carrier scan\n"); msp3400c_setvolume(client, msp->muted, msp->left, msp->right); 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(client, 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) msp3400_dbg("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; msp3400_dbg("detection still in progress\n"); } } for (i = 0; modelist[i].name != NULL; i++) if (modelist[i].retval == val) break; msp3400_dbg("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 */ msp3400_dbg("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_setstereo(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_setstereo(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_setstereo(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); /* 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); 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->left, msp->right); msp3400c_write(client, I2C_MSP3400C_DFP, 0x13, msp->acb); msp3400c_write(client,I2C_MSP3400C_DEM, 0x40, msp->i2s_mode); msp3400c_restore_dfp(client); /* monitor tv audio mode */ while (msp->watch_stereo) { if (msp34xx_sleep(msp,5000)) goto restart; watch_stereo(client); } } msp3400_dbg("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_reset(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; msp3400c_write(client,I2C_MSP3400C_DEM, 0x40, msp->i2s_mode); /* step-by-step initialisation, as described in the manual */ modus = msp34xx_modus(client, 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/*standard*/, 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_dfp_with_default(client, 0x0e, /* AM/FM Prescale */ 0x3000 /* default: [15:8] 75khz deviation */ )) return -1; if (msp3400c_write_dfp_with_default(client, 0x10, /* NICAM Prescale */ 0x5a00 /* default: 9db gain (as recommended) */ )) 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; msp3400_info("msp34xxg daemon started\n"); msp->source = 1; /* default */ for (;;) { msp3400_dbg_mediumvol("msp34xxg thread: sleep\n"); msp34xx_sleep(msp,-1); msp3400_dbg_mediumvol("msp34xxg thread: wakeup\n"); restart: msp3400_dbg("thread: restart scan\n"); msp->restart = 0; if (kthread_should_stop()) break; /* setup the chip*/ msp34xxg_reset(client); std = standard; if (std != 0x01) goto unmute; /* watch autodetect */ msp3400_dbg("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; } msp3400_dbg("detection still in progress\n"); } if (0x01 == std) { msp3400_dbg("detection still in progress after 10 tries. giving up.\n"); continue; } unmute: msp3400_dbg("current mode: %s (0x%04x)\n", msp34xx_standard_mode_name(std), std); /* unmute: dispatch sound to scart output, set scart volume */ msp3400_dbg("unmute\n"); msp3400c_setbass(client, msp->bass); msp3400c_settreble(client, msp->treble); msp3400c_setvolume(client, msp->muted, msp->left, msp->right); /* restore ACB */ if (msp3400c_write(client, I2C_MSP3400C_DFP, 0x13, /* ACB */ msp->acb)) return -1; msp3400c_write(client,I2C_MSP3400C_DEM, 0x40, msp->i2s_mode); } msp3400_dbg("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 * for MONO (source==0) downmixing set bit[7:0] to 0x30 */ int value = (source&0x07)<<8|(source==0 ? 0x30:0x20); msp3400_dbg("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 */ 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. */ } msp3400_dbg("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; 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: audmode = 0; source = 1; break; } msp->audmode = audmode; msp34xxg_set_source(client, source);}/* ----------------------------------------------------------------------- */static int msp_attach(struct i2c_adapter *adap, int addr, int kind);static int msp_detach(struct i2c_client *client);static int msp_probe(struct i2c_adapter *adap);static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg);static int msp_suspend(struct device * dev, pm_message_t state);static int msp_resume(struct device * dev);static void msp_wake_thread(struct i2c_client *client);static struct i2c_driver driver = { .owner = THIS_MODULE, .name = "msp3400", .id = I2C_DRIVERID_MSP3400, .flags = I2C_DF_NOTIFY, .attach_adapter = msp_probe, .detach_client = msp_detach, .command = msp_command, .driver = { .suspend = msp_suspend, .resume = msp_resume, },};static struct i2c_client client_template ={ .name = "(unset)", .flags = I2C_CLIENT_ALLOW_USE, .driver = &driver,};static int msp_attach(struct i2c_adapter *adap, int addr, int kind){ struct msp3400c *msp; struct i2c_client *client = &client_template; int (*thread_func)(void *data) = NULL; int i; client_template.adapter = adap; client_template.addr = addr; if (-1 == msp3400c_reset(&client_template)) { msp3400_dbg("no chip found\n"); return -1; } if (NULL == (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) return -ENOMEM; memcpy(client,&client_template,sizeof(struct i2c_client)); if (NULL == (msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL))) { kfree(client); return -ENOMEM; } memset(msp,0,sizeof(struct msp3400c)); msp->norm = VIDEO_MODE_NTSC; msp->left = 58880; /* 0db gain */ msp->right = 58880; /* 0db gain */ msp->bass = 32768; msp->treble = 32768; msp->input = -1; msp->muted = 0; msp->i2s_mode = 0; for (i = 0; i < DFP_COUNT; i++) msp->dfp_regs[i] = -1; i2c_set_clientdata(client, msp); init_waitqueue_head(&msp->wq); if (-1 == msp3400c_reset(client)) { kfree(msp); kfree(client); msp3400_dbg("no chip found\n"); return -1; } msp->rev1 = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1e); if (-1 != msp->rev1) msp->rev2 = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1f); if ((-1 == msp->rev1) || (0 == msp->rev1 && 0 == msp->rev2)) { kfree(msp); kfree(client); msp3400_dbg("error while reading chip version\n"); return -1; } msp3400_dbg("rev1=0x%04x, rev2=0x%04x\n", msp->rev1, msp->rev2); msp3400c_setvolume(client, msp->muted, msp->left, msp->right); snprintf(client->name, sizeof(client->name), "MSP%c4%02d%c-%c%d", ((msp->rev1>>4)&0x0f) + '3', (msp->rev2>>8)&0xff, (msp->rev1&0x0f)+'@', ((msp->rev1>>8)&0xff)+'@', msp->rev2&0x1f); msp->opmode = opmode; if (OPMODE_AUTO == msp->opmode) { if (HAVE_SIMPLER(msp)) msp->opmode = OPMODE_SIMPLER; else if (HAVE_SIMPLE(msp)) msp->opmode = OPMODE_SIMPLE; else msp->opmode = OPMODE_MANUAL; } /* hello world :-) */ msp3400_info("chip=%s", client->name); if (HAVE_NICAM(msp)) printk(" +nicam"); if (HAVE_SIMPLE(msp)) printk(" +simple"); if (HAVE_SIMPLER(msp)) printk(" +simpler"); if (HAVE_RADIO(msp)) printk(" +radio"); /* version-specific initialization */ switch (msp->opmode) { case OPMODE_MANUAL: printk(" mode=manual"); thread_func = msp3400c_thread; break; case OPMODE_SIMPLE:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -