Commit 5740f4e7 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

[media] tw68: add original tw68 code

This tw68 driver has been out-of-tree for many years on gitorious:
https://gitorious.org/tw68/tw68-v2.

This copies that code to the kernel as a record of that original code.

Note that William Brack's email address in these sources is no longer
valid and I have not been able to contact him. However, all the code is
standard GPL.
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <m.chehab@samsung.com>
parent 89fffac8
/*
* device driver for Techwell 68xx based cards
*
* Much of this code is derived from the cx88 and sa7134 drivers, which
* were in turn derived from the bt87x driver. The original work was by
* Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
* Hans Verkuil, Andy Walls and many others. Their work is gratefully
* acknowledged. Full credit goes to them - any problems within this code
* are mine.
*
* Copyright (C) 2009 William M. Brack <wbrack@mmm.com.hk>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h> /* must appear before i2c-algo-bit.h */
#include <linux/i2c-algo-bit.h>
#include <media/v4l2-common.h>
#include <media/tveeprom.h>
#include "tw68.h"
#include "tw68-reg.h"
/* commly used strings */
#if 0
static char name_mute[] = "mute";
static char name_radio[] = "Radio";
static char name_tv[] = "Television";
static char name_tv_mono[] = "TV (mono only)";
static char name_svideo[] = "S-Video";
static char name_comp[] = "Composite";
#endif
static char name_comp1[] = "Composite1";
static char name_comp2[] = "Composite2";
static char name_comp3[] = "Composite3";
static char name_comp4[] = "Composite4";
/* ------------------------------------------------------------------ */
/* board config info */
/* If radio_type !=UNSET, radio_addr should be specified
*/
struct tw68_board tw68_boards[] = {
[TW68_BOARD_UNKNOWN] = {
.name = "GENERIC",
.tuner_type = TUNER_ABSENT,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.inputs = {
{
.name = name_comp1,
.vmux = 0,
}, {
.name = name_comp2,
.vmux = 1,
}, {
.name = name_comp3,
.vmux = 2,
}, {
.name = name_comp4,
.vmux = 3,
}, { /* Must have a NULL entry at end of list */
.name = NULL,
.vmux = 0,
}
},
},
};
const unsigned int tw68_bcount = ARRAY_SIZE(tw68_boards);
/*
* Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps
* the PCI ID database up to date. Note that the entries must be
* added under vendor 0x1797 (Techwell Inc.) as subsystem IDs.
*/
struct pci_device_id tw68_pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_TECHWELL,
.device = PCI_DEVICE_ID_6800,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = TW68_BOARD_UNKNOWN,
}, {
.vendor = PCI_VENDOR_ID_TECHWELL,
.device = PCI_DEVICE_ID_6801,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = TW68_BOARD_UNKNOWN,
}, {
.vendor = PCI_VENDOR_ID_TECHWELL,
.device = PCI_DEVICE_ID_6804,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = TW68_BOARD_UNKNOWN,
}, {
.vendor = PCI_VENDOR_ID_TECHWELL,
.device = PCI_DEVICE_ID_6816_1,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = TW68_BOARD_UNKNOWN,
}, {
.vendor = PCI_VENDOR_ID_TECHWELL,
.device = PCI_DEVICE_ID_6816_2,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = TW68_BOARD_UNKNOWN,
}, {
.vendor = PCI_VENDOR_ID_TECHWELL,
.device = PCI_DEVICE_ID_6816_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = TW68_BOARD_UNKNOWN,
}, {
.vendor = PCI_VENDOR_ID_TECHWELL,
.device = PCI_DEVICE_ID_6816_4,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = TW68_BOARD_UNKNOWN,
}, {
/* end of list */
}
};
MODULE_DEVICE_TABLE(pci, tw68_pci_tbl);
/* ------------------------------------------------------------ */
/* stuff done before i2c enabled */
int tw68_board_init1(struct tw68_dev *dev)
{
/* Clear GPIO outputs */
tw_writel(TW68_GPOE, 0);
/* Remainder of setup according to board ID */
switch (dev->board) {
case TW68_BOARD_UNKNOWN:
printk(KERN_INFO "%s: Unable to determine board type, "
"using generic values\n", dev->name);
break;
}
dev->input = dev->hw_input = &card_in(dev,0);
return 0;
}
int tw68_tuner_setup(struct tw68_dev *dev)
{
return 0;
}
/* stuff which needs working i2c */
int tw68_board_init2(struct tw68_dev *dev)
{
return 0;
}
This diff is collapsed.
/*
* tw68 code to handle the i2c interface.
*
* Much of this code is derived from the bt87x driver. The original
* work was by Gerd Knorr; more recently the code was enhanced by Mauro
* Carvalho Chehab. Their work is gratefully acknowledged. Full credit
* goes to them - any problems within this code are mine.
*
* Copyright (C) 2009 William M. Brack <wbrack@mmm.com.hk>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include "tw68.h"
#include <media/v4l2-common.h>
#include <linux/i2c-algo-bit.h>
/*----------------------------------------------------------------*/
static unsigned int i2c_debug;
module_param(i2c_debug, int, 0644);
MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
#if 0
static unsigned int i2c_scan;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
#endif
#define d1printk if (1 == i2c_debug) printk
#define I2C_CLOCK 0xa6 /* 99.4 kHz */
/*----------------------------------------------------------------------*/
/* Although the TW68xx i2c controller has a "hardware" mode, where all of
* the low-level i2c/smbbus is handled by the chip, it appears that mode
* is not suitable for linux i2c handling routines because extended "bursts"
* of data (sequences of bytes without intervening START/STOP bits) are
* not possible. Instead, we put the chip into "software" mode, and handle
* the i2c bus at a low level. To accomplish this, we use the routines
* from the i2c modules.
*
* Because the particular boards which I had for testing did not have any
* devices attached to the i2c bus, I have been unable to test these
* routines.
*/
/*----------------------------------------------------------------------*/
/* I2C functions - "bit-banging" adapter (software i2c) */
/* tw68_bit_setcl
* Handles "toggling" the i2c clock bit
*/
static void tw68_bit_setscl(void *data, int state)
{
struct tw68_dev *dev = data;
tw_andorb(TW68_SBUSC, (state ? 1 : 0) << TW68_SSCLK, TW68_SSCLK_B);
}
/* tw68_bit_setsda
* Handles "toggling" the i2c data bit
*/
static void tw68_bit_setsda(void *data, int state)
{
struct tw68_dev *dev = data;
tw_andorb(TW68_SBUSC, (state ? 1 : 0) << TW68_SSDAT, TW68_SSDAT_B);
}
/* tw68_bit_getscl
*
* Returns the current state of the clock bit
*/
static int tw68_bit_getscl(void *data)
{
struct tw68_dev *dev = data;
return (tw_readb(TW68_SBUSC) & TW68_SSCLK_B) ? 1 : 0;
}
/* tw68_bit_getsda
*
* Returns the current state of the data bit
*/
static int tw68_bit_getsda(void *data)
{
struct tw68_dev *dev = data;
return (tw_readb(TW68_SBUSC) & TW68_SSDAT_B) ? 1 : 0;
}
static struct i2c_algo_bit_data __devinitdata tw68_i2c_algo_bit_template = {
.setsda = tw68_bit_setsda,
.setscl = tw68_bit_setscl,
.getsda = tw68_bit_getsda,
.getscl = tw68_bit_getscl,
.udelay = 16,
.timeout = 200,
};
static struct i2c_client tw68_client_template = {
.name = "tw68 internal",
};
/*----------------------------------------------------------------*/
static int attach_inform(struct i2c_client *client)
{
/* struct tw68_dev *dev = client->adapter->algo_data; */
d1printk("%s i2c attach [addr=0x%x,client=%s]\n",
client->driver->driver.name, client->addr, client->name);
switch (client->addr) {
/* No info yet on what addresses to expect */
}
return 0;
}
static struct i2c_adapter tw68_adap_sw_template = {
.owner = THIS_MODULE,
.name = "tw68_sw",
.client_register = attach_inform,
};
static int tw68_i2c_eeprom(struct tw68_dev *dev, unsigned char *eedata,
int len)
{
unsigned char buf;
int i, err;
dev->i2c_client.addr = 0xa0 >> 1;
buf = 256 - len;
err = i2c_master_send(&dev->i2c_client, &buf, 1);
if (1 != err) {
printk(KERN_INFO "%s: Huh, no eeprom present (err = %d)?\n",
dev->name, err);
return -1;
}
err = i2c_master_recv(&dev->i2c_client, eedata, len);
if (len != err) {
printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n",
dev->name, err);
return -1;
}
for (i = 0; i < len; i++) {
if (0 == (i % 16))
printk(KERN_INFO "%s: i2c eeprom %02x:",
dev->name, i);
printk(KERN_INFO " %02x", eedata[i]);
if (15 == (i % 16))
printk("\n");
}
return 0;
}
#if 0
static char *i2c_devs[128] = {
[0xa0 >> 1] = "eeprom",
};
static void do_i2c_scan(char *name, struct i2c_client *c)
{
unsigned char buf;
int i, rc;
for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
c->addr = i;
rc = i2c_master_recv(c, &buf, 1);
if (rc < 0)
continue;
printk(KERN_INFO "%s: i2c scan: found device "
"@ 0x%x [%s]\n", name, i << 1,
i2c_devs[i] ? i2c_devs[i] : "???");
}
}
#endif
int __devinit tw68_i2c_register(struct tw68_dev *dev)
{
int rc;
printk(KERN_DEBUG "%s: Registering i2c module\n", __func__);
tw_writeb(TW68_I2C_RST, 1); /* reset the i2c module */
memcpy(&dev->i2c_client, &tw68_client_template,
sizeof(tw68_client_template));
memcpy(&dev->i2c_adap, &tw68_adap_sw_template,
sizeof(tw68_adap_sw_template));
dev->i2c_adap.algo_data = &dev->i2c_algo;
dev->i2c_adap.dev.parent = &dev->pci->dev;
memcpy(&dev->i2c_algo, &tw68_i2c_algo_bit_template,
sizeof(tw68_i2c_algo_bit_template));
dev->i2c_algo.data = dev;
/* TODO - may want to set better name (see bttv code) */
i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
dev->i2c_client.adapter = &dev->i2c_adap;
/* Assure chip is in "software" mode */
tw_writel(TW68_SBUSC, TW68_SSDAT | TW68_SSCLK);
tw68_bit_setscl(dev, 1);
tw68_bit_setsda(dev, 1);
rc = i2c_bit_add_bus(&dev->i2c_adap);
tw68_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata));
#if 0
if (i2c_scan)
do_i2c_scan(dev->name, &dev->i2c_client);
#endif
return rc;
}
int tw68_i2c_unregister(struct tw68_dev *dev)
{
i2c_del_adapter(&dev->i2c_adap);
return 0;
}
/*
* tw68-reg.h - TW68xx register offsets
*
* Much of this code is derived from the cx88 and sa7134 drivers, which
* were in turn derived from the bt87x driver. The original work was by
* Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
* Hans Verkuil, Andy Walls and many others. Their work is gratefully
* acknowledged. Full credit goes to them - any problems within this code
* are mine.
*
* Copyright (C) William M. Brack <wbrack@mmm.com.hk>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _TW68_REG_H_
#define _TW68_REG_H_
/* ---------------------------------------------------------------------- */
#define TW68_DMAC 0x000
#define TW68_DMAP_SA 0x004
#define TW68_DMAP_EXE 0x008
#define TW68_DMAP_PP 0x00c
#define TW68_VBIC 0x010
#define TW68_SBUSC 0x014
#define TW68_SBUSSD 0x018
#define TW68_INTSTAT 0x01C
#define TW68_INTMASK 0x020
#define TW68_GPIOC 0x024
#define TW68_GPOE 0x028
#define TW68_TESTREG 0x02C
#define TW68_SBUSRD 0x030
#define TW68_SBUS_TRIG 0x034
#define TW68_CAP_CTL 0x040
#define TW68_SUBSYS 0x054
#define TW68_I2C_RST 0x064
#define TW68_VBIINST 0x06C
/* define bits in FIFO and DMAP Control reg */
#define TW68_DMAP_EN (1 << 0)
#define TW68_FIFO_EN (1 << 1)
/* define the Interrupt Status Register bits */
#define TW68_SBDONE (1 << 0)
#define TW68_DMAPI (1 << 1)
#define TW68_GPINT (1 << 2)
#define TW68_FFOF (1 << 3)
#define TW68_FDMIS (1 << 4)
#define TW68_DMAPERR (1 << 5)
#define TW68_PABORT (1 << 6)
#define TW68_SBDONE2 (1 << 12)
#define TW68_SBERR2 (1 << 13)
#define TW68_PPERR (1 << 14)
#define TW68_FFERR (1 << 15)
#define TW68_DET50 (1 << 16)
#define TW68_FLOCK (1 << 17)
#define TW68_CCVALID (1 << 18)
#define TW68_VLOCK (1 << 19)
#define TW68_FIELD (1 << 20)
#define TW68_SLOCK (1 << 21)
#define TW68_HLOCK (1 << 22)
#define TW68_VDLOSS (1 << 23)
#define TW68_SBERR (1 << 24)
/* define the i2c control register bits */
#define TW68_SBMODE (0)
#define TW68_WREN (1)
#define TW68_SSCLK (6)
#define TW68_SSDAT (7)
#define TW68_SBCLK (8)
#define TW68_WDLEN (16)
#define TW68_RDLEN (20)
#define TW68_SBRW (24)
#define TW68_SBDEV (25)
#define TW68_SBMODE_B (1 << TW68_SBMODE)
#define TW68_WREN_B (1 << TW68_WREN)
#define TW68_SSCLK_B (1 << TW68_SSCLK)
#define TW68_SSDAT_B (1 << TW68_SSDAT)
#define TW68_SBRW_B (1 << TW68_SBRW)
#define TW68_GPDATA 0x100
#define TW68_STATUS1 0x204
#define TW68_INFORM 0x208
#define TW68_OPFORM 0x20C
#define TW68_HSYNC 0x210
#define TW68_ACNTL 0x218
#define TW68_CROP_HI 0x21C
#define TW68_VDELAY_LO 0x220
#define TW68_VACTIVE_LO 0x224
#define TW68_HDELAY_LO 0x228
#define TW68_HACTIVE_LO 0x22C
#define TW68_CNTRL1 0x230
#define TW68_VSCALE_LO 0x234
#define TW68_SCALE_HI 0x238
#define TW68_HSCALE_LO 0x23C
#define TW68_BRIGHT 0x240
#define TW68_CONTRAST 0x244
#define TW68_SHARPNESS 0x248
#define TW68_SAT_U 0x24C
#define TW68_SAT_V 0x250
#define TW68_HUE 0x254
#define TW68_SHARP2 0x258
#define TW68_VSHARP 0x25C
#define TW68_CORING 0x260
#define TW68_VBICNTL 0x264
#define TW68_CNTRL2 0x268
#define TW68_CC_DATA 0x26C
#define TW68_SDT 0x270
#define TW68_SDTR 0x274
#define TW68_RESERV2 0x278
#define TW68_RESERV3 0x27C
#define TW68_CLMPG 0x280
#define TW68_IAGC 0x284
#define TW68_AGCGAIN 0x288
#define TW68_PEAKWT 0x28C
#define TW68_CLMPL 0x290
#define TW68_SYNCT 0x294
#define TW68_MISSCNT 0x298
#define TW68_PCLAMP 0x29C
#define TW68_VCNTL1 0x2A0
#define TW68_VCNTL2 0x2A4
#define TW68_CKILL 0x2A8
#define TW68_COMB 0x2AC
#define TW68_LDLY 0x2B0
#define TW68_MISC1 0x2B4
#define TW68_LOOP 0x2B8
#define TW68_MISC2 0x2BC
#define TW68_MVSN 0x2C0
#define TW68_STATUS2 0x2C4
#define TW68_HFREF 0x2C8
#define TW68_CLMD 0x2CC
#define TW68_IDCNTL 0x2D0
#define TW68_CLCNTL1 0x2D4
/* Audio */
#define TW68_ACKI1 0x300
#define TW68_ACKI2 0x304
#define TW68_ACKI3 0x308
#define TW68_ACKN1 0x30C
#define TW68_ACKN2 0x310
#define TW68_ACKN3 0x314
#define TW68_SDIV 0x318
#define TW68_LRDIV 0x31C
#define TW68_ACCNTL 0x320
#define TW68_VSCTL 0x3B8
#define TW68_CHROMAGVAL 0x3BC
#define TW68_F2CROP_HI 0x3DC
#define TW68_F2VDELAY_LO 0x3E0
#define TW68_F2VACTIVE_LO 0x3E4
#define TW68_F2HDELAY_LO 0x3E8
#define TW68_F2HACTIVE_LO 0x3EC
#define TW68_F2CNT 0x3F0
#define TW68_F2VSCALE_LO 0x3F4
#define TW68_F2SCALE_HI 0x3F8
#define TW68_F2HSCALE_LO 0x3FC
#define RISC_INT_BIT 0x08000000
#define RISC_SYNCO 0xC0000000
#define RISC_SYNCE 0xD0000000
#define RISC_JUMP 0xB0000000
#define RISC_LINESTART 0x90000000
#define RISC_INLINE 0xA0000000
#define VideoFormatNTSC 0
#define VideoFormatNTSCJapan 0
#define VideoFormatPALBDGHI 1
#define VideoFormatSECAM 2
#define VideoFormatNTSC443 3
#define VideoFormatPALM 4
#define VideoFormatPALN 5
#define VideoFormatPALNC 5
#define VideoFormatPAL60 6
#define VideoFormatAuto 7
#define ColorFormatRGB32 0x00
#define ColorFormatRGB24 0x10
#define ColorFormatRGB16 0x20
#define ColorFormatRGB15 0x30
#define ColorFormatYUY2 0x40
#define ColorFormatBSWAP 0x04
#define ColorFormatWSWAP 0x08
#define ColorFormatGamma 0x80
#endif
/*
* tw68_risc.c
* Part of the device driver for Techwell 68xx based cards
*
* Much of this code is derived from the cx88 and sa7134 drivers, which
* were in turn derived from the bt87x driver. The original work was by
* Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
* Hans Verkuil, Andy Walls and many others. Their work is gratefully
* acknowledged. Full credit goes to them - any problems within this code
* are mine.
*
* Copyright (C) 2009 William M. Brack <wbrack@mmm.com.hk>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "tw68.h"
#define NO_SYNC_LINE (-1U)
/**
* @rp pointer to current risc program position
* @sglist pointer to "scatter-gather list" of buffer pointers
* @offset offset to target memory buffer
* @sync_line 0 -> no sync, 1 -> odd sync, 2 -> even sync
* @bpl number of bytes per scan line
* @padding number of bytes of padding to add
* @lines number of lines in field
* @lpi lines per IRQ, or 0 to not generate irqs
* Note: IRQ to be generated _after_ lpi lines are transferred
*/
static __le32 *tw68_risc_field(__le32 *rp, struct scatterlist *sglist,
unsigned int offset, u32 sync_line,
unsigned int bpl, unsigned int padding,
unsigned int lines, unsigned int lpi)
{
struct scatterlist *sg;
unsigned int line, todo, done;
/* sync instruction */
if (sync_line != NO_SYNC_LINE) {
if (sync_line == 1)
*(rp++) = cpu_to_le32(RISC_SYNCO);
else
*(rp++) = cpu_to_le32(RISC_SYNCE);
*(rp++) = 0;
}
/* scan lines */
sg = sglist;
for (line = 0; line < lines; line++) {
/* calculate next starting position */
while (offset && offset >= sg_dma_len(sg)) {
offset -= sg_dma_len(sg);
sg++;
}
if (bpl <= sg_dma_len(sg) - offset) {
/* fits into current chunk */
*(rp++) = cpu_to_le32(RISC_LINESTART |
/* (offset<<12) |*/ bpl);
*(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
offset += bpl;
} else {
/*
* scanline needs to be split. Put the start in
* whatever memory remains using RISC_LINESTART,
* then the remainder into following addresses
* given by the scatter-gather list.
*/
todo = bpl; /* one full line to be done */
/* first fragment */
done = (sg_dma_len(sg) - offset);
*(rp++) = cpu_to_le32(RISC_LINESTART |
(7 << 24) |
done);
*(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
todo -= done;
sg++;
/* succeeding fragments have no offset */
while (todo > sg_dma_len(sg)) {
*(rp++) = cpu_to_le32(RISC_INLINE |
(done << 12) |
sg_dma_len(sg));
*(rp++) = cpu_to_le32(sg_dma_address(sg));
todo -= sg_dma_len(sg);
sg++;
done += sg_dma_len(sg);
}
if (todo) {
/* final chunk - offset 0, count 'todo' */
*(rp++) = cpu_to_le32(RISC_INLINE |
(done << 12) |
todo);
*(rp++) = cpu_to_le32(sg_dma_address(sg));
}
offset = todo;
}
offset += padding;
/* If this line needs an interrupt, put it in */
if (lpi && line > 0 && !(line % lpi))
*(rp-2) |= RISC_INT_BIT;
}
return rp;
}
/**
* tw68_risc_buffer
*
* This routine is called by tw68-video. It allocates
* memory for the dma controller "program" and then fills in that
* memory with the appropriate "instructions".
*
* @pci_dev structure with info about the pci
* slot which our device is in.
* @risc structure with info about the memory
* used for our controller program.
* @sglist scatter-gather list entry
* @top_offset offset within the risc program area for the
* first odd frame line
* @bottom_offset offset within the risc program area for the
* first even frame line
* @bpl number of data bytes per scan line
* @padding number of extra bytes to add at end of line
* @lines number of scan lines
*/
int tw68_risc_buffer(struct pci_dev *pci,
struct btcx_riscmem *risc,
struct scatterlist *sglist,
unsigned int top_offset,
unsigned int bottom_offset,
unsigned int bpl,
unsigned int padding,
unsigned int lines)
{
u32 instructions, fields;
__le32 *rp;
int rc;
fields = 0;
if (UNSET != top_offset)
fields++;
if (UNSET != bottom_offset)
fields++;
/*
* estimate risc mem: worst case is one write per page border +
* one write per scan line + syncs + jump (all 2 dwords).
* Padding can cause next bpl to start close to a page border.
* First DMA region may be smaller than PAGE_SIZE
*/
instructions = fields * (1 + (((bpl + padding) * lines) /
PAGE_SIZE) + lines) + 2;
rc = btcx_riscmem_alloc(pci, risc, instructions * 8);
if (rc < 0)
return rc;
/* write risc instructions */
rp = risc->cpu;
if (UNSET != top_offset) /* generates SYNCO */
rp = tw68_risc_field(rp, sglist, top_offset, 1,
bpl, padding, lines, 0);
if (UNSET != bottom_offset) /* generates SYNCE */
rp = tw68_risc_field(rp, sglist, bottom_offset, 2,
bpl, padding, lines, 0);
/* save pointer to jmp instruction address */
risc->jmp = rp;
/* assure risc buffer hasn't overflowed */
BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
return 0;
}
#if 0
/* ------------------------------------------------------------------ */
/* debug helper code */
static void tw68_risc_decode(u32 risc, u32 addr)
{
#define RISC_OP(reg) (((reg) >> 28) & 7)
static struct instr_details {
char *name;
u8 has_data_type;
u8 has_byte_info;
u8 has_addr;
} instr[8] = {
[RISC_OP(RISC_SYNCO)] = {"syncOdd", 0, 0, 0},
[RISC_OP(RISC_SYNCE)] = {"syncEven", 0, 0, 0},
[RISC_OP(RISC_JUMP)] = {"jump", 0, 0, 1},
[RISC_OP(RISC_LINESTART)] = {"lineStart", 1, 1, 1},
[RISC_OP(RISC_INLINE)] = {"inline", 1, 1, 1},
};
u32 p;
p = RISC_OP(risc);
if (!(risc & 0x80000000) || !instr[p].name) {
printk(KERN_DEBUG "0x%08x [ INVALID ]\n", risc);
return;
}
printk(KERN_DEBUG "0x%08x %-9s IRQ=%d",
risc, instr[p].name, (risc >> 27) & 1);
if (instr[p].has_data_type)
printk(KERN_DEBUG " Type=%d", (risc >> 24) & 7);
if (instr[p].has_byte_info)
printk(KERN_DEBUG " Start=0x%03x Count=%03u",
(risc >> 12) & 0xfff, risc & 0xfff);
if (instr[p].has_addr)
printk(KERN_DEBUG " StartAddr=0x%08x", addr);
printk(KERN_DEBUG "\n");
}
void tw68_risc_program_dump(struct tw68_core *core,
struct btcx_riscmem *risc)
{
__le32 *addr;
printk(KERN_DEBUG "%s: risc_program_dump: risc=%p, "
"risc->cpu=0x%p, risc->jmp=0x%p\n",
core->name, risc, risc->cpu, risc->jmp);
for (addr = risc->cpu; addr <= risc->jmp; addr += 2)
tw68_risc_decode(*addr, *(addr+1));
}
EXPORT_SYMBOL_GPL(tw68_risc_program_dump);
#endif
/*
* tw68_risc_stopper
* Normally, the risc code generated for a buffer ends with a
* JUMP instruction to direct the DMAP processor to the code for
* the next buffer. However, when there is no additional buffer
* currently available, the code instead jumps to this routine.
*
* My first try for a "stopper" program was just a simple
* "jump to self" instruction. Unfortunately, this caused the
* video FIFO to overflow. My next attempt was to just disable
* the DMAP processor. Unfortunately, this caused the video
* decoder to lose its synchronization. The solution to this was to
* add a "Sync-Odd" instruction, which "eats" all the video data
* until the start of the next odd field.
*/
int tw68_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc)
{
__le32 *rp;
int rc;
rc = btcx_riscmem_alloc(pci, risc, 8*4);
if (rc < 0)
return rc;
/* write risc inststructions */
rp = risc->cpu;
*(rp++) = cpu_to_le32(RISC_SYNCO);
*(rp++) = 0;
*(rp++) = cpu_to_le32(RISC_JUMP);
*(rp++) = cpu_to_le32(risc->dma);
risc->jmp = risc->cpu;
return 0;
}
/*
* tw68_ts.c
* Part of the device driver for Techwell 68xx based cards
*
* Much of this code is derived from the cx88 and sa7134 drivers, which
* were in turn derived from the bt87x driver. The original work was by
* Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
* Hans Verkuil, Andy Walls and many others. Their work is gratefully
* acknowledged. Full credit goes to them - any problems within this code
* are mine.
*
* Copyright (C) 2009 William M. Brack <wbrack@mmm.com.hk>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "tw68.h"
int tw68_ts_init1(struct tw68_dev *dev)
{
return 0;
}
int tw68_ts_ini(struct tw68_dev *dev)
{
return 0;
}
int tw68_ts_fini(struct tw68_dev *dev)
{
return 0;
}
void tw68_irq_ts_done(struct tw68_dev *dev, unsigned long status)
{
return;
}
int tw68_ts_register(struct tw68_mpeg_ops *ops)
{
return 0;
}
void tw68_ts_unregister(struct tw68_mpeg_ops *ops)
{
return;
}
int tw68_ts_init_hw(struct tw68_dev *dev)
{
return 0;
}
/*
* tw68_controls.c
* Part of the device driver for Techwell 68xx based cards
*
* Much of this code is derived from the cx88 and sa7134 drivers, which
* were in turn derived from the bt87x driver. The original work was by
* Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
* Hans Verkuil, Andy Walls and many others. Their work is gratefully
* acknowledged. Full credit goes to them - any problems within this code
* are mine.
*
* Copyright (C) 2009 William M. Brack <wbrack@mmm.com.hk>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "tw68.h"
int tw68_tvaudio_rx2mode(u32 rx)
{
return 0;
}
void tw68_tvaudio_setmute(struct tw68_dev *dev)
{
return;
}
void tw68_tvaudio_setinput(struct tw68_dev *dev, struct tw68_input *in)
{
return;
}
void tw68_tvaudio_setvolume(struct tw68_dev *dev, int level)
{
return;
}
int tw68_tvaudio_getstereo(struct tw68_dev *dev)
{
return 0;
}
void tw68_tvaudio_init(struct tw68_dev *dev)
{
return;
}
int tw68_tvaudio_init2(struct tw68_dev *dev)
{
return 0;
}
int tw68_tvaudio_fini(struct tw68_dev *dev)
{
return 0;
}
int tw68_tvaudio_do_scan(struct tw68_dev *dev)
{
return 0;
}
void tw68_enable_i2s(struct tw68_dev *dev)
{
return;
}
/*
* tw68_controls.c
* Part of the device driver for Techwell 68xx based cards
*
* Much of this code is derived from the cx88 and sa7134 drivers, which
* were in turn derived from the bt87x driver. The original work was by
* Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
* Hans Verkuil, Andy Walls and many others. Their work is gratefully
* acknowledged. Full credit goes to them - any problems within this code
* are mine.
*
* Copyright (C) 2009 William M. Brack <wbrack@mmm.com.hk>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "tw68.h"
static int buffer_setup(struct videobuf_queue *q, unsigned int *count,
unsigned int *size) {
printk(KERN_INFO "%s: shouldn't be here!\n", __func__);
return 0;
}
static int buffer_prepare(struct videobuf_queue *q,
struct videobuf_buffer *vb,
enum v4l2_field field)
{
printk(KERN_INFO "%s: shouldn't be here!\n", __func__);
return 0;
}
static void buffer_queue(struct videobuf_queue *q,
struct videobuf_buffer *vb)
{
printk(KERN_INFO "%s: shouldn't be here!\n", __func__);
}
static void buffer_release(struct videobuf_queue *q,
struct videobuf_buffer *vb)
{
printk(KERN_INFO "%s: shouldn't be here!\n", __func__);
}
struct videobuf_queue_ops tw68_vbi_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
/* ------------------------------------------------------------------ */
int tw68_vbi_init1(struct tw68_dev *dev)
{
return 0;
}
int tw68_vbi_fini(struct tw68_dev *dev)
{
return 0;
}
void tw68_irq_vbi_done(struct tw68_dev *dev, unsigned long status)
{
return;
}
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment