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, &param);	}	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 + -
显示快捷键?