diff options
Diffstat (limited to 'drivers/media/dvb-frontends/zl10039.c')
-rw-r--r-- | drivers/media/dvb-frontends/zl10039.c | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/drivers/media/dvb-frontends/zl10039.c b/drivers/media/dvb-frontends/zl10039.c new file mode 100644 index 000000000000..eff9c5fde50a --- /dev/null +++ b/drivers/media/dvb-frontends/zl10039.c | |||
@@ -0,0 +1,307 @@ | |||
1 | /* | ||
2 | * Driver for Zarlink ZL10039 DVB-S tuner | ||
3 | * | ||
4 | * Copyright 2007 Jan D. Louw <jd.louw@mweb.co.za> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/dvb/frontend.h> | ||
27 | |||
28 | #include "dvb_frontend.h" | ||
29 | #include "zl10039.h" | ||
30 | |||
31 | static int debug; | ||
32 | |||
33 | #define dprintk(args...) \ | ||
34 | do { \ | ||
35 | if (debug) \ | ||
36 | printk(KERN_DEBUG args); \ | ||
37 | } while (0) | ||
38 | |||
39 | enum zl10039_model_id { | ||
40 | ID_ZL10039 = 1 | ||
41 | }; | ||
42 | |||
43 | struct zl10039_state { | ||
44 | struct i2c_adapter *i2c; | ||
45 | u8 i2c_addr; | ||
46 | u8 id; | ||
47 | }; | ||
48 | |||
49 | enum zl10039_reg_addr { | ||
50 | PLL0 = 0, | ||
51 | PLL1, | ||
52 | PLL2, | ||
53 | PLL3, | ||
54 | RFFE, | ||
55 | BASE0, | ||
56 | BASE1, | ||
57 | BASE2, | ||
58 | LO0, | ||
59 | LO1, | ||
60 | LO2, | ||
61 | LO3, | ||
62 | LO4, | ||
63 | LO5, | ||
64 | LO6, | ||
65 | GENERAL | ||
66 | }; | ||
67 | |||
68 | static int zl10039_read(const struct zl10039_state *state, | ||
69 | const enum zl10039_reg_addr reg, u8 *buf, | ||
70 | const size_t count) | ||
71 | { | ||
72 | u8 regbuf[] = { reg }; | ||
73 | struct i2c_msg msg[] = { | ||
74 | {/* Write register address */ | ||
75 | .addr = state->i2c_addr, | ||
76 | .flags = 0, | ||
77 | .buf = regbuf, | ||
78 | .len = 1, | ||
79 | }, {/* Read count bytes */ | ||
80 | .addr = state->i2c_addr, | ||
81 | .flags = I2C_M_RD, | ||
82 | .buf = buf, | ||
83 | .len = count, | ||
84 | }, | ||
85 | }; | ||
86 | |||
87 | dprintk("%s\n", __func__); | ||
88 | |||
89 | if (i2c_transfer(state->i2c, msg, 2) != 2) { | ||
90 | dprintk("%s: i2c read error\n", __func__); | ||
91 | return -EREMOTEIO; | ||
92 | } | ||
93 | |||
94 | return 0; /* Success */ | ||
95 | } | ||
96 | |||
97 | static int zl10039_write(struct zl10039_state *state, | ||
98 | const enum zl10039_reg_addr reg, const u8 *src, | ||
99 | const size_t count) | ||
100 | { | ||
101 | u8 buf[count + 1]; | ||
102 | struct i2c_msg msg = { | ||
103 | .addr = state->i2c_addr, | ||
104 | .flags = 0, | ||
105 | .buf = buf, | ||
106 | .len = count + 1, | ||
107 | }; | ||
108 | |||
109 | dprintk("%s\n", __func__); | ||
110 | /* Write register address and data in one go */ | ||
111 | buf[0] = reg; | ||
112 | memcpy(&buf[1], src, count); | ||
113 | if (i2c_transfer(state->i2c, &msg, 1) != 1) { | ||
114 | dprintk("%s: i2c write error\n", __func__); | ||
115 | return -EREMOTEIO; | ||
116 | } | ||
117 | |||
118 | return 0; /* Success */ | ||
119 | } | ||
120 | |||
121 | static inline int zl10039_readreg(struct zl10039_state *state, | ||
122 | const enum zl10039_reg_addr reg, u8 *val) | ||
123 | { | ||
124 | return zl10039_read(state, reg, val, 1); | ||
125 | } | ||
126 | |||
127 | static inline int zl10039_writereg(struct zl10039_state *state, | ||
128 | const enum zl10039_reg_addr reg, | ||
129 | const u8 val) | ||
130 | { | ||
131 | return zl10039_write(state, reg, &val, 1); | ||
132 | } | ||
133 | |||
134 | static int zl10039_init(struct dvb_frontend *fe) | ||
135 | { | ||
136 | struct zl10039_state *state = fe->tuner_priv; | ||
137 | int ret; | ||
138 | |||
139 | dprintk("%s\n", __func__); | ||
140 | if (fe->ops.i2c_gate_ctrl) | ||
141 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
142 | /* Reset logic */ | ||
143 | ret = zl10039_writereg(state, GENERAL, 0x40); | ||
144 | if (ret < 0) { | ||
145 | dprintk("Note: i2c write error normal when resetting the " | ||
146 | "tuner\n"); | ||
147 | } | ||
148 | /* Wake up */ | ||
149 | ret = zl10039_writereg(state, GENERAL, 0x01); | ||
150 | if (ret < 0) { | ||
151 | dprintk("Tuner power up failed\n"); | ||
152 | return ret; | ||
153 | } | ||
154 | if (fe->ops.i2c_gate_ctrl) | ||
155 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int zl10039_sleep(struct dvb_frontend *fe) | ||
161 | { | ||
162 | struct zl10039_state *state = fe->tuner_priv; | ||
163 | int ret; | ||
164 | |||
165 | dprintk("%s\n", __func__); | ||
166 | if (fe->ops.i2c_gate_ctrl) | ||
167 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
168 | ret = zl10039_writereg(state, GENERAL, 0x80); | ||
169 | if (ret < 0) { | ||
170 | dprintk("Tuner sleep failed\n"); | ||
171 | return ret; | ||
172 | } | ||
173 | if (fe->ops.i2c_gate_ctrl) | ||
174 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static int zl10039_set_params(struct dvb_frontend *fe) | ||
180 | { | ||
181 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||
182 | struct zl10039_state *state = fe->tuner_priv; | ||
183 | u8 buf[6]; | ||
184 | u8 bf; | ||
185 | u32 fbw; | ||
186 | u32 div; | ||
187 | int ret; | ||
188 | |||
189 | dprintk("%s\n", __func__); | ||
190 | dprintk("Set frequency = %d, symbol rate = %d\n", | ||
191 | c->frequency, c->symbol_rate); | ||
192 | |||
193 | /* Assumed 10.111 MHz crystal oscillator */ | ||
194 | /* Cancelled num/den 80 to prevent overflow */ | ||
195 | div = (c->frequency * 1000) / 126387; | ||
196 | fbw = (c->symbol_rate * 27) / 32000; | ||
197 | /* Cancelled num/den 10 to prevent overflow */ | ||
198 | bf = ((fbw * 5088) / 1011100) - 1; | ||
199 | |||
200 | /*PLL divider*/ | ||
201 | buf[0] = (div >> 8) & 0x7f; | ||
202 | buf[1] = (div >> 0) & 0xff; | ||
203 | /*Reference divider*/ | ||
204 | /* Select reference ratio of 80 */ | ||
205 | buf[2] = 0x1D; | ||
206 | /*PLL test modes*/ | ||
207 | buf[3] = 0x40; | ||
208 | /*RF Control register*/ | ||
209 | buf[4] = 0x6E; /* Bypass enable */ | ||
210 | /*Baseband filter cutoff */ | ||
211 | buf[5] = bf; | ||
212 | |||
213 | /* Open i2c gate */ | ||
214 | if (fe->ops.i2c_gate_ctrl) | ||
215 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
216 | /* BR = 10, Enable filter adjustment */ | ||
217 | ret = zl10039_writereg(state, BASE1, 0x0A); | ||
218 | if (ret < 0) | ||
219 | goto error; | ||
220 | /* Write new config values */ | ||
221 | ret = zl10039_write(state, PLL0, buf, sizeof(buf)); | ||
222 | if (ret < 0) | ||
223 | goto error; | ||
224 | /* BR = 10, Disable filter adjustment */ | ||
225 | ret = zl10039_writereg(state, BASE1, 0x6A); | ||
226 | if (ret < 0) | ||
227 | goto error; | ||
228 | |||
229 | /* Close i2c gate */ | ||
230 | if (fe->ops.i2c_gate_ctrl) | ||
231 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
232 | return 0; | ||
233 | error: | ||
234 | dprintk("Error setting tuner\n"); | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | static int zl10039_release(struct dvb_frontend *fe) | ||
239 | { | ||
240 | struct zl10039_state *state = fe->tuner_priv; | ||
241 | |||
242 | dprintk("%s\n", __func__); | ||
243 | kfree(state); | ||
244 | fe->tuner_priv = NULL; | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | static struct dvb_tuner_ops zl10039_ops = { | ||
249 | .release = zl10039_release, | ||
250 | .init = zl10039_init, | ||
251 | .sleep = zl10039_sleep, | ||
252 | .set_params = zl10039_set_params, | ||
253 | }; | ||
254 | |||
255 | struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, | ||
256 | u8 i2c_addr, struct i2c_adapter *i2c) | ||
257 | { | ||
258 | struct zl10039_state *state = NULL; | ||
259 | |||
260 | dprintk("%s\n", __func__); | ||
261 | state = kmalloc(sizeof(struct zl10039_state), GFP_KERNEL); | ||
262 | if (state == NULL) | ||
263 | goto error; | ||
264 | |||
265 | state->i2c = i2c; | ||
266 | state->i2c_addr = i2c_addr; | ||
267 | |||
268 | /* Open i2c gate */ | ||
269 | if (fe->ops.i2c_gate_ctrl) | ||
270 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
271 | /* check if this is a valid tuner */ | ||
272 | if (zl10039_readreg(state, GENERAL, &state->id) < 0) { | ||
273 | /* Close i2c gate */ | ||
274 | if (fe->ops.i2c_gate_ctrl) | ||
275 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
276 | goto error; | ||
277 | } | ||
278 | /* Close i2c gate */ | ||
279 | if (fe->ops.i2c_gate_ctrl) | ||
280 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
281 | |||
282 | state->id = state->id & 0x0f; | ||
283 | switch (state->id) { | ||
284 | case ID_ZL10039: | ||
285 | strcpy(fe->ops.tuner_ops.info.name, | ||
286 | "Zarlink ZL10039 DVB-S tuner"); | ||
287 | break; | ||
288 | default: | ||
289 | dprintk("Chip ID=%x does not match a known type\n", state->id); | ||
290 | goto error; | ||
291 | } | ||
292 | |||
293 | memcpy(&fe->ops.tuner_ops, &zl10039_ops, sizeof(struct dvb_tuner_ops)); | ||
294 | fe->tuner_priv = state; | ||
295 | dprintk("Tuner attached @ i2c address 0x%02x\n", i2c_addr); | ||
296 | return fe; | ||
297 | error: | ||
298 | kfree(state); | ||
299 | return NULL; | ||
300 | } | ||
301 | EXPORT_SYMBOL(zl10039_attach); | ||
302 | |||
303 | module_param(debug, int, 0644); | ||
304 | MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); | ||
305 | MODULE_DESCRIPTION("Zarlink ZL10039 DVB-S tuner driver"); | ||
306 | MODULE_AUTHOR("Jan D. Louw <jd.louw@mweb.co.za>"); | ||
307 | MODULE_LICENSE("GPL"); | ||