ivtv-ioctl.c

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

C
1,920
字号
/*    ioctl system call    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>    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-version.h"#include "ivtv-mailbox.h"#include "ivtv-i2c.h"#include "ivtv-queue.h"#include "ivtv-fileops.h"#include "ivtv-vbi.h"#include "ivtv-routing.h"#include "ivtv-streams.h"#include "ivtv-yuv.h"#include "ivtv-ioctl.h"#include "ivtv-gpio.h"#include "ivtv-controls.h"#include "ivtv-cards.h"#include <media/saa7127.h>#include <media/tveeprom.h>#include <media/v4l2-chip-ident.h>#include <linux/dvb/audio.h>#include <linux/i2c-id.h>u16 ivtv_service2vbi(int type){	switch (type) {		case V4L2_SLICED_TELETEXT_B:			return IVTV_SLICED_TYPE_TELETEXT_B;		case V4L2_SLICED_CAPTION_525:			return IVTV_SLICED_TYPE_CAPTION_525;		case V4L2_SLICED_WSS_625:			return IVTV_SLICED_TYPE_WSS_625;		case V4L2_SLICED_VPS:			return IVTV_SLICED_TYPE_VPS;		default:			return 0;	}}static int valid_service_line(int field, int line, int is_pal){	return (is_pal && line >= 6 && (line != 23 || field == 0)) ||	       (!is_pal && line >= 10 && line < 22);}static u16 select_service_from_set(int field, int line, u16 set, int is_pal){	u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);	int i;	set = set & valid_set;	if (set == 0 || !valid_service_line(field, line, is_pal)) {		return 0;	}	if (!is_pal) {		if (line == 21 && (set & V4L2_SLICED_CAPTION_525))			return V4L2_SLICED_CAPTION_525;	}	else {		if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))			return V4L2_SLICED_VPS;		if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))			return V4L2_SLICED_WSS_625;		if (line == 23)			return 0;	}	for (i = 0; i < 32; i++) {		if ((1 << i) & set)			return 1 << i;	}	return 0;}void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal){	u16 set = fmt->service_set;	int f, l;	fmt->service_set = 0;	for (f = 0; f < 2; f++) {		for (l = 0; l < 24; l++) {			fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);		}	}}static void check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal){	int f, l;	for (f = 0; f < 2; f++) {		for (l = 0; l < 24; l++) {			fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);		}	}}u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt){	int f, l;	u16 set = 0;	for (f = 0; f < 2; f++) {		for (l = 0; l < 24; l++) {			set |= fmt->service_lines[f][l];		}	}	return set;}void ivtv_set_osd_alpha(struct ivtv *itv){	ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3,		itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state);	ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_chroma_key_state, itv->osd_chroma_key);}int ivtv_set_speed(struct ivtv *itv, int speed){	u32 data[CX2341X_MBOX_MAX_DATA];	struct ivtv_stream *s;	int single_step = (speed == 1 || speed == -1);	DEFINE_WAIT(wait);	if (speed == 0) speed = 1000;	/* No change? */	if (speed == itv->speed && !single_step)		return 0;	s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];	if (single_step && (speed < 0) == (itv->speed < 0)) {		/* Single step video and no need to change direction */		ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);		itv->speed = speed;		return 0;	}	if (single_step)		/* Need to change direction */		speed = speed < 0 ? -1000 : 1000;	data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0;	data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;	data[1] = (speed < 0);	data[2] = speed < 0 ? 3 : 7;	data[3] = itv->params.video_b_frames;	data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;	data[5] = 0;	data[6] = 0;	if (speed == 1500 || speed == -1500) data[0] |= 1;	else if (speed == 2000 || speed == -2000) data[0] |= 2;	else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed);	else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed);	/* If not decoding, just change speed setting */	if (atomic_read(&itv->decoding) > 0) {		int got_sig = 0;		/* Stop all DMA and decoding activity */		ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0);		/* Wait for any DMA to finish */		prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);		while (itv->i_flags & IVTV_F_I_DMA) {			got_sig = signal_pending(current);			if (got_sig)				break;			got_sig = 0;			schedule();		}		finish_wait(&itv->dma_waitq, &wait);		if (got_sig)			return -EINTR;		/* Change Speed safely */		ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data);		IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",				data[0], data[1], data[2], data[3], data[4], data[5], data[6]);	}	if (single_step) {		speed = (speed < 0) ? -1 : 1;		ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);	}	itv->speed = speed;	return 0;}static int ivtv_validate_speed(int cur_speed, int new_speed){	int fact = new_speed < 0 ? -1 : 1;	int s;	if (cur_speed == 0)		cur_speed = 1000;	if (new_speed < 0)		new_speed = -new_speed;	if (cur_speed < 0)		cur_speed = -cur_speed;	if (cur_speed <= new_speed) {		if (new_speed > 1500)			return fact * 2000;		if (new_speed > 1000)			return fact * 1500;	}	else {		if (new_speed >= 2000)			return fact * 2000;		if (new_speed >= 1500)			return fact * 1500;		if (new_speed >= 1000)			return fact * 1000;	}	if (new_speed == 0)		return 1000;	if (new_speed == 1 || new_speed == 1000)		return fact * new_speed;	s = new_speed;	new_speed = 1000 / new_speed;	if (1000 / cur_speed == new_speed)		new_speed += (cur_speed < s) ? -1 : 1;	if (new_speed > 60) return 1000 / (fact * 60);	return 1000 / (fact * new_speed);}static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,		struct video_command *vc, int try){	struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))		return -EINVAL;	switch (vc->cmd) {	case VIDEO_CMD_PLAY: {		vc->flags = 0;		vc->play.speed = ivtv_validate_speed(itv->speed, vc->play.speed);		if (vc->play.speed < 0)			vc->play.format = VIDEO_PLAY_FMT_GOP;		if (try) break;		if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG)			return -EBUSY;		if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {			/* forces ivtv_set_speed to be called */			itv->speed = 0;		}		return ivtv_start_decoding(id, vc->play.speed);	}	case VIDEO_CMD_STOP:		vc->flags &= VIDEO_CMD_STOP_IMMEDIATELY|VIDEO_CMD_STOP_TO_BLACK;		if (vc->flags & VIDEO_CMD_STOP_IMMEDIATELY)			vc->stop.pts = 0;		if (try) break;		if (atomic_read(&itv->decoding) == 0)			return 0;		if (itv->output_mode != OUT_MPG)			return -EBUSY;		itv->output_mode = OUT_NONE;		return ivtv_stop_v4l2_decode_stream(s, vc->flags, vc->stop.pts);	case VIDEO_CMD_FREEZE:		vc->flags &= VIDEO_CMD_FREEZE_TO_BLACK;		if (try) break;		if (itv->output_mode != OUT_MPG)			return -EBUSY;		if (atomic_read(&itv->decoding) > 0) {			ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1,				(vc->flags & VIDEO_CMD_FREEZE_TO_BLACK) ? 1 : 0);			set_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags);		}		break;	case VIDEO_CMD_CONTINUE:		vc->flags = 0;		if (try) break;		if (itv->output_mode != OUT_MPG)			return -EBUSY;		if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {			int speed = itv->speed;			itv->speed = 0;			return ivtv_start_decoding(id, speed);		}		break;	default:		return -EINVAL;	}	return 0;}static int ivtv_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt){	struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;	vbifmt->reserved[0] = 0;	vbifmt->reserved[1] = 0;	if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))		return -EINVAL;	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;	if (itv->is_60hz) {		vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;		vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;	} else {		vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625;		vbifmt->service_lines[0][16] = V4L2_SLICED_VPS;	}	vbifmt->service_set = ivtv_get_service_set(vbifmt);	return 0;}static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt){	struct ivtv_open_id *id = fh;	struct ivtv *itv = id->itv;	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;	pixfmt->width = itv->params.width;	pixfmt->height = itv->params.height;	pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;	pixfmt->field = V4L2_FIELD_INTERLACED;	pixfmt->priv = 0;	if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {		pixfmt->pixelformat = V4L2_PIX_FMT_HM12;		/* YUV size is (Y=(h*w) + UV=(h*(w/2))) */		pixfmt->sizeimage =			pixfmt->height * pixfmt->width +			pixfmt->height * (pixfmt->width / 2);		pixfmt->bytesperline = 720;	} else {		pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;		pixfmt->sizeimage = 128 * 1024;		pixfmt->bytesperline = 0;	}	return 0;}static int ivtv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt){	struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;	struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;	vbifmt->sampling_rate = 27000000;	vbifmt->offset = 248;	vbifmt->samples_per_line = itv->vbi.raw_decoder_line_size - 4;	vbifmt->sample_format = V4L2_PIX_FMT_GREY;	vbifmt->start[0] = itv->vbi.start[0];	vbifmt->start[1] = itv->vbi.start[1];	vbifmt->count[0] = vbifmt->count[1] = itv->vbi.count;	vbifmt->flags = 0;	vbifmt->reserved[0] = 0;	vbifmt->reserved[1] = 0;	return 0;}static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt){	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;	struct ivtv_open_id *id = fh;	struct ivtv *itv = id->itv;	vbifmt->reserved[0] = 0;	vbifmt->reserved[1] = 0;	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;	if (id->type == IVTV_DEC_STREAM_TYPE_VBI) {		vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 :			V4L2_SLICED_VBI_525;		ivtv_expand_service_set(vbifmt, itv->is_50hz);		return 0;	}	itv->video_dec_func(itv, VIDIOC_G_FMT, fmt);	vbifmt->service_set = ivtv_get_service_set(vbifmt);	return 0;}static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt){	struct ivtv_open_id *id = fh;	struct ivtv *itv = id->itv;	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))		return -EINVAL;	pixfmt->width = itv->main_rect.width;	pixfmt->height = itv->main_rect.height;	pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;	pixfmt->field = V4L2_FIELD_INTERLACED;	pixfmt->priv = 0;	if (id->type == IVTV_DEC_STREAM_TYPE_YUV) {		switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {		case IVTV_YUV_MODE_INTERLACED:			pixfmt->field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?				V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB;			break;		case IVTV_YUV_MODE_PROGRESSIVE:			pixfmt->field = V4L2_FIELD_NONE;			break;		default:			pixfmt->field = V4L2_FIELD_ANY;			break;		}		pixfmt->pixelformat = V4L2_PIX_FMT_HM12;		pixfmt->bytesperline = 720;		pixfmt->width = itv->yuv_info.v4l2_src_w;		pixfmt->height = itv->yuv_info.v4l2_src_h;		/* YUV size is (Y=(h*w) + UV=(h*(w/2))) */		pixfmt->sizeimage =			1080 * ((pixfmt->height + 31) & ~31);	} else {		pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;		pixfmt->sizeimage = 128 * 1024;		pixfmt->bytesperline = 0;	}	return 0;}static int ivtv_g_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt){	struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;	struct v4l2_window *winfmt = &fmt->fmt.win;	if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))		return -EINVAL;	winfmt->chromakey = itv->osd_chroma_key;	winfmt->global_alpha = itv->osd_global_alpha;	winfmt->field = V4L2_FIELD_INTERLACED;	winfmt->clips = NULL;	winfmt->clipcount = 0;	winfmt->bitmap = NULL;	winfmt->w.top = winfmt->w.left = 0;	winfmt->w.width = itv->osd_rect.width;	winfmt->w.height = itv->osd_rect.height;	return 0;}static int ivtv_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt){	return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt);}static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt){	struct ivtv_open_id *id = fh;	struct ivtv *itv = id->itv;	int w = fmt->fmt.pix.width;	int h = fmt->fmt.pix.height;	w = min(w, 720);	w = max(w, 2);	h = min(h, itv->is_50hz ? 576 : 480);	h = max(h, 2);	ivtv_g_fmt_vid_cap(file, fh, fmt);	fmt->fmt.pix.width = w;	fmt->fmt.pix.height = h;	return 0;

⌨️ 快捷键说明

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