[media] si2157: Silicon Labs Si2157 silicon tuner driver
[deliverable/linux.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");
This page took 0.036773 seconds and 6 git commands to generate.