aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb-frontends/sp2.c
diff options
context:
space:
mode:
authorOlli Salonen <olli.salonen@iki.fi>2014-08-08 03:06:35 -0400
committerMauro Carvalho Chehab <m.chehab@samsung.com>2014-09-02 14:06:49 -0400
commit868736ad3404b205794bc04233eca58293818dea (patch)
treec970af0d74efb2d2a770bac5471e2cd79b8fd3c1 /drivers/media/dvb-frontends/sp2.c
parent9b9beb1bf5eab918d2d54e42360dd1803b00dbfd (diff)
[media] sp2: Add I2C driver for CIMaX SP2 common interface module
Driver for the CIMaX SP2 common interface chip. It is very much based on the existing cimax2 driver for cx23885, but should be more reusable. The product has been sold with name Atmel T90FJR as well and the data sheets for that chip seem to be publicly available. It seems that the USB device that I have and the cx23885 based devices will need to interact differently with the chip for the CAM operations. Thus there is one callback function that is passed on to the sp2 driver (see function sp2_ci_op_cam for that one). IRQ functionality is not included currently (not needed by USB devices and I don't have a PCIe device for development). Signed-off-by: Olli Salonen <olli.salonen@iki.fi> Signed-off-by: Antti Palosaari <crope@iki.fi> Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
Diffstat (limited to 'drivers/media/dvb-frontends/sp2.c')
-rw-r--r--drivers/media/dvb-frontends/sp2.c441
1 files changed, 441 insertions, 0 deletions
diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c
new file mode 100644
index 000000000000..9b684d5c8f91
--- /dev/null
+++ b/drivers/media/dvb-frontends/sp2.c
@@ -0,0 +1,441 @@
1/*
2 * CIMaX SP2/SP2HF (Atmel T90FJR) CI driver
3 *
4 * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi>
5 *
6 * Heavily based on CIMax2(R) SP2 driver in conjunction with NetUp Dual
7 * DVB-S2 CI card (cimax2) with following copyrights:
8 *
9 * Copyright (C) 2009 NetUP Inc.
10 * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
11 * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 */
23
24#include "sp2_priv.h"
25
26static int sp2_read_i2c(struct sp2 *s, u8 reg, u8 *buf, int len)
27{
28 int ret;
29 struct i2c_client *client = s->client;
30 struct i2c_adapter *adap = client->adapter;
31 struct i2c_msg msg[] = {
32 {
33 .addr = client->addr,
34 .flags = 0,
35 .buf = &reg,
36 .len = 1
37 }, {
38 .addr = client->addr,
39 .flags = I2C_M_RD,
40 .buf = buf,
41 .len = len
42 }
43 };
44
45 ret = i2c_transfer(adap, msg, 2);
46
47 if (ret != 2) {
48 dev_err(&client->dev, "i2c read error, reg = 0x%02x, status = %d\n",
49 reg, ret);
50 if (ret < 0)
51 return ret;
52 else
53 return -EIO;
54 }
55
56 dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %02x\n",
57 client->addr, reg, buf[0]);
58
59 return 0;
60}
61
62static int sp2_write_i2c(struct sp2 *s, u8 reg, u8 *buf, int len)
63{
64 int ret;
65 u8 buffer[35];
66 struct i2c_client *client = s->client;
67 struct i2c_adapter *adap = client->adapter;
68 struct i2c_msg msg = {
69 .addr = client->addr,
70 .flags = 0,
71 .buf = &buffer[0],
72 .len = len + 1
73 };
74
75 if ((len + 1) > sizeof(buffer)) {
76 dev_err(&client->dev, "i2c wr reg=%02x: len=%d is too big!\n",
77 reg, len);
78 return -EINVAL;
79 }
80
81 buffer[0] = reg;
82 memcpy(&buffer[1], buf, len);
83
84 ret = i2c_transfer(adap, &msg, 1);
85
86 if (ret != 1) {
87 dev_err(&client->dev, "i2c write error, reg = 0x%02x, status = %d\n",
88 reg, ret);
89 if (ret < 0)
90 return ret;
91 else
92 return -EIO;
93 }
94
95 return 0;
96}
97
98static int sp2_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, u8 acs,
99 u8 read, int addr, u8 data)
100{
101 struct sp2 *s = en50221->data;
102 u8 store;
103 int mem, ret;
104 int (*ci_op_cam)(void*, u8, int, u8, int*) = s->ci_control;
105
106 dev_dbg(&s->client->dev, "slot=%d, acs=0x%02x, addr=0x%04x, data = 0x%02x",
107 slot, acs, addr, data);
108
109 if (slot != 0)
110 return -EINVAL;
111
112 /*
113 * change module access type between IO space and attribute memory
114 * when needed
115 */
116 if (s->module_access_type != acs) {
117 ret = sp2_read_i2c(s, 0x00, &store, 1);
118
119 if (ret)
120 return ret;
121
122 store &= ~(SP2_MOD_CTL_ACS1 | SP2_MOD_CTL_ACS0);
123 store |= acs;
124
125 ret = sp2_write_i2c(s, 0x00, &store, 1);
126 if (ret)
127 return ret;
128 }
129
130 s->module_access_type = acs;
131
132 /* implementation of ci_op_cam is device specific */
133 if (ci_op_cam) {
134 ret = ci_op_cam(s->priv, read, addr, data, &mem);
135 } else {
136 dev_err(&s->client->dev, "callback not defined");
137 return -EINVAL;
138 }
139
140 if (ret)
141 return ret;
142
143 if (read) {
144 dev_dbg(&s->client->dev, "cam read, addr=0x%04x, data = 0x%04x",
145 addr, mem);
146 return mem;
147 } else {
148 return 0;
149 }
150}
151
152int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
153 int slot, int addr)
154{
155 return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS,
156 SP2_CI_RD, addr, 0);
157}
158
159int sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
160 int slot, int addr, u8 data)
161{
162 return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS,
163 SP2_CI_WR, addr, data);
164}
165
166int sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221,
167 int slot, u8 addr)
168{
169 return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS,
170 SP2_CI_RD, addr, 0);
171}
172
173int sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221,
174 int slot, u8 addr, u8 data)
175{
176 return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS,
177 SP2_CI_WR, addr, data);
178}
179
180int sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
181{
182 struct sp2 *s = en50221->data;
183 u8 buf;
184 int ret;
185
186 dev_dbg(&s->client->dev, "slot: %d\n", slot);
187
188 if (slot != 0)
189 return -EINVAL;
190
191 /* RST on */
192 buf = SP2_MOD_CTL_RST;
193 ret = sp2_write_i2c(s, 0x00, &buf, 1);
194
195 if (ret)
196 return ret;
197
198 usleep_range(500, 600);
199
200 /* RST off */
201 buf = 0x00;
202 ret = sp2_write_i2c(s, 0x00, &buf, 1);
203
204 if (ret)
205 return ret;
206
207 msleep(1000);
208
209 return 0;
210}
211
212int sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
213{
214 struct sp2 *s = en50221->data;
215
216 dev_dbg(&s->client->dev, "slot:%d\n", slot);
217
218 /* not implemented */
219 return 0;
220}
221
222int sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot)
223{
224 struct sp2 *s = en50221->data;
225 u8 buf;
226
227 dev_dbg(&s->client->dev, "slot:%d\n", slot);
228
229 if (slot != 0)
230 return -EINVAL;
231
232 sp2_read_i2c(s, 0x00, &buf, 1);
233
234 /* disable bypass and enable TS */
235 buf |= (SP2_MOD_CTL_TSOEN | SP2_MOD_CTL_TSIEN);
236 return sp2_write_i2c(s, 0, &buf, 1);
237}
238
239int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221,
240 int slot, int open)
241{
242 struct sp2 *s = en50221->data;
243 u8 buf[2];
244 int ret;
245
246 dev_dbg(&s->client->dev, "slot:%d open:%d\n", slot, open);
247
248 /*
249 * CAM module INSERT/REMOVE processing. Slow operation because of i2c
250 * transfers. Throttle read to one per sec.
251 */
252 if (time_after(jiffies, s->next_status_checked_time)) {
253 ret = sp2_read_i2c(s, 0x00, buf, 1);
254 s->next_status_checked_time = jiffies + msecs_to_jiffies(1000);
255
256 if (ret)
257 return 0;
258
259 if (buf[0] & SP2_MOD_CTL_DET)
260 s->status = DVB_CA_EN50221_POLL_CAM_PRESENT |
261 DVB_CA_EN50221_POLL_CAM_READY;
262 else
263 s->status = 0;
264 }
265
266 return s->status;
267}
268
269int sp2_init(struct sp2 *s)
270{
271 int ret = 0;
272 u8 buf;
273 u8 cimax_init[34] = {
274 0x00, /* module A control*/
275 0x00, /* auto select mask high A */
276 0x00, /* auto select mask low A */
277 0x00, /* auto select pattern high A */
278 0x00, /* auto select pattern low A */
279 0x44, /* memory access time A, 600 ns */
280 0x00, /* invert input A */
281 0x00, /* RFU */
282 0x00, /* RFU */
283 0x00, /* module B control*/
284 0x00, /* auto select mask high B */
285 0x00, /* auto select mask low B */
286 0x00, /* auto select pattern high B */
287 0x00, /* auto select pattern low B */
288 0x44, /* memory access time B, 600 ns */
289 0x00, /* invert input B */
290 0x00, /* RFU */
291 0x00, /* RFU */
292 0x00, /* auto select mask high Ext */
293 0x00, /* auto select mask low Ext */
294 0x00, /* auto select pattern high Ext */
295 0x00, /* auto select pattern low Ext */
296 0x00, /* RFU */
297 0x02, /* destination - module A */
298 0x01, /* power control reg, VCC power on */
299 0x00, /* RFU */
300 0x00, /* int status read only */
301 0x00, /* Interrupt Mask Register */
302 0x05, /* EXTINT=active-high, INT=push-pull */
303 0x00, /* USCG1 */
304 0x04, /* ack active low */
305 0x00, /* LOCK = 0 */
306 0x22, /* unknown */
307 0x00, /* synchronization? */
308 };
309
310 dev_dbg(&s->client->dev, "\n");
311
312 s->ca.owner = THIS_MODULE;
313 s->ca.read_attribute_mem = sp2_ci_read_attribute_mem;
314 s->ca.write_attribute_mem = sp2_ci_write_attribute_mem;
315 s->ca.read_cam_control = sp2_ci_read_cam_control;
316 s->ca.write_cam_control = sp2_ci_write_cam_control;
317 s->ca.slot_reset = sp2_ci_slot_reset;
318 s->ca.slot_shutdown = sp2_ci_slot_shutdown;
319 s->ca.slot_ts_enable = sp2_ci_slot_ts_enable;
320 s->ca.poll_slot_status = sp2_ci_poll_slot_status;
321 s->ca.data = s;
322 s->module_access_type = 0;
323
324 /* initialize all regs */
325 ret = sp2_write_i2c(s, 0x00, &cimax_init[0], 34);
326 if (ret)
327 goto err;
328
329 /* lock registers */
330 buf = 1;
331 ret = sp2_write_i2c(s, 0x1f, &buf, 1);
332 if (ret)
333 goto err;
334
335 /* power on slots */
336 ret = sp2_write_i2c(s, 0x18, &buf, 1);
337 if (ret)
338 goto err;
339
340 ret = dvb_ca_en50221_init(s->dvb_adap, &s->ca, 0, 1);
341 if (ret)
342 goto err;
343
344 return 0;
345
346err:
347 dev_dbg(&s->client->dev, "init failed=%d\n", ret);
348 return ret;
349}
350
351int sp2_exit(struct i2c_client *client)
352{
353 struct sp2 *s;
354
355 dev_dbg(&client->dev, "\n");
356
357 if (client == NULL)
358 return 0;
359
360 s = i2c_get_clientdata(client);
361 if (s == NULL)
362 return 0;
363
364 if (s->ca.data == NULL)
365 return 0;
366
367 dvb_ca_en50221_release(&s->ca);
368
369 return 0;
370}
371
372static int sp2_probe(struct i2c_client *client,
373 const struct i2c_device_id *id)
374{
375 struct sp2_config *cfg = client->dev.platform_data;
376 struct sp2 *s;
377 int ret;
378
379 dev_dbg(&client->dev, "\n");
380
381 s = kzalloc(sizeof(struct sp2), GFP_KERNEL);
382 if (!s) {
383 ret = -ENOMEM;
384 dev_err(&client->dev, "kzalloc() failed\n");
385 goto err;
386 }
387
388 s->client = client;
389 s->dvb_adap = cfg->dvb_adap;
390 s->priv = cfg->priv;
391 s->ci_control = cfg->ci_control;
392
393 i2c_set_clientdata(client, s);
394
395 ret = sp2_init(s);
396 if (ret)
397 goto err;
398
399 dev_info(&s->client->dev, "CIMaX SP2 successfully attached\n");
400 return 0;
401err:
402 dev_dbg(&client->dev, "init failed=%d\n", ret);
403 kfree(s);
404
405 return ret;
406}
407
408static int sp2_remove(struct i2c_client *client)
409{
410 struct si2157 *s = i2c_get_clientdata(client);
411
412 dev_dbg(&client->dev, "\n");
413
414 sp2_exit(client);
415 if (s != NULL)
416 kfree(s);
417
418 return 0;
419}
420
421static const struct i2c_device_id sp2_id[] = {
422 {"sp2", 0},
423 {}
424};
425MODULE_DEVICE_TABLE(i2c, sp2_id);
426
427static struct i2c_driver sp2_driver = {
428 .driver = {
429 .owner = THIS_MODULE,
430 .name = "sp2",
431 },
432 .probe = sp2_probe,
433 .remove = sp2_remove,
434 .id_table = sp2_id,
435};
436
437module_i2c_driver(sp2_driver);
438
439MODULE_DESCRIPTION("CIMaX SP2/HF CI driver");
440MODULE_AUTHOR("Olli Salonen <olli.salonen@iki.fi>");
441MODULE_LICENSE("GPL");