601 lines
17 KiB
C
601 lines
17 KiB
C
/*
|
|
* Driver for the NXP SAA7164 PCIe bridge
|
|
*
|
|
* Copyright (c) 2009 Steven Toth <stoth@kernellabs.com>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <linux/wait.h>
|
|
|
|
#include "saa7164.h"
|
|
|
|
int saa7164_api_transition_port(struct saa7164_tsport *port, u8 mode)
|
|
{
|
|
int ret;
|
|
|
|
ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, SET_CUR,
|
|
SAA_STATE_CONTROL, sizeof(mode), &mode);
|
|
if (ret != SAA_OK)
|
|
printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version)
|
|
{
|
|
int ret;
|
|
|
|
ret = saa7164_cmd_send(dev, 0, GET_CUR,
|
|
GET_FW_VERSION_CONTROL, sizeof(u32), version);
|
|
if (ret != SAA_OK)
|
|
printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen)
|
|
{
|
|
u8 reg[] = { 0x0f, 0x00 };
|
|
|
|
if (buflen < 128)
|
|
return -ENOMEM;
|
|
|
|
/* Assumption: Hauppauge eeprom is at 0xa0 on on bus 0 */
|
|
/* TODO: Pull the details from the boards struct */
|
|
return saa7164_api_i2c_read(&dev->i2c_bus[0], 0xa0 >> 1, sizeof(reg),
|
|
®[0], 128, buf);
|
|
}
|
|
|
|
|
|
int saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev,
|
|
struct saa7164_tsport *port,
|
|
tmComResTSFormatDescrHeader_t *tsfmt)
|
|
{
|
|
dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", tsfmt->bFormatIndex);
|
|
dprintk(DBGLVL_API, " bDataOffset = 0x%x\n", tsfmt->bDataOffset);
|
|
dprintk(DBGLVL_API, " bPacketLength= 0x%x\n", tsfmt->bPacketLength);
|
|
dprintk(DBGLVL_API, " bStrideLength= 0x%x\n", tsfmt->bStrideLength);
|
|
dprintk(DBGLVL_API, " bguid = (....)\n");
|
|
|
|
/* Cache the hardware configuration in the port */
|
|
|
|
port->bufcounter = port->hwcfg.BARLocation;
|
|
port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
|
|
port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
|
|
port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
|
|
port->bufptr32l = port->hwcfg.BARLocation +
|
|
(4 * sizeof(u32)) +
|
|
(sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
|
|
port->bufptr32h = port->hwcfg.BARLocation +
|
|
(4 * sizeof(u32)) +
|
|
(sizeof(u32) * port->hwcfg.buffercount);
|
|
port->bufptr64 = port->hwcfg.BARLocation +
|
|
(4 * sizeof(u32)) +
|
|
(sizeof(u32) * port->hwcfg.buffercount);
|
|
dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n",
|
|
port->hwcfg.BARLocation);
|
|
|
|
dprintk(DBGLVL_API, " = VS_FORMAT_MPEGTS (becomes dev->ts[%d])\n",
|
|
port->nr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len)
|
|
{
|
|
struct saa7164_tsport *port = 0;
|
|
u32 idx, next_offset;
|
|
int i;
|
|
tmComResDescrHeader_t *hdr, *t;
|
|
tmComResExtDevDescrHeader_t *exthdr;
|
|
tmComResPathDescrHeader_t *pathhdr;
|
|
tmComResAntTermDescrHeader_t *anttermhdr;
|
|
tmComResTunerDescrHeader_t *tunerunithdr;
|
|
tmComResDMATermDescrHeader_t *vcoutputtermhdr;
|
|
tmComResTSFormatDescrHeader_t *tsfmt;
|
|
u32 currpath = 0;
|
|
|
|
dprintk(DBGLVL_API,
|
|
"%s(?,?,%d) sizeof(tmComResDescrHeader_t) = %d bytes\n",
|
|
__func__, len, (u32)sizeof(tmComResDescrHeader_t));
|
|
|
|
for (idx = 0; idx < (len - sizeof(tmComResDescrHeader_t)); ) {
|
|
|
|
hdr = (tmComResDescrHeader_t *)(buf + idx);
|
|
|
|
if (hdr->type != CS_INTERFACE)
|
|
return SAA_ERR_NOT_SUPPORTED;
|
|
|
|
dprintk(DBGLVL_API, "@ 0x%x = \n", idx);
|
|
switch (hdr->subtype) {
|
|
case GENERAL_REQUEST:
|
|
dprintk(DBGLVL_API, " GENERAL_REQUEST\n");
|
|
break;
|
|
case VC_TUNER_PATH:
|
|
dprintk(DBGLVL_API, " VC_TUNER_PATH\n");
|
|
pathhdr = (tmComResPathDescrHeader_t *)(buf + idx);
|
|
dprintk(DBGLVL_API, " pathid = 0x%x\n",
|
|
pathhdr->pathid);
|
|
currpath = pathhdr->pathid;
|
|
break;
|
|
case VC_INPUT_TERMINAL:
|
|
dprintk(DBGLVL_API, " VC_INPUT_TERMINAL\n");
|
|
anttermhdr =
|
|
(tmComResAntTermDescrHeader_t *)(buf + idx);
|
|
dprintk(DBGLVL_API, " terminalid = 0x%x\n",
|
|
anttermhdr->terminalid);
|
|
dprintk(DBGLVL_API, " terminaltype = 0x%x\n",
|
|
anttermhdr->terminaltype);
|
|
switch (anttermhdr->terminaltype) {
|
|
case ITT_ANTENNA:
|
|
dprintk(DBGLVL_API, " = ITT_ANTENNA\n");
|
|
break;
|
|
case LINE_CONNECTOR:
|
|
dprintk(DBGLVL_API, " = LINE_CONNECTOR\n");
|
|
break;
|
|
case SPDIF_CONNECTOR:
|
|
dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n");
|
|
break;
|
|
case COMPOSITE_CONNECTOR:
|
|
dprintk(DBGLVL_API,
|
|
" = COMPOSITE_CONNECTOR\n");
|
|
break;
|
|
case SVIDEO_CONNECTOR:
|
|
dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n");
|
|
break;
|
|
case COMPONENT_CONNECTOR:
|
|
dprintk(DBGLVL_API,
|
|
" = COMPONENT_CONNECTOR\n");
|
|
break;
|
|
case STANDARD_DMA:
|
|
dprintk(DBGLVL_API, " = STANDARD_DMA\n");
|
|
break;
|
|
default:
|
|
dprintk(DBGLVL_API, " = undefined (0x%x)\n",
|
|
anttermhdr->terminaltype);
|
|
}
|
|
dprintk(DBGLVL_API, " assocterminal= 0x%x\n",
|
|
anttermhdr->assocterminal);
|
|
dprintk(DBGLVL_API, " iterminal = 0x%x\n",
|
|
anttermhdr->iterminal);
|
|
dprintk(DBGLVL_API, " controlsize = 0x%x\n",
|
|
anttermhdr->controlsize);
|
|
break;
|
|
case VC_OUTPUT_TERMINAL:
|
|
dprintk(DBGLVL_API, " VC_OUTPUT_TERMINAL\n");
|
|
vcoutputtermhdr =
|
|
(tmComResDMATermDescrHeader_t *)(buf + idx);
|
|
dprintk(DBGLVL_API, " unitid = 0x%x\n",
|
|
vcoutputtermhdr->unitid);
|
|
dprintk(DBGLVL_API, " terminaltype = 0x%x\n",
|
|
vcoutputtermhdr->terminaltype);
|
|
switch (vcoutputtermhdr->terminaltype) {
|
|
case ITT_ANTENNA:
|
|
dprintk(DBGLVL_API, " = ITT_ANTENNA\n");
|
|
break;
|
|
case LINE_CONNECTOR:
|
|
dprintk(DBGLVL_API, " = LINE_CONNECTOR\n");
|
|
break;
|
|
case SPDIF_CONNECTOR:
|
|
dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n");
|
|
break;
|
|
case COMPOSITE_CONNECTOR:
|
|
dprintk(DBGLVL_API,
|
|
" = COMPOSITE_CONNECTOR\n");
|
|
break;
|
|
case SVIDEO_CONNECTOR:
|
|
dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n");
|
|
break;
|
|
case COMPONENT_CONNECTOR:
|
|
dprintk(DBGLVL_API,
|
|
" = COMPONENT_CONNECTOR\n");
|
|
break;
|
|
case STANDARD_DMA:
|
|
dprintk(DBGLVL_API, " = STANDARD_DMA\n");
|
|
break;
|
|
default:
|
|
dprintk(DBGLVL_API, " = undefined (0x%x)\n",
|
|
vcoutputtermhdr->terminaltype);
|
|
}
|
|
dprintk(DBGLVL_API, " assocterminal= 0x%x\n",
|
|
vcoutputtermhdr->assocterminal);
|
|
dprintk(DBGLVL_API, " sourceid = 0x%x\n",
|
|
vcoutputtermhdr->sourceid);
|
|
dprintk(DBGLVL_API, " iterminal = 0x%x\n",
|
|
vcoutputtermhdr->iterminal);
|
|
dprintk(DBGLVL_API, " BARLocation = 0x%x\n",
|
|
vcoutputtermhdr->BARLocation);
|
|
dprintk(DBGLVL_API, " flags = 0x%x\n",
|
|
vcoutputtermhdr->flags);
|
|
dprintk(DBGLVL_API, " interruptid = 0x%x\n",
|
|
vcoutputtermhdr->interruptid);
|
|
dprintk(DBGLVL_API, " buffercount = 0x%x\n",
|
|
vcoutputtermhdr->buffercount);
|
|
dprintk(DBGLVL_API, " metadatasize = 0x%x\n",
|
|
vcoutputtermhdr->metadatasize);
|
|
dprintk(DBGLVL_API, " controlsize = 0x%x\n",
|
|
vcoutputtermhdr->controlsize);
|
|
dprintk(DBGLVL_API, " numformats = 0x%x\n",
|
|
vcoutputtermhdr->numformats);
|
|
|
|
t = (tmComResDescrHeader_t *)
|
|
((tmComResDMATermDescrHeader_t *)(buf + idx));
|
|
next_offset = idx + (vcoutputtermhdr->len);
|
|
for (i = 0; i < vcoutputtermhdr->numformats; i++) {
|
|
t = (tmComResDescrHeader_t *)
|
|
(buf + next_offset);
|
|
switch (t->subtype) {
|
|
case VS_FORMAT_MPEG2TS:
|
|
tsfmt =
|
|
(tmComResTSFormatDescrHeader_t *)t;
|
|
if (currpath == 1)
|
|
port = &dev->ts1;
|
|
else
|
|
port = &dev->ts2;
|
|
memcpy(&port->hwcfg, vcoutputtermhdr,
|
|
sizeof(*vcoutputtermhdr));
|
|
saa7164_api_configure_port_mpeg2ts(dev,
|
|
port, tsfmt);
|
|
break;
|
|
case VS_FORMAT_MPEG2PS:
|
|
dprintk(DBGLVL_API,
|
|
" = VS_FORMAT_MPEG2PS\n");
|
|
break;
|
|
case VS_FORMAT_VBI:
|
|
dprintk(DBGLVL_API,
|
|
" = VS_FORMAT_VBI\n");
|
|
break;
|
|
case VS_FORMAT_RDS:
|
|
dprintk(DBGLVL_API,
|
|
" = VS_FORMAT_RDS\n");
|
|
break;
|
|
case VS_FORMAT_UNCOMPRESSED:
|
|
dprintk(DBGLVL_API,
|
|
" = VS_FORMAT_UNCOMPRESSED\n");
|
|
break;
|
|
case VS_FORMAT_TYPE:
|
|
dprintk(DBGLVL_API,
|
|
" = VS_FORMAT_TYPE\n");
|
|
break;
|
|
default:
|
|
dprintk(DBGLVL_API,
|
|
" = undefined (0x%x)\n",
|
|
t->subtype);
|
|
}
|
|
next_offset += t->len;
|
|
}
|
|
|
|
break;
|
|
case TUNER_UNIT:
|
|
dprintk(DBGLVL_API, " TUNER_UNIT\n");
|
|
tunerunithdr =
|
|
(tmComResTunerDescrHeader_t *)(buf + idx);
|
|
dprintk(DBGLVL_API, " unitid = 0x%x\n",
|
|
tunerunithdr->unitid);
|
|
dprintk(DBGLVL_API, " sourceid = 0x%x\n",
|
|
tunerunithdr->sourceid);
|
|
dprintk(DBGLVL_API, " iunit = 0x%x\n",
|
|
tunerunithdr->iunit);
|
|
dprintk(DBGLVL_API, " tuningstandards = 0x%x\n",
|
|
tunerunithdr->tuningstandards);
|
|
dprintk(DBGLVL_API, " controlsize = 0x%x\n",
|
|
tunerunithdr->controlsize);
|
|
dprintk(DBGLVL_API, " controls = 0x%x\n",
|
|
tunerunithdr->controls);
|
|
break;
|
|
case VC_SELECTOR_UNIT:
|
|
dprintk(DBGLVL_API, " VC_SELECTOR_UNIT\n");
|
|
break;
|
|
case VC_PROCESSING_UNIT:
|
|
dprintk(DBGLVL_API, " VC_PROCESSING_UNIT\n");
|
|
break;
|
|
case FEATURE_UNIT:
|
|
dprintk(DBGLVL_API, " FEATURE_UNIT\n");
|
|
break;
|
|
case ENCODER_UNIT:
|
|
dprintk(DBGLVL_API, " ENCODER_UNIT\n");
|
|
break;
|
|
case EXTENSION_UNIT:
|
|
dprintk(DBGLVL_API, " EXTENSION_UNIT\n");
|
|
exthdr = (tmComResExtDevDescrHeader_t *)(buf + idx);
|
|
dprintk(DBGLVL_API, " unitid = 0x%x\n",
|
|
exthdr->unitid);
|
|
dprintk(DBGLVL_API, " deviceid = 0x%x\n",
|
|
exthdr->deviceid);
|
|
dprintk(DBGLVL_API, " devicetype = 0x%x\n",
|
|
exthdr->devicetype);
|
|
if (exthdr->devicetype & 0x1)
|
|
dprintk(DBGLVL_API, " = Decoder Device\n");
|
|
if (exthdr->devicetype & 0x2)
|
|
dprintk(DBGLVL_API, " = GPIO Source\n");
|
|
if (exthdr->devicetype & 0x4)
|
|
dprintk(DBGLVL_API, " = Video Decoder\n");
|
|
if (exthdr->devicetype & 0x8)
|
|
dprintk(DBGLVL_API, " = Audio Decoder\n");
|
|
if (exthdr->devicetype & 0x20)
|
|
dprintk(DBGLVL_API, " = Crossbar\n");
|
|
if (exthdr->devicetype & 0x40)
|
|
dprintk(DBGLVL_API, " = Tuner\n");
|
|
if (exthdr->devicetype & 0x80)
|
|
dprintk(DBGLVL_API, " = IF PLL\n");
|
|
if (exthdr->devicetype & 0x100)
|
|
dprintk(DBGLVL_API, " = Demodulator\n");
|
|
if (exthdr->devicetype & 0x200)
|
|
dprintk(DBGLVL_API, " = RDS Decoder\n");
|
|
if (exthdr->devicetype & 0x400)
|
|
dprintk(DBGLVL_API, " = Encoder\n");
|
|
if (exthdr->devicetype & 0x800)
|
|
dprintk(DBGLVL_API, " = IR Decoder\n");
|
|
if (exthdr->devicetype & 0x1000)
|
|
dprintk(DBGLVL_API, " = EEPROM\n");
|
|
if (exthdr->devicetype & 0x2000)
|
|
dprintk(DBGLVL_API,
|
|
" = VBI Decoder\n");
|
|
if (exthdr->devicetype & 0x10000)
|
|
dprintk(DBGLVL_API,
|
|
" = Streaming Device\n");
|
|
if (exthdr->devicetype & 0x20000)
|
|
dprintk(DBGLVL_API,
|
|
" = DRM Device\n");
|
|
if (exthdr->devicetype & 0x40000000)
|
|
dprintk(DBGLVL_API,
|
|
" = Generic Device\n");
|
|
if (exthdr->devicetype & 0x80000000)
|
|
dprintk(DBGLVL_API,
|
|
" = Config Space Device\n");
|
|
dprintk(DBGLVL_API, " numgpiopins = 0x%x\n",
|
|
exthdr->numgpiopins);
|
|
dprintk(DBGLVL_API, " numgpiogroups = 0x%x\n",
|
|
exthdr->numgpiogroups);
|
|
dprintk(DBGLVL_API, " controlsize = 0x%x\n",
|
|
exthdr->controlsize);
|
|
break;
|
|
case PVC_INFRARED_UNIT:
|
|
dprintk(DBGLVL_API, " PVC_INFRARED_UNIT\n");
|
|
break;
|
|
case DRM_UNIT:
|
|
dprintk(DBGLVL_API, " DRM_UNIT\n");
|
|
break;
|
|
default:
|
|
dprintk(DBGLVL_API, "default %d\n", hdr->subtype);
|
|
}
|
|
|
|
dprintk(DBGLVL_API, " 1.%x\n", hdr->len);
|
|
dprintk(DBGLVL_API, " 2.%x\n", hdr->type);
|
|
dprintk(DBGLVL_API, " 3.%x\n", hdr->subtype);
|
|
dprintk(DBGLVL_API, " 4.%x\n", hdr->unitid);
|
|
|
|
idx += hdr->len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int saa7164_api_enum_subdevs(struct saa7164_dev *dev)
|
|
{
|
|
int ret;
|
|
u32 buflen = 0;
|
|
u8 *buf;
|
|
|
|
dprintk(DBGLVL_API, "%s()\n", __func__);
|
|
|
|
/* Get the total descriptor length */
|
|
ret = saa7164_cmd_send(dev, 0, GET_LEN,
|
|
GET_DESCRIPTORS_CONTROL, sizeof(buflen), &buflen);
|
|
if (ret != SAA_OK)
|
|
printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
|
|
|
|
dprintk(DBGLVL_API, "%s() total descriptor size = %d bytes.\n",
|
|
__func__, buflen);
|
|
|
|
/* Allocate enough storage for all of the descs */
|
|
buf = kzalloc(buflen, GFP_KERNEL);
|
|
if (buf == NULL)
|
|
return SAA_ERR_NO_RESOURCES;
|
|
|
|
/* Retrieve them */
|
|
ret = saa7164_cmd_send(dev, 0, GET_CUR,
|
|
GET_DESCRIPTORS_CONTROL, buflen, buf);
|
|
if (ret != SAA_OK) {
|
|
printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
|
|
goto out;
|
|
}
|
|
|
|
if (saa_debug & DBGLVL_API)
|
|
saa7164_dumphex16(dev, buf, (buflen/16)*16);
|
|
|
|
saa7164_api_dump_subdevs(dev, buf, buflen);
|
|
|
|
out:
|
|
kfree(buf);
|
|
return ret;
|
|
}
|
|
|
|
int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
|
|
u32 datalen, u8 *data)
|
|
{
|
|
struct saa7164_dev *dev = bus->dev;
|
|
u16 len = 0;
|
|
int unitid;
|
|
u32 regval;
|
|
u8 buf[256];
|
|
int ret;
|
|
|
|
dprintk(DBGLVL_API, "%s()\n", __func__);
|
|
|
|
if (reglen > 4)
|
|
return -EIO;
|
|
|
|
if (reglen == 1)
|
|
regval = *(reg);
|
|
else
|
|
if (reglen == 2)
|
|
regval = ((*(reg) << 8) || *(reg+1));
|
|
else
|
|
if (reglen == 3)
|
|
regval = ((*(reg) << 16) | (*(reg+1) << 8) | *(reg+2));
|
|
else
|
|
if (reglen == 4)
|
|
regval = ((*(reg) << 24) | (*(reg+1) << 16) |
|
|
(*(reg+2) << 8) | *(reg+3));
|
|
|
|
/* Prepare the send buffer */
|
|
/* Bytes 00-03 source register length
|
|
* 04-07 source bytes to read
|
|
* 08... register address
|
|
*/
|
|
memset(buf, 0, sizeof(buf));
|
|
memcpy((buf + 2 * sizeof(u32) + 0), reg, reglen);
|
|
*((u32 *)(buf + 0 * sizeof(u32))) = reglen;
|
|
*((u32 *)(buf + 1 * sizeof(u32))) = datalen;
|
|
|
|
unitid = saa7164_i2caddr_to_unitid(bus, addr);
|
|
if (unitid < 0) {
|
|
printk(KERN_ERR
|
|
"%s() error, cannot translate regaddr 0x%x to unitid\n",
|
|
__func__, addr);
|
|
return -EIO;
|
|
}
|
|
|
|
ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN,
|
|
EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
|
|
if (ret != SAA_OK) {
|
|
printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
|
|
return -EIO;
|
|
}
|
|
|
|
dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len);
|
|
|
|
if (saa_debug & DBGLVL_I2C)
|
|
saa7164_dumphex16(dev, buf, 2 * 16);
|
|
|
|
ret = saa7164_cmd_send(bus->dev, unitid, GET_CUR,
|
|
EXU_REGISTER_ACCESS_CONTROL, len, &buf);
|
|
if (ret != SAA_OK)
|
|
printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
|
|
else {
|
|
if (saa_debug & DBGLVL_I2C)
|
|
saa7164_dumphex16(dev, buf, sizeof(buf));
|
|
memcpy(data, (buf + 2 * sizeof(u32) + reglen), datalen);
|
|
}
|
|
|
|
return ret == SAA_OK ? 0 : -EIO;
|
|
}
|
|
|
|
/* For a given 8 bit i2c address device, write the buffer */
|
|
int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen,
|
|
u8 *data)
|
|
{
|
|
struct saa7164_dev *dev = bus->dev;
|
|
u16 len = 0;
|
|
int unitid;
|
|
int reglen;
|
|
u8 buf[256];
|
|
int ret;
|
|
|
|
dprintk(DBGLVL_API, "%s()\n", __func__);
|
|
|
|
if ((datalen == 0) || (datalen > 232))
|
|
return -EIO;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
unitid = saa7164_i2caddr_to_unitid(bus, addr);
|
|
if (unitid < 0) {
|
|
printk(KERN_ERR
|
|
"%s() error, cannot translate regaddr 0x%x to unitid\n",
|
|
__func__, addr);
|
|
return -EIO;
|
|
}
|
|
|
|
reglen = saa7164_i2caddr_to_reglen(bus, addr);
|
|
if (unitid < 0) {
|
|
printk(KERN_ERR
|
|
"%s() error, cannot translate regaddr to reglen\n",
|
|
__func__);
|
|
return -EIO;
|
|
}
|
|
|
|
ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN,
|
|
EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
|
|
if (ret != SAA_OK) {
|
|
printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
|
|
return -EIO;
|
|
}
|
|
|
|
dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len);
|
|
|
|
/* Prepare the send buffer */
|
|
/* Bytes 00-03 dest register length
|
|
* 04-07 dest bytes to write
|
|
* 08... register address
|
|
*/
|
|
*((u32 *)(buf + 0 * sizeof(u32))) = reglen;
|
|
*((u32 *)(buf + 1 * sizeof(u32))) = datalen - reglen;
|
|
memcpy((buf + 2 * sizeof(u32)), data, datalen);
|
|
|
|
if (saa_debug & DBGLVL_I2C)
|
|
saa7164_dumphex16(dev, buf, sizeof(buf));
|
|
|
|
ret = saa7164_cmd_send(bus->dev, unitid, SET_CUR,
|
|
EXU_REGISTER_ACCESS_CONTROL, len, &buf);
|
|
if (ret != SAA_OK)
|
|
printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
|
|
|
|
return ret == SAA_OK ? 0 : -EIO;
|
|
}
|
|
|
|
|
|
int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid,
|
|
u8 pin, u8 state)
|
|
{
|
|
int ret;
|
|
tmComResGPIO_t t;
|
|
|
|
dprintk(DBGLVL_API, "%s(0x%x, %d, %d)\n",
|
|
__func__, unitid, pin, state);
|
|
|
|
if ((pin > 7) || (state > 2))
|
|
return SAA_ERR_BAD_PARAMETER;
|
|
|
|
t.pin = pin;
|
|
t.state = state;
|
|
|
|
ret = saa7164_cmd_send(dev, unitid, SET_CUR,
|
|
EXU_GPIO_CONTROL, sizeof(t), &t);
|
|
if (ret != SAA_OK)
|
|
printk(KERN_ERR "%s() error, ret = 0x%x\n",
|
|
__func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid,
|
|
u8 pin)
|
|
{
|
|
return saa7164_api_modify_gpio(dev, unitid, pin, 1);
|
|
}
|
|
|
|
int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid,
|
|
u8 pin)
|
|
{
|
|
return saa7164_api_modify_gpio(dev, unitid, pin, 0);
|
|
}
|
|
|
|
|
|
|