summaryrefslogblamecommitdiffstats
path: root/drivers/media/dvb/frontends/dib0090.c
blob: 65240b7801e83c9dbfed88aa03ec1ea0eb94ff28 (plain) (tree)
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442


























                                                                      
                       



























































































































                                                                                                                       

                                                                                            










                                                                           
                                                                                                   























































































































                                                                                                                                                                                 
                                                       


                                                     
                                                  
            
                                                  
 
                                
 


































































































































































































































































































































                                                                                                                                                                                       
                                                          

         
                                      
 










































































































































                                                                                                                                                                                     
                                                                                                                            








                                                                                                                     
                                    
 











                                                                                                           
                                        
 




                                                      
                                      
 

































































































                                                                                                            
                                              


































































































































































































































































































































































































































































































































































                                                                                                                                                      








                                                                                        

































































                                                                                                                            





                                                                    
/*
 * Linux-DVB Driver for DiBcom's DiB0090 base-band RF Tuner.
 *
 * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 * This code is more or less generated from another driver, please
 * excuse some codingstyle oddities.
 *
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>

#include "dvb_frontend.h"

#include "dib0090.h"
#include "dibx000_common.h"

static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");

#define dprintk(args...) do { \
	if (debug) { \
		printk(KERN_DEBUG "DiB0090: "); \
		printk(args); \
		printk("\n"); \
	} \
} while (0)

#define CONFIG_SYS_ISDBT
#define CONFIG_BAND_CBAND
#define CONFIG_BAND_VHF
#define CONFIG_BAND_UHF
#define CONFIG_DIB0090_USE_PWM_AGC

#define EN_LNA0      0x8000
#define EN_LNA1      0x4000
#define EN_LNA2      0x2000
#define EN_LNA3      0x1000
#define EN_MIX0      0x0800
#define EN_MIX1      0x0400
#define EN_MIX2      0x0200
#define EN_MIX3      0x0100
#define EN_IQADC     0x0040
#define EN_PLL       0x0020
#define EN_TX        0x0010
#define EN_BB        0x0008
#define EN_LO        0x0004
#define EN_BIAS      0x0001

#define EN_IQANA     0x0002
#define EN_DIGCLK    0x0080	/* not in the 0x24 reg, only in 0x1b */
#define EN_CRYSTAL   0x0002

#define EN_UHF		 0x22E9
#define EN_VHF		 0x44E9
#define EN_LBD		 0x11E9
#define EN_SBD		 0x44E9
#define EN_CAB		 0x88E9

#define pgm_read_word(w) (*w)

struct dc_calibration;

struct dib0090_tuning {
	u32 max_freq;		/* for every frequency less than or equal to that field: this information is correct */
	u8 switch_trim;
	u8 lna_tune;
	u8 lna_bias;
	u16 v2i;
	u16 mix;
	u16 load;
	u16 tuner_enable;
};

struct dib0090_pll {
	u32 max_freq;		/* for every frequency less than or equal to that field: this information is correct */
	u8 vco_band;
	u8 hfdiv_code;
	u8 hfdiv;
	u8 topresc;
};

struct dib0090_state {
	struct i2c_adapter *i2c;
	struct dvb_frontend *fe;
	const struct dib0090_config *config;

	u8 current_band;
	u16 revision;
	enum frontend_tune_state tune_state;
	u32 current_rf;

	u16 wbd_offset;
	s16 wbd_target;		/* in dB */

	s16 rf_gain_limit;	/* take-over-point: where to split between bb and rf gain */
	s16 current_gain;	/* keeps the currently programmed gain */
	u8 agc_step;		/* new binary search */

	u16 gain[2];		/* for channel monitoring */

	const u16 *rf_ramp;
	const u16 *bb_ramp;

	/* for the software AGC ramps */
	u16 bb_1_def;
	u16 rf_lt_def;
	u16 gain_reg[4];

	/* for the captrim/dc-offset search */
	s8 step;
	s16 adc_diff;
	s16 min_adc_diff;

	s8 captrim;
	s8 fcaptrim;

	const struct dc_calibration *dc;
	u16 bb6, bb7;

	const struct dib0090_tuning *current_tune_table_index;
	const struct dib0090_pll *current_pll_table_index;

	u8 tuner_is_tuned;
	u8 agc_freeze;

	u8 reset;
};

static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg)
{
	u8 b[2];
	struct i2c_msg msg[2] = {
		{.addr = state->config->i2c_address, .flags = 0, .buf = &reg, .len = 1},
		{.addr = state->config->i2c_address, .flags = I2C_M_RD, .buf = b, .len = 2},
	};
	if (i2c_transfer(state->i2c, msg, 2) != 2) {
		printk(KERN_WARNING "DiB0090 I2C read failed\n");
		return 0;
	}
	return (b[0] << 8) | b[1];
}

static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val)
{
	u8 b[3] = { reg & 0xff, val >> 8, val & 0xff };
	struct i2c_msg msg = {.addr = state->config->i2c_address, .flags = 0, .buf = b, .len = 3 };
	if (i2c_transfer(state->i2c, &msg, 1) != 1) {
		printk(KERN_WARNING "DiB0090 I2C write failed\n");
		return -EREMOTEIO;
	}
	return 0;
}

#define HARD_RESET(state) do {  if (cfg->reset) {  if (cfg->sleep) cfg->sleep(fe, 0); msleep(10);  cfg->reset(fe, 1); msleep(10);  cfg->reset(fe, 0); msleep(10);  }  } while (0)
#define ADC_TARGET -220
#define GAIN_ALPHA 5
#define WBD_ALPHA 6
#define LPF	100
static void dib0090_write_regs(struct dib0090_state *state, u8 r, const u16 * b, u8 c)
{
	do {
		dib0090_write_reg(state, r++, *b++);
	} while (--c);
}

static u16 dib0090_identify(struct dvb_frontend *fe)
{
	struct dib0090_state *state = fe->tuner_priv;
	u16 v;

	v = dib0090_read_reg(state, 0x1a);

#ifdef FIRMWARE_FIREFLY
	/* pll is not locked locked */
	if (!(v & 0x800))
		dprintk("FE%d : Identification : pll is not yet locked", fe->id);
#endif

	/* without PLL lock info */
	v &= 0x3ff;
	dprintk("P/V: %04x:", v);

	if ((v >> 8) & 0xf)
		dprintk("FE%d : Product ID = 0x%x : KROSUS", fe->id, (v >> 8) & 0xf);
	else
		return 0xff;

	v &= 0xff;
	if (((v >> 5) & 0x7) == 0x1)
		dprintk("FE%d : MP001 : 9090/8096", fe->id);
	else if (((v >> 5) & 0x7) == 0x4)
		dprintk("FE%d : MP005 : Single Sband", fe->id);
	else if (((v >> 5) & 0x7) == 0x6)
		dprintk("FE%d : MP008 : diversity VHF-UHF-LBAND", fe->id);
	else if (((v >> 5) & 0x7) == 0x7)
		dprintk("FE%d : MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND", fe->id);
	else
		return 0xff;

	/* revision only */
	if ((v & 0x1f) == 0x3)
		dprintk("FE%d : P1-D/E/F detected", fe->id);
	else if ((v & 0x1f) == 0x1)
		dprintk("FE%d : P1C detected", fe->id);
	else if ((v & 0x1f) == 0x0) {
#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT
		dprintk("FE%d : P1-A/B detected: using previous driver - support will be removed soon", fe->id);
		dib0090_p1b_register(fe);
#else
		dprintk("FE%d : P1-A/B detected: driver is deactivated - not available", fe->id);
		return 0xff;
#endif
	}

	return v;
}

