1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com> |
4 | * |
5 | * Original author: |
6 | * Ben Collins <bcollins@ubuntu.com> |
7 | * |
8 | * Additional work by: |
9 | * John Brooks <john.brooks@bluecherry.net> |
10 | */ |
11 | |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/pci.h> |
15 | #include <linux/interrupt.h> |
16 | #include <linux/videodev2.h> |
17 | #include <linux/delay.h> |
18 | #include <linux/sysfs.h> |
19 | #include <linux/ktime.h> |
20 | #include <linux/slab.h> |
21 | |
22 | #include "solo6x10.h" |
23 | #include "solo6x10-tw28.h" |
24 | |
25 | MODULE_DESCRIPTION("Softlogic 6x10 MPEG4/H.264/G.723 CODEC V4L2/ALSA Driver" ); |
26 | MODULE_AUTHOR("Bluecherry <maintainers@bluecherrydvr.com>" ); |
27 | MODULE_VERSION(SOLO6X10_VERSION); |
28 | MODULE_LICENSE("GPL" ); |
29 | |
30 | static unsigned video_nr = -1; |
31 | module_param(video_nr, uint, 0644); |
32 | MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)" ); |
33 | |
34 | static int full_eeprom; /* default is only top 64B */ |
35 | module_param(full_eeprom, uint, 0644); |
36 | MODULE_PARM_DESC(full_eeprom, "Allow access to full 128B EEPROM (dangerous)" ); |
37 | |
38 | |
39 | static void solo_set_time(struct solo_dev *solo_dev) |
40 | { |
41 | struct timespec64 ts; |
42 | |
43 | ktime_get_ts64(ts: &ts); |
44 | |
45 | /* no overflow because we use monotonic timestamps */ |
46 | solo_reg_write(solo_dev, SOLO_TIMER_SEC, data: (u32)ts.tv_sec); |
47 | solo_reg_write(solo_dev, SOLO_TIMER_USEC, data: (u32)ts.tv_nsec / NSEC_PER_USEC); |
48 | } |
49 | |
50 | static void solo_timer_sync(struct solo_dev *solo_dev) |
51 | { |
52 | u32 sec, usec; |
53 | struct timespec64 ts; |
54 | long diff; |
55 | |
56 | if (solo_dev->type != SOLO_DEV_6110) |
57 | return; |
58 | |
59 | if (++solo_dev->time_sync < 60) |
60 | return; |
61 | |
62 | solo_dev->time_sync = 0; |
63 | |
64 | sec = solo_reg_read(solo_dev, SOLO_TIMER_SEC); |
65 | usec = solo_reg_read(solo_dev, SOLO_TIMER_USEC); |
66 | |
67 | ktime_get_ts64(ts: &ts); |
68 | |
69 | diff = (s32)ts.tv_sec - (s32)sec; |
70 | diff = (diff * 1000000) |
71 | + ((s32)(ts.tv_nsec / NSEC_PER_USEC) - (s32)usec); |
72 | |
73 | if (diff > 1000 || diff < -1000) { |
74 | solo_set_time(solo_dev); |
75 | } else if (diff) { |
76 | long usec_lsb = solo_dev->usec_lsb; |
77 | |
78 | usec_lsb -= diff / 4; |
79 | if (usec_lsb < 0) |
80 | usec_lsb = 0; |
81 | else if (usec_lsb > 255) |
82 | usec_lsb = 255; |
83 | |
84 | solo_dev->usec_lsb = usec_lsb; |
85 | solo_reg_write(solo_dev, SOLO_TIMER_USEC_LSB, |
86 | data: solo_dev->usec_lsb); |
87 | } |
88 | } |
89 | |
90 | static irqreturn_t solo_isr(int irq, void *data) |
91 | { |
92 | struct solo_dev *solo_dev = data; |
93 | u32 status; |
94 | int i; |
95 | |
96 | status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); |
97 | if (!status) |
98 | return IRQ_NONE; |
99 | |
100 | /* Acknowledge all interrupts immediately */ |
101 | solo_reg_write(solo_dev, SOLO_IRQ_STAT, data: status); |
102 | |
103 | if (status & SOLO_IRQ_PCI_ERR) |
104 | solo_p2m_error_isr(solo_dev); |
105 | |
106 | for (i = 0; i < SOLO_NR_P2M; i++) |
107 | if (status & SOLO_IRQ_P2M(i)) |
108 | solo_p2m_isr(solo_dev, id: i); |
109 | |
110 | if (status & SOLO_IRQ_IIC) |
111 | solo_i2c_isr(solo_dev); |
112 | |
113 | if (status & SOLO_IRQ_VIDEO_IN) { |
114 | solo_video_in_isr(solo_dev); |
115 | solo_timer_sync(solo_dev); |
116 | } |
117 | |
118 | if (status & SOLO_IRQ_ENCODER) |
119 | solo_enc_v4l2_isr(solo_dev); |
120 | |
121 | if (status & SOLO_IRQ_G723) |
122 | solo_g723_isr(solo_dev); |
123 | |
124 | return IRQ_HANDLED; |
125 | } |
126 | |
127 | static void free_solo_dev(struct solo_dev *solo_dev) |
128 | { |
129 | struct pci_dev *pdev = solo_dev->pdev; |
130 | |
131 | if (solo_dev->dev.parent) |
132 | device_unregister(dev: &solo_dev->dev); |
133 | |
134 | if (solo_dev->reg_base) { |
135 | /* Bring down the sub-devices first */ |
136 | solo_g723_exit(solo_dev); |
137 | solo_enc_v4l2_exit(solo_dev); |
138 | solo_enc_exit(solo_dev); |
139 | solo_v4l2_exit(solo_dev); |
140 | solo_disp_exit(solo_dev); |
141 | solo_gpio_exit(solo_dev); |
142 | solo_p2m_exit(solo_dev); |
143 | solo_i2c_exit(solo_dev); |
144 | |
145 | /* Now cleanup the PCI device */ |
146 | solo_irq_off(dev: solo_dev, mask: ~0); |
147 | free_irq(pdev->irq, solo_dev); |
148 | pci_iounmap(dev: pdev, solo_dev->reg_base); |
149 | } |
150 | |
151 | pci_release_regions(pdev); |
152 | pci_disable_device(dev: pdev); |
153 | v4l2_device_unregister(v4l2_dev: &solo_dev->v4l2_dev); |
154 | pci_set_drvdata(pdev, NULL); |
155 | |
156 | kfree(objp: solo_dev); |
157 | } |
158 | |
159 | static ssize_t eeprom_store(struct device *dev, struct device_attribute *attr, |
160 | const char *buf, size_t count) |
161 | { |
162 | struct solo_dev *solo_dev = |
163 | container_of(dev, struct solo_dev, dev); |
164 | u16 *p = (u16 *)buf; |
165 | int i; |
166 | |
167 | if (count & 0x1) |
168 | dev_warn(dev, "EEPROM Write not aligned (truncating)\n" ); |
169 | |
170 | if (!full_eeprom && count > 64) { |
171 | dev_warn(dev, "EEPROM Write truncated to 64 bytes\n" ); |
172 | count = 64; |
173 | } else if (full_eeprom && count > 128) { |
174 | dev_warn(dev, "EEPROM Write truncated to 128 bytes\n" ); |
175 | count = 128; |
176 | } |
177 | |
178 | solo_eeprom_ewen(solo_dev, w_en: 1); |
179 | |
180 | for (i = full_eeprom ? 0 : 32; i < min((int)(full_eeprom ? 64 : 32), |
181 | (int)(count / 2)); i++) |
182 | solo_eeprom_write(solo_dev, loc: i, cpu_to_be16(p[i])); |
183 | |
184 | solo_eeprom_ewen(solo_dev, w_en: 0); |
185 | |
186 | return count; |
187 | } |
188 | |
189 | static ssize_t eeprom_show(struct device *dev, struct device_attribute *attr, |
190 | char *buf) |
191 | { |
192 | struct solo_dev *solo_dev = |
193 | container_of(dev, struct solo_dev, dev); |
194 | u16 *p = (u16 *)buf; |
195 | int count = (full_eeprom ? 128 : 64); |
196 | int i; |
197 | |
198 | for (i = (full_eeprom ? 0 : 32); i < (count / 2); i++) |
199 | p[i] = be16_to_cpu(solo_eeprom_read(solo_dev, i)); |
200 | |
201 | return count; |
202 | } |
203 | |
204 | static ssize_t p2m_timeouts_show(struct device *dev, |
205 | struct device_attribute *attr, |
206 | char *buf) |
207 | { |
208 | struct solo_dev *solo_dev = |
209 | container_of(dev, struct solo_dev, dev); |
210 | |
211 | return sprintf(buf, fmt: "%d\n" , solo_dev->p2m_timeouts); |
212 | } |
213 | |
214 | static ssize_t sdram_size_show(struct device *dev, |
215 | struct device_attribute *attr, |
216 | char *buf) |
217 | { |
218 | struct solo_dev *solo_dev = |
219 | container_of(dev, struct solo_dev, dev); |
220 | |
221 | return sprintf(buf, fmt: "%dMegs\n" , solo_dev->sdram_size >> 20); |
222 | } |
223 | |
224 | static ssize_t tw28xx_show(struct device *dev, |
225 | struct device_attribute *attr, |
226 | char *buf) |
227 | { |
228 | struct solo_dev *solo_dev = |
229 | container_of(dev, struct solo_dev, dev); |
230 | |
231 | return sprintf(buf, fmt: "tw2815[%d] tw2864[%d] tw2865[%d]\n" , |
232 | hweight32(solo_dev->tw2815), |
233 | hweight32(solo_dev->tw2864), |
234 | hweight32(solo_dev->tw2865)); |
235 | } |
236 | |
237 | static ssize_t input_map_show(struct device *dev, |
238 | struct device_attribute *attr, |
239 | char *buf) |
240 | { |
241 | struct solo_dev *solo_dev = |
242 | container_of(dev, struct solo_dev, dev); |
243 | unsigned int val; |
244 | char *out = buf; |
245 | |
246 | val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_0); |
247 | out += sprintf(buf: out, fmt: "Channel 0 => Input %d\n" , val & 0x1f); |
248 | out += sprintf(buf: out, fmt: "Channel 1 => Input %d\n" , (val >> 5) & 0x1f); |
249 | out += sprintf(buf: out, fmt: "Channel 2 => Input %d\n" , (val >> 10) & 0x1f); |
250 | out += sprintf(buf: out, fmt: "Channel 3 => Input %d\n" , (val >> 15) & 0x1f); |
251 | out += sprintf(buf: out, fmt: "Channel 4 => Input %d\n" , (val >> 20) & 0x1f); |
252 | out += sprintf(buf: out, fmt: "Channel 5 => Input %d\n" , (val >> 25) & 0x1f); |
253 | |
254 | val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_1); |
255 | out += sprintf(buf: out, fmt: "Channel 6 => Input %d\n" , val & 0x1f); |
256 | out += sprintf(buf: out, fmt: "Channel 7 => Input %d\n" , (val >> 5) & 0x1f); |
257 | out += sprintf(buf: out, fmt: "Channel 8 => Input %d\n" , (val >> 10) & 0x1f); |
258 | out += sprintf(buf: out, fmt: "Channel 9 => Input %d\n" , (val >> 15) & 0x1f); |
259 | out += sprintf(buf: out, fmt: "Channel 10 => Input %d\n" , (val >> 20) & 0x1f); |
260 | out += sprintf(buf: out, fmt: "Channel 11 => Input %d\n" , (val >> 25) & 0x1f); |
261 | |
262 | val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_2); |
263 | out += sprintf(buf: out, fmt: "Channel 12 => Input %d\n" , val & 0x1f); |
264 | out += sprintf(buf: out, fmt: "Channel 13 => Input %d\n" , (val >> 5) & 0x1f); |
265 | out += sprintf(buf: out, fmt: "Channel 14 => Input %d\n" , (val >> 10) & 0x1f); |
266 | out += sprintf(buf: out, fmt: "Channel 15 => Input %d\n" , (val >> 15) & 0x1f); |
267 | out += sprintf(buf: out, fmt: "Spot Output => Input %d\n" , (val >> 20) & 0x1f); |
268 | |
269 | return out - buf; |
270 | } |
271 | |
272 | static ssize_t p2m_timeout_store(struct device *dev, |
273 | struct device_attribute *attr, |
274 | const char *buf, size_t count) |
275 | { |
276 | struct solo_dev *solo_dev = |
277 | container_of(dev, struct solo_dev, dev); |
278 | unsigned long ms; |
279 | int ret = kstrtoul(s: buf, base: 10, res: &ms); |
280 | |
281 | if (ret < 0 || ms > 200) |
282 | return -EINVAL; |
283 | solo_dev->p2m_jiffies = msecs_to_jiffies(m: ms); |
284 | |
285 | return count; |
286 | } |
287 | |
288 | static ssize_t p2m_timeout_show(struct device *dev, |
289 | struct device_attribute *attr, |
290 | char *buf) |
291 | { |
292 | struct solo_dev *solo_dev = |
293 | container_of(dev, struct solo_dev, dev); |
294 | |
295 | return sprintf(buf, fmt: "%ums\n" , jiffies_to_msecs(j: solo_dev->p2m_jiffies)); |
296 | } |
297 | |
298 | static ssize_t intervals_show(struct device *dev, |
299 | struct device_attribute *attr, |
300 | char *buf) |
301 | { |
302 | struct solo_dev *solo_dev = |
303 | container_of(dev, struct solo_dev, dev); |
304 | char *out = buf; |
305 | int fps = solo_dev->fps; |
306 | int i; |
307 | |
308 | for (i = 0; i < solo_dev->nr_chans; i++) { |
309 | out += sprintf(buf: out, fmt: "Channel %d: %d/%d (0x%08x)\n" , |
310 | i, solo_dev->v4l2_enc[i]->interval, fps, |
311 | solo_reg_read(solo_dev, SOLO_CAP_CH_INTV(i))); |
312 | } |
313 | |
314 | return out - buf; |
315 | } |
316 | |
317 | static ssize_t sdram_offsets_show(struct device *dev, |
318 | struct device_attribute *attr, |
319 | char *buf) |
320 | { |
321 | struct solo_dev *solo_dev = |
322 | container_of(dev, struct solo_dev, dev); |
323 | char *out = buf; |
324 | |
325 | out += sprintf(buf: out, fmt: "DISP: 0x%08x @ 0x%08x\n" , |
326 | SOLO_DISP_EXT_ADDR, |
327 | SOLO_DISP_EXT_SIZE); |
328 | |
329 | out += sprintf(buf: out, fmt: "EOSD: 0x%08x @ 0x%08x (0x%08x * %d)\n" , |
330 | SOLO_EOSD_EXT_ADDR, |
331 | SOLO_EOSD_EXT_AREA(solo_dev), |
332 | SOLO_EOSD_EXT_SIZE(solo_dev), |
333 | SOLO_EOSD_EXT_AREA(solo_dev) / |
334 | SOLO_EOSD_EXT_SIZE(solo_dev)); |
335 | |
336 | out += sprintf(buf: out, fmt: "MOTI: 0x%08x @ 0x%08x\n" , |
337 | SOLO_MOTION_EXT_ADDR(solo_dev), |
338 | SOLO_MOTION_EXT_SIZE); |
339 | |
340 | out += sprintf(buf: out, fmt: "G723: 0x%08x @ 0x%08x\n" , |
341 | SOLO_G723_EXT_ADDR(solo_dev), |
342 | SOLO_G723_EXT_SIZE); |
343 | |
344 | out += sprintf(buf: out, fmt: "CAPT: 0x%08x @ 0x%08x (0x%08x * %d)\n" , |
345 | SOLO_CAP_EXT_ADDR(solo_dev), |
346 | SOLO_CAP_EXT_SIZE(solo_dev), |
347 | SOLO_CAP_PAGE_SIZE, |
348 | SOLO_CAP_EXT_SIZE(solo_dev) / SOLO_CAP_PAGE_SIZE); |
349 | |
350 | out += sprintf(buf: out, fmt: "EREF: 0x%08x @ 0x%08x (0x%08x * %d)\n" , |
351 | SOLO_EREF_EXT_ADDR(solo_dev), |
352 | SOLO_EREF_EXT_AREA(solo_dev), |
353 | SOLO_EREF_EXT_SIZE, |
354 | SOLO_EREF_EXT_AREA(solo_dev) / SOLO_EREF_EXT_SIZE); |
355 | |
356 | out += sprintf(buf: out, fmt: "MPEG: 0x%08x @ 0x%08x\n" , |
357 | SOLO_MP4E_EXT_ADDR(solo_dev), |
358 | SOLO_MP4E_EXT_SIZE(solo_dev)); |
359 | |
360 | out += sprintf(buf: out, fmt: "JPEG: 0x%08x @ 0x%08x\n" , |
361 | SOLO_JPEG_EXT_ADDR(solo_dev), |
362 | SOLO_JPEG_EXT_SIZE(solo_dev)); |
363 | |
364 | return out - buf; |
365 | } |
366 | |
367 | static ssize_t sdram_show(struct file *file, struct kobject *kobj, |
368 | struct bin_attribute *a, char *buf, |
369 | loff_t off, size_t count) |
370 | { |
371 | struct device *dev = kobj_to_dev(kobj); |
372 | struct solo_dev *solo_dev = |
373 | container_of(dev, struct solo_dev, dev); |
374 | const int size = solo_dev->sdram_size; |
375 | |
376 | if (off >= size) |
377 | return 0; |
378 | |
379 | if (off + count > size) |
380 | count = size - off; |
381 | |
382 | if (solo_p2m_dma(solo_dev, wr: 0, sys_addr: buf, ext_addr: off, size: count, repeat: 0, ext_size: 0)) |
383 | return -EIO; |
384 | |
385 | return count; |
386 | } |
387 | |
388 | static const struct device_attribute solo_dev_attrs[] = { |
389 | __ATTR(eeprom, 0640, eeprom_show, eeprom_store), |
390 | __ATTR(p2m_timeout, 0644, p2m_timeout_show, p2m_timeout_store), |
391 | __ATTR_RO(p2m_timeouts), |
392 | __ATTR_RO(sdram_size), |
393 | __ATTR_RO(tw28xx), |
394 | __ATTR_RO(input_map), |
395 | __ATTR_RO(intervals), |
396 | __ATTR_RO(sdram_offsets), |
397 | }; |
398 | |
399 | static void solo_device_release(struct device *dev) |
400 | { |
401 | /* Do nothing */ |
402 | } |
403 | |
404 | static int solo_sysfs_init(struct solo_dev *solo_dev) |
405 | { |
406 | struct bin_attribute *sdram_attr = &solo_dev->sdram_attr; |
407 | struct device *dev = &solo_dev->dev; |
408 | const char *driver; |
409 | int i; |
410 | |
411 | if (solo_dev->type == SOLO_DEV_6110) |
412 | driver = "solo6110" ; |
413 | else |
414 | driver = "solo6010" ; |
415 | |
416 | dev->release = solo_device_release; |
417 | dev->parent = &solo_dev->pdev->dev; |
418 | set_dev_node(dev, node: dev_to_node(dev: &solo_dev->pdev->dev)); |
419 | dev_set_name(dev, name: "%s-%d-%d" , driver, solo_dev->vfd->num, |
420 | solo_dev->nr_chans); |
421 | |
422 | if (device_register(dev)) { |
423 | put_device(dev); |
424 | dev->parent = NULL; |
425 | return -ENOMEM; |
426 | } |
427 | |
428 | for (i = 0; i < ARRAY_SIZE(solo_dev_attrs); i++) { |
429 | if (device_create_file(device: dev, entry: &solo_dev_attrs[i])) { |
430 | device_unregister(dev); |
431 | return -ENOMEM; |
432 | } |
433 | } |
434 | |
435 | sysfs_attr_init(&sdram_attr->attr); |
436 | sdram_attr->attr.name = "sdram" ; |
437 | sdram_attr->attr.mode = 0440; |
438 | sdram_attr->read = sdram_show; |
439 | sdram_attr->size = solo_dev->sdram_size; |
440 | |
441 | if (device_create_bin_file(dev, attr: sdram_attr)) { |
442 | device_unregister(dev); |
443 | return -ENOMEM; |
444 | } |
445 | |
446 | return 0; |
447 | } |
448 | |
449 | static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
450 | { |
451 | struct solo_dev *solo_dev; |
452 | int ret; |
453 | u8 chip_id; |
454 | |
455 | solo_dev = kzalloc(size: sizeof(*solo_dev), GFP_KERNEL); |
456 | if (solo_dev == NULL) |
457 | return -ENOMEM; |
458 | |
459 | if (id->driver_data == SOLO_DEV_6010) |
460 | dev_info(&pdev->dev, "Probing Softlogic 6010\n" ); |
461 | else |
462 | dev_info(&pdev->dev, "Probing Softlogic 6110\n" ); |
463 | |
464 | solo_dev->type = id->driver_data; |
465 | solo_dev->pdev = pdev; |
466 | ret = v4l2_device_register(dev: &pdev->dev, v4l2_dev: &solo_dev->v4l2_dev); |
467 | if (ret) |
468 | goto fail_probe; |
469 | |
470 | /* Only for during init */ |
471 | solo_dev->p2m_jiffies = msecs_to_jiffies(m: 100); |
472 | |
473 | ret = pci_enable_device(dev: pdev); |
474 | if (ret) |
475 | goto fail_probe; |
476 | |
477 | pci_set_master(dev: pdev); |
478 | |
479 | /* RETRY/TRDY Timeout disabled */ |
480 | pci_write_config_byte(dev: pdev, where: 0x40, val: 0x00); |
481 | pci_write_config_byte(dev: pdev, where: 0x41, val: 0x00); |
482 | |
483 | ret = pci_request_regions(pdev, SOLO6X10_NAME); |
484 | if (ret) |
485 | goto fail_probe; |
486 | |
487 | solo_dev->reg_base = pci_ioremap_bar(pdev, bar: 0); |
488 | if (solo_dev->reg_base == NULL) { |
489 | ret = -ENOMEM; |
490 | goto fail_probe; |
491 | } |
492 | |
493 | chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & |
494 | SOLO_CHIP_ID_MASK; |
495 | switch (chip_id) { |
496 | case 7: |
497 | solo_dev->nr_chans = 16; |
498 | solo_dev->nr_ext = 5; |
499 | break; |
500 | case 6: |
501 | solo_dev->nr_chans = 8; |
502 | solo_dev->nr_ext = 2; |
503 | break; |
504 | default: |
505 | dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n" , |
506 | chip_id); |
507 | fallthrough; |
508 | case 5: |
509 | solo_dev->nr_chans = 4; |
510 | solo_dev->nr_ext = 1; |
511 | } |
512 | |
513 | /* Disable all interrupts to start */ |
514 | solo_irq_off(dev: solo_dev, mask: ~0); |
515 | |
516 | /* Initial global settings */ |
517 | if (solo_dev->type == SOLO_DEV_6010) { |
518 | solo_dev->clock_mhz = 108; |
519 | solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT |
520 | | SOLO_SYS_CFG_INPUTDIV(25) |
521 | | SOLO_SYS_CFG_FEEDBACKDIV(solo_dev->clock_mhz * 2 - 2) |
522 | | SOLO_SYS_CFG_OUTDIV(3); |
523 | solo_reg_write(solo_dev, SOLO_SYS_CFG, data: solo_dev->sys_config); |
524 | } else { |
525 | u32 divq, divf; |
526 | |
527 | solo_dev->clock_mhz = 135; |
528 | |
529 | if (solo_dev->clock_mhz < 125) { |
530 | divq = 3; |
531 | divf = (solo_dev->clock_mhz * 4) / 3 - 1; |
532 | } else { |
533 | divq = 2; |
534 | divf = (solo_dev->clock_mhz * 2) / 3 - 1; |
535 | } |
536 | |
537 | solo_reg_write(solo_dev, SOLO_PLL_CONFIG, |
538 | data: (1 << 20) | /* PLL_RANGE */ |
539 | (8 << 15) | /* PLL_DIVR */ |
540 | (divq << 12) | |
541 | (divf << 4) | |
542 | (1 << 1) /* PLL_FSEN */); |
543 | |
544 | solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT; |
545 | } |
546 | |
547 | solo_reg_write(solo_dev, SOLO_SYS_CFG, data: solo_dev->sys_config); |
548 | solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, |
549 | data: solo_dev->clock_mhz - 1); |
550 | |
551 | /* PLL locking time of 1ms */ |
552 | mdelay(1); |
553 | |
554 | ret = request_irq(irq: pdev->irq, handler: solo_isr, IRQF_SHARED, SOLO6X10_NAME, |
555 | dev: solo_dev); |
556 | if (ret) |
557 | goto fail_probe; |
558 | |
559 | /* Handle this from the start */ |
560 | solo_irq_on(dev: solo_dev, SOLO_IRQ_PCI_ERR); |
561 | |
562 | ret = solo_i2c_init(solo_dev); |
563 | if (ret) |
564 | goto fail_probe; |
565 | |
566 | /* Setup the DMA engine */ |
567 | solo_reg_write(solo_dev, SOLO_DMA_CTRL, |
568 | SOLO_DMA_CTRL_REFRESH_CYCLE(1) | |
569 | SOLO_DMA_CTRL_SDRAM_SIZE(2) | |
570 | SOLO_DMA_CTRL_SDRAM_CLK_INVERT | |
571 | SOLO_DMA_CTRL_READ_CLK_SELECT | |
572 | SOLO_DMA_CTRL_LATENCY(1)); |
573 | |
574 | /* Undocumented crap */ |
575 | solo_reg_write(solo_dev, SOLO_DMA_CTRL1, |
576 | data: solo_dev->type == SOLO_DEV_6010 ? 0x100 : 0x300); |
577 | |
578 | if (solo_dev->type != SOLO_DEV_6010) { |
579 | solo_dev->usec_lsb = 0x3f; |
580 | solo_set_time(solo_dev); |
581 | } |
582 | |
583 | /* Disable watchdog */ |
584 | solo_reg_write(solo_dev, SOLO_WATCHDOG, data: 0); |
585 | |
586 | /* Initialize sub components */ |
587 | |
588 | ret = solo_p2m_init(solo_dev); |
589 | if (ret) |
590 | goto fail_probe; |
591 | |
592 | ret = solo_disp_init(solo_dev); |
593 | if (ret) |
594 | goto fail_probe; |
595 | |
596 | ret = solo_gpio_init(solo_dev); |
597 | if (ret) |
598 | goto fail_probe; |
599 | |
600 | ret = solo_tw28_init(solo_dev); |
601 | if (ret) |
602 | goto fail_probe; |
603 | |
604 | ret = solo_v4l2_init(solo_dev, nr: video_nr); |
605 | if (ret) |
606 | goto fail_probe; |
607 | |
608 | ret = solo_enc_init(solo_dev); |
609 | if (ret) |
610 | goto fail_probe; |
611 | |
612 | ret = solo_enc_v4l2_init(solo_dev, nr: video_nr); |
613 | if (ret) |
614 | goto fail_probe; |
615 | |
616 | ret = solo_g723_init(solo_dev); |
617 | if (ret) |
618 | goto fail_probe; |
619 | |
620 | ret = solo_sysfs_init(solo_dev); |
621 | if (ret) |
622 | goto fail_probe; |
623 | |
624 | /* Now that init is over, set this lower */ |
625 | solo_dev->p2m_jiffies = msecs_to_jiffies(m: 20); |
626 | |
627 | return 0; |
628 | |
629 | fail_probe: |
630 | free_solo_dev(solo_dev); |
631 | return ret; |
632 | } |
633 | |
634 | static void solo_pci_remove(struct pci_dev *pdev) |
635 | { |
636 | struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); |
637 | struct solo_dev *solo_dev = container_of(v4l2_dev, struct solo_dev, v4l2_dev); |
638 | |
639 | free_solo_dev(solo_dev); |
640 | } |
641 | |
642 | static const struct pci_device_id solo_id_table[] = { |
643 | /* 6010 based cards */ |
644 | { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010), |
645 | .driver_data = SOLO_DEV_6010 }, |
646 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4), |
647 | .driver_data = SOLO_DEV_6010 }, |
648 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9), |
649 | .driver_data = SOLO_DEV_6010 }, |
650 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16), |
651 | .driver_data = SOLO_DEV_6010 }, |
652 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4), |
653 | .driver_data = SOLO_DEV_6010 }, |
654 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9), |
655 | .driver_data = SOLO_DEV_6010 }, |
656 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16), |
657 | .driver_data = SOLO_DEV_6010 }, |
658 | /* 6110 based cards */ |
659 | { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), |
660 | .driver_data = SOLO_DEV_6110 }, |
661 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4), |
662 | .driver_data = SOLO_DEV_6110 }, |
663 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8), |
664 | .driver_data = SOLO_DEV_6110 }, |
665 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16), |
666 | .driver_data = SOLO_DEV_6110 }, |
667 | {0,} |
668 | }; |
669 | |
670 | MODULE_DEVICE_TABLE(pci, solo_id_table); |
671 | |
672 | static struct pci_driver solo_pci_driver = { |
673 | .name = SOLO6X10_NAME, |
674 | .id_table = solo_id_table, |
675 | .probe = solo_pci_probe, |
676 | .remove = solo_pci_remove, |
677 | }; |
678 | |
679 | module_pci_driver(solo_pci_driver); |
680 | |