1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ddbridge-ci.c: Digital Devices bridge CI (DuoFlex, CI Bridge) support |
4 | * |
5 | * Copyright (C) 2010-2017 Digital Devices GmbH |
6 | * Marcus Metzler <mocm@metzlerbros.de> |
7 | * Ralph Metzler <rjkm@metzlerbros.de> |
8 | */ |
9 | |
10 | #include "ddbridge.h" |
11 | #include "ddbridge-regs.h" |
12 | #include "ddbridge-ci.h" |
13 | #include "ddbridge-io.h" |
14 | #include "ddbridge-i2c.h" |
15 | |
16 | #include "cxd2099.h" |
17 | |
18 | /* Octopus CI internal CI interface */ |
19 | |
20 | static int wait_ci_ready(struct ddb_ci *ci) |
21 | { |
22 | u32 count = 10; |
23 | |
24 | ndelay(500); |
25 | do { |
26 | if (ddbreadl(dev: ci->port->dev, |
27 | CI_CONTROL(ci->nr)) & CI_READY) |
28 | break; |
29 | usleep_range(min: 1, max: 2); |
30 | if ((--count) == 0) |
31 | return -1; |
32 | } while (1); |
33 | return 0; |
34 | } |
35 | |
36 | static int read_attribute_mem(struct dvb_ca_en50221 *ca, |
37 | int slot, int address) |
38 | { |
39 | struct ddb_ci *ci = ca->data; |
40 | u32 val, off = (address >> 1) & (CI_BUFFER_SIZE - 1); |
41 | |
42 | if (address > CI_BUFFER_SIZE) |
43 | return -1; |
44 | ddbwritel(dev: ci->port->dev, CI_READ_CMD | (1 << 16) | address, |
45 | CI_DO_READ_ATTRIBUTES(ci->nr)); |
46 | wait_ci_ready(ci); |
47 | val = 0xff & ddbreadl(dev: ci->port->dev, CI_BUFFER(ci->nr) + off); |
48 | return val; |
49 | } |
50 | |
51 | static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, |
52 | int address, u8 value) |
53 | { |
54 | struct ddb_ci *ci = ca->data; |
55 | |
56 | ddbwritel(dev: ci->port->dev, CI_WRITE_CMD | (value << 16) | address, |
57 | CI_DO_ATTRIBUTE_RW(ci->nr)); |
58 | wait_ci_ready(ci); |
59 | return 0; |
60 | } |
61 | |
62 | static int read_cam_control(struct dvb_ca_en50221 *ca, |
63 | int slot, u8 address) |
64 | { |
65 | u32 count = 100; |
66 | struct ddb_ci *ci = ca->data; |
67 | u32 res; |
68 | |
69 | ddbwritel(dev: ci->port->dev, CI_READ_CMD | address, |
70 | CI_DO_IO_RW(ci->nr)); |
71 | ndelay(500); |
72 | do { |
73 | res = ddbreadl(dev: ci->port->dev, CI_READDATA(ci->nr)); |
74 | if (res & CI_READY) |
75 | break; |
76 | usleep_range(min: 1, max: 2); |
77 | if ((--count) == 0) |
78 | return -1; |
79 | } while (1); |
80 | return 0xff & res; |
81 | } |
82 | |
83 | static int write_cam_control(struct dvb_ca_en50221 *ca, int slot, |
84 | u8 address, u8 value) |
85 | { |
86 | struct ddb_ci *ci = ca->data; |
87 | |
88 | ddbwritel(dev: ci->port->dev, CI_WRITE_CMD | (value << 16) | address, |
89 | CI_DO_IO_RW(ci->nr)); |
90 | wait_ci_ready(ci); |
91 | return 0; |
92 | } |
93 | |
94 | static int slot_reset(struct dvb_ca_en50221 *ca, int slot) |
95 | { |
96 | struct ddb_ci *ci = ca->data; |
97 | |
98 | ddbwritel(dev: ci->port->dev, CI_POWER_ON, |
99 | CI_CONTROL(ci->nr)); |
100 | msleep(msecs: 100); |
101 | ddbwritel(dev: ci->port->dev, CI_POWER_ON | CI_RESET_CAM, |
102 | CI_CONTROL(ci->nr)); |
103 | ddbwritel(dev: ci->port->dev, CI_ENABLE | CI_POWER_ON | CI_RESET_CAM, |
104 | CI_CONTROL(ci->nr)); |
105 | usleep_range(min: 20, max: 25); |
106 | ddbwritel(dev: ci->port->dev, CI_ENABLE | CI_POWER_ON, |
107 | CI_CONTROL(ci->nr)); |
108 | return 0; |
109 | } |
110 | |
111 | static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) |
112 | { |
113 | struct ddb_ci *ci = ca->data; |
114 | |
115 | ddbwritel(dev: ci->port->dev, val: 0, CI_CONTROL(ci->nr)); |
116 | msleep(msecs: 300); |
117 | return 0; |
118 | } |
119 | |
120 | static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) |
121 | { |
122 | struct ddb_ci *ci = ca->data; |
123 | u32 val = ddbreadl(dev: ci->port->dev, CI_CONTROL(ci->nr)); |
124 | |
125 | ddbwritel(dev: ci->port->dev, val: val | CI_BYPASS_DISABLE, |
126 | CI_CONTROL(ci->nr)); |
127 | return 0; |
128 | } |
129 | |
130 | static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) |
131 | { |
132 | struct ddb_ci *ci = ca->data; |
133 | u32 val = ddbreadl(dev: ci->port->dev, CI_CONTROL(ci->nr)); |
134 | int stat = 0; |
135 | |
136 | if (val & CI_CAM_DETECT) |
137 | stat |= DVB_CA_EN50221_POLL_CAM_PRESENT; |
138 | if (val & CI_CAM_READY) |
139 | stat |= DVB_CA_EN50221_POLL_CAM_READY; |
140 | return stat; |
141 | } |
142 | |
143 | static struct dvb_ca_en50221 en_templ = { |
144 | .read_attribute_mem = read_attribute_mem, |
145 | .write_attribute_mem = write_attribute_mem, |
146 | .read_cam_control = read_cam_control, |
147 | .write_cam_control = write_cam_control, |
148 | .slot_reset = slot_reset, |
149 | .slot_shutdown = slot_shutdown, |
150 | .slot_ts_enable = slot_ts_enable, |
151 | .poll_slot_status = poll_slot_status, |
152 | }; |
153 | |
154 | static void ci_attach(struct ddb_port *port) |
155 | { |
156 | struct ddb_ci *ci; |
157 | |
158 | ci = kzalloc(size: sizeof(*ci), GFP_KERNEL); |
159 | if (!ci) |
160 | return; |
161 | memcpy(&ci->en, &en_templ, sizeof(en_templ)); |
162 | ci->en.data = ci; |
163 | port->en = &ci->en; |
164 | port->en_freedata = 1; |
165 | ci->port = port; |
166 | ci->nr = port->nr - 2; |
167 | } |
168 | |
169 | /* DuoFlex Dual CI support */ |
170 | |
171 | static int write_creg(struct ddb_ci *ci, u8 data, u8 mask) |
172 | { |
173 | struct i2c_adapter *i2c = &ci->port->i2c->adap; |
174 | u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13; |
175 | |
176 | ci->port->creg = (ci->port->creg & ~mask) | data; |
177 | return i2c_write_reg(adap: i2c, adr, reg: 0x02, val: ci->port->creg); |
178 | } |
179 | |
180 | static int read_attribute_mem_xo2(struct dvb_ca_en50221 *ca, |
181 | int slot, int address) |
182 | { |
183 | struct ddb_ci *ci = ca->data; |
184 | struct i2c_adapter *i2c = &ci->port->i2c->adap; |
185 | u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13; |
186 | int res; |
187 | u8 val; |
188 | |
189 | res = i2c_read_reg16(adapter: i2c, adr, reg: 0x8000 | address, val: &val); |
190 | return res ? res : val; |
191 | } |
192 | |
193 | static int write_attribute_mem_xo2(struct dvb_ca_en50221 *ca, int slot, |
194 | int address, u8 value) |
195 | { |
196 | struct ddb_ci *ci = ca->data; |
197 | struct i2c_adapter *i2c = &ci->port->i2c->adap; |
198 | u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13; |
199 | |
200 | return i2c_write_reg16(adap: i2c, adr, reg: 0x8000 | address, val: value); |
201 | } |
202 | |
203 | static int read_cam_control_xo2(struct dvb_ca_en50221 *ca, |
204 | int slot, u8 address) |
205 | { |
206 | struct ddb_ci *ci = ca->data; |
207 | struct i2c_adapter *i2c = &ci->port->i2c->adap; |
208 | u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13; |
209 | u8 val; |
210 | int res; |
211 | |
212 | res = i2c_read_reg(adapter: i2c, adr, reg: 0x20 | (address & 3), val: &val); |
213 | return res ? res : val; |
214 | } |
215 | |
216 | static int write_cam_control_xo2(struct dvb_ca_en50221 *ca, int slot, |
217 | u8 address, u8 value) |
218 | { |
219 | struct ddb_ci *ci = ca->data; |
220 | struct i2c_adapter *i2c = &ci->port->i2c->adap; |
221 | u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13; |
222 | |
223 | return i2c_write_reg(adap: i2c, adr, reg: 0x20 | (address & 3), val: value); |
224 | } |
225 | |
226 | static int slot_reset_xo2(struct dvb_ca_en50221 *ca, int slot) |
227 | { |
228 | struct ddb_ci *ci = ca->data; |
229 | |
230 | dev_dbg(ci->port->dev->dev, "%s\n" , __func__); |
231 | write_creg(ci, data: 0x01, mask: 0x01); |
232 | write_creg(ci, data: 0x04, mask: 0x04); |
233 | msleep(msecs: 20); |
234 | write_creg(ci, data: 0x02, mask: 0x02); |
235 | write_creg(ci, data: 0x00, mask: 0x04); |
236 | write_creg(ci, data: 0x18, mask: 0x18); |
237 | return 0; |
238 | } |
239 | |
240 | static int slot_shutdown_xo2(struct dvb_ca_en50221 *ca, int slot) |
241 | { |
242 | struct ddb_ci *ci = ca->data; |
243 | |
244 | dev_dbg(ci->port->dev->dev, "%s\n" , __func__); |
245 | write_creg(ci, data: 0x10, mask: 0xff); |
246 | write_creg(ci, data: 0x08, mask: 0x08); |
247 | return 0; |
248 | } |
249 | |
250 | static int slot_ts_enable_xo2(struct dvb_ca_en50221 *ca, int slot) |
251 | { |
252 | struct ddb_ci *ci = ca->data; |
253 | |
254 | dev_dbg(ci->port->dev->dev, "%s\n" , __func__); |
255 | write_creg(ci, data: 0x00, mask: 0x10); |
256 | return 0; |
257 | } |
258 | |
259 | static int poll_slot_status_xo2(struct dvb_ca_en50221 *ca, int slot, int open) |
260 | { |
261 | struct ddb_ci *ci = ca->data; |
262 | struct i2c_adapter *i2c = &ci->port->i2c->adap; |
263 | u8 adr = (ci->port->type == DDB_CI_EXTERNAL_XO2) ? 0x12 : 0x13; |
264 | u8 val = 0; |
265 | int stat = 0; |
266 | |
267 | i2c_read_reg(adapter: i2c, adr, reg: 0x01, val: &val); |
268 | |
269 | if (val & 2) |
270 | stat |= DVB_CA_EN50221_POLL_CAM_PRESENT; |
271 | if (val & 1) |
272 | stat |= DVB_CA_EN50221_POLL_CAM_READY; |
273 | return stat; |
274 | } |
275 | |
276 | static struct dvb_ca_en50221 en_xo2_templ = { |
277 | .read_attribute_mem = read_attribute_mem_xo2, |
278 | .write_attribute_mem = write_attribute_mem_xo2, |
279 | .read_cam_control = read_cam_control_xo2, |
280 | .write_cam_control = write_cam_control_xo2, |
281 | .slot_reset = slot_reset_xo2, |
282 | .slot_shutdown = slot_shutdown_xo2, |
283 | .slot_ts_enable = slot_ts_enable_xo2, |
284 | .poll_slot_status = poll_slot_status_xo2, |
285 | }; |
286 | |
287 | static void ci_xo2_attach(struct ddb_port *port) |
288 | { |
289 | struct ddb_ci *ci; |
290 | |
291 | ci = kzalloc(size: sizeof(*ci), GFP_KERNEL); |
292 | if (!ci) |
293 | return; |
294 | memcpy(&ci->en, &en_xo2_templ, sizeof(en_xo2_templ)); |
295 | ci->en.data = ci; |
296 | port->en = &ci->en; |
297 | port->en_freedata = 1; |
298 | ci->port = port; |
299 | ci->nr = port->nr - 2; |
300 | ci->port->creg = 0; |
301 | write_creg(ci, data: 0x10, mask: 0xff); |
302 | write_creg(ci, data: 0x08, mask: 0x08); |
303 | } |
304 | |
305 | static const struct cxd2099_cfg cxd_cfgtmpl = { |
306 | .bitrate = 72000, |
307 | .polarity = 1, |
308 | .clock_mode = 1, |
309 | .max_i2c = 512, |
310 | }; |
311 | |
312 | static int ci_cxd2099_attach(struct ddb_port *port, u32 bitrate) |
313 | { |
314 | struct cxd2099_cfg cxd_cfg = cxd_cfgtmpl; |
315 | struct i2c_client *client; |
316 | |
317 | cxd_cfg.bitrate = bitrate; |
318 | cxd_cfg.en = &port->en; |
319 | |
320 | client = dvb_module_probe(module_name: "cxd2099" , NULL, adap: &port->i2c->adap, |
321 | addr: 0x40, platform_data: &cxd_cfg); |
322 | if (!client) |
323 | goto err; |
324 | |
325 | port->dvb[0].i2c_client[0] = client; |
326 | port->en_freedata = 0; |
327 | return 0; |
328 | |
329 | err: |
330 | dev_err(port->dev->dev, "CXD2099AR attach failed\n" ); |
331 | return -ENODEV; |
332 | } |
333 | |
334 | int ddb_ci_attach(struct ddb_port *port, u32 bitrate) |
335 | { |
336 | int ret; |
337 | |
338 | switch (port->type) { |
339 | case DDB_CI_EXTERNAL_SONY: |
340 | ret = ci_cxd2099_attach(port, bitrate); |
341 | if (ret) |
342 | return -ENODEV; |
343 | break; |
344 | case DDB_CI_EXTERNAL_XO2: |
345 | case DDB_CI_EXTERNAL_XO2_B: |
346 | ci_xo2_attach(port); |
347 | break; |
348 | case DDB_CI_INTERNAL: |
349 | ci_attach(port); |
350 | break; |
351 | default: |
352 | return -ENODEV; |
353 | } |
354 | |
355 | if (!port->en) |
356 | return -ENODEV; |
357 | dvb_ca_en50221_init(dvb_adapter: port->dvb[0].adap, ca: port->en, flags: 0, slot_count: 1); |
358 | return 0; |
359 | } |
360 | |
361 | void ddb_ci_detach(struct ddb_port *port) |
362 | { |
363 | if (port->dvb[0].dev) |
364 | dvb_unregister_device(dvbdev: port->dvb[0].dev); |
365 | if (port->en) { |
366 | dvb_ca_en50221_release(ca: port->en); |
367 | |
368 | dvb_module_release(client: port->dvb[0].i2c_client[0]); |
369 | port->dvb[0].i2c_client[0] = NULL; |
370 | |
371 | /* free alloc'ed memory if needed */ |
372 | if (port->en_freedata) |
373 | kfree(objp: port->en->data); |
374 | |
375 | port->en = NULL; |
376 | } |
377 | } |
378 | |