ts2020.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. Montage Technology TS2020 - Silicon Tuner driver
  4. Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
  5. Copyright (C) 2009-2012 TurboSight.com
  6. */
  7. #include <media/dvb_frontend.h>
  8. #include "ts2020.h"
  9. #include <linux/regmap.h>
  10. #include <linux/math64.h>
  11. #define TS2020_XTAL_FREQ 27000 /* in kHz */
  12. #define FREQ_OFFSET_LOW_SYM_RATE 3000
  13. struct ts2020_priv {
  14. struct i2c_client *client;
  15. struct mutex regmap_mutex;
  16. struct regmap_config regmap_config;
  17. struct regmap *regmap;
  18. struct dvb_frontend *fe;
  19. struct delayed_work stat_work;
  20. int (*get_agc_pwm)(struct dvb_frontend *fe, u8 *_agc_pwm);
  21. /* i2c details */
  22. struct i2c_adapter *i2c;
  23. int i2c_address;
  24. bool loop_through:1;
  25. u8 clk_out:2;
  26. u8 clk_out_div:5;
  27. bool dont_poll:1;
  28. u32 frequency_div; /* LO output divider switch frequency */
  29. u32 frequency_khz; /* actual used LO frequency */
  30. #define TS2020_M88TS2020 0
  31. #define TS2020_M88TS2022 1
  32. u8 tuner;
  33. };
  34. struct ts2020_reg_val {
  35. u8 reg;
  36. u8 val;
  37. };
  38. static void ts2020_stat_work(struct work_struct *work);
  39. static void ts2020_release(struct dvb_frontend *fe)
  40. {
  41. struct ts2020_priv *priv = fe->tuner_priv;
  42. struct i2c_client *client = priv->client;
  43. dev_dbg(&client->dev, "\n");
  44. i2c_unregister_device(client);
  45. }
  46. static int ts2020_sleep(struct dvb_frontend *fe)
  47. {
  48. struct ts2020_priv *priv = fe->tuner_priv;
  49. int ret;
  50. u8 u8tmp;
  51. if (priv->tuner == TS2020_M88TS2020)
  52. u8tmp = 0x0a; /* XXX: probably wrong */
  53. else
  54. u8tmp = 0x00;
  55. ret = regmap_write(priv->regmap, u8tmp, 0x00);
  56. if (ret < 0)
  57. return ret;
  58. /* stop statistics polling */
  59. if (!priv->dont_poll)
  60. cancel_delayed_work_sync(&priv->stat_work);
  61. return 0;
  62. }
  63. static int ts2020_init(struct dvb_frontend *fe)
  64. {
  65. struct dtv_frontend_properties *c = &fe->dtv_property_cache;
  66. struct ts2020_priv *priv = fe->tuner_priv;
  67. int i;
  68. u8 u8tmp;
  69. if (priv->tuner == TS2020_M88TS2020) {
  70. regmap_write(priv->regmap, 0x42, 0x73);
  71. regmap_write(priv->regmap, 0x05, priv->clk_out_div);
  72. regmap_write(priv->regmap, 0x20, 0x27);
  73. regmap_write(priv->regmap, 0x07, 0x02);
  74. regmap_write(priv->regmap, 0x11, 0xff);
  75. regmap_write(priv->regmap, 0x60, 0xf9);
  76. regmap_write(priv->regmap, 0x08, 0x01);
  77. regmap_write(priv->regmap, 0x00, 0x41);
  78. } else {
  79. static const struct ts2020_reg_val reg_vals[] = {
  80. {0x7d, 0x9d},
  81. {0x7c, 0x9a},
  82. {0x7a, 0x76},
  83. {0x3b, 0x01},
  84. {0x63, 0x88},
  85. {0x61, 0x85},
  86. {0x22, 0x30},
  87. {0x30, 0x40},
  88. {0x20, 0x23},
  89. {0x24, 0x02},
  90. {0x12, 0xa0},
  91. };
  92. regmap_write(priv->regmap, 0x00, 0x01);
  93. regmap_write(priv->regmap, 0x00, 0x03);
  94. switch (priv->clk_out) {
  95. case TS2020_CLK_OUT_DISABLED:
  96. u8tmp = 0x60;
  97. break;
  98. case TS2020_CLK_OUT_ENABLED:
  99. u8tmp = 0x70;
  100. regmap_write(priv->regmap, 0x05, priv->clk_out_div);
  101. break;
  102. case TS2020_CLK_OUT_ENABLED_XTALOUT:
  103. u8tmp = 0x6c;
  104. break;
  105. default:
  106. u8tmp = 0x60;
  107. break;
  108. }
  109. regmap_write(priv->regmap, 0x42, u8tmp);
  110. if (priv->loop_through)
  111. u8tmp = 0xec;
  112. else
  113. u8tmp = 0x6c;
  114. regmap_write(priv->regmap, 0x62, u8tmp);
  115. for (i = 0; i < ARRAY_SIZE(reg_vals); i++)
  116. regmap_write(priv->regmap, reg_vals[i].reg,
  117. reg_vals[i].val);
  118. }
  119. /* Initialise v5 stats here */
  120. c->strength.len = 1;
  121. c->strength.stat[0].scale = FE_SCALE_DECIBEL;
  122. c->strength.stat[0].uvalue = 0;
  123. /* Start statistics polling by invoking the work function */
  124. ts2020_stat_work(&priv->stat_work.work);
  125. return 0;
  126. }
  127. static int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset)
  128. {
  129. struct ts2020_priv *priv = fe->tuner_priv;
  130. int ret;
  131. ret = regmap_write(priv->regmap, 0x51, 0x1f - offset);
  132. ret |= regmap_write(priv->regmap, 0x51, 0x1f);
  133. ret |= regmap_write(priv->regmap, 0x50, offset);
  134. ret |= regmap_write(priv->regmap, 0x50, 0x00);
  135. msleep(20);
  136. return ret;
  137. }
  138. static int ts2020_set_tuner_rf(struct dvb_frontend *fe)
  139. {
  140. struct ts2020_priv *dev = fe->tuner_priv;
  141. int ret;
  142. unsigned int utmp;
  143. ret = regmap_read(dev->regmap, 0x3d, &utmp);
  144. if (ret)
  145. return ret;
  146. utmp &= 0x7f;
  147. if (utmp < 0x16)
  148. utmp = 0xa1;
  149. else if (utmp == 0x16)
  150. utmp = 0x99;
  151. else
  152. utmp = 0xf9;
  153. regmap_write(dev->regmap, 0x60, utmp);
  154. ret = ts2020_tuner_gate_ctrl(fe, 0x08);
  155. return ret;
  156. }
  157. static int ts2020_set_params(struct dvb_frontend *fe)
  158. {
  159. struct dtv_frontend_properties *c = &fe->dtv_property_cache;
  160. struct ts2020_priv *priv = fe->tuner_priv;
  161. int ret;
  162. unsigned int utmp;
  163. u32 f3db, gdiv28;
  164. u16 u16tmp, value, lpf_coeff;
  165. u8 buf[3], reg10, lpf_mxdiv, mlpf_max, mlpf_min, nlpf;
  166. unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n;
  167. unsigned int frequency_khz = c->frequency;
  168. /*
  169. * Integer-N PLL synthesizer
  170. * kHz is used for all calculations to keep calculations within 32-bit
  171. */
  172. f_ref_khz = TS2020_XTAL_FREQ;
  173. div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000);
  174. /* select LO output divider */
  175. if (frequency_khz < priv->frequency_div) {
  176. div_out = 4;
  177. reg10 = 0x10;
  178. } else {
  179. div_out = 2;
  180. reg10 = 0x00;
  181. }
  182. f_vco_khz = frequency_khz * div_out;
  183. pll_n = f_vco_khz * div_ref / f_ref_khz;
  184. pll_n += pll_n % 2;
  185. priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out;
  186. pr_debug("frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n",
  187. priv->frequency_khz, priv->frequency_khz - c->frequency,
  188. f_vco_khz, pll_n, div_ref, div_out);
  189. if (priv->tuner == TS2020_M88TS2020) {
  190. lpf_coeff = 2766;
  191. reg10 |= 0x01;
  192. ret = regmap_write(priv->regmap, 0x10, reg10);
  193. } else {
  194. lpf_coeff = 3200;
  195. reg10 |= 0x0b;
  196. ret = regmap_write(priv->regmap, 0x10, reg10);
  197. ret |= regmap_write(priv->regmap, 0x11, 0x40);
  198. }
  199. u16tmp = pll_n - 1024;
  200. buf[0] = (u16tmp >> 8) & 0xff;
  201. buf[1] = (u16tmp >> 0) & 0xff;
  202. buf[2] = div_ref - 8;
  203. ret |= regmap_write(priv->regmap, 0x01, buf[0]);
  204. ret |= regmap_write(priv->regmap, 0x02, buf[1]);
  205. ret |= regmap_write(priv->regmap, 0x03, buf[2]);
  206. ret |= ts2020_tuner_gate_ctrl(fe, 0x10);
  207. if (ret < 0)
  208. return -ENODEV;
  209. ret |= ts2020_tuner_gate_ctrl(fe, 0x08);
  210. /* Tuner RF */
  211. if (priv->tuner == TS2020_M88TS2020)
  212. ret |= ts2020_set_tuner_rf(fe);
  213. gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000;
  214. ret |= regmap_write(priv->regmap, 0x04, gdiv28 & 0xff);
  215. ret |= ts2020_tuner_gate_ctrl(fe, 0x04);
  216. if (ret < 0)
  217. return -ENODEV;
  218. if (priv->tuner == TS2020_M88TS2022) {
  219. ret = regmap_write(priv->regmap, 0x25, 0x00);
  220. ret |= regmap_write(priv->regmap, 0x27, 0x70);
  221. ret |= regmap_write(priv->regmap, 0x41, 0x09);
  222. ret |= regmap_write(priv->regmap, 0x08, 0x0b);
  223. if (ret < 0)
  224. return -ENODEV;
  225. }
  226. regmap_read(priv->regmap, 0x26, &utmp);
  227. value = utmp;
  228. f3db = (c->bandwidth_hz / 1000 / 2) + 2000;
  229. f3db += FREQ_OFFSET_LOW_SYM_RATE; /* FIXME: ~always too wide filter */
  230. f3db = clamp(f3db, 7000U, 40000U);
  231. gdiv28 = gdiv28 * 207 / (value * 2 + 151);
  232. mlpf_max = gdiv28 * 135 / 100;
  233. mlpf_min = gdiv28 * 78 / 100;
  234. if (mlpf_max > 63)
  235. mlpf_max = 63;
  236. nlpf = (f3db * gdiv28 * 2 / lpf_coeff /
  237. (TS2020_XTAL_FREQ / 1000) + 1) / 2;
  238. if (nlpf > 23)
  239. nlpf = 23;
  240. if (nlpf < 1)
  241. nlpf = 1;
  242. lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000)
  243. * lpf_coeff * 2 / f3db + 1) / 2;
  244. if (lpf_mxdiv < mlpf_min) {
  245. nlpf++;
  246. lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000)
  247. * lpf_coeff * 2 / f3db + 1) / 2;
  248. }
  249. if (lpf_mxdiv > mlpf_max)
  250. lpf_mxdiv = mlpf_max;
  251. ret = regmap_write(priv->regmap, 0x04, lpf_mxdiv);
  252. ret |= regmap_write(priv->regmap, 0x06, nlpf);
  253. ret |= ts2020_tuner_gate_ctrl(fe, 0x04);
  254. ret |= ts2020_tuner_gate_ctrl(fe, 0x01);
  255. msleep(80);
  256. return (ret < 0) ? -EINVAL : 0;
  257. }
  258. static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency)
  259. {
  260. struct ts2020_priv *priv = fe->tuner_priv;
  261. *frequency = priv->frequency_khz;
  262. return 0;
  263. }
  264. static int ts2020_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
  265. {
  266. *frequency = 0; /* Zero-IF */
  267. return 0;
  268. }
  269. /*
  270. * Get the tuner gain.
  271. * @fe: The front end for which we're determining the gain
  272. * @v_agc: The voltage of the AGC from the demodulator (0-2600mV)
  273. * @_gain: Where to store the gain (in 0.001dB units)
  274. *
  275. * Returns 0 or a negative error code.
  276. */
  277. static int ts2020_read_tuner_gain(struct dvb_frontend *fe, unsigned v_agc,
  278. __s64 *_gain)
  279. {
  280. struct ts2020_priv *priv = fe->tuner_priv;
  281. unsigned long gain1, gain2, gain3;
  282. unsigned utmp;
  283. int ret;
  284. /* Read the RF gain */
  285. ret = regmap_read(priv->regmap, 0x3d, &utmp);
  286. if (ret < 0)
  287. return ret;
  288. gain1 = utmp & 0x1f;
  289. /* Read the baseband gain */
  290. ret = regmap_read(priv->regmap, 0x21, &utmp);
  291. if (ret < 0)
  292. return ret;
  293. gain2 = utmp & 0x1f;
  294. switch (priv->tuner) {
  295. case TS2020_M88TS2020:
  296. gain1 = clamp_t(long, gain1, 0, 15);
  297. gain2 = clamp_t(long, gain2, 0, 13);
  298. v_agc = clamp_t(long, v_agc, 400, 1100);
  299. *_gain = -((__s64)gain1 * 2330 +
  300. gain2 * 3500 +
  301. v_agc * 24 / 10 * 10 +
  302. 10000);
  303. /* gain in range -19600 to -116850 in units of 0.001dB */
  304. break;
  305. case TS2020_M88TS2022:
  306. ret = regmap_read(priv->regmap, 0x66, &utmp);
  307. if (ret < 0)
  308. return ret;
  309. gain3 = (utmp >> 3) & 0x07;
  310. gain1 = clamp_t(long, gain1, 0, 15);
  311. gain2 = clamp_t(long, gain2, 2, 16);
  312. gain3 = clamp_t(long, gain3, 0, 6);
  313. v_agc = clamp_t(long, v_agc, 600, 1600);
  314. *_gain = -((__s64)gain1 * 2650 +
  315. gain2 * 3380 +
  316. gain3 * 2850 +
  317. v_agc * 176 / 100 * 10 -
  318. 30000);
  319. /* gain in range -47320 to -158950 in units of 0.001dB */
  320. break;
  321. }
  322. return 0;
  323. }
  324. /*
  325. * Get the AGC information from the demodulator and use that to calculate the
  326. * tuner gain.
  327. */
  328. static int ts2020_get_tuner_gain(struct dvb_frontend *fe, __s64 *_gain)
  329. {
  330. struct ts2020_priv *priv = fe->tuner_priv;
  331. int v_agc = 0, ret;
  332. u8 agc_pwm;
  333. /* Read the AGC PWM rate from the demodulator */
  334. if (priv->get_agc_pwm) {
  335. ret = priv->get_agc_pwm(fe, &agc_pwm);
  336. if (ret < 0)
  337. return ret;
  338. switch (priv->tuner) {
  339. case TS2020_M88TS2020:
  340. v_agc = (int)agc_pwm * 20 - 1166;
  341. break;
  342. case TS2020_M88TS2022:
  343. v_agc = (int)agc_pwm * 16 - 670;
  344. break;
  345. }
  346. if (v_agc < 0)
  347. v_agc = 0;
  348. }
  349. return ts2020_read_tuner_gain(fe, v_agc, _gain);
  350. }
  351. /*
  352. * Gather statistics on a regular basis
  353. */
  354. static void ts2020_stat_work(struct work_struct *work)
  355. {
  356. struct ts2020_priv *priv = container_of(work, struct ts2020_priv,
  357. stat_work.work);
  358. struct i2c_client *client = priv->client;
  359. struct dtv_frontend_properties *c = &priv->fe->dtv_property_cache;
  360. int ret;
  361. dev_dbg(&client->dev, "\n");
  362. ret = ts2020_get_tuner_gain(priv->fe, &c->strength.stat[0].svalue);
  363. if (ret < 0)
  364. goto err;
  365. c->strength.stat[0].scale = FE_SCALE_DECIBEL;
  366. if (!priv->dont_poll)
  367. schedule_delayed_work(&priv->stat_work, msecs_to_jiffies(2000));
  368. return;
  369. err:
  370. dev_dbg(&client->dev, "failed=%d\n", ret);
  371. }
  372. /*
  373. * Read TS2020 signal strength in v3 format.
  374. */
  375. static int ts2020_read_signal_strength(struct dvb_frontend *fe,
  376. u16 *_signal_strength)
  377. {
  378. struct dtv_frontend_properties *c = &fe->dtv_property_cache;
  379. struct ts2020_priv *priv = fe->tuner_priv;
  380. unsigned strength;
  381. __s64 gain;
  382. if (priv->dont_poll)
  383. ts2020_stat_work(&priv->stat_work.work);
  384. if (c->strength.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
  385. *_signal_strength = 0;
  386. return 0;
  387. }
  388. gain = c->strength.stat[0].svalue;
  389. /* Calculate the signal strength based on the total gain of the tuner */
  390. if (gain < -85000)
  391. /* 0%: no signal or weak signal */
  392. strength = 0;
  393. else if (gain < -65000)
  394. /* 0% - 60%: weak signal */
  395. strength = 0 + div64_s64((85000 + gain) * 3, 1000);
  396. else if (gain < -45000)
  397. /* 60% - 90%: normal signal */
  398. strength = 60 + div64_s64((65000 + gain) * 3, 2000);
  399. else
  400. /* 90% - 99%: strong signal */
  401. strength = 90 + div64_s64((45000 + gain), 5000);
  402. *_signal_strength = strength * 65535 / 100;
  403. return 0;
  404. }
  405. static const struct dvb_tuner_ops ts2020_tuner_ops = {
  406. .info = {
  407. .name = "TS2020",
  408. .frequency_min_hz = 950 * MHz,
  409. .frequency_max_hz = 2150 * MHz
  410. },
  411. .init = ts2020_init,
  412. .release = ts2020_release,
  413. .sleep = ts2020_sleep,
  414. .set_params = ts2020_set_params,
  415. .get_frequency = ts2020_get_frequency,
  416. .get_if_frequency = ts2020_get_if_frequency,
  417. .get_rf_strength = ts2020_read_signal_strength,
  418. };
  419. struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe,
  420. const struct ts2020_config *config,
  421. struct i2c_adapter *i2c)
  422. {
  423. struct i2c_client *client;
  424. struct i2c_board_info board_info;
  425. /* This is only used by ts2020_probe() so can be on the stack */
  426. struct ts2020_config pdata;
  427. memcpy(&pdata, config, sizeof(pdata));
  428. pdata.fe = fe;
  429. pdata.attach_in_use = true;
  430. memset(&board_info, 0, sizeof(board_info));
  431. strscpy(board_info.type, "ts2020", I2C_NAME_SIZE);
  432. board_info.addr = config->tuner_address;
  433. board_info.platform_data = &pdata;
  434. client = i2c_new_device(i2c, &board_info);
  435. if (!client || !client->dev.driver)
  436. return NULL;
  437. return fe;
  438. }
  439. EXPORT_SYMBOL(ts2020_attach);
  440. /*
  441. * We implement own regmap locking due to legacy DVB attach which uses frontend
  442. * gate control callback to control I2C bus access. We can open / close gate and
  443. * serialize whole open / I2C-operation / close sequence at the same.
  444. */
  445. static void ts2020_regmap_lock(void *__dev)
  446. {
  447. struct ts2020_priv *dev = __dev;
  448. mutex_lock(&dev->regmap_mutex);
  449. if (dev->fe->ops.i2c_gate_ctrl)
  450. dev->fe->ops.i2c_gate_ctrl(dev->fe, 1);
  451. }
  452. static void ts2020_regmap_unlock(void *__dev)
  453. {
  454. struct ts2020_priv *dev = __dev;
  455. if (dev->fe->ops.i2c_gate_ctrl)
  456. dev->fe->ops.i2c_gate_ctrl(dev->fe, 0);
  457. mutex_unlock(&dev->regmap_mutex);
  458. }
  459. static int ts2020_probe(struct i2c_client *client,
  460. const struct i2c_device_id *id)
  461. {
  462. struct ts2020_config *pdata = client->dev.platform_data;
  463. struct dvb_frontend *fe = pdata->fe;
  464. struct ts2020_priv *dev;
  465. int ret;
  466. u8 u8tmp;
  467. unsigned int utmp;
  468. char *chip_str;
  469. dev = kzalloc(sizeof(*dev), GFP_KERNEL);
  470. if (!dev) {
  471. ret = -ENOMEM;
  472. goto err;
  473. }
  474. /* create regmap */
  475. mutex_init(&dev->regmap_mutex);
  476. dev->regmap_config.reg_bits = 8,
  477. dev->regmap_config.val_bits = 8,
  478. dev->regmap_config.lock = ts2020_regmap_lock,
  479. dev->regmap_config.unlock = ts2020_regmap_unlock,
  480. dev->regmap_config.lock_arg = dev,
  481. dev->regmap = regmap_init_i2c(client, &dev->regmap_config);
  482. if (IS_ERR(dev->regmap)) {
  483. ret = PTR_ERR(dev->regmap);
  484. goto err_kfree;
  485. }
  486. dev->i2c = client->adapter;
  487. dev->i2c_address = client->addr;
  488. dev->loop_through = pdata->loop_through;
  489. dev->clk_out = pdata->clk_out;
  490. dev->clk_out_div = pdata->clk_out_div;
  491. dev->dont_poll = pdata->dont_poll;
  492. dev->frequency_div = pdata->frequency_div;
  493. dev->fe = fe;
  494. dev->get_agc_pwm = pdata->get_agc_pwm;
  495. fe->tuner_priv = dev;
  496. dev->client = client;
  497. INIT_DELAYED_WORK(&dev->stat_work, ts2020_stat_work);
  498. /* check if the tuner is there */
  499. ret = regmap_read(dev->regmap, 0x00, &utmp);
  500. if (ret)
  501. goto err_regmap_exit;
  502. if ((utmp & 0x03) == 0x00) {
  503. ret = regmap_write(dev->regmap, 0x00, 0x01);
  504. if (ret)
  505. goto err_regmap_exit;
  506. usleep_range(2000, 50000);
  507. }
  508. ret = regmap_write(dev->regmap, 0x00, 0x03);
  509. if (ret)
  510. goto err_regmap_exit;
  511. usleep_range(2000, 50000);
  512. ret = regmap_read(dev->regmap, 0x00, &utmp);
  513. if (ret)
  514. goto err_regmap_exit;
  515. dev_dbg(&client->dev, "chip_id=%02x\n", utmp);
  516. switch (utmp) {
  517. case 0x01:
  518. case 0x41:
  519. case 0x81:
  520. dev->tuner = TS2020_M88TS2020;
  521. chip_str = "TS2020";
  522. if (!dev->frequency_div)
  523. dev->frequency_div = 1060000;
  524. break;
  525. case 0xc3:
  526. case 0x83:
  527. dev->tuner = TS2020_M88TS2022;
  528. chip_str = "TS2022";
  529. if (!dev->frequency_div)
  530. dev->frequency_div = 1103000;
  531. break;
  532. default:
  533. ret = -ENODEV;
  534. goto err_regmap_exit;
  535. }
  536. if (dev->tuner == TS2020_M88TS2022) {
  537. switch (dev->clk_out) {
  538. case TS2020_CLK_OUT_DISABLED:
  539. u8tmp = 0x60;
  540. break;
  541. case TS2020_CLK_OUT_ENABLED:
  542. u8tmp = 0x70;
  543. ret = regmap_write(dev->regmap, 0x05, dev->clk_out_div);
  544. if (ret)
  545. goto err_regmap_exit;
  546. break;
  547. case TS2020_CLK_OUT_ENABLED_XTALOUT:
  548. u8tmp = 0x6c;
  549. break;
  550. default:
  551. ret = -EINVAL;
  552. goto err_regmap_exit;
  553. }
  554. ret = regmap_write(dev->regmap, 0x42, u8tmp);
  555. if (ret)
  556. goto err_regmap_exit;
  557. if (dev->loop_through)
  558. u8tmp = 0xec;
  559. else
  560. u8tmp = 0x6c;
  561. ret = regmap_write(dev->regmap, 0x62, u8tmp);
  562. if (ret)
  563. goto err_regmap_exit;
  564. }
  565. /* sleep */
  566. ret = regmap_write(dev->regmap, 0x00, 0x00);
  567. if (ret)
  568. goto err_regmap_exit;
  569. dev_info(&client->dev,
  570. "Montage Technology %s successfully identified\n", chip_str);
  571. memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops,
  572. sizeof(struct dvb_tuner_ops));
  573. if (!pdata->attach_in_use)
  574. fe->ops.tuner_ops.release = NULL;
  575. i2c_set_clientdata(client, dev);
  576. return 0;
  577. err_regmap_exit:
  578. regmap_exit(dev->regmap);
  579. err_kfree:
  580. kfree(dev);
  581. err:
  582. dev_dbg(&client->dev, "failed=%d\n", ret);
  583. return ret;
  584. }
  585. static int ts2020_remove(struct i2c_client *client)
  586. {
  587. struct ts2020_priv *dev = i2c_get_clientdata(client);
  588. dev_dbg(&client->dev, "\n");
  589. /* stop statistics polling */
  590. if (!dev->dont_poll)
  591. cancel_delayed_work_sync(&dev->stat_work);
  592. regmap_exit(dev->regmap);
  593. kfree(dev);
  594. return 0;
  595. }
  596. static const struct i2c_device_id ts2020_id_table[] = {
  597. {"ts2020", 0},
  598. {"ts2022", 0},
  599. {}
  600. };
  601. MODULE_DEVICE_TABLE(i2c, ts2020_id_table);
  602. static struct i2c_driver ts2020_driver = {
  603. .driver = {
  604. .name = "ts2020",
  605. },
  606. .probe = ts2020_probe,
  607. .remove = ts2020_remove,
  608. .id_table = ts2020_id_table,
  609. };
  610. module_i2c_driver(ts2020_driver);
  611. MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>");
  612. MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module");
  613. MODULE_LICENSE("GPL");