📄 dv1394.c
字号:
sizeof(struct CIP_header), /* data size */ cip_dma); if(first_packet) f->frame_begin_timestamp = &(block->u.out.u.empty.ol.q[3]); else if(mid_packet) f->mid_frame_timestamp = &(block->u.out.u.empty.ol.q[3]); else if(last_packet) { f->frame_end_timestamp = &(block->u.out.u.empty.ol.q[3]); f->frame_end_branch = &(block->u.out.u.empty.ol.q[2]); } branch_address = &(block->u.out.u.empty.ol.q[2]); n_descriptors = 3; if(first_packet) f->first_n_descriptors = n_descriptors; } else { /* full packet */ /* second descriptor - OUTPUT_MORE for CIP header */ fill_output_more( &(block->u.out.u.full.om), 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_offset_to_bus(&f->video->user_dma, data_p - (unsigned long) f->video->user_buf)); 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_offset_to_bus(&f->video->user_dma, data_p + PAGE_SIZE - (data_p % PAGE_SIZE) - (unsigned long) f->video->user_buf)); 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_offset_to_bus(&f->video->user_dma, data_p - (unsigned long) f->video->user_buf)); 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 the frame buffer visible to the PCI card */ /* could optimize this by only syncing the pages associated with this frame */ pci_dma_sync_sg(video->ohci->dev, &video->user_dma.sglist[0], video->user_dma.n_dma_pages, PCI_DMA_TODEVICE); /* 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 /* 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 inlineframe_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]; 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, struct frame *frame){ /* 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, frame->descriptor_pool_dma | 1); /* Z=1 */ wmb(); /* 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}/* receive_packets() - build the DMA program for receiving*/static void receive_packets(struct video_card *video, struct frame *f){ 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; spin_lock_irqsave(&video->spinlock, irq_flags); video->n_clear_frames = 0; video->first_clear_frame = -1; for (video->current_packet = 0; video->current_packet < MAX_PACKET_BUFFER; ++video->current_packet) { /* locate a descriptor block and packet from the buffer */ block = &(f->descriptor_pool[video->current_packet]); block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; data = &(video->packet_buffer[video->current_packet]); data_dma = ((unsigned long) data - (unsigned long) video->packet_buffer) + video->packet_buffer_dma; /* setup DMA descriptor block */ fill_input_last( &(block->u.in.il), 512, data_dma); /* link descriptors */ last_branch_address = f->frame_end_branch; if (last_branch_address) *(last_branch_address) = cpu_to_le32(block_dma | 1); /* set Z=1 */ f->frame_end_branch = &(block->u.in.il.q[2]); } /* loop tail to head */ if (f->frame_end_branch) *(f->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | 1); /* set Z=1 */ spin_unlock_irqrestore(&video->spinlock, irq_flags); if (video->first_run) { /* start DMA once all of the frames are READY */ video->first_run = 0; video->current_packet = 0; video->active_frame = f->frame_num; start_dma_receive(video, f); } 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)); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -