Path: blob/master/drivers/media/video/cx18/cx18-ioctl.c
17686 views
/*1* cx18 ioctl system call2*3* Derived from ivtv-ioctl.c4*5* Copyright (C) 2007 Hans Verkuil <[email protected]>6* Copyright (C) 2008 Andy Walls <[email protected]>7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2 of the License, or11* (at your option) any later version.12*13* This program is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with this program; if not, write to the Free Software20* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA21* 02111-1307 USA22*/2324#include "cx18-driver.h"25#include "cx18-io.h"26#include "cx18-version.h"27#include "cx18-mailbox.h"28#include "cx18-i2c.h"29#include "cx18-queue.h"30#include "cx18-fileops.h"31#include "cx18-vbi.h"32#include "cx18-audio.h"33#include "cx18-video.h"34#include "cx18-streams.h"35#include "cx18-ioctl.h"36#include "cx18-gpio.h"37#include "cx18-controls.h"38#include "cx18-cards.h"39#include "cx18-av-core.h"40#include <media/tveeprom.h>41#include <media/v4l2-chip-ident.h>4243u16 cx18_service2vbi(int type)44{45switch (type) {46case V4L2_SLICED_TELETEXT_B:47return CX18_SLICED_TYPE_TELETEXT_B;48case V4L2_SLICED_CAPTION_525:49return CX18_SLICED_TYPE_CAPTION_525;50case V4L2_SLICED_WSS_625:51return CX18_SLICED_TYPE_WSS_625;52case V4L2_SLICED_VPS:53return CX18_SLICED_TYPE_VPS;54default:55return 0;56}57}5859/* Check if VBI services are allowed on the (field, line) for the video std */60static int valid_service_line(int field, int line, int is_pal)61{62return (is_pal && line >= 6 &&63((field == 0 && line <= 23) || (field == 1 && line <= 22))) ||64(!is_pal && line >= 10 && line < 22);65}6667/*68* For a (field, line, std) and inbound potential set of services for that line,69* return the first valid service of those passed in the incoming set for that70* line in priority order:71* CC, VPS, or WSS over TELETEXT for well known lines72* TELETEXT, before VPS, before CC, before WSS, for other lines73*/74static u16 select_service_from_set(int field, int line, u16 set, int is_pal)75{76u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);77int i;7879set = set & valid_set;80if (set == 0 || !valid_service_line(field, line, is_pal))81return 0;82if (!is_pal) {83if (line == 21 && (set & V4L2_SLICED_CAPTION_525))84return V4L2_SLICED_CAPTION_525;85} else {86if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))87return V4L2_SLICED_VPS;88if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))89return V4L2_SLICED_WSS_625;90if (line == 23)91return 0;92}93for (i = 0; i < 32; i++) {94if ((1 << i) & set)95return 1 << i;96}97return 0;98}99100/*101* Expand the service_set of *fmt into valid service_lines for the std,102* and clear the passed in fmt->service_set103*/104void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)105{106u16 set = fmt->service_set;107int f, l;108109fmt->service_set = 0;110for (f = 0; f < 2; f++) {111for (l = 0; l < 24; l++)112fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);113}114}115116/*117* Sanitize the service_lines in *fmt per the video std, and return 1118* if any service_line is left as valid after santization119*/120static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)121{122int f, l;123u16 set = 0;124125for (f = 0; f < 2; f++) {126for (l = 0; l < 24; l++) {127fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);128set |= fmt->service_lines[f][l];129}130}131return set != 0;132}133134/* Compute the service_set from the assumed valid service_lines of *fmt */135u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)136{137int f, l;138u16 set = 0;139140for (f = 0; f < 2; f++) {141for (l = 0; l < 24; l++)142set |= fmt->service_lines[f][l];143}144return set;145}146147static int cx18_g_fmt_vid_cap(struct file *file, void *fh,148struct v4l2_format *fmt)149{150struct cx18_open_id *id = fh2id(fh);151struct cx18 *cx = id->cx;152struct cx18_stream *s = &cx->streams[id->type];153struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;154155pixfmt->width = cx->cxhdl.width;156pixfmt->height = cx->cxhdl.height;157pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;158pixfmt->field = V4L2_FIELD_INTERLACED;159pixfmt->priv = 0;160if (id->type == CX18_ENC_STREAM_TYPE_YUV) {161pixfmt->pixelformat = s->pixelformat;162/* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))163UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */164if (s->pixelformat == V4L2_PIX_FMT_HM12)165pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2;166else167pixfmt->sizeimage = pixfmt->height * 720 * 2;168pixfmt->bytesperline = 720;169} else {170pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;171pixfmt->sizeimage = 128 * 1024;172pixfmt->bytesperline = 0;173}174return 0;175}176177static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,178struct v4l2_format *fmt)179{180struct cx18 *cx = fh2id(fh)->cx;181struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;182183vbifmt->sampling_rate = 27000000;184vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */185vbifmt->samples_per_line = vbi_active_samples - 4;186vbifmt->sample_format = V4L2_PIX_FMT_GREY;187vbifmt->start[0] = cx->vbi.start[0];188vbifmt->start[1] = cx->vbi.start[1];189vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count;190vbifmt->flags = 0;191vbifmt->reserved[0] = 0;192vbifmt->reserved[1] = 0;193return 0;194}195196static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,197struct v4l2_format *fmt)198{199struct cx18 *cx = fh2id(fh)->cx;200struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;201202/* sane, V4L2 spec compliant, defaults */203vbifmt->reserved[0] = 0;204vbifmt->reserved[1] = 0;205vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;206memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));207vbifmt->service_set = 0;208209/*210* Fetch the configured service_lines and total service_set from the211* digitizer/slicer. Note, cx18_av_vbi() wipes the passed in212* fmt->fmt.sliced under valid calling conditions213*/214if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced))215return -EINVAL;216217/* Ensure V4L2 spec compliant output */218vbifmt->reserved[0] = 0;219vbifmt->reserved[1] = 0;220vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;221vbifmt->service_set = cx18_get_service_set(vbifmt);222return 0;223}224225static int cx18_try_fmt_vid_cap(struct file *file, void *fh,226struct v4l2_format *fmt)227{228struct cx18_open_id *id = fh2id(fh);229struct cx18 *cx = id->cx;230int w = fmt->fmt.pix.width;231int h = fmt->fmt.pix.height;232int min_h = 2;233234w = min(w, 720);235w = max(w, 2);236if (id->type == CX18_ENC_STREAM_TYPE_YUV) {237/* YUV height must be a multiple of 32 */238h &= ~0x1f;239min_h = 32;240}241h = min(h, cx->is_50hz ? 576 : 480);242h = max(h, min_h);243244fmt->fmt.pix.width = w;245fmt->fmt.pix.height = h;246return 0;247}248249static int cx18_try_fmt_vbi_cap(struct file *file, void *fh,250struct v4l2_format *fmt)251{252return cx18_g_fmt_vbi_cap(file, fh, fmt);253}254255static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,256struct v4l2_format *fmt)257{258struct cx18 *cx = fh2id(fh)->cx;259struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;260261vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;262vbifmt->reserved[0] = 0;263vbifmt->reserved[1] = 0;264265/* If given a service set, expand it validly & clear passed in set */266if (vbifmt->service_set)267cx18_expand_service_set(vbifmt, cx->is_50hz);268/* Sanitize the service_lines, and compute the new set if any valid */269if (check_service_set(vbifmt, cx->is_50hz))270vbifmt->service_set = cx18_get_service_set(vbifmt);271return 0;272}273274static int cx18_s_fmt_vid_cap(struct file *file, void *fh,275struct v4l2_format *fmt)276{277struct cx18_open_id *id = fh2id(fh);278struct cx18 *cx = id->cx;279struct v4l2_mbus_framefmt mbus_fmt;280struct cx18_stream *s = &cx->streams[id->type];281int ret;282int w, h;283284ret = cx18_try_fmt_vid_cap(file, fh, fmt);285if (ret)286return ret;287w = fmt->fmt.pix.width;288h = fmt->fmt.pix.height;289290if (cx->cxhdl.width == w && cx->cxhdl.height == h &&291s->pixelformat == fmt->fmt.pix.pixelformat)292return 0;293294if (atomic_read(&cx->ana_capturing) > 0)295return -EBUSY;296297s->pixelformat = fmt->fmt.pix.pixelformat;298299mbus_fmt.width = cx->cxhdl.width = w;300mbus_fmt.height = cx->cxhdl.height = h;301mbus_fmt.code = V4L2_MBUS_FMT_FIXED;302v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &mbus_fmt);303return cx18_g_fmt_vid_cap(file, fh, fmt);304}305306static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,307struct v4l2_format *fmt)308{309struct cx18_open_id *id = fh2id(fh);310struct cx18 *cx = id->cx;311int ret;312313/*314* Changing the Encoder's Raw VBI parameters won't have any effect315* if any analog capture is ongoing316*/317if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)318return -EBUSY;319320/*321* Set the digitizer registers for raw active VBI.322* Note cx18_av_vbi_wipes out a lot of the passed in fmt under valid323* calling conditions324*/325ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi);326if (ret)327return ret;328329/* Store our new v4l2 (non-)sliced VBI state */330cx->vbi.sliced_in->service_set = 0;331cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;332333return cx18_g_fmt_vbi_cap(file, fh, fmt);334}335336static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,337struct v4l2_format *fmt)338{339struct cx18_open_id *id = fh2id(fh);340struct cx18 *cx = id->cx;341int ret;342struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;343344cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);345346/*347* Changing the Encoder's Raw VBI parameters won't have any effect348* if any analog capture is ongoing349*/350if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)351return -EBUSY;352353/*354* Set the service_lines requested in the digitizer/slicer registers.355* Note, cx18_av_vbi() wipes some "impossible" service lines in the356* passed in fmt->fmt.sliced under valid calling conditions357*/358ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced);359if (ret)360return ret;361/* Store our current v4l2 sliced VBI settings */362cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;363memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));364return 0;365}366367static int cx18_g_chip_ident(struct file *file, void *fh,368struct v4l2_dbg_chip_ident *chip)369{370struct cx18 *cx = fh2id(fh)->cx;371int err = 0;372373chip->ident = V4L2_IDENT_NONE;374chip->revision = 0;375switch (chip->match.type) {376case V4L2_CHIP_MATCH_HOST:377switch (chip->match.addr) {378case 0:379chip->ident = V4L2_IDENT_CX23418;380chip->revision = cx18_read_reg(cx, 0xC72028);381break;382case 1:383/*384* The A/V decoder is always present, but in the rare385* case that the card doesn't have analog, we don't386* use it. We find it w/o using the cx->sd_av pointer387*/388cx18_call_hw(cx, CX18_HW_418_AV,389core, g_chip_ident, chip);390break;391default:392/*393* Could return ident = V4L2_IDENT_UNKNOWN if we had394* other host chips at higher addresses, but we don't395*/396err = -EINVAL; /* per V4L2 spec */397break;398}399break;400case V4L2_CHIP_MATCH_I2C_DRIVER:401/* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */402cx18_call_all(cx, core, g_chip_ident, chip);403break;404case V4L2_CHIP_MATCH_I2C_ADDR:405/*406* We could return V4L2_IDENT_UNKNOWN, but we don't do the work407* to look if a chip is at the address with no driver. That's a408* dangerous thing to do with EEPROMs anyway.409*/410cx18_call_all(cx, core, g_chip_ident, chip);411break;412default:413err = -EINVAL;414break;415}416return err;417}418419#ifdef CONFIG_VIDEO_ADV_DEBUG420static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)421{422struct v4l2_dbg_register *regs = arg;423424if (!capable(CAP_SYS_ADMIN))425return -EPERM;426if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)427return -EINVAL;428429regs->size = 4;430if (cmd == VIDIOC_DBG_S_REGISTER)431cx18_write_enc(cx, regs->val, regs->reg);432else433regs->val = cx18_read_enc(cx, regs->reg);434return 0;435}436437static int cx18_g_register(struct file *file, void *fh,438struct v4l2_dbg_register *reg)439{440struct cx18 *cx = fh2id(fh)->cx;441442if (v4l2_chip_match_host(®->match))443return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg);444/* FIXME - errors shouldn't be ignored */445cx18_call_all(cx, core, g_register, reg);446return 0;447}448449static int cx18_s_register(struct file *file, void *fh,450struct v4l2_dbg_register *reg)451{452struct cx18 *cx = fh2id(fh)->cx;453454if (v4l2_chip_match_host(®->match))455return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg);456/* FIXME - errors shouldn't be ignored */457cx18_call_all(cx, core, s_register, reg);458return 0;459}460#endif461462static int cx18_querycap(struct file *file, void *fh,463struct v4l2_capability *vcap)464{465struct cx18 *cx = fh2id(fh)->cx;466467strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));468strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));469snprintf(vcap->bus_info, sizeof(vcap->bus_info),470"PCI:%s", pci_name(cx->pci_dev));471vcap->version = CX18_DRIVER_VERSION; /* version */472vcap->capabilities = cx->v4l2_cap; /* capabilities */473return 0;474}475476static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)477{478struct cx18 *cx = fh2id(fh)->cx;479480return cx18_get_audio_input(cx, vin->index, vin);481}482483static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)484{485struct cx18 *cx = fh2id(fh)->cx;486487vin->index = cx->audio_input;488return cx18_get_audio_input(cx, vin->index, vin);489}490491static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout)492{493struct cx18 *cx = fh2id(fh)->cx;494495if (vout->index >= cx->nof_audio_inputs)496return -EINVAL;497cx->audio_input = vout->index;498cx18_audio_set_io(cx);499return 0;500}501502static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin)503{504struct cx18 *cx = fh2id(fh)->cx;505506/* set it to defaults from our table */507return cx18_get_input(cx, vin->index, vin);508}509510static int cx18_cropcap(struct file *file, void *fh,511struct v4l2_cropcap *cropcap)512{513struct cx18 *cx = fh2id(fh)->cx;514515if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)516return -EINVAL;517cropcap->bounds.top = cropcap->bounds.left = 0;518cropcap->bounds.width = 720;519cropcap->bounds.height = cx->is_50hz ? 576 : 480;520cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;521cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;522cropcap->defrect = cropcap->bounds;523return 0;524}525526static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)527{528struct cx18_open_id *id = fh2id(fh);529struct cx18 *cx = id->cx;530531if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)532return -EINVAL;533CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n");534return -EINVAL;535}536537static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)538{539struct cx18 *cx = fh2id(fh)->cx;540541if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)542return -EINVAL;543CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n");544return -EINVAL;545}546547static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,548struct v4l2_fmtdesc *fmt)549{550static const struct v4l2_fmtdesc formats[] = {551{ 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,552"HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 }553},554{ 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED,555"MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 }556},557{ 2, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,558"UYVY 4:2:2", V4L2_PIX_FMT_UYVY, { 0, 0, 0, 0 }559},560};561562if (fmt->index > ARRAY_SIZE(formats) - 1)563return -EINVAL;564*fmt = formats[fmt->index];565return 0;566}567568static int cx18_g_input(struct file *file, void *fh, unsigned int *i)569{570struct cx18 *cx = fh2id(fh)->cx;571572*i = cx->active_input;573return 0;574}575576int cx18_s_input(struct file *file, void *fh, unsigned int inp)577{578struct cx18_open_id *id = fh2id(fh);579struct cx18 *cx = id->cx;580581if (inp >= cx->nof_inputs)582return -EINVAL;583584if (inp == cx->active_input) {585CX18_DEBUG_INFO("Input unchanged\n");586return 0;587}588589CX18_DEBUG_INFO("Changing input from %d to %d\n",590cx->active_input, inp);591592cx->active_input = inp;593/* Set the audio input to whatever is appropriate for the input type. */594cx->audio_input = cx->card->video_inputs[inp].audio_index;595596/* prevent others from messing with the streams until597we're finished changing inputs. */598cx18_mute(cx);599cx18_video_set_io(cx);600cx18_audio_set_io(cx);601cx18_unmute(cx);602return 0;603}604605static int cx18_g_frequency(struct file *file, void *fh,606struct v4l2_frequency *vf)607{608struct cx18 *cx = fh2id(fh)->cx;609610if (vf->tuner != 0)611return -EINVAL;612613cx18_call_all(cx, tuner, g_frequency, vf);614return 0;615}616617int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)618{619struct cx18_open_id *id = fh2id(fh);620struct cx18 *cx = id->cx;621622if (vf->tuner != 0)623return -EINVAL;624625cx18_mute(cx);626CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);627cx18_call_all(cx, tuner, s_frequency, vf);628cx18_unmute(cx);629return 0;630}631632static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std)633{634struct cx18 *cx = fh2id(fh)->cx;635636*std = cx->std;637return 0;638}639640int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)641{642struct cx18_open_id *id = fh2id(fh);643struct cx18 *cx = id->cx;644645if ((*std & V4L2_STD_ALL) == 0)646return -EINVAL;647648if (*std == cx->std)649return 0;650651if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||652atomic_read(&cx->ana_capturing) > 0) {653/* Switching standard would turn off the radio or mess654with already running streams, prevent that by655returning EBUSY. */656return -EBUSY;657}658659cx->std = *std;660cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;661cx->is_50hz = !cx->is_60hz;662cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz);663cx->cxhdl.width = 720;664cx->cxhdl.height = cx->is_50hz ? 576 : 480;665cx->vbi.count = cx->is_50hz ? 18 : 12;666cx->vbi.start[0] = cx->is_50hz ? 6 : 10;667cx->vbi.start[1] = cx->is_50hz ? 318 : 273;668CX18_DEBUG_INFO("Switching standard to %llx.\n",669(unsigned long long) cx->std);670671/* Tuner */672cx18_call_all(cx, core, s_std, cx->std);673return 0;674}675676static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)677{678struct cx18_open_id *id = fh2id(fh);679struct cx18 *cx = id->cx;680681if (vt->index != 0)682return -EINVAL;683684cx18_call_all(cx, tuner, s_tuner, vt);685return 0;686}687688static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)689{690struct cx18 *cx = fh2id(fh)->cx;691692if (vt->index != 0)693return -EINVAL;694695cx18_call_all(cx, tuner, g_tuner, vt);696697if (vt->type == V4L2_TUNER_RADIO)698strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));699else700strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));701return 0;702}703704static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,705struct v4l2_sliced_vbi_cap *cap)706{707struct cx18 *cx = fh2id(fh)->cx;708int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;709int f, l;710711if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)712return -EINVAL;713714cap->service_set = 0;715for (f = 0; f < 2; f++) {716for (l = 0; l < 24; l++) {717if (valid_service_line(f, l, cx->is_50hz)) {718/*719* We can find all v4l2 supported vbi services720* for the standard, on a valid line for the std721*/722cap->service_lines[f][l] = set;723cap->service_set |= set;724} else725cap->service_lines[f][l] = 0;726}727}728for (f = 0; f < 3; f++)729cap->reserved[f] = 0;730return 0;731}732733static int _cx18_process_idx_data(struct cx18_buffer *buf,734struct v4l2_enc_idx *idx)735{736int consumed, remaining;737struct v4l2_enc_idx_entry *e_idx;738struct cx18_enc_idx_entry *e_buf;739740/* Frame type lookup: 1=I, 2=P, 4=B */741const int mapping[8] = {742-1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P,743-1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1744};745746/*747* Assumption here is that a buf holds an integral number of748* struct cx18_enc_idx_entry objects and is properly aligned.749* This is enforced by the module options on IDX buffer sizes.750*/751remaining = buf->bytesused - buf->readpos;752consumed = 0;753e_idx = &idx->entry[idx->entries];754e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos];755756while (remaining >= sizeof(struct cx18_enc_idx_entry) &&757idx->entries < V4L2_ENC_IDX_ENTRIES) {758759e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32)760| le32_to_cpu(e_buf->offset_low);761762e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32)763| le32_to_cpu(e_buf->pts_low);764765e_idx->length = le32_to_cpu(e_buf->length);766767e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7];768769e_idx->reserved[0] = 0;770e_idx->reserved[1] = 0;771772idx->entries++;773e_idx = &idx->entry[idx->entries];774e_buf++;775776remaining -= sizeof(struct cx18_enc_idx_entry);777consumed += sizeof(struct cx18_enc_idx_entry);778}779780/* Swallow any partial entries at the end, if there are any */781if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry))782consumed += remaining;783784buf->readpos += consumed;785return consumed;786}787788static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,789struct v4l2_enc_idx *idx)790{791if (s->type != CX18_ENC_STREAM_TYPE_IDX)792return -EINVAL;793794if (mdl->curr_buf == NULL)795mdl->curr_buf = list_first_entry(&mdl->buf_list,796struct cx18_buffer, list);797798if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {799/*800* For some reason we've exhausted the buffers, but the MDL801* object still said some data was unread.802* Fix that and bail out.803*/804mdl->readpos = mdl->bytesused;805return 0;806}807808list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {809810/* Skip any empty buffers in the MDL */811if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)812continue;813814mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx);815816/* exit when MDL drained or request satisfied */817if (idx->entries >= V4L2_ENC_IDX_ENTRIES ||818mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||819mdl->readpos >= mdl->bytesused)820break;821}822return 0;823}824825static int cx18_g_enc_index(struct file *file, void *fh,826struct v4l2_enc_idx *idx)827{828struct cx18 *cx = fh2id(fh)->cx;829struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];830s32 tmp;831struct cx18_mdl *mdl;832833if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */834return -EINVAL;835836/* Compute the best case number of entries we can buffer */837tmp = s->buffers -838s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN;839if (tmp <= 0)840tmp = 1;841tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry);842843/* Fill out the header of the return structure */844idx->entries = 0;845idx->entries_cap = tmp;846memset(idx->reserved, 0, sizeof(idx->reserved));847848/* Pull IDX MDLs and buffers from q_full and populate the entries */849do {850mdl = cx18_dequeue(s, &s->q_full);851if (mdl == NULL) /* No more IDX data right now */852break;853854/* Extract the Index entry data from the MDL and buffers */855cx18_process_idx_data(s, mdl, idx);856if (mdl->readpos < mdl->bytesused) {857/* We finished with data remaining, push the MDL back */858cx18_push(s, mdl, &s->q_full);859break;860}861862/* We drained this MDL, schedule it to go to the firmware */863cx18_enqueue(s, mdl, &s->q_free);864865} while (idx->entries < V4L2_ENC_IDX_ENTRIES);866867/* Tell the work handler to send free IDX MDLs to the firmware */868cx18_stream_load_fw_queue(s);869return 0;870}871872static struct videobuf_queue *cx18_vb_queue(struct cx18_open_id *id)873{874struct videobuf_queue *q = NULL;875struct cx18 *cx = id->cx;876struct cx18_stream *s = &cx->streams[id->type];877878switch (s->vb_type) {879case V4L2_BUF_TYPE_VIDEO_CAPTURE:880q = &s->vbuf_q;881break;882case V4L2_BUF_TYPE_VBI_CAPTURE:883break;884default:885break;886}887return q;888}889890static int cx18_streamon(struct file *file, void *priv,891enum v4l2_buf_type type)892{893struct cx18_open_id *id = file->private_data;894struct cx18 *cx = id->cx;895struct cx18_stream *s = &cx->streams[id->type];896897/* Start the hardware only if we're the video device */898if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&899(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))900return -EINVAL;901902if (id->type != CX18_ENC_STREAM_TYPE_YUV)903return -EINVAL;904905/* Establish a buffer timeout */906mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies);907908return videobuf_streamon(cx18_vb_queue(id));909}910911static int cx18_streamoff(struct file *file, void *priv,912enum v4l2_buf_type type)913{914struct cx18_open_id *id = file->private_data;915struct cx18 *cx = id->cx;916struct cx18_stream *s = &cx->streams[id->type];917918/* Start the hardware only if we're the video device */919if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&920(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))921return -EINVAL;922923if (id->type != CX18_ENC_STREAM_TYPE_YUV)924return -EINVAL;925926return videobuf_streamoff(cx18_vb_queue(id));927}928929static int cx18_reqbufs(struct file *file, void *priv,930struct v4l2_requestbuffers *rb)931{932struct cx18_open_id *id = file->private_data;933struct cx18 *cx = id->cx;934struct cx18_stream *s = &cx->streams[id->type];935936if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&937(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))938return -EINVAL;939940return videobuf_reqbufs(cx18_vb_queue(id), rb);941}942943static int cx18_querybuf(struct file *file, void *priv,944struct v4l2_buffer *b)945{946struct cx18_open_id *id = file->private_data;947struct cx18 *cx = id->cx;948struct cx18_stream *s = &cx->streams[id->type];949950if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&951(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))952return -EINVAL;953954return videobuf_querybuf(cx18_vb_queue(id), b);955}956957static int cx18_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)958{959struct cx18_open_id *id = file->private_data;960struct cx18 *cx = id->cx;961struct cx18_stream *s = &cx->streams[id->type];962963if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&964(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))965return -EINVAL;966967return videobuf_qbuf(cx18_vb_queue(id), b);968}969970static int cx18_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)971{972struct cx18_open_id *id = file->private_data;973struct cx18 *cx = id->cx;974struct cx18_stream *s = &cx->streams[id->type];975976if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&977(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))978return -EINVAL;979980return videobuf_dqbuf(cx18_vb_queue(id), b, file->f_flags & O_NONBLOCK);981}982983static int cx18_encoder_cmd(struct file *file, void *fh,984struct v4l2_encoder_cmd *enc)985{986struct cx18_open_id *id = fh2id(fh);987struct cx18 *cx = id->cx;988u32 h;989990switch (enc->cmd) {991case V4L2_ENC_CMD_START:992CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");993enc->flags = 0;994return cx18_start_capture(id);995996case V4L2_ENC_CMD_STOP:997CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");998enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;999cx18_stop_capture(id,1000enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);1001break;10021003case V4L2_ENC_CMD_PAUSE:1004CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");1005enc->flags = 0;1006if (!atomic_read(&cx->ana_capturing))1007return -EPERM;1008if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))1009return 0;1010h = cx18_find_handle(cx);1011if (h == CX18_INVALID_TASK_HANDLE) {1012CX18_ERR("Can't find valid task handle for "1013"V4L2_ENC_CMD_PAUSE\n");1014return -EBADFD;1015}1016cx18_mute(cx);1017cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h);1018break;10191020case V4L2_ENC_CMD_RESUME:1021CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");1022enc->flags = 0;1023if (!atomic_read(&cx->ana_capturing))1024return -EPERM;1025if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))1026return 0;1027h = cx18_find_handle(cx);1028if (h == CX18_INVALID_TASK_HANDLE) {1029CX18_ERR("Can't find valid task handle for "1030"V4L2_ENC_CMD_RESUME\n");1031return -EBADFD;1032}1033cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h);1034cx18_unmute(cx);1035break;10361037default:1038CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);1039return -EINVAL;1040}1041return 0;1042}10431044static int cx18_try_encoder_cmd(struct file *file, void *fh,1045struct v4l2_encoder_cmd *enc)1046{1047struct cx18 *cx = fh2id(fh)->cx;10481049switch (enc->cmd) {1050case V4L2_ENC_CMD_START:1051CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");1052enc->flags = 0;1053break;10541055case V4L2_ENC_CMD_STOP:1056CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");1057enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;1058break;10591060case V4L2_ENC_CMD_PAUSE:1061CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");1062enc->flags = 0;1063break;10641065case V4L2_ENC_CMD_RESUME:1066CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");1067enc->flags = 0;1068break;10691070default:1071CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);1072return -EINVAL;1073}1074return 0;1075}10761077static int cx18_log_status(struct file *file, void *fh)1078{1079struct cx18 *cx = fh2id(fh)->cx;1080struct v4l2_input vidin;1081struct v4l2_audio audin;1082int i;10831084CX18_INFO("================= START STATUS CARD #%d "1085"=================\n", cx->instance);1086CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name);1087if (cx->hw_flags & CX18_HW_TVEEPROM) {1088struct tveeprom tv;10891090cx18_read_eeprom(cx, &tv);1091}1092cx18_call_all(cx, core, log_status);1093cx18_get_input(cx, cx->active_input, &vidin);1094cx18_get_audio_input(cx, cx->audio_input, &audin);1095CX18_INFO("Video Input: %s\n", vidin.name);1096CX18_INFO("Audio Input: %s\n", audin.name);1097mutex_lock(&cx->gpio_lock);1098CX18_INFO("GPIO: direction 0x%08x, value 0x%08x\n",1099cx->gpio_dir, cx->gpio_val);1100mutex_unlock(&cx->gpio_lock);1101CX18_INFO("Tuner: %s\n",1102test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV");1103v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name);1104CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);1105for (i = 0; i < CX18_MAX_STREAMS; i++) {1106struct cx18_stream *s = &cx->streams[i];11071108if (s->video_dev == NULL || s->buffers == 0)1109continue;1110CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",1111s->name, s->s_flags,1112atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 1001113/ s->buffers,1114(s->buffers * s->buf_size) / 1024, s->buffers);1115}1116CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",1117(long long)cx->mpg_data_received,1118(long long)cx->vbi_data_inserted);1119CX18_INFO("================== END STATUS CARD #%d "1120"==================\n", cx->instance);1121return 0;1122}11231124static long cx18_default(struct file *file, void *fh, bool valid_prio,1125int cmd, void *arg)1126{1127struct cx18 *cx = fh2id(fh)->cx;11281129switch (cmd) {1130case VIDIOC_INT_RESET: {1131u32 val = *(u32 *)arg;11321133if ((val == 0) || (val & 0x01))1134cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset,1135(u32) CX18_GPIO_RESET_Z8F0811);1136break;1137}11381139default:1140return -EINVAL;1141}1142return 0;1143}11441145long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,1146unsigned long arg)1147{1148struct video_device *vfd = video_devdata(filp);1149struct cx18_open_id *id = file2id(filp);1150struct cx18 *cx = id->cx;1151long res;11521153mutex_lock(&cx->serialize_lock);11541155if (cx18_debug & CX18_DBGFLG_IOCTL)1156vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;1157res = video_ioctl2(filp, cmd, arg);1158vfd->debug = 0;1159mutex_unlock(&cx->serialize_lock);1160return res;1161}11621163static const struct v4l2_ioctl_ops cx18_ioctl_ops = {1164.vidioc_querycap = cx18_querycap,1165.vidioc_s_audio = cx18_s_audio,1166.vidioc_g_audio = cx18_g_audio,1167.vidioc_enumaudio = cx18_enumaudio,1168.vidioc_enum_input = cx18_enum_input,1169.vidioc_cropcap = cx18_cropcap,1170.vidioc_s_crop = cx18_s_crop,1171.vidioc_g_crop = cx18_g_crop,1172.vidioc_g_input = cx18_g_input,1173.vidioc_s_input = cx18_s_input,1174.vidioc_g_frequency = cx18_g_frequency,1175.vidioc_s_frequency = cx18_s_frequency,1176.vidioc_s_tuner = cx18_s_tuner,1177.vidioc_g_tuner = cx18_g_tuner,1178.vidioc_g_enc_index = cx18_g_enc_index,1179.vidioc_g_std = cx18_g_std,1180.vidioc_s_std = cx18_s_std,1181.vidioc_log_status = cx18_log_status,1182.vidioc_enum_fmt_vid_cap = cx18_enum_fmt_vid_cap,1183.vidioc_encoder_cmd = cx18_encoder_cmd,1184.vidioc_try_encoder_cmd = cx18_try_encoder_cmd,1185.vidioc_g_fmt_vid_cap = cx18_g_fmt_vid_cap,1186.vidioc_g_fmt_vbi_cap = cx18_g_fmt_vbi_cap,1187.vidioc_g_fmt_sliced_vbi_cap = cx18_g_fmt_sliced_vbi_cap,1188.vidioc_s_fmt_vid_cap = cx18_s_fmt_vid_cap,1189.vidioc_s_fmt_vbi_cap = cx18_s_fmt_vbi_cap,1190.vidioc_s_fmt_sliced_vbi_cap = cx18_s_fmt_sliced_vbi_cap,1191.vidioc_try_fmt_vid_cap = cx18_try_fmt_vid_cap,1192.vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap,1193.vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap,1194.vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap,1195.vidioc_g_chip_ident = cx18_g_chip_ident,1196#ifdef CONFIG_VIDEO_ADV_DEBUG1197.vidioc_g_register = cx18_g_register,1198.vidioc_s_register = cx18_s_register,1199#endif1200.vidioc_default = cx18_default,1201.vidioc_streamon = cx18_streamon,1202.vidioc_streamoff = cx18_streamoff,1203.vidioc_reqbufs = cx18_reqbufs,1204.vidioc_querybuf = cx18_querybuf,1205.vidioc_qbuf = cx18_qbuf,1206.vidioc_dqbuf = cx18_dqbuf,1207};12081209void cx18_set_funcs(struct video_device *vdev)1210{1211vdev->ioctl_ops = &cx18_ioctl_ops;1212}121312141215