📄 dv1394.c
字号:
sizeof(struct CIP_header), /* data size */
cip_dma);
/* third (and possibly fourth) descriptor - for DV data */
/* the 480-byte payload can cross a page boundary; if so,
we need to split it into two DMA descriptors */
/* does the 480-byte data payload cross a page boundary? */
if ( (PAGE_SIZE- ((unsigned long)data_p % PAGE_SIZE) ) < 480 ) {
/* page boundary crossed */
fill_output_more( &(block->u.out.u.full.u.cross.om),
/* data size - how much of data_p fits on the first page */
PAGE_SIZE - (data_p % PAGE_SIZE),
/* DMA address of data_p */
dma_region_offset_to_bus(&video->dv_buf,
data_p - (unsigned long) video->dv_buf.kvirt));
fill_output_last( &(block->u.out.u.full.u.cross.ol),
/* want completion status on all interesting packets */
(first_packet || mid_packet || last_packet) ? 1 : 0,
/* want interrupt on all interesting packets */
(first_packet || mid_packet || last_packet) ? 1 : 0,
/* data size - remaining portion of data_p */
480 - (PAGE_SIZE - (data_p % PAGE_SIZE)),
/* DMA address of data_p + PAGE_SIZE - (data_p % PAGE_SIZE) */
dma_region_offset_to_bus(&video->dv_buf,
data_p + PAGE_SIZE - (data_p % PAGE_SIZE) - (unsigned long) video->dv_buf.kvirt));
if (first_packet)
f->frame_begin_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
else if (mid_packet)
f->mid_frame_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
else if (last_packet) {
f->frame_end_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
f->frame_end_branch = &(block->u.out.u.full.u.cross.ol.q[2]);
}
branch_address = &(block->u.out.u.full.u.cross.ol.q[2]);
n_descriptors = 5;
if (first_packet)
f->first_n_descriptors = n_descriptors;
full_packets++;
} else {
/* fits on one page */
fill_output_last( &(block->u.out.u.full.u.nocross.ol),
/* want completion status on all interesting packets */
(first_packet || mid_packet || last_packet) ? 1 : 0,
/* want interrupt on all interesting packets */
(first_packet || mid_packet || last_packet) ? 1 : 0,
480, /* data size (480 bytes of DV data) */
/* DMA address of data_p */
dma_region_offset_to_bus(&video->dv_buf,
data_p - (unsigned long) video->dv_buf.kvirt));
if (first_packet)
f->frame_begin_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
else if (mid_packet)
f->mid_frame_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
else if (last_packet) {
f->frame_end_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
f->frame_end_branch = &(block->u.out.u.full.u.nocross.ol.q[2]);
}
branch_address = &(block->u.out.u.full.u.nocross.ol.q[2]);
n_descriptors = 4;
if (first_packet)
f->first_n_descriptors = n_descriptors;
full_packets++;
}
}
/* link this descriptor block into the DMA program by filling in
the branch address of the previous block */
/* note: we are not linked into the active DMA chain yet */
if (last_branch_address) {
*(last_branch_address) = cpu_to_le32(block_dma | n_descriptors);
}
last_branch_address = branch_address;
f->n_packets++;
}
/* when we first assemble a new frame, set the final branch
to loop back up to the top */
*(f->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors);
/* make the latest version of this frame visible to the PCI card */
dma_region_sync_for_device(&video->dv_buf, f->data - (unsigned long) video->dv_buf.kvirt, video->frame_size);
/* lock against DMA interrupt */
spin_lock_irqsave(&video->spinlock, irq_flags);
f->state = FRAME_READY;
video->n_clear_frames--;
last_frame = video->first_clear_frame - 1;
if (last_frame == -1)
last_frame = video->n_frames-1;
video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
irq_printk(" frame %d prepared, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n last=%d\n",
this_frame, video->active_frame, video->n_clear_frames, video->first_clear_frame, last_frame);
irq_printk(" begin_ts %08lx mid_ts %08lx end_ts %08lx end_br %08lx\n",
(unsigned long) f->frame_begin_timestamp,
(unsigned long) f->mid_frame_timestamp,
(unsigned long) f->frame_end_timestamp,
(unsigned long) f->frame_end_branch);
if (video->active_frame != -1) {
/* if DMA is already active, we are almost done */
/* just link us onto the active DMA chain */
if (video->frames[last_frame]->frame_end_branch) {
u32 temp;
/* point the previous frame's tail to this frame's head */
*(video->frames[last_frame]->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors);
/* this write MUST precede the next one, or we could silently drop frames */
wmb();
/* disable the want_status semaphore on the last packet */
temp = le32_to_cpu(*(video->frames[last_frame]->frame_end_branch - 2));
temp &= 0xF7CFFFFF;
*(video->frames[last_frame]->frame_end_branch - 2) = cpu_to_le32(temp);
/* flush these writes to memory ASAP */
flush_pci_write(video->ohci);
/* NOTE:
ideally the writes should be "atomic": if
the OHCI card reads the want_status flag in
between them, we'll falsely report a
dropped frame. Hopefully this window is too
small to really matter, and the consequence
is rather harmless. */
irq_printk(" new frame %d linked onto DMA chain\n", this_frame);
} else {
printk(KERN_ERR "dv1394: last frame not ready???\n");
}
} else {
u32 transmit_sec, transmit_cyc;
u32 ts_cyc, ts_off;
/* DMA is stopped, so this is the very first frame */
video->active_frame = this_frame;
/* set CommandPtr to address and size of first descriptor block */
reg_write(video->ohci, video->ohci_IsoXmitCommandPtr,
video->frames[video->active_frame]->descriptor_pool_dma |
f->first_n_descriptors);
/* assign a timestamp based on the current cycle time...
We'll tell the card to begin DMA 100 cycles from now,
and assign a timestamp 103 cycles from now */
cycleTimer = reg_read(video->ohci, OHCI1394_IsochronousCycleTimer);
ct_sec = cycleTimer >> 25;
ct_cyc = (cycleTimer >> 12) & 0x1FFF;
ct_off = cycleTimer & 0xFFF;
transmit_sec = ct_sec;
transmit_cyc = ct_cyc + 100;
transmit_sec += transmit_cyc/8000;
transmit_cyc %= 8000;
ts_off = ct_off;
ts_cyc = transmit_cyc + 3;
ts_cyc %= 8000;
f->assigned_timestamp = (ts_cyc&0xF) << 12;
/* now actually write the timestamp into the appropriate CIP headers */
if (f->cip_syt1) {
f->cip_syt1->b[6] = f->assigned_timestamp >> 8;
f->cip_syt1->b[7] = f->assigned_timestamp & 0xFF;
}
if (f->cip_syt2) {
f->cip_syt2->b[6] = f->assigned_timestamp >> 8;
f->cip_syt2->b[7] = f->assigned_timestamp & 0xFF;
}
/* --- start DMA --- */
/* clear all bits in ContextControl register */
reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, 0xFFFFFFFF);
wmb();
/* the OHCI card has the ability to start ISO transmission on a
particular cycle (start-on-cycle). This way we can ensure that
the first DV frame will have an accurate timestamp.
However, start-on-cycle only appears to work if the OHCI card
is cycle master! Since the consequences of messing up the first
timestamp are minimal*, just disable start-on-cycle for now.
* my DV deck drops the first few frames before it "locks in;"
so the first frame having an incorrect timestamp is inconsequential.
*/
#if 0
reg_write(video->ohci, video->ohci_IsoXmitContextControlSet,
(1 << 31) /* enable start-on-cycle */
| ( (transmit_sec & 0x3) << 29)
| (transmit_cyc << 16));
wmb();
#endif
video->dma_running = 1;
/* set the 'run' bit */
reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, 0x8000);
flush_pci_write(video->ohci);
/* --- DMA should be running now --- */
debug_printk(" Cycle = %4u ContextControl = %08x CmdPtr = %08x\n",
(reg_read(video->ohci, OHCI1394_IsochronousCycleTimer) >> 12) & 0x1FFF,
reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
reg_read(video->ohci, video->ohci_IsoXmitCommandPtr));
debug_printk(" DMA start - current cycle %4u, transmit cycle %4u (%2u), assigning ts cycle %2u\n",
ct_cyc, transmit_cyc, transmit_cyc & 0xF, ts_cyc & 0xF);
#if DV1394_DEBUG_LEVEL >= 2
{
/* check if DMA is really running */
int i = 0;
while (i < 20) {
mb();
mdelay(1);
if (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) {
printk("DMA ACTIVE after %d msec\n", i);
break;
}
i++;
}
printk("set = %08x, cmdPtr = %08x\n",
reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)
);
if ( ! (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) {
printk("DMA did NOT go active after 20ms, event = %x\n",
reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & 0x1F);
} else
printk("DMA is RUNNING!\n");
}
#endif
}
spin_unlock_irqrestore(&video->spinlock, irq_flags);
}
/*** RECEIVE FUNCTIONS *****************************************************/
/*
frame method put_packet
map and copy the packet data to its location in the frame
based upon DIF section and sequence
*/
static void inline
frame_put_packet (struct frame *f, struct packet *p)
{
int section_type = p->data[0] >> 5; /* section type is in bits 5 - 7 */
int dif_sequence = p->data[1] >> 4; /* dif sequence number is in bits 4 - 7 */
int dif_block = p->data[2];
/* sanity check */
if (dif_sequence > 11 || dif_block > 149) return;
switch (section_type) {
case 0: /* 1 Header block */
memcpy( (void *) f->data + dif_sequence * 150 * 80, p->data, 480);
break;
case 1: /* 2 Subcode blocks */
memcpy( (void *) f->data + dif_sequence * 150 * 80 + (1 + dif_block) * 80, p->data, 480);
break;
case 2: /* 3 VAUX blocks */
memcpy( (void *) f->data + dif_sequence * 150 * 80 + (3 + dif_block) * 80, p->data, 480);
break;
case 3: /* 9 Audio blocks interleaved with video */
memcpy( (void *) f->data + dif_sequence * 150 * 80 + (6 + dif_block * 16) * 80, p->data, 480);
break;
case 4: /* 135 Video blocks interleaved with audio */
memcpy( (void *) f->data + dif_sequence * 150 * 80 + (7 + (dif_block / 15) + dif_block) * 80, p->data, 480);
break;
default: /* we can not handle any other data */
break;
}
}
static void start_dma_receive(struct video_card *video)
{
if (video->first_run == 1) {
video->first_run = 0;
/* start DMA once all of the frames are READY */
video->n_clear_frames = 0;
video->first_clear_frame = -1;
video->current_packet = 0;
video->active_frame = 0;
/* reset iso recv control register */
reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, 0xFFFFFFFF);
wmb();
/* clear bufferFill, set isochHeader and speed (0=100) */
reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x40000000);
/* match on all tags, listen on channel */
reg_write(video->ohci, video->ohci_IsoRcvContextMatch, 0xf0000000 | video->channel);
/* address and first descriptor block + Z=1 */
reg_write(video->ohci, video->ohci_IsoRcvCommandPtr,
video->frames[0]->descriptor_pool_dma | 1); /* Z=1 */
wmb();
video->dma_running = 1;
/* run */
reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x8000);
flush_pci_write(video->ohci);
debug_printk("dv1394: DMA started\n");
#if DV1394_DEBUG_LEVEL >= 2
{
int i;
for (i = 0; i < 1000; ++i) {
mdelay(1);
if (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) {
printk("DMA ACTIVE after %d msec\n", i);
break;
}
}
if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) {
printk("DEAD, event = %x\n",
reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F);
} else
printk("RUNNING!\n");
}
#endif
} else if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) {
debug_printk("DEAD, event = %x\n",
reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F);
/* wake */
reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12));
}
}
/*
receive_packets() - build the DMA program for receiving
*/
static void receive_packets(struct video_card *video)
{
struct DMA_descriptor_block *block = NULL;
dma_addr_t block_dma = 0;
struct packet *data = NULL;
dma_addr_t data_dma = 0;
u32 *last_branch_address = NULL;
unsigned long irq_flags;
int want_interrupt = 0;
struct frame *f = NULL;
int i, j;
spin_lock_irqsave(&video->spinlock, irq_flags);
for (j = 0; j < video->n_frames; j++) {
/* connect frames */
if (j > 0 && f != NULL && f->frame_end_branch != NULL)
*(f->frame_end_branch) = cpu_to_le32(video->frames[j]->descriptor_pool_dma | 1); /* set Z=1 */
f = video->frames[j];
for (i = 0; i < MAX_PACKETS; i++) {
/* locate a descriptor block and packet from the buffer */
block = &(f->descriptor_pool[i]);
block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
data = ((struct packet*)video->packet_buf.kvirt) + f->frame_num * MAX_PACKETS + i;
data_dma = dma_region_offset_to_bus( &video->packet_buf,
((unsigned long) data - (unsigned long) video->packet_buf.kvirt) );
/* setup DMA descriptor block */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -