blob: 20b4a659e2e459dd9f536566f60c8fdda300eaa4 [file] [log] [blame]
Antti Palosaari711615d2014-04-14 21:55:12 -03001/*
2 * Silicon Labs Si2168 DVB-T/T2/C demodulator 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
Antti Palosaari845f3502014-04-10 22:00:50 -030017#include "si2168_priv.h"
18
19static const struct dvb_frontend_ops si2168_ops;
20
Antti Palosaarid2b72f62015-05-29 16:42:33 -030021/* execute firmware command */
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020022static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd)
Antti Palosaarid2b72f62015-05-29 16:42:33 -030023{
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020024 struct si2168_dev *dev = i2c_get_clientdata(client);
Antti Palosaari845f3502014-04-10 22:00:50 -030025 int ret;
26 unsigned long timeout;
27
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020028 mutex_lock(&dev->i2c_mutex);
29
Antti Palosaari845f3502014-04-10 22:00:50 -030030 if (cmd->wlen) {
31 /* write cmd and args for firmware */
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020032 ret = i2c_master_send(client, cmd->args, cmd->wlen);
Antti Palosaari845f3502014-04-10 22:00:50 -030033 if (ret < 0) {
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020034 goto err_mutex_unlock;
Antti Palosaari845f3502014-04-10 22:00:50 -030035 } else if (ret != cmd->wlen) {
36 ret = -EREMOTEIO;
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020037 goto err_mutex_unlock;
Antti Palosaari845f3502014-04-10 22:00:50 -030038 }
39 }
40
41 if (cmd->rlen) {
42 /* wait cmd execution terminate */
Jurgen Kramer551c33e2014-12-08 05:30:44 -030043 #define TIMEOUT 70
Antti Palosaari845f3502014-04-10 22:00:50 -030044 timeout = jiffies + msecs_to_jiffies(TIMEOUT);
45 while (!time_after(jiffies, timeout)) {
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020046 ret = i2c_master_recv(client, cmd->args, cmd->rlen);
Antti Palosaari845f3502014-04-10 22:00:50 -030047 if (ret < 0) {
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020048 goto err_mutex_unlock;
Antti Palosaari845f3502014-04-10 22:00:50 -030049 } else if (ret != cmd->rlen) {
50 ret = -EREMOTEIO;
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020051 goto err_mutex_unlock;
Antti Palosaari845f3502014-04-10 22:00:50 -030052 }
53
54 /* firmware ready? */
55 if ((cmd->args[0] >> 7) & 0x01)
56 break;
57 }
58
Antti Palosaari3de35832014-11-25 17:53:21 -030059 dev_dbg(&client->dev, "cmd execution took %d ms\n",
Antti Palosaari845f3502014-04-10 22:00:50 -030060 jiffies_to_msecs(jiffies) -
61 (jiffies_to_msecs(timeout) - TIMEOUT));
62
Olli Salonen7adf99d2015-05-05 13:54:16 -030063 /* error bit set? */
64 if ((cmd->args[0] >> 6) & 0x01) {
65 ret = -EREMOTEIO;
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020066 goto err_mutex_unlock;
Olli Salonen7adf99d2015-05-05 13:54:16 -030067 }
68
Antti Palosaarieefae302014-06-13 11:08:25 -030069 if (!((cmd->args[0] >> 7) & 0x01)) {
Antti Palosaari845f3502014-04-10 22:00:50 -030070 ret = -ETIMEDOUT;
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020071 goto err_mutex_unlock;
Antti Palosaari845f3502014-04-10 22:00:50 -030072 }
73 }
74
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020075 mutex_unlock(&dev->i2c_mutex);
Antti Palosaari4affbe12014-12-05 14:30:44 -030076 return 0;
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +020077err_mutex_unlock:
78 mutex_unlock(&dev->i2c_mutex);
Antti Palosaari3de35832014-11-25 17:53:21 -030079 dev_dbg(&client->dev, "failed=%d\n", ret);
Antti Palosaari845f3502014-04-10 22:00:50 -030080 return ret;
81}
82
Mauro Carvalho Chehab0df289a2015-06-07 14:53:52 -030083static int si2168_read_status(struct dvb_frontend *fe, enum fe_status *status)
Antti Palosaari845f3502014-04-10 22:00:50 -030084{
Antti Palosaari6307b562014-11-25 17:31:43 -030085 struct i2c_client *client = fe->demodulator_priv;
86 struct si2168_dev *dev = i2c_get_clientdata(client);
Antti Palosaaribffab932014-04-11 20:35:27 -030087 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
Antti Palosaari845f3502014-04-10 22:00:50 -030088 int ret;
89 struct si2168_cmd cmd;
90
91 *status = 0;
92
Antti Palosaaribd01c762014-11-25 16:46:16 -030093 if (!dev->active) {
Antti Palosaari845f3502014-04-10 22:00:50 -030094 ret = -EAGAIN;
95 goto err;
96 }
97
Antti Palosaaribffab932014-04-11 20:35:27 -030098 switch (c->delivery_system) {
99 case SYS_DVBT:
Antti Palosaari888680f2014-07-09 19:36:58 -0300100 memcpy(cmd.args, "\xa0\x01", 2);
Antti Palosaaribffab932014-04-11 20:35:27 -0300101 cmd.wlen = 2;
102 cmd.rlen = 13;
103 break;
Antti Palosaaric7908852014-04-12 01:58:32 -0300104 case SYS_DVBC_ANNEX_A:
Antti Palosaari888680f2014-07-09 19:36:58 -0300105 memcpy(cmd.args, "\x90\x01", 2);
Antti Palosaaric7908852014-04-12 01:58:32 -0300106 cmd.wlen = 2;
107 cmd.rlen = 9;
108 break;
Antti Palosaaribffab932014-04-11 20:35:27 -0300109 case SYS_DVBT2:
Antti Palosaari888680f2014-07-09 19:36:58 -0300110 memcpy(cmd.args, "\x50\x01", 2);
Antti Palosaaribffab932014-04-11 20:35:27 -0300111 cmd.wlen = 2;
112 cmd.rlen = 14;
113 break;
114 default:
115 ret = -EINVAL;
116 goto err;
117 }
118
Antti Palosaari6307b562014-11-25 17:31:43 -0300119 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300120 if (ret)
121 goto err;
122
Antti Palosaari722a0422014-04-22 21:36:32 -0300123 switch ((cmd.args[2] >> 1) & 0x03) {
124 case 0x01:
Antti Palosaari845f3502014-04-10 22:00:50 -0300125 *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
126 break;
Antti Palosaari722a0422014-04-22 21:36:32 -0300127 case 0x03:
Antti Palosaari845f3502014-04-10 22:00:50 -0300128 *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
129 FE_HAS_SYNC | FE_HAS_LOCK;
130 break;
131 }
132
Antti Palosaaribd01c762014-11-25 16:46:16 -0300133 dev->fe_status = *status;
Antti Palosaari845f3502014-04-10 22:00:50 -0300134
Antti Palosaari88ac8f82014-07-09 20:22:27 -0300135 if (*status & FE_HAS_LOCK) {
136 c->cnr.len = 1;
137 c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
138 c->cnr.stat[0].svalue = cmd.args[3] * 1000 / 4;
139 } else {
140 c->cnr.len = 1;
141 c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
142 }
143
Antti Palosaari3de35832014-11-25 17:53:21 -0300144 dev_dbg(&client->dev, "status=%02x args=%*ph\n",
Olli Salonen37b4e432014-08-05 08:54:08 -0300145 *status, cmd.rlen, cmd.args);
Antti Palosaari845f3502014-04-10 22:00:50 -0300146
147 return 0;
148err:
Antti Palosaari3de35832014-11-25 17:53:21 -0300149 dev_dbg(&client->dev, "failed=%d\n", ret);
Antti Palosaari845f3502014-04-10 22:00:50 -0300150 return ret;
151}
152
153static int si2168_set_frontend(struct dvb_frontend *fe)
154{
Antti Palosaari6307b562014-11-25 17:31:43 -0300155 struct i2c_client *client = fe->demodulator_priv;
156 struct si2168_dev *dev = i2c_get_clientdata(client);
Antti Palosaari845f3502014-04-10 22:00:50 -0300157 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
158 int ret;
159 struct si2168_cmd cmd;
Antti Palosaaribffab932014-04-11 20:35:27 -0300160 u8 bandwidth, delivery_system;
Antti Palosaari845f3502014-04-10 22:00:50 -0300161
Antti Palosaari3de35832014-11-25 17:53:21 -0300162 dev_dbg(&client->dev,
Antti Palosaarie5dd1102014-12-05 15:57:03 -0300163 "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u stream_id=%u\n",
164 c->delivery_system, c->modulation, c->frequency,
165 c->bandwidth_hz, c->symbol_rate, c->inversion,
166 c->stream_id);
Antti Palosaari845f3502014-04-10 22:00:50 -0300167
Antti Palosaaribd01c762014-11-25 16:46:16 -0300168 if (!dev->active) {
Antti Palosaari845f3502014-04-10 22:00:50 -0300169 ret = -EAGAIN;
170 goto err;
171 }
172
Antti Palosaaribffab932014-04-11 20:35:27 -0300173 switch (c->delivery_system) {
174 case SYS_DVBT:
175 delivery_system = 0x20;
176 break;
Antti Palosaaric7908852014-04-12 01:58:32 -0300177 case SYS_DVBC_ANNEX_A:
178 delivery_system = 0x30;
179 break;
Antti Palosaaribffab932014-04-11 20:35:27 -0300180 case SYS_DVBT2:
181 delivery_system = 0x70;
182 break;
183 default:
184 ret = -EINVAL;
185 goto err;
186 }
187
Olli Salonen683e98b2015-01-16 09:35:19 -0300188 if (c->bandwidth_hz == 0) {
189 ret = -EINVAL;
190 goto err;
Olli Salonen17d4d6a2015-01-16 09:35:20 -0300191 } else if (c->bandwidth_hz <= 2000000)
192 bandwidth = 0x02;
193 else if (c->bandwidth_hz <= 5000000)
Antti Palosaaribffab932014-04-11 20:35:27 -0300194 bandwidth = 0x05;
Antti Palosaaric7908852014-04-12 01:58:32 -0300195 else if (c->bandwidth_hz <= 6000000)
Antti Palosaaribffab932014-04-11 20:35:27 -0300196 bandwidth = 0x06;
Antti Palosaaric7908852014-04-12 01:58:32 -0300197 else if (c->bandwidth_hz <= 7000000)
Antti Palosaaribffab932014-04-11 20:35:27 -0300198 bandwidth = 0x07;
Antti Palosaaric7908852014-04-12 01:58:32 -0300199 else if (c->bandwidth_hz <= 8000000)
Antti Palosaaribffab932014-04-11 20:35:27 -0300200 bandwidth = 0x08;
Antti Palosaaric7908852014-04-12 01:58:32 -0300201 else if (c->bandwidth_hz <= 9000000)
202 bandwidth = 0x09;
203 else if (c->bandwidth_hz <= 10000000)
204 bandwidth = 0x0a;
205 else
206 bandwidth = 0x0f;
Antti Palosaari845f3502014-04-10 22:00:50 -0300207
208 /* program tuner */
209 if (fe->ops.tuner_ops.set_params) {
210 ret = fe->ops.tuner_ops.set_params(fe);
211 if (ret)
212 goto err;
213 }
214
215 memcpy(cmd.args, "\x88\x02\x02\x02\x02", 5);
216 cmd.wlen = 5;
217 cmd.rlen = 5;
Antti Palosaari6307b562014-11-25 17:31:43 -0300218 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300219 if (ret)
220 goto err;
221
Antti Palosaaribffab932014-04-11 20:35:27 -0300222 /* that has no big effect */
223 if (c->delivery_system == SYS_DVBT)
224 memcpy(cmd.args, "\x89\x21\x06\x11\xff\x98", 6);
Antti Palosaaric7908852014-04-12 01:58:32 -0300225 else if (c->delivery_system == SYS_DVBC_ANNEX_A)
226 memcpy(cmd.args, "\x89\x21\x06\x11\x89\xf0", 6);
Antti Palosaaribffab932014-04-11 20:35:27 -0300227 else if (c->delivery_system == SYS_DVBT2)
228 memcpy(cmd.args, "\x89\x21\x06\x11\x89\x20", 6);
Antti Palosaari845f3502014-04-10 22:00:50 -0300229 cmd.wlen = 6;
230 cmd.rlen = 3;
Antti Palosaari6307b562014-11-25 17:31:43 -0300231 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300232 if (ret)
233 goto err;
234
CrazyCate395e572014-08-16 18:33:14 -0300235 if (c->delivery_system == SYS_DVBT2) {
236 /* select PLP */
237 cmd.args[0] = 0x52;
238 cmd.args[1] = c->stream_id & 0xff;
239 cmd.args[2] = c->stream_id == NO_STREAM_ID_FILTER ? 0 : 1;
240 cmd.wlen = 3;
241 cmd.rlen = 1;
Antti Palosaari6307b562014-11-25 17:31:43 -0300242 ret = si2168_cmd_execute(client, &cmd);
CrazyCate395e572014-08-16 18:33:14 -0300243 if (ret)
244 goto err;
245 }
246
Antti Palosaari845f3502014-04-10 22:00:50 -0300247 memcpy(cmd.args, "\x51\x03", 2);
248 cmd.wlen = 2;
249 cmd.rlen = 12;
Antti Palosaari6307b562014-11-25 17:31:43 -0300250 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300251 if (ret)
252 goto err;
253
254 memcpy(cmd.args, "\x12\x08\x04", 3);
255 cmd.wlen = 3;
256 cmd.rlen = 3;
Antti Palosaari6307b562014-11-25 17:31:43 -0300257 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300258 if (ret)
259 goto err;
260
Antti Palosaari845f3502014-04-10 22:00:50 -0300261 memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6);
262 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300263 cmd.rlen = 4;
Antti Palosaari6307b562014-11-25 17:31:43 -0300264 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300265 if (ret)
266 goto err;
267
268 memcpy(cmd.args, "\x14\x00\x06\x10\x24\x00", 6);
269 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300270 cmd.rlen = 4;
Antti Palosaari6307b562014-11-25 17:31:43 -0300271 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300272 if (ret)
273 goto err;
274
Antti Palosaari845f3502014-04-10 22:00:50 -0300275 memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6);
276 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300277 cmd.rlen = 4;
Antti Palosaari6307b562014-11-25 17:31:43 -0300278 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300279 if (ret)
280 goto err;
281
282 memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6);
Antti Palosaaribffab932014-04-11 20:35:27 -0300283 cmd.args[4] = delivery_system | bandwidth;
Antti Palosaari845f3502014-04-10 22:00:50 -0300284 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300285 cmd.rlen = 4;
Antti Palosaari6307b562014-11-25 17:31:43 -0300286 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300287 if (ret)
288 goto err;
289
Luis Alves32bf8812014-07-17 14:31:28 -0300290 /* set DVB-C symbol rate */
291 if (c->delivery_system == SYS_DVBC_ANNEX_A) {
292 memcpy(cmd.args, "\x14\x00\x02\x11", 4);
Antti Palosaari346d4902014-12-05 14:54:14 -0300293 cmd.args[4] = ((c->symbol_rate / 1000) >> 0) & 0xff;
Luis Alves32bf8812014-07-17 14:31:28 -0300294 cmd.args[5] = ((c->symbol_rate / 1000) >> 8) & 0xff;
295 cmd.wlen = 6;
296 cmd.rlen = 4;
Antti Palosaari6307b562014-11-25 17:31:43 -0300297 ret = si2168_cmd_execute(client, &cmd);
Luis Alves32bf8812014-07-17 14:31:28 -0300298 if (ret)
299 goto err;
300 }
301
Antti Palosaari845f3502014-04-10 22:00:50 -0300302 memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6);
303 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300304 cmd.rlen = 4;
Antti Palosaari6307b562014-11-25 17:31:43 -0300305 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300306 if (ret)
307 goto err;
308
CrazyCat52791972014-11-14 18:22:10 -0300309 memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x08", 6);
Antti Palosaaribd01c762014-11-25 16:46:16 -0300310 cmd.args[5] |= dev->ts_clock_inv ? 0x00 : 0x10;
Antti Palosaari845f3502014-04-10 22:00:50 -0300311 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300312 cmd.rlen = 4;
Antti Palosaari6307b562014-11-25 17:31:43 -0300313 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300314 if (ret)
315 goto err;
316
CrazyCat52791972014-11-14 18:22:10 -0300317 memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x05", 6);
Antti Palosaaribd01c762014-11-25 16:46:16 -0300318 cmd.args[5] |= dev->ts_clock_inv ? 0x00 : 0x10;
Antti Palosaari845f3502014-04-10 22:00:50 -0300319 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300320 cmd.rlen = 4;
Antti Palosaari6307b562014-11-25 17:31:43 -0300321 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300322 if (ret)
323 goto err;
324
Antti Palosaari845f3502014-04-10 22:00:50 -0300325 memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6);
326 cmd.wlen = 6;
Antti Palosaari1d518c22014-07-11 10:40:29 -0300327 cmd.rlen = 4;
Antti Palosaari6307b562014-11-25 17:31:43 -0300328 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300329 if (ret)
330 goto err;
331
Olli Salonen43911772014-07-17 15:43:27 -0300332 memcpy(cmd.args, "\x14\x00\x01\x03\x0c\x00", 6);
333 cmd.wlen = 6;
334 cmd.rlen = 4;
Antti Palosaari6307b562014-11-25 17:31:43 -0300335 ret = si2168_cmd_execute(client, &cmd);
Olli Salonen43911772014-07-17 15:43:27 -0300336 if (ret)
337 goto err;
338
Antti Palosaari888680f2014-07-09 19:36:58 -0300339 memcpy(cmd.args, "\x85", 1);
Antti Palosaari845f3502014-04-10 22:00:50 -0300340 cmd.wlen = 1;
341 cmd.rlen = 1;
Antti Palosaari6307b562014-11-25 17:31:43 -0300342 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300343 if (ret)
344 goto err;
345
Antti Palosaaribd01c762014-11-25 16:46:16 -0300346 dev->delivery_system = c->delivery_system;
Antti Palosaari845f3502014-04-10 22:00:50 -0300347
348 return 0;
349err:
Antti Palosaari3de35832014-11-25 17:53:21 -0300350 dev_dbg(&client->dev, "failed=%d\n", ret);
Antti Palosaari845f3502014-04-10 22:00:50 -0300351 return ret;
352}
353
354static int si2168_init(struct dvb_frontend *fe)
355{
Antti Palosaari6307b562014-11-25 17:31:43 -0300356 struct i2c_client *client = fe->demodulator_priv;
357 struct si2168_dev *dev = i2c_get_clientdata(client);
Antti Palosaari845f3502014-04-10 22:00:50 -0300358 int ret, len, remaining;
Antti Palosaari58f66932014-12-05 17:22:42 -0300359 const struct firmware *fw;
Antti Palosaari845f3502014-04-10 22:00:50 -0300360 struct si2168_cmd cmd;
361
Antti Palosaari3de35832014-11-25 17:53:21 -0300362 dev_dbg(&client->dev, "\n");
Antti Palosaari845f3502014-04-10 22:00:50 -0300363
Olli Salonen8e417222014-08-25 15:07:04 -0300364 /* initialize */
Antti Palosaari888680f2014-07-09 19:36:58 -0300365 memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13);
Antti Palosaari845f3502014-04-10 22:00:50 -0300366 cmd.wlen = 13;
367 cmd.rlen = 0;
Antti Palosaari6307b562014-11-25 17:31:43 -0300368 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300369 if (ret)
370 goto err;
371
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300372 if (dev->warm) {
Olli Salonen8e417222014-08-25 15:07:04 -0300373 /* resume */
374 memcpy(cmd.args, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8);
375 cmd.wlen = 8;
376 cmd.rlen = 1;
Antti Palosaari6307b562014-11-25 17:31:43 -0300377 ret = si2168_cmd_execute(client, &cmd);
Olli Salonen8e417222014-08-25 15:07:04 -0300378 if (ret)
379 goto err;
380
381 memcpy(cmd.args, "\x85", 1);
382 cmd.wlen = 1;
383 cmd.rlen = 1;
Antti Palosaari6307b562014-11-25 17:31:43 -0300384 ret = si2168_cmd_execute(client, &cmd);
Olli Salonen8e417222014-08-25 15:07:04 -0300385 if (ret)
386 goto err;
387
388 goto warm;
389 }
390
391 /* power up */
Antti Palosaari888680f2014-07-09 19:36:58 -0300392 memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8);
Antti Palosaari845f3502014-04-10 22:00:50 -0300393 cmd.wlen = 8;
394 cmd.rlen = 1;
Antti Palosaari6307b562014-11-25 17:31:43 -0300395 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300396 if (ret)
397 goto err;
398
Antti Palosaari845f3502014-04-10 22:00:50 -0300399 /* request the firmware, this will block and timeout */
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300400 ret = request_firmware(&fw, dev->firmware_name, &client->dev);
Antti Palosaari845f3502014-04-10 22:00:50 -0300401 if (ret) {
Antti Palosaarib6b6fd62014-07-13 19:22:19 -0300402 /* fallback mechanism to handle old name for Si2168 B40 fw */
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300403 if (dev->chip_id == SI2168_CHIP_ID_B40) {
404 dev->firmware_name = SI2168_B40_FIRMWARE_FALLBACK;
405 ret = request_firmware(&fw, dev->firmware_name,
406 &client->dev);
Olli Salonenc9cb0822014-07-13 10:52:18 -0300407 }
Antti Palosaarib6b6fd62014-07-13 19:22:19 -0300408
409 if (ret == 0) {
Antti Palosaari3de35832014-11-25 17:53:21 -0300410 dev_notice(&client->dev,
Olli Salonen37b4e432014-08-05 08:54:08 -0300411 "please install firmware file '%s'\n",
412 SI2168_B40_FIRMWARE);
Antti Palosaarib6b6fd62014-07-13 19:22:19 -0300413 } else {
Antti Palosaari3de35832014-11-25 17:53:21 -0300414 dev_err(&client->dev,
Olli Salonen37b4e432014-08-05 08:54:08 -0300415 "firmware file '%s' not found\n",
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300416 dev->firmware_name);
Antti Palosaari346d4902014-12-05 14:54:14 -0300417 goto err_release_firmware;
Olli Salonenc9cb0822014-07-13 10:52:18 -0300418 }
Antti Palosaari845f3502014-04-10 22:00:50 -0300419 }
420
Antti Palosaari3de35832014-11-25 17:53:21 -0300421 dev_info(&client->dev, "downloading firmware from file '%s'\n",
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300422 dev->firmware_name);
Antti Palosaari845f3502014-04-10 22:00:50 -0300423
Olli Salonen1b97dc92014-11-27 16:42:23 -0300424 if ((fw->size % 17 == 0) && (fw->data[0] > 5)) {
425 /* firmware is in the new format */
426 for (remaining = fw->size; remaining > 0; remaining -= 17) {
427 len = fw->data[fw->size - remaining];
Laura Abbott47810b42015-09-29 21:10:09 -0300428 if (len > SI2168_ARGLEN) {
429 ret = -EINVAL;
430 break;
431 }
Olli Salonen1b97dc92014-11-27 16:42:23 -0300432 memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len);
433 cmd.wlen = len;
434 cmd.rlen = 1;
Antti Palosaari6307b562014-11-25 17:31:43 -0300435 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari68c16a72014-12-05 17:08:22 -0300436 if (ret)
437 break;
Olli Salonen1b97dc92014-11-27 16:42:23 -0300438 }
Antti Palosaari68c16a72014-12-05 17:08:22 -0300439 } else if (fw->size % 8 == 0) {
Olli Salonen1b97dc92014-11-27 16:42:23 -0300440 /* firmware is in the old format */
Antti Palosaari68c16a72014-12-05 17:08:22 -0300441 for (remaining = fw->size; remaining > 0; remaining -= 8) {
442 len = 8;
Olli Salonen1b97dc92014-11-27 16:42:23 -0300443 memcpy(cmd.args, &fw->data[fw->size - remaining], len);
444 cmd.wlen = len;
445 cmd.rlen = 1;
Antti Palosaari6307b562014-11-25 17:31:43 -0300446 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari68c16a72014-12-05 17:08:22 -0300447 if (ret)
448 break;
Antti Palosaari845f3502014-04-10 22:00:50 -0300449 }
Antti Palosaari68c16a72014-12-05 17:08:22 -0300450 } else {
451 /* bad or unknown firmware format */
452 ret = -EINVAL;
453 }
454
455 if (ret) {
456 dev_err(&client->dev, "firmware download failed %d\n", ret);
457 goto err_release_firmware;
Antti Palosaari845f3502014-04-10 22:00:50 -0300458 }
459
460 release_firmware(fw);
Antti Palosaari845f3502014-04-10 22:00:50 -0300461
Antti Palosaari888680f2014-07-09 19:36:58 -0300462 memcpy(cmd.args, "\x01\x01", 2);
Antti Palosaari845f3502014-04-10 22:00:50 -0300463 cmd.wlen = 2;
464 cmd.rlen = 1;
Antti Palosaari6307b562014-11-25 17:31:43 -0300465 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari845f3502014-04-10 22:00:50 -0300466 if (ret)
467 goto err;
468
Olli Salonena594cf22014-11-27 16:42:22 -0300469 /* query firmware version */
470 memcpy(cmd.args, "\x11", 1);
471 cmd.wlen = 1;
472 cmd.rlen = 10;
Antti Palosaari6307b562014-11-25 17:31:43 -0300473 ret = si2168_cmd_execute(client, &cmd);
Olli Salonena594cf22014-11-27 16:42:22 -0300474 if (ret)
475 goto err;
476
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300477 dev->version = (cmd.args[9] + '@') << 24 | (cmd.args[6] - '0') << 16 |
478 (cmd.args[7] - '0') << 8 | (cmd.args[8]) << 0;
479 dev_info(&client->dev, "firmware version: %c %d.%d.%d\n",
480 dev->version >> 24 & 0xff, dev->version >> 16 & 0xff,
481 dev->version >> 8 & 0xff, dev->version >> 0 & 0xff);
Olli Salonena594cf22014-11-27 16:42:22 -0300482
Olli Salonen389ce392014-08-11 16:58:10 -0300483 /* set ts mode */
484 memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6);
Antti Palosaaribd01c762014-11-25 16:46:16 -0300485 cmd.args[4] |= dev->ts_mode;
Olli Salonen8117a312015-05-05 13:54:14 -0300486 if (dev->ts_clock_gapped)
487 cmd.args[4] |= 0x40;
Olli Salonen389ce392014-08-11 16:58:10 -0300488 cmd.wlen = 6;
489 cmd.rlen = 4;
Antti Palosaari6307b562014-11-25 17:31:43 -0300490 ret = si2168_cmd_execute(client, &cmd);
Olli Salonen389ce392014-08-11 16:58:10 -0300491 if (ret)
492 goto err;
493
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300494 dev->warm = true;
Antti Palosaari45c3cbb2014-11-03 18:28:39 -0300495warm:
Antti Palosaaribd01c762014-11-25 16:46:16 -0300496 dev->active = true;
Antti Palosaari845f3502014-04-10 22:00:50 -0300497
498 return 0;
Antti Palosaari9b7839c2014-12-06 17:43:27 -0300499
Antti Palosaari346d4902014-12-05 14:54:14 -0300500err_release_firmware:
Markus Elfring034e1ec2014-11-19 19:23:15 -0300501 release_firmware(fw);
502err:
Antti Palosaari3de35832014-11-25 17:53:21 -0300503 dev_dbg(&client->dev, "failed=%d\n", ret);
Antti Palosaari845f3502014-04-10 22:00:50 -0300504 return ret;
505}
506
507static int si2168_sleep(struct dvb_frontend *fe)
508{
Antti Palosaari6307b562014-11-25 17:31:43 -0300509 struct i2c_client *client = fe->demodulator_priv;
510 struct si2168_dev *dev = i2c_get_clientdata(client);
Antti Palosaari4de0ed72014-07-09 18:45:31 -0300511 int ret;
512 struct si2168_cmd cmd;
Antti Palosaari845f3502014-04-10 22:00:50 -0300513
Antti Palosaari3de35832014-11-25 17:53:21 -0300514 dev_dbg(&client->dev, "\n");
Antti Palosaari845f3502014-04-10 22:00:50 -0300515
Antti Palosaaribd01c762014-11-25 16:46:16 -0300516 dev->active = false;
Antti Palosaari845f3502014-04-10 22:00:50 -0300517
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300518 /* Firmware B 4.0-11 or later loses warm state during sleep */
519 if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0))
520 dev->warm = false;
521
Antti Palosaari4de0ed72014-07-09 18:45:31 -0300522 memcpy(cmd.args, "\x13", 1);
523 cmd.wlen = 1;
524 cmd.rlen = 0;
Antti Palosaari6307b562014-11-25 17:31:43 -0300525 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaari4de0ed72014-07-09 18:45:31 -0300526 if (ret)
527 goto err;
528
Antti Palosaari845f3502014-04-10 22:00:50 -0300529 return 0;
Antti Palosaari4de0ed72014-07-09 18:45:31 -0300530err:
Antti Palosaari3de35832014-11-25 17:53:21 -0300531 dev_dbg(&client->dev, "failed=%d\n", ret);
Antti Palosaari4de0ed72014-07-09 18:45:31 -0300532 return ret;
Antti Palosaari845f3502014-04-10 22:00:50 -0300533}
534
535static int si2168_get_tune_settings(struct dvb_frontend *fe,
536 struct dvb_frontend_tune_settings *s)
537{
538 s->min_delay_ms = 900;
539
540 return 0;
541}
542
Peter Rosin58d7b542016-04-20 08:41:36 +0200543static int si2168_select(struct i2c_mux_core *muxc, u32 chan)
Antti Palosaari845f3502014-04-10 22:00:50 -0300544{
Peter Rosin58d7b542016-04-20 08:41:36 +0200545 struct i2c_client *client = i2c_mux_priv(muxc);
Antti Palosaari845f3502014-04-10 22:00:50 -0300546 int ret;
Antti Palosaarid2b72f62015-05-29 16:42:33 -0300547 struct si2168_cmd cmd;
Antti Palosaari845f3502014-04-10 22:00:50 -0300548
Antti Palosaarid2b72f62015-05-29 16:42:33 -0300549 /* open I2C gate */
550 memcpy(cmd.args, "\xc0\x0d\x01", 3);
551 cmd.wlen = 3;
552 cmd.rlen = 0;
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +0200553 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaarid2b72f62015-05-29 16:42:33 -0300554 if (ret)
555 goto err;
Antti Palosaari845f3502014-04-10 22:00:50 -0300556
Antti Palosaarid2b72f62015-05-29 16:42:33 -0300557 return 0;
558err:
559 dev_dbg(&client->dev, "failed=%d\n", ret);
Antti Palosaari845f3502014-04-10 22:00:50 -0300560 return ret;
561}
562
Peter Rosin58d7b542016-04-20 08:41:36 +0200563static int si2168_deselect(struct i2c_mux_core *muxc, u32 chan)
Antti Palosaari845f3502014-04-10 22:00:50 -0300564{
Peter Rosin58d7b542016-04-20 08:41:36 +0200565 struct i2c_client *client = i2c_mux_priv(muxc);
Antti Palosaari845f3502014-04-10 22:00:50 -0300566 int ret;
Antti Palosaarid2b72f62015-05-29 16:42:33 -0300567 struct si2168_cmd cmd;
Antti Palosaari845f3502014-04-10 22:00:50 -0300568
Antti Palosaarid2b72f62015-05-29 16:42:33 -0300569 /* close I2C gate */
570 memcpy(cmd.args, "\xc0\x0d\x00", 3);
571 cmd.wlen = 3;
572 cmd.rlen = 0;
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +0200573 ret = si2168_cmd_execute(client, &cmd);
Antti Palosaarid2b72f62015-05-29 16:42:33 -0300574 if (ret)
575 goto err;
Antti Palosaari845f3502014-04-10 22:00:50 -0300576
Antti Palosaarid2b72f62015-05-29 16:42:33 -0300577 return 0;
578err:
579 dev_dbg(&client->dev, "failed=%d\n", ret);
Antti Palosaari845f3502014-04-10 22:00:50 -0300580 return ret;
581}
582
583static const struct dvb_frontend_ops si2168_ops = {
Antti Palosaaric7908852014-04-12 01:58:32 -0300584 .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A},
Antti Palosaari845f3502014-04-10 22:00:50 -0300585 .info = {
586 .name = "Silicon Labs Si2168",
Antti Palosaarif1ecc5d2014-11-25 16:26:49 -0300587 .symbol_rate_min = 1000000,
588 .symbol_rate_max = 7200000,
Antti Palosaari845f3502014-04-10 22:00:50 -0300589 .caps = FE_CAN_FEC_1_2 |
590 FE_CAN_FEC_2_3 |
591 FE_CAN_FEC_3_4 |
592 FE_CAN_FEC_5_6 |
593 FE_CAN_FEC_7_8 |
594 FE_CAN_FEC_AUTO |
595 FE_CAN_QPSK |
596 FE_CAN_QAM_16 |
597 FE_CAN_QAM_32 |
598 FE_CAN_QAM_64 |
599 FE_CAN_QAM_128 |
600 FE_CAN_QAM_256 |
601 FE_CAN_QAM_AUTO |
602 FE_CAN_TRANSMISSION_MODE_AUTO |
603 FE_CAN_GUARD_INTERVAL_AUTO |
604 FE_CAN_HIERARCHY_AUTO |
605 FE_CAN_MUTE_TS |
Olli Salonen327eeb32014-09-23 13:53:09 -0300606 FE_CAN_2G_MODULATION |
607 FE_CAN_MULTISTREAM
Antti Palosaari845f3502014-04-10 22:00:50 -0300608 },
609
610 .get_tune_settings = si2168_get_tune_settings,
611
612 .init = si2168_init,
613 .sleep = si2168_sleep,
614
615 .set_frontend = si2168_set_frontend,
616
617 .read_status = si2168_read_status,
618};
619
620static int si2168_probe(struct i2c_client *client,
621 const struct i2c_device_id *id)
622{
623 struct si2168_config *config = client->dev.platform_data;
Antti Palosaaribd01c762014-11-25 16:46:16 -0300624 struct si2168_dev *dev;
Antti Palosaari845f3502014-04-10 22:00:50 -0300625 int ret;
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300626 struct si2168_cmd cmd;
Antti Palosaari845f3502014-04-10 22:00:50 -0300627
Olli Salonen37b4e432014-08-05 08:54:08 -0300628 dev_dbg(&client->dev, "\n");
Antti Palosaari845f3502014-04-10 22:00:50 -0300629
Antti Palosaaribd01c762014-11-25 16:46:16 -0300630 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
631 if (!dev) {
Antti Palosaari845f3502014-04-10 22:00:50 -0300632 ret = -ENOMEM;
Olli Salonen37b4e432014-08-05 08:54:08 -0300633 dev_err(&client->dev, "kzalloc() failed\n");
Antti Palosaari1ee5e7d2014-12-05 16:02:42 -0300634 goto err;
Antti Palosaari845f3502014-04-10 22:00:50 -0300635 }
636
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300637 i2c_set_clientdata(client, dev);
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +0200638 mutex_init(&dev->i2c_mutex);
639
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300640 /* Initialize */
641 memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13);
642 cmd.wlen = 13;
643 cmd.rlen = 0;
644 ret = si2168_cmd_execute(client, &cmd);
645 if (ret)
646 goto err_kfree;
647
648 /* Power up */
649 memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8);
650 cmd.wlen = 8;
651 cmd.rlen = 1;
652 ret = si2168_cmd_execute(client, &cmd);
653 if (ret)
654 goto err_kfree;
655
656 /* Query chip revision */
657 memcpy(cmd.args, "\x02", 1);
658 cmd.wlen = 1;
659 cmd.rlen = 13;
660 ret = si2168_cmd_execute(client, &cmd);
661 if (ret)
662 goto err_kfree;
663
664 dev->chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 |
665 cmd.args[3] << 8 | cmd.args[4] << 0;
666
667 switch (dev->chip_id) {
668 case SI2168_CHIP_ID_A20:
669 dev->firmware_name = SI2168_A20_FIRMWARE;
670 break;
671 case SI2168_CHIP_ID_A30:
672 dev->firmware_name = SI2168_A30_FIRMWARE;
673 break;
674 case SI2168_CHIP_ID_B40:
675 dev->firmware_name = SI2168_B40_FIRMWARE;
676 break;
677 default:
678 dev_dbg(&client->dev, "unknown chip version Si21%d-%c%c%c\n",
679 cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]);
680 ret = -ENODEV;
681 goto err_kfree;
682 }
683
684 dev->version = (cmd.args[1]) << 24 | (cmd.args[3] - '0') << 16 |
685 (cmd.args[4] - '0') << 8 | (cmd.args[5]) << 0;
686
Antti Palosaari845f3502014-04-10 22:00:50 -0300687 /* create mux i2c adapter for tuner */
Peter Rosin58d7b542016-04-20 08:41:36 +0200688 dev->muxc = i2c_mux_alloc(client->adapter, &client->dev,
Antti Palosaarie6d7ffc2016-05-04 22:15:32 +0200689 1, 0, I2C_MUX_LOCKED,
Peter Rosin58d7b542016-04-20 08:41:36 +0200690 si2168_select, si2168_deselect);
691 if (!dev->muxc) {
692 ret = -ENOMEM;
Antti Palosaari346d4902014-12-05 14:54:14 -0300693 goto err_kfree;
Luis Alves4d6efc72014-07-17 16:38:08 -0300694 }
Peter Rosin58d7b542016-04-20 08:41:36 +0200695 dev->muxc->priv = client;
696 ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0);
697 if (ret)
698 goto err_kfree;
Antti Palosaari845f3502014-04-10 22:00:50 -0300699
700 /* create dvb_frontend */
Antti Palosaaribd01c762014-11-25 16:46:16 -0300701 memcpy(&dev->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops));
Antti Palosaari6307b562014-11-25 17:31:43 -0300702 dev->fe.demodulator_priv = client;
Peter Rosin58d7b542016-04-20 08:41:36 +0200703 *config->i2c_adapter = dev->muxc->adapter[0];
Antti Palosaaribd01c762014-11-25 16:46:16 -0300704 *config->fe = &dev->fe;
705 dev->ts_mode = config->ts_mode;
706 dev->ts_clock_inv = config->ts_clock_inv;
Olli Salonen8117a312015-05-05 13:54:14 -0300707 dev->ts_clock_gapped = config->ts_clock_gapped;
Antti Palosaari845f3502014-04-10 22:00:50 -0300708
Antti Palosaari6ab1e942016-06-29 20:38:17 -0300709 dev_info(&client->dev, "Silicon Labs Si2168-%c%d%d successfully identified\n",
710 dev->version >> 24 & 0xff, dev->version >> 16 & 0xff,
711 dev->version >> 8 & 0xff);
712 dev_info(&client->dev, "firmware version: %c %d.%d.%d\n",
713 dev->version >> 24 & 0xff, dev->version >> 16 & 0xff,
714 dev->version >> 8 & 0xff, dev->version >> 0 & 0xff);
Antti Palosaari845f3502014-04-10 22:00:50 -0300715
Antti Palosaari845f3502014-04-10 22:00:50 -0300716 return 0;
Antti Palosaari346d4902014-12-05 14:54:14 -0300717err_kfree:
Antti Palosaaribd01c762014-11-25 16:46:16 -0300718 kfree(dev);
Antti Palosaari1ee5e7d2014-12-05 16:02:42 -0300719err:
Olli Salonen37b4e432014-08-05 08:54:08 -0300720 dev_dbg(&client->dev, "failed=%d\n", ret);
Antti Palosaari845f3502014-04-10 22:00:50 -0300721 return ret;
722}
723
724static int si2168_remove(struct i2c_client *client)
725{
Antti Palosaaribd01c762014-11-25 16:46:16 -0300726 struct si2168_dev *dev = i2c_get_clientdata(client);
Antti Palosaari845f3502014-04-10 22:00:50 -0300727
Olli Salonen37b4e432014-08-05 08:54:08 -0300728 dev_dbg(&client->dev, "\n");
Antti Palosaari845f3502014-04-10 22:00:50 -0300729
Peter Rosin58d7b542016-04-20 08:41:36 +0200730 i2c_mux_del_adapters(dev->muxc);
Antti Palosaari845f3502014-04-10 22:00:50 -0300731
Antti Palosaaribd01c762014-11-25 16:46:16 -0300732 dev->fe.ops.release = NULL;
733 dev->fe.demodulator_priv = NULL;
Antti Palosaari845f3502014-04-10 22:00:50 -0300734
Antti Palosaaribd01c762014-11-25 16:46:16 -0300735 kfree(dev);
Antti Palosaari845f3502014-04-10 22:00:50 -0300736
737 return 0;
738}
739
Antti Palosaari346d4902014-12-05 14:54:14 -0300740static const struct i2c_device_id si2168_id_table[] = {
Antti Palosaari845f3502014-04-10 22:00:50 -0300741 {"si2168", 0},
742 {}
743};
Antti Palosaari346d4902014-12-05 14:54:14 -0300744MODULE_DEVICE_TABLE(i2c, si2168_id_table);
Antti Palosaari845f3502014-04-10 22:00:50 -0300745
746static struct i2c_driver si2168_driver = {
747 .driver = {
Antti Palosaarie06be1d2016-06-29 20:38:18 -0300748 .name = "si2168",
749 .suppress_bind_attrs = true,
Antti Palosaari845f3502014-04-10 22:00:50 -0300750 },
751 .probe = si2168_probe,
752 .remove = si2168_remove,
Antti Palosaari346d4902014-12-05 14:54:14 -0300753 .id_table = si2168_id_table,
Antti Palosaari845f3502014-04-10 22:00:50 -0300754};
755
756module_i2c_driver(si2168_driver);
757
758MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
759MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver");
760MODULE_LICENSE("GPL");
Luis Alves635a90c2014-07-17 19:43:37 -0300761MODULE_FIRMWARE(SI2168_A20_FIRMWARE);
Antti Palosaari668aa632014-07-13 16:09:03 -0300762MODULE_FIRMWARE(SI2168_A30_FIRMWARE);
Olli Salonenc9cb0822014-07-13 10:52:18 -0300763MODULE_FIRMWARE(SI2168_B40_FIRMWARE);