static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg)
{
	struct dib0090_state *state = fe->tuner_priv;

	HARD_RESET(state);

	dib0090_write_reg(state, 0x24, EN_PLL);
	dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL);	/* PLL, DIG_CLK and CRYSTAL remain */

	/* adcClkOutRatio=8->7, release reset */
	dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0);
	if (cfg->clkoutdrive != 0)
		dib0090_write_reg(state, 0x23,
				  (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 10) | (1 << 9) | (0 << 8) | (cfg->clkoutdrive << 5) | (cfg->
																	   clkouttobamse
																	   << 4) | (0
																		    <<
																		    2)
				  | (0));
	else
		dib0090_write_reg(state, 0x23,
				  (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 10) | (1 << 9) | (0 << 8) | (7 << 5) | (cfg->
															    clkouttobamse << 4) | (0
																		   <<
																		   2)
				  | (0));

	/* enable pll, de-activate reset, ratio: 2/1 = 60MHz */
	dib0090_write_reg(state, 0x21,
			  (cfg->io.pll_bypass << 15) | (1 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv));

}

static int dib0090_wakeup(struct dvb_frontend *fe)
{
	struct dib0090_state *state = fe->tuner_priv;
	if (state->config->sleep)
		state->config->sleep(fe, 0);
	return 0;
}

static int dib0090_sleep(struct dvb_frontend *fe)
{
	struct dib0090_state *state = fe->tuner_priv;
	if (state->config->sleep)
		state->config->sleep(fe, 1);
	return 0;
}

void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast)
{
	struct dib0090_state *state = fe->tuner_priv;
	if (fast)
		dib0090_write_reg(state, 0x04, 0);
	else
		dib0090_write_reg(state, 0x04, 1);
}
EXPORT_SYMBOL(dib0090_dcc_freq);

static const u16 rf_ramp_pwm_cband[] = {
	0,			/* max RF gain in 10th of dB */
	0,			/* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */
	0,			/* ramp_max = maximum X used on the ramp */
	(0 << 10) | 0,		/* 0x2c, LNA 1 = 0dB */
	(0 << 10) | 0,		/* 0x2d, LNA 1 */
	(0 << 10) | 0,		/* 0x2e, LNA 2 = 0dB */
	(0 << 10) | 0,		/* 0x2f, LNA 2 */
	(0 << 10) | 0,		/* 0x30, LNA 3 = 0dB */
	(0 << 10) | 0,		/* 0x31, LNA 3 */
	(0 << 10) | 0,		/* GAIN_4_1, LNA 4 = 0dB */
	(0 << 10) | 0,		/* GAIN_4_2, LNA 4 */
};

static const u16 rf_ramp_vhf[] = {
	412,			/* max RF gain in 10th of dB */
	132, 307, 127,		/* LNA1,  13.2dB */
	105, 412, 255,		/* LNA2,  10.5dB */
	50, 50, 127,		/* LNA3,  5dB */
	125, 175, 127,		/* LNA4,  12.5dB */
	0, 0, 127,		/* CBAND, 0dB */
};

static const u16 rf_ramp_uhf[] = {
	412,			/* max RF gain in 10th of dB */
	132, 307, 127,		/* LNA1  : total gain = 13.2dB, point on the ramp where this amp is full gain, value to write to get full gain */
	105, 412, 255,		/* LNA2  : 10.5 dB */
	50, 50, 127,		/* LNA3  :  5.0 dB */
	125, 175, 127,		/* LNA4  : 12.5 dB */
	0, 0, 127,		/* CBAND :  0.0 dB */
};

static const u16 rf_ramp_cband[] = {
	332,			/* max RF gain in 10th of dB */
	132, 252, 127,		/* LNA1,  dB */
	80, 332, 255,		/* LNA2,  dB */
	0, 0, 127,		/* LNA3,  dB */
	0, 0, 127,		/* LNA4,  dB */
	120, 120, 127,		/* LT1 CBAND */
};

static const u16 rf_ramp_pwm_vhf[] = {
	404,			/* max RF gain in 10th of dB */
	25,			/* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */
	1011,			/* ramp_max = maximum X used on the ramp */
	(6 << 10) | 417,	/* 0x2c, LNA 1 = 13.2dB */
	(0 << 10) | 756,	/* 0x2d, LNA 1 */
	(16 << 10) | 756,	/* 0x2e, LNA 2 = 10.5dB */
	(0 << 10) | 1011,	/* 0x2f, LNA 2 */
	(16 << 10) | 290,	/* 0x30, LNA 3 = 5dB */
	(0 << 10) | 417,	/* 0x31, LNA 3 */
	(7 << 10) | 0,		/* GAIN_4_1, LNA 4 = 12.5dB */
	(0 << 10) | 290,	/* GAIN_4_2, LNA 4 */
};

static const u16 rf_ramp_pwm_uhf[] = {
	404,			/* max RF gain in 10th of dB */
	25,			/* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */
	1011,			/* ramp_max = maximum X used on the ramp */
	(6 << 10) | 417,	/* 0x2c, LNA 1 = 13.2dB */
	(0 << 10) | 756,	/* 0x2d, LNA 1 */
	(16 << 10) | 756,	/* 0x2e, LNA 2 = 10.5dB */
	(0 << 10) | 1011,	/* 0x2f, LNA 2 */
	(16 << 10) | 0,		/* 0x30, LNA 3 = 5dB */
	(0 << 10) | 127,	/* 0x31, LNA 3 */
	(7 << 10) | 127,	/* GAIN_4_1, LNA 4 = 12.5dB */
	(0 << 10) | 417,	/* GAIN_4_2, LNA 4 */
};

static const u16 bb_ramp_boost[] = {
	550,			/* max BB gain in 10th of dB */
	260, 260, 26,		/* BB1, 26dB */
	290, 550, 29,		/* BB2, 29dB */
};

