1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * cxd2880_tnrdmd_dvbt.c |
4 | * Sony CXD2880 DVB-T2/T tuner + demodulator driver |
5 | * control functions for DVB-T |
6 | * |
7 | * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation |
8 | */ |
9 | |
10 | #include <media/dvb_frontend.h> |
11 | |
12 | #include "cxd2880_tnrdmd_dvbt.h" |
13 | #include "cxd2880_tnrdmd_dvbt_mon.h" |
14 | |
15 | static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = { |
16 | {0x00, 0x00}, {0x31, 0x01}, |
17 | }; |
18 | |
19 | static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = { |
20 | {0x00, 0x04}, {0x5c, 0xfb}, {0x00, 0x10}, {0xa4, 0x03}, |
21 | {0x00, 0x14}, {0xb0, 0x00}, {0x00, 0x25}, |
22 | }; |
23 | |
24 | static const struct cxd2880_reg_value tune_dmd_setting_seq3[] = { |
25 | {0x00, 0x12}, {0x44, 0x00}, |
26 | }; |
27 | |
28 | static const struct cxd2880_reg_value tune_dmd_setting_seq4[] = { |
29 | {0x00, 0x11}, {0x87, 0xd2}, |
30 | }; |
31 | |
32 | static const struct cxd2880_reg_value tune_dmd_setting_seq5[] = { |
33 | {0x00, 0x00}, {0xfd, 0x01}, |
34 | }; |
35 | |
36 | static const struct cxd2880_reg_value sleep_dmd_setting_seq1[] = { |
37 | {0x00, 0x04}, {0x5c, 0xd8}, {0x00, 0x10}, {0xa4, 0x00}, |
38 | }; |
39 | |
40 | static const struct cxd2880_reg_value sleep_dmd_setting_seq2[] = { |
41 | {0x00, 0x11}, {0x87, 0x04}, |
42 | }; |
43 | |
44 | static int x_tune_dvbt_demod_setting(struct cxd2880_tnrdmd |
45 | *tnr_dmd, |
46 | enum cxd2880_dtv_bandwidth |
47 | bandwidth, |
48 | enum cxd2880_tnrdmd_clockmode |
49 | clk_mode) |
50 | { |
51 | static const u8 clk_mode_ckffrq_a[2] = { 0x52, 0x49 }; |
52 | static const u8 clk_mode_ckffrq_b[2] = { 0x5d, 0x55 }; |
53 | static const u8 clk_mode_ckffrq_c[2] = { 0x60, 0x00 }; |
54 | static const u8 ratectl_margin[2] = { 0x01, 0xf0 }; |
55 | static const u8 maxclkcnt_a[3] = { 0x73, 0xca, 0x49 }; |
56 | static const u8 maxclkcnt_b[3] = { 0xc8, 0x13, 0xaa }; |
57 | static const u8 maxclkcnt_c[3] = { 0xdc, 0x6c, 0x00 }; |
58 | |
59 | static const u8 bw8_nomi_ac[5] = { 0x15, 0x00, 0x00, 0x00, 0x00}; |
60 | static const u8 bw8_nomi_b[5] = { 0x14, 0x6a, 0xaa, 0xaa, 0xaa}; |
61 | static const u8 bw8_gtdofst_a[2] = { 0x01, 0x28 }; |
62 | static const u8 bw8_gtdofst_b[2] = { 0x11, 0x44 }; |
63 | static const u8 bw8_gtdofst_c[2] = { 0x15, 0x28 }; |
64 | static const u8 bw8_mrc_a[5] = { 0x30, 0x00, 0x00, 0x90, 0x00 }; |
65 | static const u8 bw8_mrc_b[5] = { 0x36, 0x71, 0x00, 0xa3, 0x55 }; |
66 | static const u8 bw8_mrc_c[5] = { 0x38, 0x00, 0x00, 0xa8, 0x00 }; |
67 | static const u8 bw8_notch[4] = { 0xb3, 0x00, 0x01, 0x02 }; |
68 | |
69 | static const u8 bw7_nomi_ac[5] = { 0x18, 0x00, 0x00, 0x00, 0x00}; |
70 | static const u8 bw7_nomi_b[5] = { 0x17, 0x55, 0x55, 0x55, 0x55}; |
71 | static const u8 bw7_gtdofst_a[2] = { 0x12, 0x4c }; |
72 | static const u8 bw7_gtdofst_b[2] = { 0x1f, 0x15 }; |
73 | static const u8 bw7_gtdofst_c[2] = { 0x1f, 0xf8 }; |
74 | static const u8 bw7_mrc_a[5] = { 0x36, 0xdb, 0x00, 0xa4, 0x92 }; |
75 | static const u8 bw7_mrc_b[5] = { 0x3e, 0x38, 0x00, 0xba, 0xaa }; |
76 | static const u8 bw7_mrc_c[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 }; |
77 | static const u8 bw7_notch[4] = { 0xb8, 0x00, 0x00, 0x03 }; |
78 | |
79 | static const u8 bw6_nomi_ac[5] = { 0x1c, 0x00, 0x00, 0x00, 0x00}; |
80 | static const u8 bw6_nomi_b[5] = { 0x1b, 0x38, 0xe3, 0x8e, 0x38}; |
81 | static const u8 bw6_gtdofst_a[2] = { 0x1f, 0xf8 }; |
82 | static const u8 bw6_gtdofst_b[2] = { 0x24, 0x43 }; |
83 | static const u8 bw6_gtdofst_c[2] = { 0x25, 0x4c }; |
84 | static const u8 bw6_mrc_a[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 }; |
85 | static const u8 bw6_mrc_b[5] = { 0x48, 0x97, 0x00, 0xd9, 0xc7 }; |
86 | static const u8 bw6_mrc_c[5] = { 0x4a, 0xaa, 0x00, 0xdf, 0xff }; |
87 | static const u8 bw6_notch[4] = { 0xbe, 0xab, 0x00, 0x03 }; |
88 | |
89 | static const u8 bw5_nomi_ac[5] = { 0x21, 0x99, 0x99, 0x99, 0x99}; |
90 | static const u8 bw5_nomi_b[5] = { 0x20, 0xaa, 0xaa, 0xaa, 0xaa}; |
91 | static const u8 bw5_gtdofst_a[2] = { 0x26, 0x5d }; |
92 | static const u8 bw5_gtdofst_b[2] = { 0x2b, 0x84 }; |
93 | static const u8 bw5_gtdofst_c[2] = { 0x2c, 0xc2 }; |
94 | static const u8 bw5_mrc_a[5] = { 0x4c, 0xcc, 0x00, 0xe6, 0x66 }; |
95 | static const u8 bw5_mrc_b[5] = { 0x57, 0x1c, 0x01, 0x05, 0x55 }; |
96 | static const u8 bw5_mrc_c[5] = { 0x59, 0x99, 0x01, 0x0c, 0xcc }; |
97 | static const u8 bw5_notch[4] = { 0xc8, 0x01, 0x00, 0x03 }; |
98 | const u8 *data = NULL; |
99 | u8 sst_data; |
100 | int ret; |
101 | |
102 | if (!tnr_dmd) |
103 | return -EINVAL; |
104 | |
105 | ret = cxd2880_io_write_multi_regs(io: tnr_dmd->io, |
106 | tgt: CXD2880_IO_TGT_SYS, |
107 | reg_value: tune_dmd_setting_seq1, |
108 | ARRAY_SIZE(tune_dmd_setting_seq1)); |
109 | if (ret) |
110 | return ret; |
111 | |
112 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
113 | CXD2880_IO_TGT_DMD, |
114 | 0x00, 0x04); |
115 | if (ret) |
116 | return ret; |
117 | |
118 | switch (clk_mode) { |
119 | case CXD2880_TNRDMD_CLOCKMODE_A: |
120 | data = clk_mode_ckffrq_a; |
121 | break; |
122 | case CXD2880_TNRDMD_CLOCKMODE_B: |
123 | data = clk_mode_ckffrq_b; |
124 | break; |
125 | case CXD2880_TNRDMD_CLOCKMODE_C: |
126 | data = clk_mode_ckffrq_c; |
127 | break; |
128 | default: |
129 | return -EINVAL; |
130 | } |
131 | |
132 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
133 | CXD2880_IO_TGT_DMD, |
134 | 0x65, data, 2); |
135 | if (ret) |
136 | return ret; |
137 | |
138 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
139 | CXD2880_IO_TGT_DMD, |
140 | 0x5d, 0x07); |
141 | if (ret) |
142 | return ret; |
143 | |
144 | if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) { |
145 | u8 data[2] = { 0x01, 0x01 }; |
146 | |
147 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
148 | CXD2880_IO_TGT_DMD, |
149 | 0x00, 0x00); |
150 | if (ret) |
151 | return ret; |
152 | |
153 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
154 | CXD2880_IO_TGT_DMD, |
155 | 0xce, data, 2); |
156 | if (ret) |
157 | return ret; |
158 | } |
159 | |
160 | ret = cxd2880_io_write_multi_regs(io: tnr_dmd->io, |
161 | tgt: CXD2880_IO_TGT_DMD, |
162 | reg_value: tune_dmd_setting_seq2, |
163 | ARRAY_SIZE(tune_dmd_setting_seq2)); |
164 | if (ret) |
165 | return ret; |
166 | |
167 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
168 | CXD2880_IO_TGT_DMD, |
169 | 0xf0, ratectl_margin, 2); |
170 | if (ret) |
171 | return ret; |
172 | |
173 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN || |
174 | tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) { |
175 | ret = cxd2880_io_write_multi_regs(io: tnr_dmd->io, |
176 | tgt: CXD2880_IO_TGT_DMD, |
177 | reg_value: tune_dmd_setting_seq3, |
178 | ARRAY_SIZE(tune_dmd_setting_seq3)); |
179 | if (ret) |
180 | return ret; |
181 | } |
182 | |
183 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) { |
184 | ret = cxd2880_io_write_multi_regs(io: tnr_dmd->io, |
185 | tgt: CXD2880_IO_TGT_DMD, |
186 | reg_value: tune_dmd_setting_seq4, |
187 | ARRAY_SIZE(tune_dmd_setting_seq4)); |
188 | if (ret) |
189 | return ret; |
190 | } |
191 | |
192 | if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) { |
193 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
194 | CXD2880_IO_TGT_DMD, |
195 | 0x00, 0x04); |
196 | if (ret) |
197 | return ret; |
198 | |
199 | switch (clk_mode) { |
200 | case CXD2880_TNRDMD_CLOCKMODE_A: |
201 | data = maxclkcnt_a; |
202 | break; |
203 | case CXD2880_TNRDMD_CLOCKMODE_B: |
204 | data = maxclkcnt_b; |
205 | break; |
206 | case CXD2880_TNRDMD_CLOCKMODE_C: |
207 | data = maxclkcnt_c; |
208 | break; |
209 | default: |
210 | return -EINVAL; |
211 | } |
212 | |
213 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
214 | CXD2880_IO_TGT_DMD, |
215 | 0x68, data, 3); |
216 | if (ret) |
217 | return ret; |
218 | } |
219 | |
220 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
221 | CXD2880_IO_TGT_DMD, |
222 | 0x00, 0x04); |
223 | if (ret) |
224 | return ret; |
225 | |
226 | switch (bandwidth) { |
227 | case CXD2880_DTV_BW_8_MHZ: |
228 | switch (clk_mode) { |
229 | case CXD2880_TNRDMD_CLOCKMODE_A: |
230 | case CXD2880_TNRDMD_CLOCKMODE_C: |
231 | data = bw8_nomi_ac; |
232 | break; |
233 | case CXD2880_TNRDMD_CLOCKMODE_B: |
234 | data = bw8_nomi_b; |
235 | break; |
236 | default: |
237 | return -EINVAL; |
238 | } |
239 | |
240 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
241 | CXD2880_IO_TGT_DMD, |
242 | 0x60, data, 5); |
243 | if (ret) |
244 | return ret; |
245 | |
246 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
247 | CXD2880_IO_TGT_DMD, |
248 | 0x4a, 0x00); |
249 | if (ret) |
250 | return ret; |
251 | |
252 | switch (clk_mode) { |
253 | case CXD2880_TNRDMD_CLOCKMODE_A: |
254 | data = bw8_gtdofst_a; |
255 | break; |
256 | case CXD2880_TNRDMD_CLOCKMODE_B: |
257 | data = bw8_gtdofst_b; |
258 | break; |
259 | case CXD2880_TNRDMD_CLOCKMODE_C: |
260 | data = bw8_gtdofst_c; |
261 | break; |
262 | default: |
263 | return -EINVAL; |
264 | } |
265 | |
266 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
267 | CXD2880_IO_TGT_DMD, |
268 | 0x7d, data, 2); |
269 | if (ret) |
270 | return ret; |
271 | |
272 | switch (clk_mode) { |
273 | case CXD2880_TNRDMD_CLOCKMODE_A: |
274 | case CXD2880_TNRDMD_CLOCKMODE_B: |
275 | sst_data = 0x35; |
276 | break; |
277 | case CXD2880_TNRDMD_CLOCKMODE_C: |
278 | sst_data = 0x34; |
279 | break; |
280 | default: |
281 | return -EINVAL; |
282 | } |
283 | |
284 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
285 | CXD2880_IO_TGT_DMD, |
286 | 0x71, sst_data); |
287 | if (ret) |
288 | return ret; |
289 | |
290 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { |
291 | switch (clk_mode) { |
292 | case CXD2880_TNRDMD_CLOCKMODE_A: |
293 | data = bw8_mrc_a; |
294 | break; |
295 | case CXD2880_TNRDMD_CLOCKMODE_B: |
296 | data = bw8_mrc_b; |
297 | break; |
298 | case CXD2880_TNRDMD_CLOCKMODE_C: |
299 | data = bw8_mrc_c; |
300 | break; |
301 | default: |
302 | return -EINVAL; |
303 | } |
304 | |
305 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
306 | CXD2880_IO_TGT_DMD, |
307 | 0x4b, &data[0], 2); |
308 | if (ret) |
309 | return ret; |
310 | |
311 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
312 | CXD2880_IO_TGT_DMD, |
313 | 0x51, &data[2], 3); |
314 | if (ret) |
315 | return ret; |
316 | } |
317 | |
318 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
319 | CXD2880_IO_TGT_DMD, |
320 | 0x72, &bw8_notch[0], 2); |
321 | if (ret) |
322 | return ret; |
323 | |
324 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
325 | CXD2880_IO_TGT_DMD, |
326 | 0x6b, &bw8_notch[2], 2); |
327 | if (ret) |
328 | return ret; |
329 | break; |
330 | |
331 | case CXD2880_DTV_BW_7_MHZ: |
332 | switch (clk_mode) { |
333 | case CXD2880_TNRDMD_CLOCKMODE_A: |
334 | case CXD2880_TNRDMD_CLOCKMODE_C: |
335 | data = bw7_nomi_ac; |
336 | break; |
337 | case CXD2880_TNRDMD_CLOCKMODE_B: |
338 | data = bw7_nomi_b; |
339 | break; |
340 | default: |
341 | return -EINVAL; |
342 | } |
343 | |
344 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
345 | CXD2880_IO_TGT_DMD, |
346 | 0x60, data, 5); |
347 | if (ret) |
348 | return ret; |
349 | |
350 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
351 | CXD2880_IO_TGT_DMD, |
352 | 0x4a, 0x02); |
353 | if (ret) |
354 | return ret; |
355 | |
356 | switch (clk_mode) { |
357 | case CXD2880_TNRDMD_CLOCKMODE_A: |
358 | data = bw7_gtdofst_a; |
359 | break; |
360 | case CXD2880_TNRDMD_CLOCKMODE_B: |
361 | data = bw7_gtdofst_b; |
362 | break; |
363 | case CXD2880_TNRDMD_CLOCKMODE_C: |
364 | data = bw7_gtdofst_c; |
365 | break; |
366 | default: |
367 | return -EINVAL; |
368 | } |
369 | |
370 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
371 | CXD2880_IO_TGT_DMD, |
372 | 0x7d, data, 2); |
373 | if (ret) |
374 | return ret; |
375 | |
376 | switch (clk_mode) { |
377 | case CXD2880_TNRDMD_CLOCKMODE_A: |
378 | case CXD2880_TNRDMD_CLOCKMODE_B: |
379 | sst_data = 0x2f; |
380 | break; |
381 | case CXD2880_TNRDMD_CLOCKMODE_C: |
382 | sst_data = 0x2e; |
383 | break; |
384 | default: |
385 | return -EINVAL; |
386 | } |
387 | |
388 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
389 | CXD2880_IO_TGT_DMD, |
390 | 0x71, sst_data); |
391 | if (ret) |
392 | return ret; |
393 | |
394 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { |
395 | switch (clk_mode) { |
396 | case CXD2880_TNRDMD_CLOCKMODE_A: |
397 | data = bw7_mrc_a; |
398 | break; |
399 | case CXD2880_TNRDMD_CLOCKMODE_B: |
400 | data = bw7_mrc_b; |
401 | break; |
402 | case CXD2880_TNRDMD_CLOCKMODE_C: |
403 | data = bw7_mrc_c; |
404 | break; |
405 | default: |
406 | return -EINVAL; |
407 | } |
408 | |
409 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
410 | CXD2880_IO_TGT_DMD, |
411 | 0x4b, &data[0], 2); |
412 | if (ret) |
413 | return ret; |
414 | |
415 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
416 | CXD2880_IO_TGT_DMD, |
417 | 0x51, &data[2], 3); |
418 | if (ret) |
419 | return ret; |
420 | } |
421 | |
422 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
423 | CXD2880_IO_TGT_DMD, |
424 | 0x72, &bw7_notch[0], 2); |
425 | if (ret) |
426 | return ret; |
427 | |
428 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
429 | CXD2880_IO_TGT_DMD, |
430 | 0x6b, &bw7_notch[2], 2); |
431 | if (ret) |
432 | return ret; |
433 | break; |
434 | |
435 | case CXD2880_DTV_BW_6_MHZ: |
436 | switch (clk_mode) { |
437 | case CXD2880_TNRDMD_CLOCKMODE_A: |
438 | case CXD2880_TNRDMD_CLOCKMODE_C: |
439 | data = bw6_nomi_ac; |
440 | break; |
441 | case CXD2880_TNRDMD_CLOCKMODE_B: |
442 | data = bw6_nomi_b; |
443 | break; |
444 | default: |
445 | return -EINVAL; |
446 | } |
447 | |
448 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
449 | CXD2880_IO_TGT_DMD, |
450 | 0x60, data, 5); |
451 | if (ret) |
452 | return ret; |
453 | |
454 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
455 | CXD2880_IO_TGT_DMD, |
456 | 0x4a, 0x04); |
457 | if (ret) |
458 | return ret; |
459 | |
460 | switch (clk_mode) { |
461 | case CXD2880_TNRDMD_CLOCKMODE_A: |
462 | data = bw6_gtdofst_a; |
463 | break; |
464 | case CXD2880_TNRDMD_CLOCKMODE_B: |
465 | data = bw6_gtdofst_b; |
466 | break; |
467 | case CXD2880_TNRDMD_CLOCKMODE_C: |
468 | data = bw6_gtdofst_c; |
469 | break; |
470 | default: |
471 | return -EINVAL; |
472 | } |
473 | |
474 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
475 | CXD2880_IO_TGT_DMD, |
476 | 0x7d, data, 2); |
477 | if (ret) |
478 | return ret; |
479 | |
480 | switch (clk_mode) { |
481 | case CXD2880_TNRDMD_CLOCKMODE_A: |
482 | case CXD2880_TNRDMD_CLOCKMODE_C: |
483 | sst_data = 0x29; |
484 | break; |
485 | case CXD2880_TNRDMD_CLOCKMODE_B: |
486 | sst_data = 0x2a; |
487 | break; |
488 | default: |
489 | return -EINVAL; |
490 | } |
491 | |
492 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
493 | CXD2880_IO_TGT_DMD, |
494 | 0x71, sst_data); |
495 | if (ret) |
496 | return ret; |
497 | |
498 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { |
499 | switch (clk_mode) { |
500 | case CXD2880_TNRDMD_CLOCKMODE_A: |
501 | data = bw6_mrc_a; |
502 | break; |
503 | case CXD2880_TNRDMD_CLOCKMODE_B: |
504 | data = bw6_mrc_b; |
505 | break; |
506 | case CXD2880_TNRDMD_CLOCKMODE_C: |
507 | data = bw6_mrc_c; |
508 | break; |
509 | default: |
510 | return -EINVAL; |
511 | } |
512 | |
513 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
514 | CXD2880_IO_TGT_DMD, |
515 | 0x4b, &data[0], 2); |
516 | if (ret) |
517 | return ret; |
518 | |
519 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
520 | CXD2880_IO_TGT_DMD, |
521 | 0x51, &data[2], 3); |
522 | if (ret) |
523 | return ret; |
524 | } |
525 | |
526 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
527 | CXD2880_IO_TGT_DMD, |
528 | 0x72, &bw6_notch[0], 2); |
529 | if (ret) |
530 | return ret; |
531 | |
532 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
533 | CXD2880_IO_TGT_DMD, |
534 | 0x6b, &bw6_notch[2], 2); |
535 | if (ret) |
536 | return ret; |
537 | break; |
538 | |
539 | case CXD2880_DTV_BW_5_MHZ: |
540 | switch (clk_mode) { |
541 | case CXD2880_TNRDMD_CLOCKMODE_A: |
542 | case CXD2880_TNRDMD_CLOCKMODE_C: |
543 | data = bw5_nomi_ac; |
544 | break; |
545 | case CXD2880_TNRDMD_CLOCKMODE_B: |
546 | data = bw5_nomi_b; |
547 | break; |
548 | default: |
549 | return -EINVAL; |
550 | } |
551 | |
552 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
553 | CXD2880_IO_TGT_DMD, |
554 | 0x60, data, 5); |
555 | if (ret) |
556 | return ret; |
557 | |
558 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
559 | CXD2880_IO_TGT_DMD, |
560 | 0x4a, 0x06); |
561 | if (ret) |
562 | return ret; |
563 | |
564 | switch (clk_mode) { |
565 | case CXD2880_TNRDMD_CLOCKMODE_A: |
566 | data = bw5_gtdofst_a; |
567 | break; |
568 | case CXD2880_TNRDMD_CLOCKMODE_B: |
569 | data = bw5_gtdofst_b; |
570 | break; |
571 | case CXD2880_TNRDMD_CLOCKMODE_C: |
572 | data = bw5_gtdofst_c; |
573 | break; |
574 | default: |
575 | return -EINVAL; |
576 | } |
577 | |
578 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
579 | CXD2880_IO_TGT_DMD, |
580 | 0x7d, data, 2); |
581 | if (ret) |
582 | return ret; |
583 | |
584 | switch (clk_mode) { |
585 | case CXD2880_TNRDMD_CLOCKMODE_A: |
586 | case CXD2880_TNRDMD_CLOCKMODE_B: |
587 | sst_data = 0x24; |
588 | break; |
589 | case CXD2880_TNRDMD_CLOCKMODE_C: |
590 | sst_data = 0x23; |
591 | break; |
592 | default: |
593 | return -EINVAL; |
594 | } |
595 | |
596 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
597 | CXD2880_IO_TGT_DMD, |
598 | 0x71, sst_data); |
599 | if (ret) |
600 | return ret; |
601 | |
602 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { |
603 | switch (clk_mode) { |
604 | case CXD2880_TNRDMD_CLOCKMODE_A: |
605 | data = bw5_mrc_a; |
606 | break; |
607 | case CXD2880_TNRDMD_CLOCKMODE_B: |
608 | data = bw5_mrc_b; |
609 | break; |
610 | case CXD2880_TNRDMD_CLOCKMODE_C: |
611 | data = bw5_mrc_c; |
612 | break; |
613 | default: |
614 | return -EINVAL; |
615 | } |
616 | |
617 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
618 | CXD2880_IO_TGT_DMD, |
619 | 0x4b, &data[0], 2); |
620 | if (ret) |
621 | return ret; |
622 | |
623 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
624 | CXD2880_IO_TGT_DMD, |
625 | 0x51, &data[2], 3); |
626 | if (ret) |
627 | return ret; |
628 | } |
629 | |
630 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
631 | CXD2880_IO_TGT_DMD, |
632 | 0x72, &bw5_notch[0], 2); |
633 | if (ret) |
634 | return ret; |
635 | |
636 | ret = tnr_dmd->io->write_regs(tnr_dmd->io, |
637 | CXD2880_IO_TGT_DMD, |
638 | 0x6b, &bw5_notch[2], 2); |
639 | if (ret) |
640 | return ret; |
641 | break; |
642 | |
643 | default: |
644 | return -EINVAL; |
645 | } |
646 | |
647 | return cxd2880_io_write_multi_regs(io: tnr_dmd->io, |
648 | tgt: CXD2880_IO_TGT_DMD, |
649 | reg_value: tune_dmd_setting_seq5, |
650 | ARRAY_SIZE(tune_dmd_setting_seq5)); |
651 | } |
652 | |
653 | static int x_sleep_dvbt_demod_setting(struct cxd2880_tnrdmd |
654 | *tnr_dmd) |
655 | { |
656 | int ret; |
657 | |
658 | if (!tnr_dmd) |
659 | return -EINVAL; |
660 | |
661 | ret = cxd2880_io_write_multi_regs(io: tnr_dmd->io, |
662 | tgt: CXD2880_IO_TGT_DMD, |
663 | reg_value: sleep_dmd_setting_seq1, |
664 | ARRAY_SIZE(sleep_dmd_setting_seq1)); |
665 | if (ret) |
666 | return ret; |
667 | |
668 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) |
669 | ret = cxd2880_io_write_multi_regs(io: tnr_dmd->io, |
670 | tgt: CXD2880_IO_TGT_DMD, |
671 | reg_value: sleep_dmd_setting_seq2, |
672 | ARRAY_SIZE(sleep_dmd_setting_seq2)); |
673 | |
674 | return ret; |
675 | } |
676 | |
677 | static int dvbt_set_profile(struct cxd2880_tnrdmd *tnr_dmd, |
678 | enum cxd2880_dvbt_profile profile) |
679 | { |
680 | int ret; |
681 | |
682 | if (!tnr_dmd) |
683 | return -EINVAL; |
684 | |
685 | ret = tnr_dmd->io->write_reg(tnr_dmd->io, |
686 | CXD2880_IO_TGT_DMD, |
687 | 0x00, 0x10); |
688 | if (ret) |
689 | return ret; |
690 | |
691 | return tnr_dmd->io->write_reg(tnr_dmd->io, |
692 | CXD2880_IO_TGT_DMD, |
693 | 0x67, |
694 | (profile == CXD2880_DVBT_PROFILE_HP) |
695 | ? 0x00 : 0x01); |
696 | } |
697 | |
698 | int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd, |
699 | struct cxd2880_dvbt_tune_param |
700 | *tune_param) |
701 | { |
702 | int ret; |
703 | |
704 | if (!tnr_dmd || !tune_param) |
705 | return -EINVAL; |
706 | |
707 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) |
708 | return -EINVAL; |
709 | |
710 | if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && |
711 | tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) |
712 | return -EINVAL; |
713 | |
714 | ret = |
715 | cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, sys: CXD2880_DTV_SYS_DVBT, |
716 | frequency_khz: tune_param->center_freq_khz, |
717 | bandwidth: tune_param->bandwidth, one_seg_opt: 0, one_seg_opt_shft_dir: 0); |
718 | if (ret) |
719 | return ret; |
720 | |
721 | ret = |
722 | x_tune_dvbt_demod_setting(tnr_dmd, bandwidth: tune_param->bandwidth, |
723 | clk_mode: tnr_dmd->clk_mode); |
724 | if (ret) |
725 | return ret; |
726 | |
727 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { |
728 | ret = |
729 | x_tune_dvbt_demod_setting(tnr_dmd: tnr_dmd->diver_sub, |
730 | bandwidth: tune_param->bandwidth, |
731 | clk_mode: tnr_dmd->diver_sub->clk_mode); |
732 | if (ret) |
733 | return ret; |
734 | } |
735 | |
736 | return dvbt_set_profile(tnr_dmd, profile: tune_param->profile); |
737 | } |
738 | |
739 | int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd, |
740 | struct cxd2880_dvbt_tune_param |
741 | *tune_param) |
742 | { |
743 | int ret; |
744 | |
745 | if (!tnr_dmd || !tune_param) |
746 | return -EINVAL; |
747 | |
748 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) |
749 | return -EINVAL; |
750 | |
751 | if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && |
752 | tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) |
753 | return -EINVAL; |
754 | |
755 | ret = |
756 | cxd2880_tnrdmd_common_tune_setting2(tnr_dmd, sys: CXD2880_DTV_SYS_DVBT, |
757 | en_fef_intmtnt_ctrl: 0); |
758 | if (ret) |
759 | return ret; |
760 | |
761 | tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE; |
762 | tnr_dmd->frequency_khz = tune_param->center_freq_khz; |
763 | tnr_dmd->sys = CXD2880_DTV_SYS_DVBT; |
764 | tnr_dmd->bandwidth = tune_param->bandwidth; |
765 | |
766 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) { |
767 | tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE; |
768 | tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz; |
769 | tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT; |
770 | tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth; |
771 | } |
772 | |
773 | return 0; |
774 | } |
775 | |
776 | int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd *tnr_dmd) |
777 | { |
778 | int ret; |
779 | |
780 | if (!tnr_dmd) |
781 | return -EINVAL; |
782 | |
783 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) |
784 | return -EINVAL; |
785 | |
786 | if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP && |
787 | tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) |
788 | return -EINVAL; |
789 | |
790 | ret = x_sleep_dvbt_demod_setting(tnr_dmd); |
791 | if (ret) |
792 | return ret; |
793 | |
794 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) |
795 | ret = x_sleep_dvbt_demod_setting(tnr_dmd: tnr_dmd->diver_sub); |
796 | |
797 | return ret; |
798 | } |
799 | |
800 | int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd |
801 | *tnr_dmd, |
802 | enum |
803 | cxd2880_tnrdmd_lock_result |
804 | *lock) |
805 | { |
806 | int ret; |
807 | |
808 | u8 sync_stat = 0; |
809 | u8 ts_lock = 0; |
810 | u8 unlock_detected = 0; |
811 | u8 unlock_detected_sub = 0; |
812 | |
813 | if (!tnr_dmd || !lock) |
814 | return -EINVAL; |
815 | |
816 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) |
817 | return -EINVAL; |
818 | |
819 | if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) |
820 | return -EINVAL; |
821 | |
822 | ret = |
823 | cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, sync_stat: &sync_stat, ts_lock_stat: &ts_lock, |
824 | unlock_detected: &unlock_detected); |
825 | if (ret) |
826 | return ret; |
827 | |
828 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { |
829 | if (sync_stat == 6) |
830 | *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; |
831 | else if (unlock_detected) |
832 | *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; |
833 | else |
834 | *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; |
835 | |
836 | return 0; |
837 | } |
838 | |
839 | if (sync_stat == 6) { |
840 | *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; |
841 | return 0; |
842 | } |
843 | |
844 | ret = |
845 | cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, sync_stat: &sync_stat, |
846 | unlock_detected: &unlock_detected_sub); |
847 | if (ret) |
848 | return ret; |
849 | |
850 | if (sync_stat == 6) |
851 | *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; |
852 | else if (unlock_detected && unlock_detected_sub) |
853 | *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; |
854 | else |
855 | *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; |
856 | |
857 | return 0; |
858 | } |
859 | |
860 | int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd |
861 | *tnr_dmd, |
862 | enum |
863 | cxd2880_tnrdmd_lock_result |
864 | *lock) |
865 | { |
866 | int ret; |
867 | |
868 | u8 sync_stat = 0; |
869 | u8 ts_lock = 0; |
870 | u8 unlock_detected = 0; |
871 | u8 unlock_detected_sub = 0; |
872 | |
873 | if (!tnr_dmd || !lock) |
874 | return -EINVAL; |
875 | |
876 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) |
877 | return -EINVAL; |
878 | |
879 | if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE) |
880 | return -EINVAL; |
881 | |
882 | ret = |
883 | cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, sync_stat: &sync_stat, ts_lock_stat: &ts_lock, |
884 | unlock_detected: &unlock_detected); |
885 | if (ret) |
886 | return ret; |
887 | |
888 | if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) { |
889 | if (ts_lock) |
890 | *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; |
891 | else if (unlock_detected) |
892 | *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; |
893 | else |
894 | *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; |
895 | |
896 | return 0; |
897 | } |
898 | |
899 | if (ts_lock) { |
900 | *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED; |
901 | return 0; |
902 | } else if (!unlock_detected) { |
903 | *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; |
904 | return 0; |
905 | } |
906 | |
907 | ret = |
908 | cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, sync_stat: &sync_stat, |
909 | unlock_detected: &unlock_detected_sub); |
910 | if (ret) |
911 | return ret; |
912 | |
913 | if (unlock_detected && unlock_detected_sub) |
914 | *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED; |
915 | else |
916 | *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT; |
917 | |
918 | return 0; |
919 | } |
920 | |