Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // For Philips TEA5761 FM Chip
0003 // I2C address is always 0x20 (0x10 at 7-bit mode).
0004 //
0005 // Copyright (c) 2005-2007 Mauro Carvalho Chehab <mchehab@kernel.org>
0006 
0007 #include <linux/i2c.h>
0008 #include <linux/slab.h>
0009 #include <linux/delay.h>
0010 #include <linux/videodev2.h>
0011 #include <media/tuner.h>
0012 #include "tuner-i2c.h"
0013 #include "tea5761.h"
0014 
0015 static int debug;
0016 module_param(debug, int, 0644);
0017 MODULE_PARM_DESC(debug, "enable verbose debug messages");
0018 
0019 struct tea5761_priv {
0020     struct tuner_i2c_props i2c_props;
0021 
0022     u32 frequency;
0023     bool standby;
0024 };
0025 
0026 /*****************************************************************************/
0027 
0028 /***************************
0029  * TEA5761HN I2C registers *
0030  ***************************/
0031 
0032 /* INTREG - Read: bytes 0 and 1 / Write: byte 0 */
0033 
0034     /* first byte for reading */
0035 #define TEA5761_INTREG_IFFLAG       0x10
0036 #define TEA5761_INTREG_LEVFLAG      0x8
0037 #define TEA5761_INTREG_FRRFLAG      0x2
0038 #define TEA5761_INTREG_BLFLAG       0x1
0039 
0040     /* second byte for reading / byte for writing */
0041 #define TEA5761_INTREG_IFMSK        0x10
0042 #define TEA5761_INTREG_LEVMSK       0x8
0043 #define TEA5761_INTREG_FRMSK        0x2
0044 #define TEA5761_INTREG_BLMSK        0x1
0045 
0046 /* FRQSET - Read: bytes 2 and 3 / Write: byte 1 and 2 */
0047 
0048     /* First byte */
0049 #define TEA5761_FRQSET_SEARCH_UP 0x80       /* 1=Station search from botton to up */
0050 #define TEA5761_FRQSET_SEARCH_MODE 0x40     /* 1=Search mode */
0051 
0052     /* Bits 0-5 for divider MSB */
0053 
0054     /* Second byte */
0055     /* Bits 0-7 for divider LSB */
0056 
0057 /* TNCTRL - Read: bytes 4 and 5 / Write: Bytes 3 and 4 */
0058 
0059     /* first byte */
0060 
0061 #define TEA5761_TNCTRL_PUPD_0   0x40    /* Power UP/Power Down MSB */
0062 #define TEA5761_TNCTRL_BLIM 0X20    /* 1= Japan Frequencies, 0= European frequencies */
0063 #define TEA5761_TNCTRL_SWPM 0x10    /* 1= software port is FRRFLAG */
0064 #define TEA5761_TNCTRL_IFCTC    0x08    /* 1= IF count time 15.02 ms, 0= IF count time 2.02 ms */
0065 #define TEA5761_TNCTRL_AFM  0x04
0066 #define TEA5761_TNCTRL_SMUTE    0x02    /* 1= Soft mute */
0067 #define TEA5761_TNCTRL_SNC  0x01
0068 
0069     /* second byte */
0070 
0071 #define TEA5761_TNCTRL_MU   0x80    /* 1=Hard mute */
0072 #define TEA5761_TNCTRL_SSL_1    0x40
0073 #define TEA5761_TNCTRL_SSL_0    0x20
0074 #define TEA5761_TNCTRL_HLSI 0x10
0075 #define TEA5761_TNCTRL_MST  0x08    /* 1 = mono */
0076 #define TEA5761_TNCTRL_SWP  0x04
0077 #define TEA5761_TNCTRL_DTC  0x02    /* 1 = deemphasis 50 us, 0 = deemphasis 75 us */
0078 #define TEA5761_TNCTRL_AHLSI    0x01
0079 
0080 /* FRQCHECK - Read: bytes 6 and 7  */
0081     /* First byte */
0082 
0083     /* Bits 0-5 for divider MSB */
0084 
0085     /* Second byte */
0086     /* Bits 0-7 for divider LSB */
0087 
0088 /* TUNCHECK - Read: bytes 8 and 9  */
0089 
0090     /* First byte */
0091 #define TEA5761_TUNCHECK_IF_MASK    0x7e    /* IF count */
0092 #define TEA5761_TUNCHECK_TUNTO      0x01
0093 
0094     /* Second byte */
0095 #define TEA5761_TUNCHECK_LEV_MASK   0xf0    /* Level Count */
0096 #define TEA5761_TUNCHECK_LD     0x08
0097 #define TEA5761_TUNCHECK_STEREO     0x04
0098 
0099 /* TESTREG - Read: bytes 10 and 11 / Write: bytes 5 and 6 */
0100 
0101     /* All zero = no test mode */
0102 
0103 /* MANID - Read: bytes 12 and 13 */
0104 
0105     /* First byte - should be 0x10 */
0106 #define TEA5767_MANID_VERSION_MASK  0xf0    /* Version = 1 */
0107 #define TEA5767_MANID_ID_MSB_MASK   0x0f    /* Manufacurer ID - should be 0 */
0108 
0109     /* Second byte - Should be 0x2b */
0110 
0111 #define TEA5767_MANID_ID_LSB_MASK   0xfe    /* Manufacturer ID - should be 0x15 */
0112 #define TEA5767_MANID_IDAV      0x01    /* 1 = Chip has ID, 0 = Chip has no ID */
0113 
0114 /* Chip ID - Read: bytes 14 and 15 */
0115 
0116     /* First byte - should be 0x57 */
0117 
0118     /* Second byte - should be 0x61 */
0119 
0120 /*****************************************************************************/
0121 
0122 #define FREQ_OFFSET 0 /* for TEA5767, it is 700 to give the right freq */
0123 static void tea5761_status_dump(unsigned char *buffer)
0124 {
0125     unsigned int div, frq;
0126 
0127     div = ((buffer[2] & 0x3f) << 8) | buffer[3];
0128 
0129     frq = 1000 * (div * 32768 / 1000 + FREQ_OFFSET + 225) / 4;  /* Freq in KHz */
0130 
0131     printk(KERN_INFO "tea5761: Frequency %d.%03d KHz (divider = 0x%04x)\n",
0132            frq / 1000, frq % 1000, div);
0133 }
0134 
0135 /* Freq should be specifyed at 62.5 Hz */
0136 static int __set_radio_freq(struct dvb_frontend *fe,
0137                 unsigned int freq,
0138                 bool mono)
0139 {
0140     struct tea5761_priv *priv = fe->tuner_priv;
0141     unsigned int frq = freq;
0142     unsigned char buffer[7] = {0, 0, 0, 0, 0, 0, 0 };
0143     unsigned div;
0144     int rc;
0145 
0146     tuner_dbg("radio freq counter %d\n", frq);
0147 
0148     if (priv->standby) {
0149         tuner_dbg("TEA5761 set to standby mode\n");
0150         buffer[5] |= TEA5761_TNCTRL_MU;
0151     } else {
0152         buffer[4] |= TEA5761_TNCTRL_PUPD_0;
0153     }
0154 
0155 
0156     if (mono) {
0157         tuner_dbg("TEA5761 set to mono\n");
0158         buffer[5] |= TEA5761_TNCTRL_MST;
0159     } else {
0160         tuner_dbg("TEA5761 set to stereo\n");
0161     }
0162 
0163     div = (1000 * (frq * 4 / 16 + 700 + 225) ) >> 15;
0164     buffer[1] = (div >> 8) & 0x3f;
0165     buffer[2] = div & 0xff;
0166 
0167     if (debug)
0168         tea5761_status_dump(buffer);
0169 
0170     if (7 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 7)))
0171         tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
0172 
0173     priv->frequency = frq * 125 / 2;
0174 
0175     return 0;
0176 }
0177 
0178 static int set_radio_freq(struct dvb_frontend *fe,
0179               struct analog_parameters *params)
0180 {
0181     struct tea5761_priv *priv = fe->analog_demod_priv;
0182 
0183     priv->standby = false;
0184 
0185     return __set_radio_freq(fe, params->frequency,
0186                 params->audmode == V4L2_TUNER_MODE_MONO);
0187 }
0188 
0189 static int set_radio_sleep(struct dvb_frontend *fe)
0190 {
0191     struct tea5761_priv *priv = fe->analog_demod_priv;
0192 
0193     priv->standby = true;
0194 
0195     return __set_radio_freq(fe, priv->frequency, false);
0196 }
0197 
0198 static int tea5761_read_status(struct dvb_frontend *fe, char *buffer)
0199 {
0200     struct tea5761_priv *priv = fe->tuner_priv;
0201     int rc;
0202 
0203     memset(buffer, 0, 16);
0204     if (16 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 16))) {
0205         tuner_warn("i2c i/o error: rc == %d (should be 16)\n", rc);
0206         return -EREMOTEIO;
0207     }
0208 
0209     return 0;
0210 }
0211 
0212 static inline int tea5761_signal(struct dvb_frontend *fe, const char *buffer)
0213 {
0214     struct tea5761_priv *priv = fe->tuner_priv;
0215 
0216     int signal = ((buffer[9] & TEA5761_TUNCHECK_LEV_MASK) << (13 - 4));
0217 
0218     tuner_dbg("Signal strength: %d\n", signal);
0219 
0220     return signal;
0221 }
0222 
0223 static inline int tea5761_stereo(struct dvb_frontend *fe, const char *buffer)
0224 {
0225     struct tea5761_priv *priv = fe->tuner_priv;
0226 
0227     int stereo = buffer[9] & TEA5761_TUNCHECK_STEREO;
0228 
0229     tuner_dbg("Radio ST GET = %02x\n", stereo);
0230 
0231     return (stereo ? V4L2_TUNER_SUB_STEREO : 0);
0232 }
0233 
0234 static int tea5761_get_status(struct dvb_frontend *fe, u32 *status)
0235 {
0236     unsigned char buffer[16];
0237 
0238     *status = 0;
0239 
0240     if (0 == tea5761_read_status(fe, buffer)) {
0241         if (tea5761_signal(fe, buffer))
0242             *status = TUNER_STATUS_LOCKED;
0243         if (tea5761_stereo(fe, buffer))
0244             *status |= TUNER_STATUS_STEREO;
0245     }
0246 
0247     return 0;
0248 }
0249 
0250 static int tea5761_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
0251 {
0252     unsigned char buffer[16];
0253 
0254     *strength = 0;
0255 
0256     if (0 == tea5761_read_status(fe, buffer))
0257         *strength = tea5761_signal(fe, buffer);
0258 
0259     return 0;
0260 }
0261 
0262 int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr)
0263 {
0264     unsigned char buffer[16];
0265     int rc;
0266     struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr };
0267 
0268     if (16 != (rc = tuner_i2c_xfer_recv(&i2c, buffer, 16))) {
0269         printk(KERN_WARNING "it is not a TEA5761. Received %i chars.\n", rc);
0270         return -EINVAL;
0271     }
0272 
0273     if ((buffer[13] != 0x2b) || (buffer[14] != 0x57) || (buffer[15] != 0x061)) {
0274         printk(KERN_WARNING "Manufacturer ID= 0x%02x, Chip ID = %02x%02x. It is not a TEA5761\n",
0275                     buffer[13], buffer[14], buffer[15]);
0276         return -EINVAL;
0277     }
0278     printk(KERN_WARNING "tea5761: TEA%02x%02x detected. Manufacturer ID= 0x%02x\n",
0279                 buffer[14], buffer[15], buffer[13]);
0280 
0281     return 0;
0282 }
0283 
0284 static void tea5761_release(struct dvb_frontend *fe)
0285 {
0286     kfree(fe->tuner_priv);
0287     fe->tuner_priv = NULL;
0288 }
0289 
0290 static int tea5761_get_frequency(struct dvb_frontend *fe, u32 *frequency)
0291 {
0292     struct tea5761_priv *priv = fe->tuner_priv;
0293     *frequency = priv->frequency;
0294     return 0;
0295 }
0296 
0297 static const struct dvb_tuner_ops tea5761_tuner_ops = {
0298     .info = {
0299         .name           = "tea5761", // Philips TEA5761HN FM Radio
0300     },
0301     .set_analog_params = set_radio_freq,
0302     .sleep         = set_radio_sleep,
0303     .release           = tea5761_release,
0304     .get_frequency     = tea5761_get_frequency,
0305     .get_status        = tea5761_get_status,
0306     .get_rf_strength   = tea5761_get_rf_strength,
0307 };
0308 
0309 struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
0310                     struct i2c_adapter* i2c_adap,
0311                     u8 i2c_addr)
0312 {
0313     struct tea5761_priv *priv = NULL;
0314 
0315     if (tea5761_autodetection(i2c_adap, i2c_addr) != 0)
0316         return NULL;
0317 
0318     priv = kzalloc(sizeof(struct tea5761_priv), GFP_KERNEL);
0319     if (priv == NULL)
0320         return NULL;
0321     fe->tuner_priv = priv;
0322 
0323     priv->i2c_props.addr = i2c_addr;
0324     priv->i2c_props.adap = i2c_adap;
0325     priv->i2c_props.name = "tea5761";
0326 
0327     memcpy(&fe->ops.tuner_ops, &tea5761_tuner_ops,
0328            sizeof(struct dvb_tuner_ops));
0329 
0330     tuner_info("type set to %s\n", "Philips TEA5761HN FM Radio");
0331 
0332     return fe;
0333 }
0334 
0335 
0336 EXPORT_SYMBOL_GPL(tea5761_attach);
0337 EXPORT_SYMBOL_GPL(tea5761_autodetection);
0338 
0339 MODULE_DESCRIPTION("Philips TEA5761 FM tuner driver");
0340 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
0341 MODULE_LICENSE("GPL v2");