ivtv-irq.c
来自「trident tm5600的linux驱动」· C语言 代码 · 共 999 行 · 第 1/3 页
C
999 行
/* interrupt handling Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> Copyright (C) 2004 Chris Kennedy <c@groovy.org> Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> 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 */#include "ivtv-driver.h"#include "ivtv-queue.h"#include "ivtv-udma.h"#include "ivtv-irq.h"#include "ivtv-mailbox.h"#include "ivtv-vbi.h"#include "ivtv-yuv.h"#define DMA_MAGIC_COOKIE 0x000001festatic void ivtv_dma_dec_start(struct ivtv_stream *s);static const int ivtv_stream_map[] = { IVTV_ENC_STREAM_TYPE_MPG, IVTV_ENC_STREAM_TYPE_YUV, IVTV_ENC_STREAM_TYPE_PCM, IVTV_ENC_STREAM_TYPE_VBI,};static void ivtv_pio_work_handler(struct ivtv *itv){ struct ivtv_stream *s = &itv->streams[itv->cur_pio_stream]; struct ivtv_buffer *buf; int i = 0; IVTV_DEBUG_HI_DMA("ivtv_pio_work_handler\n"); if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS || s->v4l2dev == NULL || !ivtv_use_pio(s)) { itv->cur_pio_stream = -1; /* trigger PIO complete user interrupt */ write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44); return; } IVTV_DEBUG_HI_DMA("Process PIO %s\n", s->name); list_for_each_entry(buf, &s->q_dma.list, list) { u32 size = s->sg_processing[i].size & 0x3ffff; /* Copy the data from the card to the buffer */ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) { memcpy_fromio(buf->buf, itv->dec_mem + s->sg_processing[i].src - IVTV_DECODER_OFFSET, size); } else { memcpy_fromio(buf->buf, itv->enc_mem + s->sg_processing[i].src, size); } i++; if (i == s->sg_processing_size) break; } write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);}#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)void ivtv_irq_work_handler(struct work_struct *work){ struct ivtv *itv = container_of(work, struct ivtv, irq_work_queue);#elsevoid ivtv_irq_work_handler(void *arg){ struct ivtv *itv = arg;#endif DEFINE_WAIT(wait); if (test_and_clear_bit(IVTV_F_I_WORK_INITED, &itv->i_flags)) { struct sched_param param = { .sched_priority = 99 }; /* This thread must use the FIFO scheduler as it is realtime sensitive. */ sched_setscheduler(current, SCHED_FIFO, ¶m); } if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags)) ivtv_pio_work_handler(itv); if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags)) ivtv_vbi_work_handler(itv); if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags)) ivtv_yuv_work_handler(itv);}/* Determine the required DMA size, setup enough buffers in the predma queue and actually copy the data from the card to the buffers in case a PIO transfer is required for this stream. */static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA]){ struct ivtv *itv = s->itv; struct ivtv_buffer *buf; u32 bytes_needed = 0; u32 offset, size; u32 UVoffset = 0, UVsize = 0; int skip_bufs = s->q_predma.buffers; int idx = s->sg_pending_size; int rc; /* sanity checks */ if (s->v4l2dev == NULL) { IVTV_DEBUG_WARN("Stream %s not started\n", s->name); return -1; } if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { IVTV_DEBUG_WARN("Stream %s not open\n", s->name); return -1; } /* determine offset, size and PTS for the various streams */ switch (s->type) { case IVTV_ENC_STREAM_TYPE_MPG: offset = data[1]; size = data[2]; s->pending_pts = 0; break; case IVTV_ENC_STREAM_TYPE_YUV: offset = data[1]; size = data[2]; UVoffset = data[3]; UVsize = data[4]; s->pending_pts = ((u64) data[5] << 32) | data[6]; break; case IVTV_ENC_STREAM_TYPE_PCM: offset = data[1] + 12; size = data[2] - 12; s->pending_pts = read_dec(offset - 8) | ((u64)(read_dec(offset - 12)) << 32); if (itv->has_cx23415) offset += IVTV_DECODER_OFFSET; break; case IVTV_ENC_STREAM_TYPE_VBI: size = itv->vbi.enc_size * itv->vbi.fpi; offset = read_enc(itv->vbi.enc_start - 4) + 12; if (offset == 12) { IVTV_DEBUG_INFO("VBI offset == 0\n"); return -1; } s->pending_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32); break; case IVTV_DEC_STREAM_TYPE_VBI: size = read_dec(itv->vbi.dec_start + 4) + 8; offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start; s->pending_pts = 0; offset += IVTV_DECODER_OFFSET; break; default: /* shouldn't happen */ return -1; } /* if this is the start of the DMA then fill in the magic cookie */ if (s->sg_pending_size == 0 && ivtv_use_dma(s)) { if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || s->type == IVTV_DEC_STREAM_TYPE_VBI)) { s->pending_backup = read_dec(offset - IVTV_DECODER_OFFSET); write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET); } else { s->pending_backup = read_enc(offset); write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset); } s->pending_offset = offset; } bytes_needed = size; if (s->type == IVTV_ENC_STREAM_TYPE_YUV) { /* The size for the Y samples needs to be rounded upwards to a multiple of the buf_size. The UV samples then start in the next buffer. */ bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size); bytes_needed += UVsize; } IVTV_DEBUG_HI_DMA("%s %s: 0x%08x bytes at 0x%08x\n", ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset); rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed); if (rc < 0) { /* Insufficient buffers */ IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n", bytes_needed, s->name); return -1; } if (rc && !s->buffers_stolen && (s->s_flags & IVTV_F_S_APPL_IO)) { IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name); IVTV_WARN("Cause: the application is not reading fast enough.\n"); } s->buffers_stolen = rc; /* got the buffers, now fill in sg_pending */ buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list); memset(buf->buf, 0, 128); list_for_each_entry(buf, &s->q_predma.list, list) { if (skip_bufs-- > 0) continue; s->sg_pending[idx].dst = buf->dma_handle; s->sg_pending[idx].src = offset; s->sg_pending[idx].size = s->buf_size; buf->bytesused = min(size, s->buf_size); buf->dma_xfer_cnt = s->dma_xfer_cnt; s->q_predma.bytesused += buf->bytesused; size -= buf->bytesused; offset += s->buf_size; /* Sync SG buffers */ ivtv_buf_sync_for_device(s, buf); if (size == 0) { /* YUV */ /* process the UV section */ offset = UVoffset; size = UVsize; } idx++; } s->sg_pending_size = idx; return 0;}static void dma_post(struct ivtv_stream *s){ struct ivtv *itv = s->itv; struct ivtv_buffer *buf = NULL; struct list_head *p; u32 offset; __le32 *u32buf; int x = 0; IVTV_DEBUG_HI_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA", s->name, s->dma_offset); list_for_each(p, &s->q_dma.list) { buf = list_entry(p, struct ivtv_buffer, list); u32buf = (__le32 *)buf->buf; /* Sync Buffer */ ivtv_buf_sync_for_cpu(s, buf); if (x == 0 && ivtv_use_dma(s)) { offset = s->dma_last_offset; if (u32buf[offset / 4] != DMA_MAGIC_COOKIE) { for (offset = 0; offset < 64; offset++) { if (u32buf[offset] == DMA_MAGIC_COOKIE) { break; } } offset *= 4; if (offset == 256) { IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name); offset = s->dma_last_offset; } if (s->dma_last_offset != offset) IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset); s->dma_last_offset = offset; } if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || s->type == IVTV_DEC_STREAM_TYPE_VBI)) { write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET); } else { write_enc_sync(0, s->dma_offset); } if (offset) { buf->bytesused -= offset; memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset); } *u32buf = cpu_to_le32(s->dma_backup); } x++; /* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */ if (s->type == IVTV_ENC_STREAM_TYPE_MPG || s->type == IVTV_ENC_STREAM_TYPE_VBI) buf->b_flags |= IVTV_F_B_NEED_BUF_SWAP; } if (buf) buf->bytesused += s->dma_last_offset; if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) { list_for_each_entry(buf, &s->q_dma.list, list) { /* Parse and Groom VBI Data */ s->q_dma.bytesused -= buf->bytesused; ivtv_process_vbi_data(itv, buf, 0, s->type); s->q_dma.bytesused += buf->bytesused; } if (s->id == -1) { ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0); return; } } ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused); if (s->id != -1) wake_up(&s->waitq);}void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock){ struct ivtv *itv = s->itv; struct yuv_playback_info *yi = &itv->yuv_info; u8 frame = yi->draw_frame; struct yuv_frame_info *f = &yi->new_frame_info[frame]; struct ivtv_buffer *buf; u32 y_size = 720 * ((f->src_h + 31) & ~31); u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET; int y_done = 0; int bytes_written = 0; unsigned long flags = 0; int idx = 0; IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset); /* Insert buffer block for YUV if needed */ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && f->offset_y) { if (yi->blanking_dmaptr) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?