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