Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Driver for Infineon tua6100 pll.
0004  *
0005  * (c) 2006 Andrew de Quincey
0006  *
0007  * Based on code found in budget-av.c, which has the following:
0008  * Compiled from various sources by Michael Hunold <michael@mihu.de>
0009  *
0010  * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> &
0011  *                               Andrew de Quincey <adq_dvb@lidskialf.net>
0012  *
0013  * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
0014  *
0015  * Copyright (C) 1999-2002 Ralph  Metzler
0016  *                       & Marcus Metzler for convergence integrated media GmbH
0017  */
0018 
0019 #include <linux/slab.h>
0020 #include <linux/module.h>
0021 #include <linux/dvb/frontend.h>
0022 #include <asm/types.h>
0023 
0024 #include "tua6100.h"
0025 
0026 struct tua6100_priv {
0027     /* i2c details */
0028     int i2c_address;
0029     struct i2c_adapter *i2c;
0030     u32 frequency;
0031 };
0032 
0033 static void tua6100_release(struct dvb_frontend *fe)
0034 {
0035     kfree(fe->tuner_priv);
0036     fe->tuner_priv = NULL;
0037 }
0038 
0039 static int tua6100_sleep(struct dvb_frontend *fe)
0040 {
0041     struct tua6100_priv *priv = fe->tuner_priv;
0042     int ret;
0043     u8 reg0[] = { 0x00, 0x00 };
0044     struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 };
0045 
0046     if (fe->ops.i2c_gate_ctrl)
0047         fe->ops.i2c_gate_ctrl(fe, 1);
0048     if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
0049         printk("%s: i2c error\n", __func__);
0050     }
0051     if (fe->ops.i2c_gate_ctrl)
0052         fe->ops.i2c_gate_ctrl(fe, 0);
0053 
0054     return (ret == 1) ? 0 : ret;
0055 }
0056 
0057 static int tua6100_set_params(struct dvb_frontend *fe)
0058 {
0059     struct dtv_frontend_properties *c = &fe->dtv_property_cache;
0060     struct tua6100_priv *priv = fe->tuner_priv;
0061     u32 div;
0062     u32 prediv;
0063     u8 reg0[] = { 0x00, 0x00 };
0064     u8 reg1[] = { 0x01, 0x00, 0x00, 0x00 };
0065     u8 reg2[] = { 0x02, 0x00, 0x00 };
0066     struct i2c_msg msg0 = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 };
0067     struct i2c_msg msg1 = { .addr = priv->i2c_address, .flags = 0, .buf = reg1, .len = 4 };
0068     struct i2c_msg msg2 = { .addr = priv->i2c_address, .flags = 0, .buf = reg2, .len = 3 };
0069 
0070 #define _R_VAL 4
0071 #define _P_VAL 32
0072 #define _ri 4000000
0073 
0074     // setup register 0
0075     if (c->frequency < 2000000)
0076         reg0[1] = 0x03;
0077     else
0078         reg0[1] = 0x07;
0079 
0080     // setup register 1
0081     if (c->frequency < 1630000)
0082         reg1[1] = 0x2c;
0083     else
0084         reg1[1] = 0x0c;
0085 
0086     if (_P_VAL == 64)
0087         reg1[1] |= 0x40;
0088     if (c->frequency >= 1525000)
0089         reg1[1] |= 0x80;
0090 
0091     // register 2
0092     reg2[1] = (_R_VAL >> 8) & 0x03;
0093     reg2[2] = _R_VAL;
0094     if (c->frequency < 1455000)
0095         reg2[1] |= 0x1c;
0096     else if (c->frequency < 1630000)
0097         reg2[1] |= 0x0c;
0098     else
0099         reg2[1] |= 0x1c;
0100 
0101     /*
0102      * The N divisor ratio (note: c->frequency is in kHz, but we
0103      * need it in Hz)
0104      */
0105     prediv = (c->frequency * _R_VAL) / (_ri / 1000);
0106     div = prediv / _P_VAL;
0107     reg1[1] |= (div >> 9) & 0x03;
0108     reg1[2] = div >> 1;
0109     reg1[3] = (div << 7);
0110     priv->frequency = ((div * _P_VAL) * (_ri / 1000)) / _R_VAL;
0111 
0112     // Finally, calculate and store the value for A
0113     reg1[3] |= (prediv - (div*_P_VAL)) & 0x7f;
0114 
0115 #undef _R_VAL
0116 #undef _P_VAL
0117 #undef _ri
0118 
0119     if (fe->ops.i2c_gate_ctrl)
0120         fe->ops.i2c_gate_ctrl(fe, 1);
0121     if (i2c_transfer(priv->i2c, &msg0, 1) != 1)
0122         return -EIO;
0123 
0124     if (fe->ops.i2c_gate_ctrl)
0125         fe->ops.i2c_gate_ctrl(fe, 1);
0126     if (i2c_transfer(priv->i2c, &msg2, 1) != 1)
0127         return -EIO;
0128 
0129     if (fe->ops.i2c_gate_ctrl)
0130         fe->ops.i2c_gate_ctrl(fe, 1);
0131     if (i2c_transfer(priv->i2c, &msg1, 1) != 1)
0132         return -EIO;
0133 
0134     if (fe->ops.i2c_gate_ctrl)
0135         fe->ops.i2c_gate_ctrl(fe, 0);
0136 
0137     return 0;
0138 }
0139 
0140 static int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency)
0141 {
0142     struct tua6100_priv *priv = fe->tuner_priv;
0143     *frequency = priv->frequency;
0144     return 0;
0145 }
0146 
0147 static const struct dvb_tuner_ops tua6100_tuner_ops = {
0148     .info = {
0149         .name = "Infineon TUA6100",
0150         .frequency_min_hz  =  950 * MHz,
0151         .frequency_max_hz  = 2150 * MHz,
0152         .frequency_step_hz =    1 * MHz,
0153     },
0154     .release = tua6100_release,
0155     .sleep = tua6100_sleep,
0156     .set_params = tua6100_set_params,
0157     .get_frequency = tua6100_get_frequency,
0158 };
0159 
0160 struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c)
0161 {
0162     struct tua6100_priv *priv = NULL;
0163     u8 b1 [] = { 0x80 };
0164     u8 b2 [] = { 0x00 };
0165     struct i2c_msg msg [] = { { .addr = addr, .flags = 0, .buf = b1, .len = 1 },
0166                   { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } };
0167     int ret;
0168 
0169     if (fe->ops.i2c_gate_ctrl)
0170         fe->ops.i2c_gate_ctrl(fe, 1);
0171     ret = i2c_transfer (i2c, msg, 2);
0172     if (fe->ops.i2c_gate_ctrl)
0173         fe->ops.i2c_gate_ctrl(fe, 0);
0174 
0175     if (ret != 2)
0176         return NULL;
0177 
0178     priv = kzalloc(sizeof(struct tua6100_priv), GFP_KERNEL);
0179     if (priv == NULL)
0180         return NULL;
0181 
0182     priv->i2c_address = addr;
0183     priv->i2c = i2c;
0184 
0185     memcpy(&fe->ops.tuner_ops, &tua6100_tuner_ops, sizeof(struct dvb_tuner_ops));
0186     fe->tuner_priv = priv;
0187     return fe;
0188 }
0189 EXPORT_SYMBOL(tua6100_attach);
0190 
0191 MODULE_DESCRIPTION("DVB tua6100 driver");
0192 MODULE_AUTHOR("Andrew de Quincey");
0193 MODULE_LICENSE("GPL");