static const u16 bb_ramp_pwm_normal[] = {
	500,			/* max RF gain in 10th of dB */
	8,			/* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x34 */
	400,
	(2 << 9) | 0,		/* 0x35 = 21dB */
	(0 << 9) | 168,		/* 0x36 */
	(2 << 9) | 168,		/* 0x37 = 29dB */
	(0 << 9) | 400,		/* 0x38 */
};

struct slope {
	int16_t range;
	int16_t slope;
};
static u16 slopes_to_scale(const struct slope *slopes, u8 num, s16 val)
{
	u8 i;
	u16 rest;
	u16 ret = 0;
	for (i = 0; i < num; i++) {
		if (val > slopes[i].range)
			rest = slopes[i].range;
		else
			rest = val;
		ret += (rest * slopes[i].slope) / slopes[i].range;
		val -= rest;
	}
	return ret;
}

static const struct slope dib0090_wbd_slopes[3] = {
	{66, 120},		/* -64,-52: offset -   65 */
	{600, 170},		/* -52,-35: 65     -  665 */
	{170, 250},		/* -45,-10: 665    - 835 */
};

static s16 dib0090_wbd_to_db(struct dib0090_state *state, u16 wbd)
{
	wbd &= 0x3ff;
	if (wbd < state->wbd_offset)
		wbd = 0;
	else
		wbd -= state->wbd_offset;
	/* -64dB is the floor */
	return -640 + (s16) slopes_to_scale(dib0090_wbd_slopes, ARRAY_SIZE(dib0090_wbd_slopes), wbd);
}

static void dib0090_wbd_target(struct dib0090_state *state, u32 rf)
{
	u16 offset = 250;

	/* TODO : DAB digital N+/-1 interferer perfs : offset = 10 */

	if (state->current_band == BAND_VHF)
		offset = 650;
#ifndef FIRMWARE_FIREFLY
	if (state->current_band == BAND_VHF)
		offset = state->config->wbd_vhf_offset;
	if (state->current_band == BAND_CBAND)
		offset = state->config->wbd_cband_offset;
#endif

	state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + offset);
	dprintk("wbd-target: %d dB", (u32) state->wbd_target);
}

static const int gain_reg_addr[4] = {
	0x08, 0x0a, 0x0f, 0x01
};

static void dib0090_gain_apply(struct dib0090_state *state, s16 gain_delta, s16 top_delta, u8 force)
{
	u16 rf, bb, ref;
	u16 i, v, gain_reg[4] = { 0 }, gain;
	const u16 *g;

	if (top_delta < -511)
		top_delta = -511;
	if (top_delta > 511)
		top_delta = 511;

	if (force) {
		top_delta *= (1 << WBD_ALPHA);
		gain_delta *= (1 << GAIN_ALPHA);
	}

	if (top_delta >= ((s16) (state->rf_ramp[0] << WBD_ALPHA) - state->rf_gain_limit))	/* overflow */
		state->rf_gain_limit = state->rf_ramp[0] << WBD_ALPHA;
	else
		state->rf_gain_limit += top_delta;

	if (state->rf_gain_limit < 0)	/*underflow */
		state->rf_gain_limit = 0;

	/* use gain as a temporary variable and correct current_gain */
	gain = ((state->rf_gain_limit >> WBD_ALPHA) + state->bb_ramp[0]) << GAIN_ALPHA;
	if (gain_delta >= ((s16) gain - state->current_gain))	/* overflow */
		state->current_gain = gain;
	else
		state->current_gain += gain_delta;
	/* cannot be less than 0 (only if gain_delta is less than 0 we can have current_gain < 0) */
	if (state->current_gain < 0)
		state->current_gain = 0;

	/* now split total gain to rf and bb gain */
	gain = state->current_gain >> GAIN_ALPHA;

	/* requested gain is bigger than rf gain limit - ACI/WBD adjustment */
	if (gain > (state->rf_gain_limit >> WBD_ALPHA)) {
		rf = state->rf_gain_limit >> WBD_ALPHA;
		bb = gain - rf;
		if (bb > state->bb_ramp[0])
			bb = state->bb_ramp[0];
	} else {		/* high signal level -> all gains put on RF */
		rf = gain;
		bb = 0;
	}

	state->gain[0] = rf;
	state->gain[1] = bb;

	/* software ramp */
	/* Start with RF gains */
	g = state->rf_ramp + 1;	/* point on RF LNA1 max gain */
	ref = rf;
	for (i = 0; i < 7; i++) {	/* Go over all amplifiers => 5RF amps + 2 BB amps = 7 amps */
		if (g[0] == 0 || ref < (g[1] - g[0]))	/* if total gain of the current amp is null or this amp is not concerned because it starts to work from an higher gain value */
			v = 0;	/* force the gain to write for the current amp to be null */
		else if (ref >= g[1])	/* Gain to set is higher than the high working point of this amp */
			v = g[2];	/* force this amp to be full gain */
		else		/* compute the value to set to this amp because we are somewhere in his range */
			v = ((ref - (g[1] - g[0])) * g[2]) / g[0];

		if (i == 0)	/* LNA 1 reg mapping */
			gain_reg[0] = v;
		else if (i == 1)	/* LNA 2 reg mapping */
			gain_reg[0] |= v << 7;
		else if (i == 2)	/* LNA 3 reg mapping */
			gain_reg[1] = v;
		else if (i == 3)	/* LNA 4 reg mapping */
			gain_reg[1] |= v << 7;
		else if (i == 4)	/* CBAND LNA reg mapping */
			gain_reg[2] = v | state->rf_lt_def;
		else if (i == 5)	/* BB gain 1 reg mapping */
			gain_reg[3] = v << 3;
		else if (i == 6)	/* BB gain 2 reg mapping */
			gain_reg[3] |= v << 8;

		g += 3;		/* go to next gain bloc */

		/* When RF is finished, start with BB */
		if (i == 4) {
			g = state->bb_ramp + 1;	/* point on BB gain 1 max gain */
			ref = bb;
		}
	}
	gain_reg[3] |= state->bb_1_def;
	gain_reg[3] |= ((bb % 10) * 100) / 125;

#ifdef DEBUG_AGC
	dprintk("GA CALC: DB: %3d(rf) + %3d(bb) = %3d gain_reg[0]=%04x gain_reg[1]=%04x gain_reg[2]=%04x gain_reg[0]=%04x", rf, bb, rf + bb,
		gain_reg[0], gain_reg[1], gain_reg[2], gain_reg[3]);
#endif

	/* Write the amplifier regs */
	for (i = 0; i < 4; i++) {
		v = gain_reg[i];
		if (force || state->gain_reg[i] != v) {
			state->gain_reg[i] = v;
			dib0090_write_reg(state, gain_reg_addr[i], v);
		}
	}
}

