📄 dv1394.c
字号:
/* 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(&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 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]; /* 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 */ want_interrupt = ((i % (MAX_PACKETS/2)) == 0 || i == (MAX_PACKETS-1)); fill_input_last( &(block->u.in.il), want_interrupt, 512, data_dma); /* link descriptors */ last_branch_address = f->frame_end_branch; if (last_branch_address != NULL) *(last_branch_address) = cpu_to_le32(block_dma | 1); /* set Z=1 */ f->frame_end_branch = &(block->u.in.il.q[2]); } } /* next j */ spin_unlock_irqrestore(&video->spinlock, irq_flags);}/*** MANAGEMENT FUNCTIONS **************************************************/static int do_dv1394_init(struct video_card *video, struct dv1394_init *init){ unsigned long flags, new_buf_size; int i; u64 chan_mask; int retval = -EINVAL; debug_printk("dv1394: initialising %d\n", video->id);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -