V4L/DVB (4846): Create new lgh06xf atsc tuner module
[linux-2.6.git] / drivers / media / dvb / frontends / lgh06xf.c
1 /*
2  *  lgh06xf.c - ATSC Tuner support for LG TDVS-H06xF
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include "dvb-pll.h"
20 #include "lgh06xf.h"
21
22 #define LG_H06XF_PLL_I2C_ADDR 0x61
23
24 struct lgh06xf_priv {
25         struct i2c_adapter *i2c;
26         u32 frequency;
27 };
28
29 static int lgh06xf_release(struct dvb_frontend *fe)
30 {
31         kfree(fe->tuner_priv);
32         fe->tuner_priv = NULL;
33         return 0;
34 }
35
36 static int lgh06xf_set_params(struct dvb_frontend* fe,
37                               struct dvb_frontend_parameters* params)
38 {
39         struct lgh06xf_priv *priv = fe->tuner_priv;
40         u8 buf[4];
41         struct i2c_msg msg = { .addr = LG_H06XF_PLL_I2C_ADDR, .flags = 0,
42                                .buf = buf, .len = sizeof(buf) };
43         u32 div;
44         int i;
45         int err;
46
47         dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, params->frequency, 0);
48         if (fe->ops.i2c_gate_ctrl)
49                 fe->ops.i2c_gate_ctrl(fe, 1);
50         if ((err = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
51                 printk(KERN_WARNING "lgh06xf: %s error "
52                        "(addr %02x <- %02x, err = %i)\n",
53                        __FUNCTION__, buf[0], buf[1], err);
54                 if (err < 0)
55                         return err;
56                 else
57                         return -EREMOTEIO;
58         }
59
60         /* Set the Auxiliary Byte. */
61         buf[0] = buf[2];
62         buf[0] &= ~0x20;
63         buf[0] |= 0x18;
64         buf[1] = 0x50;
65         msg.len = 2;
66         if (fe->ops.i2c_gate_ctrl)
67                 fe->ops.i2c_gate_ctrl(fe, 1);
68         if ((err = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
69                 printk(KERN_WARNING "lgh06xf: %s error "
70                        "(addr %02x <- %02x, err = %i)\n",
71                        __FUNCTION__, buf[0], buf[1], err);
72                 if (err < 0)
73                         return err;
74                 else
75                         return -EREMOTEIO;
76         }
77
78         // calculate the frequency we set it to
79         for (i = 0; i < dvb_pll_lg_tdvs_h06xf.count; i++) {
80                 if (params->frequency > dvb_pll_lg_tdvs_h06xf.entries[i].limit)
81                         continue;
82                 break;
83         }
84         div = (params->frequency + dvb_pll_lg_tdvs_h06xf.entries[i].offset) /
85                 dvb_pll_lg_tdvs_h06xf.entries[i].stepsize;
86         priv->frequency = (div * dvb_pll_lg_tdvs_h06xf.entries[i].stepsize) -
87                 dvb_pll_lg_tdvs_h06xf.entries[i].offset;
88
89         return 0;
90 }
91
92 static int lgh06xf_get_frequency(struct dvb_frontend *fe, u32 *frequency)
93 {
94         struct lgh06xf_priv *priv = fe->tuner_priv;
95         *frequency = priv->frequency;
96         return 0;
97 }
98
99 static struct dvb_tuner_ops lgh06xf_tuner_ops = {
100         .release       = lgh06xf_release,
101         .set_params    = lgh06xf_set_params,
102         .get_frequency = lgh06xf_get_frequency,
103 };
104
105 struct dvb_frontend* lgh06xf_attach(struct dvb_frontend *fe,
106                                     struct i2c_adapter *i2c)
107 {
108         struct lgh06xf_priv *priv = NULL;
109
110         priv = kzalloc(sizeof(struct lgh06xf_priv), GFP_KERNEL);
111         if (priv == NULL)
112                 return NULL;
113
114         priv->i2c = i2c;
115
116         memcpy(&fe->ops.tuner_ops, &lgh06xf_tuner_ops,
117                sizeof(struct dvb_tuner_ops));
118
119         strlcpy(fe->ops.tuner_ops.info.name, dvb_pll_lg_tdvs_h06xf.name,
120                 sizeof(fe->ops.tuner_ops.info.name));
121
122         fe->ops.tuner_ops.info.frequency_min = dvb_pll_lg_tdvs_h06xf.min;
123         fe->ops.tuner_ops.info.frequency_max = dvb_pll_lg_tdvs_h06xf.max;
124
125         fe->tuner_priv = priv;
126         return fe;
127 }
128
129 EXPORT_SYMBOL(lgh06xf_attach);
130
131 MODULE_DESCRIPTION("LG TDVS-H06xF ATSC Tuner support");
132 MODULE_AUTHOR("Michael Krufky");
133 MODULE_LICENSE("GPL");
134
135 /*
136  * Local variables:
137  * c-basic-offset: 8
138  * End:
139  */