static void dib0090_set_boost(struct dib0090_state *state, int onoff)
{
	state->bb_1_def &= 0xdfff;
	state->bb_1_def |= onoff << 13;
}

static void dib0090_set_rframp(struct dib0090_state *state, const u16 * cfg)
{
	state->rf_ramp = cfg;
}

static void dib0090_set_rframp_pwm(struct dib0090_state *state, const u16 * cfg)
{
	state->rf_ramp = cfg;

	dib0090_write_reg(state, 0x2a, 0xffff);

	dprintk("total RF gain: %ddB, step: %d", (u32) cfg[0], dib0090_read_reg(state, 0x2a));

	dib0090_write_regs(state, 0x2c, cfg + 3, 6);
	dib0090_write_regs(state, 0x3e, cfg + 9, 2);
}

static void dib0090_set_bbramp(struct dib0090_state *state, const u16 * cfg)
{
	state->bb_ramp = cfg;
	dib0090_set_boost(state, cfg[0] > 500);	/* we want the boost if the gain is higher that 50dB */
}

static void dib0090_set_bbramp_pwm(struct dib0090_state *state, const u16 * cfg)
{
	state->bb_ramp = cfg;

	dib0090_set_boost(state, cfg[0] > 500);	/* we want the boost if the gain is higher that 50dB */

	dib0090_write_reg(state, 0x33, 0xffff);
	dprintk("total BB gain: %ddB, step: %d", (u32) cfg[0], dib0090_read_reg(state, 0x33));
	dib0090_write_regs(state, 0x35, cfg + 3, 4);
}

void dib0090_pwm_gain_reset(struct dvb_frontend *fe)
{
	struct dib0090_state *state = fe->tuner_priv;
	/* reset the AGC */

	if (state->config->use_pwm_agc) {
#ifdef CONFIG_BAND_SBAND
		if (state->current_band == BAND_SBAND) {
			dib0090_set_rframp_pwm(state, rf_ramp_pwm_sband);
			dib0090_set_bbramp_pwm(state, bb_ramp_pwm_boost);
		} else
#endif
#ifdef CONFIG_BAND_CBAND
		if (state->current_band == BAND_CBAND) {
			dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband);
			dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
		} else
#endif
#ifdef CONFIG_BAND_VHF
		if (state->current_band == BAND_VHF) {
			dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf);
			dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
		} else
#endif
		{
			dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf);
			dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
		}

		if (state->rf_ramp[0] != 0)
			dib0090_write_reg(state, 0x32, (3 << 11));
		else
			dib0090_write_reg(state, 0x32, (0 << 11));

		dib0090_write_reg(state, 0x39, (1 << 10));
	}
}
EXPORT_SYMBOL(dib0090_pwm_gain_reset);

int dib0090_gain_control(struct dvb_frontend *fe)
{
	struct dib0090_state *state = fe->tuner_priv;
	enum frontend_tune_state *tune_state = &state->tune_state;
	int ret = 10;

	u16 wbd_val = 0;
	u8 apply_gain_immediatly = 1;
	s16 wbd_error = 0, adc_error = 0;

	if (*tune_state == CT_AGC_START) {
		state->agc_freeze = 0;
		dib0090_write_reg(state, 0x04, 0x0);

#ifdef CONFIG_BAND_SBAND
		if (state->current_band == BAND_SBAND) {
			dib0090_set_rframp(state, rf_ramp_sband);
			dib0090_set_bbramp(state, bb_ramp_boost);
		} else
#endif
#ifdef CONFIG_BAND_VHF
		if (state->current_band == BAND_VHF) {
			dib0090_set_rframp(state, rf_ramp_vhf);
			dib0090_set_bbramp(state, bb_ramp_boost);
		} else
#endif
#ifdef CONFIG_BAND_CBAND
		if (state->current_band == BAND_CBAND) {
			dib0090_set_rframp(state, rf_ramp_cband);
			dib0090_set_bbramp(state, bb_ramp_boost);
		} else
#endif
		{
			dib0090_set_rframp(state, rf_ramp_uhf);
			dib0090_set_bbramp(state, bb_ramp_boost);
		}

		dib0090_write_reg(state, 0x32, 0);
		dib0090_write_reg(state, 0x39, 0);

		dib0090_wbd_target(state, state->current_rf);

		state->rf_gain_limit = state->rf_ramp[0] << WBD_ALPHA;
		state->current_gain = ((state->rf_ramp[0] + state->bb_ramp[0]) / 2) << GAIN_ALPHA;

		*tune_state = CT_AGC_STEP_0;
	} else if (!state->agc_freeze) {
		s16 wbd;

		int adc;
		wbd_val = dib0090_read_reg(state, 0x1d);

		/* read and calc the wbd power */
		wbd = dib0090_wbd_to_db(state, wbd_val);
		wbd_error = state->wbd_target - wbd;

		if (*tune_state == CT_AGC_STEP_0) {
			if (wbd_error < 0 && state->rf_gain_limit > 0) {
#ifdef CONFIG_BAND_CBAND
				/* in case of CBAND tune reduce first the lt_gain2 before adjusting the RF gain */
				u8 ltg2 = (state->rf_lt_def >> 10) & 0x7;
				if (state->current_band == BAND_CBAND && ltg2) {
					ltg2 >>= 1;
					state->rf_lt_def &= ltg2 << 10;	/* reduce in 3 steps from 7 to 0 */
				}
#endif
			} else {
				state->agc_step = 0;
				*tune_state = CT_AGC_STEP_1;
			}
		} else {
			/* calc the adc power */
			adc = state->config->get_adc_power(fe);
			adc = (adc * ((s32) 355774) + (((s32) 1) << 20)) >> 21;	/* included in [0:-700] */

			adc_error = (s16) (((s32) ADC_TARGET) - adc);
#ifdef CONFIG_STANDARD_DAB
			if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB)
				adc_error += 130;
#endif
#ifdef CONFIG_STANDARD_DVBT
			if (state->fe->dtv_property_cache.delivery_system == STANDARD_DVBT &&
			    (state->fe->dtv_property_cache.modulation == QAM_64 || state->fe->dtv_property_cache.modulation == QAM_16))
				adc_error += 60;
#endif
#ifdef CONFIG_SYS_ISDBT
			if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) && (((state->fe->dtv_property_cache.layer[0].segment_count >
											       0)
											      &&
											      ((state->fe->dtv_property_cache.layer[0].modulation ==
												QAM_64)
											       || (state->fe->dtv_property_cache.layer[0].
												   modulation == QAM_16)))
											     ||
											     ((state->fe->dtv_property_cache.layer[1].segment_count >
											       0)
											      &&
											      ((state->fe->dtv_property_cache.layer[1].modulation ==
												QAM_64)
											       || (state->fe->dtv_property_cache.layer[1].
												   modulation == QAM_16)))
											     ||
											     ((state->fe->dtv_property_cache.layer[2].segment_count >
											       0)
											      &&
											      ((state->fe->dtv_property_cache.layer[2].modulation ==
												QAM_64)
											       || (state->fe->dtv_property_cache.layer[2].
												   modulation == QAM_16)))
			    )
			    )
				adc_error += 60;
