Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * cxd2880_tnrdmd_dvbt_mon.c
0004  * Sony CXD2880 DVB-T2/T tuner + demodulator driver
0005  * DVB-T monitor functions
0006  *
0007  * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
0008  */
0009 
0010 #include "cxd2880_tnrdmd_mon.h"
0011 #include "cxd2880_tnrdmd_dvbt.h"
0012 #include "cxd2880_tnrdmd_dvbt_mon.h"
0013 
0014 #include <media/dvb_math.h>
0015 
0016 static const int ref_dbm_1000[3][5] = {
0017     {-93000, -91000, -90000, -89000, -88000},
0018     {-87000, -85000, -84000, -83000, -82000},
0019     {-82000, -80000, -78000, -77000, -76000},
0020 };
0021 
0022 static int is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd);
0023 
0024 int cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd
0025                       *tnr_dmd, u8 *sync_stat,
0026                       u8 *ts_lock_stat,
0027                       u8 *unlock_detected)
0028 {
0029     u8 rdata = 0x00;
0030     int ret;
0031 
0032     if (!tnr_dmd || !sync_stat || !ts_lock_stat || !unlock_detected)
0033         return -EINVAL;
0034 
0035     if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
0036         return -EINVAL;
0037     if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
0038         return -EINVAL;
0039 
0040     ret = tnr_dmd->io->write_reg(tnr_dmd->io,
0041                      CXD2880_IO_TGT_DMD,
0042                      0x00, 0x0d);
0043     if (ret)
0044         return ret;
0045 
0046     ret = tnr_dmd->io->read_regs(tnr_dmd->io,
0047                      CXD2880_IO_TGT_DMD,
0048                      0x10, &rdata, 1);
0049     if (ret)
0050         return ret;
0051 
0052     *unlock_detected = (rdata & 0x10) ? 1 : 0;
0053     *sync_stat = rdata & 0x07;
0054     *ts_lock_stat = (rdata & 0x20) ? 1 : 0;
0055 
0056     if (*sync_stat == 0x07)
0057         return -EAGAIN;
0058 
0059     return ret;
0060 }
0061 
0062 int cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd
0063                       *tnr_dmd, u8 *sync_stat,
0064                       u8 *unlock_detected)
0065 {
0066     u8 ts_lock_stat = 0;
0067 
0068     if (!tnr_dmd || !sync_stat || !unlock_detected)
0069         return -EINVAL;
0070 
0071     if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
0072         return -EINVAL;
0073 
0074     return cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd->diver_sub,
0075                          sync_stat,
0076                          &ts_lock_stat,
0077                          unlock_detected);
0078 }
0079 
0080 int cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd
0081                        *tnr_dmd,
0082                        enum cxd2880_dvbt_mode
0083                        *mode,
0084                        enum cxd2880_dvbt_guard
0085                        *guard)
0086 {
0087     u8 rdata = 0x00;
0088     int ret;
0089 
0090     if (!tnr_dmd || !mode || !guard)
0091         return -EINVAL;
0092 
0093     if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
0094         return -EINVAL;
0095 
0096     if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
0097         return -EINVAL;
0098 
0099     ret = slvt_freeze_reg(tnr_dmd);
0100     if (ret)
0101         return ret;
0102 
0103     ret = is_tps_locked(tnr_dmd);
0104     if (ret) {
0105         slvt_unfreeze_reg(tnr_dmd);
0106 
0107         if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
0108             ret =
0109                 cxd2880_tnrdmd_dvbt_mon_mode_guard(tnr_dmd->diver_sub,
0110                                    mode, guard);
0111 
0112         return ret;
0113     }
0114 
0115     ret = tnr_dmd->io->write_reg(tnr_dmd->io,
0116                      CXD2880_IO_TGT_DMD,
0117                      0x00, 0x0d);
0118     if (ret) {
0119         slvt_unfreeze_reg(tnr_dmd);
0120         return ret;
0121     }
0122 
0123     ret = tnr_dmd->io->read_regs(tnr_dmd->io,
0124                      CXD2880_IO_TGT_DMD,
0125                      0x1b, &rdata, 1);
0126     if (ret) {
0127         slvt_unfreeze_reg(tnr_dmd);
0128         return ret;
0129     }
0130 
0131     slvt_unfreeze_reg(tnr_dmd);
0132 
0133     *mode = (enum cxd2880_dvbt_mode)((rdata >> 2) & 0x03);
0134     *guard = (enum cxd2880_dvbt_guard)(rdata & 0x03);
0135 
0136     return ret;
0137 }
0138 
0139 int cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd
0140                        *tnr_dmd, int *offset)
0141 {
0142     u8 rdata[4];
0143     u32 ctl_val = 0;
0144     int ret;
0145 
0146     if (!tnr_dmd || !offset)
0147         return -EINVAL;
0148 
0149     if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
0150         return -EINVAL;
0151 
0152     if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
0153         return -EINVAL;
0154 
0155     ret = slvt_freeze_reg(tnr_dmd);
0156     if (ret)
0157         return ret;
0158 
0159     ret = is_tps_locked(tnr_dmd);
0160     if (ret) {
0161         slvt_unfreeze_reg(tnr_dmd);
0162         return ret;
0163     }
0164 
0165     ret = tnr_dmd->io->write_reg(tnr_dmd->io,
0166                      CXD2880_IO_TGT_DMD,
0167                      0x00, 0x0d);
0168     if (ret) {
0169         slvt_unfreeze_reg(tnr_dmd);
0170         return ret;
0171     }
0172 
0173     ret = tnr_dmd->io->read_regs(tnr_dmd->io,
0174                      CXD2880_IO_TGT_DMD,
0175                      0x1d, rdata, 4);
0176     if (ret) {
0177         slvt_unfreeze_reg(tnr_dmd);
0178         return ret;
0179     }
0180 
0181     slvt_unfreeze_reg(tnr_dmd);
0182 
0183     ctl_val =
0184         ((rdata[0] & 0x1f) << 24) | (rdata[1] << 16) | (rdata[2] << 8) |
0185         (rdata[3]);
0186     *offset = cxd2880_convert2s_complement(ctl_val, 29);
0187     *offset = -1 * ((*offset) * tnr_dmd->bandwidth / 235);
0188 
0189     return ret;
0190 }
0191 
0192 int cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct
0193                            cxd2880_tnrdmd
0194                            *tnr_dmd,
0195                            int *offset)
0196 {
0197     if (!tnr_dmd || !offset)
0198         return -EINVAL;
0199 
0200     if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
0201         return -EINVAL;
0202 
0203     return cxd2880_tnrdmd_dvbt_mon_carrier_offset(tnr_dmd->diver_sub,
0204                               offset);
0205 }
0206 
0207 int cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd
0208                      *tnr_dmd,
0209                      struct cxd2880_dvbt_tpsinfo
0210                      *info)
0211 {
0212     u8 rdata[7];
0213     u8 cell_id_ok = 0;
0214     int ret;
0215 
0216     if (!tnr_dmd || !info)
0217         return -EINVAL;
0218 
0219     if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
0220         return -EINVAL;
0221 
0222     if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
0223         return -EINVAL;
0224 
0225     ret = slvt_freeze_reg(tnr_dmd);
0226     if (ret)
0227         return ret;
0228 
0229     ret = is_tps_locked(tnr_dmd);
0230     if (ret) {
0231         slvt_unfreeze_reg(tnr_dmd);
0232 
0233         if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
0234             ret =
0235                 cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd->diver_sub,
0236                                  info);
0237 
0238         return ret;
0239     }
0240 
0241     ret = tnr_dmd->io->write_reg(tnr_dmd->io,
0242                      CXD2880_IO_TGT_DMD,
0243                      0x00, 0x0d);
0244     if (ret) {
0245         slvt_unfreeze_reg(tnr_dmd);
0246         return ret;
0247     }
0248 
0249     ret = tnr_dmd->io->read_regs(tnr_dmd->io,
0250                      CXD2880_IO_TGT_DMD,
0251                      0x29, rdata, 7);
0252     if (ret) {
0253         slvt_unfreeze_reg(tnr_dmd);
0254         return ret;
0255     }
0256 
0257     ret = tnr_dmd->io->write_reg(tnr_dmd->io,
0258                      CXD2880_IO_TGT_DMD,
0259                      0x00, 0x11);
0260     if (ret) {
0261         slvt_unfreeze_reg(tnr_dmd);
0262         return ret;
0263     }
0264 
0265     ret = tnr_dmd->io->read_regs(tnr_dmd->io,
0266                      CXD2880_IO_TGT_DMD,
0267                      0xd5, &cell_id_ok, 1);
0268     if (ret) {
0269         slvt_unfreeze_reg(tnr_dmd);
0270         return ret;
0271     }
0272 
0273     slvt_unfreeze_reg(tnr_dmd);
0274 
0275     info->constellation =
0276         (enum cxd2880_dvbt_constellation)((rdata[0] >> 6) & 0x03);
0277     info->hierarchy = (enum cxd2880_dvbt_hierarchy)((rdata[0] >> 3) & 0x07);
0278     info->rate_hp = (enum cxd2880_dvbt_coderate)(rdata[0] & 0x07);
0279     info->rate_lp = (enum cxd2880_dvbt_coderate)((rdata[1] >> 5) & 0x07);
0280     info->guard = (enum cxd2880_dvbt_guard)((rdata[1] >> 3) & 0x03);
0281     info->mode = (enum cxd2880_dvbt_mode)((rdata[1] >> 1) & 0x03);
0282     info->fnum = (rdata[2] >> 6) & 0x03;
0283     info->length_indicator = rdata[2] & 0x3f;
0284     info->cell_id = (rdata[3] << 8) | rdata[4];
0285     info->reserved_even = rdata[5] & 0x3f;
0286     info->reserved_odd = rdata[6] & 0x3f;
0287 
0288     info->cell_id_ok = cell_id_ok & 0x01;
0289 
0290     return ret;
0291 }
0292 
0293 int cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct
0294                         cxd2880_tnrdmd
0295                         *tnr_dmd,
0296                         u32 *pen)
0297 {
0298     u8 rdata[3];
0299     int ret;
0300 
0301     if (!tnr_dmd || !pen)
0302         return -EINVAL;
0303 
0304     if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
0305         return -EINVAL;
0306 
0307     if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
0308         return -EINVAL;
0309 
0310     if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
0311         return -EINVAL;
0312 
0313     ret = tnr_dmd->io->write_reg(tnr_dmd->io,
0314                      CXD2880_IO_TGT_DMD,
0315                      0x00, 0x0d);
0316     if (ret)
0317         return ret;
0318 
0319     ret = tnr_dmd->io->read_regs(tnr_dmd->io,
0320                      CXD2880_IO_TGT_DMD,
0321                      0x26, rdata, 3);
0322     if (ret)
0323         return ret;
0324 
0325     if (!(rdata[0] & 0x01))
0326         return -EAGAIN;
0327 
0328     *pen = (rdata[1] << 8) | rdata[2];
0329 
0330     return ret;
0331 }
0332 
0333 int cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd
0334                        *tnr_dmd,
0335                         enum
0336                         cxd2880_tnrdmd_spectrum_sense
0337                         *sense)
0338 {
0339     u8 data = 0;
0340     int ret;
0341 
0342     if (!tnr_dmd || !sense)
0343         return -EINVAL;
0344 
0345     if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
0346         return -EINVAL;
0347 
0348     if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
0349         return -EINVAL;
0350 
0351     ret = slvt_freeze_reg(tnr_dmd);
0352     if (ret)
0353         return ret;
0354 
0355     ret = is_tps_locked(tnr_dmd);
0356     if (ret) {
0357         slvt_unfreeze_reg(tnr_dmd);
0358 
0359         if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
0360             ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(tnr_dmd->diver_sub,
0361                                      sense);
0362 
0363         return ret;
0364     }
0365 
0366     ret = tnr_dmd->io->write_reg(tnr_dmd->io,
0367                      CXD2880_IO_TGT_DMD,
0368                      0x00, 0x0d);
0369     if (ret) {
0370         slvt_unfreeze_reg(tnr_dmd);
0371         return ret;
0372     }
0373 
0374     ret = tnr_dmd->io->read_regs(tnr_dmd->io,
0375                      CXD2880_IO_TGT_DMD,
0376                      0x1c, &data, sizeof(data));
0377     if (ret) {
0378         slvt_unfreeze_reg(tnr_dmd);
0379         return ret;
0380     }
0381 
0382     slvt_unfreeze_reg(tnr_dmd);
0383 
0384     *sense =
0385         (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV :
0386         CXD2880_TNRDMD_SPECTRUM_NORMAL;
0387 
0388     return ret;
0389 }
0390 
0391 static int dvbt_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd,
0392                  u16 *reg_value)
0393 {
0394     u8 rdata[2];
0395     int ret;
0396 
0397     if (!tnr_dmd || !reg_value)
0398         return -EINVAL;
0399 
0400     ret = slvt_freeze_reg(tnr_dmd);
0401     if (ret)
0402         return ret;
0403 
0404     ret = is_tps_locked(tnr_dmd);
0405     if (ret) {
0406         slvt_unfreeze_reg(tnr_dmd);
0407         return ret;
0408     }
0409 
0410     ret = tnr_dmd->io->write_reg(tnr_dmd->io,
0411                      CXD2880_IO_TGT_DMD,
0412                      0x00, 0x0d);
0413     if (ret) {
0414         slvt_unfreeze_reg(tnr_dmd);
0415         return ret;
0416     }
0417 
0418     ret = tnr_dmd->io->read_regs(tnr_dmd->io,
0419                      CXD2880_IO_TGT_DMD,
0420                      0x13, rdata, 2);
0421     if (ret) {
0422         slvt_unfreeze_reg(tnr_dmd);
0423         return ret;
0424     }
0425 
0426     slvt_unfreeze_reg(tnr_dmd);
0427 
0428     *reg_value = (rdata[0] << 8) | rdata[1];
0429 
0430     return ret;
0431 }
0432 
0433 static int dvbt_calc_snr(struct cxd2880_tnrdmd *tnr_dmd,
0434              u32 reg_value, int *snr)
0435 {
0436     if (!tnr_dmd || !snr)
0437         return -EINVAL;
0438 
0439     if (reg_value == 0)
0440         return -EAGAIN;
0441 
0442     if (reg_value > 4996)
0443         reg_value = 4996;
0444 
0445     *snr = intlog10(reg_value) - intlog10(5350 - reg_value);
0446     *snr = (*snr + 839) / 1678 + 28500;
0447 
0448     return 0;
0449 }
0450 
0451 int cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
0452                 int *snr)
0453 {
0454     u16 reg_value = 0;
0455     int ret;
0456 
0457     if (!tnr_dmd || !snr)
0458         return -EINVAL;
0459 
0460     *snr = -1000 * 1000;
0461 
0462     if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
0463         return -EINVAL;
0464 
0465     if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
0466         return -EINVAL;
0467 
0468     if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
0469         return -EINVAL;
0470 
0471     if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
0472         ret = dvbt_read_snr_reg(tnr_dmd, &reg_value);
0473         if (ret)
0474             return ret;
0475 
0476         ret = dvbt_calc_snr(tnr_dmd, reg_value, snr);
0477     } else {
0478         int snr_main = 0;
0479         int snr_sub = 0;
0480 
0481         ret =
0482             cxd2880_tnrdmd_dvbt_mon_snr_diver(tnr_dmd, snr, &snr_main,
0483                               &snr_sub);
0484     }
0485 
0486     return ret;
0487 }
0488 
0489 int cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd
0490                       *tnr_dmd, int *snr,
0491                       int *snr_main, int *snr_sub)
0492 {
0493     u16 reg_value = 0;
0494     u32 reg_value_sum = 0;
0495     int ret;
0496 
0497     if (!tnr_dmd || !snr || !snr_main || !snr_sub)
0498         return -EINVAL;
0499 
0500     *snr = -1000 * 1000;
0501     *snr_main = -1000 * 1000;
0502     *snr_sub = -1000 * 1000;
0503 
0504     if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
0505         return -EINVAL;
0506 
0507     if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
0508         return -EINVAL;
0509 
0510     if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
0511         return -EINVAL;
0512 
0513     ret = dvbt_read_snr_reg(tnr_dmd, &reg_value);
0514     if (!ret) {
0515         ret = dvbt_calc_snr(tnr_dmd, reg_value, snr_main);
0516         if (ret)
0517             reg_value = 0;
0518     } else if (ret == -EAGAIN) {
0519         reg_value = 0;
0520     } else {
0521         return ret;
0522     }
0523 
0524     reg_value_sum += reg_value;
0525 
0526     ret = dvbt_read_snr_reg(tnr_dmd->diver_sub, &reg_value);
0527     if (!ret) {
0528         ret = dvbt_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub);
0529         if (ret)
0530             reg_value = 0;
0531     } else if (ret == -EAGAIN) {
0532         reg_value = 0;
0533     } else {
0534         return ret;
0535     }
0536 
0537     reg_value_sum += reg_value;
0538 
0539     return dvbt_calc_snr(tnr_dmd, reg_value_sum, snr);
0540 }
0541 
0542 int cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd
0543                         *tnr_dmd, int *ppm)
0544 {
0545     u8 ctl_val_reg[5];
0546     u8 nominal_rate_reg[5];
0547     u32 trl_ctl_val = 0;
0548     u32 trcg_nominal_rate = 0;
0549     int num;
0550     int den;
0551     s8 diff_upper = 0;
0552     int ret;
0553 
0554     if (!tnr_dmd || !ppm)
0555         return -EINVAL;
0556 
0557     if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
0558         return -EINVAL;
0559 
0560     if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
0561         return -EINVAL;
0562 
0563     ret = slvt_freeze_reg(tnr_dmd);
0564     if (ret)
0565         return ret;
0566 
0567     ret = is_tps_locked(tnr_dmd);
0568     if (ret) {
0569         slvt_unfreeze_reg(tnr_dmd);
0570         return ret;
0571     }
0572 
0573     ret = tnr_dmd->io->write_reg(tnr_dmd->io,
0574                      CXD2880_IO_TGT_DMD,
0575                      0x00, 0x0d);
0576     if (ret) {
0577         slvt_unfreeze_reg(tnr_dmd);
0578         return ret;
0579     }
0580 
0581     ret = tnr_dmd->io->read_regs(tnr_dmd->io,
0582                      CXD2880_IO_TGT_DMD,
0583                      0x21, ctl_val_reg,
0584                      sizeof(ctl_val_reg));
0585     if (ret) {
0586         slvt_unfreeze_reg(tnr_dmd);
0587         return ret;
0588     }
0589 
0590     ret = tnr_dmd->io->write_reg(tnr_dmd->io,
0591                      CXD2880_IO_TGT_DMD,
0592                      0x00, 0x04);
0593     if (ret) {
0594         slvt_unfreeze_reg(tnr_dmd);
0595         return ret;
0596     }
0597 
0598     ret = tnr_dmd->io->read_regs(tnr_dmd->io,
0599                      CXD2880_IO_TGT_DMD,
0600                      0x60, nominal_rate_reg,
0601                      sizeof(nominal_rate_reg));
0602     if (ret) {
0603         slvt_unfreeze_reg(tnr_dmd);
0604         return ret;
0605     }
0606 
0607     slvt_unfreeze_reg(tnr_dmd);
0608 
0609     diff_upper =
0610         (ctl_val_reg[0] & 0x7f) - (nominal_rate_reg[0] & 0x7f);
0611 
0612     if (diff_upper < -1 || diff_upper > 1)
0613         return -EAGAIN;
0614 
0615     trl_ctl_val = ctl_val_reg[1] << 24;
0616     trl_ctl_val |= ctl_val_reg[2] << 16;
0617     trl_ctl_val |= ctl_val_reg[3] << 8;
0618     trl_ctl_val |= ctl_val_reg[4];
0619 
0620     trcg_nominal_rate = nominal_rate_reg[1] << 24;
0621     trcg_nominal_rate |= nominal_rate_reg[2] << 16;
0622     trcg_nominal_rate |= nominal_rate_reg[3] << 8;
0623     trcg_nominal_rate |= nominal_rate_reg[4];
0624 
0625     trl_ctl_val >>= 1;
0626     trcg_nominal_rate >>= 1;
0627 
0628     if (diff_upper == 1)
0629         num =
0630             (int)((trl_ctl_val + 0x80000000u) -
0631               trcg_nominal_rate);
0632     else if (diff_upper == -1)
0633         num =
0634             -(int)((trcg_nominal_rate + 0x80000000u) -
0635                trl_ctl_val);
0636     else
0637         num = (int)(trl_ctl_val - trcg_nominal_rate);
0638 
0639     den = (nominal_rate_reg[0] & 0x7f) << 24;
0640     den |= nominal_rate_reg[1] << 16;
0641     den |= nominal_rate_reg[2] << 8;
0642     den |= nominal_rate_reg[3];
0643     den = (den + (390625 / 2)) / 390625;
0644 
0645     den >>= 1;
0646 
0647     if (num >= 0)
0648         *ppm = (num + (den / 2)) / den;
0649     else
0650         *ppm = (num - (den / 2)) / den;
0651 
0652     return ret;
0653 }
0654 
0655 int cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct
0656                         cxd2880_tnrdmd
0657                         *tnr_dmd, int *ppm)
0658 {
0659     if (!tnr_dmd || !ppm)
0660         return -EINVAL;
0661 
0662     if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
0663         return -EINVAL;
0664 
0665     return cxd2880_tnrdmd_dvbt_mon_sampling_offset(tnr_dmd->diver_sub, ppm);
0666 }
0667 
0668 static int dvbt_calc_ssi(struct cxd2880_tnrdmd *tnr_dmd,
0669              int rf_lvl, u8 *ssi)
0670 {
0671     struct cxd2880_dvbt_tpsinfo tps;
0672     int prel;
0673     int temp_ssi = 0;
0674     int ret;
0675 
0676     if (!tnr_dmd || !ssi)
0677         return -EINVAL;
0678 
0679     ret = cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd, &tps);
0680     if (ret)
0681         return ret;
0682 
0683     if (tps.constellation >= CXD2880_DVBT_CONSTELLATION_RESERVED_3 ||
0684         tps.rate_hp >= CXD2880_DVBT_CODERATE_RESERVED_5)
0685         return -EINVAL;
0686 
0687     prel = rf_lvl - ref_dbm_1000[tps.constellation][tps.rate_hp];
0688 
0689     if (prel < -15000)
0690         temp_ssi = 0;
0691     else if (prel < 0)
0692         temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000;
0693     else if (prel < 20000)
0694         temp_ssi = (((4 * prel) + 500) / 1000) + 10;
0695     else if (prel < 35000)
0696         temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90;
0697     else
0698         temp_ssi = 100;
0699 
0700     *ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi;
0701 
0702     return ret;
0703 }
0704 
0705 int cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
0706                 u8 *ssi)
0707 {
0708     int rf_lvl = 0;
0709     int ret;
0710 
0711     if (!tnr_dmd || !ssi)
0712         return -EINVAL;
0713 
0714     if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
0715         return -EINVAL;
0716 
0717     if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
0718         return -EINVAL;
0719 
0720     if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
0721         return -EINVAL;
0722 
0723     ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl);
0724     if (ret)
0725         return ret;
0726 
0727     return dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
0728 }
0729 
0730 int cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd,
0731                     u8 *ssi)
0732 {
0733     int rf_lvl = 0;
0734     int ret;
0735 
0736     if (!tnr_dmd || !ssi)
0737         return -EINVAL;
0738 
0739     if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
0740         return -EINVAL;
0741 
0742     if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
0743         return -EINVAL;
0744 
0745     if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
0746         return -EINVAL;
0747 
0748     ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl);
0749     if (ret)
0750         return ret;
0751 
0752     return dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
0753 }
0754 
0755 static int is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd)
0756 {
0757     u8 sync = 0;
0758     u8 tslock = 0;
0759     u8 early_unlock = 0;
0760     int ret;
0761 
0762     if (!tnr_dmd)
0763         return -EINVAL;
0764 
0765     ret =
0766         cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync, &tslock,
0767                           &early_unlock);
0768     if (ret)
0769         return ret;
0770 
0771     if (sync != 6)
0772         return -EAGAIN;
0773 
0774     return 0;
0775 }