📄 budget-av.c
字号:
/* * budget-av.c: driver for the SAA7146 based Budget DVB cards * with analog video in * * Compiled from various sources by Michael Hunold <michael@mihu.de> * * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> & * Andrew de Quincey <adq_dvb@lidskialf.net> * * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> * * Copyright (C) 1999-2002 Ralph Metzler * & Marcus Metzler for convergence integrated media GmbH * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Or, point your browser to http://www.gnu.org/copyleft/gpl.html * * * the project's page is at http://www.linuxtv.org/dvb/ */#include "budget.h"#include "stv0299.h"#include "tda1002x.h"#include "tda1004x.h"#include "tua6100.h"#include "dvb-pll.h"#include <media/saa7146_vv.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/input.h>#include <linux/spinlock.h>#include "dvb_ca_en50221.h"#define DEBICICAM 0x02420000#define SLOTSTATUS_NONE 1#define SLOTSTATUS_PRESENT 2#define SLOTSTATUS_RESET 4#define SLOTSTATUS_READY 8#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);struct budget_av { struct budget budget; struct video_device *vd; int cur_input; int has_saa7113; struct tasklet_struct ciintf_irq_tasklet; int slot_status; struct dvb_ca_en50221 ca; u8 reinitialise_demod:1;};static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot);/* GPIO Connections: * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*! * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory * 2 - CI Card Enable (Active Low) * 3 - CI Card Detect *//**************************************************************************** * INITIALIZATION ****************************************************************************/static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg){ u8 mm1[] = { 0x00 }; u8 mm2[] = { 0x00 }; struct i2c_msg msgs[2]; msgs[0].flags = 0; msgs[1].flags = I2C_M_RD; msgs[0].addr = msgs[1].addr = id / 2; mm1[0] = reg; msgs[0].len = 1; msgs[1].len = 1; msgs[0].buf = mm1; msgs[1].buf = mm2; i2c_transfer(i2c, msgs, 2); return mm2[0];}static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len){ u8 mm1[] = { reg }; struct i2c_msg msgs[2] = { {.addr = id / 2,.flags = 0,.buf = mm1,.len = 1}, {.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len} }; if (i2c_transfer(i2c, msgs, 2) != 2) return -EIO; return 0;}static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val){ u8 msg[2] = { reg, val }; struct i2c_msg msgs; msgs.flags = 0; msgs.addr = id / 2; msgs.len = 2; msgs.buf = msg; return i2c_transfer(i2c, &msgs, 1);}static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address){ struct budget_av *budget_av = (struct budget_av *) ca->data; int result; if (slot != 0) return -EINVAL; saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); udelay(1); result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1); if (result == -ETIMEDOUT) { ciintf_slot_shutdown(ca, slot); printk(KERN_INFO "budget-av: cam ejected 1\n"); } return result;}static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value){ struct budget_av *budget_av = (struct budget_av *) ca->data; int result; if (slot != 0) return -EINVAL; saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); udelay(1); result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1); if (result == -ETIMEDOUT) { ciintf_slot_shutdown(ca, slot); printk(KERN_INFO "budget-av: cam ejected 2\n"); } return result;}static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address){ struct budget_av *budget_av = (struct budget_av *) ca->data; int result; if (slot != 0) return -EINVAL; saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); udelay(1); result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0); if (result == -ETIMEDOUT) { ciintf_slot_shutdown(ca, slot); printk(KERN_INFO "budget-av: cam ejected 3\n"); return -ETIMEDOUT; } return result;}static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value){ struct budget_av *budget_av = (struct budget_av *) ca->data; int result; if (slot != 0) return -EINVAL; saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); udelay(1); result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0); if (result == -ETIMEDOUT) { ciintf_slot_shutdown(ca, slot); printk(KERN_INFO "budget-av: cam ejected 5\n"); } return result;}static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot){ struct budget_av *budget_av = (struct budget_av *) ca->data; struct saa7146_dev *saa = budget_av->budget.dev; if (slot != 0) return -EINVAL; dprintk(1, "ciintf_slot_reset\n"); budget_av->slot_status = SLOTSTATUS_RESET; saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); /* Vcc off */ msleep(2); saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); /* Vcc on */ msleep(20); /* 20 ms Vcc settling time */ saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); msleep(20); /* reinitialise the frontend if necessary */ if (budget_av->reinitialise_demod) dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); return 0;}static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot){ struct budget_av *budget_av = (struct budget_av *) ca->data; struct saa7146_dev *saa = budget_av->budget.dev; if (slot != 0) return -EINVAL; dprintk(1, "ciintf_slot_shutdown\n"); ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); budget_av->slot_status = SLOTSTATUS_NONE; return 0;}static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot){ struct budget_av *budget_av = (struct budget_av *) ca->data; struct saa7146_dev *saa = budget_av->budget.dev; if (slot != 0) return -EINVAL; dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status); ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); return 0;}static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open){ struct budget_av *budget_av = (struct budget_av *) ca->data; struct saa7146_dev *saa = budget_av->budget.dev; int result; if (slot != 0) return -EINVAL; /* test the card detect line - needs to be done carefully * since it never goes high for some CAMs on this interface (e.g. topuptv) */ if (budget_av->slot_status == SLOTSTATUS_NONE) { saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); udelay(1); if (saa7146_read(saa, PSR) & MASK_06) { if (budget_av->slot_status == SLOTSTATUS_NONE) { budget_av->slot_status = SLOTSTATUS_PRESENT; printk(KERN_INFO "budget-av: cam inserted A\n"); } } saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); } /* We also try and read from IO memory to work round the above detection bug. If * there is no CAM, we will get a timeout. Only done if there is no cam * present, since this test actually breaks some cams :( * * if the CI interface is not open, we also do the above test since we * don't care if the cam has problems - we'll be resetting it on open() anyway */ if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) { saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1); if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) { budget_av->slot_status = SLOTSTATUS_PRESENT; printk(KERN_INFO "budget-av: cam inserted B\n"); } else if (result < 0) { if (budget_av->slot_status != SLOTSTATUS_NONE) { ciintf_slot_shutdown(ca, slot); printk(KERN_INFO "budget-av: cam ejected 5\n"); return 0; } } } /* read from attribute memory in reset/ready state to know when the CAM is ready */ if (budget_av->slot_status == SLOTSTATUS_RESET) { result = ciintf_read_attribute_mem(ca, slot, 0); if (result == 0x1d) { budget_av->slot_status = SLOTSTATUS_READY; } } /* work out correct return code */ if (budget_av->slot_status != SLOTSTATUS_NONE) { if (budget_av->slot_status & SLOTSTATUS_READY) { return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; } return DVB_CA_EN50221_POLL_CAM_PRESENT; } return 0;}static int ciintf_init(struct budget_av *budget_av){ struct saa7146_dev *saa = budget_av->budget.dev; int result; memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221)); saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); /* Enable DEBI pins */ saa7146_write(saa, MC1, MASK_27 | MASK_11); /* register CI interface */ budget_av->ca.owner = THIS_MODULE; budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem; budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem; budget_av->ca.read_cam_control = ciintf_read_cam_control; budget_av->ca.write_cam_control = ciintf_write_cam_control; budget_av->ca.slot_reset = ciintf_slot_reset; budget_av->ca.slot_shutdown = ciintf_slot_shutdown; budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable; budget_av->ca.poll_slot_status = ciintf_poll_slot_status; budget_av->ca.data = budget_av; budget_av->budget.ci_present = 1; budget_av->slot_status = SLOTSTATUS_NONE; if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter, &budget_av->ca, 0, 1)) != 0) { printk(KERN_ERR "budget-av: ci initialisation failed.\n"); goto error; } printk(KERN_INFO "budget-av: ci interface initialised.\n"); return 0;error: saa7146_write(saa, MC1, MASK_27); return result;}static void ciintf_deinit(struct budget_av *budget_av){ struct saa7146_dev *saa = budget_av->budget.dev; saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); /* release the CA device */ dvb_ca_en50221_release(&budget_av->ca); /* disable DEBI pins */ saa7146_write(saa, MC1, MASK_27);}static const u8 saa7113_tab[] = { 0x01, 0x08, 0x02, 0xc0, 0x03, 0x33, 0x04, 0x00, 0x05, 0x00, 0x06, 0xeb, 0x07, 0xe0, 0x08, 0x28, 0x09, 0x00, 0x0a, 0x80, 0x0b, 0x47, 0x0c, 0x40, 0x0d, 0x00, 0x0e, 0x01, 0x0f, 0x44, 0x10, 0x08, 0x11, 0x0c, 0x12, 0x7b, 0x13, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0x57, 0xff, 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07, 0x5b, 0x83, 0x5e, 0x00, 0xff};static int saa7113_init(struct budget_av *budget_av){ struct budget *budget = &budget_av->budget; struct saa7146_dev *saa = budget->dev; const u8 *data = saa7113_tab; saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); msleep(200); if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) { dprintk(1, "saa7113 not found on KNC card\n"); return -ENODEV; } dprintk(1, "saa7113 detected and initializing\n"); while (*data != 0xff) { i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1)); data += 2; } dprintk(1, "saa7113 status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f)); return 0;}static int saa7113_setinput(struct budget_av *budget_av, int input){ struct budget *budget = &budget_av->budget;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -