summaryrefslogtreecommitdiffstats
path: root/drivers/ieee1394/dv1394.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/ieee1394/dv1394.c
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/ieee1394/dv1394.c')
-rw-r--r--drivers/ieee1394/dv1394.c2663
1 files changed, 2663 insertions, 0 deletions
diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c
new file mode 100644
index 000000000000..68c7a5f07842
--- /dev/null
+++ b/drivers/ieee1394/dv1394.c
@@ -0,0 +1,2663 @@
+/*
+ * dv1394.c - DV input/output over IEEE 1394 on OHCI chips
+ * Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ * receive by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ * video1394.c - video driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ OVERVIEW
+
+ I designed dv1394 as a "pipe" that you can use to shoot DV onto a
+ FireWire bus. In transmission mode, dv1394 does the following:
+
+ 1. accepts contiguous frames of DV data from user-space, via write()
+ or mmap() (see dv1394.h for the complete API)
+ 2. wraps IEC 61883 packets around the DV data, inserting
+ empty synchronization packets as necessary
+ 3. assigns accurate SYT timestamps to the outgoing packets
+ 4. shoots them out using the OHCI card's IT DMA engine
+
+ Thanks to Dan Dennedy, we now have a receive mode that does the following:
+
+ 1. accepts raw IEC 61883 packets from the OHCI card
+ 2. re-assembles the DV data payloads into contiguous frames,
+ discarding empty packets
+ 3. sends the DV data to user-space via read() or mmap()
+*/
+
+/*
+ TODO:
+
+ - tunable frame-drop behavior: either loop last frame, or halt transmission
+
+ - use a scatter/gather buffer for DMA programs (f->descriptor_pool)
+ so that we don't rely on allocating 64KB of contiguous kernel memory
+ via pci_alloc_consistent()
+
+ DONE:
+ - during reception, better handling of dropped frames and continuity errors
+ - during reception, prevent DMA from bypassing the irq tasklets
+ - reduce irq rate during reception (1/250 packets).
+ - add many more internal buffers during reception with scatter/gather dma.
+ - add dbc (continuity) checking on receive, increment status.dropped_frames
+ if not continuous.
+ - restart IT DMA after a bus reset
+ - safely obtain and release ISO Tx channels in cooperation with OHCI driver
+ - map received DIF blocks to their proper location in DV frame (ensure
+ recovery if dropped packet)
+ - handle bus resets gracefully (OHCI card seems to take care of this itself(!))
+ - do not allow resizing the user_buf once allocated; eliminate nuke_buffer_mappings
+ - eliminated #ifdef DV1394_DEBUG_LEVEL by inventing macros debug_printk and irq_printk
+ - added wmb() and mb() to places where PCI read/write ordering needs to be enforced
+ - set video->id correctly
+ - store video_cards in an array indexed by OHCI card ID, rather than a list
+ - implement DMA context allocation to cooperate with other users of the OHCI
+ - fix all XXX showstoppers
+ - disable IR/IT DMA interrupts on shutdown
+ - flush pci writes to the card by issuing a read
+ - devfs and character device dispatching (* needs testing with Linux 2.2.x)
+ - switch over to the new kernel DMA API (pci_map_*()) (* needs testing on platforms with IOMMU!)
+ - keep all video_cards in a list (for open() via chardev), set file->private_data = video
+ - dv1394_poll should indicate POLLIN when receiving buffers are available
+ - add proc fs interface to set cip_n, cip_d, syt_offset, and video signal
+ - expose xmit and recv as separate devices (not exclusive)
+ - expose NTSC and PAL as separate devices (can be overridden)
+
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/bitops.h>
+#include <asm/byteorder.h>
+#include <asm/atomic.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+#include <linux/ioctl32.h>
+#include <linux/compat.h>
+#include <linux/cdev.h>
+
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "nodemgr.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"
+#include "dv1394.h"
+#include "dv1394-private.h"
+
+#include "ohci1394.h"
+
+#ifndef virt_to_page
+#define virt_to_page(x) MAP_NR(x)
+#endif
+
+#ifndef vmalloc_32
+#define vmalloc_32(x) vmalloc(x)
+#endif
+
+
+/* DEBUG LEVELS:
+ 0 - no debugging messages
+ 1 - some debugging messages, but none during DMA frame transmission
+ 2 - lots of messages, including during DMA frame transmission
+ (will cause undeflows if your machine is too slow!)
+*/
+
+#define DV1394_DEBUG_LEVEL 0
+
+/* for debugging use ONLY: allow more than one open() of the device */
+/* #define DV1394_ALLOW_MORE_THAN_ONE_OPEN 1 */
+
+#if DV1394_DEBUG_LEVEL >= 2
+#define irq_printk( args... ) printk( args )
+#else
+#define irq_printk( args... )
+#endif
+
+#if DV1394_DEBUG_LEVEL >= 1
+#define debug_printk( args... ) printk( args)
+#else
+#define debug_printk( args... )
+#endif
+
+/* issue a dummy PCI read to force the preceding write
+ to be posted to the PCI bus immediately */
+
+static inline void flush_pci_write(struct ti_ohci *ohci)
+{
+ mb();
+ reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+}
+
+static void it_tasklet_func(unsigned long data);
+static void ir_tasklet_func(unsigned long data);
+
+#ifdef CONFIG_COMPAT
+static long dv1394_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+#endif
+
+/* GLOBAL DATA */
+
+/* list of all video_cards */
+static LIST_HEAD(dv1394_cards);
+static DEFINE_SPINLOCK(dv1394_cards_lock);
+
+/* translate from a struct file* to the corresponding struct video_card* */
+
+static inline struct video_card* file_to_video_card(struct file *file)
+{
+ return (struct video_card*) file->private_data;
+}
+
+/*** FRAME METHODS *********************************************************/
+
+static void frame_reset(struct frame *f)
+{
+ f->state = FRAME_CLEAR;
+ f->done = 0;
+ f->n_packets = 0;
+ f->frame_begin_timestamp = NULL;
+ f->assigned_timestamp = 0;
+ f->cip_syt1 = NULL;
+ f->cip_syt2 = NULL;
+ f->mid_frame_timestamp = NULL;
+ f->frame_end_timestamp = NULL;
+ f->frame_end_branch = NULL;
+}
+
+static struct frame* frame_new(unsigned int frame_num, struct video_card *video)
+{
+ struct frame *f = kmalloc(sizeof(*f), GFP_KERNEL);
+ if (!f)
+ return NULL;
+
+ f->video = video;
+ f->frame_num = frame_num;
+
+ f->header_pool = pci_alloc_consistent(f->video->ohci->dev, PAGE_SIZE, &f->header_pool_dma);
+ if (!f->header_pool) {
+ printk(KERN_ERR "dv1394: failed to allocate CIP header pool\n");
+ kfree(f);
+ return NULL;
+ }
+
+ debug_printk("dv1394: frame_new: allocated CIP header pool at virt 0x%08lx (contig) dma 0x%08lx size %ld\n",
+ (unsigned long) f->header_pool, (unsigned long) f->header_pool_dma, PAGE_SIZE);
+
+ f->descriptor_pool_size = MAX_PACKETS * sizeof(struct DMA_descriptor_block);
+ /* make it an even # of pages */
+ f->descriptor_pool_size += PAGE_SIZE - (f->descriptor_pool_size%PAGE_SIZE);
+
+ f->descriptor_pool = pci_alloc_consistent(f->video->ohci->dev,
+ f->descriptor_pool_size,
+ &f->descriptor_pool_dma);
+ if (!f->descriptor_pool) {
+ pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma);
+ kfree(f);
+ return NULL;
+ }
+
+ debug_printk("dv1394: frame_new: allocated DMA program memory at virt 0x%08lx (contig) dma 0x%08lx size %ld\n",
+ (unsigned long) f->descriptor_pool, (unsigned long) f->descriptor_pool_dma, f->descriptor_pool_size);
+
+ f->data = 0;
+ frame_reset(f);
+
+ return f;
+}
+
+static void frame_delete(struct frame *f)
+{
+ pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma);
+ pci_free_consistent(f->video->ohci->dev, f->descriptor_pool_size, f->descriptor_pool, f->descriptor_pool_dma);
+ kfree(f);
+}
+
+
+
+
+/*
+ frame_prepare() - build the DMA program for transmitting
+
+ Frame_prepare() must be called OUTSIDE the video->spinlock.
+ However, frame_prepare() must still be serialized, so
+ it should be called WITH the video->sem taken.
+ */
+
+static void frame_prepare(struct video_card *video, unsigned int this_frame)
+{
+ struct frame *f = video->frames[this_frame];
+ int last_frame;
+
+ struct DMA_descriptor_block *block;
+ dma_addr_t block_dma;
+ struct CIP_header *cip;
+ dma_addr_t cip_dma;
+
+ unsigned int n_descriptors, full_packets, packets_per_frame, payload_size;
+
+ /* these flags denote packets that need special attention */
+ int empty_packet, first_packet, last_packet, mid_packet;
+
+ u32 *branch_address, *last_branch_address = NULL;
+ unsigned long data_p;
+ int first_packet_empty = 0;
+ u32 cycleTimer, ct_sec, ct_cyc, ct_off;
+ unsigned long irq_flags;
+
+ irq_printk("frame_prepare( %d ) ---------------------\n", this_frame);
+
+ full_packets = 0;
+
+
+
+ if (video->pal_or_ntsc == DV1394_PAL)
+ packets_per_frame = DV1394_PAL_PACKETS_PER_FRAME;
+ else
+ packets_per_frame = DV1394_NTSC_PACKETS_PER_FRAME;
+
+ while ( full_packets < packets_per_frame ) {
+ empty_packet = first_packet = last_packet = mid_packet = 0;
+
+ data_p = f->data + full_packets * 480;
+
+ /************************************************/
+ /* allocate a descriptor block and a CIP header */
+ /************************************************/
+
+ /* note: these should NOT cross a page boundary (DMA restriction) */
+
+ if (f->n_packets >= MAX_PACKETS) {
+ printk(KERN_ERR "dv1394: FATAL ERROR: max packet count exceeded\n");
+ return;
+ }
+
+ /* the block surely won't cross a page boundary,
+ since an even number of descriptor_blocks fit on a page */
+ block = &(f->descriptor_pool[f->n_packets]);
+
+ /* DMA address of the block = offset of block relative
+ to the kernel base address of the descriptor pool
+ + DMA base address of the descriptor pool */
+ block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
+
+
+ /* the whole CIP pool fits on one page, so no worries about boundaries */
+ if ( ((unsigned long) &(f->header_pool[f->n_packets]) - (unsigned long) f->header_pool)
+ > PAGE_SIZE) {
+ printk(KERN_ERR "dv1394: FATAL ERROR: no room to allocate CIP header\n");
+ return;
+ }
+
+ cip = &(f->header_pool[f->n_packets]);
+
+ /* DMA address of the CIP header = offset of cip
+ relative to kernel base address of the header pool
+ + DMA base address of the header pool */
+ cip_dma = (unsigned long) cip % PAGE_SIZE + f->header_pool_dma;
+
+ /* is this an empty packet? */
+
+ if (video->cip_accum > (video->cip_d - video->cip_n)) {
+ empty_packet = 1;
+ payload_size = 8;
+ video->cip_accum -= (video->cip_d - video->cip_n);
+ } else {
+ payload_size = 488;
+ video->cip_accum += video->cip_n;
+ }
+
+ /* there are three important packets each frame:
+
+ the first packet in the frame - we ask the card to record the timestamp when
+ this packet is actually sent, so we can monitor
+ how accurate our timestamps are. Also, the first
+ packet serves as a semaphore to let us know that
+ it's OK to free the *previous* frame's DMA buffer
+
+ the last packet in the frame - this packet is used to detect buffer underflows.
+ if this is the last ready frame, the last DMA block
+ will have a branch back to the beginning of the frame
+ (so that the card will re-send the frame on underflow).
+ if this branch gets taken, we know that at least one
+ frame has been dropped. When the next frame is ready,
+ the branch is pointed to its first packet, and the
+ semaphore is disabled.
+
+ a "mid" packet slightly before the end of the frame - this packet should trigger
+ an interrupt so we can go and assign a timestamp to the first packet
+ in the next frame. We don't use the very last packet in the frame
+ for this purpose, because that would leave very little time to set
+ the timestamp before DMA starts on the next frame.
+ */
+
+ if (f->n_packets == 0) {
+ first_packet = 1;
+ } else if ( full_packets == (packets_per_frame-1) ) {
+ last_packet = 1;
+ } else if (f->n_packets == packets_per_frame) {
+ mid_packet = 1;
+ }
+
+
+ /********************/
+ /* setup CIP header */
+ /********************/
+
+ /* the timestamp will be written later from the
+ mid-frame interrupt handler. For now we just
+ store the address of the CIP header(s) that
+ need a timestamp. */
+
+ /* first packet in the frame needs a timestamp */
+ if (first_packet) {
+ f->cip_syt1 = cip;
+ if (empty_packet)
+ first_packet_empty = 1;
+
+ } else if (first_packet_empty && (f->n_packets == 1) ) {
+ /* if the first packet was empty, the second
+ packet's CIP header also needs a timestamp */
+ f->cip_syt2 = cip;
+ }
+
+ fill_cip_header(cip,
+ /* the node ID number of the OHCI card */
+ reg_read(video->ohci, OHCI1394_NodeID) & 0x3F,
+ video->continuity_counter,
+ video->pal_or_ntsc,
+ 0xFFFF /* the timestamp is filled in later */);
+
+ /* advance counter, only for full packets */
+ if ( ! empty_packet )
+ video->continuity_counter++;
+
+ /******************************/
+ /* setup DMA descriptor block */
+ /******************************/
+
+ /* first descriptor - OUTPUT_MORE_IMMEDIATE, for the controller's IT header */
+ fill_output_more_immediate( &(block->u.out.omi), 1, video->channel, 0, payload_size);
+
+ if (empty_packet) {
+ /* second descriptor - OUTPUT_LAST for CIP header */
+ fill_output_last( &(block->u.out.u.empty.ol),
+
+ /* want completion status on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ /* want interrupts on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ 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_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 */
+ 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);
+ if (init->api_version != DV1394_API_VERSION)
+ return -EINVAL;
+
+ /* first sanitize all the parameters */
+ if ( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) )
+ return -EINVAL;
+
+ if ( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) )
+ return -EINVAL;
+
+ if ( (init->syt_offset == 0) || (init->syt_offset > 50) )
+ /* default SYT offset is 3 cycles */
+ init->syt_offset = 3;
+
+ if ( (init->channel > 63) || (init->channel < 0) )
+ init->channel = 63;
+
+ chan_mask = (u64)1 << init->channel;
+
+ /* calculate what size DMA buffer is needed */
+ if (init->format == DV1394_NTSC)
+ new_buf_size = DV1394_NTSC_FRAME_SIZE * init->n_frames;
+ else
+ new_buf_size = DV1394_PAL_FRAME_SIZE * init->n_frames;
+
+ /* round up to PAGE_SIZE */
+ if (new_buf_size % PAGE_SIZE) new_buf_size += PAGE_SIZE - (new_buf_size % PAGE_SIZE);
+
+ /* don't allow the user to allocate the DMA buffer more than once */
+ if (video->dv_buf.kvirt && video->dv_buf_size != new_buf_size) {
+ printk("dv1394: re-sizing the DMA buffer is not allowed\n");
+ return -EINVAL;
+ }
+
+ /* shutdown the card if it's currently active */
+ /* (the card should not be reset if the parameters are screwy) */
+
+ do_dv1394_shutdown(video, 0);
+
+ /* try to claim the ISO channel */
+ spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
+ if (video->ohci->ISO_channel_usage & chan_mask) {
+ spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+ retval = -EBUSY;
+ goto err;
+ }
+ video->ohci->ISO_channel_usage |= chan_mask;
+ spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+
+ video->channel = init->channel;
+
+ /* initialize misc. fields of video */
+ video->n_frames = init->n_frames;
+ video->pal_or_ntsc = init->format;
+
+ video->cip_accum = 0;
+ video->continuity_counter = 0;
+
+ video->active_frame = -1;
+ video->first_clear_frame = 0;
+ video->n_clear_frames = video->n_frames;
+ video->dropped_frames = 0;
+
+ video->write_off = 0;
+
+ video->first_run = 1;
+ video->current_packet = -1;
+ video->first_frame = 0;
+
+ if (video->pal_or_ntsc == DV1394_NTSC) {
+ video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_NTSC;
+ video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_NTSC;
+ video->frame_size = DV1394_NTSC_FRAME_SIZE;
+ } else {
+ video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_PAL;
+ video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_PAL;
+ video->frame_size = DV1394_PAL_FRAME_SIZE;
+ }
+
+ video->syt_offset = init->syt_offset;
+
+ /* find and claim DMA contexts on the OHCI card */
+
+ if (video->ohci_it_ctx == -1) {
+ ohci1394_init_iso_tasklet(&video->it_tasklet, OHCI_ISO_TRANSMIT,
+ it_tasklet_func, (unsigned long) video);
+
+ if (ohci1394_register_iso_tasklet(video->ohci, &video->it_tasklet) < 0) {
+ printk(KERN_ERR "dv1394: could not find an available IT DMA context\n");
+ retval = -EBUSY;
+ goto err;
+ }
+
+ video->ohci_it_ctx = video->it_tasklet.context;
+ debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx);
+ }
+
+ if (video->ohci_ir_ctx == -1) {
+ ohci1394_init_iso_tasklet(&video->ir_tasklet, OHCI_ISO_RECEIVE,
+ ir_tasklet_func, (unsigned long) video);
+
+ if (ohci1394_register_iso_tasklet(video->ohci, &video->ir_tasklet) < 0) {
+ printk(KERN_ERR "dv1394: could not find an available IR DMA context\n");
+ retval = -EBUSY;
+ goto err;
+ }
+ video->ohci_ir_ctx = video->ir_tasklet.context;
+ debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx);
+ }
+
+ /* allocate struct frames */
+ for (i = 0; i < init->n_frames; i++) {
+ video->frames[i] = frame_new(i, video);
+
+ if (!video->frames[i]) {