mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-13 17:24:28 +08:00
V4L/DVB (5173): Pvrusb2: encoder comm protocol cleanup
Update the implementation of the communication protocol for operating the encoder, using updated knowledge about the encoder. Signed-off-by: Mike Isely <isely@pobox.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
parent
567d7115b9
commit
c43000ef0c
@ -35,34 +35,41 @@
|
|||||||
#define IVTV_MBOX_DRIVER_DONE 0x00000002
|
#define IVTV_MBOX_DRIVER_DONE 0x00000002
|
||||||
#define IVTV_MBOX_DRIVER_BUSY 0x00000001
|
#define IVTV_MBOX_DRIVER_BUSY 0x00000001
|
||||||
|
|
||||||
|
#define MBOX_BASE 0x44
|
||||||
|
|
||||||
|
|
||||||
static int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
|
static int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
|
||||||
|
unsigned int offs,
|
||||||
const u32 *data, unsigned int dlen)
|
const u32 *data, unsigned int dlen)
|
||||||
{
|
{
|
||||||
unsigned int idx;
|
unsigned int idx,addr;
|
||||||
|
unsigned int bAddr;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned int offs = 0;
|
|
||||||
unsigned int chunkCnt;
|
unsigned int chunkCnt;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Format: First byte must be 0x01. Remaining 32 bit words are
|
Format: First byte must be 0x01. Remaining 32 bit words are
|
||||||
spread out into chunks of 7 bytes each, little-endian ordered,
|
spread out into chunks of 7 bytes each, with the first 4 bytes
|
||||||
offset at zero within each 2 blank bytes following and a
|
being the data word (little endian), and the next 3 bytes
|
||||||
single byte that is 0x44 plus the offset of the word. Repeat
|
being the address where that data word is to be written (big
|
||||||
request for additional words, with offset adjusted
|
endian). Repeat request for additional words, with offset
|
||||||
accordingly.
|
adjusted accordingly.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
while (dlen) {
|
while (dlen) {
|
||||||
chunkCnt = 8;
|
chunkCnt = 8;
|
||||||
if (chunkCnt > dlen) chunkCnt = dlen;
|
if (chunkCnt > dlen) chunkCnt = dlen;
|
||||||
memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
|
memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
|
||||||
hdw->cmd_buffer[0] = FX2CMD_MEM_WRITE_DWORD;
|
bAddr = 0;
|
||||||
|
hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD;
|
||||||
for (idx = 0; idx < chunkCnt; idx++) {
|
for (idx = 0; idx < chunkCnt; idx++) {
|
||||||
hdw->cmd_buffer[1+(idx*7)+6] = 0x44 + idx + offs;
|
addr = idx + offs;
|
||||||
PVR2_DECOMPOSE_LE(hdw->cmd_buffer, 1+(idx*7),
|
hdw->cmd_buffer[bAddr+6] = (addr & 0xffu);
|
||||||
data[idx]);
|
hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu);
|
||||||
|
hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu);
|
||||||
|
PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]);
|
||||||
|
bAddr += 7;
|
||||||
}
|
}
|
||||||
ret = pvr2_send_request(hdw,
|
ret = pvr2_send_request(hdw,
|
||||||
hdw->cmd_buffer,1+(chunkCnt*7),
|
hdw->cmd_buffer,1+(chunkCnt*7),
|
||||||
@ -77,34 +84,42 @@ static int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,int statusFl,
|
static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,
|
||||||
|
unsigned int offs,
|
||||||
u32 *data, unsigned int dlen)
|
u32 *data, unsigned int dlen)
|
||||||
{
|
{
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned int offs = 0;
|
|
||||||
unsigned int chunkCnt;
|
unsigned int chunkCnt;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Format: First byte must be 0x02 (status check) or 0x28 (read
|
Format: First byte must be 0x02 (status check) or 0x28 (read
|
||||||
back block of 32 bit words). Next 6 bytes must be zero,
|
back block of 32 bit words). Next 6 bytes must be zero,
|
||||||
followed by a single byte of 0x44+offset for portion to be
|
followed by a single byte of MBOX_BASE+offset for portion to
|
||||||
read. Returned data is packed set of 32 bits words that were
|
be read. Returned data is packed set of 32 bits words that
|
||||||
read.
|
were read.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
while (dlen) {
|
while (dlen) {
|
||||||
chunkCnt = 16;
|
chunkCnt = 16;
|
||||||
if (chunkCnt > dlen) chunkCnt = dlen;
|
if (chunkCnt > dlen) chunkCnt = dlen;
|
||||||
memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
|
if (chunkCnt < 16) chunkCnt = 1;
|
||||||
hdw->cmd_buffer[0] =
|
hdw->cmd_buffer[0] =
|
||||||
(statusFl ? FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES);
|
((chunkCnt == 1) ?
|
||||||
hdw->cmd_buffer[7] = 0x44 + offs;
|
FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES);
|
||||||
|
hdw->cmd_buffer[1] = 0;
|
||||||
|
hdw->cmd_buffer[2] = 0;
|
||||||
|
hdw->cmd_buffer[3] = 0;
|
||||||
|
hdw->cmd_buffer[4] = 0;
|
||||||
|
hdw->cmd_buffer[5] = ((offs>>16) & 0xffu);
|
||||||
|
hdw->cmd_buffer[6] = ((offs>>8) & 0xffu);
|
||||||
|
hdw->cmd_buffer[7] = (offs & 0xffu);
|
||||||
ret = pvr2_send_request(hdw,
|
ret = pvr2_send_request(hdw,
|
||||||
hdw->cmd_buffer,8,
|
hdw->cmd_buffer,8,
|
||||||
hdw->cmd_buffer,chunkCnt * 4);
|
hdw->cmd_buffer,
|
||||||
|
(chunkCnt == 1 ? 4 : 16 * 4));
|
||||||
if (ret) return ret;
|
if (ret) return ret;
|
||||||
|
|
||||||
for (idx = 0; idx < chunkCnt; idx++) {
|
for (idx = 0; idx < chunkCnt; idx++) {
|
||||||
@ -131,6 +146,8 @@ static int pvr2_encoder_cmd(void *ctxt,
|
|||||||
u32 *argp)
|
u32 *argp)
|
||||||
{
|
{
|
||||||
unsigned int poll_count;
|
unsigned int poll_count;
|
||||||
|
unsigned int try_count = 0;
|
||||||
|
int retry_flag;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
/* These sizes look to be limited by the FX2 firmware implementation */
|
/* These sizes look to be limited by the FX2 firmware implementation */
|
||||||
@ -142,14 +159,15 @@ static int pvr2_encoder_cmd(void *ctxt,
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
The encoder seems to speak entirely using blocks 32 bit words.
|
The encoder seems to speak entirely using blocks 32 bit words.
|
||||||
In ivtv driver terms, this is a mailbox which we populate with
|
In ivtv driver terms, this is a mailbox at MBOX_BASE which we
|
||||||
data and watch what the hardware does with it. The first word
|
populate with data and watch what the hardware does with it.
|
||||||
is a set of flags used to control the transaction, the second
|
The first word is a set of flags used to control the
|
||||||
word is the command to execute, the third byte is zero (ivtv
|
transaction, the second word is the command to execute, the
|
||||||
driver suggests that this is some kind of return value), and
|
third byte is zero (ivtv driver suggests that this is some
|
||||||
the fourth byte is a specified timeout (windows driver always
|
kind of return value), and the fourth byte is a specified
|
||||||
uses 0x00060000 except for one case when it is zero). All
|
timeout (windows driver always uses 0x00060000 except for one
|
||||||
successive words are the argument words for the command.
|
case when it is zero). All successive words are the argument
|
||||||
|
words for the command.
|
||||||
|
|
||||||
First, write out the entire set of words, with the first word
|
First, write out the entire set of words, with the first word
|
||||||
being zero.
|
being zero.
|
||||||
@ -158,13 +176,10 @@ static int pvr2_encoder_cmd(void *ctxt,
|
|||||||
IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which
|
IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which
|
||||||
probably means "go").
|
probably means "go").
|
||||||
|
|
||||||
Next, read back 16 words as status. Check the first word,
|
Next, read back the return count words. Check the first word,
|
||||||
which should have IVTV_MBOX_FIRMWARE_DONE set. If however
|
which should have IVTV_MBOX_FIRMWARE_DONE set. If however
|
||||||
that bit is not set, then the command isn't done so repeat the
|
that bit is not set, then the command isn't done so repeat the
|
||||||
read.
|
read until it is set.
|
||||||
|
|
||||||
Next, read back 32 words and compare with the original
|
|
||||||
arugments. Hopefully they will match.
|
|
||||||
|
|
||||||
Finally, write out just the first word again, but set it to
|
Finally, write out just the first word again, but set it to
|
||||||
0x0 this time (which probably means "idle").
|
0x0 this time (which probably means "idle").
|
||||||
@ -194,6 +209,9 @@ static int pvr2_encoder_cmd(void *ctxt,
|
|||||||
|
|
||||||
LOCK_TAKE(hdw->ctl_lock); do {
|
LOCK_TAKE(hdw->ctl_lock); do {
|
||||||
|
|
||||||
|
retry_flag = 0;
|
||||||
|
try_count++;
|
||||||
|
ret = 0;
|
||||||
wrData[0] = 0;
|
wrData[0] = 0;
|
||||||
wrData[1] = cmd;
|
wrData[1] = cmd;
|
||||||
wrData[2] = 0;
|
wrData[2] = 0;
|
||||||
@ -205,54 +223,70 @@ static int pvr2_encoder_cmd(void *ctxt,
|
|||||||
wrData[idx+4] = 0;
|
wrData[idx+4] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = pvr2_encoder_write_words(hdw,wrData,idx);
|
ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx);
|
||||||
if (ret) break;
|
if (ret) break;
|
||||||
wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
|
wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
|
||||||
ret = pvr2_encoder_write_words(hdw,wrData,1);
|
ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1);
|
||||||
if (ret) break;
|
if (ret) break;
|
||||||
poll_count = 0;
|
poll_count = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
if (poll_count < 10000000) poll_count++;
|
poll_count++;
|
||||||
ret = pvr2_encoder_read_words(hdw,!0,rdData,1);
|
ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData,
|
||||||
if (ret) break;
|
arg_cnt_recv+4);
|
||||||
|
if (ret) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
|
if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (poll_count == 100) {
|
if (rdData[0] && (poll_count < 1000)) continue;
|
||||||
|
if (!rdData[0]) {
|
||||||
|
retry_flag = !0;
|
||||||
|
pvr2_trace(
|
||||||
|
PVR2_TRACE_ERROR_LEGS,
|
||||||
|
"Encoder timed out waiting for us"
|
||||||
|
"; arranging to retry");
|
||||||
|
} else {
|
||||||
pvr2_trace(
|
pvr2_trace(
|
||||||
PVR2_TRACE_ERROR_LEGS,
|
PVR2_TRACE_ERROR_LEGS,
|
||||||
"***WARNING*** device's encoder"
|
"***WARNING*** device's encoder"
|
||||||
" appears to be stuck"
|
" appears to be stuck"
|
||||||
" (status=0%08x)",rdData[0]);
|
" (status=0%08x)",rdData[0]);
|
||||||
pvr2_trace(
|
|
||||||
PVR2_TRACE_ERROR_LEGS,
|
|
||||||
"Encoder command: 0x%02x",cmd);
|
|
||||||
for (idx = 4; idx < arg_cnt_send; idx++) {
|
|
||||||
pvr2_trace(
|
|
||||||
PVR2_TRACE_ERROR_LEGS,
|
|
||||||
"Encoder arg%d: 0x%08x",
|
|
||||||
idx-3,wrData[idx]);
|
|
||||||
}
|
|
||||||
pvr2_trace(
|
|
||||||
PVR2_TRACE_ERROR_LEGS,
|
|
||||||
"Giving up waiting."
|
|
||||||
" It is likely that"
|
|
||||||
" this is a bad idea...");
|
|
||||||
ret = -EBUSY;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
pvr2_trace(
|
||||||
|
PVR2_TRACE_ERROR_LEGS,
|
||||||
|
"Encoder command: 0x%02x",cmd);
|
||||||
|
for (idx = 4; idx < arg_cnt_send; idx++) {
|
||||||
|
pvr2_trace(
|
||||||
|
PVR2_TRACE_ERROR_LEGS,
|
||||||
|
"Encoder arg%d: 0x%08x",
|
||||||
|
idx-3,wrData[idx]);
|
||||||
|
}
|
||||||
|
ret = -EBUSY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (retry_flag) {
|
||||||
|
if (try_count < 20) continue;
|
||||||
|
pvr2_trace(
|
||||||
|
PVR2_TRACE_ERROR_LEGS,
|
||||||
|
"Too many retries...");
|
||||||
|
ret = -EBUSY;
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
|
pvr2_trace(
|
||||||
|
PVR2_TRACE_ERROR_LEGS,
|
||||||
|
"Giving up on command."
|
||||||
|
" It is likely that"
|
||||||
|
" this is a bad idea...");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (ret) break;
|
|
||||||
wrData[0] = 0x7;
|
wrData[0] = 0x7;
|
||||||
ret = pvr2_encoder_read_words(
|
|
||||||
hdw,0,rdData, ARRAY_SIZE(rdData));
|
|
||||||
if (ret) break;
|
|
||||||
for (idx = 0; idx < arg_cnt_recv; idx++) {
|
for (idx = 0; idx < arg_cnt_recv; idx++) {
|
||||||
argp[idx] = rdData[idx+4];
|
argp[idx] = rdData[idx+4];
|
||||||
}
|
}
|
||||||
|
|
||||||
wrData[0] = 0x0;
|
wrData[0] = 0x0;
|
||||||
ret = pvr2_encoder_write_words(hdw,wrData,1);
|
ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1);
|
||||||
if (ret) break;
|
if (ret) break;
|
||||||
|
|
||||||
} while(0); LOCK_GIVE(hdw->ctl_lock);
|
} while(0); LOCK_GIVE(hdw->ctl_lock);
|
||||||
|
Loading…
Reference in New Issue
Block a user