xref: /haiku/src/add-ons/kernel/drivers/dvb/cx23882/dvb_interface.c (revision c90684742e7361651849be4116d0e5de3a817194)
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
46 static void
47 dump_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 
68 status_t
69 interface_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 %02lx, changing to %02x\n", val, PCI_LATENCY);
99 	gPci->write_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_latency, 1, PCI_LATENCY);
100 
101 	// get IRQ
102 	device->irq = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_interrupt_line, 1);
103 	if (device->irq == 0 || device->irq == 0xff) {
104 		dprintf("cx23882: Error, no IRQ assigned\n");
105 		goto err;
106 	}
107 	TRACE("IRQ %d\n", device->irq);
108 
109 	// map registers into memory
110 	val = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, 0x10, 4);
111 	val &= PCI_address_memory_32_mask;
112 	if (val == 0) {
113 		dprintf("cx23882: Error, no memory space assigned\n");
114 		goto err;
115 	}
116 	TRACE("hardware register address %p\n", (void *) val);
117 	device->regs_area = map_mem(&device->regs, (void *)val, 16777216 /* 16 MB */, 0, "cx23882 registers");
118 	if (device->regs_area < B_OK) {
119 		dprintf("cx23882: Error, can't map hardware registers\n");
120 		goto err;
121 	}
122 	TRACE("mapped registers to %p\n", device->regs);
123 
124 	device->capture_sem = create_sem(0, "cx23882 capture");
125 
126 	cx23882_reset(device);
127 
128 	if (i2c_init(device) < B_OK) {
129 		dprintf("cx23882: Error, can't init I2C\n");
130 	}
131 
132 
133 	if (cx23882_init(device) < B_OK) {
134 		dprintf("cx23882: Error, can't init hardware\n");
135 	}
136 
137 
138 	for (i = 0; i < 20; i++)
139 		if (cx22702_init(device->i2c_bus) == B_OK)
140 			break;
141 	if (i == 20) {
142 		TRACE("cx22702_init failed\n");
143 		goto err;
144 	}
145 
146 	// setup interrupt handler
147 	if (install_io_interrupt_handler(device->irq, cx23882_int, device, 0) < B_OK) {
148 		dprintf("cx23882: Error, can't install interrupt handler\n");
149 		goto err;
150 	}
151 
152 //	dump_eeprom(device);
153 //	dtt7582_test(device->i2c_bus);
154 
155 	return B_OK;
156 err:
157 	free(cookie);
158 	return B_ERROR;
159 }
160 
161 
162 void
163 interface_detach(void *cookie)
164 {
165 	cx23882_device *device = cookie;
166 
167 	i2c_terminate(device);
168 
169 	if (cx23882_terminate(device) < B_OK) {
170 	}
171 
172   	remove_io_interrupt_handler(device->irq, cx23882_int, device);
173 
174 	delete_area(device->regs_area);
175 
176 	delete_sem(device->capture_sem);
177 
178 	TRACE("interface_detach\n");
179 }
180 
181 
182 static void
183 interface_get_interface_info(dvb_interface_info_t *info)
184 {
185 	memset(info, 0, sizeof(*info));
186 	info->version = 1;
187 	info->flags = 0;
188 	info->type = DVB_TYPE_DVB_T;
189 	strcpy(info->name, "CX23882");
190 	strcpy(info->info, "Hauppauge WinTV-NOVA-T model 928 driver, Copyright (c) 2005 Marcus Overhagen");
191 }
192 
193 
194 status_t
195 interface_ioctl(void *cookie, uint32 op, void *arg, size_t len)
196 {
197 	cx23882_device *device = cookie;
198 	status_t res;
199 
200 	switch (op) {
201 		case DVB_GET_INTERFACE_INFO:
202 		{
203 			dvb_interface_info_t info;
204 			interface_get_interface_info(&info);
205 			if (user_memcpy(arg, &info, sizeof(info)) < B_OK)
206 				return B_BAD_ADDRESS;
207 			break;
208 		}
209 
210 		case DVB_GET_FREQUENCY_INFO:
211 		{
212 			dvb_frequency_info_t info;
213 			if ((res = cx22702_get_frequency_info(device->i2c_bus, &info)) < B_OK)
214 				return res;
215 			if (user_memcpy(arg, &info, sizeof(info)) < B_OK)
216 				return B_BAD_ADDRESS;
217 			break;
218 		}
219 
220 		case DVB_START_CAPTURE:
221 		{
222 			return cx23882_start_capture(device);
223 		}
224 
225 		case DVB_STOP_CAPTURE:
226 		{
227 			return cx23882_stop_capture(device);
228 		}
229 
230 		case DVB_SET_TUNING_PARAMETERS:
231 		{
232 			dvb_tuning_parameters_t params;
233 			if (user_memcpy(&params, arg, sizeof(params)) < B_OK)
234 				return B_BAD_ADDRESS;
235 			if ((res = cx22702_set_tuning_parameters(device->i2c_bus, &params.u.dvb_t)) < B_OK)
236 				return res;
237 			break;
238 		}
239 
240 		case DVB_GET_TUNING_PARAMETERS:
241 		{
242 			dvb_tuning_parameters_t params;
243 			if ((res = cx22702_get_tuning_parameters(device->i2c_bus, &params.u.dvb_t)) < B_OK)
244 				return res;
245 			if (user_memcpy(arg, &params, sizeof(params)) < B_OK)
246 				return B_BAD_ADDRESS;
247 			break;
248 		}
249 
250 		case DVB_GET_STATUS:
251 		{
252 			dvb_status_t status;
253 			if ((res = cx22702_get_status(device->i2c_bus, &status)) < B_OK)
254 				return res;
255 			if (user_memcpy(arg, &status, sizeof(status)) < B_OK)
256 				return B_BAD_ADDRESS;
257 			break;
258 		}
259 
260 		case DVB_GET_SS:
261 		{
262 			uint32 value;
263 			if ((res = cx22702_get_ss(device->i2c_bus, &value)) < B_OK)
264 				return res;
265 			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
266 				return B_BAD_ADDRESS;
267 			break;
268 		}
269 
270 		case DVB_GET_BER:
271 		{
272 			uint32 value;
273 			if ((res = cx22702_get_ber(device->i2c_bus, &value)) < B_OK)
274 				return res;
275 			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
276 				return B_BAD_ADDRESS;
277 			break;
278 		}
279 
280 		case DVB_GET_SNR:
281 		{
282 			uint32 value;
283 			if ((res = cx22702_get_snr(device->i2c_bus, &value)) < B_OK)
284 				return res;
285 			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
286 				return B_BAD_ADDRESS;
287 			break;
288 		}
289 
290 		case DVB_GET_UPC:
291 		{
292 			uint32 value;
293 			if ((res = cx22702_get_upc(device->i2c_bus, &value)) < B_OK)
294 				return res;
295 			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
296 				return B_BAD_ADDRESS;
297 			break;
298 		}
299 
300 		case DVB_CAPTURE:
301 		{
302 			dvb_capture_t cap_data;
303 			// wait for data ready interrupt, with 100 ms timeout (in case tuning failed, bad reception, etc)
304 			if ((res = acquire_sem_etc(device->capture_sem, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, 100000)) < B_OK)
305 				return res;
306 			cap_data.data = device->capture_data;
307 			cap_data.size = device->capture_size;
308 			cap_data.end_time = device->capture_end_time;
309 			if (user_memcpy(arg, &cap_data, sizeof(cap_data)) < B_OK)
310 				return B_BAD_ADDRESS;
311 			break;
312 		}
313 
314 		default:
315 		{
316 			TRACE("interface_ioctl\n");
317 			return B_BAD_VALUE;
318 		}
319 	}
320 
321 	return B_OK;
322 }
323