1/*
2 * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify,
8 * merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include <KernelExport.h>
26#include <stdlib.h>
27#include <string.h>
28#include "dvb_interface.h"
29#include "cx23882.h"
30#include "cx23882_i2c.h"
31#include "cx22702.h"
32#include "dtt7592.h"
33#include "driver.h"
34#include "config.h"
35#include "util.h"
36
37#define TRACE_INTERFACE
38#ifdef TRACE_INTERFACE
39  #define TRACE dprintf
40#else
41  #define TRACE(a...)
42#endif
43
44
45#if 0
46static void
47dump_eeprom(cx23882_device *device)
48{
49	uint8 d[256+8];
50	uint8 adr;
51	uint8 *p;
52	int i;
53	status_t res;
54
55	adr = 0;
56	res = i2c_xfer(device->i2c_bus, I2C_ADDR_EEPROM, &adr, 1, d, sizeof(d));
57	if (res != B_OK) {
58		TRACE("i2c_read failed: %08lx\n", res);
59		return;
60	}
61	for (p = d, i = 0; i < ((int)sizeof(d) / 8); i++, p+= 8)
62		TRACE("EEPROM %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n", i * 8, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
63
64}
65#endif
66
67
68status_t
69interface_attach(void **cookie, const pci_info *info)
70{
71	cx23882_device *device;
72	uint32 val;
73	int i;
74
75	TRACE("interface_attach\n");
76
77	device = malloc(sizeof(cx23882_device));
78	if (!device)
79		return B_NO_MEMORY;
80	*cookie = device;
81
82	// initialize cookie
83	memset(device, 0, sizeof(*device));
84	device->regs_area = -1;
85	device->dma_buf1_area = -1;
86	device->dma_buf2_area = -1;
87	device->capture_sem = -1;
88
89	device->pci_info = info;
90
91	// enable busmaster and memory mapped access, disable io port access
92	val = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_command, 2);
93	val = PCI_PCICMD_BME | PCI_PCICMD_MSE | (val & ~PCI_PCICMD_IOS);
94	gPci->write_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_command, 2, val);
95
96	// adjust PCI latency timer
97	val = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_latency, 1);
98	TRACE("PCI latency is %02" B_PRIx32 ", changing to %02x\n", val,
99		PCI_LATENCY);
100	gPci->write_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_latency, 1, PCI_LATENCY);
101
102	// get IRQ
103	device->irq = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_interrupt_line, 1);
104	if (device->irq == 0 || device->irq == 0xff) {
105		dprintf("cx23882: Error, no IRQ assigned\n");
106		goto err;
107	}
108	TRACE("IRQ %d\n", device->irq);
109
110	// map registers into memory
111	val = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, 0x10, 4);
112	val &= PCI_address_memory_32_mask;
113	if (val == 0) {
114		dprintf("cx23882: Error, no memory space assigned\n");
115		goto err;
116	}
117	TRACE("hardware register address 0x%" B_PRIx32 "\n", val);
118	device->regs_area = map_mem(&device->regs, (addr_t)val,
119		16777216 /* 16 MB */, 0, "cx23882 registers");
120	if (device->regs_area < B_OK) {
121		dprintf("cx23882: Error, can't map hardware registers\n");
122		goto err;
123	}
124	TRACE("mapped registers to %p\n", device->regs);
125
126	device->capture_sem = create_sem(0, "cx23882 capture");
127
128	cx23882_reset(device);
129
130	if (i2c_init(device) < B_OK) {
131		dprintf("cx23882: Error, can't init I2C\n");
132	}
133
134
135	if (cx23882_init(device) < B_OK) {
136		dprintf("cx23882: Error, can't init hardware\n");
137	}
138
139
140	for (i = 0; i < 20; i++)
141		if (cx22702_init(device->i2c_bus) == B_OK)
142			break;
143	if (i == 20) {
144		TRACE("cx22702_init failed\n");
145		goto err;
146	}
147
148	// setup interrupt handler
149	if (install_io_interrupt_handler(device->irq, cx23882_int, device, 0) < B_OK) {
150		dprintf("cx23882: Error, can't install interrupt handler\n");
151		goto err;
152	}
153
154//	dump_eeprom(device);
155//	dtt7582_test(device->i2c_bus);
156
157	return B_OK;
158err:
159	free(cookie);
160	return B_ERROR;
161}
162
163
164void
165interface_detach(void *cookie)
166{
167	cx23882_device *device = cookie;
168
169	i2c_terminate(device);
170
171	if (cx23882_terminate(device) < B_OK) {
172	}
173
174  	remove_io_interrupt_handler(device->irq, cx23882_int, device);
175
176	delete_area(device->regs_area);
177
178	delete_sem(device->capture_sem);
179
180	TRACE("interface_detach\n");
181}
182
183
184static void
185interface_get_interface_info(dvb_interface_info_t *info)
186{
187	memset(info, 0, sizeof(*info));
188	info->version = 1;
189	info->flags = 0;
190	info->type = DVB_TYPE_DVB_T;
191	strcpy(info->name, "CX23882");
192	strcpy(info->info, "Hauppauge WinTV-NOVA-T model 928 driver, Copyright (c) 2005 Marcus Overhagen");
193}
194
195
196status_t
197interface_ioctl(void *cookie, uint32 op, void *arg, size_t len)
198{
199	cx23882_device *device = cookie;
200	status_t res;
201
202	switch (op) {
203		case DVB_GET_INTERFACE_INFO:
204		{
205			dvb_interface_info_t info;
206			interface_get_interface_info(&info);
207			if (user_memcpy(arg, &info, sizeof(info)) < B_OK)
208				return B_BAD_ADDRESS;
209			break;
210		}
211
212		case DVB_GET_FREQUENCY_INFO:
213		{
214			dvb_frequency_info_t info;
215			if ((res = cx22702_get_frequency_info(device->i2c_bus, &info)) < B_OK)
216				return res;
217			if (user_memcpy(arg, &info, sizeof(info)) < B_OK)
218				return B_BAD_ADDRESS;
219			break;
220		}
221
222		case DVB_START_CAPTURE:
223		{
224			return cx23882_start_capture(device);
225		}
226
227		case DVB_STOP_CAPTURE:
228		{
229			return cx23882_stop_capture(device);
230		}
231
232		case DVB_SET_TUNING_PARAMETERS:
233		{
234			dvb_tuning_parameters_t params;
235			if (user_memcpy(&params, arg, sizeof(params)) < B_OK)
236				return B_BAD_ADDRESS;
237			if ((res = cx22702_set_tuning_parameters(device->i2c_bus, &params.u.dvb_t)) < B_OK)
238				return res;
239			break;
240		}
241
242		case DVB_GET_TUNING_PARAMETERS:
243		{
244			dvb_tuning_parameters_t params;
245			if ((res = cx22702_get_tuning_parameters(device->i2c_bus, &params.u.dvb_t)) < B_OK)
246				return res;
247			if (user_memcpy(arg, &params, sizeof(params)) < B_OK)
248				return B_BAD_ADDRESS;
249			break;
250		}
251
252		case DVB_GET_STATUS:
253		{
254			dvb_status_t status;
255			if ((res = cx22702_get_status(device->i2c_bus, &status)) < B_OK)
256				return res;
257			if (user_memcpy(arg, &status, sizeof(status)) < B_OK)
258				return B_BAD_ADDRESS;
259			break;
260		}
261
262		case DVB_GET_SS:
263		{
264			uint32 value;
265			if ((res = cx22702_get_ss(device->i2c_bus, &value)) < B_OK)
266				return res;
267			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
268				return B_BAD_ADDRESS;
269			break;
270		}
271
272		case DVB_GET_BER:
273		{
274			uint32 value;
275			if ((res = cx22702_get_ber(device->i2c_bus, &value)) < B_OK)
276				return res;
277			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
278				return B_BAD_ADDRESS;
279			break;
280		}
281
282		case DVB_GET_SNR:
283		{
284			uint32 value;
285			if ((res = cx22702_get_snr(device->i2c_bus, &value)) < B_OK)
286				return res;
287			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
288				return B_BAD_ADDRESS;
289			break;
290		}
291
292		case DVB_GET_UPC:
293		{
294			uint32 value;
295			if ((res = cx22702_get_upc(device->i2c_bus, &value)) < B_OK)
296				return res;
297			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
298				return B_BAD_ADDRESS;
299			break;
300		}
301
302		case DVB_CAPTURE:
303		{
304			dvb_capture_t cap_data;
305			// wait for data ready interrupt, with 100 ms timeout (in case tuning failed, bad reception, etc)
306			if ((res = acquire_sem_etc(device->capture_sem, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, 100000)) < B_OK)
307				return res;
308			cap_data.data = device->capture_data;
309			cap_data.size = device->capture_size;
310			cap_data.end_time = device->capture_end_time;
311			if (user_memcpy(arg, &cap_data, sizeof(cap_data)) < B_OK)
312				return B_BAD_ADDRESS;
313			break;
314		}
315
316		default:
317		{
318			TRACE("interface_ioctl\n");
319			return B_BAD_VALUE;
320		}
321	}
322
323	return B_OK;
324}
325