⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dv1394.c

📁 Ieee1394驱动实现
💻 C
📖 第 1 页 / 共 5 页
字号:
					  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 + -