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