Commit | Line | Data |
---|---|---|
faa4fd2a BC |
1 | /* |
2 | * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com | |
3 | * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | */ | |
19 | ||
20 | #include <linux/kernel.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/pci.h> | |
23 | #include <linux/interrupt.h> | |
24 | #include <linux/videodev2.h> | |
25 | ||
26 | #include "solo6010.h" | |
27 | #include "solo6010-tw28.h" | |
28 | ||
29 | MODULE_DESCRIPTION("Softlogic 6010 MP4 Encoder/Decoder V4L2/ALSA Driver"); | |
30 | MODULE_AUTHOR("Ben Collins <bcollins@bluecherry.net>"); | |
31 | MODULE_VERSION(SOLO6010_VERSION); | |
32 | MODULE_LICENSE("GPL"); | |
33 | ||
34 | void solo6010_irq_on(struct solo6010_dev *solo_dev, u32 mask) | |
35 | { | |
36 | solo_dev->irq_mask |= mask; | |
37 | solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); | |
38 | } | |
39 | ||
40 | void solo6010_irq_off(struct solo6010_dev *solo_dev, u32 mask) | |
41 | { | |
42 | solo_dev->irq_mask &= ~mask; | |
43 | solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); | |
44 | } | |
45 | ||
46 | /* XXX We should check the return value of the sub-device ISR's */ | |
47 | static irqreturn_t solo6010_isr(int irq, void *data) | |
48 | { | |
49 | struct solo6010_dev *solo_dev = data; | |
50 | u32 status; | |
51 | int i; | |
52 | ||
53 | status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); | |
54 | if (!status) | |
55 | return IRQ_NONE; | |
56 | ||
57 | if (status & ~solo_dev->irq_mask) { | |
58 | solo_reg_write(solo_dev, SOLO_IRQ_STAT, | |
59 | status & ~solo_dev->irq_mask); | |
60 | status &= solo_dev->irq_mask; | |
61 | } | |
62 | ||
63 | if (status & SOLO_IRQ_PCI_ERR) { | |
64 | u32 err = solo_reg_read(solo_dev, SOLO_PCI_ERR); | |
65 | solo_p2m_error_isr(solo_dev, err); | |
66 | solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_PCI_ERR); | |
67 | } | |
68 | ||
69 | for (i = 0; i < SOLO_NR_P2M; i++) | |
70 | if (status & SOLO_IRQ_P2M(i)) | |
71 | solo_p2m_isr(solo_dev, i); | |
72 | ||
73 | if (status & SOLO_IRQ_IIC) | |
74 | solo_i2c_isr(solo_dev); | |
75 | ||
76 | if (status & SOLO_IRQ_VIDEO_IN) | |
77 | solo_video_in_isr(solo_dev); | |
78 | ||
79 | /* Call this first so enc gets detected flag set */ | |
80 | if (status & SOLO_IRQ_MOTION) | |
81 | solo_motion_isr(solo_dev); | |
82 | ||
83 | if (status & SOLO_IRQ_ENCODER) | |
84 | solo_enc_v4l2_isr(solo_dev); | |
85 | ||
86 | if (status & SOLO_IRQ_G723) | |
87 | solo_g723_isr(solo_dev); | |
88 | ||
89 | return IRQ_HANDLED; | |
90 | } | |
91 | ||
92 | static void free_solo_dev(struct solo6010_dev *solo_dev) | |
93 | { | |
94 | struct pci_dev *pdev; | |
95 | ||
96 | if (!solo_dev) | |
97 | return; | |
98 | ||
99 | pdev = solo_dev->pdev; | |
100 | ||
101 | /* If we never initialized the PCI device, then nothing else | |
102 | * below here needs cleanup */ | |
103 | if (!pdev) { | |
104 | kfree(solo_dev); | |
105 | return; | |
106 | } | |
107 | ||
108 | /* Bring down the sub-devices first */ | |
109 | solo_g723_exit(solo_dev); | |
110 | solo_enc_v4l2_exit(solo_dev); | |
111 | solo_enc_exit(solo_dev); | |
112 | solo_v4l2_exit(solo_dev); | |
113 | solo_disp_exit(solo_dev); | |
114 | solo_gpio_exit(solo_dev); | |
115 | solo_p2m_exit(solo_dev); | |
116 | solo_i2c_exit(solo_dev); | |
117 | ||
118 | /* Now cleanup the PCI device */ | |
119 | if (solo_dev->reg_base) { | |
120 | solo6010_irq_off(solo_dev, ~0); | |
121 | pci_iounmap(pdev, solo_dev->reg_base); | |
122 | free_irq(pdev->irq, solo_dev); | |
123 | } | |
124 | ||
125 | pci_release_regions(pdev); | |
126 | pci_disable_device(pdev); | |
127 | pci_set_drvdata(pdev, NULL); | |
128 | ||
129 | kfree(solo_dev); | |
130 | } | |
131 | ||
132 | static int __devinit solo6010_pci_probe(struct pci_dev *pdev, | |
133 | const struct pci_device_id *id) | |
134 | { | |
135 | struct solo6010_dev *solo_dev; | |
136 | int ret; | |
137 | int sdram; | |
138 | u8 chip_id; | |
15300c1d PS |
139 | solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); |
140 | if (solo_dev == NULL) | |
faa4fd2a BC |
141 | return -ENOMEM; |
142 | ||
143 | solo_dev->pdev = pdev; | |
144 | spin_lock_init(&solo_dev->reg_io_lock); | |
145 | pci_set_drvdata(pdev, solo_dev); | |
146 | ||
15300c1d PS |
147 | ret = pci_enable_device(pdev); |
148 | if (ret) | |
faa4fd2a BC |
149 | goto fail_probe; |
150 | ||
151 | pci_set_master(pdev); | |
152 | ||
15300c1d PS |
153 | ret = pci_request_regions(pdev, SOLO6010_NAME); |
154 | if (ret) | |
faa4fd2a BC |
155 | goto fail_probe; |
156 | ||
15300c1d PS |
157 | solo_dev->reg_base = pci_ioremap_bar(pdev, 0); |
158 | if (solo_dev->reg_base == NULL) { | |
faa4fd2a BC |
159 | ret = -ENOMEM; |
160 | goto fail_probe; | |
161 | } | |
162 | ||
163 | chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & | |
164 | SOLO_CHIP_ID_MASK; | |
165 | switch (chip_id) { | |
166 | case 7: | |
167 | solo_dev->nr_chans = 16; | |
168 | solo_dev->nr_ext = 5; | |
169 | break; | |
170 | case 6: | |
171 | solo_dev->nr_chans = 8; | |
172 | solo_dev->nr_ext = 2; | |
173 | break; | |
174 | default: | |
175 | dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, " | |
176 | "defaulting to 4 channels\n", | |
177 | chip_id); | |
178 | case 5: | |
179 | solo_dev->nr_chans = 4; | |
180 | solo_dev->nr_ext = 1; | |
181 | } | |
182 | ||
183 | /* Disable all interrupts to start */ | |
184 | solo6010_irq_off(solo_dev, ~0); | |
185 | ||
186 | /* Initial global settings */ | |
187 | solo_reg_write(solo_dev, SOLO_SYS_CFG, SOLO_SYS_CFG_SDRAM64BIT | | |
188 | SOLO_SYS_CFG_INPUTDIV(25) | | |
189 | SOLO_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) | | |
190 | SOLO_SYS_CFG_OUTDIV(3)); | |
191 | solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1); | |
192 | ||
193 | /* PLL locking time of 1ms */ | |
194 | mdelay(1); | |
195 | ||
196 | ret = request_irq(pdev->irq, solo6010_isr, IRQF_SHARED, SOLO6010_NAME, | |
197 | solo_dev); | |
198 | if (ret) | |
199 | goto fail_probe; | |
200 | ||
201 | /* Handle this from the start */ | |
202 | solo6010_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); | |
203 | ||
15300c1d PS |
204 | ret = solo_i2c_init(solo_dev); |
205 | if (ret) | |
faa4fd2a BC |
206 | goto fail_probe; |
207 | ||
208 | /* Setup the DMA engine */ | |
209 | sdram = (solo_dev->nr_chans >= 8) ? 2 : 1; | |
210 | solo_reg_write(solo_dev, SOLO_DMA_CTRL, | |
211 | SOLO_DMA_CTRL_REFRESH_CYCLE(1) | | |
212 | SOLO_DMA_CTRL_SDRAM_SIZE(sdram) | | |
213 | SOLO_DMA_CTRL_SDRAM_CLK_INVERT | | |
214 | SOLO_DMA_CTRL_READ_CLK_SELECT | | |
215 | SOLO_DMA_CTRL_LATENCY(1)); | |
216 | ||
15300c1d PS |
217 | ret = solo_p2m_init(solo_dev); |
218 | if (ret) | |
faa4fd2a BC |
219 | goto fail_probe; |
220 | ||
15300c1d PS |
221 | ret = solo_disp_init(solo_dev); |
222 | if (ret) | |
faa4fd2a BC |
223 | goto fail_probe; |
224 | ||
15300c1d PS |
225 | ret = solo_gpio_init(solo_dev); |
226 | if (ret) | |
faa4fd2a BC |
227 | goto fail_probe; |
228 | ||
15300c1d PS |
229 | ret = solo_tw28_init(solo_dev); |
230 | if (ret) | |
faa4fd2a BC |
231 | goto fail_probe; |
232 | ||
15300c1d PS |
233 | ret = solo_v4l2_init(solo_dev); |
234 | if (ret) | |
faa4fd2a BC |
235 | goto fail_probe; |
236 | ||
15300c1d PS |
237 | ret = solo_enc_init(solo_dev); |
238 | if (ret) | |
faa4fd2a BC |
239 | goto fail_probe; |
240 | ||
15300c1d PS |
241 | ret = solo_enc_v4l2_init(solo_dev); |
242 | if (ret) | |
faa4fd2a BC |
243 | goto fail_probe; |
244 | ||
15300c1d PS |
245 | ret = solo_g723_init(solo_dev); |
246 | if (ret) | |
faa4fd2a BC |
247 | goto fail_probe; |
248 | ||
249 | return 0; | |
250 | ||
251 | fail_probe: | |
252 | free_solo_dev(solo_dev); | |
253 | return ret; | |
254 | } | |
255 | ||
256 | static void __devexit solo6010_pci_remove(struct pci_dev *pdev) | |
257 | { | |
258 | struct solo6010_dev *solo_dev = pci_get_drvdata(pdev); | |
259 | ||
260 | free_solo_dev(solo_dev); | |
261 | } | |
262 | ||
263 | static struct pci_device_id solo6010_id_table[] = { | |
264 | {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)}, | |
265 | {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4)}, | |
266 | {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)}, | |
267 | {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)}, | |
268 | {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_4)}, | |
269 | {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_9)}, | |
270 | {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_16)}, | |
271 | {0,} | |
272 | }; | |
273 | ||
274 | MODULE_DEVICE_TABLE(pci, solo6010_id_table); | |
275 | ||
276 | static struct pci_driver solo6010_pci_driver = { | |
277 | .name = SOLO6010_NAME, | |
278 | .id_table = solo6010_id_table, | |
279 | .probe = solo6010_pci_probe, | |
280 | .remove = solo6010_pci_remove, | |
281 | }; | |
282 | ||
283 | static int __init solo6010_module_init(void) | |
284 | { | |
285 | return pci_register_driver(&solo6010_pci_driver); | |
286 | } | |
287 | ||
288 | static void __exit solo6010_module_exit(void) | |
289 | { | |
290 | pci_unregister_driver(&solo6010_pci_driver); | |
291 | } | |
292 | ||
293 | module_init(solo6010_module_init); | |
294 | module_exit(solo6010_module_exit); |