xref: /haiku/src/add-ons/kernel/drivers/dvb/cx23882/dvb_interface.c (revision b8ded2f89783a220c7b3019d48266a682cc79158)
1083a11a3SMarcus Overhagen /*
2083a11a3SMarcus Overhagen  * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
3083a11a3SMarcus Overhagen  *
4083a11a3SMarcus Overhagen  * Permission is hereby granted, free of charge, to any person
5083a11a3SMarcus Overhagen  * obtaining a copy of this software and associated documentation
6083a11a3SMarcus Overhagen  * files (the "Software"), to deal in the Software without restriction,
7083a11a3SMarcus Overhagen  * including without limitation the rights to use, copy, modify,
8083a11a3SMarcus Overhagen  * merge, publish, distribute, sublicense, and/or sell copies of
9083a11a3SMarcus Overhagen  * the Software, and to permit persons to whom the Software is
10083a11a3SMarcus Overhagen  * furnished to do so, subject to the following conditions:
11083a11a3SMarcus Overhagen  *
12083a11a3SMarcus Overhagen  * The above copyright notice and this permission notice shall be
13083a11a3SMarcus Overhagen  * included in all copies or substantial portions of the Software.
14083a11a3SMarcus Overhagen  *
15083a11a3SMarcus Overhagen  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16083a11a3SMarcus Overhagen  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17083a11a3SMarcus Overhagen  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18083a11a3SMarcus Overhagen  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19083a11a3SMarcus Overhagen  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20083a11a3SMarcus Overhagen  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21083a11a3SMarcus Overhagen  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22083a11a3SMarcus Overhagen  * OTHER DEALINGS IN THE SOFTWARE.
23083a11a3SMarcus Overhagen  */
24083a11a3SMarcus Overhagen 
25083a11a3SMarcus Overhagen #include <KernelExport.h>
26083a11a3SMarcus Overhagen #include <stdlib.h>
271caca357SMarcus Overhagen #include <string.h>
281caca357SMarcus Overhagen #include "dvb_interface.h"
29083a11a3SMarcus Overhagen #include "cx23882.h"
301caca357SMarcus Overhagen #include "cx23882_i2c.h"
31083a11a3SMarcus Overhagen #include "cx22702.h"
32083a11a3SMarcus Overhagen #include "dtt7592.h"
33083a11a3SMarcus Overhagen #include "driver.h"
34083a11a3SMarcus Overhagen #include "config.h"
35083a11a3SMarcus Overhagen #include "util.h"
36083a11a3SMarcus Overhagen 
37083a11a3SMarcus Overhagen #define TRACE_INTERFACE
38083a11a3SMarcus Overhagen #ifdef TRACE_INTERFACE
39083a11a3SMarcus Overhagen   #define TRACE dprintf
40083a11a3SMarcus Overhagen #else
41083a11a3SMarcus Overhagen   #define TRACE(a...)
42083a11a3SMarcus Overhagen #endif
43083a11a3SMarcus Overhagen 
44083a11a3SMarcus Overhagen 
451caca357SMarcus Overhagen #if 0
46083a11a3SMarcus Overhagen static void
47083a11a3SMarcus Overhagen dump_eeprom(cx23882_device *device)
48083a11a3SMarcus Overhagen {
49083a11a3SMarcus Overhagen 	uint8 d[256+8];
50083a11a3SMarcus Overhagen 	uint8 adr;
51083a11a3SMarcus Overhagen 	uint8 *p;
52083a11a3SMarcus Overhagen 	int i;
53083a11a3SMarcus Overhagen 	status_t res;
54083a11a3SMarcus Overhagen 
55083a11a3SMarcus Overhagen 	adr = 0;
56083a11a3SMarcus Overhagen 	res = i2c_xfer(device->i2c_bus, I2C_ADDR_EEPROM, &adr, 1, d, sizeof(d));
57083a11a3SMarcus Overhagen 	if (res != B_OK) {
58083a11a3SMarcus Overhagen 		TRACE("i2c_read failed: %08lx\n", res);
59083a11a3SMarcus Overhagen 		return;
60083a11a3SMarcus Overhagen 	}
61083a11a3SMarcus Overhagen 	for (p = d, i = 0; i < ((int)sizeof(d) / 8); i++, p+= 8)
62083a11a3SMarcus 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]);
63083a11a3SMarcus Overhagen 
64083a11a3SMarcus Overhagen }
651caca357SMarcus Overhagen #endif
66083a11a3SMarcus Overhagen 
67083a11a3SMarcus Overhagen 
68083a11a3SMarcus Overhagen status_t
interface_attach(void ** cookie,const pci_info * info)69083a11a3SMarcus Overhagen interface_attach(void **cookie, const pci_info *info)
70083a11a3SMarcus Overhagen {
71083a11a3SMarcus Overhagen 	cx23882_device *device;
72083a11a3SMarcus Overhagen 	uint32 val;
73083a11a3SMarcus Overhagen 	int i;
74083a11a3SMarcus Overhagen 
75083a11a3SMarcus Overhagen 	TRACE("interface_attach\n");
76083a11a3SMarcus Overhagen 
77083a11a3SMarcus Overhagen 	device = malloc(sizeof(cx23882_device));
78083a11a3SMarcus Overhagen 	if (!device)
79083a11a3SMarcus Overhagen 		return B_NO_MEMORY;
80083a11a3SMarcus Overhagen 	*cookie = device;
81083a11a3SMarcus Overhagen 
82083a11a3SMarcus Overhagen 	// initialize cookie
83083a11a3SMarcus Overhagen 	memset(device, 0, sizeof(*device));
84083a11a3SMarcus Overhagen 	device->regs_area = -1;
85083a11a3SMarcus Overhagen 	device->dma_buf1_area = -1;
86083a11a3SMarcus Overhagen 	device->dma_buf2_area = -1;
87083a11a3SMarcus Overhagen 	device->capture_sem = -1;
88083a11a3SMarcus Overhagen 
89083a11a3SMarcus Overhagen 	device->pci_info = info;
90083a11a3SMarcus Overhagen 
91083a11a3SMarcus Overhagen 	// enable busmaster and memory mapped access, disable io port access
92083a11a3SMarcus Overhagen 	val = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_command, 2);
93083a11a3SMarcus Overhagen 	val = PCI_PCICMD_BME | PCI_PCICMD_MSE | (val & ~PCI_PCICMD_IOS);
94083a11a3SMarcus Overhagen 	gPci->write_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_command, 2, val);
95083a11a3SMarcus Overhagen 
96083a11a3SMarcus Overhagen 	// adjust PCI latency timer
97083a11a3SMarcus Overhagen 	val = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_latency, 1);
98*6849e7ccSJérôme Duval 	TRACE("PCI latency is %02" B_PRIx32 ", changing to %02x\n", val,
99*6849e7ccSJérôme Duval 		PCI_LATENCY);
100083a11a3SMarcus Overhagen 	gPci->write_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_latency, 1, PCI_LATENCY);
101083a11a3SMarcus Overhagen 
102083a11a3SMarcus Overhagen 	// get IRQ
103083a11a3SMarcus Overhagen 	device->irq = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, PCI_interrupt_line, 1);
104083a11a3SMarcus Overhagen 	if (device->irq == 0 || device->irq == 0xff) {
105083a11a3SMarcus Overhagen 		dprintf("cx23882: Error, no IRQ assigned\n");
106083a11a3SMarcus Overhagen 		goto err;
107083a11a3SMarcus Overhagen 	}
108083a11a3SMarcus Overhagen 	TRACE("IRQ %d\n", device->irq);
109083a11a3SMarcus Overhagen 
110083a11a3SMarcus Overhagen 	// map registers into memory
111083a11a3SMarcus Overhagen 	val = gPci->read_pci_config(device->pci_info->bus, device->pci_info->device, device->pci_info->function, 0x10, 4);
112083a11a3SMarcus Overhagen 	val &= PCI_address_memory_32_mask;
113083a11a3SMarcus Overhagen 	if (val == 0) {
114083a11a3SMarcus Overhagen 		dprintf("cx23882: Error, no memory space assigned\n");
115083a11a3SMarcus Overhagen 		goto err;
116083a11a3SMarcus Overhagen 	}
117*6849e7ccSJérôme Duval 	TRACE("hardware register address 0x%" B_PRIx32 "\n", val);
118*6849e7ccSJérôme Duval 	device->regs_area = map_mem(&device->regs, (addr_t)val,
119*6849e7ccSJérôme Duval 		16777216 /* 16 MB */, 0, "cx23882 registers");
120083a11a3SMarcus Overhagen 	if (device->regs_area < B_OK) {
121083a11a3SMarcus Overhagen 		dprintf("cx23882: Error, can't map hardware registers\n");
122083a11a3SMarcus Overhagen 		goto err;
123083a11a3SMarcus Overhagen 	}
124083a11a3SMarcus Overhagen 	TRACE("mapped registers to %p\n", device->regs);
125083a11a3SMarcus Overhagen 
126083a11a3SMarcus Overhagen 	device->capture_sem = create_sem(0, "cx23882 capture");
127083a11a3SMarcus Overhagen 
128083a11a3SMarcus Overhagen 	cx23882_reset(device);
129083a11a3SMarcus Overhagen 
130083a11a3SMarcus Overhagen 	if (i2c_init(device) < B_OK) {
131083a11a3SMarcus Overhagen 		dprintf("cx23882: Error, can't init I2C\n");
132083a11a3SMarcus Overhagen 	}
133083a11a3SMarcus Overhagen 
134083a11a3SMarcus Overhagen 
135083a11a3SMarcus Overhagen 	if (cx23882_init(device) < B_OK) {
136083a11a3SMarcus Overhagen 		dprintf("cx23882: Error, can't init hardware\n");
137083a11a3SMarcus Overhagen 	}
138083a11a3SMarcus Overhagen 
139083a11a3SMarcus Overhagen 
140083a11a3SMarcus Overhagen 	for (i = 0; i < 20; i++)
141083a11a3SMarcus Overhagen 		if (cx22702_init(device->i2c_bus) == B_OK)
142083a11a3SMarcus Overhagen 			break;
143083a11a3SMarcus Overhagen 	if (i == 20) {
144083a11a3SMarcus Overhagen 		TRACE("cx22702_init failed\n");
145083a11a3SMarcus Overhagen 		goto err;
146083a11a3SMarcus Overhagen 	}
147083a11a3SMarcus Overhagen 
148083a11a3SMarcus Overhagen 	// setup interrupt handler
149083a11a3SMarcus Overhagen 	if (install_io_interrupt_handler(device->irq, cx23882_int, device, 0) < B_OK) {
150083a11a3SMarcus Overhagen 		dprintf("cx23882: Error, can't install interrupt handler\n");
151083a11a3SMarcus Overhagen 		goto err;
152083a11a3SMarcus Overhagen 	}
153083a11a3SMarcus Overhagen 
154083a11a3SMarcus Overhagen //	dump_eeprom(device);
155083a11a3SMarcus Overhagen //	dtt7582_test(device->i2c_bus);
156083a11a3SMarcus Overhagen 
157083a11a3SMarcus Overhagen 	return B_OK;
158083a11a3SMarcus Overhagen err:
159083a11a3SMarcus Overhagen 	free(cookie);
160083a11a3SMarcus Overhagen 	return B_ERROR;
161083a11a3SMarcus Overhagen }
162083a11a3SMarcus Overhagen 
163083a11a3SMarcus Overhagen 
164083a11a3SMarcus Overhagen void
interface_detach(void * cookie)165083a11a3SMarcus Overhagen interface_detach(void *cookie)
166083a11a3SMarcus Overhagen {
167083a11a3SMarcus Overhagen 	cx23882_device *device = cookie;
168083a11a3SMarcus Overhagen 
169083a11a3SMarcus Overhagen 	i2c_terminate(device);
170083a11a3SMarcus Overhagen 
171083a11a3SMarcus Overhagen 	if (cx23882_terminate(device) < B_OK) {
172083a11a3SMarcus Overhagen 	}
173083a11a3SMarcus Overhagen 
174083a11a3SMarcus Overhagen   	remove_io_interrupt_handler(device->irq, cx23882_int, device);
175083a11a3SMarcus Overhagen 
176083a11a3SMarcus Overhagen 	delete_area(device->regs_area);
177083a11a3SMarcus Overhagen 
178083a11a3SMarcus Overhagen 	delete_sem(device->capture_sem);
179083a11a3SMarcus Overhagen 
180083a11a3SMarcus Overhagen 	TRACE("interface_detach\n");
181083a11a3SMarcus Overhagen }
182083a11a3SMarcus Overhagen 
183083a11a3SMarcus Overhagen 
184083a11a3SMarcus Overhagen static void
interface_get_interface_info(dvb_interface_info_t * info)185083a11a3SMarcus Overhagen interface_get_interface_info(dvb_interface_info_t *info)
186083a11a3SMarcus Overhagen {
187083a11a3SMarcus Overhagen 	memset(info, 0, sizeof(*info));
188083a11a3SMarcus Overhagen 	info->version = 1;
189083a11a3SMarcus Overhagen 	info->flags = 0;
190083a11a3SMarcus Overhagen 	info->type = DVB_TYPE_DVB_T;
191083a11a3SMarcus Overhagen 	strcpy(info->name, "CX23882");
192083a11a3SMarcus Overhagen 	strcpy(info->info, "Hauppauge WinTV-NOVA-T model 928 driver, Copyright (c) 2005 Marcus Overhagen");
193083a11a3SMarcus Overhagen }
194083a11a3SMarcus Overhagen 
195083a11a3SMarcus Overhagen 
196083a11a3SMarcus Overhagen status_t
interface_ioctl(void * cookie,uint32 op,void * arg,size_t len)197083a11a3SMarcus Overhagen interface_ioctl(void *cookie, uint32 op, void *arg, size_t len)
198083a11a3SMarcus Overhagen {
199083a11a3SMarcus Overhagen 	cx23882_device *device = cookie;
200083a11a3SMarcus Overhagen 	status_t res;
201083a11a3SMarcus Overhagen 
202083a11a3SMarcus Overhagen 	switch (op) {
203083a11a3SMarcus Overhagen 		case DVB_GET_INTERFACE_INFO:
204083a11a3SMarcus Overhagen 		{
205083a11a3SMarcus Overhagen 			dvb_interface_info_t info;
206083a11a3SMarcus Overhagen 			interface_get_interface_info(&info);
207083a11a3SMarcus Overhagen 			if (user_memcpy(arg, &info, sizeof(info)) < B_OK)
208083a11a3SMarcus Overhagen 				return B_BAD_ADDRESS;
209083a11a3SMarcus Overhagen 			break;
210083a11a3SMarcus Overhagen 		}
211083a11a3SMarcus Overhagen 
212083a11a3SMarcus Overhagen 		case DVB_GET_FREQUENCY_INFO:
213083a11a3SMarcus Overhagen 		{
214083a11a3SMarcus Overhagen 			dvb_frequency_info_t info;
215083a11a3SMarcus Overhagen 			if ((res = cx22702_get_frequency_info(device->i2c_bus, &info)) < B_OK)
216083a11a3SMarcus Overhagen 				return res;
217083a11a3SMarcus Overhagen 			if (user_memcpy(arg, &info, sizeof(info)) < B_OK)
218083a11a3SMarcus Overhagen 				return B_BAD_ADDRESS;
219083a11a3SMarcus Overhagen 			break;
220083a11a3SMarcus Overhagen 		}
221083a11a3SMarcus Overhagen 
222083a11a3SMarcus Overhagen 		case DVB_START_CAPTURE:
223083a11a3SMarcus Overhagen 		{
224083a11a3SMarcus Overhagen 			return cx23882_start_capture(device);
225083a11a3SMarcus Overhagen 		}
226083a11a3SMarcus Overhagen 
227083a11a3SMarcus Overhagen 		case DVB_STOP_CAPTURE:
228083a11a3SMarcus Overhagen 		{
229083a11a3SMarcus Overhagen 			return cx23882_stop_capture(device);
230083a11a3SMarcus Overhagen 		}
231083a11a3SMarcus Overhagen 
232083a11a3SMarcus Overhagen 		case DVB_SET_TUNING_PARAMETERS:
233083a11a3SMarcus Overhagen 		{
234083a11a3SMarcus Overhagen 			dvb_tuning_parameters_t params;
235083a11a3SMarcus Overhagen 			if (user_memcpy(&params, arg, sizeof(params)) < B_OK)
236083a11a3SMarcus Overhagen 				return B_BAD_ADDRESS;
237083a11a3SMarcus Overhagen 			if ((res = cx22702_set_tuning_parameters(device->i2c_bus, &params.u.dvb_t)) < B_OK)
238083a11a3SMarcus Overhagen 				return res;
239083a11a3SMarcus Overhagen 			break;
240083a11a3SMarcus Overhagen 		}
241083a11a3SMarcus Overhagen 
242083a11a3SMarcus Overhagen 		case DVB_GET_TUNING_PARAMETERS:
243083a11a3SMarcus Overhagen 		{
244083a11a3SMarcus Overhagen 			dvb_tuning_parameters_t params;
245083a11a3SMarcus Overhagen 			if ((res = cx22702_get_tuning_parameters(device->i2c_bus, &params.u.dvb_t)) < B_OK)
246083a11a3SMarcus Overhagen 				return res;
247083a11a3SMarcus Overhagen 			if (user_memcpy(arg, &params, sizeof(params)) < B_OK)
248083a11a3SMarcus Overhagen 				return B_BAD_ADDRESS;
249083a11a3SMarcus Overhagen 			break;
250083a11a3SMarcus Overhagen 		}
251083a11a3SMarcus Overhagen 
252083a11a3SMarcus Overhagen 		case DVB_GET_STATUS:
253083a11a3SMarcus Overhagen 		{
254083a11a3SMarcus Overhagen 			dvb_status_t status;
255083a11a3SMarcus Overhagen 			if ((res = cx22702_get_status(device->i2c_bus, &status)) < B_OK)
256083a11a3SMarcus Overhagen 				return res;
257083a11a3SMarcus Overhagen 			if (user_memcpy(arg, &status, sizeof(status)) < B_OK)
258083a11a3SMarcus Overhagen 				return B_BAD_ADDRESS;
259083a11a3SMarcus Overhagen 			break;
260083a11a3SMarcus Overhagen 		}
261083a11a3SMarcus Overhagen 
262083a11a3SMarcus Overhagen 		case DVB_GET_SS:
263083a11a3SMarcus Overhagen 		{
264083a11a3SMarcus Overhagen 			uint32 value;
265083a11a3SMarcus Overhagen 			if ((res = cx22702_get_ss(device->i2c_bus, &value)) < B_OK)
266083a11a3SMarcus Overhagen 				return res;
267083a11a3SMarcus Overhagen 			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
268083a11a3SMarcus Overhagen 				return B_BAD_ADDRESS;
269083a11a3SMarcus Overhagen 			break;
270083a11a3SMarcus Overhagen 		}
271083a11a3SMarcus Overhagen 
272083a11a3SMarcus Overhagen 		case DVB_GET_BER:
273083a11a3SMarcus Overhagen 		{
274083a11a3SMarcus Overhagen 			uint32 value;
275083a11a3SMarcus Overhagen 			if ((res = cx22702_get_ber(device->i2c_bus, &value)) < B_OK)
276083a11a3SMarcus Overhagen 				return res;
277083a11a3SMarcus Overhagen 			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
278083a11a3SMarcus Overhagen 				return B_BAD_ADDRESS;
279083a11a3SMarcus Overhagen 			break;
280083a11a3SMarcus Overhagen 		}
281083a11a3SMarcus Overhagen 
282083a11a3SMarcus Overhagen 		case DVB_GET_SNR:
283083a11a3SMarcus Overhagen 		{
284083a11a3SMarcus Overhagen 			uint32 value;
285083a11a3SMarcus Overhagen 			if ((res = cx22702_get_snr(device->i2c_bus, &value)) < B_OK)
286083a11a3SMarcus Overhagen 				return res;
287083a11a3SMarcus Overhagen 			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
288083a11a3SMarcus Overhagen 				return B_BAD_ADDRESS;
289083a11a3SMarcus Overhagen 			break;
290083a11a3SMarcus Overhagen 		}
291083a11a3SMarcus Overhagen 
292083a11a3SMarcus Overhagen 		case DVB_GET_UPC:
293083a11a3SMarcus Overhagen 		{
294083a11a3SMarcus Overhagen 			uint32 value;
295083a11a3SMarcus Overhagen 			if ((res = cx22702_get_upc(device->i2c_bus, &value)) < B_OK)
296083a11a3SMarcus Overhagen 				return res;
297083a11a3SMarcus Overhagen 			if (user_memcpy(arg, &value, sizeof(value)) < B_OK)
298083a11a3SMarcus Overhagen 				return B_BAD_ADDRESS;
299083a11a3SMarcus Overhagen 			break;
300083a11a3SMarcus Overhagen 		}
301083a11a3SMarcus Overhagen 
302083a11a3SMarcus Overhagen 		case DVB_CAPTURE:
303083a11a3SMarcus Overhagen 		{
304083a11a3SMarcus Overhagen 			dvb_capture_t cap_data;
305083a11a3SMarcus Overhagen 			// wait for data ready interrupt, with 100 ms timeout (in case tuning failed, bad reception, etc)
306083a11a3SMarcus Overhagen 			if ((res = acquire_sem_etc(device->capture_sem, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, 100000)) < B_OK)
307083a11a3SMarcus Overhagen 				return res;
308083a11a3SMarcus Overhagen 			cap_data.data = device->capture_data;
309083a11a3SMarcus Overhagen 			cap_data.size = device->capture_size;
310083a11a3SMarcus Overhagen 			cap_data.end_time = device->capture_end_time;
311083a11a3SMarcus Overhagen 			if (user_memcpy(arg, &cap_data, sizeof(cap_data)) < B_OK)
312083a11a3SMarcus Overhagen 				return B_BAD_ADDRESS;
313083a11a3SMarcus Overhagen 			break;
314083a11a3SMarcus Overhagen 		}
315083a11a3SMarcus Overhagen 
316083a11a3SMarcus Overhagen 		default:
317083a11a3SMarcus Overhagen 		{
318083a11a3SMarcus Overhagen 			TRACE("interface_ioctl\n");
319083a11a3SMarcus Overhagen 			return B_BAD_VALUE;
320083a11a3SMarcus Overhagen 		}
321083a11a3SMarcus Overhagen 	}
322083a11a3SMarcus Overhagen 
323083a11a3SMarcus Overhagen 	return B_OK;
324083a11a3SMarcus Overhagen }
325