#endif

			if (*tune_state == CT_AGC_STEP_1) {	/* quickly go to the correct range of the ADC power */
				if (ABS(adc_error) < 50 || state->agc_step++ > 5) {

#ifdef CONFIG_STANDARD_DAB
					if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) {
						dib0090_write_reg(state, 0x02, (1 << 15) | (15 << 11) | (31 << 6) | (63));	/* cap value = 63 : narrow BB filter : Fc = 1.8MHz */
						dib0090_write_reg(state, 0x04, 0x0);
					} else
#endif
					{
						dib0090_write_reg(state, 0x02, (1 << 15) | (3 << 11) | (6 << 6) | (32));
						dib0090_write_reg(state, 0x04, 0x01);	/*0 = 1KHz ; 1 = 150Hz ; 2 = 50Hz ; 3 = 50KHz ; 4 = servo fast */
					}

					*tune_state = CT_AGC_STOP;
				}
			} else {
				/* everything higher than or equal to CT_AGC_STOP means tracking */
				ret = 100;	/* 10ms interval */
				apply_gain_immediatly = 0;
			}
		}
#ifdef DEBUG_AGC
		dprintk
		    ("FE: %d, tune state %d, ADC = %3ddB (ADC err %3d) WBD %3ddB (WBD err %3d, WBD val SADC: %4d), RFGainLimit (TOP): %3d, signal: %3ddBm",
		     (u32) fe->id, (u32) *tune_state, (u32) adc, (u32) adc_error, (u32) wbd, (u32) wbd_error, (u32) wbd_val,
		     (u32) state->rf_gain_limit >> WBD_ALPHA, (s32) 200 + adc - (state->current_gain >> GAIN_ALPHA));
#endif
	}

	/* apply gain */
	if (!state->agc_freeze)
		dib0090_gain_apply(state, adc_error, wbd_error, apply_gain_immediatly);
	return ret;
}
EXPORT_SYMBOL(dib0090_gain_control);

void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt)
{
	struct dib0090_state *state = fe->tuner_priv;
	if (rf)
		*rf = state->gain[0];
	if (bb)
		*bb = state->gain[1];
	if (rf_gain_limit)
		*rf_gain_limit = state->rf_gain_limit;
	if (rflt)
		*rflt = (state->rf_lt_def >> 10) & 0x7;
}
EXPORT_SYMBOL(dib0090_get_current_gain);

u16 dib0090_get_wbd_offset(struct dvb_frontend *tuner)
{
	struct dib0090_state *st = tuner->tuner_priv;
	return st->wbd_offset;
}
EXPORT_SYMBOL(dib0090_get_wbd_offset);

static const u16 dib0090_defaults[] = {

	25, 0x01,
	0x0000,
	0x99a0,
	0x6008,
	0x0000,
	0x8acb,
	0x0000,
	0x0405,
	0x0000,
	0x0000,
	0x0000,
	0xb802,
	0x0300,
	0x2d12,
	0xbac0,
	0x7c00,
	0xdbb9,
	0x0954,
	0x0743,
	0x8000,
	0x0001,
	0x0040,
	0x0100,
	0x0000,
	0xe910,
	0x149e,

	1, 0x1c,
	0xff2d,

	1, 0x39,
	0x0000,

	1, 0x1b,
	EN_IQADC | EN_BB | EN_BIAS | EN_DIGCLK | EN_PLL | EN_CRYSTAL,
	2, 0x1e,
	0x07FF,
	0x0007,

	1, 0x24,
	EN_UHF | EN_CRYSTAL,

	2, 0x3c,
	0x3ff,
	0x111,
	0
};

static int dib0090_reset(struct dvb_frontend *fe)
{
	struct dib0090_state *state = fe->tuner_priv;
	u16 l, r, *n;

	dib0090_reset_digital(fe, state->config);
	state->revision = dib0090_identify(fe);

	/* Revision definition */
	if (state->revision == 0xff)
		return -EINVAL;
#ifdef EFUSE
	else if ((state->revision & 0x1f) >= 3)	/* Update the efuse : Only available for KROSUS > P1C */
		dib0090_set_EFUSE(state);
#endif

#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT
	if (!(state->revision & 0x1))	/* it is P1B - reset is already done */
		return 0;
#endif

	/* Upload the default values */
	n = (u16 *) dib0090_defaults;
	l = pgm_read_word(n++);
	while (l) {
		r = pgm_read_word(n++);
		do {
			/* DEBUG_TUNER */
			/* dprintk("%d, %d, %d", l, r, pgm_read_word(n)); */
			dib0090_write_reg(state, r, pgm_read_word(n++));
			r++;
		} while (--l);
		l = pgm_read_word(n++);
	}

	/* Congigure in function of the crystal */
	if (state->config->io.clock_khz >= 24000)
		l = 1;
	else
		l = 2;
	dib0090_write_reg(state, 0x14, l);
	dprintk("Pll lock : %d", (dib0090_read_reg(state, 0x1a) >> 11) & 0x1);

	state->reset = 3;	/* enable iq-offset-calibration and wbd-calibration when tuning next time */

	return 0;
}

#define steps(u) (((u) > 15) ? ((u)-16) : (u))
#define INTERN_WAIT 10
static int dib0090_get_offset(struct dib0090_state *state, enum frontend_tune_state *tune_state)
{
	int ret = INTERN_WAIT * 10;

	switch (*tune_state) {
	case CT_TUNER_STEP_2:
		/* Turns to positive */
		dib0090_write_reg(state, 0x1f, 0x7);
		*tune_state = CT_TUNER_STEP_3;
		break;

	case CT_TUNER_STEP_3:
		state->adc_diff = dib0090_read_reg(state, 0x1d);

		/* Turns to negative */
		dib0090_write_reg(state, 0x1f, 0x4);
		*tune_state = CT_TUNER_STEP_4;
		break;

	case CT_TUNER_STEP_4:
		state->adc_diff -= dib0090_read_reg(state, 0x1d);
		*tune_state = CT_TUNER_STEP_5;
		ret = 0;
		break;

	default:
		break;
	}

	return ret;
}

struct dc_calibration {
	uint8_t addr;
	uint8_t offset;
	uint8_t pga:1;
	uint16_t bb1;
	uint8_t i:1;
};

