aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2013-01-16 13:12:05 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2013-01-23 16:10:49 -0500
commit25188bd0e66f244d8111c4459f2c2f262a13d272 (patch)
treec14fb32b183e2ab6246ebe34e0531d007e4b1c24 /drivers/media
parentd01a8ee37afdeb1a00458a79854a1672ada5c9f0 (diff)
[media] mb86a20s: add CNR measurement
Add Signal/Noise ratio measurement. On this device, a global measure is taken by the demod. It also provides per-layer CNR measurements, based on Modulation Error measures (MER). Reviewed-by: Antti Palosaari <crope@iki.fi> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.c336
1 files changed, 334 insertions, 2 deletions
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index c68e4676e5fd..4f3e222a2bcf 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -118,10 +118,12 @@ static struct regdata mb86a20s_init[] = {
118 { 0x50, 0xb5 }, { 0x51, 0xff }, 118 { 0x50, 0xb5 }, { 0x51, 0xff },
119 { 0x50, 0xb6 }, { 0x51, 0xff }, 119 { 0x50, 0xb6 }, { 0x51, 0xff },
120 { 0x50, 0xb7 }, { 0x51, 0xff }, 120 { 0x50, 0xb7 }, { 0x51, 0xff },
121 { 0x50, 0x50 }, { 0x51, 0x02 }, 121
122 { 0x50, 0x50 }, { 0x51, 0x02 }, /* MER manual mode */
122 { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */ 123 { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */
123 { 0x45, 0x04 }, /* CN symbol 4 */ 124 { 0x45, 0x04 }, /* CN symbol 4 */
124 { 0x48, 0x04 }, 125 { 0x48, 0x04 }, /* CN manual mode */
126
125 { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ 127 { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */
126 { 0x50, 0xd6 }, { 0x51, 0x1f }, 128 { 0x50, 0xd6 }, { 0x51, 0x1f },
127 { 0x50, 0xd2 }, { 0x51, 0x03 }, 129 { 0x50, 0xd2 }, { 0x51, 0x03 },
@@ -891,6 +893,330 @@ static int mb86a20s_get_ber_before_vterbi(struct dvb_frontend *fe,
891 return 0; 893 return 0;
892} 894}
893 895
896struct linear_segments {
897 unsigned x, y;
898};
899
900/*
901 * All tables below return a dB/1000 measurement
902 */
903
904static struct linear_segments cnr_to_db_table[] = {
905 { 19648, 0},
906 { 18187, 1000},
907 { 16534, 2000},
908 { 14823, 3000},
909 { 13161, 4000},
910 { 11622, 5000},
911 { 10279, 6000},
912 { 9089, 7000},
913 { 8042, 8000},
914 { 7137, 9000},
915 { 6342, 10000},
916 { 5641, 11000},
917 { 5030, 12000},
918 { 4474, 13000},
919 { 3988, 14000},
920 { 3556, 15000},
921 { 3180, 16000},
922 { 2841, 17000},
923 { 2541, 18000},
924 { 2276, 19000},
925 { 2038, 20000},
926 { 1800, 21000},
927 { 1625, 22000},
928 { 1462, 23000},
929 { 1324, 24000},
930 { 1175, 25000},
931 { 1063, 26000},
932 { 980, 27000},
933 { 907, 28000},
934 { 840, 29000},
935 { 788, 30000},
936};
937
938static struct linear_segments cnr_64qam_table[] = {
939 { 3922688, 0},
940 { 3920384, 1000},
941 { 3902720, 2000},
942 { 3894784, 3000},
943 { 3882496, 4000},
944 { 3872768, 5000},
945 { 3858944, 6000},
946 { 3851520, 7000},
947 { 3838976, 8000},
948 { 3829248, 9000},
949 { 3818240, 10000},
950 { 3806976, 11000},
951 { 3791872, 12000},
952 { 3767040, 13000},
953 { 3720960, 14000},
954 { 3637504, 15000},
955 { 3498496, 16000},
956 { 3296000, 17000},
957 { 3031040, 18000},
958 { 2715392, 19000},
959 { 2362624, 20000},
960 { 1963264, 21000},
961 { 1649664, 22000},
962 { 1366784, 23000},
963 { 1120768, 24000},
964 { 890880, 25000},
965 { 723456, 26000},
966 { 612096, 27000},
967 { 518912, 28000},
968 { 448256, 29000},
969 { 388864, 30000},
970};
971
972static struct linear_segments cnr_16qam_table[] = {
973 { 5314816, 0},
974 { 5219072, 1000},
975 { 5118720, 2000},
976 { 4998912, 3000},
977 { 4875520, 4000},
978 { 4736000, 5000},
979 { 4604160, 6000},
980 { 4458752, 7000},
981 { 4300288, 8000},
982 { 4092928, 9000},
983 { 3836160, 10000},
984 { 3521024, 11000},
985 { 3155968, 12000},
986 { 2756864, 13000},
987 { 2347008, 14000},
988 { 1955072, 15000},
989 { 1593600, 16000},
990 { 1297920, 17000},
991 { 1043968, 18000},
992 { 839680, 19000},
993 { 672256, 20000},
994 { 523008, 21000},
995 { 424704, 22000},
996 { 345088, 23000},
997 { 280064, 24000},
998 { 221440, 25000},
999 { 179712, 26000},
1000 { 151040, 27000},
1001 { 128512, 28000},
1002 { 110080, 29000},
1003 { 95744, 30000},
1004};
1005
1006struct linear_segments cnr_qpsk_table[] = {
1007 { 2834176, 0},
1008 { 2683648, 1000},
1009 { 2536960, 2000},
1010 { 2391808, 3000},
1011 { 2133248, 4000},
1012 { 1906176, 5000},
1013 { 1666560, 6000},
1014 { 1422080, 7000},
1015 { 1189632, 8000},
1016 { 976384, 9000},
1017 { 790272, 10000},
1018 { 633344, 11000},
1019 { 505600, 12000},
1020 { 402944, 13000},
1021 { 320768, 14000},
1022 { 255488, 15000},
1023 { 204032, 16000},
1024 { 163072, 17000},
1025 { 130304, 18000},
1026 { 105216, 19000},
1027 { 83456, 20000},
1028 { 65024, 21000},
1029 { 52480, 22000},
1030 { 42752, 23000},
1031 { 34560, 24000},
1032 { 27136, 25000},
1033 { 22016, 26000},
1034 { 18432, 27000},
1035 { 15616, 28000},
1036 { 13312, 29000},
1037 { 11520, 30000},
1038};
1039
1040static u32 interpolate_value(u32 value, struct linear_segments *segments,
1041 unsigned len)
1042{
1043 u64 tmp64;
1044 u32 dx, dy;
1045 int i, ret;
1046
1047 if (value >= segments[0].x)
1048 return segments[0].y;
1049 if (value < segments[len-1].x)
1050 return segments[len-1].y;
1051
1052 for (i = 1; i < len - 1; i++) {
1053 /* If value is identical, no need to interpolate */
1054 if (value == segments[i].x)
1055 return segments[i].y;
1056 if (value > segments[i].x)
1057 break;
1058 }
1059
1060 /* Linear interpolation between the two (x,y) points */
1061 dy = segments[i].y - segments[i - 1].y;
1062 dx = segments[i - 1].x - segments[i].x;
1063 tmp64 = value - segments[i].x;
1064 tmp64 *= dy;
1065 do_div(tmp64, dx);
1066 ret = segments[i].y - tmp64;
1067
1068 return ret;
1069}
1070
1071static int mb86a20s_get_main_CNR(struct dvb_frontend *fe)
1072{
1073 struct mb86a20s_state *state = fe->demodulator_priv;
1074 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
1075 u32 cnr_linear, cnr;
1076 int rc, val;
1077
1078 /* Check if CNR is available */
1079 rc = mb86a20s_readreg(state, 0x45);
1080 if (rc < 0)
1081 return rc;
1082
1083 if (!(rc & 0x40)) {
1084 dev_info(&state->i2c->dev, "%s: CNR is not available yet.\n",
1085 __func__);
1086 return -EBUSY;
1087 }
1088 val = rc;
1089
1090 rc = mb86a20s_readreg(state, 0x46);
1091 if (rc < 0)
1092 return rc;
1093 cnr_linear = rc << 8;
1094
1095 rc = mb86a20s_readreg(state, 0x46);
1096 if (rc < 0)
1097 return rc;
1098 cnr_linear |= rc;
1099
1100 cnr = interpolate_value(cnr_linear,
1101 cnr_to_db_table, ARRAY_SIZE(cnr_to_db_table));
1102
1103 c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
1104 c->cnr.stat[0].svalue = cnr;
1105
1106 dev_dbg(&state->i2c->dev, "%s: CNR is %d.%03d dB (%d)\n",
1107 __func__, cnr / 1000, cnr % 1000, cnr_linear);
1108
1109 /* CNR counter reset */
1110 rc = mb86a20s_writereg(state, 0x45, val | 0x10);
1111 if (rc < 0)
1112 return rc;
1113 rc = mb86a20s_writereg(state, 0x45, val & 0x6f);
1114
1115 return rc;
1116}
1117
1118static int mb86a20s_get_per_layer_CNR(struct dvb_frontend *fe)
1119{
1120 struct mb86a20s_state *state = fe->demodulator_priv;
1121 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
1122 u32 mer, cnr;
1123 int rc, val, i;
1124 struct linear_segments *segs;
1125 unsigned segs_len;
1126
1127 dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
1128
1129 /* Check if the measures are already available */
1130 rc = mb86a20s_writereg(state, 0x50, 0x5b);
1131 if (rc < 0)
1132 return rc;
1133 rc = mb86a20s_readreg(state, 0x51);
1134 if (rc < 0)
1135 return rc;
1136
1137 /* Check if data is available */
1138 if (!(rc & 0x01)) {
1139 dev_info(&state->i2c->dev,
1140 "%s: MER measures aren't available yet.\n", __func__);
1141 return -EBUSY;
1142 }
1143
1144 /* Read all layers */
1145 for (i = 0; i < 3; i++) {
1146 if (!(c->isdbt_layer_enabled & (1 << i))) {
1147 c->cnr.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
1148 continue;
1149 }
1150
1151 rc = mb86a20s_writereg(state, 0x50, 0x52 + i * 3);
1152 if (rc < 0)
1153 return rc;
1154 rc = mb86a20s_readreg(state, 0x51);
1155 if (rc < 0)
1156 return rc;
1157 mer = rc << 16;
1158 rc = mb86a20s_writereg(state, 0x50, 0x53 + i * 3);
1159 if (rc < 0)
1160 return rc;
1161 rc = mb86a20s_readreg(state, 0x51);
1162 if (rc < 0)
1163 return rc;
1164 mer |= rc << 8;
1165 rc = mb86a20s_writereg(state, 0x50, 0x54 + i * 3);
1166 if (rc < 0)
1167 return rc;
1168 rc = mb86a20s_readreg(state, 0x51);
1169 if (rc < 0)
1170 return rc;
1171 mer |= rc;
1172
1173 switch (c->layer[i].modulation) {
1174 case DQPSK:
1175 case QPSK:
1176 segs = cnr_qpsk_table;
1177 segs_len = ARRAY_SIZE(cnr_qpsk_table);
1178 break;
1179 case QAM_16:
1180 segs = cnr_16qam_table;
1181 segs_len = ARRAY_SIZE(cnr_16qam_table);
1182 break;
1183 default:
1184 case QAM_64:
1185 segs = cnr_64qam_table;
1186 segs_len = ARRAY_SIZE(cnr_64qam_table);
1187 break;
1188 }
1189 cnr = interpolate_value(mer, segs, segs_len);
1190
1191 c->cnr.stat[1 + i].scale = FE_SCALE_DECIBEL;
1192 c->cnr.stat[1 + i].svalue = cnr;
1193
1194 dev_dbg(&state->i2c->dev,
1195 "%s: CNR for layer %c is %d.%03d dB (MER = %d).\n",
1196 __func__, 'A' + i, cnr / 1000, cnr % 1000, mer);
1197
1198 }
1199
1200 /* Start a new MER measurement */
1201 /* MER counter reset */
1202 rc = mb86a20s_writereg(state, 0x50, 0x50);
1203 if (rc < 0)
1204 return rc;
1205 rc = mb86a20s_readreg(state, 0x51);
1206 if (rc < 0)
1207 return rc;
1208 val = rc;
1209
1210 rc = mb86a20s_writereg(state, 0x51, val | 0x01);
1211 if (rc < 0)
1212 return rc;
1213 rc = mb86a20s_writereg(state, 0x51, val & 0x06);
1214 if (rc < 0)
1215 return rc;
1216
1217 return 0;
1218}
1219
894static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) 1220static void mb86a20s_stats_not_ready(struct dvb_frontend *fe)
895{ 1221{
896 struct mb86a20s_state *state = fe->demodulator_priv; 1222 struct mb86a20s_state *state = fe->demodulator_priv;
@@ -934,7 +1260,13 @@ static int mb86a20s_get_stats(struct dvb_frontend *fe)
934 u32 t_pre_bit_error = 0, t_pre_bit_count = 0; 1260 u32 t_pre_bit_error = 0, t_pre_bit_count = 0;
935 int active_layers = 0, ber_layers = 0; 1261 int active_layers = 0, ber_layers = 0;
936 1262
1263 dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
1264
1265 mb86a20s_get_main_CNR(fe);
1266
937 /* Get per-layer stats */ 1267 /* Get per-layer stats */
1268 mb86a20s_get_per_layer_CNR(fe);
1269
938 for (i = 0; i < 3; i++) { 1270 for (i = 0; i < 3; i++) {
939 if (c->isdbt_layer_enabled & (1 << i)) { 1271 if (c->isdbt_layer_enabled & (1 << i)) {
940 /* Layer is active and has rc segments */ 1272 /* Layer is active and has rc segments */