]>
Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
faa4fd2a | 2 | /* |
dcae5dac HV |
3 | * Copyright (C) 2010-2013 Bluecherry, LLC <http://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> | |
faa4fd2a BC |
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> | |
dcae5dac HV |
17 | #include <linux/delay.h> |
18 | #include <linux/sysfs.h> | |
19 | #include <linux/ktime.h> | |
20 | #include <linux/slab.h> | |
21 | ||
ae69b22c | 22 | #include "solo6x10.h" |
dad7fab9 | 23 | #include "solo6x10-tw28.h" |
faa4fd2a | 24 | |
dcae5dac HV |
25 | MODULE_DESCRIPTION("Softlogic 6x10 MPEG4/H.264/G.723 CODEC V4L2/ALSA Driver"); |
26 | MODULE_AUTHOR("Bluecherry <maintainers@bluecherrydvr.com>"); | |
decebabf | 27 | MODULE_VERSION(SOLO6X10_VERSION); |
faa4fd2a BC |
28 | MODULE_LICENSE("GPL"); |
29 | ||
04ad3a64 | 30 | static unsigned video_nr = -1; |
dcae5dac HV |
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) | |
faa4fd2a | 40 | { |
6c542426 | 41 | struct timespec64 ts; |
dcae5dac | 42 | |
6c542426 | 43 | ktime_get_ts64(&ts); |
dcae5dac | 44 | |
6c542426 AB |
45 | /* no overflow because we use monotonic timestamps */ |
46 | solo_reg_write(solo_dev, SOLO_TIMER_SEC, (u32)ts.tv_sec); | |
47 | solo_reg_write(solo_dev, SOLO_TIMER_USEC, (u32)ts.tv_nsec / NSEC_PER_USEC); | |
faa4fd2a BC |
48 | } |
49 | ||
dcae5dac | 50 | static void solo_timer_sync(struct solo_dev *solo_dev) |
faa4fd2a | 51 | { |
dcae5dac | 52 | u32 sec, usec; |
6c542426 | 53 | struct timespec64 ts; |
dcae5dac HV |
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 | ||
6c542426 | 67 | ktime_get_ts64(&ts); |
dcae5dac | 68 | |
6c542426 | 69 | diff = (s32)ts.tv_sec - (s32)sec; |
dcae5dac | 70 | diff = (diff * 1000000) |
6c542426 | 71 | + ((s32)(ts.tv_nsec / NSEC_PER_USEC) - (s32)usec); |
dcae5dac HV |
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 | solo_dev->usec_lsb); | |
87 | } | |
faa4fd2a BC |
88 | } |
89 | ||
decebabf | 90 | static irqreturn_t solo_isr(int irq, void *data) |
faa4fd2a | 91 | { |
decebabf | 92 | struct solo_dev *solo_dev = data; |
faa4fd2a BC |
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 | ||
3c787b10 KH |
100 | /* Acknowledge all interrupts immediately */ |
101 | solo_reg_write(solo_dev, SOLO_IRQ_STAT, status); | |
faa4fd2a | 102 | |
dcae5dac HV |
103 | if (status & SOLO_IRQ_PCI_ERR) |
104 | solo_p2m_error_isr(solo_dev); | |
faa4fd2a BC |
105 | |
106 | for (i = 0; i < SOLO_NR_P2M; i++) | |
107 | if (status & SOLO_IRQ_P2M(i)) | |
108 | solo_p2m_isr(solo_dev, i); | |
109 | ||
110 | if (status & SOLO_IRQ_IIC) | |
111 | solo_i2c_isr(solo_dev); | |
112 | ||
dcae5dac | 113 | if (status & SOLO_IRQ_VIDEO_IN) { |
faa4fd2a | 114 | solo_video_in_isr(solo_dev); |
dcae5dac HV |
115 | solo_timer_sync(solo_dev); |
116 | } | |
faa4fd2a BC |
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 | ||
decebabf | 127 | static void free_solo_dev(struct solo_dev *solo_dev) |
faa4fd2a | 128 | { |
d9b82522 | 129 | struct pci_dev *pdev = solo_dev->pdev; |
faa4fd2a | 130 | |
dcae5dac HV |
131 | if (solo_dev->dev.parent) |
132 | device_unregister(&solo_dev->dev); | |
133 | ||
faa4fd2a | 134 | if (solo_dev->reg_base) { |
dcae5dac HV |
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 */ | |
decebabf | 146 | solo_irq_off(solo_dev, ~0); |
d9b82522 | 147 | free_irq(pdev->irq, solo_dev); |
dd43a627 | 148 | pci_iounmap(pdev, solo_dev->reg_base); |
faa4fd2a BC |
149 | } |
150 | ||
151 | pci_release_regions(pdev); | |
152 | pci_disable_device(pdev); | |
d9ebd623 | 153 | v4l2_device_unregister(&solo_dev->v4l2_dev); |
faa4fd2a BC |
154 | pci_set_drvdata(pdev, NULL); |
155 | ||
156 | kfree(solo_dev); | |
157 | } | |
158 | ||
dcae5dac HV |
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); | |
469ac53e | 164 | u16 *p = (u16 *)buf; |
dcae5dac HV |
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, 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, i, cpu_to_be16(p[i])); | |
183 | ||
184 | solo_eeprom_ewen(solo_dev, 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); | |
469ac53e | 194 | u16 *p = (u16 *)buf; |
dcae5dac HV |
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 | ||
dcae5dac HV |
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, "%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, "%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, "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(out, "Channel 0 => Input %d\n", val & 0x1f); | |
248 | out += sprintf(out, "Channel 1 => Input %d\n", (val >> 5) & 0x1f); | |
249 | out += sprintf(out, "Channel 2 => Input %d\n", (val >> 10) & 0x1f); | |
250 | out += sprintf(out, "Channel 3 => Input %d\n", (val >> 15) & 0x1f); | |
251 | out += sprintf(out, "Channel 4 => Input %d\n", (val >> 20) & 0x1f); | |
252 | out += sprintf(out, "Channel 5 => Input %d\n", (val >> 25) & 0x1f); | |
253 | ||
254 | val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_1); | |
255 | out += sprintf(out, "Channel 6 => Input %d\n", val & 0x1f); | |
256 | out += sprintf(out, "Channel 7 => Input %d\n", (val >> 5) & 0x1f); | |
257 | out += sprintf(out, "Channel 8 => Input %d\n", (val >> 10) & 0x1f); | |
258 | out += sprintf(out, "Channel 9 => Input %d\n", (val >> 15) & 0x1f); | |
259 | out += sprintf(out, "Channel 10 => Input %d\n", (val >> 20) & 0x1f); | |
260 | out += sprintf(out, "Channel 11 => Input %d\n", (val >> 25) & 0x1f); | |
261 | ||
262 | val = solo_reg_read(solo_dev, SOLO_VI_CH_SWITCH_2); | |
263 | out += sprintf(out, "Channel 12 => Input %d\n", val & 0x1f); | |
264 | out += sprintf(out, "Channel 13 => Input %d\n", (val >> 5) & 0x1f); | |
265 | out += sprintf(out, "Channel 14 => Input %d\n", (val >> 10) & 0x1f); | |
266 | out += sprintf(out, "Channel 15 => Input %d\n", (val >> 15) & 0x1f); | |
267 | out += sprintf(out, "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; | |
dcae5dac | 279 | int ret = kstrtoul(buf, 10, &ms); |
1c6f3db0 | 280 | |
dcae5dac HV |
281 | if (ret < 0 || ms > 200) |
282 | return -EINVAL; | |
283 | solo_dev->p2m_jiffies = msecs_to_jiffies(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, "%ums\n", jiffies_to_msecs(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(out, "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(out, "DISP: 0x%08x @ 0x%08x\n", | |
326 | SOLO_DISP_EXT_ADDR, | |
327 | SOLO_DISP_EXT_SIZE); | |
328 | ||
329 | out += sprintf(out, "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(out, "MOTI: 0x%08x @ 0x%08x\n", | |
337 | SOLO_MOTION_EXT_ADDR(solo_dev), | |
338 | SOLO_MOTION_EXT_SIZE); | |
339 | ||
340 | out += sprintf(out, "G723: 0x%08x @ 0x%08x\n", | |
341 | SOLO_G723_EXT_ADDR(solo_dev), | |
342 | SOLO_G723_EXT_SIZE); | |
343 | ||
344 | out += sprintf(out, "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(out, "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(out, "MPEG: 0x%08x @ 0x%08x\n", | |
357 | SOLO_MP4E_EXT_ADDR(solo_dev), | |
358 | SOLO_MP4E_EXT_SIZE(solo_dev)); | |
359 | ||
360 | out += sprintf(out, "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 = container_of(kobj, struct device, 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, 0, buf, off, count, 0, 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), | |
dcae5dac HV |
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, dev_to_node(&solo_dev->pdev->dev)); | |
419 | dev_set_name(dev, "%s-%d-%d", driver, solo_dev->vfd->num, | |
420 | solo_dev->nr_chans); | |
421 | ||
422 | if (device_register(dev)) { | |
423 | dev->parent = NULL; | |
424 | return -ENOMEM; | |
425 | } | |
426 | ||
427 | for (i = 0; i < ARRAY_SIZE(solo_dev_attrs); i++) { | |
428 | if (device_create_file(dev, &solo_dev_attrs[i])) { | |
429 | device_unregister(dev); | |
430 | return -ENOMEM; | |
431 | } | |
432 | } | |
433 | ||
ff82ad22 | 434 | sysfs_attr_init(&sdram_attr->attr); |
dcae5dac HV |
435 | sdram_attr->attr.name = "sdram"; |
436 | sdram_attr->attr.mode = 0440; | |
437 | sdram_attr->read = sdram_show; | |
438 | sdram_attr->size = solo_dev->sdram_size; | |
439 | ||
440 | if (device_create_bin_file(dev, sdram_attr)) { | |
441 | device_unregister(dev); | |
442 | return -ENOMEM; | |
443 | } | |
444 | ||
445 | return 0; | |
446 | } | |
447 | ||
448 | static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |
faa4fd2a | 449 | { |
decebabf | 450 | struct solo_dev *solo_dev; |
faa4fd2a | 451 | int ret; |
faa4fd2a | 452 | u8 chip_id; |
f62de9be | 453 | |
15300c1d PS |
454 | solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); |
455 | if (solo_dev == NULL) | |
faa4fd2a BC |
456 | return -ENOMEM; |
457 | ||
dcae5dac HV |
458 | if (id->driver_data == SOLO_DEV_6010) |
459 | dev_info(&pdev->dev, "Probing Softlogic 6010\n"); | |
460 | else | |
461 | dev_info(&pdev->dev, "Probing Softlogic 6110\n"); | |
462 | ||
463 | solo_dev->type = id->driver_data; | |
faa4fd2a | 464 | solo_dev->pdev = pdev; |
d9ebd623 HV |
465 | ret = v4l2_device_register(&pdev->dev, &solo_dev->v4l2_dev); |
466 | if (ret) | |
467 | goto fail_probe; | |
faa4fd2a | 468 | |
dcae5dac HV |
469 | /* Only for during init */ |
470 | solo_dev->p2m_jiffies = msecs_to_jiffies(100); | |
471 | ||
15300c1d PS |
472 | ret = pci_enable_device(pdev); |
473 | if (ret) | |
faa4fd2a BC |
474 | goto fail_probe; |
475 | ||
476 | pci_set_master(pdev); | |
477 | ||
dcae5dac HV |
478 | /* RETRY/TRDY Timeout disabled */ |
479 | pci_write_config_byte(pdev, 0x40, 0x00); | |
480 | pci_write_config_byte(pdev, 0x41, 0x00); | |
481 | ||
decebabf | 482 | ret = pci_request_regions(pdev, SOLO6X10_NAME); |
15300c1d | 483 | if (ret) |
faa4fd2a BC |
484 | goto fail_probe; |
485 | ||
15300c1d PS |
486 | solo_dev->reg_base = pci_ioremap_bar(pdev, 0); |
487 | if (solo_dev->reg_base == NULL) { | |
faa4fd2a BC |
488 | ret = -ENOMEM; |
489 | goto fail_probe; | |
490 | } | |
491 | ||
492 | chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & | |
dcae5dac | 493 | SOLO_CHIP_ID_MASK; |
faa4fd2a | 494 | switch (chip_id) { |
afabbe6d | 495 | case 7: |
496 | solo_dev->nr_chans = 16; | |
497 | solo_dev->nr_ext = 5; | |
498 | break; | |
499 | case 6: | |
500 | solo_dev->nr_chans = 8; | |
501 | solo_dev->nr_ext = 2; | |
502 | break; | |
503 | default: | |
dcae5dac | 504 | dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n", |
afabbe6d | 505 | chip_id); |
06eeefe8 | 506 | /* fall through */ |
afabbe6d | 507 | case 5: |
508 | solo_dev->nr_chans = 4; | |
509 | solo_dev->nr_ext = 1; | |
faa4fd2a BC |
510 | } |
511 | ||
512 | /* Disable all interrupts to start */ | |
decebabf | 513 | solo_irq_off(solo_dev, ~0); |
faa4fd2a BC |
514 | |
515 | /* Initial global settings */ | |
dcae5dac HV |
516 | if (solo_dev->type == SOLO_DEV_6010) { |
517 | solo_dev->clock_mhz = 108; | |
518 | solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT | |
519 | | SOLO_SYS_CFG_INPUTDIV(25) | |
520 | | SOLO_SYS_CFG_FEEDBACKDIV(solo_dev->clock_mhz * 2 - 2) | |
521 | | SOLO_SYS_CFG_OUTDIV(3); | |
522 | solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); | |
523 | } else { | |
524 | u32 divq, divf; | |
525 | ||
526 | solo_dev->clock_mhz = 135; | |
527 | ||
528 | if (solo_dev->clock_mhz < 125) { | |
529 | divq = 3; | |
530 | divf = (solo_dev->clock_mhz * 4) / 3 - 1; | |
23bfb5be | 531 | } else { |
dcae5dac HV |
532 | divq = 2; |
533 | divf = (solo_dev->clock_mhz * 2) / 3 - 1; | |
23bfb5be AM |
534 | } |
535 | ||
dcae5dac HV |
536 | solo_reg_write(solo_dev, SOLO_PLL_CONFIG, |
537 | (1 << 20) | /* PLL_RANGE */ | |
538 | (8 << 15) | /* PLL_DIVR */ | |
539 | (divq << 12) | | |
540 | (divf << 4) | | |
541 | (1 << 1) /* PLL_FSEN */); | |
908113d8 | 542 | |
dcae5dac HV |
543 | solo_dev->sys_config = SOLO_SYS_CFG_SDRAM64BIT; |
544 | } | |
908113d8 | 545 | |
dcae5dac HV |
546 | solo_reg_write(solo_dev, SOLO_SYS_CFG, solo_dev->sys_config); |
547 | solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, | |
548 | solo_dev->clock_mhz - 1); | |
faa4fd2a BC |
549 | |
550 | /* PLL locking time of 1ms */ | |
551 | mdelay(1); | |
552 | ||
decebabf | 553 | ret = request_irq(pdev->irq, solo_isr, IRQF_SHARED, SOLO6X10_NAME, |
faa4fd2a BC |
554 | solo_dev); |
555 | if (ret) | |
556 | goto fail_probe; | |
557 | ||
558 | /* Handle this from the start */ | |
decebabf | 559 | solo_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); |
faa4fd2a | 560 | |
15300c1d PS |
561 | ret = solo_i2c_init(solo_dev); |
562 | if (ret) | |
faa4fd2a BC |
563 | goto fail_probe; |
564 | ||
565 | /* Setup the DMA engine */ | |
faa4fd2a BC |
566 | solo_reg_write(solo_dev, SOLO_DMA_CTRL, |
567 | SOLO_DMA_CTRL_REFRESH_CYCLE(1) | | |
dcae5dac | 568 | SOLO_DMA_CTRL_SDRAM_SIZE(2) | |
faa4fd2a BC |
569 | SOLO_DMA_CTRL_SDRAM_CLK_INVERT | |
570 | SOLO_DMA_CTRL_READ_CLK_SELECT | | |
571 | SOLO_DMA_CTRL_LATENCY(1)); | |
572 | ||
dcae5dac HV |
573 | /* Undocumented crap */ |
574 | solo_reg_write(solo_dev, SOLO_DMA_CTRL1, | |
575 | solo_dev->type == SOLO_DEV_6010 ? 0x100 : 0x300); | |
576 | ||
577 | if (solo_dev->type != SOLO_DEV_6010) { | |
578 | solo_dev->usec_lsb = 0x3f; | |
579 | solo_set_time(solo_dev); | |
580 | } | |
581 | ||
582 | /* Disable watchdog */ | |
583 | solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); | |
584 | ||
585 | /* Initialize sub components */ | |
586 | ||
15300c1d PS |
587 | ret = solo_p2m_init(solo_dev); |
588 | if (ret) | |
faa4fd2a BC |
589 | goto fail_probe; |
590 | ||
15300c1d PS |
591 | ret = solo_disp_init(solo_dev); |
592 | if (ret) | |
faa4fd2a BC |
593 | goto fail_probe; |
594 | ||
15300c1d PS |
595 | ret = solo_gpio_init(solo_dev); |
596 | if (ret) | |
faa4fd2a BC |
597 | goto fail_probe; |
598 | ||
15300c1d PS |
599 | ret = solo_tw28_init(solo_dev); |
600 | if (ret) | |
faa4fd2a BC |
601 | goto fail_probe; |
602 | ||
dcae5dac | 603 | ret = solo_v4l2_init(solo_dev, video_nr); |
15300c1d | 604 | if (ret) |
faa4fd2a BC |
605 | goto fail_probe; |
606 | ||
15300c1d PS |
607 | ret = solo_enc_init(solo_dev); |
608 | if (ret) | |
faa4fd2a BC |
609 | goto fail_probe; |
610 | ||
dcae5dac | 611 | ret = solo_enc_v4l2_init(solo_dev, video_nr); |
15300c1d | 612 | if (ret) |
faa4fd2a BC |
613 | goto fail_probe; |
614 | ||
15300c1d PS |
615 | ret = solo_g723_init(solo_dev); |
616 | if (ret) | |
faa4fd2a BC |
617 | goto fail_probe; |
618 | ||
dcae5dac HV |
619 | ret = solo_sysfs_init(solo_dev); |
620 | if (ret) | |
621 | goto fail_probe; | |
622 | ||
623 | /* Now that init is over, set this lower */ | |
624 | solo_dev->p2m_jiffies = msecs_to_jiffies(20); | |
625 | ||
faa4fd2a BC |
626 | return 0; |
627 | ||
628 | fail_probe: | |
629 | free_solo_dev(solo_dev); | |
630 | return ret; | |
631 | } | |
632 | ||
1c979395 | 633 | static void solo_pci_remove(struct pci_dev *pdev) |
faa4fd2a | 634 | { |
d9ebd623 HV |
635 | struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); |
636 | struct solo_dev *solo_dev = container_of(v4l2_dev, struct solo_dev, v4l2_dev); | |
faa4fd2a BC |
637 | |
638 | free_solo_dev(solo_dev); | |
639 | } | |
640 | ||
41e043fc | 641 | static const struct pci_device_id solo_id_table[] = { |
f62de9be | 642 | /* 6010 based cards */ |
dcae5dac HV |
643 | { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010), |
644 | .driver_data = SOLO_DEV_6010 }, | |
645 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4), | |
646 | .driver_data = SOLO_DEV_6010 }, | |
647 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9), | |
648 | .driver_data = SOLO_DEV_6010 }, | |
649 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16), | |
650 | .driver_data = SOLO_DEV_6010 }, | |
651 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4), | |
652 | .driver_data = SOLO_DEV_6010 }, | |
653 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9), | |
654 | .driver_data = SOLO_DEV_6010 }, | |
655 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16), | |
656 | .driver_data = SOLO_DEV_6010 }, | |
f62de9be | 657 | /* 6110 based cards */ |
dcae5dac HV |
658 | { PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), |
659 | .driver_data = SOLO_DEV_6110 }, | |
660 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4), | |
661 | .driver_data = SOLO_DEV_6110 }, | |
662 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8), | |
663 | .driver_data = SOLO_DEV_6110 }, | |
664 | { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16), | |
665 | .driver_data = SOLO_DEV_6110 }, | |
faa4fd2a BC |
666 | {0,} |
667 | }; | |
668 | ||
decebabf | 669 | MODULE_DEVICE_TABLE(pci, solo_id_table); |
faa4fd2a | 670 | |
decebabf KH |
671 | static struct pci_driver solo_pci_driver = { |
672 | .name = SOLO6X10_NAME, | |
673 | .id_table = solo_id_table, | |
674 | .probe = solo_pci_probe, | |
675 | .remove = solo_pci_remove, | |
faa4fd2a BC |
676 | }; |
677 | ||
b1f093ac | 678 | module_pci_driver(solo_pci_driver); |