static const struct dc_calibration dc_table[] = {
	/* Step1 BB gain1= 26 with boost 1, gain 2 = 0 */
	{0x06, 5, 1, (1 << 13) | (0 << 8) | (26 << 3), 1},
	{0x07, 11, 1, (1 << 13) | (0 << 8) | (26 << 3), 0},
	/* Step 2 BB gain 1 = 26 with boost = 1 & gain 2 = 29 */
	{0x06, 0, 0, (1 << 13) | (29 << 8) | (26 << 3), 1},
	{0x06, 10, 0, (1 << 13) | (29 << 8) | (26 << 3), 0},
	{0},
};

static void dib0090_set_trim(struct dib0090_state *state)
{
	u16 *val;

	if (state->dc->addr == 0x07)
		val = &state->bb7;
	else
		val = &state->bb6;

	*val &= ~(0x1f << state->dc->offset);
	*val |= state->step << state->dc->offset;

	dib0090_write_reg(state, state->dc->addr, *val);
}

static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state)
{
	int ret = 0;

	switch (*tune_state) {

	case CT_TUNER_START:
		/* init */
		dprintk("Internal DC calibration");

		/* the LNA is off */
		dib0090_write_reg(state, 0x24, 0x02ed);

		/* force vcm2 = 0.8V */
		state->bb6 = 0;
		state->bb7 = 0x040d;

		state->dc = dc_table;

		*tune_state = CT_TUNER_STEP_0;

		/* fall through */

	case CT_TUNER_STEP_0:
		dib0090_write_reg(state, 0x01, state->dc->bb1);
		dib0090_write_reg(state, 0x07, state->bb7 | (state->dc->i << 7));

		state->step = 0;

		state->min_adc_diff = 1023;

		*tune_state = CT_TUNER_STEP_1;
		ret = 50;
		break;

	case CT_TUNER_STEP_1:
		dib0090_set_trim(state);

		*tune_state = CT_TUNER_STEP_2;
		break;

	case CT_TUNER_STEP_2:
	case CT_TUNER_STEP_3:
	case CT_TUNER_STEP_4:
		ret = dib0090_get_offset(state, tune_state);
		break;

	case CT_TUNER_STEP_5:	/* found an offset */
		dprintk("FE%d: IQC read=%d, current=%x", state->fe->id, (u32) state->adc_diff, state->step);

		/* first turn for this frequency */
		if (state->step == 0) {
			if (state->dc->pga && state->adc_diff < 0)
				state->step = 0x10;
			if (state->dc->pga == 0 && state->adc_diff > 0)
				state->step = 0x10;
		}

		state->adc_diff = ABS(state->adc_diff);

		if (state->adc_diff < state->min_adc_diff && steps(state->step) < 15) {	/* stop search when the delta to 0 is increasing */
			state->step++;
			state->min_adc_diff = state->adc_diff;
			*tune_state = CT_TUNER_STEP_1;
		} else {

			/* the minimum was what we have seen in the step before */
			state->step--;
			dib0090_set_trim(state);

			dprintk("FE%d: BB Offset Cal, BBreg=%hd,Offset=%hd,Value Set=%hd", state->fe->id, state->dc->addr, state->adc_diff,
				state->step);

			state->dc++;
			if (state->dc->addr == 0)	/* done */
				*tune_state = CT_TUNER_STEP_6;
			else
				*tune_state = CT_TUNER_STEP_0;

		}
		break;

	case CT_TUNER_STEP_6:
		dib0090_write_reg(state, 0x07, state->bb7 & ~0x0008);
		dib0090_write_reg(state, 0x1f, 0x7);
		*tune_state = CT_TUNER_START;	/* reset done -> real tuning can now begin */
		state->reset &= ~0x1;
	default:
		break;
	}
	return ret;
}

static int dib0090_wbd_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state)
{
	switch (*tune_state) {
	case CT_TUNER_START:
		/* WBD-mode=log, Bias=2, Gain=6, Testmode=1, en=1, WBDMUX=1 */
		dib0090_write_reg(state, 0x10, 0xdb09 | (1 << 10));
		dib0090_write_reg(state, 0x24, EN_UHF & 0x0fff);

		*tune_state = CT_TUNER_STEP_0;
		return 90;	/* wait for the WBDMUX to switch and for the ADC to sample */
	case CT_TUNER_STEP_0:
		state->wbd_offset = dib0090_read_reg(state, 0x1d);
		dprintk("WBD calibration offset = %d", state->wbd_offset);

		*tune_state = CT_TUNER_START;	/* reset done -> real tuning can now begin */
		state->reset &= ~0x2;
		break;
	default:
		break;
	}
	return 0;
}

static void dib0090_set_bandwidth(struct dib0090_state *state)
{
	u16 tmp;

	if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 5000)
		tmp = (3 << 14);
	else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 6000)
		tmp = (2 << 14);
	else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 7000)
		tmp = (1 << 14);
	else
		tmp = (0 << 14);

	state->bb_1_def &= 0x3fff;
	state->bb_1_def |= tmp;

	dib0090_write_reg(state, 0x01, state->bb_1_def);	/* be sure that we have the right bb-filter */
}

static const struct dib0090_pll dib0090_pll_table[] = {
#ifdef CONFIG_BAND_CBAND
	{56000, 0, 9, 48, 6},
	{70000, 1, 9, 48, 6},
	{87000, 0, 8, 32, 4},
	{105000, 1, 8, 32, 4},
	{115000, 0, 7, 24, 6},
	{140000, 1, 7, 24, 6},
	{170000, 0, 6, 16, 4},
#endif
#ifdef CONFIG_BAND_VHF
	{200000, 1, 6, 16, 4},
	{230000, 0, 5, 12, 6},
	{280000, 1, 5, 12, 6},
	{340000, 0, 4, 8, 4},
	{380000, 1, 4, 8, 4},
	{450000, 0, 3, 6, 6},
#endif
#ifdef CONFIG_BAND_UHF
	{580000, 1, 3, 6, 6},
	{700000, 0, 2, 4, 4},
	{860000, 1, 2, 4, 4},
#endif
#ifdef CONFIG_BAND_LBAND
	{1800000, 1, 0, 2, 4},
#endif
#ifdef CONFIG_BAND_SBAND
	{2900000, 0, 14, 1, 4},
#endif
};

