📄 tuner-simple.c
字号:
/* * i2c tv tuner chip device driver * controls all those simple 4-control-bytes style tuners. * * This "tuner-simple" module was split apart from the original "tuner" module. */#include <linux/delay.h>#include <linux/i2c.h>#include "compat.h"#include <linux/videodev2.h>#include <media/tuner.h>#include <media/v4l2-common.h>#include <media/tuner-types.h>#include "tuner-i2c.h"#include "tuner-simple.h"static int debug;module_param(debug, int, 0644);MODULE_PARM_DESC(debug, "enable verbose debug messages");#define TUNER_SIMPLE_MAX 64static unsigned int simple_devcount;static int offset;module_param(offset, int, 0664);MODULE_PARM_DESC(offset, "Allows to specify an offset for tuner");static unsigned int atv_input[TUNER_SIMPLE_MAX] = \ { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 };static unsigned int dtv_input[TUNER_SIMPLE_MAX] = \ { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 };module_param_array(atv_input, int, NULL, 0644);module_param_array(dtv_input, int, NULL, 0644);MODULE_PARM_DESC(atv_input, "specify atv rf input, 0 for autoselect");MODULE_PARM_DESC(dtv_input, "specify dtv rf input, 0 for autoselect");/* ---------------------------------------------------------------------- *//* tv standard selection for Temic 4046 FM5 this value takes the low bits of control byte 2 from datasheet Rev.01, Feb.00 standard BG I L L2 D picture IF 38.9 38.9 38.9 33.95 38.9 sound 1 33.4 32.9 32.4 40.45 32.4 sound 2 33.16 NICAM 33.05 32.348 33.05 33.05 */#define TEMIC_SET_PAL_I 0x05#define TEMIC_SET_PAL_DK 0x09#define TEMIC_SET_PAL_L 0x0a /* SECAM ? */#define TEMIC_SET_PAL_L2 0x0b /* change IF ! */#define TEMIC_SET_PAL_BG 0x0c/* tv tuner system standard selection for Philips FQ1216ME this value takes the low bits of control byte 2 from datasheet "1999 Nov 16" (supersedes "1999 Mar 23") standard BG DK I L L` picture carrier 38.90 38.90 38.90 38.90 33.95 colour 34.47 34.47 34.47 34.47 38.38 sound 1 33.40 32.40 32.90 32.40 40.45 sound 2 33.16 - - - - NICAM 33.05 33.05 32.35 33.05 39.80 */#define PHILIPS_SET_PAL_I 0x01 /* Bit 2 always zero !*/#define PHILIPS_SET_PAL_BGDK 0x09#define PHILIPS_SET_PAL_L2 0x0a#define PHILIPS_SET_PAL_L 0x0b/* system switching for Philips FI1216MF MK2 from datasheet "1996 Jul 09", standard BG L L' picture carrier 38.90 38.90 33.95 colour 34.47 34.37 38.38 sound 1 33.40 32.40 40.45 sound 2 33.16 - - NICAM 33.05 33.05 39.80 */#define PHILIPS_MF_SET_STD_BG 0x01 /* Bit 2 must be zero, Bit 3 is system output */#define PHILIPS_MF_SET_STD_L 0x03 /* Used on Secam France */#define PHILIPS_MF_SET_STD_LC 0x02 /* Used on SECAM L' *//* Control byte */#define TUNER_RATIO_MASK 0x06 /* Bit cb1:cb2 */#define TUNER_RATIO_SELECT_50 0x00#define TUNER_RATIO_SELECT_32 0x02#define TUNER_RATIO_SELECT_166 0x04#define TUNER_RATIO_SELECT_62 0x06#define TUNER_CHARGE_PUMP 0x40 /* Bit cb6 *//* Status byte */#define TUNER_POR 0x80#define TUNER_FL 0x40#define TUNER_MODE 0x38#define TUNER_AFC 0x07#define TUNER_SIGNAL 0x07#define TUNER_STEREO 0x10#define TUNER_PLL_LOCKED 0x40#define TUNER_STEREO_MK3 0x04static DEFINE_MUTEX(tuner_simple_list_mutex);static LIST_HEAD(hybrid_tuner_instance_list);struct tuner_simple_priv { unsigned int nr; u16 last_div; struct tuner_i2c_props i2c_props; struct list_head hybrid_tuner_instance_list; unsigned int type; struct tunertype *tun; u32 frequency; u32 bandwidth;};/* ---------------------------------------------------------------------- */static int tuner_read_status(struct dvb_frontend *fe){ struct tuner_simple_priv *priv = fe->tuner_priv; unsigned char byte; if (1 != tuner_i2c_xfer_recv(&priv->i2c_props, &byte, 1)) return 0; return byte;}static inline int tuner_signal(const int status){ return (status & TUNER_SIGNAL) << 13;}static inline int tuner_stereo(const int type, const int status){ switch (type) { case TUNER_PHILIPS_FM1216ME_MK3: case TUNER_PHILIPS_FM1236_MK3: case TUNER_PHILIPS_FM1256_IH3: case TUNER_LG_NTSC_TAPE: case TUNER_TCL_MF02GIP_5N: return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3); default: return status & TUNER_STEREO; }}static inline int tuner_islocked(const int status){ return (status & TUNER_FL);}static inline int tuner_afcstatus(const int status){ return (status & TUNER_AFC) - 2;}#if 0 /* unused */static inline int tuner_mode(const int status){ return (status & TUNER_MODE) >> 3;}#endifstatic int simple_get_status(struct dvb_frontend *fe, u32 *status){ struct tuner_simple_priv *priv = fe->tuner_priv; int tuner_status; if (priv->i2c_props.adap == NULL) return -EINVAL; tuner_status = tuner_read_status(fe); *status = 0; if (tuner_islocked(tuner_status)) *status = TUNER_STATUS_LOCKED; if (tuner_stereo(priv->type, tuner_status)) *status |= TUNER_STATUS_STEREO; tuner_dbg("AFC Status: %d\n", tuner_afcstatus(tuner_status)); return 0;}static int simple_get_rf_strength(struct dvb_frontend *fe, u16 *strength){ struct tuner_simple_priv *priv = fe->tuner_priv; int signal; if (priv->i2c_props.adap == NULL) return -EINVAL; signal = tuner_signal(tuner_read_status(fe)); *strength = signal; tuner_dbg("Signal strength: %d\n", signal); return 0;}/* ---------------------------------------------------------------------- */static inline char *tuner_param_name(enum param_type type){ char *name; switch (type) { case TUNER_PARAM_TYPE_RADIO: name = "radio"; break; case TUNER_PARAM_TYPE_PAL: name = "pal"; break; case TUNER_PARAM_TYPE_SECAM: name = "secam"; break; case TUNER_PARAM_TYPE_NTSC: name = "ntsc"; break; case TUNER_PARAM_TYPE_DIGITAL: name = "digital"; break; default: name = "unknown"; break; } return name;}static struct tuner_params *simple_tuner_params(struct dvb_frontend *fe, enum param_type desired_type){ struct tuner_simple_priv *priv = fe->tuner_priv; struct tunertype *tun = priv->tun; int i; for (i = 0; i < tun->count; i++) if (desired_type == tun->params[i].type) break; /* use default tuner params if desired_type not available */ if (i == tun->count) { tuner_dbg("desired params (%s) undefined for tuner %d\n", tuner_param_name(desired_type), priv->type); i = 0; } tuner_dbg("using tuner params #%d (%s)\n", i, tuner_param_name(tun->params[i].type)); return &tun->params[i];}static int simple_config_lookup(struct dvb_frontend *fe, struct tuner_params *t_params, unsigned *frequency, u8 *config, u8 *cb){ struct tuner_simple_priv *priv = fe->tuner_priv; int i; for (i = 0; i < t_params->count; i++) { if (*frequency > t_params->ranges[i].limit) continue; break; } if (i == t_params->count) { tuner_dbg("frequency out of range (%d > %d)\n", *frequency, t_params->ranges[i - 1].limit); *frequency = t_params->ranges[--i].limit; } *config = t_params->ranges[i].config; *cb = t_params->ranges[i].cb; tuner_dbg("freq = %d.%02d (%d), range = %d, " "config = 0x%02x, cb = 0x%02x\n", *frequency / 16, *frequency % 16 * 100 / 16, *frequency, i, *config, *cb); return i;}/* ---------------------------------------------------------------------- */static void simple_set_rf_input(struct dvb_frontend *fe, u8 *config, u8 *cb, unsigned int rf){ struct tuner_simple_priv *priv = fe->tuner_priv; switch (priv->type) { case TUNER_PHILIPS_TUV1236D: switch (rf) { case 1: *cb |= 0x08; break; default: *cb &= ~0x08; break; } break; case TUNER_PHILIPS_FCV1236D: switch (rf) { case 1: *cb |= 0x01; break; default: *cb &= ~0x01; break; } break; default: break; }}static int simple_std_setup(struct dvb_frontend *fe, struct analog_parameters *params, u8 *config, u8 *cb){ struct tuner_simple_priv *priv = fe->tuner_priv; u8 tuneraddr; int rc; /* tv norm specific stuff for multi-norm tuners */ switch (priv->type) { case TUNER_PHILIPS_SECAM: /* FI1216MF */ /* 0x01 -> ??? no change ??? */ /* 0x02 -> PAL BDGHI / SECAM L */ /* 0x04 -> ??? PAL others / SECAM others ??? */ *cb &= ~0x03; if (params->std & V4L2_STD_SECAM_L) /* also valid for V4L2_STD_SECAM */ *cb |= PHILIPS_MF_SET_STD_L; else if (params->std & V4L2_STD_SECAM_LC) *cb |= PHILIPS_MF_SET_STD_LC; else /* V4L2_STD_B|V4L2_STD_GH */ *cb |= PHILIPS_MF_SET_STD_BG; break; case TUNER_TEMIC_4046FM5: *cb &= ~0x0f; if (params->std & V4L2_STD_PAL_BG) { *cb |= TEMIC_SET_PAL_BG; } else if (params->std & V4L2_STD_PAL_I) { *cb |= TEMIC_SET_PAL_I; } else if (params->std & V4L2_STD_PAL_DK) { *cb |= TEMIC_SET_PAL_DK; } else if (params->std & V4L2_STD_SECAM_L) { *cb |= TEMIC_SET_PAL_L; } break; case TUNER_PHILIPS_FQ1216ME: *cb &= ~0x0f; if (params->std & (V4L2_STD_PAL_BG|V4L2_STD_PAL_DK)) { *cb |= PHILIPS_SET_PAL_BGDK; } else if (params->std & V4L2_STD_PAL_I) { *cb |= PHILIPS_SET_PAL_I; } else if (params->std & V4L2_STD_SECAM_L) { *cb |= PHILIPS_SET_PAL_L; } break; case TUNER_PHILIPS_FCV1236D: /* 0x00 -> ATSC antenna input 1 */ /* 0x01 -> ATSC antenna input 2 */ /* 0x02 -> NTSC antenna input 1 */ /* 0x03 -> NTSC antenna input 2 */ *cb &= ~0x03; if (!(params->std & V4L2_STD_ATSC)) *cb |= 2; break; case TUNER_MICROTUNE_4042FI5: /* Set the charge pump for fast tuning */ *config |= TUNER_CHARGE_PUMP; break; case TUNER_PHILIPS_TUV1236D: { /* 0x40 -> ATSC antenna input 1 */ /* 0x48 -> ATSC antenna input 2 */ /* 0x00 -> NTSC antenna input 1 */ /* 0x08 -> NTSC antenna input 2 */ u8 buffer[4] = { 0x14, 0x00, 0x17, 0x00}; *cb &= ~0x40; if (params->std & V4L2_STD_ATSC) { *cb |= 0x40; buffer[1] = 0x04; } /* set to the correct mode (analog or digital) */ tuneraddr = priv->i2c_props.addr; priv->i2c_props.addr = 0x0a; rc = tuner_i2c_xfer_send(&priv->i2c_props, &buffer[0], 2); if (2 != rc) tuner_warn("i2c i/o error: rc == %d " "(should be 2)\n", rc); rc = tuner_i2c_xfer_send(&priv->i2c_props, &buffer[2], 2); if (2 != rc) tuner_warn("i2c i/o error: rc == %d " "(should be 2)\n", rc); priv->i2c_props.addr = tuneraddr; break; } } if (atv_input[priv->nr]) simple_set_rf_input(fe, config, cb, atv_input[priv->nr]); return 0;}static int simple_post_tune(struct dvb_frontend *fe, u8 *buffer, u16 div, u8 config, u8 cb){ struct tuner_simple_priv *priv = fe->tuner_priv; int rc; switch (priv->type) { case TUNER_LG_TDVS_H06XF: /* Set the Auxiliary Byte. */#if 0 buffer[2] &= ~0x20; buffer[2] |= 0x18; buffer[3] = 0x20; tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", buffer[0], buffer[1], buffer[2], buffer[3]); rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); if (4 != rc) tuner_warn("i2c i/o error: rc == %d " "(should be 4)\n", rc);#else buffer[0] = buffer[2]; buffer[0] &= ~0x20; buffer[0] |= 0x18; buffer[1] = 0x20; tuner_dbg("tv 0x%02x 0x%02x\n", buffer[0], buffer[1]); rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 2); if (2 != rc) tuner_warn("i2c i/o error: rc == %d " "(should be 2)\n", rc);#endif break; case TUNER_MICROTUNE_4042FI5: { /* FIXME - this may also work for other tuners */ unsigned long timeout = jiffies + msecs_to_jiffies(1); u8 status_byte = 0; /* Wait until the PLL locks */ for (;;) { if (time_after(jiffies, timeout)) return 0; rc = tuner_i2c_xfer_recv(&priv->i2c_props, &status_byte, 1); if (1 != rc) { tuner_warn("i2c i/o read error: rc == %d " "(should be 1)\n", rc); break; } if (status_byte & TUNER_PLL_LOCKED) break; udelay(10); } /* Set the charge pump for optimized phase noise figure */ config &= ~TUNER_CHARGE_PUMP; buffer[0] = (div>>8) & 0x7f; buffer[1] = div & 0xff; buffer[2] = config; buffer[3] = cb; tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", buffer[0], buffer[1], buffer[2], buffer[3]); rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); if (4 != rc) tuner_warn("i2c i/o error: rc == %d " "(should be 4)\n", rc); break; } } return 0;}static int simple_radio_bandswitch(struct dvb_frontend *fe, u8 *buffer){ struct tuner_simple_priv *priv = fe->tuner_priv; switch (priv->type) { case TUNER_TENA_9533_DI: case TUNER_YMEC_TVF_5533MF: tuner_dbg("This tuner doesn't have FM. " "Most cards have a TEA5767 for FM\n"); return 0; case TUNER_PHILIPS_FM1216ME_MK3: case TUNER_PHILIPS_FM1236_MK3: case TUNER_PHILIPS_FMD1216ME_MK3: case TUNER_PHILIPS_FMD1216MEX_MK3: case TUNER_LG_NTSC_TAPE: case TUNER_PHILIPS_FM1256_IH3: case TUNER_TCL_MF02GIP_5N: buffer[3] = 0x19; break; case TUNER_TNF_5335MF: buffer[3] = 0x11; break; case TUNER_LG_PAL_FM: buffer[3] = 0xa5; break; case TUNER_THOMSON_DTT761X: buffer[3] = 0x39; break; case TUNER_MICROTUNE_4049FM5: default: buffer[3] = 0xa4; break; } return 0;}/* ---------------------------------------------------------------------- */static int simple_set_tv_freq(struct dvb_frontend *fe, struct analog_parameters *params){ struct tuner_simple_priv *priv = fe->tuner_priv; u8 config, cb; u16 div; struct tunertype *tun; u8 buffer[4]; int rc, IFPCoff, i; enum param_type desired_type; struct tuner_params *t_params; tun = priv->tun; /* IFPCoff = Video Intermediate Frequency - Vif: 940 =16*58.75 NTSC/J (Japan) 732 =16*45.75 M/N STD 704 =16*44 ATSC (at DVB code) 632 =16*39.50 I U.K. 622.4=16*38.90 B/G D/K I, L STD 592 =16*37.00 D China 590 =16.36.875 B Australia 543.2=16*33.95 L' STD 171.2=16*10.70 FM Radio (at set_radio_freq) */ if (params->std == V4L2_STD_NTSC_M_JP) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -