si2157.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*
  2. * Silicon Labs Si2157 silicon tuner driver
  3. *
  4. * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. */
  16. #include "si2157_priv.h"
  17. /* execute firmware command */
  18. static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd)
  19. {
  20. int ret;
  21. u8 buf[1];
  22. unsigned long timeout;
  23. mutex_lock(&s->i2c_mutex);
  24. if (cmd->len) {
  25. /* write cmd and args for firmware */
  26. ret = i2c_master_send(s->client, cmd->args, cmd->len);
  27. if (ret < 0) {
  28. goto err_mutex_unlock;
  29. } else if (ret != cmd->len) {
  30. ret = -EREMOTEIO;
  31. goto err_mutex_unlock;
  32. }
  33. }
  34. /* wait cmd execution terminate */
  35. #define TIMEOUT 80
  36. timeout = jiffies + msecs_to_jiffies(TIMEOUT);
  37. while (!time_after(jiffies, timeout)) {
  38. ret = i2c_master_recv(s->client, buf, 1);
  39. if (ret < 0) {
  40. goto err_mutex_unlock;
  41. } else if (ret != 1) {
  42. ret = -EREMOTEIO;
  43. goto err_mutex_unlock;
  44. }
  45. /* firmware ready? */
  46. if ((buf[0] >> 7) & 0x01)
  47. break;
  48. }
  49. dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", __func__,
  50. jiffies_to_msecs(jiffies) -
  51. (jiffies_to_msecs(timeout) - TIMEOUT));
  52. if (!((buf[0] >> 7) & 0x01)) {
  53. ret = -ETIMEDOUT;
  54. goto err_mutex_unlock;
  55. } else {
  56. ret = 0;
  57. }
  58. err_mutex_unlock:
  59. mutex_unlock(&s->i2c_mutex);
  60. if (ret)
  61. goto err;
  62. return 0;
  63. err:
  64. dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
  65. return ret;
  66. }
  67. static int si2157_init(struct dvb_frontend *fe)
  68. {
  69. struct si2157 *s = fe->tuner_priv;
  70. dev_dbg(&s->client->dev, "%s:\n", __func__);
  71. s->active = true;
  72. return 0;
  73. }
  74. static int si2157_sleep(struct dvb_frontend *fe)
  75. {
  76. struct si2157 *s = fe->tuner_priv;
  77. dev_dbg(&s->client->dev, "%s:\n", __func__);
  78. s->active = false;
  79. return 0;
  80. }
  81. static int si2157_set_params(struct dvb_frontend *fe)
  82. {
  83. struct si2157 *s = fe->tuner_priv;
  84. struct dtv_frontend_properties *c = &fe->dtv_property_cache;
  85. int ret;
  86. struct si2157_cmd cmd;
  87. dev_dbg(&s->client->dev,
  88. "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n",
  89. __func__, c->delivery_system, c->frequency,
  90. c->bandwidth_hz);
  91. if (!s->active) {
  92. ret = -EAGAIN;
  93. goto err;
  94. }
  95. /* configure? */
  96. cmd.args[0] = 0xc0;
  97. cmd.args[1] = 0x00;
  98. cmd.args[2] = 0x0c;
  99. cmd.args[3] = 0x00;
  100. cmd.args[4] = 0x00;
  101. cmd.args[5] = 0x01;
  102. cmd.args[6] = 0x01;
  103. cmd.args[7] = 0x01;
  104. cmd.args[8] = 0x01;
  105. cmd.args[9] = 0x01;
  106. cmd.args[10] = 0x01;
  107. cmd.args[11] = 0x02;
  108. cmd.args[12] = 0x00;
  109. cmd.args[13] = 0x00;
  110. cmd.args[14] = 0x01;
  111. cmd.len = 15;
  112. ret = si2157_cmd_execute(s, &cmd);
  113. if (ret)
  114. goto err;
  115. cmd.args[0] = 0x02;
  116. cmd.len = 1;
  117. ret = si2157_cmd_execute(s, &cmd);
  118. if (ret)
  119. goto err;
  120. cmd.args[0] = 0x01;
  121. cmd.args[1] = 0x01;
  122. cmd.len = 2;
  123. ret = si2157_cmd_execute(s, &cmd);
  124. if (ret)
  125. goto err;
  126. /* set frequency */
  127. cmd.args[0] = 0x41;
  128. cmd.args[1] = 0x00;
  129. cmd.args[2] = 0x00;
  130. cmd.args[3] = 0x00;
  131. cmd.args[4] = (c->frequency >> 0) & 0xff;
  132. cmd.args[5] = (c->frequency >> 8) & 0xff;
  133. cmd.args[6] = (c->frequency >> 16) & 0xff;
  134. cmd.args[7] = (c->frequency >> 24) & 0xff;
  135. cmd.len = 8;
  136. ret = si2157_cmd_execute(s, &cmd);
  137. if (ret)
  138. goto err;
  139. return 0;
  140. err:
  141. dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
  142. return ret;
  143. }
  144. static const struct dvb_tuner_ops si2157_tuner_ops = {
  145. .info = {
  146. .name = "Silicon Labs Si2157",
  147. .frequency_min = 110000000,
  148. .frequency_max = 862000000,
  149. },
  150. .init = si2157_init,
  151. .sleep = si2157_sleep,
  152. .set_params = si2157_set_params,
  153. };
  154. static int si2157_probe(struct i2c_client *client,
  155. const struct i2c_device_id *id)
  156. {
  157. struct si2157_config *cfg = client->dev.platform_data;
  158. struct dvb_frontend *fe = cfg->fe;
  159. struct si2157 *s;
  160. struct si2157_cmd cmd;
  161. int ret;
  162. s = kzalloc(sizeof(struct si2157), GFP_KERNEL);
  163. if (!s) {
  164. ret = -ENOMEM;
  165. dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
  166. goto err;
  167. }
  168. s->client = client;
  169. s->fe = cfg->fe;
  170. mutex_init(&s->i2c_mutex);
  171. /* check if the tuner is there */
  172. cmd.len = 0;
  173. ret = si2157_cmd_execute(s, &cmd);
  174. if (ret)
  175. goto err;
  176. fe->tuner_priv = s;
  177. memcpy(&fe->ops.tuner_ops, &si2157_tuner_ops,
  178. sizeof(struct dvb_tuner_ops));
  179. i2c_set_clientdata(client, s);
  180. dev_info(&s->client->dev,
  181. "%s: Silicon Labs Si2157 successfully attached\n",
  182. KBUILD_MODNAME);
  183. return 0;
  184. err:
  185. dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
  186. kfree(s);
  187. return ret;
  188. }
  189. static int si2157_remove(struct i2c_client *client)
  190. {
  191. struct si2157 *s = i2c_get_clientdata(client);
  192. struct dvb_frontend *fe = s->fe;
  193. dev_dbg(&client->dev, "%s:\n", __func__);
  194. memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
  195. fe->tuner_priv = NULL;
  196. kfree(s);
  197. return 0;
  198. }
  199. static const struct i2c_device_id si2157_id[] = {
  200. {"si2157", 0},
  201. {}
  202. };
  203. MODULE_DEVICE_TABLE(i2c, si2157_id);
  204. static struct i2c_driver si2157_driver = {
  205. .driver = {
  206. .owner = THIS_MODULE,
  207. .name = "si2157",
  208. },
  209. .probe = si2157_probe,
  210. .remove = si2157_remove,
  211. .id_table = si2157_id,
  212. };
  213. module_i2c_driver(si2157_driver);
  214. MODULE_DESCRIPTION("Silicon Labs Si2157 silicon tuner driver");
  215. MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
  216. MODULE_LICENSE("GPL");