static const struct dib0090_tuning dib0090_tuning_table_fm_vhf_on_cband[] = {

#ifdef CONFIG_BAND_CBAND
	{184000, 4, 1, 15, 0x280, 0x2912, 0xb94e, EN_CAB},
	{227000, 4, 3, 15, 0x280, 0x2912, 0xb94e, EN_CAB},
	{380000, 4, 7, 15, 0x280, 0x2912, 0xb94e, EN_CAB},
#endif
#ifdef CONFIG_BAND_UHF
	{520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
	{550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
	{650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
	{750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
	{850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
	{900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
#endif
#ifdef CONFIG_BAND_LBAND
	{1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
	{1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
	{1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
#endif
#ifdef CONFIG_BAND_SBAND
	{2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD},
	{2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD},
#endif
};

static const struct dib0090_tuning dib0090_tuning_table[] = {

#ifdef CONFIG_BAND_CBAND
	{170000, 4, 1, 15, 0x280, 0x2912, 0xb94e, EN_CAB},
#endif
#ifdef CONFIG_BAND_VHF
	{184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF},
	{227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF},
	{380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF},
#endif
#ifdef CONFIG_BAND_UHF
	{520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
	{550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
	{650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
	{750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
	{850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
	{900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF},
#endif
#ifdef CONFIG_BAND_LBAND
	{1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
	{1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
	{1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD},
#endif
#ifdef CONFIG_BAND_SBAND
	{2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD},
	{2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD},
#endif
};

#define WBD     0x781		/* 1 1 1 1 0000 0 0 1 */
static int dib0090_tune(struct dvb_frontend *fe)
{
	struct dib0090_state *state = fe->tuner_priv;
	const struct dib0090_tuning *tune = state->current_tune_table_index;
	const struct dib0090_pll *pll = state->current_pll_table_index;
	enum frontend_tune_state *tune_state = &state->tune_state;

	u32 rf;
	u16 lo4 = 0xe900, lo5, lo6, Den;
	u32 FBDiv, Rest, FREF, VCOF_kHz = 0;
	u16 tmp, adc;
	int8_t step_sign;
	int ret = 10;		/* 1ms is the default delay most of the time */
	u8 c, i;

	state->current_band = (u8) BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000);
	rf = fe->dtv_property_cache.frequency / 1000 + (state->current_band ==
							BAND_UHF ? state->config->freq_offset_khz_uhf : state->config->freq_offset_khz_vhf);
	/* in any case we first need to do a reset if needed */
	if (state->reset & 0x1)
		return dib0090_dc_offset_calibration(state, tune_state);
	else if (state->reset & 0x2)
		return dib0090_wbd_calibration(state, tune_state);

    /************************* VCO ***************************/
	/* Default values for FG                                 */
	/* from these are needed :                               */
	/* Cp,HFdiv,VCOband,SD,Num,Den,FB and REFDiv             */

#ifdef CONFIG_SYS_ISDBT
	if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1)
		rf += 850;
#endif

	if (state->current_rf != rf) {
		state->tuner_is_tuned = 0;

		tune = dib0090_tuning_table;

		tmp = (state->revision >> 5) & 0x7;
		if (tmp == 0x4 || tmp == 0x7) {
			/* CBAND tuner version for VHF */
			if (state->current_band == BAND_FM || state->current_band == BAND_VHF) {
				/* Force CBAND */
				state->current_band = BAND_CBAND;
				tune = dib0090_tuning_table_fm_vhf_on_cband;
			}
		}

		pll = dib0090_pll_table;
		/* Look for the interval */
		while (rf > tune->max_freq)
			tune++;
		while (rf > pll->max_freq)
			pll++;
		state->current_tune_table_index = tune;
		state->current_pll_table_index = pll;
	}

	if (*tune_state == CT_TUNER_START) {

		if (state->tuner_is_tuned == 0)
			state->current_rf = 0;

		if (state->current_rf != rf) {

			dib0090_write_reg(state, 0x0b, 0xb800 | (tune->switch_trim));

			/* external loop filter, otherwise:
			 * lo5 = (0 << 15) | (0 << 12) | (0 << 11) | (3 << 9) | (4 << 6) | (3 << 4) | 4;
			 * lo6 = 0x0e34 */
			if (pll->vco_band)
				lo5 = 0x049e;
			else if (state->config->analog_output)
				lo5 = 0x041d;
			else
				lo5 = 0x041c;

			lo5 |= (pll->hfdiv_code << 11) | (pll->vco_band << 7);	/* bit 15 is the split to the slave, we do not do it here */

			if (!state->config->io.pll_int_loop_filt)
				lo6 = 0xff28;
			else
				lo6 = (state->config->io.pll_int_loop_filt << 3);

			VCOF_kHz = (pll->hfdiv * rf) * 2;

			FREF = state->config->io.clock_khz;

			FBDiv = (VCOF_kHz / pll->topresc / FREF);
			Rest = (VCOF_kHz / pll->topresc) - FBDiv * FREF;

			if (Rest < LPF)
				Rest = 0;
			else if (Rest < 2 * LPF)
				Rest = 2 * LPF;
			else if (Rest > (FREF - LPF)) {
				Rest = 0;
				FBDiv += 1;
			} else if (Rest > (FREF - 2 * LPF))
				Rest = FREF - 2 * LPF;
			Rest = (Rest * 6528) / (FREF / 10);

			Den = 1;

			dprintk(" *****  ******* Rest value = %d", Rest);

			if (Rest > 0) {
				if (state->config->analog_output)
					lo6 |= (1 << 2) | 2;
				else
					lo6 |= (1 << 2) | 1;
				Den = 255;
			}
#ifdef CONFIG_BAND_SBAND
			if (state->current_band == BAND_SBAND)
				lo6 &= 0xfffb;
#endif

			dib0090_write_reg(state, 0x15, (u16) FBDiv);

			dib0090_write_reg(state, 0x16, (Den << 8) | 1);

			dib0090_write_reg(state, 0x17, (u16) Rest);

			dib0090_write_reg(state, 0x19, lo5);

			dib0090_write_reg(state, 0x1c, lo6);

			lo6 = tune->tuner_enable;
			if (state->config->analog_output)
				lo6 = (lo6 & 0xff9f) | 0x2;

			dib0090_write_reg(state, 0x24, lo6 | EN_LO
#ifdef CONFIG_DIB0090_USE_PWM_AGC
					  | state->config->use_pwm_agc * EN_CRYSTAL
#endif
			    );

			state->current_rf = rf;

			/* prepare a complete captrim */
			state->step = state->captrim = state->fcaptrim = 64;

		} else {	/* we are already tuned to this frequency - the configuration is correct  */

			/* do a minimal captrim even if the frequency has not changed */
			state->step = 4;
			state->captrim = state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7f;
		}
		state->adc_diff = 3000;

		dib0090_write_reg(state, 0x10, 0x2B1);

		dib0090_write_reg(state, 0x1e, 0x0032);

		ret = 20;
		*tune_state = CT_TUNER_STEP_1;
	} else if (*tune_state == CT_TUNER_STEP_0) {
		/* nothing */
	} else if (*tune_state == CT_TUNER_STEP_1) {
		state->step /= 2;
		dib0090_write_reg(state, 0x18, lo4 | state->captrim);
		*tune_state = CT_TUNER_STEP_2;
	} else if (*tune_state == CT_TUNER_STEP_2) {

		adc = dib0090_read_reg(state, 0x1d);
		dprintk("FE %d CAPTRIM=%d; ADC = %d (ADC) & %dmV", (u32) fe->id, (u32) state->captrim, (u32) adc,
			(u32) (adc) * (u32) 1800 / (u32) 1024);

		if (adc >= 400) {
			adc -= 400;
			step_sign = -1;
		} else {
			adc = 400 - adc;
			step_sign = 1;
		}

		if (adc < state->adc_diff) {
			dprintk("FE %d CAPTRIM=%d is closer to target (%d/%d)", (u32) fe->id, (u32) state->captrim, (u32) adc, (u32) state->adc_diff);
			state->adc_diff = adc;
			state->fcaptrim = state->captrim;

		}

		state->captrim += step_sign * state->step;
		if (state->step >= 1)
			*tune_state = CT_TUNER_STEP_1;
		else
			*tune_state = CT_TUNER_STEP_3;

		ret = 15;
	} else if (*tune_state == CT_TUNER_STEP_3) {
		/*write the final cptrim config */
		dib0090_write_reg(state, 0x18, lo4 | state->fcaptrim);

#ifdef CONFIG_TUNER_DIB0090_CAPTRIM_MEMORY
		state->memory[state->memory_index].cap = state->fcaptrim;
#endif

		*tune_state = CT_TUNER_STEP_4;
	} else if (*tune_state == CT_TUNER_STEP_4) {
		dib0090_write_reg(state, 0x1e, 0x07ff);

		dprintk("FE %d Final Captrim: %d", (u32) fe->id, (u32) state->fcaptrim);
		dprintk("FE %d HFDIV code: %d", (u32) fe->id, (u32) pll->hfdiv_code);
		dprintk("FE %d VCO = %d", (u32) fe->id, (u32) pll->vco_band);
		dprintk("FE %d VCOF in kHz: %d ((%d*%d) << 1))", (u32) fe->id, (u32) ((pll->hfdiv * rf) * 2), (u32) pll->hfdiv, (u32) rf);
		dprintk("FE %d REFDIV: %d, FREF: %d", (u32) fe->id, (u32) 1, (u32) state->config->io.clock_khz);
		dprintk("FE %d FBDIV: %d, Rest: %d", (u32) fe->id, (u32) dib0090_read_reg(state, 0x15), (u32) dib0090_read_reg(state, 0x17));
		dprintk("FE %d Num: %d, Den: %d, SD: %d", (u32) fe->id, (u32) dib0090_read_reg(state, 0x17),
			(u32) (dib0090_read_reg(state, 0x16) >> 8), (u32) dib0090_read_reg(state, 0x1c) & 0x3);

		c = 4;
		i = 3;
#if defined(CONFIG_BAND_LBAND) || defined(CONFIG_BAND_SBAND)
		if ((state->current_band == BAND_LBAND) || (state->current_band == BAND_SBAND)) {
			c = 2;
			i = 2;
		}
#endif
		dib0090_write_reg(state, 0x10, (c << 13) | (i << 11) | (WBD
#ifdef CONFIG_DIB0090_USE_PWM_AGC
									| (state->config->use_pwm_agc << 1)
#endif
				  ));
		dib0090_write_reg(state, 0x09, (tune->lna_tune << 5) | (tune->lna_bias << 0));
		dib0090_write_reg(state, 0x0c, tune->v2i);
		dib0090_write_reg(state, 0x0d, tune->mix);
		dib0090_write_reg(state, 0x0e, tune->load);

		*tune_state = CT_TUNER_STEP_5;
	} else if (*tune_state == CT_TUNER_STEP_5) {

		/* initialize the lt gain register */
		state->rf_lt_def = 0x7c00;
		dib0090_write_reg(state, 0x0f, state->rf_lt_def);

		dib0090_set_bandwidth(state);
		state->tuner_is_tuned = 1;
		*tune_state = CT_TUNER_STOP;
	} else
		ret = FE_CALLBACK_TIME_NEVER;
	return ret;
}

static int dib0090_release(struct dvb_frontend *fe)
{
	kfree(fe->tuner_priv);
	fe->tuner_priv = NULL;
	return 0;
}

enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe)
{
	struct dib0090_state *state = fe->tuner_priv;

	return state->tune_state;
}
EXPORT_SYMBOL(dib0090_get_tune_state);

int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
{
	struct dib0090_state *state = fe->tuner_priv;

	state->tune_state = tune_state;
	return 0;
}
EXPORT_SYMBOL(dib0090_set_tune_state);

static int dib0090_get_frequency(struct dvb_frontend *fe, u32 * frequency)
{
	struct dib0090_state *state = fe->tuner_priv;

	*frequency = 1000 * state->current_rf;
	return 0;
}

static int dib0090_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
{
	struct dib0090_state *state = fe->tuner_priv;
	uint32_t ret;

	state->tune_state = CT_TUNER_START;

	do {
		ret = dib0090_tune(fe);
		if (ret != FE_CALLBACK_TIME_NEVER)
			msleep(ret / 10);
		else
			break;
	} while (state->tune_state != CT_TUNER_STOP);

	return 0;
}

static const struct dvb_tuner_ops dib0090_ops = {
	.info = {
		 .name = "DiBcom DiB0090",
		 .frequency_min = 45000000,
		 .frequency_max = 860000000,
		 .frequency_step = 1000,
		 },
	.release = dib0090_release,

	.init = dib0090_wakeup,
	.sleep = dib0090_sleep,
	.set_params = dib0090_set_params,
	.get_frequency = dib0090_get_frequency,
};

struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config)
{
	struct dib0090_state *st = kzalloc(sizeof(struct dib0090_state), GFP_KERNEL);
	if (st == NULL)
		return NULL;

	st->config = config;
	st->i2c = i2c;
	st->fe = fe;
	fe->tuner_priv = st;

	if (dib0090_reset(fe) != 0)
		goto free_mem;

	printk(KERN_INFO "DiB0090: successfully identified\n");
	memcpy(&fe->ops.tuner_ops, &dib0090_ops, sizeof(struct dvb_tuner_ops));

	return fe;
 free_mem:
	kfree(st);
	fe->tuner_priv = NULL;
	return NULL;
}
EXPORT_SYMBOL(dib0090_register);

MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
MODULE_AUTHOR("Olivier Grenie <olivier.grenie@dibcom.fr>");
MODULE_DESCRIPTION("Driver for the DiBcom 0090 base-band RF Tuner");
MODULE_LICENSE("GPL");