| /* |
| init/start/stop/exit stream functions |
| 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 |
| */ |
| |
| /* License: GPL |
| * Author: Kevin Thayer <nufan_wfk at yahoo dot com> |
| * |
| * This file will hold API related functions, both internal (firmware api) |
| * and external (v4l2, etc) |
| * |
| * ----- |
| * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com> |
| * and Takeru KOMORIYA<komoriya@paken.org> |
| * |
| * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org> |
| * using information provided by Jiun-Kuei Jung @ AVerMedia. |
| */ |
| |
| #include "ivtv-driver.h" |
| #include "ivtv-fileops.h" |
| #include "ivtv-queue.h" |
| #include "ivtv-mailbox.h" |
| #include "ivtv-ioctl.h" |
| #include "ivtv-irq.h" |
| #include "ivtv-yuv.h" |
| #include "ivtv-cards.h" |
| #include "ivtv-streams.h" |
| #include "ivtv-firmware.h" |
| #include <media/v4l2-event.h> |
| |
| static const struct v4l2_file_operations ivtv_v4l2_enc_fops = { |
| .owner = THIS_MODULE, |
| .read = ivtv_v4l2_read, |
| .write = ivtv_v4l2_write, |
| .open = ivtv_v4l2_open, |
| .unlocked_ioctl = video_ioctl2, |
| .release = ivtv_v4l2_close, |
| .poll = ivtv_v4l2_enc_poll, |
| }; |
| |
| static const struct v4l2_file_operations ivtv_v4l2_dec_fops = { |
| .owner = THIS_MODULE, |
| .read = ivtv_v4l2_read, |
| .write = ivtv_v4l2_write, |
| .open = ivtv_v4l2_open, |
| .unlocked_ioctl = video_ioctl2, |
| .release = ivtv_v4l2_close, |
| .poll = ivtv_v4l2_dec_poll, |
| }; |
| |
| static const struct v4l2_file_operations ivtv_v4l2_radio_fops = { |
| .owner = THIS_MODULE, |
| .open = ivtv_v4l2_open, |
| .unlocked_ioctl = video_ioctl2, |
| .release = ivtv_v4l2_close, |
| .poll = ivtv_v4l2_enc_poll, |
| }; |
| |
| #define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */ |
| #define IVTV_V4L2_ENC_PCM_OFFSET 24 /* offset from 0 to register pcm v4l2 minors on */ |
| #define IVTV_V4L2_ENC_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */ |
| #define IVTV_V4L2_DEC_YUV_OFFSET 48 /* offset from 0 to register decoder yuv v4l2 minors on */ |
| #define IVTV_V4L2_DEC_VBI_OFFSET 8 /* offset from 0 to register decoder vbi input v4l2 minors on */ |
| #define IVTV_V4L2_DEC_VOUT_OFFSET 16 /* offset from 0 to register vbi output v4l2 minors on */ |
| |
| static struct { |
| const char *name; |
| int vfl_type; |
| int num_offset; |
| int dma, pio; |
| u32 v4l2_caps; |
| const struct v4l2_file_operations *fops; |
| } ivtv_stream_info[] = { |
| { /* IVTV_ENC_STREAM_TYPE_MPG */ |
| "encoder MPG", |
| VFL_TYPE_GRABBER, 0, |
| PCI_DMA_FROMDEVICE, 0, |
| V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | |
| V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, |
| &ivtv_v4l2_enc_fops |
| }, |
| { /* IVTV_ENC_STREAM_TYPE_YUV */ |
| "encoder YUV", |
| VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET, |
| PCI_DMA_FROMDEVICE, 0, |
| V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | |
| V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, |
| &ivtv_v4l2_enc_fops |
| }, |
| { /* IVTV_ENC_STREAM_TYPE_VBI */ |
| "encoder VBI", |
| VFL_TYPE_VBI, 0, |
| PCI_DMA_FROMDEVICE, 0, |
| V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_TUNER | |
| V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, |
| &ivtv_v4l2_enc_fops |
| }, |
| { /* IVTV_ENC_STREAM_TYPE_PCM */ |
| "encoder PCM", |
| VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET, |
| PCI_DMA_FROMDEVICE, 0, |
| V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, |
| &ivtv_v4l2_enc_fops |
| }, |
| { /* IVTV_ENC_STREAM_TYPE_RAD */ |
| "encoder radio", |
| VFL_TYPE_RADIO, 0, |
| PCI_DMA_NONE, 1, |
| V4L2_CAP_RADIO | V4L2_CAP_TUNER, |
| &ivtv_v4l2_radio_fops |
| }, |
| { /* IVTV_DEC_STREAM_TYPE_MPG */ |
| "decoder MPG", |
| VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET, |
| PCI_DMA_TODEVICE, 0, |
| V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | |
| V4L2_CAP_VIDEO_OUTPUT_OVERLAY, |
| &ivtv_v4l2_dec_fops |
| }, |
| { /* IVTV_DEC_STREAM_TYPE_VBI */ |
| "decoder VBI", |
| VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET, |
| PCI_DMA_NONE, 1, |
| V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_READWRITE, |
| &ivtv_v4l2_enc_fops |
| }, |
| { /* IVTV_DEC_STREAM_TYPE_VOUT */ |
| "decoder VOUT", |
| VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET, |
| PCI_DMA_NONE, 1, |
| V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, |
| &ivtv_v4l2_dec_fops |
| }, |
| { /* IVTV_DEC_STREAM_TYPE_YUV */ |
| "decoder YUV", |
| VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET, |
| PCI_DMA_TODEVICE, 0, |
| V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | |
| V4L2_CAP_VIDEO_OUTPUT_OVERLAY, |
| &ivtv_v4l2_dec_fops |
| } |
| }; |
| |
| static void ivtv_stream_init(struct ivtv *itv, int type) |
| { |
| struct ivtv_stream *s = &itv->streams[type]; |
| |
| /* we need to keep vdev, so restore it afterwards */ |
| memset(s, 0, sizeof(*s)); |
| |
| /* initialize ivtv_stream fields */ |
| s->itv = itv; |
| s->type = type; |
| s->name = ivtv_stream_info[type].name; |
| s->caps = ivtv_stream_info[type].v4l2_caps; |
| |
| if (ivtv_stream_info[type].pio) |
| s->dma = PCI_DMA_NONE; |
| else |
| s->dma = ivtv_stream_info[type].dma; |
| s->buf_size = itv->stream_buf_size[type]; |
| if (s->buf_size) |
| s->buffers = (itv->options.kilobytes[type] * 1024 + s->buf_size - 1) / s->buf_size; |
| spin_lock_init(&s->qlock); |
| init_waitqueue_head(&s->waitq); |
| s->sg_handle = IVTV_DMA_UNMAPPED; |
| ivtv_queue_init(&s->q_free); |
| ivtv_queue_init(&s->q_full); |
| ivtv_queue_init(&s->q_dma); |
| ivtv_queue_init(&s->q_predma); |
| ivtv_queue_init(&s->q_io); |
| } |
| |
| static int ivtv_prep_dev(struct ivtv *itv, int type) |
| { |
| struct ivtv_stream *s = &itv->streams[type]; |
| int num_offset = ivtv_stream_info[type].num_offset; |
| int num = itv->instance + ivtv_first_minor + num_offset; |
| |
| /* These four fields are always initialized. If vdev.v4l2_dev == NULL, then |
| this stream is not in use. In that case no other fields but these |
| four can be used. */ |
| s->vdev.v4l2_dev = NULL; |
| s->itv = itv; |
| s->type = type; |
| s->name = ivtv_stream_info[type].name; |
| |
| /* Check whether the radio is supported */ |
| if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO)) |
| return 0; |
| if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) |
| return 0; |
| |
| /* User explicitly selected 0 buffers for these streams, so don't |
| create them. */ |
| if (ivtv_stream_info[type].dma != PCI_DMA_NONE && |
| itv->options.kilobytes[type] == 0) { |
| IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name); |
| return 0; |
| } |
| |
| ivtv_stream_init(itv, type); |
| |
| snprintf(s->vdev.name, sizeof(s->vdev.name), "%s %s", |
| itv->v4l2_dev.name, s->name); |
| |
| s->vdev.num = num; |
| s->vdev.v4l2_dev = &itv->v4l2_dev; |
| if (ivtv_stream_info[type].v4l2_caps & |
| (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT)) |
| s->vdev.vfl_dir = VFL_DIR_TX; |
| s->vdev.fops = ivtv_stream_info[type].fops; |
| s->vdev.ctrl_handler = itv->v4l2_dev.ctrl_handler; |
| s->vdev.release = video_device_release_empty; |
| s->vdev.tvnorms = V4L2_STD_ALL; |
| s->vdev.lock = &itv->serialize_lock; |
| if (s->type == IVTV_DEC_STREAM_TYPE_VBI) { |
| v4l2_disable_ioctl(&s->vdev, VIDIOC_S_AUDIO); |
| v4l2_disable_ioctl(&s->vdev, VIDIOC_G_AUDIO); |
| v4l2_disable_ioctl(&s->vdev, VIDIOC_ENUMAUDIO); |
| v4l2_disable_ioctl(&s->vdev, VIDIOC_ENUMINPUT); |
| v4l2_disable_ioctl(&s->vdev, VIDIOC_S_INPUT); |
| v4l2_disable_ioctl(&s->vdev, VIDIOC_G_INPUT); |
| v4l2_disable_ioctl(&s->vdev, VIDIOC_S_FREQUENCY); |
| v4l2_disable_ioctl(&s->vdev, VIDIOC_G_FREQUENCY); |
| v4l2_disable_ioctl(&s->vdev, VIDIOC_S_TUNER); |
| v4l2_disable_ioctl(&s->vdev, VIDIOC_G_TUNER); |
| v4l2_disable_ioctl(&s->vdev, VIDIOC_S_STD); |
| } |
| ivtv_set_funcs(&s->vdev); |
| return 0; |
| } |
| |
| /* Initialize v4l2 variables and prepare v4l2 devices */ |
| int ivtv_streams_setup(struct ivtv *itv) |
| { |
| int type; |
| |
| /* Setup V4L2 Devices */ |
| for (type = 0; type < IVTV_MAX_STREAMS; type++) { |
| /* Prepare device */ |
| if (ivtv_prep_dev(itv, type)) |
| break; |
| |
| if (itv->streams[type].vdev.v4l2_dev == NULL) |
| continue; |
| |
| /* Allocate Stream */ |
| if (ivtv_stream_alloc(&itv->streams[type])) |
| break; |
| } |
| if (type == IVTV_MAX_STREAMS) |
| return 0; |
| |
| /* One or more streams could not be initialized. Clean 'em all up. */ |
| ivtv_streams_cleanup(itv); |
| return -ENOMEM; |
| } |
| |
| static int ivtv_reg_dev(struct ivtv *itv, int type) |
| { |
| struct ivtv_stream *s = &itv->streams[type]; |
| int vfl_type = ivtv_stream_info[type].vfl_type; |
| const char *name; |
| int num; |
| |
| if (s->vdev.v4l2_dev == NULL) |
| return 0; |
| |
| num = s->vdev.num; |
| /* card number + user defined offset + device offset */ |
| if (type != IVTV_ENC_STREAM_TYPE_MPG) { |
| struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG]; |
| |
| if (s_mpg->vdev.v4l2_dev) |
| num = s_mpg->vdev.num + ivtv_stream_info[type].num_offset; |
| } |
| video_set_drvdata(&s->vdev, s); |
| |
| /* Register device. First try the desired minor, then any free one. */ |
| if (video_register_device_no_warn(&s->vdev, vfl_type, num)) { |
| IVTV_ERR("Couldn't register v4l2 device for %s (device node number %d)\n", |
| s->name, num); |
| return -ENOMEM; |
| } |
| name = video_device_node_name(&s->vdev); |
| |
| switch (vfl_type) { |
| case VFL_TYPE_GRABBER: |
| IVTV_INFO("Registered device %s for %s (%d kB)\n", |
| name, s->name, itv->options.kilobytes[type]); |
| break; |
| case VFL_TYPE_RADIO: |
| IVTV_INFO("Registered device %s for %s\n", |
| name, s->name); |
| break; |
| case VFL_TYPE_VBI: |
| if (itv->options.kilobytes[type]) |
| IVTV_INFO("Registered device %s for %s (%d kB)\n", |
| name, s->name, itv->options.kilobytes[type]); |
| else |
| IVTV_INFO("Registered device %s for %s\n", |
| name, s->name); |
| break; |
| } |
| return 0; |
| } |
| |
| /* Register v4l2 devices */ |
| int ivtv_streams_register(struct ivtv *itv) |
| { |
| int type; |
| int err = 0; |
| |
| /* Register V4L2 devices */ |
| for (type = 0; type < IVTV_MAX_STREAMS; type++) |
| err |= ivtv_reg_dev(itv, type); |
| |
| if (err == 0) |
| return 0; |
| |
| /* One or more streams could not be initialized. Clean 'em all up. */ |
| ivtv_streams_cleanup(itv); |
| return -ENOMEM; |
| } |
| |
| /* Unregister v4l2 devices */ |
| void ivtv_streams_cleanup(struct ivtv *itv) |
| { |
| int type; |
| |
| /* Teardown all streams */ |
| for (type = 0; type < IVTV_MAX_STREAMS; type++) { |
| struct video_device *vdev = &itv->streams[type].vdev; |
| |
| if (vdev->v4l2_dev == NULL) |
| continue; |
| |
| video_unregister_device(vdev); |
| ivtv_stream_free(&itv->streams[type]); |
| itv->streams[type].vdev.v4l2_dev = NULL; |
| } |
| } |
| |
| static void ivtv_vbi_setup(struct ivtv *itv) |
| { |
| int raw = ivtv_raw_vbi(itv); |
| u32 data[CX2341X_MBOX_MAX_DATA]; |
| int lines; |
| int i; |
| |
| /* Reset VBI */ |
| ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0); |
| |
| /* setup VBI registers */ |
| if (raw) |
| v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &itv->vbi.in.fmt.vbi); |
| else |
| v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, &itv->vbi.in.fmt.sliced); |
| |
| /* determine number of lines and total number of VBI bytes. |
| A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 |
| The '- 1' byte is probably an unused U or V byte. Or something... |
| A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal |
| header, 42 data bytes + checksum (to be confirmed) */ |
| if (raw) { |
| lines = itv->vbi.count * 2; |
| } else { |
| lines = itv->is_60hz ? 24 : 38; |
| if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840)) |
| lines += 2; |
| } |
| |
| itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size); |
| |
| /* Note: sliced vs raw flag doesn't seem to have any effect |
| TODO: check mode (0x02) value with older ivtv versions. */ |
| data[0] = raw | 0x02 | (0xbd << 8); |
| |
| /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ |
| data[1] = 1; |
| /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */ |
| data[2] = raw ? 4 : 4 * (itv->vbi.raw_size / itv->vbi.enc_size); |
| /* The start/stop codes determine which VBI lines end up in the raw VBI data area. |
| The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line |
| is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video) |
| code. These values for raw VBI are obtained from a driver disassembly. The sliced |
| start/stop codes was deduced from this, but they do not appear in the driver. |
| Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54. |
| However, I have no idea what these values are for. */ |
| if (itv->hw_flags & IVTV_HW_CX25840) { |
| /* Setup VBI for the cx25840 digitizer */ |
| if (raw) { |
| data[3] = 0x20602060; |
| data[4] = 0x30703070; |
| } else { |
| data[3] = 0xB0F0B0F0; |
| data[4] = 0xA0E0A0E0; |
| } |
| /* Lines per frame */ |
| data[5] = lines; |
| /* bytes per line */ |
| data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size); |
| } else { |
| /* Setup VBI for the saa7115 digitizer */ |
| if (raw) { |
| data[3] = 0x25256262; |
| data[4] = 0x387F7F7F; |
| } else { |
| data[3] = 0xABABECEC; |
| data[4] = 0xB6F1F1F1; |
| } |
| /* Lines per frame */ |
| data[5] = lines; |
| /* bytes per line */ |
| data[6] = itv->vbi.enc_size / lines; |
| } |
| |
| IVTV_DEBUG_INFO( |
| "Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n", |
| data[0], data[1], data[2], data[5], data[6]); |
| |
| ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data); |
| |
| /* returns the VBI encoder memory area. */ |
| itv->vbi.enc_start = data[2]; |
| itv->vbi.fpi = data[0]; |
| if (!itv->vbi.fpi) |
| itv->vbi.fpi = 1; |
| |
| IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d\n", |
| itv->vbi.enc_start, data[1], itv->vbi.fpi); |
| |
| /* select VBI lines. |
| Note that the sliced argument seems to have no effect. */ |
| for (i = 2; i <= 24; i++) { |
| int valid; |
| |
| if (itv->is_60hz) { |
| valid = i >= 10 && i < 22; |
| } else { |
| valid = i >= 6 && i < 24; |
| } |
| ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1, |
| valid, 0 , 0, 0); |
| ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000, |
| valid, 0, 0, 0); |
| } |
| |
| /* Remaining VBI questions: |
| - Is it possible to select particular VBI lines only for inclusion in the MPEG |
| stream? Currently you can only get the first X lines. |
| - Is mixed raw and sliced VBI possible? |
| - What's the meaning of the raw/sliced flag? |
| - What's the meaning of params 2, 3 & 4 of the Select VBI command? */ |
| } |
| |
| int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) |
| { |
| u32 data[CX2341X_MBOX_MAX_DATA]; |
| struct ivtv *itv = s->itv; |
| int captype = 0, subtype = 0; |
| int enable_passthrough = 0; |
| |
| if (s->vdev.v4l2_dev == NULL) |
| return -EINVAL; |
| |
| IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name); |
| |
| switch (s->type) { |
| case IVTV_ENC_STREAM_TYPE_MPG: |
| captype = 0; |
| subtype = 3; |
| |
| /* Stop Passthrough */ |
| if (itv->output_mode == OUT_PASSTHROUGH) { |
| ivtv_passthrough_mode(itv, 0); |
| enable_passthrough = 1; |
| } |
| itv->mpg_data_received = itv->vbi_data_inserted = 0; |
| itv->dualwatch_jiffies = jiffies; |
| itv->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode); |
| itv->search_pack_header = 0; |
| break; |
| |
| case IVTV_ENC_STREAM_TYPE_YUV: |
| if (itv->output_mode == OUT_PASSTHROUGH) { |
| captype = 2; |
| subtype = 11; /* video+audio+decoder */ |
| break; |
| } |
| captype = 1; |
| subtype = 1; |
| break; |
| case IVTV_ENC_STREAM_TYPE_PCM: |
| captype = 1; |
| subtype = 2; |
| break; |
| case IVTV_ENC_STREAM_TYPE_VBI: |
| captype = 1; |
| subtype = 4; |
| |
| itv->vbi.frame = 0; |
| itv->vbi.inserted_frame = 0; |
| memset(itv->vbi.sliced_mpeg_size, |
| 0, sizeof(itv->vbi.sliced_mpeg_size)); |
| break; |
| default: |
| return -EINVAL; |
| } |
| s->subtype = subtype; |
| s->buffers_stolen = 0; |
| |
| /* Clear Streamoff flags in case left from last capture */ |
| clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); |
| |
| if (atomic_read(&itv->capturing) == 0) { |
| int digitizer; |
| |
| /* Always use frame based mode. Experiments have demonstrated that byte |
| stream based mode results in dropped frames and corruption. Not often, |
| but occasionally. Many thanks go to Leonard Orb who spent a lot of |
| effort and time trying to trace the cause of the drop outs. */ |
| /* 1 frame per DMA */ |
| /*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */ |
| ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1); |
| |
| /* Stuff from Windows, we don't know what it is */ |
| ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0); |
| /* According to the docs, this should be correct. However, this is |
| untested. I don't dare enable this without having tested it. |
| Only very few old cards actually have this hardware combination. |
| ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, |
| ((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0); |
| */ |
| ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415); |
| ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0); |
| ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1); |
| ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12); |
| |
| /* assign placeholder */ |
| ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); |
| |
| if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X)) |
| digitizer = 0xF1; |
| else if (itv->card->hw_all & IVTV_HW_SAA7114) |
| digitizer = 0xEF; |
| else /* cx25840 */ |
| digitizer = 0x140; |
| |
| ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, digitizer, digitizer); |
| |
| /* Setup VBI */ |
| if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) { |
| ivtv_vbi_setup(itv); |
| } |
| |
| /* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */ |
| ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400); |
| itv->pgm_info_offset = data[0]; |
| itv->pgm_info_num = data[1]; |
| itv->pgm_info_write_idx = 0; |
| itv->pgm_info_read_idx = 0; |
| |
| IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n", |
| itv->pgm_info_offset, itv->pgm_info_num); |
| |
| /* Setup API for Stream */ |
| cx2341x_handler_setup(&itv->cxhdl); |
| |
| /* mute if capturing radio */ |
| if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) |
| ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, |
| 1 | (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8)); |
| } |
| |
| /* Vsync Setup */ |
| if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { |
| /* event notification (on) */ |
| ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1); |
| ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); |
| } |
| |
| if (atomic_read(&itv->capturing) == 0) { |
| /* Clear all Pending Interrupts */ |
| ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); |
| |
| clear_bit(IVTV_F_I_EOS, &itv->i_flags); |
| |
| cx2341x_handler_set_busy(&itv->cxhdl, 1); |
| |
| /* Initialize Digitizer for Capture */ |
| /* Avoid tinny audio problem - ensure audio clocks are going */ |
| v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1); |
| /* Avoid unpredictable PCI bus hang - disable video clocks */ |
| v4l2_subdev_call(itv->sd_video, video, s_stream, 0); |
| ivtv_msleep_timeout(300, 0); |
| ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); |
| v4l2_subdev_call(itv->sd_video, video, s_stream, 1); |
| } |
| |
| /* begin_capture */ |
| if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype)) |
| { |
| IVTV_DEBUG_WARN( "Error starting capture!\n"); |
| return -EINVAL; |
| } |
| |
| /* Start Passthrough */ |
| if (enable_passthrough) { |
| ivtv_passthrough_mode(itv, 1); |
| } |
| |
| if (s->type == IVTV_ENC_STREAM_TYPE_VBI) |
| ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); |
| else |
| ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); |
| |
| /* you're live! sit back and await interrupts :) */ |
| atomic_inc(&itv->capturing); |
| return 0; |
| } |
| EXPORT_SYMBOL(ivtv_start_v4l2_encode_stream); |
| |
| static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s) |
| { |
| u32 data[CX2341X_MBOX_MAX_DATA]; |
| struct ivtv *itv = s->itv; |
| int datatype; |
| u16 width; |
| u16 height; |
| |
| if (s->vdev.v4l2_dev == NULL) |
| return -EINVAL; |
| |
| IVTV_DEBUG_INFO("Setting some initial decoder settings\n"); |
| |
| width = itv->cxhdl.width; |
| height = itv->cxhdl.height; |
| |
| /* set audio mode to left/stereo for dual/stereo mode. */ |
| ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); |
| |
| /* set number of internal decoder buffers */ |
| ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0); |
| |
| /* prebuffering */ |
| ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1); |
| |
| /* extract from user packets */ |
| ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1); |
| itv->vbi.dec_start = data[0]; |
| |
| IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n", |
| itv->vbi.dec_start, data[1]); |
| |
| /* set decoder source settings */ |
| /* Data type: 0 = mpeg from host, |
| 1 = yuv from encoder, |
| 2 = yuv_from_host */ |
| switch (s->type) { |
| case IVTV_DEC_STREAM_TYPE_YUV: |
| if (itv->output_mode == OUT_PASSTHROUGH) { |
| datatype = 1; |
| } else { |
| /* Fake size to avoid switching video standard */ |
| datatype = 2; |
| width = 720; |
| height = itv->is_out_50hz ? 576 : 480; |
| } |
| IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype); |
| break; |
| case IVTV_DEC_STREAM_TYPE_MPG: |
| default: |
| datatype = 0; |
| break; |
| } |
| if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype, |
| width, height, itv->cxhdl.audio_properties)) { |
| IVTV_DEBUG_WARN("Couldn't initialize decoder source\n"); |
| } |
| |
| /* Decoder sometimes dies here, so wait a moment */ |
| ivtv_msleep_timeout(10, 0); |
| |
| /* Known failure point for firmware, so check */ |
| return ivtv_firmware_check(itv, "ivtv_setup_v4l2_decode_stream"); |
| } |
| |
| int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) |
| { |
| struct ivtv *itv = s->itv; |
| int rc; |
| |
| if (s->vdev.v4l2_dev == NULL) |
| return -EINVAL; |
| |
| if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) |
| return 0; /* already started */ |
| |
| IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset); |
| |
| rc = ivtv_setup_v4l2_decode_stream(s); |
| if (rc < 0) { |
| clear_bit(IVTV_F_S_STREAMING, &s->s_flags); |
| return rc; |
| } |
| |
| /* set dma size to 65536 bytes */ |
| ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536); |
| |
| /* Clear Streamoff */ |
| clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); |
| |
| /* Zero out decoder counters */ |
| writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[0]); |
| writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[1]); |
| writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[2]); |
| writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[3]); |
| writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]); |
| writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]); |
| writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]); |
| writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]); |
| |
| /* turn on notification of dual/stereo mode change */ |
| ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); |
| |
| /* start playback */ |
| ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0); |
| |
| /* Let things settle before we actually start */ |
| ivtv_msleep_timeout(10, 0); |
| |
| /* Clear the following Interrupt mask bits for decoding */ |
| ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE); |
| IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask); |
| |
| /* you're live! sit back and await interrupts :) */ |
| atomic_inc(&itv->decoding); |
| return 0; |
| } |
| |
| void ivtv_stop_all_captures(struct ivtv *itv) |
| { |
| int i; |
| |
| for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) { |
| struct ivtv_stream *s = &itv->streams[i]; |
| |
| if (s->vdev.v4l2_dev == NULL) |
| continue; |
| if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { |
| ivtv_stop_v4l2_encode_stream(s, 0); |
| } |
| } |
| } |
| |
| int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) |
| { |
| struct ivtv *itv = s->itv; |
| DECLARE_WAITQUEUE(wait, current); |
| int cap_type; |
| int stopmode; |
| |
| if (s->vdev.v4l2_dev == NULL) |
| return -EINVAL; |
| |
| /* This function assumes that you are allowed to stop the capture |
| and that we are actually capturing */ |
| |
| IVTV_DEBUG_INFO("Stop Capture\n"); |
| |
| if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) |
| return 0; |
| if (atomic_read(&itv->capturing) == 0) |
| return 0; |
| |
| switch (s->type) { |
| case IVTV_ENC_STREAM_TYPE_YUV: |
| cap_type = 1; |
| break; |
| case IVTV_ENC_STREAM_TYPE_PCM: |
| cap_type = 1; |
| break; |
| case IVTV_ENC_STREAM_TYPE_VBI: |
| cap_type = 1; |
| break; |
| case IVTV_ENC_STREAM_TYPE_MPG: |
| default: |
| cap_type = 0; |
| break; |
| } |
| |
| /* Stop Capture Mode */ |
| if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { |
| stopmode = 0; |
| } else { |
| stopmode = 1; |
| } |
| |
| /* end_capture */ |
| /* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */ |
| ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype); |
| |
| if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) { |
| if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { |
| /* only run these if we're shutting down the last cap */ |
| unsigned long duration; |
| unsigned long then = jiffies; |
| |
| add_wait_queue(&itv->eos_waitq, &wait); |
| |
| set_current_state(TASK_INTERRUPTIBLE); |
| |
| /* wait 2s for EOS interrupt */ |
| while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) && |
| time_before(jiffies, |
| then + msecs_to_jiffies(2000))) { |
| schedule_timeout(msecs_to_jiffies(10)); |
| } |
| |
| /* To convert jiffies to ms, we must multiply by 1000 |
| * and divide by HZ. To avoid runtime division, we |
| * convert this to multiplication by 1000/HZ. |
| * Since integer division truncates, we get the best |
| * accuracy if we do a rounding calculation of the constant. |
| * Think of the case where HZ is 1024. |
| */ |
| duration = ((1000 + HZ / 2) / HZ) * (jiffies - then); |
| |
| if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) { |
| IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name); |
| IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration); |
| } else { |
| IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration); |
| } |
| set_current_state(TASK_RUNNING); |
| remove_wait_queue(&itv->eos_waitq, &wait); |
| set_bit(IVTV_F_S_STREAMOFF, &s->s_flags); |
| } |
| |
| /* Handle any pending interrupts */ |
| ivtv_msleep_timeout(100, 0); |
| } |
| |
| atomic_dec(&itv->capturing); |
| |
| /* Clear capture and no-read bits */ |
| clear_bit(IVTV_F_S_STREAMING, &s->s_flags); |
| |
| if (s->type == IVTV_ENC_STREAM_TYPE_VBI) |
| ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); |
| |
| if (atomic_read(&itv->capturing) > 0) { |
| return 0; |
| } |
| |
| cx2341x_handler_set_busy(&itv->cxhdl, 0); |
| |
| /* Set the following Interrupt mask bits for capture */ |
| ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); |
| del_timer(&itv->dma_timer); |
| |
| /* event notification (off) */ |
| if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { |
| /* type: 0 = refresh */ |
| /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */ |
| ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1); |
| ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); |
| } |
| |
| /* Raw-passthrough is implied on start. Make sure it's stopped so |
| the encoder will re-initialize when next started */ |
| ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 7); |
| |
| wake_up(&s->waitq); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(ivtv_stop_v4l2_encode_stream); |
| |
| int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) |
| { |
| static const struct v4l2_event ev = { |
| .type = V4L2_EVENT_EOS, |
| }; |
| struct ivtv *itv = s->itv; |
| |
| if (s->vdev.v4l2_dev == NULL) |
| return -EINVAL; |
| |
| if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG) |
| return -EINVAL; |
| |
| if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags)) |
| return 0; |
| |
| IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", (unsigned long long)pts, flags); |
| |
| /* Stop Decoder */ |
| if (!(flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) || pts) { |
| u32 tmp = 0; |
| |
| /* Wait until the decoder is no longer running */ |
| if (pts) { |
| ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, |
| 0, (u32)(pts & 0xffffffff), (u32)(pts >> 32)); |
| } |
| while (1) { |
| u32 data[CX2341X_MBOX_MAX_DATA]; |
| ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0); |
| if (s->q_full.buffers + s->q_dma.buffers == 0) { |
| if (tmp == data[3]) |
| break; |
| tmp = data[3]; |
| } |
| if (ivtv_msleep_timeout(100, 1)) |
| break; |
| } |
| } |
| ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & V4L2_DEC_CMD_STOP_TO_BLACK, 0, 0); |
| |
| /* turn off notification of dual/stereo mode change */ |
| ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); |
| |
| ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE); |
| del_timer(&itv->dma_timer); |
| |
| clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); |
| clear_bit(IVTV_F_S_STREAMING, &s->s_flags); |
| ivtv_flush_queues(s); |
| |
| /* decoder needs time to settle */ |
| ivtv_msleep_timeout(40, 0); |
| |
| /* decrement decoding */ |
| atomic_dec(&itv->decoding); |
| |
| set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags); |
| wake_up(&itv->event_waitq); |
| v4l2_event_queue(&s->vdev, &ev); |
| |
| /* wake up wait queues */ |
| wake_up(&s->waitq); |
| |
| return 0; |
| } |
| |
| int ivtv_passthrough_mode(struct ivtv *itv, int enable) |
| { |
| struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV]; |
| struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; |
| |
| if (yuv_stream->vdev.v4l2_dev == NULL || dec_stream->vdev.v4l2_dev == NULL) |
| return -EINVAL; |
| |
| IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n"); |
| |
| /* Prevent others from starting/stopping streams while we |
| initiate/terminate passthrough mode */ |
| if (enable) { |
| if (itv->output_mode == OUT_PASSTHROUGH) { |
| return 0; |
| } |
| if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH) |
| return -EBUSY; |
| |
| /* Fully initialize stream, and then unflag init */ |
| set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); |
| set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); |
| |
| /* Setup YUV Decoder */ |
| ivtv_setup_v4l2_decode_stream(dec_stream); |
| |
| /* Start Decoder */ |
| ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1); |
| atomic_inc(&itv->decoding); |
| |
| /* Setup capture if not already done */ |
| if (atomic_read(&itv->capturing) == 0) { |
| cx2341x_handler_setup(&itv->cxhdl); |
| cx2341x_handler_set_busy(&itv->cxhdl, 1); |
| } |
| |
| /* Start Passthrough Mode */ |
| ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11); |
| atomic_inc(&itv->capturing); |
| return 0; |
| } |
| |
| if (itv->output_mode != OUT_PASSTHROUGH) |
| return 0; |
| |
| /* Stop Passthrough Mode */ |
| ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11); |
| ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0); |
| |
| atomic_dec(&itv->capturing); |
| atomic_dec(&itv->decoding); |
| clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); |
| clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); |
| itv->output_mode = OUT_NONE; |
| if (atomic_read(&itv->capturing) == 0) |
| cx2341x_handler_set_busy(&itv->cxhdl, 0); |
| |
| return 0; |
| } |