Commit | Line | Data |
---|---|---|
47b75ec1 | 1 | /* |
955e6ed8 MCC |
2 | * Samsung s5h1432 DVB-T demodulator driver |
3 | * | |
4 | * Copyright (C) 2009 Bill Liu <Bill.Liu@Conexant.com> | |
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 | * GNU General Public License for more details. | |
955e6ed8 | 15 | */ |
47b75ec1 PB |
16 | |
17 | #include <linux/kernel.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/string.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/delay.h> | |
fada1935 | 23 | #include <media/dvb_frontend.h> |
47b75ec1 PB |
24 | #include "s5h1432.h" |
25 | ||
26 | struct s5h1432_state { | |
27 | ||
28 | struct i2c_adapter *i2c; | |
29 | ||
30 | /* configuration settings */ | |
31 | const struct s5h1432_config *config; | |
32 | ||
33 | struct dvb_frontend frontend; | |
34 | ||
0df289a2 | 35 | enum fe_modulation current_modulation; |
47b75ec1 PB |
36 | unsigned int first_tune:1; |
37 | ||
38 | u32 current_frequency; | |
39 | int if_freq; | |
40 | ||
41 | u8 inversion; | |
42 | }; | |
43 | ||
44 | static int debug; | |
45 | ||
46 | #define dprintk(arg...) do { \ | |
47 | if (debug) \ | |
48 | printk(arg); \ | |
49 | } while (0) | |
50 | ||
47b75ec1 | 51 | static int s5h1432_writereg(struct s5h1432_state *state, |
bda7f4ee | 52 | u8 addr, u8 reg, u8 data) |
47b75ec1 PB |
53 | { |
54 | int ret; | |
55 | u8 buf[] = { reg, data }; | |
56 | ||
bda7f4ee | 57 | struct i2c_msg msg = {.addr = addr, .flags = 0, .buf = buf, .len = 2 }; |
47b75ec1 PB |
58 | |
59 | ret = i2c_transfer(state->i2c, &msg, 1); | |
60 | ||
61 | if (ret != 1) | |
4bd69e7b MCC |
62 | printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, ret == %i)\n", |
63 | __func__, addr, reg, data, ret); | |
47b75ec1 PB |
64 | |
65 | return (ret != 1) ? -1 : 0; | |
66 | } | |
67 | ||
68 | static u8 s5h1432_readreg(struct s5h1432_state *state, u8 addr, u8 reg) | |
69 | { | |
70 | int ret; | |
71 | u8 b0[] = { reg }; | |
72 | u8 b1[] = { 0 }; | |
73 | ||
74 | struct i2c_msg msg[] = { | |
bda7f4ee DH |
75 | {.addr = addr, .flags = 0, .buf = b0, .len = 1}, |
76 | {.addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 1} | |
77 | }; | |
47b75ec1 PB |
78 | |
79 | ret = i2c_transfer(state->i2c, msg, 2); | |
80 | ||
81 | if (ret != 2) | |
82 | printk(KERN_ERR "%s: readreg error (ret == %i)\n", | |
bda7f4ee | 83 | __func__, ret); |
47b75ec1 PB |
84 | return b1[0]; |
85 | } | |
86 | ||
87 | static int s5h1432_sleep(struct dvb_frontend *fe) | |
88 | { | |
89 | return 0; | |
90 | } | |
91 | ||
bda7f4ee DH |
92 | static int s5h1432_set_channel_bandwidth(struct dvb_frontend *fe, |
93 | u32 bandwidth) | |
47b75ec1 | 94 | { |
47b75ec1 PB |
95 | struct s5h1432_state *state = fe->demodulator_priv; |
96 | ||
97 | u8 reg = 0; | |
98 | ||
bda7f4ee | 99 | /* Register [0x2E] bit 3:2 : 8MHz = 0; 7MHz = 1; 6MHz = 2 */ |
47b75ec1 PB |
100 | reg = s5h1432_readreg(state, S5H1432_I2C_TOP_ADDR, 0x2E); |
101 | reg &= ~(0x0C); | |
102 | switch (bandwidth) { | |
103 | case 6: | |
104 | reg |= 0x08; | |
105 | break; | |
106 | case 7: | |
107 | reg |= 0x04; | |
108 | break; | |
109 | case 8: | |
110 | reg |= 0x00; | |
111 | break; | |
112 | default: | |
bda7f4ee | 113 | return 0; |
47b75ec1 PB |
114 | } |
115 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x2E, reg); | |
bda7f4ee | 116 | return 1; |
47b75ec1 PB |
117 | } |
118 | ||
119 | static int s5h1432_set_IF(struct dvb_frontend *fe, u32 ifFreqHz) | |
120 | { | |
47b75ec1 PB |
121 | struct s5h1432_state *state = fe->demodulator_priv; |
122 | ||
123 | switch (ifFreqHz) { | |
124 | case TAIWAN_HI_IF_FREQ_44_MHZ: | |
bda7f4ee DH |
125 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x55); |
126 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x55); | |
127 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0x15); | |
128 | break; | |
47b75ec1 | 129 | case EUROPE_HI_IF_FREQ_36_MHZ: |
bda7f4ee DH |
130 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x00); |
131 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x00); | |
132 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0x40); | |
133 | break; | |
47b75ec1 | 134 | case IF_FREQ_6_MHZ: |
bda7f4ee DH |
135 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x00); |
136 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x00); | |
137 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xe0); | |
138 | break; | |
47b75ec1 | 139 | case IF_FREQ_3point3_MHZ: |
bda7f4ee DH |
140 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x66); |
141 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x66); | |
142 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEE); | |
143 | break; | |
47b75ec1 | 144 | case IF_FREQ_3point5_MHZ: |
bda7f4ee DH |
145 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x55); |
146 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x55); | |
147 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xED); | |
148 | break; | |
47b75ec1 | 149 | case IF_FREQ_4_MHZ: |
bda7f4ee DH |
150 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0xAA); |
151 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0xAA); | |
152 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEA); | |
153 | break; | |
47b75ec1 PB |
154 | default: |
155 | { | |
bda7f4ee DH |
156 | u32 value = 0; |
157 | value = (u32) (((48000 - (ifFreqHz / 1000)) * 512 * | |
158 | (u32) 32768) / (48 * 1000)); | |
159 | printk(KERN_INFO | |
955e6ed8 | 160 | "Default IFFreq %d :reg value = 0x%x\n", |
bda7f4ee DH |
161 | ifFreqHz, value); |
162 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, | |
163 | (u8) value & 0xFF); | |
164 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, | |
165 | (u8) (value >> 8) & 0xFF); | |
166 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, | |
167 | (u8) (value >> 16) & 0xFF); | |
168 | break; | |
169 | } | |
47b75ec1 PB |
170 | |
171 | } | |
172 | ||
bda7f4ee | 173 | return 1; |
47b75ec1 PB |
174 | } |
175 | ||
176 | /* Talk to the demod, set the FEC, GUARD, QAM settings etc */ | |
6bfc3667 | 177 | static int s5h1432_set_frontend(struct dvb_frontend *fe) |
47b75ec1 | 178 | { |
6bfc3667 | 179 | struct dtv_frontend_properties *p = &fe->dtv_property_cache; |
47b75ec1 PB |
180 | u32 dvb_bandwidth = 8; |
181 | struct s5h1432_state *state = fe->demodulator_priv; | |
182 | ||
183 | if (p->frequency == state->current_frequency) { | |
bda7f4ee DH |
184 | /*current_frequency = p->frequency; */ |
185 | /*state->current_frequency = p->frequency; */ | |
47b75ec1 | 186 | } else { |
14d24d14 | 187 | fe->ops.tuner_ops.set_params(fe); |
bda7f4ee | 188 | msleep(300); |
47b75ec1 | 189 | s5h1432_set_channel_bandwidth(fe, dvb_bandwidth); |
6bfc3667 MCC |
190 | switch (p->bandwidth_hz) { |
191 | case 6000000: | |
bda7f4ee DH |
192 | dvb_bandwidth = 6; |
193 | s5h1432_set_IF(fe, IF_FREQ_4_MHZ); | |
194 | break; | |
6bfc3667 | 195 | case 7000000: |
bda7f4ee DH |
196 | dvb_bandwidth = 7; |
197 | s5h1432_set_IF(fe, IF_FREQ_4_MHZ); | |
198 | break; | |
6bfc3667 | 199 | case 8000000: |
bda7f4ee DH |
200 | dvb_bandwidth = 8; |
201 | s5h1432_set_IF(fe, IF_FREQ_4_MHZ); | |
202 | break; | |
47b75ec1 | 203 | default: |
bda7f4ee DH |
204 | return 0; |
205 | } | |
14d24d14 | 206 | /*fe->ops.tuner_ops.set_params(fe); */ |
47b75ec1 | 207 | /*Soft Reset chip*/ |
bda7f4ee DH |
208 | msleep(30); |
209 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); | |
210 | msleep(30); | |
211 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); | |
47b75ec1 PB |
212 | |
213 | s5h1432_set_channel_bandwidth(fe, dvb_bandwidth); | |
6bfc3667 MCC |
214 | switch (p->bandwidth_hz) { |
215 | case 6000000: | |
bda7f4ee DH |
216 | dvb_bandwidth = 6; |
217 | s5h1432_set_IF(fe, IF_FREQ_4_MHZ); | |
218 | break; | |
6bfc3667 | 219 | case 7000000: |
bda7f4ee DH |
220 | dvb_bandwidth = 7; |
221 | s5h1432_set_IF(fe, IF_FREQ_4_MHZ); | |
222 | break; | |
6bfc3667 | 223 | case 8000000: |
bda7f4ee DH |
224 | dvb_bandwidth = 8; |
225 | s5h1432_set_IF(fe, IF_FREQ_4_MHZ); | |
226 | break; | |
47b75ec1 | 227 | default: |
bda7f4ee DH |
228 | return 0; |
229 | } | |
14d24d14 | 230 | /*fe->ops.tuner_ops.set_params(fe); */ |
bda7f4ee DH |
231 | /*Soft Reset chip*/ |
232 | msleep(30); | |
233 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); | |
234 | msleep(30); | |
235 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); | |
47b75ec1 PB |
236 | |
237 | } | |
238 | ||
239 | state->current_frequency = p->frequency; | |
240 | ||
241 | return 0; | |
242 | } | |
243 | ||
47b75ec1 PB |
244 | static int s5h1432_init(struct dvb_frontend *fe) |
245 | { | |
246 | struct s5h1432_state *state = fe->demodulator_priv; | |
247 | ||
248 | u8 reg = 0; | |
249 | state->current_frequency = 0; | |
250 | printk(KERN_INFO " s5h1432_init().\n"); | |
251 | ||
bda7f4ee DH |
252 | /*Set VSB mode as default, this also does a soft reset */ |
253 | /*Initialize registers */ | |
254 | ||
255 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x04, 0xa8); | |
256 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x05, 0x01); | |
257 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x07, 0x70); | |
258 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x19, 0x80); | |
259 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1b, 0x9D); | |
260 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1c, 0x30); | |
261 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1d, 0x20); | |
262 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1e, 0x1B); | |
263 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x2e, 0x40); | |
264 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x42, 0x84); | |
265 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x50, 0x5a); | |
266 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x5a, 0xd3); | |
267 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x68, 0x50); | |
268 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xb8, 0x3c); | |
269 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xc4, 0x10); | |
270 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xcc, 0x9c); | |
271 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xDA, 0x00); | |
272 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe1, 0x94); | |
273 | /* s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xf4, 0xa1); */ | |
274 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xf9, 0x00); | |
275 | ||
276 | /*For NXP tuner*/ | |
277 | ||
278 | /*Set 3.3MHz as default IF frequency */ | |
279 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x66); | |
280 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x66); | |
281 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEE); | |
282 | /* Set reg 0x1E to get the full dynamic range */ | |
283 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1e, 0x31); | |
284 | ||
285 | /* Mode setting in demod */ | |
47b75ec1 PB |
286 | reg = s5h1432_readreg(state, S5H1432_I2C_TOP_ADDR, 0x42); |
287 | reg |= 0x80; | |
288 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x42, reg); | |
bda7f4ee | 289 | /* Serial mode */ |
47b75ec1 | 290 | |
bda7f4ee | 291 | /* Soft Reset chip */ |
47b75ec1 | 292 | |
bda7f4ee | 293 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); |
47b75ec1 | 294 | msleep(30); |
bda7f4ee | 295 | s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); |
47b75ec1 PB |
296 | |
297 | ||
298 | return 0; | |
299 | } | |
300 | ||
0df289a2 | 301 | static int s5h1432_read_status(struct dvb_frontend *fe, enum fe_status *status) |
47b75ec1 PB |
302 | { |
303 | return 0; | |
304 | } | |
305 | ||
47b75ec1 | 306 | static int s5h1432_read_signal_strength(struct dvb_frontend *fe, |
bda7f4ee | 307 | u16 *signal_strength) |
47b75ec1 PB |
308 | { |
309 | return 0; | |
310 | } | |
311 | ||
312 | static int s5h1432_read_snr(struct dvb_frontend *fe, u16 *snr) | |
313 | { | |
314 | return 0; | |
315 | } | |
316 | ||
317 | static int s5h1432_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) | |
318 | { | |
319 | ||
320 | return 0; | |
321 | } | |
322 | ||
323 | static int s5h1432_read_ber(struct dvb_frontend *fe, u32 *ber) | |
324 | { | |
325 | return 0; | |
326 | } | |
327 | ||
47b75ec1 PB |
328 | static int s5h1432_get_tune_settings(struct dvb_frontend *fe, |
329 | struct dvb_frontend_tune_settings *tune) | |
330 | { | |
331 | return 0; | |
332 | } | |
333 | ||
334 | static void s5h1432_release(struct dvb_frontend *fe) | |
335 | { | |
336 | struct s5h1432_state *state = fe->demodulator_priv; | |
337 | kfree(state); | |
338 | } | |
339 | ||
bd336e63 | 340 | static const struct dvb_frontend_ops s5h1432_ops; |
47b75ec1 PB |
341 | |
342 | struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config, | |
343 | struct i2c_adapter *i2c) | |
344 | { | |
345 | struct s5h1432_state *state = NULL; | |
346 | ||
347 | printk(KERN_INFO " Enter s5h1432_attach(). attach success!\n"); | |
348 | /* allocate memory for the internal state */ | |
349 | state = kmalloc(sizeof(struct s5h1432_state), GFP_KERNEL); | |
47aab4ab PST |
350 | if (!state) |
351 | return NULL; | |
47b75ec1 PB |
352 | |
353 | /* setup the state */ | |
354 | state->config = config; | |
355 | state->i2c = i2c; | |
356 | state->current_modulation = QAM_16; | |
357 | state->inversion = state->config->inversion; | |
358 | ||
359 | /* create dvb_frontend */ | |
360 | memcpy(&state->frontend.ops, &s5h1432_ops, | |
361 | sizeof(struct dvb_frontend_ops)); | |
362 | ||
363 | state->frontend.demodulator_priv = state; | |
364 | ||
365 | return &state->frontend; | |
47b75ec1 PB |
366 | } |
367 | EXPORT_SYMBOL(s5h1432_attach); | |
368 | ||
bd336e63 | 369 | static const struct dvb_frontend_ops s5h1432_ops = { |
533b673b | 370 | .delsys = { SYS_DVBT }, |
47b75ec1 | 371 | .info = { |
bda7f4ee | 372 | .name = "Samsung s5h1432 DVB-T Frontend", |
bda7f4ee DH |
373 | .frequency_min = 177000000, |
374 | .frequency_max = 858000000, | |
375 | .frequency_stepsize = 166666, | |
376 | .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | | |
377 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | | |
378 | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | | |
379 | FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | | |
380 | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER}, | |
381 | ||
382 | .init = s5h1432_init, | |
383 | .sleep = s5h1432_sleep, | |
6bfc3667 | 384 | .set_frontend = s5h1432_set_frontend, |
bda7f4ee DH |
385 | .get_tune_settings = s5h1432_get_tune_settings, |
386 | .read_status = s5h1432_read_status, | |
387 | .read_ber = s5h1432_read_ber, | |
47b75ec1 | 388 | .read_signal_strength = s5h1432_read_signal_strength, |
bda7f4ee DH |
389 | .read_snr = s5h1432_read_snr, |
390 | .read_ucblocks = s5h1432_read_ucblocks, | |
391 | .release = s5h1432_release, | |
47b75ec1 PB |
392 | }; |
393 | ||
394 | module_param(debug, int, 0644); | |
395 | MODULE_PARM_DESC(debug, "Enable verbose debug messages"); | |
396 | ||
397 | MODULE_DESCRIPTION("Samsung s5h1432 DVB-T Demodulator driver"); | |
398 | MODULE_AUTHOR("Bill Liu"); | |
399 | MODULE_LICENSE("GPL"); |