• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/staging/solo6x10/
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
29MODULE_DESCRIPTION("Softlogic 6010 MP4 Encoder/Decoder V4L2/ALSA Driver");
30MODULE_AUTHOR("Ben Collins <bcollins@bluecherry.net>");
31MODULE_VERSION(SOLO6010_VERSION);
32MODULE_LICENSE("GPL");
33
34void 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
40void 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
46static irqreturn_t solo6010_isr(int irq, void *data)
47{
48	struct solo6010_dev *solo_dev = data;
49	u32 status;
50	int i;
51
52	status = solo_reg_read(solo_dev, SOLO_IRQ_STAT);
53	if (!status)
54		return IRQ_NONE;
55
56	if (status & ~solo_dev->irq_mask) {
57		solo_reg_write(solo_dev, SOLO_IRQ_STAT,
58			       status & ~solo_dev->irq_mask);
59		status &= solo_dev->irq_mask;
60	}
61
62	if (status & SOLO_IRQ_PCI_ERR) {
63		u32 err = solo_reg_read(solo_dev, SOLO_PCI_ERR);
64		solo_p2m_error_isr(solo_dev, err);
65		solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_PCI_ERR);
66	}
67
68	for (i = 0; i < SOLO_NR_P2M; i++)
69		if (status & SOLO_IRQ_P2M(i))
70			solo_p2m_isr(solo_dev, i);
71
72	if (status & SOLO_IRQ_IIC)
73		solo_i2c_isr(solo_dev);
74
75	if (status & SOLO_IRQ_VIDEO_IN)
76		solo_video_in_isr(solo_dev);
77
78	/* Call this first so enc gets detected flag set */
79	if (status & SOLO_IRQ_MOTION)
80		solo_motion_isr(solo_dev);
81
82	if (status & SOLO_IRQ_ENCODER)
83		solo_enc_v4l2_isr(solo_dev);
84
85	if (status & SOLO_IRQ_G723)
86		solo_g723_isr(solo_dev);
87
88	return IRQ_HANDLED;
89}
90
91static void free_solo_dev(struct solo6010_dev *solo_dev)
92{
93	struct pci_dev *pdev;
94
95	if (!solo_dev)
96		return;
97
98	pdev = solo_dev->pdev;
99
100	/* If we never initialized the PCI device, then nothing else
101	 * below here needs cleanup */
102	if (!pdev) {
103		kfree(solo_dev);
104		return;
105	}
106
107	/* Bring down the sub-devices first */
108	solo_g723_exit(solo_dev);
109	solo_enc_v4l2_exit(solo_dev);
110	solo_enc_exit(solo_dev);
111	solo_v4l2_exit(solo_dev);
112	solo_disp_exit(solo_dev);
113	solo_gpio_exit(solo_dev);
114	solo_p2m_exit(solo_dev);
115	solo_i2c_exit(solo_dev);
116
117	/* Now cleanup the PCI device */
118	if (solo_dev->reg_base) {
119		solo6010_irq_off(solo_dev, ~0);
120		pci_iounmap(pdev, solo_dev->reg_base);
121		free_irq(pdev->irq, solo_dev);
122	}
123
124	pci_release_regions(pdev);
125	pci_disable_device(pdev);
126	pci_set_drvdata(pdev, NULL);
127
128	kfree(solo_dev);
129}
130
131static int __devinit solo6010_pci_probe(struct pci_dev *pdev,
132					const struct pci_device_id *id)
133{
134	struct solo6010_dev *solo_dev;
135	int ret;
136	int sdram;
137	u8 chip_id;
138
139	if ((solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL)) == NULL)
140		return -ENOMEM;
141
142	solo_dev->pdev = pdev;
143	spin_lock_init(&solo_dev->reg_io_lock);
144	pci_set_drvdata(pdev, solo_dev);
145
146	if ((ret = pci_enable_device(pdev)))
147		goto fail_probe;
148
149	pci_set_master(pdev);
150
151	if ((ret = pci_request_regions(pdev, SOLO6010_NAME)))
152		goto fail_probe;
153
154	if ((solo_dev->reg_base = pci_ioremap_bar(pdev, 0)) == NULL) {
155		ret = -ENOMEM;
156		goto fail_probe;
157	}
158
159	chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) &
160					SOLO_CHIP_ID_MASK;
161	switch (chip_id) {
162		case 7:
163			solo_dev->nr_chans = 16;
164			solo_dev->nr_ext = 5;
165			break;
166		case 6:
167			solo_dev->nr_chans = 8;
168			solo_dev->nr_ext = 2;
169			break;
170		default:
171			dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, "
172				 "defaulting to 4 channels\n",
173				 chip_id);
174		case 5:
175			solo_dev->nr_chans = 4;
176			solo_dev->nr_ext = 1;
177	}
178
179	/* Disable all interrupts to start */
180	solo6010_irq_off(solo_dev, ~0);
181
182	/* Initial global settings */
183	solo_reg_write(solo_dev, SOLO_SYS_CFG, SOLO_SYS_CFG_SDRAM64BIT |
184		       SOLO_SYS_CFG_INPUTDIV(25) |
185		       SOLO_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) |
186		       SOLO_SYS_CFG_OUTDIV(3));
187	solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1);
188
189	/* PLL locking time of 1ms */
190	mdelay(1);
191
192	ret = request_irq(pdev->irq, solo6010_isr, IRQF_SHARED, SOLO6010_NAME,
193			  solo_dev);
194	if (ret)
195		goto fail_probe;
196
197	/* Handle this from the start */
198	solo6010_irq_on(solo_dev, SOLO_IRQ_PCI_ERR);
199
200	if ((ret = solo_i2c_init(solo_dev)))
201		goto fail_probe;
202
203	/* Setup the DMA engine */
204	sdram = (solo_dev->nr_chans >= 8) ? 2 : 1;
205	solo_reg_write(solo_dev, SOLO_DMA_CTRL,
206		       SOLO_DMA_CTRL_REFRESH_CYCLE(1) |
207		       SOLO_DMA_CTRL_SDRAM_SIZE(sdram) |
208		       SOLO_DMA_CTRL_SDRAM_CLK_INVERT |
209		       SOLO_DMA_CTRL_READ_CLK_SELECT |
210		       SOLO_DMA_CTRL_LATENCY(1));
211
212	if ((ret = solo_p2m_init(solo_dev)))
213		goto fail_probe;
214
215	if ((ret = solo_disp_init(solo_dev)))
216		goto fail_probe;
217
218	if ((ret = solo_gpio_init(solo_dev)))
219		goto fail_probe;
220
221	if ((ret = solo_tw28_init(solo_dev)))
222		goto fail_probe;
223
224	if ((ret = solo_v4l2_init(solo_dev)))
225		goto fail_probe;
226
227	if ((ret = solo_enc_init(solo_dev)))
228		goto fail_probe;
229
230	if ((ret = solo_enc_v4l2_init(solo_dev)))
231		goto fail_probe;
232
233	if ((ret = solo_g723_init(solo_dev)))
234		goto fail_probe;
235
236	return 0;
237
238fail_probe:
239	free_solo_dev(solo_dev);
240	return ret;
241}
242
243static void __devexit solo6010_pci_remove(struct pci_dev *pdev)
244{
245	struct solo6010_dev *solo_dev = pci_get_drvdata(pdev);
246
247	free_solo_dev(solo_dev);
248}
249
250static struct pci_device_id solo6010_id_table[] = {
251	{PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)},
252	{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4)},
253	{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)},
254	{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)},
255	{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_4)},
256	{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_9)},
257	{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_16)},
258	{0,}
259};
260
261MODULE_DEVICE_TABLE(pci, solo6010_id_table);
262
263static struct pci_driver solo6010_pci_driver = {
264	.name = SOLO6010_NAME,
265	.id_table = solo6010_id_table,
266	.probe = solo6010_pci_probe,
267	.remove = solo6010_pci_remove,
268};
269
270static int __init solo6010_module_init(void)
271{
272	return pci_register_driver(&solo6010_pci_driver);
273}
274
275static void __exit solo6010_module_exit(void)
276{
277	pci_unregister_driver(&solo6010_pci_driver);
278}
279
280module_init(solo6010_module_init);
281module_exit(solo6010_module_exit);
282