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