ivtv-irq.c

来自「trident tm5600的linux驱动」· C语言 代码 · 共 999 行 · 第 1/3 页

C
999
字号
			s->sg_pending[idx].src = yi->blanking_dmaptr;			s->sg_pending[idx].dst = offset;			s->sg_pending[idx].size = 720 * 16;		}		offset += 720 * 16;		idx++;	}	list_for_each_entry(buf, &s->q_predma.list, list) {		/* YUV UV Offset from Y Buffer */		if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done &&				(bytes_written + buf->bytesused) >= y_size) {			s->sg_pending[idx].src = buf->dma_handle;			s->sg_pending[idx].dst = offset;			s->sg_pending[idx].size = y_size - bytes_written;			offset = uv_offset;			if (s->sg_pending[idx].size != buf->bytesused) {				idx++;				s->sg_pending[idx].src =				  buf->dma_handle + s->sg_pending[idx - 1].size;				s->sg_pending[idx].dst = offset;				s->sg_pending[idx].size =				   buf->bytesused - s->sg_pending[idx - 1].size;				offset += s->sg_pending[idx].size;			}			y_done = 1;		} else {			s->sg_pending[idx].src = buf->dma_handle;			s->sg_pending[idx].dst = offset;			s->sg_pending[idx].size = buf->bytesused;			offset += buf->bytesused;		}		bytes_written += buf->bytesused;		/* Sync SG buffers */		ivtv_buf_sync_for_device(s, buf);		idx++;	}	s->sg_pending_size = idx;	/* Sync Hardware SG List of buffers */	ivtv_stream_sync_for_device(s);	if (lock)		spin_lock_irqsave(&itv->dma_reg_lock, flags);	if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) {		ivtv_dma_dec_start(s);	}	else {		set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);	}	if (lock)		spin_unlock_irqrestore(&itv->dma_reg_lock, flags);}static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s){	struct ivtv *itv = s->itv;	s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);	s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);	s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);	s->sg_processed++;	/* Sync Hardware SG List of buffers */	ivtv_stream_sync_for_device(s);	write_reg(s->sg_handle, IVTV_REG_ENCDMAADDR);	write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);	itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);	add_timer(&itv->dma_timer);}static void ivtv_dma_dec_start_xfer(struct ivtv_stream *s){	struct ivtv *itv = s->itv;	s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);	s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);	s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);	s->sg_processed++;	/* Sync Hardware SG List of buffers */	ivtv_stream_sync_for_device(s);	write_reg(s->sg_handle, IVTV_REG_DECDMAADDR);	write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);	itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);	add_timer(&itv->dma_timer);}/* start the encoder DMA */static void ivtv_dma_enc_start(struct ivtv_stream *s){	struct ivtv *itv = s->itv;	struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];	int i;	IVTV_DEBUG_HI_DMA("start %s for %s\n", ivtv_use_dma(s) ? "DMA" : "PIO", s->name);	if (s->q_predma.bytesused)		ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);	if (ivtv_use_dma(s))		s->sg_pending[s->sg_pending_size - 1].size += 256;	/* If this is an MPEG stream, and VBI data is also pending, then append the	   VBI DMA to the MPEG DMA and transfer both sets of data at once.	   VBI DMA is a second class citizen compared to MPEG and mixing them together	   will confuse the firmware (the end of a VBI DMA is seen as the end of a	   MPEG DMA, thus effectively dropping an MPEG frame). So instead we make	   sure we only use the MPEG DMA to transfer the VBI DMA if both are in	   use. This way no conflicts occur. */	clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);	if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->sg_pending_size &&			s->sg_pending_size + s_vbi->sg_pending_size <= s->buffers) {		ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused);		if (ivtv_use_dma(s_vbi))			s_vbi->sg_pending[s_vbi->sg_pending_size - 1].size += 256;		for (i = 0; i < s_vbi->sg_pending_size; i++) {			s->sg_pending[s->sg_pending_size++] = s_vbi->sg_pending[i];		}		s_vbi->dma_offset = s_vbi->pending_offset;		s_vbi->sg_pending_size = 0;		s_vbi->dma_xfer_cnt++;		set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);		IVTV_DEBUG_HI_DMA("include DMA for %s\n", s_vbi->name);	}	s->dma_xfer_cnt++;	memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);	s->sg_processing_size = s->sg_pending_size;	s->sg_pending_size = 0;	s->sg_processed = 0;	s->dma_offset = s->pending_offset;	s->dma_backup = s->pending_backup;	s->dma_pts = s->pending_pts;	if (ivtv_use_pio(s)) {		set_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags);		set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);		set_bit(IVTV_F_I_PIO, &itv->i_flags);		itv->cur_pio_stream = s->type;	}	else {		itv->dma_retries = 0;		ivtv_dma_enc_start_xfer(s);		set_bit(IVTV_F_I_DMA, &itv->i_flags);		itv->cur_dma_stream = s->type;	}}static void ivtv_dma_dec_start(struct ivtv_stream *s){	struct ivtv *itv = s->itv;	if (s->q_predma.bytesused)		ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);	s->dma_xfer_cnt++;	memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);	s->sg_processing_size = s->sg_pending_size;	s->sg_pending_size = 0;	s->sg_processed = 0;	IVTV_DEBUG_HI_DMA("start DMA for %s\n", s->name);	itv->dma_retries = 0;	ivtv_dma_dec_start_xfer(s);	set_bit(IVTV_F_I_DMA, &itv->i_flags);	itv->cur_dma_stream = s->type;}static void ivtv_irq_dma_read(struct ivtv *itv){	struct ivtv_stream *s = NULL;	struct ivtv_buffer *buf;	int hw_stream_type = 0;	IVTV_DEBUG_HI_IRQ("DEC DMA READ\n");	del_timer(&itv->dma_timer);	if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && itv->cur_dma_stream < 0)		return;	if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {		s = &itv->streams[itv->cur_dma_stream];		ivtv_stream_sync_for_cpu(s);		if (read_reg(IVTV_REG_DMASTATUS) & 0x14) {			IVTV_DEBUG_WARN("DEC DMA ERROR %x (xfer %d of %d, retry %d)\n",					read_reg(IVTV_REG_DMASTATUS),					s->sg_processed, s->sg_processing_size, itv->dma_retries);			write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);			if (itv->dma_retries == 3) {				/* Too many retries, give up on this frame */				itv->dma_retries = 0;				s->sg_processed = s->sg_processing_size;			}			else {				/* Retry, starting with the first xfer segment.				   Just retrying the current segment is not sufficient. */				s->sg_processed = 0;				itv->dma_retries++;			}		}		if (s->sg_processed < s->sg_processing_size) {			/* DMA next buffer */			ivtv_dma_dec_start_xfer(s);			return;		}		if (s->type == IVTV_DEC_STREAM_TYPE_YUV)			hw_stream_type = 2;		IVTV_DEBUG_HI_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused);		/* For some reason must kick the firmware, like PIO mode,		   I think this tells the firmware we are done and the size		   of the xfer so it can calculate what we need next.		   I think we can do this part ourselves but would have to		   fully calculate xfer info ourselves and not use interrupts		 */		ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused,				hw_stream_type);		/* Free last DMA call */		while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) {			ivtv_buf_sync_for_cpu(s, buf);			ivtv_enqueue(s, buf, &s->q_free);		}		wake_up(&s->waitq);	}	clear_bit(IVTV_F_I_UDMA, &itv->i_flags);	clear_bit(IVTV_F_I_DMA, &itv->i_flags);	itv->cur_dma_stream = -1;	wake_up(&itv->dma_waitq);}static void ivtv_irq_enc_dma_complete(struct ivtv *itv){	u32 data[CX2341X_MBOX_MAX_DATA];	struct ivtv_stream *s;	ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);	IVTV_DEBUG_HI_IRQ("ENC DMA COMPLETE %x %d (%d)\n", data[0], data[1], itv->cur_dma_stream);	del_timer(&itv->dma_timer);	if (itv->cur_dma_stream < 0)		return;	s = &itv->streams[itv->cur_dma_stream];	ivtv_stream_sync_for_cpu(s);	if (data[0] & 0x18) {		IVTV_DEBUG_WARN("ENC DMA ERROR %x (offset %08x, xfer %d of %d, retry %d)\n", data[0],			s->dma_offset, s->sg_processed, s->sg_processing_size, itv->dma_retries);		write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);		if (itv->dma_retries == 3) {			/* Too many retries, give up on this frame */			itv->dma_retries = 0;			s->sg_processed = s->sg_processing_size;		}		else {			/* Retry, starting with the first xfer segment.			   Just retrying the current segment is not sufficient. */			s->sg_processed = 0;			itv->dma_retries++;		}	}	if (s->sg_processed < s->sg_processing_size) {		/* DMA next buffer */		ivtv_dma_enc_start_xfer(s);		return;	}	clear_bit(IVTV_F_I_DMA, &itv->i_flags);	itv->cur_dma_stream = -1;	dma_post(s);	if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {		s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];		dma_post(s);	}	s->sg_processing_size = 0;	s->sg_processed = 0;	wake_up(&itv->dma_waitq);}static void ivtv_irq_enc_pio_complete(struct ivtv *itv){	struct ivtv_stream *s;	if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS) {		itv->cur_pio_stream = -1;		return;	}	s = &itv->streams[itv->cur_pio_stream];	IVTV_DEBUG_HI_IRQ("ENC PIO COMPLETE %s\n", s->name);	clear_bit(IVTV_F_I_PIO, &itv->i_flags);	itv->cur_pio_stream = -1;	dma_post(s);	if (s->type == IVTV_ENC_STREAM_TYPE_MPG)		ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 0);	else if (s->type == IVTV_ENC_STREAM_TYPE_YUV)		ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 1);	else if (s->type == IVTV_ENC_STREAM_TYPE_PCM)		ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 2);	clear_bit(IVTV_F_I_PIO, &itv->i_flags);	if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {		s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];		dma_post(s);	}	wake_up(&itv->dma_waitq);}static void ivtv_irq_dma_err(struct ivtv *itv){	u32 data[CX2341X_MBOX_MAX_DATA];	del_timer(&itv->dma_timer);	ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);	IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1],				read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);	write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);	if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) &&	    itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) {		struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream];		/* retry */		if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)			ivtv_dma_dec_start(s);		else			ivtv_dma_enc_start(s);		return;	}	if (test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {		ivtv_udma_start(itv);		return;	}	clear_bit(IVTV_F_I_UDMA, &itv->i_flags);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?