media: dvb: symbol fixup for dvb_attach()
[platform/kernel/linux-starfive.git] / drivers / media / dvb-frontends / lgs8gl5.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3     Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
4
5     Copyright (C) 2008 Sirius International (Hong Kong) Limited
6         Timothy Lee <timothy.lee@siriushk.com>
7
8
9 */
10
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/string.h>
15 #include <linux/slab.h>
16 #include <media/dvb_frontend.h>
17 #include "lgs8gl5.h"
18
19
20 #define REG_RESET               0x02
21 #define REG_RESET_OFF                   0x01
22 #define REG_03                  0x03
23 #define REG_04                  0x04
24 #define REG_07                  0x07
25 #define REG_09                  0x09
26 #define REG_0A                  0x0a
27 #define REG_0B                  0x0b
28 #define REG_0C                  0x0c
29 #define REG_37                  0x37
30 #define REG_STRENGTH            0x4b
31 #define REG_STRENGTH_MASK               0x7f
32 #define REG_STRENGTH_CARRIER            0x80
33 #define REG_INVERSION           0x7c
34 #define REG_INVERSION_ON                0x80
35 #define REG_7D                  0x7d
36 #define REG_7E                  0x7e
37 #define REG_A2                  0xa2
38 #define REG_STATUS              0xa4
39 #define REG_STATUS_SYNC         0x04
40 #define REG_STATUS_LOCK         0x01
41
42
43 struct lgs8gl5_state {
44         struct i2c_adapter *i2c;
45         const struct lgs8gl5_config *config;
46         struct dvb_frontend frontend;
47 };
48
49
50 static int debug;
51 #define dprintk(args...) \
52         do { \
53                 if (debug) \
54                         printk(KERN_DEBUG "lgs8gl5: " args); \
55         } while (0)
56
57
58 /* Writes into demod's register */
59 static int
60 lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
61 {
62         int ret;
63         u8 buf[] = {reg, data};
64         struct i2c_msg msg = {
65                 .addr  = state->config->demod_address,
66                 .flags = 0,
67                 .buf   = buf,
68                 .len   = 2
69         };
70
71         ret = i2c_transfer(state->i2c, &msg, 1);
72         if (ret != 1)
73                 dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n",
74                         __func__, reg, data, ret);
75         return (ret != 1) ? -1 : 0;
76 }
77
78
79 /* Reads from demod's register */
80 static int
81 lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg)
82 {
83         int ret;
84         u8 b0[] = {reg};
85         u8 b1[] = {0};
86         struct i2c_msg msg[2] = {
87                 {
88                         .addr  = state->config->demod_address,
89                         .flags = 0,
90                         .buf   = b0,
91                         .len   = 1
92                 },
93                 {
94                         .addr  = state->config->demod_address,
95                         .flags = I2C_M_RD,
96                         .buf   = b1,
97                         .len   = 1
98                 }
99         };
100
101         ret = i2c_transfer(state->i2c, msg, 2);
102         if (ret != 2)
103                 return -EIO;
104
105         return b1[0];
106 }
107
108
109 static int
110 lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
111 {
112         lgs8gl5_read_reg(state, reg);
113         lgs8gl5_write_reg(state, reg, data);
114         return 0;
115 }
116
117
118 /* Writes into alternate device's register */
119 /* TODO:  Find out what that device is for! */
120 static int
121 lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
122 {
123         int ret;
124         u8 b0[] = {reg};
125         u8 b1[] = {0};
126         u8 b2[] = {reg, data};
127         struct i2c_msg msg[3] = {
128                 {
129                         .addr  = state->config->demod_address + 2,
130                         .flags = 0,
131                         .buf   = b0,
132                         .len   = 1
133                 },
134                 {
135                         .addr  = state->config->demod_address + 2,
136                         .flags = I2C_M_RD,
137                         .buf   = b1,
138                         .len   = 1
139                 },
140                 {
141                         .addr  = state->config->demod_address + 2,
142                         .flags = 0,
143                         .buf   = b2,
144                         .len   = 2
145                 },
146         };
147
148         ret = i2c_transfer(state->i2c, msg, 3);
149         return (ret != 3) ? -1 : 0;
150 }
151
152
153 static void
154 lgs8gl5_soft_reset(struct lgs8gl5_state *state)
155 {
156         u8 val;
157
158         dprintk("%s\n", __func__);
159
160         val = lgs8gl5_read_reg(state, REG_RESET);
161         lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF);
162         lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF);
163         msleep(5);
164 }
165
166
167 /* Starts demodulation */
168 static void
169 lgs8gl5_start_demod(struct lgs8gl5_state *state)
170 {
171         u8  val;
172         int n;
173
174         dprintk("%s\n", __func__);
175
176         lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
177         lgs8gl5_soft_reset(state);
178         lgs8gl5_update_reg(state, REG_07, 0x10);
179         lgs8gl5_update_reg(state, REG_07, 0x10);
180         lgs8gl5_write_reg(state, REG_09, 0x0e);
181         lgs8gl5_write_reg(state, REG_0A, 0xe5);
182         lgs8gl5_write_reg(state, REG_0B, 0x35);
183         lgs8gl5_write_reg(state, REG_0C, 0x30);
184
185         lgs8gl5_update_reg(state, REG_03, 0x00);
186         lgs8gl5_update_reg(state, REG_7E, 0x01);
187         lgs8gl5_update_alt_reg(state, 0xc5, 0x00);
188         lgs8gl5_update_reg(state, REG_04, 0x02);
189         lgs8gl5_update_reg(state, REG_37, 0x01);
190         lgs8gl5_soft_reset(state);
191
192         /* Wait for carrier */
193         for (n = 0;  n < 10;  n++) {
194                 val = lgs8gl5_read_reg(state, REG_STRENGTH);
195                 dprintk("Wait for carrier[%d] 0x%02X\n", n, val);
196                 if (val & REG_STRENGTH_CARRIER)
197                         break;
198                 msleep(4);
199         }
200         if (!(val & REG_STRENGTH_CARRIER))
201                 return;
202
203         /* Wait for lock */
204         for (n = 0;  n < 20;  n++) {
205                 val = lgs8gl5_read_reg(state, REG_STATUS);
206                 dprintk("Wait for lock[%d] 0x%02X\n", n, val);
207                 if (val & REG_STATUS_LOCK)
208                         break;
209                 msleep(12);
210         }
211         if (!(val & REG_STATUS_LOCK))
212                 return;
213
214         lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2));
215         lgs8gl5_soft_reset(state);
216 }
217
218
219 static int
220 lgs8gl5_init(struct dvb_frontend *fe)
221 {
222         struct lgs8gl5_state *state = fe->demodulator_priv;
223
224         dprintk("%s\n", __func__);
225
226         lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
227         lgs8gl5_soft_reset(state);
228         lgs8gl5_update_reg(state, REG_07, 0x10);
229         lgs8gl5_update_reg(state, REG_07, 0x10);
230         lgs8gl5_write_reg(state, REG_09, 0x0e);
231         lgs8gl5_write_reg(state, REG_0A, 0xe5);
232         lgs8gl5_write_reg(state, REG_0B, 0x35);
233         lgs8gl5_write_reg(state, REG_0C, 0x30);
234
235         return 0;
236 }
237
238
239 static int
240 lgs8gl5_read_status(struct dvb_frontend *fe, enum fe_status *status)
241 {
242         struct lgs8gl5_state *state = fe->demodulator_priv;
243         u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
244         u8 flags = lgs8gl5_read_reg(state, REG_STATUS);
245
246         *status = 0;
247
248         if ((level & REG_STRENGTH_MASK) > 0)
249                 *status |= FE_HAS_SIGNAL;
250         if (level & REG_STRENGTH_CARRIER)
251                 *status |= FE_HAS_CARRIER;
252         if (flags & REG_STATUS_SYNC)
253                 *status |= FE_HAS_SYNC;
254         if (flags & REG_STATUS_LOCK)
255                 *status |= FE_HAS_LOCK;
256
257         return 0;
258 }
259
260
261 static int
262 lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber)
263 {
264         *ber = 0;
265
266         return 0;
267 }
268
269
270 static int
271 lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength)
272 {
273         struct lgs8gl5_state *state = fe->demodulator_priv;
274         u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
275         *signal_strength = (level & REG_STRENGTH_MASK) << 8;
276
277         return 0;
278 }
279
280
281 static int
282 lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr)
283 {
284         struct lgs8gl5_state *state = fe->demodulator_priv;
285         u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
286         *snr = (level & REG_STRENGTH_MASK) << 8;
287
288         return 0;
289 }
290
291
292 static int
293 lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
294 {
295         *ucblocks = 0;
296
297         return 0;
298 }
299
300
301 static int
302 lgs8gl5_set_frontend(struct dvb_frontend *fe)
303 {
304         struct dtv_frontend_properties *p = &fe->dtv_property_cache;
305         struct lgs8gl5_state *state = fe->demodulator_priv;
306
307         dprintk("%s\n", __func__);
308
309         if (p->bandwidth_hz != 8000000)
310                 return -EINVAL;
311
312         if (fe->ops.tuner_ops.set_params) {
313                 fe->ops.tuner_ops.set_params(fe);
314                 if (fe->ops.i2c_gate_ctrl)
315                         fe->ops.i2c_gate_ctrl(fe, 0);
316         }
317
318         /* lgs8gl5_set_inversion(state, p->inversion); */
319
320         lgs8gl5_start_demod(state);
321
322         return 0;
323 }
324
325
326 static int
327 lgs8gl5_get_frontend(struct dvb_frontend *fe,
328                      struct dtv_frontend_properties *p)
329 {
330         struct lgs8gl5_state *state = fe->demodulator_priv;
331
332         u8 inv = lgs8gl5_read_reg(state, REG_INVERSION);
333
334         p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF;
335
336         p->code_rate_HP = FEC_1_2;
337         p->code_rate_LP = FEC_7_8;
338         p->guard_interval = GUARD_INTERVAL_1_32;
339         p->transmission_mode = TRANSMISSION_MODE_2K;
340         p->modulation = QAM_64;
341         p->hierarchy = HIERARCHY_NONE;
342         p->bandwidth_hz = 8000000;
343
344         return 0;
345 }
346
347
348 static int
349 lgs8gl5_get_tune_settings(struct dvb_frontend *fe,
350                 struct dvb_frontend_tune_settings *fesettings)
351 {
352         fesettings->min_delay_ms = 240;
353         fesettings->step_size    = 0;
354         fesettings->max_drift    = 0;
355         return 0;
356 }
357
358
359 static void
360 lgs8gl5_release(struct dvb_frontend *fe)
361 {
362         struct lgs8gl5_state *state = fe->demodulator_priv;
363         kfree(state);
364 }
365
366
367 static const struct dvb_frontend_ops lgs8gl5_ops;
368
369
370 struct dvb_frontend*
371 lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)
372 {
373         struct lgs8gl5_state *state = NULL;
374
375         dprintk("%s\n", __func__);
376
377         /* Allocate memory for the internal state */
378         state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
379         if (state == NULL)
380                 goto error;
381
382         /* Setup the state */
383         state->config = config;
384         state->i2c    = i2c;
385
386         /* Check if the demod is there */
387         if (lgs8gl5_read_reg(state, REG_RESET) < 0)
388                 goto error;
389
390         /* Create dvb_frontend */
391         memcpy(&state->frontend.ops, &lgs8gl5_ops,
392                 sizeof(struct dvb_frontend_ops));
393         state->frontend.demodulator_priv = state;
394         return &state->frontend;
395
396 error:
397         kfree(state);
398         return NULL;
399 }
400 EXPORT_SYMBOL(lgs8gl5_attach);
401
402
403 static const struct dvb_frontend_ops lgs8gl5_ops = {
404         .delsys = { SYS_DTMB },
405         .info = {
406                 .name                   = "Legend Silicon LGS-8GL5 DMB-TH",
407                 .frequency_min_hz       = 474 * MHz,
408                 .frequency_max_hz       = 858 * MHz,
409                 .frequency_stepsize_hz  =  10 * kHz,
410                 .caps = FE_CAN_FEC_AUTO |
411                         FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 |
412                         FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
413                         FE_CAN_TRANSMISSION_MODE_AUTO |
414                         FE_CAN_BANDWIDTH_AUTO |
415                         FE_CAN_GUARD_INTERVAL_AUTO |
416                         FE_CAN_HIERARCHY_AUTO |
417                         FE_CAN_RECOVER
418         },
419
420         .release = lgs8gl5_release,
421
422         .init = lgs8gl5_init,
423
424         .set_frontend = lgs8gl5_set_frontend,
425         .get_frontend = lgs8gl5_get_frontend,
426         .get_tune_settings = lgs8gl5_get_tune_settings,
427
428         .read_status = lgs8gl5_read_status,
429         .read_ber = lgs8gl5_read_ber,
430         .read_signal_strength = lgs8gl5_read_signal_strength,
431         .read_snr = lgs8gl5_read_snr,
432         .read_ucblocks = lgs8gl5_read_ucblocks,
433 };
434
435
436 module_param(debug, int, 0644);
437 MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
438
439 MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver");
440 MODULE_AUTHOR("Timothy Lee");
441 MODULE_LICENSE("GPL");