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 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 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 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 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(¶ms, arg, sizeof(params)) < B_OK) 236083a11a3SMarcus Overhagen return B_BAD_ADDRESS; 237083a11a3SMarcus Overhagen if ((res = cx22702_set_tuning_parameters(device->i2c_bus, ¶ms.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, ¶ms.u.dvb_t)) < B_OK) 246083a11a3SMarcus Overhagen return res; 247083a11a3SMarcus Overhagen if (user_memcpy(arg, ¶ms, 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