linux/drivers/media/pci/saa7164/saa7164-encoder.c

1146 lines
30 KiB
C
Raw Normal View History

treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 157 Based on 3 normalized pattern(s): 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 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 [author] [kishon] [vijay] [abraham] [i] [kishon]@[ti] [com] 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 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 [author] [graeme] [gregory] [gg]@[slimlogic] [co] [uk] [author] [kishon] [vijay] [abraham] [i] [kishon]@[ti] [com] [based] [on] [twl6030]_[usb] [c] [author] [hema] [hk] [hemahk]@[ti] [com] 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 extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 1105 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Richard Fontana <rfontana@redhat.com> Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070033.202006027@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-05-27 14:55:06 +08:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the NXP SAA7164 PCIe bridge
*
* Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com>
*/
#include "saa7164.h"
#define ENCODER_MAX_BITRATE 6500000
#define ENCODER_MIN_BITRATE 1000000
#define ENCODER_DEF_BITRATE 5000000
/*
* This is a dummy non-zero value for the sizeimage field of v4l2_pix_format.
* It is not actually used for anything since this driver does not support
* stream I/O, only read(), and because this driver produces an MPEG stream
* and not discrete frames. But the V4L2 spec doesn't allow for this value
* to be 0, so set it to 0x10000 instead.
*
* If we ever change this driver to support stream I/O, then this field
* will be the size of the streaming buffers.
*/
#define SAA7164_SIZEIMAGE (0x10000)
static struct saa7164_tvnorm saa7164_tvnorms[] = {
{
.name = "NTSC-M",
.id = V4L2_STD_NTSC_M,
}, {
.name = "NTSC-JP",
.id = V4L2_STD_NTSC_M_JP,
}
};
/* Take the encoder configuration form the port struct and
* flush it to the hardware.
*/
static void saa7164_encoder_configure(struct saa7164_port *port)
{
struct saa7164_dev *dev = port->dev;
dprintk(DBGLVL_ENC, "%s()\n", __func__);
port->encoder_params.width = port->width;
port->encoder_params.height = port->height;
port->encoder_params.is_50hz =
(port->encodernorm.id & V4L2_STD_625_50) != 0;
/* Set up the DIF (enable it) for analog mode by default */
saa7164_api_initialize_dif(port);
/* Configure the correct video standard */
saa7164_api_configure_dif(port, port->encodernorm.id);
/* Ensure the audio decoder is correct configured */
saa7164_api_set_audio_std(port);
}
static int saa7164_encoder_buffers_dealloc(struct saa7164_port *port)
{
struct list_head *c, *n, *p, *q, *l, *v;
struct saa7164_dev *dev = port->dev;
struct saa7164_buffer *buf;
struct saa7164_user_buffer *ubuf;
/* Remove any allocated buffers */
mutex_lock(&port->dmaqueue_lock);
dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr);
list_for_each_safe(c, n, &port->dmaqueue.list) {
buf = list_entry(c, struct saa7164_buffer, list);
list_del(c);
saa7164_buffer_dealloc(buf);
}
dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr);
list_for_each_safe(p, q, &port->list_buf_used.list) {
ubuf = list_entry(p, struct saa7164_user_buffer, list);
list_del(p);
saa7164_buffer_dealloc_user(ubuf);
}
dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr);
list_for_each_safe(l, v, &port->list_buf_free.list) {
ubuf = list_entry(l, struct saa7164_user_buffer, list);
list_del(l);
saa7164_buffer_dealloc_user(ubuf);
}
mutex_unlock(&port->dmaqueue_lock);
dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
return 0;
}
/* Dynamic buffer switch at encoder start time */
static int saa7164_encoder_buffers_alloc(struct saa7164_port *port)
{
struct saa7164_dev *dev = port->dev;
struct saa7164_buffer *buf;
struct saa7164_user_buffer *ubuf;
[media] saa7164: Don't use typedefs According with CodingStyle, drivers shouldn't use typedef, except on very special cases. This is not the case of saa7164. So, convert all usecases to struct/enum. After changing the saa7164-types.h, all we need to do is to run those scripts to fix all occurrences of the bad types and double check/fix everything that might be broken after the test (of course, I did a small script to generate those scripts). for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmBusType_t/enum tmBusType/; print " \>a \&\& mv a tmBusType; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResCmd_t/enum tmComResCmd/; print " \>a \&\& mv a tmComResCmd; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResTermType_t/enum tmComResTermType/; print " \>a \&\& mv a tmComResTermType; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmBufferFlag_t/enum tmBufferFlag/; print " \>a \&\& mv a tmBufferFlag; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResHWDescr_t/struct tmComResHWDescr/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResInterfaceDescr_t/struct tmComResInterfaceDescr/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResBusDescr_t/struct tmComResBusDescr/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmBusType_t/struct tmBusType/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResBusInfo_t/struct tmComResBusInfo/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResInfo_t/struct tmComResInfo/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResCmd_t/struct tmComResCmd/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmDescriptor_t/struct tmDescriptor/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResDescrHeader_t/struct tmComResDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResExtDevDescrHeader_t/struct tmComResExtDevDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResGPIO_t/struct tmComResGPIO/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResPathDescrHeader_t/struct tmComResPathDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResTermType_t/struct tmComResTermType/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResAntTermDescrHeader_t/struct tmComResAntTermDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResTunerDescrHeader_t/struct tmComResTunerDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmBufferFlag_t/struct tmBufferFlag/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmBuffer_t/struct tmBuffer/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmHWStreamParameters_t/struct tmHWStreamParameters/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmStreamParameters_t/struct tmStreamParameters/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResDMATermDescrHeader_t/struct tmComResDMATermDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResTSFormatDescrHeader_t/struct tmComResTSFormatDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResSelDescrHeader_t/struct tmComResSelDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResProcDescrHeader_t/struct tmComResProcDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResEncVideoBitRate_t/struct tmComResEncVideoBitRate/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResEncVideoInputAspectRatio_t/struct tmComResEncVideoInputAspectRatio/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResEncVideoGopStructure_t/struct tmComResEncVideoGopStructure/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResEncoderDescrHeader_t/struct tmComResEncoderDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResAFeatureDescrHeader_t/struct tmComResAFeatureDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResAudioDefaults_t/struct tmComResAudioDefaults/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResEncAudioBitRate_t/struct tmComResEncAudioBitRate/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResTunerStandard_t/struct tmComResTunerStandard/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResTunerStandardAuto_t/struct tmComResTunerStandardAuto/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResPSFormatDescrHeader_t/struct tmComResPSFormatDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResVBIFormatDescrHeader_t/struct tmComResVBIFormatDescrHeader/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResProbeCommit_t/struct tmComResProbeCommit/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResDebugSetLevel_t/struct tmComResDebugSetLevel/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmComResDebugGetData_t/struct tmComResDebugGetData/g; print " $i >a && mv a $i; done for i in drivers/media/video/saa7164/*.[ch]; do perl -ne "s/tmFwInfoStruct_t/struct tmFwInfoStruct/g; print " $i >a && mv a $i; done Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2010-10-12 04:17:45 +08:00
struct tmHWStreamParameters *params = &port->hw_streamingparams;
int result = -ENODEV, i;
int len = 0;
dprintk(DBGLVL_ENC, "%s()\n", __func__);
if (port->encoder_params.stream_type ==
V4L2_MPEG_STREAM_TYPE_MPEG2_PS) {
dprintk(DBGLVL_ENC,
"%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_PS\n",
__func__);
params->samplesperline = 128;
params->numberoflines = 256;
params->pitch = 128;
params->numpagetables = 2 +
((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE);
} else
if (port->encoder_params.stream_type ==
V4L2_MPEG_STREAM_TYPE_MPEG2_TS) {
dprintk(DBGLVL_ENC,
"%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_TS\n",
__func__);
params->samplesperline = 188;
params->numberoflines = 312;
params->pitch = 188;
params->numpagetables = 2 +
((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE);
} else
BUG();
/* Init and establish defaults */
params->bitspersample = 8;
params->linethreshold = 0;
params->pagetablelistvirt = NULL;
params->pagetablelistphys = NULL;
params->numpagetableentries = port->hwcfg.buffercount;
/* Allocate the PCI resources, buffers (hard) */
for (i = 0; i < port->hwcfg.buffercount; i++) {
buf = saa7164_buffer_alloc(port,
params->numberoflines *
params->pitch);
if (!buf) {
printk(KERN_ERR "%s() failed (errno = %d), unable to allocate buffer\n",
__func__, result);
result = -ENOMEM;
goto failed;
} else {
mutex_lock(&port->dmaqueue_lock);
list_add_tail(&buf->list, &port->dmaqueue.list);
mutex_unlock(&port->dmaqueue_lock);
}
}
/* Allocate some kernel buffers for copying
* to userpsace.
*/
len = params->numberoflines * params->pitch;
if (encoder_buffers < 16)
encoder_buffers = 16;
if (encoder_buffers > 512)
encoder_buffers = 512;
for (i = 0; i < encoder_buffers; i++) {
ubuf = saa7164_buffer_alloc_user(dev, len);
if (ubuf) {
mutex_lock(&port->dmaqueue_lock);
list_add_tail(&ubuf->list, &port->list_buf_free.list);
mutex_unlock(&port->dmaqueue_lock);
}
}
result = 0;
failed:
return result;
}
static int saa7164_encoder_initialize(struct saa7164_port *port)
{
saa7164_encoder_configure(port);
return 0;
}
/* -- V4L2 --------------------------------------------------------- */
int saa7164_s_std(struct saa7164_port *port, v4l2_std_id id)
{
struct saa7164_dev *dev = port->dev;
unsigned int i;
dprintk(DBGLVL_ENC, "%s(id=0x%x)\n", __func__, (u32)id);
for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) {
if (id & saa7164_tvnorms[i].id)
break;
}
if (i == ARRAY_SIZE(saa7164_tvnorms))
return -EINVAL;
port->encodernorm = saa7164_tvnorms[i];
port->std = id;
/* Update the audio decoder while is not running in
* auto detect mode.
*/
saa7164_api_set_audio_std(port);
dprintk(DBGLVL_ENC, "%s(id=0x%x) OK\n", __func__, (u32)id);
return 0;
}
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)
{
struct saa7164_encoder_fh *fh = file->private_data;
return saa7164_s_std(fh->port, id);
}
int saa7164_g_std(struct saa7164_port *port, v4l2_std_id *id)
{
*id = port->std;
return 0;
}
static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
{
struct saa7164_encoder_fh *fh = file->private_data;
return saa7164_g_std(fh->port, id);
}
int saa7164_enum_input(struct file *file, void *priv, struct v4l2_input *i)
{
static const char * const inputs[] = {
"tuner", "composite", "svideo", "aux",
"composite 2", "svideo 2", "aux 2"
};
int n;
if (i->index >= 7)
return -EINVAL;
strscpy(i->name, inputs[i->index], sizeof(i->name));
if (i->index == 0)
i->type = V4L2_INPUT_TYPE_TUNER;
else
i->type = V4L2_INPUT_TYPE_CAMERA;
for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++)
i->std |= saa7164_tvnorms[n].id;
return 0;
}
int saa7164_g_input(struct saa7164_port *port, unsigned int *i)
{
struct saa7164_dev *dev = port->dev;
if (saa7164_api_get_videomux(port) != SAA_OK)
return -EIO;
*i = (port->mux_input - 1);
dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, *i);
return 0;
}
static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
{
struct saa7164_encoder_fh *fh = file->private_data;
return saa7164_g_input(fh->port, i);
}
int saa7164_s_input(struct saa7164_port *port, unsigned int i)
{
struct saa7164_dev *dev = port->dev;
dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, i);
if (i >= 7)
return -EINVAL;
port->mux_input = i + 1;
if (saa7164_api_set_videomux(port) != SAA_OK)
return -EIO;
return 0;
}
static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
struct saa7164_encoder_fh *fh = file->private_data;
return saa7164_s_input(fh->port, i);
}
int saa7164_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
{
struct saa7164_encoder_fh *fh = file->private_data;
struct saa7164_port *port = fh->port;
struct saa7164_dev *dev = port->dev;
if (0 != t->index)
return -EINVAL;
strscpy(t->name, "tuner", sizeof(t->name));
t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO;
t->rangelow = SAA7164_TV_MIN_FREQ;
t->rangehigh = SAA7164_TV_MAX_FREQ;
dprintk(DBGLVL_ENC, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
return 0;
}
int saa7164_s_tuner(struct file *file, void *priv,
const struct v4l2_tuner *t)
{
if (0 != t->index)
return -EINVAL;
/* Update the A/V core */
return 0;
}
int saa7164_g_frequency(struct saa7164_port *port, struct v4l2_frequency *f)
{
if (f->tuner)
return -EINVAL;
f->frequency = port->freq;
return 0;
}
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct saa7164_encoder_fh *fh = file->private_data;
return saa7164_g_frequency(fh->port, f);
}
int saa7164_s_frequency(struct saa7164_port *port,
const struct v4l2_frequency *f)
{
struct saa7164_dev *dev = port->dev;
struct saa7164_port *tsport;
struct dvb_frontend *fe;
/* TODO: Pull this for the std */
struct analog_parameters params = {
.mode = V4L2_TUNER_ANALOG_TV,
.audmode = V4L2_TUNER_MODE_STEREO,
.std = port->encodernorm.id,
.frequency = f->frequency
};
/* Stop the encoder */
dprintk(DBGLVL_ENC, "%s() frequency=%d tuner=%d\n", __func__,
f->frequency, f->tuner);
if (f->tuner != 0)
return -EINVAL;
port->freq = clamp(f->frequency,
SAA7164_TV_MIN_FREQ, SAA7164_TV_MAX_FREQ);
/* Update the hardware */
if (port->nr == SAA7164_PORT_ENC1)
tsport = &dev->ports[SAA7164_PORT_TS1];
else if (port->nr == SAA7164_PORT_ENC2)
tsport = &dev->ports[SAA7164_PORT_TS2];
else
BUG();
fe = tsport->dvb.frontend;
if (fe && fe->ops.tuner_ops.set_analog_params)
fe->ops.tuner_ops.set_analog_params(fe, &params);
else
printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
saa7164_encoder_initialize(port);
return 0;
}
static int vidioc_s_frequency(struct file *file, void *priv,
const struct v4l2_frequency *f)
{
struct saa7164_encoder_fh *fh = file->private_data;
return saa7164_s_frequency(fh->port, f);
}
static int saa7164_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct saa7164_port *port =
container_of(ctrl->handler, struct saa7164_port, ctrl_handler);
struct saa7164_encoder_params *params = &port->encoder_params;
int ret = 0;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
port->ctl_brightness = ctrl->val;
saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL);
break;
case V4L2_CID_CONTRAST:
port->ctl_contrast = ctrl->val;
saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
break;
case V4L2_CID_SATURATION:
port->ctl_saturation = ctrl->val;
saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL);
break;
case V4L2_CID_HUE:
port->ctl_hue = ctrl->val;
saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
break;
case V4L2_CID_SHARPNESS:
port->ctl_sharpness = ctrl->val;
saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
break;
case V4L2_CID_AUDIO_VOLUME:
port->ctl_volume = ctrl->val;
saa7164_api_set_audio_volume(port, port->ctl_volume);
break;
case V4L2_CID_MPEG_VIDEO_BITRATE:
params->bitrate = ctrl->val;
break;
case V4L2_CID_MPEG_STREAM_TYPE:
params->stream_type = ctrl->val;
break;
case V4L2_CID_MPEG_AUDIO_MUTE:
params->ctl_mute = ctrl->val;
ret = saa7164_api_audio_mute(port, params->ctl_mute);
if (ret != SAA_OK) {
printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
ret);
ret = -EIO;
}
break;
case V4L2_CID_MPEG_VIDEO_ASPECT:
params->ctl_aspect = ctrl->val;
ret = saa7164_api_set_aspect_ratio(port);
if (ret != SAA_OK) {
printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
ret);
ret = -EIO;
}
break;
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
params->bitrate_mode = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_B_FRAMES:
params->refdist = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
params->bitrate_peak = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
params->gop_size = ctrl->val;
break;
default:
ret = -EINVAL;
}
return ret;
}
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct saa7164_encoder_fh *fh = file->private_data;
struct saa7164_port *port = fh->port;
struct saa7164_dev *dev = port->dev;
strscpy(cap->driver, dev->name, sizeof(cap->driver));
strscpy(cap->card, saa7164_boards[dev->board].name,
sizeof(cap->card));
sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE |
V4L2_CAP_DEVICE_CAPS;
return 0;
}
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index != 0)
return -EINVAL;
f->pixelformat = V4L2_PIX_FMT_MPEG;
return 0;
}
static int vidioc_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7164_encoder_fh *fh = file->private_data;
struct saa7164_port *port = fh->port;
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
f->fmt.pix.bytesperline = 0;
f->fmt.pix.sizeimage = SAA7164_SIZEIMAGE;
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
f->fmt.pix.width = port->width;
f->fmt.pix.height = port->height;
return 0;
}
static int saa7164_encoder_stop_port(struct saa7164_port *port)
{
struct saa7164_dev *dev = port->dev;
int ret;
ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
__func__, ret);
ret = -EIO;
} else {
dprintk(DBGLVL_ENC, "%s() Stopped\n", __func__);
ret = 0;
}
return ret;
}
static int saa7164_encoder_acquire_port(struct saa7164_port *port)
{
struct saa7164_dev *dev = port->dev;
int ret;
ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
__func__, ret);
ret = -EIO;
} else {
dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__);
ret = 0;
}
return ret;
}
static int saa7164_encoder_pause_port(struct saa7164_port *port)
{
struct saa7164_dev *dev = port->dev;
int ret;
ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
__func__, ret);
ret = -EIO;
} else {
dprintk(DBGLVL_ENC, "%s() Paused\n", __func__);
ret = 0;
}
return ret;
}
/* Firmware is very windows centric, meaning you have to transition
* the part through AVStream / KS Windows stages, forwards or backwards.
* States are: stopped, acquired (h/w), paused, started.
* We have to leave here will all of the soft buffers on the free list,
* else the cfg_post() func won't have soft buffers to correctly configure.
*/
static int saa7164_encoder_stop_streaming(struct saa7164_port *port)
{
struct saa7164_dev *dev = port->dev;
struct saa7164_buffer *buf;
struct saa7164_user_buffer *ubuf;
struct list_head *c, *n;
int ret;
dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
ret = saa7164_encoder_pause_port(port);
ret = saa7164_encoder_acquire_port(port);
ret = saa7164_encoder_stop_port(port);
dprintk(DBGLVL_ENC, "%s(port=%d) Hardware stopped\n", __func__,
port->nr);
/* Reset the state of any allocated buffer resources */
mutex_lock(&port->dmaqueue_lock);
/* Reset the hard and soft buffer state */
list_for_each_safe(c, n, &port->dmaqueue.list) {
buf = list_entry(c, struct saa7164_buffer, list);
buf->flags = SAA7164_BUFFER_FREE;
buf->pos = 0;
}
list_for_each_safe(c, n, &port->list_buf_used.list) {
ubuf = list_entry(c, struct saa7164_user_buffer, list);
ubuf->pos = 0;
list_move_tail(&ubuf->list, &port->list_buf_free.list);
}
mutex_unlock(&port->dmaqueue_lock);
/* Free any allocated resources */
saa7164_encoder_buffers_dealloc(port);
dprintk(DBGLVL_ENC, "%s(port=%d) Released\n", __func__, port->nr);
return ret;
}
static int saa7164_encoder_start_streaming(struct saa7164_port *port)
{
struct saa7164_dev *dev = port->dev;
int result, ret = 0;
dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
port->done_first_interrupt = 0;
/* allocate all of the PCIe DMA buffer resources on the fly,
* allowing switching between TS and PS payloads without
* requiring a complete driver reload.
*/
saa7164_encoder_buffers_alloc(port);
/* Configure the encoder with any cache values */
saa7164_api_set_encoder(port);
saa7164_api_get_encoder(port);
/* Place the empty buffers on the hardware */
saa7164_buffer_cfg_port(port);
/* Acquire the hardware */
result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
__func__, result);
/* Stop the hardware, regardless */
result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
printk(KERN_ERR "%s() acquire/forced stop transition failed, res = 0x%x\n",
__func__, result);
}
ret = -EIO;
goto out;
} else
dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__);
/* Pause the hardware */
result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
__func__, result);
/* Stop the hardware, regardless */
result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
printk(KERN_ERR "%s() pause/forced stop transition failed, res = 0x%x\n",
__func__, result);
}
ret = -EIO;
goto out;
} else
dprintk(DBGLVL_ENC, "%s() Paused\n", __func__);
/* Start the hardware */
result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
__func__, result);
/* Stop the hardware, regardless */
result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
printk(KERN_ERR "%s() run/forced stop transition failed, res = 0x%x\n",
__func__, result);
}
ret = -EIO;
} else
dprintk(DBGLVL_ENC, "%s() Running\n", __func__);
out:
return ret;
}
static int fops_open(struct file *file)
{
struct saa7164_dev *dev;
struct saa7164_port *port;
struct saa7164_encoder_fh *fh;
port = (struct saa7164_port *)video_get_drvdata(video_devdata(file));
if (!port)
return -ENODEV;
dev = port->dev;
dprintk(DBGLVL_ENC, "%s()\n", __func__);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh)
return -ENOMEM;
fh->port = port;
v4l2_fh_init(&fh->fh, video_devdata(file));
v4l2_fh_add(&fh->fh);
file->private_data = fh;
return 0;
}
static int fops_release(struct file *file)
{
struct saa7164_encoder_fh *fh = file->private_data;
struct saa7164_port *port = fh->port;
struct saa7164_dev *dev = port->dev;
dprintk(DBGLVL_ENC, "%s()\n", __func__);
/* Shut device down on last close */
if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
if (atomic_dec_return(&port->v4l_reader_count) == 0) {
/* stop mpeg capture then cancel buffers */
saa7164_encoder_stop_streaming(port);
}
}
v4l2_fh_del(&fh->fh);
v4l2_fh_exit(&fh->fh);
kfree(fh);
return 0;
}
[media] saa7164: get rid of warning: no previous prototype drivers/media/pci/saa7164/saa7164-api.c:1489:5: warning: no previous prototype for 'saa7164_api_modify_gpio' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-api.c:168:5: warning: no previous prototype for 'saa7164_api_set_gop_size' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-api.c:622:5: warning: no previous prototype for 'saa7164_api_set_dif' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-api.c:825:5: warning: no previous prototype for 'saa7164_api_configure_port_vbi' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-api.c:861:5: warning: no previous prototype for 'saa7164_api_configure_port_mpeg2ts' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-api.c:895:5: warning: no previous prototype for 'saa7164_api_configure_port_mpeg2ps' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-api.c:928:5: warning: no previous prototype for 'saa7164_api_dump_subdevs' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-bus.c:109:6: warning: no previous prototype for 'saa7164_bus_dumpmsg' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-bus.c:84:6: warning: no previous prototype for 'saa7164_bus_verify' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-cmd.c:135:5: warning: no previous prototype for 'saa7164_cmd_dequeue' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-cmd.c:189:5: warning: no previous prototype for 'saa7164_cmd_set' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-cmd.c:262:5: warning: no previous prototype for 'saa7164_cmd_wait' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-cmd.c:26:5: warning: no previous prototype for 'saa7164_cmd_alloc_seqno' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-cmd.c:45:6: warning: no previous prototype for 'saa7164_cmd_free_seqno' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-cmd.c:57:6: warning: no previous prototype for 'saa7164_cmd_timeout_seqno' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-cmd.c:67:5: warning: no previous prototype for 'saa7164_cmd_timeout_get' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-encoder.c:1104:29: warning: no previous prototype for 'saa7164_enc_next_buf' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-encoder.c:1290:5: warning: no previous prototype for 'saa7164_g_chip_ident' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-encoder.c:1300:5: warning: no previous prototype for 'saa7164_g_register' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-encoder.c:1313:5: warning: no previous prototype for 'saa7164_s_register' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-fw.c:40:5: warning: no previous prototype for 'saa7164_dl_wait_ack' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-fw.c:56:5: warning: no previous prototype for 'saa7164_dl_wait_clr' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-fw.c:74:5: warning: no previous prototype for 'saa7164_downloadimage' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-vbi.c:1050:29: warning: no previous prototype for 'saa7164_vbi_next_buf' [-Wmissing-prototypes] drivers/media/pci/saa7164/saa7164-vbi.c:987:5: warning: no previous prototype for 'saa7164_vbi_fmt' [-Wmissing-prototypes] Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2012-10-28 00:11:11 +08:00
static struct
saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port)
{
struct saa7164_user_buffer *ubuf = NULL;
struct saa7164_dev *dev = port->dev;
u32 crc;
mutex_lock(&port->dmaqueue_lock);
if (!list_empty(&port->list_buf_used.list)) {
ubuf = list_first_entry(&port->list_buf_used.list,
struct saa7164_user_buffer, list);
if (crc_checking) {
crc = crc32(0, ubuf->data, ubuf->actual_size);
if (crc != ubuf->crc) {
printk(KERN_ERR
"%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n",
__func__,
ubuf, ubuf->crc, crc);
}
}
}
mutex_unlock(&port->dmaqueue_lock);
dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, ubuf);
return ubuf;
}
static ssize_t fops_read(struct file *file, char __user *buffer,
size_t count, loff_t *pos)
{
struct saa7164_encoder_fh *fh = file->private_data;
struct saa7164_port *port = fh->port;
struct saa7164_user_buffer *ubuf = NULL;
struct saa7164_dev *dev = port->dev;
int ret = 0;
int rem, cnt;
u8 *p;
port->last_read_msecs_diff = port->last_read_msecs;
port->last_read_msecs = jiffies_to_msecs(jiffies);
port->last_read_msecs_diff = port->last_read_msecs -
port->last_read_msecs_diff;
saa7164_histogram_update(&port->read_interval,
port->last_read_msecs_diff);
if (*pos) {
printk(KERN_ERR "%s() ESPIPE\n", __func__);
return -ESPIPE;
}
if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
if (atomic_inc_return(&port->v4l_reader_count) == 1) {
if (saa7164_encoder_initialize(port) < 0) {
printk(KERN_ERR "%s() EINVAL\n", __func__);
return -EINVAL;
}
saa7164_encoder_start_streaming(port);
msleep(200);
}
}
/* blocking wait for buffer */
if ((file->f_flags & O_NONBLOCK) == 0) {
if (wait_event_interruptible(port->wait_read,
saa7164_enc_next_buf(port))) {
printk(KERN_ERR "%s() ERESTARTSYS\n", __func__);
return -ERESTARTSYS;
}
}
/* Pull the first buffer from the used list */
ubuf = saa7164_enc_next_buf(port);
while ((count > 0) && ubuf) {
/* set remaining bytes to copy */
rem = ubuf->actual_size - ubuf->pos;
cnt = rem > count ? count : rem;
p = ubuf->data + ubuf->pos;
dprintk(DBGLVL_ENC,
"%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n",
__func__, (int)count, cnt, rem, ubuf, ubuf->pos);
if (copy_to_user(buffer, p, cnt)) {
printk(KERN_ERR "%s() copy_to_user failed\n", __func__);
if (!ret) {
printk(KERN_ERR "%s() EFAULT\n", __func__);
ret = -EFAULT;
}
goto err;
}
ubuf->pos += cnt;
count -= cnt;
buffer += cnt;
ret += cnt;
if (ubuf->pos > ubuf->actual_size)
printk(KERN_ERR "read() pos > actual, huh?\n");
if (ubuf->pos == ubuf->actual_size) {
/* finished with current buffer, take next buffer */
/* Requeue the buffer on the free list */
ubuf->pos = 0;
mutex_lock(&port->dmaqueue_lock);
list_move_tail(&ubuf->list, &port->list_buf_free.list);
mutex_unlock(&port->dmaqueue_lock);
/* Dequeue next */
if ((file->f_flags & O_NONBLOCK) == 0) {
if (wait_event_interruptible(port->wait_read,
saa7164_enc_next_buf(port))) {
break;
}
}
ubuf = saa7164_enc_next_buf(port);
}
}
err:
if (!ret && !ubuf)
ret = -EAGAIN;
return ret;
}
static __poll_t fops_poll(struct file *file, poll_table *wait)
{
__poll_t req_events = poll_requested_events(wait);
struct saa7164_encoder_fh *fh =
(struct saa7164_encoder_fh *)file->private_data;
struct saa7164_port *port = fh->port;
__poll_t mask = v4l2_ctrl_poll(file, wait);
port->last_poll_msecs_diff = port->last_poll_msecs;
port->last_poll_msecs = jiffies_to_msecs(jiffies);
port->last_poll_msecs_diff = port->last_poll_msecs -
port->last_poll_msecs_diff;
saa7164_histogram_update(&port->poll_interval,
port->last_poll_msecs_diff);
if (!(req_events & (EPOLLIN | EPOLLRDNORM)))
return mask;
if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
if (atomic_inc_return(&port->v4l_reader_count) == 1) {
if (saa7164_encoder_initialize(port) < 0)
return mask | EPOLLERR;
saa7164_encoder_start_streaming(port);
msleep(200);
}
}
/* Pull the first buffer from the used list */
if (!list_empty(&port->list_buf_used.list))
mask |= EPOLLIN | EPOLLRDNORM;
return mask;
}
static const struct v4l2_ctrl_ops saa7164_ctrl_ops = {
.s_ctrl = saa7164_s_ctrl,
};
static const struct v4l2_file_operations mpeg_fops = {
.owner = THIS_MODULE,
.open = fops_open,
.release = fops_release,
.read = fops_read,
.poll = fops_poll,
.unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
.vidioc_s_std = vidioc_s_std,
.vidioc_g_std = vidioc_g_std,
.vidioc_enum_input = saa7164_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_tuner = saa7164_g_tuner,
.vidioc_s_tuner = saa7164_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_fmt_vid_cap,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static struct video_device saa7164_mpeg_template = {
.name = "saa7164",
.fops = &mpeg_fops,
.ioctl_ops = &mpeg_ioctl_ops,
.minor = -1,
.tvnorms = SAA7164_NORMS,
.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
V4L2_CAP_TUNER,
};
static struct video_device *saa7164_encoder_alloc(
struct saa7164_port *port,
struct pci_dev *pci,
struct video_device *template,
char *type)
{
struct video_device *vfd;
struct saa7164_dev *dev = port->dev;
dprintk(DBGLVL_ENC, "%s()\n", __func__);
vfd = video_device_alloc();
if (NULL == vfd)
return NULL;
*vfd = *template;
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
type, saa7164_boards[dev->board].name);
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->release = video_device_release;
return vfd;
}
int saa7164_encoder_register(struct saa7164_port *port)
{
struct saa7164_dev *dev = port->dev;
struct v4l2_ctrl_handler *hdl = &port->ctrl_handler;
int result = -ENODEV;
dprintk(DBGLVL_ENC, "%s()\n", __func__);
BUG_ON(port->type != SAA7164_MPEG_ENCODER);
/* Sanity check that the PCI configuration space is active */
if (port->hwcfg.BARLocation == 0) {
printk(KERN_ERR "%s() failed (errno = %d), NO PCI configuration\n",
__func__, result);
result = -ENOMEM;
goto failed;
}
/* Establish encoder defaults here */
/* Set default TV standard */
port->encodernorm = saa7164_tvnorms[0];
port->width = 720;
port->mux_input = 1; /* Composite */
port->video_format = EU_VIDEO_FORMAT_MPEG_2;
port->audio_format = 0;
port->video_resolution = 0;
port->freq = SAA7164_TV_MIN_FREQ;
v4l2_ctrl_handler_init(hdl, 14);
v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 66);
v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
V4L2_CID_SATURATION, 0, 255, 1, 62);
v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
V4L2_CID_HUE, 0, 255, 1, 128);
v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
V4L2_CID_SHARPNESS, 0x0, 0x0f, 1, 8);
v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
V4L2_CID_MPEG_AUDIO_MUTE, 0x0, 0x01, 1, 0);
v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, -83, 24, 1, 20);
v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
V4L2_CID_MPEG_VIDEO_BITRATE,
ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
100000, ENCODER_DEF_BITRATE);
v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops,
V4L2_CID_MPEG_STREAM_TYPE,
V4L2_MPEG_STREAM_TYPE_MPEG2_TS, 0,
V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops,
V4L2_CID_MPEG_VIDEO_ASPECT,
V4L2_MPEG_VIDEO_ASPECT_221x100, 0,
V4L2_MPEG_VIDEO_ASPECT_4x3);
v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, 15);
v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops,
V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
V4L2_CID_MPEG_VIDEO_B_FRAMES, 1, 3, 1, 1);
v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
100000, ENCODER_DEF_BITRATE);
if (hdl->error) {
result = hdl->error;
goto failed;
}
port->std = V4L2_STD_NTSC_M;
if (port->encodernorm.id & V4L2_STD_525_60)
port->height = 480;
else
port->height = 576;
/* Allocate and register the video device node */
port->v4l_device = saa7164_encoder_alloc(port,
dev->pci, &saa7164_mpeg_template, "mpeg");
if (!port->v4l_device) {
printk(KERN_INFO "%s: can't allocate mpeg device\n",
dev->name);
result = -ENOMEM;
goto failed;
}
port->v4l_device->ctrl_handler = hdl;
v4l2_ctrl_handler_setup(hdl);
video_set_drvdata(port->v4l_device, port);
result = video_register_device(port->v4l_device,
VFL_TYPE_VIDEO, -1);
if (result < 0) {
printk(KERN_INFO "%s: can't register mpeg device\n",
dev->name);
/* TODO: We're going to leak here if we don't dealloc
The buffers above. The unreg function can't deal wit it.
*/
goto failed;
}
printk(KERN_INFO "%s: registered device video%d [mpeg]\n",
dev->name, port->v4l_device->num);
/* Configure the hardware defaults */
saa7164_api_set_videomux(port);
saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL);
saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL);
saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
saa7164_api_audio_mute(port, 0);
saa7164_api_set_audio_volume(port, 20);
saa7164_api_set_aspect_ratio(port);
/* Disable audio standard detection, it's buggy */
saa7164_api_set_audio_detection(port, 0);
saa7164_api_set_encoder(port);
saa7164_api_get_encoder(port);
result = 0;
failed:
return result;
}
void saa7164_encoder_unregister(struct saa7164_port *port)
{
struct saa7164_dev *dev = port->dev;
dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
BUG_ON(port->type != SAA7164_MPEG_ENCODER);
if (port->v4l_device) {
if (port->v4l_device->minor != -1)
video_unregister_device(port->v4l_device);
else
video_device_release(port->v4l_device);
port->v4l_device = NULL;
}
v4l2_ctrl_handler_free(&port->ctrl_handler);
dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
}