linux_dsm_epyc7002/drivers/media/pci/saa7134/saa7134-video.c

2276 lines
59 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 13:55:06 +07:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* device driver for philips saa7134 based TV cards
* video4linux video interface
*
* (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
*/
#include "saa7134.h"
#include "saa7134-reg.h"
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
[media] include/media: split I2C headers from V4L2 core Currently, include/media is messy, as it contains both the V4L2 core headers and some driver-specific headers on the same place. That makes harder to identify what core headers should be documented and what headers belong to I2C drivers that are included only by bridge/main drivers that would require the functions provided by them. Let's move those i2c specific files to its own subdirectory. The files to move were produced via the following script: mkdir include/media/i2c (cd include/media; for i in *.h; do n=`echo $i|sed s/.h$/.c/`; if [ -e ../../drivers/media/i2c/$n ]; then echo $i; git mv $i i2c/; fi; done) (cd include/media; for i in *.h; do n=`echo $i|sed s/.h$/.c/`; if [ -e ../../drivers/media/*/i2c/$n ]; then echo $i; git mv $i i2c/; fi; done) for i in include/media/*.h; do n=`basename $i`; (for j in $(git grep -l $n); do dirname $j; done)|sort|uniq|grep -ve '^.$' > list; num=$(wc -l list|cut -d' ' -f1); if [ $num == 1 ]; then if [ "`grep i2c list`" != "" ]; then git mv $i include/media/i2c; fi; fi; done And the references corrected via this script: MAIN_DIR="media/" PREV_DIR="media/" DIRS="i2c/" echo "Checking affected files" >&2 for i in $DIRS; do for j in $(find include/$MAIN_DIR/$i -type f -name '*.h'); do n=`basename $j` git grep -l $n done done|sort|uniq >files && ( echo "Handling files..." >&2; echo "for i in \$(cat files|grep -v Documentation); do cat \$i | \\"; ( cd include/$MAIN_DIR; for j in $DIRS; do for i in $(ls $j); do echo "perl -ne 's,(include [\\\"\\<])$PREV_DIR($i)([\\\"\\>]),\1$MAIN_DIR$j\2\3,; print \$_' |\\"; done; done; echo "cat > a && mv a \$i; done"; ); echo "Handling documentation..." >&2; echo "for i in MAINTAINERS \$(cat files); do cat \$i | \\"; ( cd include/$MAIN_DIR; for j in $DIRS; do for i in $(ls $j); do echo " perl -ne 's,include/$PREV_DIR($i)\b,include/$MAIN_DIR$j\1,; print \$_' |\\"; done; done; echo "cat > a && mv a \$i; done" ); ) >script && . ./script Merged Sakari Ailus patch that moves smiapp.h to include/media/i2c. Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> Acked-by: Arnd Bergmann <arnd@arndb.de>
2015-11-10 21:01:44 +07:00
#include <media/i2c/saa6588.h>
/* ------------------------------------------------------------------ */
unsigned int video_debug;
static unsigned int gbuffers = 8;
static unsigned int noninterlaced; /* 0 */
static unsigned int gbufsize = 720*576*4;
static unsigned int gbufsize_max = 720*576*4;
static char secam[] = "--";
module_param(video_debug, int, 0644);
MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
module_param(gbuffers, int, 0444);
MODULE_PARM_DESC(gbuffers,"number of capture buffers, range 2-32");
module_param(noninterlaced, int, 0644);
MODULE_PARM_DESC(noninterlaced,"capture non interlaced video");
module_param_string(secam, secam, sizeof(secam), 0644);
MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc");
#define video_dbg(fmt, arg...) do { \
if (video_debug & 0x04) \
printk(KERN_DEBUG pr_fmt("video: " fmt), ## arg); \
} while (0)
/* ------------------------------------------------------------------ */
/* Defines for Video Output Port Register at address 0x191 */
/* Bit 0: VIP code T bit polarity */
#define VP_T_CODE_P_NON_INVERTED 0x00
#define VP_T_CODE_P_INVERTED 0x01
/* ------------------------------------------------------------------ */
/* Defines for Video Output Port Register at address 0x195 */
/* Bit 2: Video output clock delay control */
#define VP_CLK_CTRL2_NOT_DELAYED 0x00
#define VP_CLK_CTRL2_DELAYED 0x04
/* Bit 1: Video output clock invert control */
#define VP_CLK_CTRL1_NON_INVERTED 0x00
#define VP_CLK_CTRL1_INVERTED 0x02
/* ------------------------------------------------------------------ */
/* Defines for Video Output Port Register at address 0x196 */
/* Bits 2 to 0: VSYNC pin video vertical sync type */
#define VP_VS_TYPE_MASK 0x07
#define VP_VS_TYPE_OFF 0x00
#define VP_VS_TYPE_V123 0x01
#define VP_VS_TYPE_V_ITU 0x02
#define VP_VS_TYPE_VGATE_L 0x03
#define VP_VS_TYPE_RESERVED1 0x04
#define VP_VS_TYPE_RESERVED2 0x05
#define VP_VS_TYPE_F_ITU 0x06
#define VP_VS_TYPE_SC_FID 0x07
/* ------------------------------------------------------------------ */
/* data structs for video */
static int video_out[][9] = {
[CCIR656] = { 0x00, 0xb1, 0x00, 0xa1, 0x00, 0x04, 0x06, 0x00, 0x00 },
};
static struct saa7134_format formats[] = {
{
.fourcc = V4L2_PIX_FMT_GREY,
.depth = 8,
.pm = 0x06,
},{
.fourcc = V4L2_PIX_FMT_RGB555,
.depth = 16,
.pm = 0x13 | 0x80,
},{
.fourcc = V4L2_PIX_FMT_RGB555X,
.depth = 16,
.pm = 0x13 | 0x80,
.bswap = 1,
},{
.fourcc = V4L2_PIX_FMT_RGB565,
.depth = 16,
.pm = 0x10 | 0x80,
},{
.fourcc = V4L2_PIX_FMT_RGB565X,
.depth = 16,
.pm = 0x10 | 0x80,
.bswap = 1,
},{
.fourcc = V4L2_PIX_FMT_BGR24,
.depth = 24,
.pm = 0x11,
},{
.fourcc = V4L2_PIX_FMT_RGB24,
.depth = 24,
.pm = 0x11,
.bswap = 1,
},{
.fourcc = V4L2_PIX_FMT_BGR32,
.depth = 32,
.pm = 0x12,
},{
.fourcc = V4L2_PIX_FMT_RGB32,
.depth = 32,
.pm = 0x12,
.bswap = 1,
.wswap = 1,
},{
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16,
.pm = 0x00,
.bswap = 1,
.yuv = 1,
},{
.fourcc = V4L2_PIX_FMT_UYVY,
.depth = 16,
.pm = 0x00,
.yuv = 1,
},{
.fourcc = V4L2_PIX_FMT_YUV422P,
.depth = 16,
.pm = 0x09,
.yuv = 1,
.planar = 1,
.hshift = 1,
.vshift = 0,
},{
.fourcc = V4L2_PIX_FMT_YUV420,
.depth = 12,
.pm = 0x0a,
.yuv = 1,
.planar = 1,
.hshift = 1,
.vshift = 1,
},{
.fourcc = V4L2_PIX_FMT_YVU420,
.depth = 12,
.pm = 0x0a,
.yuv = 1,
.planar = 1,
.uvswap = 1,
.hshift = 1,
.vshift = 1,
}
};
#define FORMATS ARRAY_SIZE(formats)
#define NORM_625_50 \
.h_start = 0, \
.h_stop = 719, \
.video_v_start = 24, \
.video_v_stop = 311, \
.vbi_v_start_0 = 7, \
.vbi_v_stop_0 = 23, \
.vbi_v_start_1 = 319, \
.src_timing = 4
#define NORM_525_60 \
.h_start = 0, \
.h_stop = 719, \
.video_v_start = 23, \
.video_v_stop = 262, \
.vbi_v_start_0 = 10, \
.vbi_v_stop_0 = 21, \
.vbi_v_start_1 = 273, \
.src_timing = 7
static struct saa7134_tvnorm tvnorms[] = {
{
.name = "PAL", /* autodetect */
.id = V4L2_STD_PAL,
NORM_625_50,
.sync_control = 0x18,
.luma_control = 0x40,
.chroma_ctrl1 = 0x81,
.chroma_gain = 0x2a,
.chroma_ctrl2 = 0x06,
.vgate_misc = 0x1c,
},{
.name = "PAL-BG",
.id = V4L2_STD_PAL_BG,
NORM_625_50,
.sync_control = 0x18,
.luma_control = 0x40,
.chroma_ctrl1 = 0x81,
.chroma_gain = 0x2a,
.chroma_ctrl2 = 0x06,
.vgate_misc = 0x1c,
},{
.name = "PAL-I",
.id = V4L2_STD_PAL_I,
NORM_625_50,
.sync_control = 0x18,
.luma_control = 0x40,
.chroma_ctrl1 = 0x81,
.chroma_gain = 0x2a,
.chroma_ctrl2 = 0x06,
.vgate_misc = 0x1c,
},{
.name = "PAL-DK",
.id = V4L2_STD_PAL_DK,
NORM_625_50,
.sync_control = 0x18,
.luma_control = 0x40,
.chroma_ctrl1 = 0x81,
.chroma_gain = 0x2a,
.chroma_ctrl2 = 0x06,
.vgate_misc = 0x1c,
},{
.name = "NTSC",
.id = V4L2_STD_NTSC,
NORM_525_60,
.sync_control = 0x59,
.luma_control = 0x40,
.chroma_ctrl1 = 0x89,
.chroma_gain = 0x2a,
.chroma_ctrl2 = 0x0e,
.vgate_misc = 0x18,
},{
.name = "SECAM",
.id = V4L2_STD_SECAM,
NORM_625_50,
.sync_control = 0x18,
.luma_control = 0x1b,
.chroma_ctrl1 = 0xd1,
.chroma_gain = 0x80,
.chroma_ctrl2 = 0x00,
.vgate_misc = 0x1c,
},{
.name = "SECAM-DK",
.id = V4L2_STD_SECAM_DK,
NORM_625_50,
.sync_control = 0x18,
.luma_control = 0x1b,
.chroma_ctrl1 = 0xd1,
.chroma_gain = 0x80,
.chroma_ctrl2 = 0x00,
.vgate_misc = 0x1c,
},{
.name = "SECAM-L",
.id = V4L2_STD_SECAM_L,
NORM_625_50,
.sync_control = 0x18,
.luma_control = 0x1b,
.chroma_ctrl1 = 0xd1,
.chroma_gain = 0x80,
.chroma_ctrl2 = 0x00,
.vgate_misc = 0x1c,
},{
.name = "SECAM-Lc",
.id = V4L2_STD_SECAM_LC,
NORM_625_50,
.sync_control = 0x18,
.luma_control = 0x1b,
.chroma_ctrl1 = 0xd1,
.chroma_gain = 0x80,
.chroma_ctrl2 = 0x00,
.vgate_misc = 0x1c,
},{
.name = "PAL-M",
.id = V4L2_STD_PAL_M,
NORM_525_60,
.sync_control = 0x59,
.luma_control = 0x40,
.chroma_ctrl1 = 0xb9,
.chroma_gain = 0x2a,
.chroma_ctrl2 = 0x0e,
.vgate_misc = 0x18,
},{
.name = "PAL-Nc",
.id = V4L2_STD_PAL_Nc,
NORM_625_50,
.sync_control = 0x18,
.luma_control = 0x40,
.chroma_ctrl1 = 0xa1,
.chroma_gain = 0x2a,
.chroma_ctrl2 = 0x06,
.vgate_misc = 0x1c,
},{
.name = "PAL-60",
.id = V4L2_STD_PAL_60,
.h_start = 0,
.h_stop = 719,
.video_v_start = 23,
.video_v_stop = 262,
.vbi_v_start_0 = 10,
.vbi_v_stop_0 = 21,
.vbi_v_start_1 = 273,
.src_timing = 7,
.sync_control = 0x18,
.luma_control = 0x40,
.chroma_ctrl1 = 0x81,
.chroma_gain = 0x2a,
.chroma_ctrl2 = 0x06,
.vgate_misc = 0x1c,
}
};
#define TVNORMS ARRAY_SIZE(tvnorms)
static struct saa7134_format* format_by_fourcc(unsigned int fourcc)
{
unsigned int i;
for (i = 0; i < FORMATS; i++)
if (formats[i].fourcc == fourcc)
return formats+i;
return NULL;
}
/* ------------------------------------------------------------------ */
static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
{
video_dbg("set tv norm = %s\n", norm->name);
dev->tvnorm = norm;
/* setup cropping */
dev->crop_bounds.left = norm->h_start;
dev->crop_defrect.left = norm->h_start;
dev->crop_bounds.width = norm->h_stop - norm->h_start +1;
dev->crop_defrect.width = norm->h_stop - norm->h_start +1;
dev->crop_bounds.top = (norm->vbi_v_stop_0+1)*2;
dev->crop_defrect.top = norm->video_v_start*2;
dev->crop_bounds.height = ((norm->id & V4L2_STD_525_60) ? 524 : 624)
- dev->crop_bounds.top;
dev->crop_defrect.height = (norm->video_v_stop - norm->video_v_start +1)*2;
dev->crop_current = dev->crop_defrect;
saa7134_set_tvnorm_hw(dev);
}
static void video_mux(struct saa7134_dev *dev, int input)
{
video_dbg("video input = %d [%s]\n",
input, saa7134_input_name[card_in(dev, input).type]);
dev->ctl_input = input;
set_tvnorm(dev, dev->tvnorm);
saa7134_tvaudio_setinput(dev, &card_in(dev, input));
}
static void saa7134_set_decoder(struct saa7134_dev *dev)
{
int luma_control, sync_control, chroma_ctrl1, mux;
struct saa7134_tvnorm *norm = dev->tvnorm;
mux = card_in(dev, dev->ctl_input).vmux;
luma_control = norm->luma_control;
sync_control = norm->sync_control;
chroma_ctrl1 = norm->chroma_ctrl1;
if (mux > 5)
luma_control |= 0x80; /* svideo */
if (noninterlaced || dev->nosignal)
sync_control |= 0x20;
/* switch on auto standard detection */
sync_control |= SAA7134_SYNC_CTRL_AUFD;
chroma_ctrl1 |= SAA7134_CHROMA_CTRL1_AUTO0;
chroma_ctrl1 &= ~SAA7134_CHROMA_CTRL1_FCTC;
luma_control &= ~SAA7134_LUMA_CTRL_LDEL;
/* setup video decoder */
saa_writeb(SAA7134_INCR_DELAY, 0x08);
saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux);
saa_writeb(SAA7134_ANALOG_IN_CTRL2, 0x00);
saa_writeb(SAA7134_ANALOG_IN_CTRL3, 0x90);
saa_writeb(SAA7134_ANALOG_IN_CTRL4, 0x90);
saa_writeb(SAA7134_HSYNC_START, 0xeb);
saa_writeb(SAA7134_HSYNC_STOP, 0xe0);
saa_writeb(SAA7134_SOURCE_TIMING1, norm->src_timing);
saa_writeb(SAA7134_SYNC_CTRL, sync_control);
saa_writeb(SAA7134_LUMA_CTRL, luma_control);
saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright);
saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue);
saa_writeb(SAA7134_CHROMA_CTRL1, chroma_ctrl1);
saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain);
saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2);
saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00);
saa_writeb(SAA7134_ANALOG_ADC, 0x01);
saa_writeb(SAA7134_VGATE_START, 0x11);
saa_writeb(SAA7134_VGATE_STOP, 0xfe);
saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc);
saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40);
saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80);
}
void saa7134_set_tvnorm_hw(struct saa7134_dev *dev)
{
saa7134_set_decoder(dev);
saa_call_all(dev, video, s_std, dev->tvnorm->id);
/* Set the correct norm for the saa6752hs. This function
does nothing if there is no saa6752hs. */
saa_call_empress(dev, video, s_std, dev->tvnorm->id);
}
static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale)
{
static const struct {
int xpsc;
int xacl;
int xc2_1;
int xdcg;
int vpfy;
} vals[] = {
/* XPSC XACL XC2_1 XDCG VPFY */
{ 1, 0, 0, 0, 0 },
{ 2, 2, 1, 2, 2 },
{ 3, 4, 1, 3, 2 },
{ 4, 8, 1, 4, 2 },
{ 5, 8, 1, 4, 2 },
{ 6, 8, 1, 4, 3 },
{ 7, 8, 1, 4, 3 },
{ 8, 15, 0, 4, 3 },
{ 9, 15, 0, 4, 3 },
{ 10, 16, 1, 5, 3 },
};
static const int count = ARRAY_SIZE(vals);
int i;
for (i = 0; i < count; i++)
if (vals[i].xpsc == prescale)
break;
if (i == count)
return;
saa_writeb(SAA7134_H_PRESCALE(task), vals[i].xpsc);
saa_writeb(SAA7134_ACC_LENGTH(task), vals[i].xacl);
saa_writeb(SAA7134_LEVEL_CTRL(task),
(vals[i].xc2_1 << 3) | (vals[i].xdcg));
saa_andorb(SAA7134_FIR_PREFILTER_CTRL(task), 0x0f,
(vals[i].vpfy << 2) | vals[i].vpfy);
}
static void set_v_scale(struct saa7134_dev *dev, int task, int yscale)
{
int val,mirror;
saa_writeb(SAA7134_V_SCALE_RATIO1(task), yscale & 0xff);
saa_writeb(SAA7134_V_SCALE_RATIO2(task), yscale >> 8);
mirror = (dev->ctl_mirror) ? 0x02 : 0x00;
if (yscale < 2048) {
/* LPI */
video_dbg("yscale LPI yscale=%d\n", yscale);
saa_writeb(SAA7134_V_FILTER(task), 0x00 | mirror);
saa_writeb(SAA7134_LUMA_CONTRAST(task), 0x40);
saa_writeb(SAA7134_CHROMA_SATURATION(task), 0x40);
} else {
/* ACM */
val = 0x40 * 1024 / yscale;
video_dbg("yscale ACM yscale=%d val=0x%x\n", yscale, val);
saa_writeb(SAA7134_V_FILTER(task), 0x01 | mirror);
saa_writeb(SAA7134_LUMA_CONTRAST(task), val);
saa_writeb(SAA7134_CHROMA_SATURATION(task), val);
}
saa_writeb(SAA7134_LUMA_BRIGHT(task), 0x80);
}
static void set_size(struct saa7134_dev *dev, int task,
int width, int height, int interlace)
{
int prescale,xscale,yscale,y_even,y_odd;
int h_start, h_stop, v_start, v_stop;
int div = interlace ? 2 : 1;
/* setup video scaler */
h_start = dev->crop_current.left;
v_start = dev->crop_current.top/2;
h_stop = (dev->crop_current.left + dev->crop_current.width -1);
v_stop = (dev->crop_current.top + dev->crop_current.height -1)/2;
saa_writeb(SAA7134_VIDEO_H_START1(task), h_start & 0xff);
saa_writeb(SAA7134_VIDEO_H_START2(task), h_start >> 8);
saa_writeb(SAA7134_VIDEO_H_STOP1(task), h_stop & 0xff);
saa_writeb(SAA7134_VIDEO_H_STOP2(task), h_stop >> 8);
saa_writeb(SAA7134_VIDEO_V_START1(task), v_start & 0xff);
saa_writeb(SAA7134_VIDEO_V_START2(task), v_start >> 8);
saa_writeb(SAA7134_VIDEO_V_STOP1(task), v_stop & 0xff);
saa_writeb(SAA7134_VIDEO_V_STOP2(task), v_stop >> 8);
prescale = dev->crop_current.width / width;
if (0 == prescale)
prescale = 1;
xscale = 1024 * dev->crop_current.width / prescale / width;
yscale = 512 * div * dev->crop_current.height / height;
video_dbg("prescale=%d xscale=%d yscale=%d\n",
prescale, xscale, yscale);
set_h_prescale(dev,task,prescale);
saa_writeb(SAA7134_H_SCALE_INC1(task), xscale & 0xff);
saa_writeb(SAA7134_H_SCALE_INC2(task), xscale >> 8);
set_v_scale(dev,task,yscale);
saa_writeb(SAA7134_VIDEO_PIXELS1(task), width & 0xff);
saa_writeb(SAA7134_VIDEO_PIXELS2(task), width >> 8);
saa_writeb(SAA7134_VIDEO_LINES1(task), height/div & 0xff);
saa_writeb(SAA7134_VIDEO_LINES2(task), height/div >> 8);
/* deinterlace y offsets */
y_odd = dev->ctl_y_odd;
y_even = dev->ctl_y_even;
saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd);
saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even);
saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd);
saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even);
}
/* ------------------------------------------------------------------ */
struct cliplist {
__u16 position;
__u8 enable;
__u8 disable;
};
static void set_cliplist(struct saa7134_dev *dev, int reg,
struct cliplist *cl, int entries, char *name)
{
__u8 winbits = 0;
int i;
for (i = 0; i < entries; i++) {
winbits |= cl[i].enable;
winbits &= ~cl[i].disable;
if (i < 15 && cl[i].position == cl[i+1].position)
continue;
saa_writeb(reg + 0, winbits);
saa_writeb(reg + 2, cl[i].position & 0xff);
saa_writeb(reg + 3, cl[i].position >> 8);
video_dbg("clip: %s winbits=%02x pos=%d\n",
name,winbits,cl[i].position);
reg += 8;
}
for (; reg < 0x400; reg += 8) {
saa_writeb(reg+ 0, 0);
saa_writeb(reg + 1, 0);
saa_writeb(reg + 2, 0);
saa_writeb(reg + 3, 0);
}
}
static int clip_range(int val)
{
if (val < 0)
val = 0;
return val;
}
/* Sort into smallest position first order */
static int cliplist_cmp(const void *a, const void *b)
{
const struct cliplist *cla = a;
const struct cliplist *clb = b;
if (cla->position < clb->position)
return -1;
if (cla->position > clb->position)
return 1;
return 0;
}
static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips,
int nclips, int interlace)
{
struct cliplist col[16], row[16];
int cols = 0, rows = 0, i;
int div = interlace ? 2 : 1;
memset(col, 0, sizeof(col));
memset(row, 0, sizeof(row));
for (i = 0; i < nclips && i < 8; i++) {
col[cols].position = clip_range(clips[i].c.left);
col[cols].enable = (1 << i);
cols++;
col[cols].position = clip_range(clips[i].c.left+clips[i].c.width);
col[cols].disable = (1 << i);
cols++;
row[rows].position = clip_range(clips[i].c.top / div);
row[rows].enable = (1 << i);
rows++;
row[rows].position = clip_range((clips[i].c.top + clips[i].c.height)
/ div);
row[rows].disable = (1 << i);
rows++;
}
sort(col, cols, sizeof col[0], cliplist_cmp, NULL);
sort(row, rows, sizeof row[0], cliplist_cmp, NULL);
set_cliplist(dev,0x380,col,cols,"cols");
set_cliplist(dev,0x384,row,rows,"rows");
return 0;
}
static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win, bool try)
{
enum v4l2_field field;
int maxw, maxh;
if (!try && (dev->ovbuf.base == NULL || dev->ovfmt == NULL))
return -EINVAL;
if (win->w.width < 48)
win->w.width = 48;
if (win->w.height < 32)
win->w.height = 32;
if (win->clipcount > 8)
win->clipcount = 8;
win->chromakey = 0;
win->global_alpha = 0;
field = win->field;
maxw = dev->crop_current.width;
maxh = dev->crop_current.height;
if (V4L2_FIELD_ANY == field) {
field = (win->w.height > maxh/2)
? V4L2_FIELD_INTERLACED
: V4L2_FIELD_TOP;
}
switch (field) {
case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTOM:
maxh = maxh / 2;
break;
default:
field = V4L2_FIELD_INTERLACED;
break;
}
win->field = field;
if (win->w.width > maxw)
win->w.width = maxw;
if (win->w.height > maxh)
win->w.height = maxh;
return 0;
}
static int start_preview(struct saa7134_dev *dev)
{
unsigned long base,control,bpl;
int err;
err = verify_preview(dev, &dev->win, false);
if (0 != err)
return err;
dev->ovfield = dev->win.field;
video_dbg("%s %dx%d+%d+%d 0x%08x field=%s\n", __func__,
dev->win.w.width, dev->win.w.height,
dev->win.w.left, dev->win.w.top,
dev->ovfmt->fourcc, v4l2_field_names[dev->ovfield]);
/* setup window + clipping */
set_size(dev, TASK_B, dev->win.w.width, dev->win.w.height,
V4L2_FIELD_HAS_BOTH(dev->ovfield));
setup_clipping(dev, dev->clips, dev->nclips,
V4L2_FIELD_HAS_BOTH(dev->ovfield));
if (dev->ovfmt->yuv)
saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x03);
else
saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x01);
saa_writeb(SAA7134_OFMT_VIDEO_B, dev->ovfmt->pm | 0x20);
/* dma: setup channel 1 (= Video Task B) */
base = (unsigned long)dev->ovbuf.base;
base += dev->ovbuf.fmt.bytesperline * dev->win.w.top;
base += dev->ovfmt->depth/8 * dev->win.w.left;
bpl = dev->ovbuf.fmt.bytesperline;
control = SAA7134_RS_CONTROL_BURST_16;
if (dev->ovfmt->bswap)
control |= SAA7134_RS_CONTROL_BSWAP;
if (dev->ovfmt->wswap)
control |= SAA7134_RS_CONTROL_WSWAP;
if (V4L2_FIELD_HAS_BOTH(dev->ovfield)) {
saa_writel(SAA7134_RS_BA1(1),base);
saa_writel(SAA7134_RS_BA2(1),base+bpl);
saa_writel(SAA7134_RS_PITCH(1),bpl*2);
saa_writel(SAA7134_RS_CONTROL(1),control);
} else {
saa_writel(SAA7134_RS_BA1(1),base);
saa_writel(SAA7134_RS_BA2(1),base);
saa_writel(SAA7134_RS_PITCH(1),bpl);
saa_writel(SAA7134_RS_CONTROL(1),control);
}
/* start dma */
dev->ovenable = 1;
saa7134_set_dmabits(dev);
return 0;
}
static int stop_preview(struct saa7134_dev *dev)
{
dev->ovenable = 0;
saa7134_set_dmabits(dev);
return 0;
}
/*
* Media Controller helper functions
*/
static int saa7134_enable_analog_tuner(struct saa7134_dev *dev)
{
#ifdef CONFIG_MEDIA_CONTROLLER
struct media_device *mdev = dev->media_dev;
struct media_entity *source;
struct media_link *link, *found_link = NULL;
int ret, active_links = 0;
if (!mdev || !dev->decoder)
return 0;
/*
* This will find the tuner that is connected into the decoder.
* Technically, this is not 100% correct, as the device may be
* using an analog input instead of the tuner. However, as we can't
* do DVB streaming while the DMA engine is being used for V4L2,
* this should be enough for the actual needs.
*/
list_for_each_entry(link, &dev->decoder->links, list) {
if (link->sink->entity == dev->decoder) {
found_link = link;
if (link->flags & MEDIA_LNK_FL_ENABLED)
active_links++;
break;
}
}
if (active_links == 1 || !found_link)
return 0;
source = found_link->source->entity;
list_for_each_entry(link, &source->links, list) {
struct media_entity *sink;
int flags = 0;
sink = link->sink->entity;
if (sink == dev->decoder)
flags = MEDIA_LNK_FL_ENABLED;
ret = media_entity_setup_link(link, flags);
if (ret) {
pr_err("Couldn't change link %s->%s to %s. Error %d\n",
source->name, sink->name,
flags ? "enabled" : "disabled",
ret);
return ret;
}
}
#endif
return 0;
}
/* ------------------------------------------------------------------ */
static int buffer_activate(struct saa7134_dev *dev,
struct saa7134_buf *buf,
struct saa7134_buf *next)
{
struct saa7134_dmaqueue *dmaq = buf->vb2.vb2_buf.vb2_queue->drv_priv;
unsigned long base,control,bpl;
unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */
video_dbg("buffer_activate buf=%p\n", buf);
buf->top_seen = 0;
set_size(dev, TASK_A, dev->width, dev->height,
V4L2_FIELD_HAS_BOTH(dev->field));
if (dev->fmt->yuv)
saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03);
else
saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01);
saa_writeb(SAA7134_OFMT_VIDEO_A, dev->fmt->pm);
/* DMA: setup channel 0 (= Video Task A0) */
base = saa7134_buffer_base(buf);
if (dev->fmt->planar)
bpl = dev->width;
else
bpl = (dev->width * dev->fmt->depth) / 8;
control = SAA7134_RS_CONTROL_BURST_16 |
SAA7134_RS_CONTROL_ME |
(dmaq->pt.dma >> 12);
if (dev->fmt->bswap)
control |= SAA7134_RS_CONTROL_BSWAP;
if (dev->fmt->wswap)
control |= SAA7134_RS_CONTROL_WSWAP;
if (V4L2_FIELD_HAS_BOTH(dev->field)) {
/* interlaced */
saa_writel(SAA7134_RS_BA1(0),base);
saa_writel(SAA7134_RS_BA2(0),base+bpl);
saa_writel(SAA7134_RS_PITCH(0),bpl*2);
} else {
/* non-interlaced */
saa_writel(SAA7134_RS_BA1(0),base);
saa_writel(SAA7134_RS_BA2(0),base);
saa_writel(SAA7134_RS_PITCH(0),bpl);
}
saa_writel(SAA7134_RS_CONTROL(0),control);
if (dev->fmt->planar) {
/* DMA: setup channel 4+5 (= planar task A) */
bpl_uv = bpl >> dev->fmt->hshift;
lines_uv = dev->height >> dev->fmt->vshift;
base2 = base + bpl * dev->height;
base3 = base2 + bpl_uv * lines_uv;
if (dev->fmt->uvswap)
tmp = base2, base2 = base3, base3 = tmp;
video_dbg("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n",
bpl_uv,lines_uv,base2,base3);
if (V4L2_FIELD_HAS_BOTH(dev->field)) {
/* interlaced */
saa_writel(SAA7134_RS_BA1(4),base2);
saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv);
saa_writel(SAA7134_RS_PITCH(4),bpl_uv*2);
saa_writel(SAA7134_RS_BA1(5),base3);
saa_writel(SAA7134_RS_BA2(5),base3+bpl_uv);
saa_writel(SAA7134_RS_PITCH(5),bpl_uv*2);
} else {
/* non-interlaced */
saa_writel(SAA7134_RS_BA1(4),base2);
saa_writel(SAA7134_RS_BA2(4),base2);
saa_writel(SAA7134_RS_PITCH(4),bpl_uv);
saa_writel(SAA7134_RS_BA1(5),base3);
saa_writel(SAA7134_RS_BA2(5),base3);
saa_writel(SAA7134_RS_PITCH(5),bpl_uv);
}
saa_writel(SAA7134_RS_CONTROL(4),control);
saa_writel(SAA7134_RS_CONTROL(5),control);
}
/* start DMA */
saa7134_set_dmabits(dev);
mod_timer(&dmaq->timeout, jiffies + BUFFER_TIMEOUT);
return 0;
}
static int buffer_init(struct vb2_buffer *vb2)
{
struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2);
dmaq->curr = NULL;
buf->activate = buffer_activate;
return 0;
}
static int buffer_prepare(struct vb2_buffer *vb2)
{
struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
struct saa7134_dev *dev = dmaq->dev;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2);
struct sg_table *dma = vb2_dma_sg_plane_desc(vb2, 0);
unsigned int size;
if (dma->sgl->offset) {
pr_err("The buffer is not page-aligned\n");
return -EINVAL;
}
size = (dev->width * dev->height * dev->fmt->depth) >> 3;
if (vb2_plane_size(vb2, 0) < size)
return -EINVAL;
vb2_set_plane_payload(vb2, 0, size);
vbuf->field = dev->field;
return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents,
saa7134_buffer_startpage(buf));
}
static int queue_setup(struct vb2_queue *q,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], struct device *alloc_devs[])
{
struct saa7134_dmaqueue *dmaq = q->drv_priv;
struct saa7134_dev *dev = dmaq->dev;
int size = dev->fmt->depth * dev->width * dev->height >> 3;
if (dev->width < 48 ||
dev->height < 32 ||
dev->width/4 > dev->crop_current.width ||
dev->height/4 > dev->crop_current.height ||
dev->width > dev->crop_bounds.width ||
dev->height > dev->crop_bounds.height)
return -EINVAL;
*nbuffers = saa7134_buffer_count(size, *nbuffers);
*nplanes = 1;
sizes[0] = size;
saa7134_enable_analog_tuner(dev);
return 0;
}
/*
* move buffer to hardware queue
*/
void saa7134_vb2_buffer_queue(struct vb2_buffer *vb)
{
struct saa7134_dmaqueue *dmaq = vb->vb2_queue->drv_priv;
struct saa7134_dev *dev = dmaq->dev;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2);
saa7134_buffer_queue(dev, dmaq, buf);
}
EXPORT_SYMBOL_GPL(saa7134_vb2_buffer_queue);
int saa7134_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct saa7134_dmaqueue *dmaq = vq->drv_priv;
struct saa7134_dev *dev = dmaq->dev;
/*
* Planar video capture and TS share the same DMA channel,
* so only one can be active at a time.
*/
if (card_is_empress(dev) && vb2_is_busy(&dev->empress_vbq) &&
dmaq == &dev->video_q && dev->fmt->planar) {
struct saa7134_buf *buf, *tmp;
list_for_each_entry_safe(buf, tmp, &dmaq->queue, entry) {
list_del(&buf->entry);
vb2_buffer_done(&buf->vb2.vb2_buf,
VB2_BUF_STATE_QUEUED);
}
if (dmaq->curr) {
vb2_buffer_done(&dmaq->curr->vb2.vb2_buf,
VB2_BUF_STATE_QUEUED);
dmaq->curr = NULL;
}
return -EBUSY;
}
/* The SAA7134 has a 1K FIFO; the datasheet suggests that when
* configured conservatively, there's 22 usec of buffering for video.
* We therefore request a DMA latency of 20 usec, giving us 2 usec of
* margin in case the FIFO is configured differently to the datasheet.
* Unfortunately, I lack register-level documentation to check the
* Linux FIFO setup and confirm the perfect value.
*/
if ((dmaq == &dev->video_q && !vb2_is_streaming(&dev->vbi_vbq)) ||
(dmaq == &dev->vbi_q && !vb2_is_streaming(&dev->video_vbq)))
cpu_latency_qos_add_request(&dev->qos_request, 20);
dmaq->seq_nr = 0;
return 0;
}
void saa7134_vb2_stop_streaming(struct vb2_queue *vq)
{
struct saa7134_dmaqueue *dmaq = vq->drv_priv;
struct saa7134_dev *dev = dmaq->dev;
saa7134_stop_streaming(dev, dmaq);
if ((dmaq == &dev->video_q && !vb2_is_streaming(&dev->vbi_vbq)) ||
(dmaq == &dev->vbi_q && !vb2_is_streaming(&dev->video_vbq)))
cpu_latency_qos_remove_request(&dev->qos_request);
}
static const struct vb2_ops vb2_qops = {
.queue_setup = queue_setup,
.buf_init = buffer_init,
.buf_prepare = buffer_prepare,
.buf_queue = saa7134_vb2_buffer_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
.start_streaming = saa7134_vb2_start_streaming,
.stop_streaming = saa7134_vb2_stop_streaming,
};
/* ------------------------------------------------------------------ */
static int saa7134_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct saa7134_dev *dev = container_of(ctrl->handler, struct saa7134_dev, ctrl_handler);
unsigned long flags;
int restart_overlay = 0;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
dev->ctl_bright = ctrl->val;
saa_writeb(SAA7134_DEC_LUMA_BRIGHT, ctrl->val);
break;
case V4L2_CID_HUE:
dev->ctl_hue = ctrl->val;
saa_writeb(SAA7134_DEC_CHROMA_HUE, ctrl->val);
break;
case V4L2_CID_CONTRAST:
dev->ctl_contrast = ctrl->val;
saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
break;
case V4L2_CID_SATURATION:
dev->ctl_saturation = ctrl->val;
saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
break;
case V4L2_CID_AUDIO_MUTE:
dev->ctl_mute = ctrl->val;
saa7134_tvaudio_setmute(dev);
break;
case V4L2_CID_AUDIO_VOLUME:
dev->ctl_volume = ctrl->val;
saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
break;
case V4L2_CID_PRIVATE_INVERT:
dev->ctl_invert = ctrl->val;
saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
break;
case V4L2_CID_HFLIP:
dev->ctl_mirror = ctrl->val;
restart_overlay = 1;
break;
case V4L2_CID_PRIVATE_Y_EVEN:
dev->ctl_y_even = ctrl->val;
restart_overlay = 1;
break;
case V4L2_CID_PRIVATE_Y_ODD:
dev->ctl_y_odd = ctrl->val;
restart_overlay = 1;
break;
case V4L2_CID_PRIVATE_AUTOMUTE:
{
struct v4l2_priv_tun_config tda9887_cfg;
tda9887_cfg.tuner = TUNER_TDA9887;
tda9887_cfg.priv = &dev->tda9887_conf;
dev->ctl_automute = ctrl->val;
if (dev->tda9887_conf) {
if (dev->ctl_automute)
dev->tda9887_conf |= TDA9887_AUTOMUTE;
else
dev->tda9887_conf &= ~TDA9887_AUTOMUTE;
saa_call_all(dev, tuner, s_config, &tda9887_cfg);
}
break;
}
default:
return -EINVAL;
}
if (restart_overlay && dev->overlay_owner) {
spin_lock_irqsave(&dev->slock, flags);
stop_preview(dev);
start_preview(dev);
spin_unlock_irqrestore(&dev->slock, flags);
}
return 0;
}
/* ------------------------------------------------------------------ */
static int video_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct saa7134_dev *dev = video_drvdata(file);
int ret = v4l2_fh_open(file);
if (ret < 0)
return ret;
mutex_lock(&dev->lock);
if (vdev->vfl_type == VFL_TYPE_RADIO) {
/* switch to radio mode */
saa7134_tvaudio_setinput(dev, &card(dev).radio);
saa_call_all(dev, tuner, s_radio);
} else {
/* switch to video/vbi mode */
video_mux(dev, dev->ctl_input);
}
mutex_unlock(&dev->lock);
return 0;
}
static int video_release(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct saa7134_dev *dev = video_drvdata(file);
struct v4l2_fh *fh = file->private_data;
struct saa6588_command cmd;
unsigned long flags;
mutex_lock(&dev->lock);
saa7134_tvaudio_close(dev);
/* turn off overlay */
if (fh == dev->overlay_owner) {
spin_lock_irqsave(&dev->slock,flags);
stop_preview(dev);
spin_unlock_irqrestore(&dev->slock,flags);
dev->overlay_owner = NULL;
}
if (vdev->vfl_type == VFL_TYPE_RADIO)
v4l2_fh_release(file);
else
_vb2_fop_release(file, NULL);
/* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/
saa_andorb(SAA7134_OFMT_VIDEO_A, 0x1f, 0);
saa_andorb(SAA7134_OFMT_VIDEO_B, 0x1f, 0);
saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0);
saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0);
saa_call_all(dev, tuner, standby);
if (vdev->vfl_type == VFL_TYPE_RADIO)
saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
mutex_unlock(&dev->lock);
return 0;
}
static ssize_t radio_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
struct saa7134_dev *dev = video_drvdata(file);
struct saa6588_command cmd;
cmd.block_count = count/3;
cmd.nonblocking = file->f_flags & O_NONBLOCK;
cmd.buffer = data;
cmd.instance = file;
cmd.result = -ENODEV;
mutex_lock(&dev->lock);
saa_call_all(dev, core, ioctl, SAA6588_CMD_READ, &cmd);
mutex_unlock(&dev->lock);
return cmd.result;
}
static __poll_t radio_poll(struct file *file, poll_table *wait)
{
struct saa7134_dev *dev = video_drvdata(file);
struct saa6588_command cmd;
__poll_t rc = v4l2_ctrl_poll(file, wait);
cmd.instance = file;
cmd.event_list = wait;
cmd.poll_mask = 0;
mutex_lock(&dev->lock);
saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd);
mutex_unlock(&dev->lock);
return rc | cmd.poll_mask;
}
/* ------------------------------------------------------------------ */
static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_dev *dev = video_drvdata(file);
struct saa7134_tvnorm *norm = dev->tvnorm;
memset(&f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));
f->fmt.vbi.sampling_rate = 6750000 * 4;
f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */;
f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
f->fmt.vbi.offset = 64 * 4;
f->fmt.vbi.start[0] = norm->vbi_v_start_0;
f->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1;
f->fmt.vbi.start[1] = norm->vbi_v_start_1;
f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */
return 0;
}
static int saa7134_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_dev *dev = video_drvdata(file);
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
f->fmt.pix.field = dev->field;
f->fmt.pix.pixelformat = dev->fmt->fourcc;
if (dev->fmt->planar)
f->fmt.pix.bytesperline = f->fmt.pix.width;
else
f->fmt.pix.bytesperline =
(f->fmt.pix.width * dev->fmt->depth) / 8;
f->fmt.pix.sizeimage =
(f->fmt.pix.height * f->fmt.pix.width * dev->fmt->depth) / 8;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_dev *dev = video_drvdata(file);
struct v4l2_clip __user *clips = f->fmt.win.clips;
u32 clipcount = f->fmt.win.clipcount;
int err = 0;
int i;
if (saa7134_no_overlay > 0) {
pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
f->fmt.win = dev->win;
f->fmt.win.clips = clips;
if (clips == NULL)
clipcount = 0;
if (dev->nclips < clipcount)
clipcount = dev->nclips;
f->fmt.win.clipcount = clipcount;
for (i = 0; !err && i < clipcount; i++) {
if (copy_to_user(&f->fmt.win.clips[i].c, &dev->clips[i].c,
sizeof(struct v4l2_rect)))
err = -EFAULT;
}
return err;
}
static int saa7134_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_dev *dev = video_drvdata(file);
struct saa7134_format *fmt;
enum v4l2_field field;
unsigned int maxw, maxh;
fmt = format_by_fourcc(f->fmt.pix.pixelformat);
if (NULL == fmt)
return -EINVAL;
field = f->fmt.pix.field;
maxw = min(dev->crop_current.width*4, dev->crop_bounds.width);
maxh = min(dev->crop_current.height*4, dev->crop_bounds.height);
if (V4L2_FIELD_ANY == field) {
field = (f->fmt.pix.height > maxh/2)
? V4L2_FIELD_INTERLACED
: V4L2_FIELD_BOTTOM;
}
switch (field) {
case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTOM:
maxh = maxh / 2;
break;
default:
field = V4L2_FIELD_INTERLACED;
break;
}
f->fmt.pix.field = field;
if (f->fmt.pix.width < 48)
f->fmt.pix.width = 48;
if (f->fmt.pix.height < 32)
f->fmt.pix.height = 32;
if (f->fmt.pix.width > maxw)
f->fmt.pix.width = maxw;
if (f->fmt.pix.height > maxh)
f->fmt.pix.height = maxh;
f->fmt.pix.width &= ~0x03;
if (fmt->planar)
f->fmt.pix.bytesperline = f->fmt.pix.width;
else
f->fmt.pix.bytesperline =
(f->fmt.pix.width * fmt->depth) / 8;
f->fmt.pix.sizeimage =
(f->fmt.pix.height * f->fmt.pix.width * fmt->depth) / 8;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_dev *dev = video_drvdata(file);
if (saa7134_no_overlay > 0) {
pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
if (f->fmt.win.clips == NULL)
f->fmt.win.clipcount = 0;
return verify_preview(dev, &f->fmt.win, true);
}
static int saa7134_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_dev *dev = video_drvdata(file);
int err;
err = saa7134_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
dev->field = f->fmt.pix.field;
return 0;
}
static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_dev *dev = video_drvdata(file);
int err;
unsigned long flags;
if (saa7134_no_overlay > 0) {
pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
if (f->fmt.win.clips == NULL)
f->fmt.win.clipcount = 0;
err = verify_preview(dev, &f->fmt.win, true);
if (0 != err)
return err;
dev->win = f->fmt.win;
dev->nclips = f->fmt.win.clipcount;
if (copy_from_user(dev->clips, f->fmt.win.clips,
sizeof(struct v4l2_clip) * dev->nclips))
return -EFAULT;
if (priv == dev->overlay_owner) {
spin_lock_irqsave(&dev->slock, flags);
stop_preview(dev);
start_preview(dev);
spin_unlock_irqrestore(&dev->slock, flags);
}
return 0;
}
int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i)
{
struct saa7134_dev *dev = video_drvdata(file);
unsigned int n;
n = i->index;
if (n >= SAA7134_INPUT_MAX)
return -EINVAL;
if (card_in(dev, i->index).type == SAA7134_NO_INPUT)
return -EINVAL;
i->index = n;
strscpy(i->name, saa7134_input_name[card_in(dev, n).type],
sizeof(i->name));
switch (card_in(dev, n).type) {
case SAA7134_INPUT_TV:
case SAA7134_INPUT_TV_MONO:
i->type = V4L2_INPUT_TYPE_TUNER;
break;
default:
i->type = V4L2_INPUT_TYPE_CAMERA;
break;
}
if (n == dev->ctl_input) {
int v1 = saa_readb(SAA7134_STATUS_VIDEO1);
int v2 = saa_readb(SAA7134_STATUS_VIDEO2);
if (0 != (v1 & 0x40))
i->status |= V4L2_IN_ST_NO_H_LOCK;
if (0 != (v2 & 0x40))
i->status |= V4L2_IN_ST_NO_SIGNAL;
if (0 != (v2 & 0x0e))
i->status |= V4L2_IN_ST_MACROVISION;
}
i->std = SAA7134_NORMS;
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_enum_input);
int saa7134_g_input(struct file *file, void *priv, unsigned int *i)
{
struct saa7134_dev *dev = video_drvdata(file);
*i = dev->ctl_input;
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_g_input);
int saa7134_s_input(struct file *file, void *priv, unsigned int i)
{
struct saa7134_dev *dev = video_drvdata(file);
if (i >= SAA7134_INPUT_MAX)
return -EINVAL;
if (card_in(dev, i).type == SAA7134_NO_INPUT)
return -EINVAL;
video_mux(dev, i);
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_s_input);
int saa7134_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct saa7134_dev *dev = video_drvdata(file);
strscpy(cap->driver, "saa7134", sizeof(cap->driver));
strscpy(cap->card, saa7134_boards[dev->board].name,
sizeof(cap->card));
sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
cap->capabilities = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
V4L2_CAP_RADIO | V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_VBI_CAPTURE | V4L2_CAP_DEVICE_CAPS;
if (dev->tuner_type != TUNER_ABSENT && dev->tuner_type != UNSET)
cap->capabilities |= V4L2_CAP_TUNER;
if (dev->has_rds)
cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
if (saa7134_no_overlay <= 0)
cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_querycap);
int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id)
{
struct saa7134_dev *dev = video_drvdata(file);
struct v4l2_fh *fh = priv;
unsigned long flags;
unsigned int i;
v4l2_std_id fixup;
if (is_empress(file) && dev->overlay_owner) {
/* Don't change the std from the mpeg device
if overlay is active. */
return -EBUSY;
}
for (i = 0; i < TVNORMS; i++)
if (id == tvnorms[i].id)
break;
if (i == TVNORMS)
for (i = 0; i < TVNORMS; i++)
if (id & tvnorms[i].id)
break;
if (i == TVNORMS)
return -EINVAL;
if ((id & V4L2_STD_SECAM) && (secam[0] != '-')) {
if (secam[0] == 'L' || secam[0] == 'l') {
if (secam[1] == 'C' || secam[1] == 'c')
fixup = V4L2_STD_SECAM_LC;
else
fixup = V4L2_STD_SECAM_L;
} else {
if (secam[0] == 'D' || secam[0] == 'd')
fixup = V4L2_STD_SECAM_DK;
else
fixup = V4L2_STD_SECAM;
}
for (i = 0; i < TVNORMS; i++) {
if (fixup == tvnorms[i].id)
break;
}
if (i == TVNORMS)
return -EINVAL;
}
id = tvnorms[i].id;
if (!is_empress(file) && fh == dev->overlay_owner) {
spin_lock_irqsave(&dev->slock, flags);
stop_preview(dev);
spin_unlock_irqrestore(&dev->slock, flags);
set_tvnorm(dev, &tvnorms[i]);
spin_lock_irqsave(&dev->slock, flags);
start_preview(dev);
spin_unlock_irqrestore(&dev->slock, flags);
} else
set_tvnorm(dev, &tvnorms[i]);
saa7134_tvaudio_do_scan(dev);
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_s_std);
int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id)
{
struct saa7134_dev *dev = video_drvdata(file);
*id = dev->tvnorm->id;
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_g_std);
static v4l2_std_id saa7134_read_std(struct saa7134_dev *dev)
{
static v4l2_std_id stds[] = {
V4L2_STD_UNKNOWN,
V4L2_STD_NTSC,
V4L2_STD_PAL,
V4L2_STD_SECAM };
v4l2_std_id result = 0;
u8 st1 = saa_readb(SAA7134_STATUS_VIDEO1);
u8 st2 = saa_readb(SAA7134_STATUS_VIDEO2);
if (!(st2 & 0x1)) /* RDCAP == 0 */
result = V4L2_STD_UNKNOWN;
else
result = stds[st1 & 0x03];
return result;
}
int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std)
{
struct saa7134_dev *dev = video_drvdata(file);
*std &= saa7134_read_std(dev);
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_querystd);
static int saa7134_g_pixelaspect(struct file *file, void *priv,
int type, struct v4l2_fract *f)
{
struct saa7134_dev *dev = video_drvdata(file);
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
return -EINVAL;
if (dev->tvnorm->id & V4L2_STD_525_60) {
f->numerator = 11;
f->denominator = 10;
}
if (dev->tvnorm->id & V4L2_STD_625_50) {
f->numerator = 54;
f->denominator = 59;
}
return 0;
}
static int saa7134_g_selection(struct file *file, void *f, struct v4l2_selection *sel)
{
struct saa7134_dev *dev = video_drvdata(file);
if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
sel->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
sel->r = dev->crop_current;
break;
case V4L2_SEL_TGT_CROP_DEFAULT:
sel->r = dev->crop_defrect;
break;
case V4L2_SEL_TGT_CROP_BOUNDS:
sel->r = dev->crop_bounds;
break;
default:
return -EINVAL;
}
return 0;
}
static int saa7134_s_selection(struct file *file, void *f, struct v4l2_selection *sel)
{
struct saa7134_dev *dev = video_drvdata(file);
struct v4l2_rect *b = &dev->crop_bounds;
struct v4l2_rect *c = &dev->crop_current;
if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
sel->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
return -EINVAL;
if (sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
if (dev->overlay_owner)
return -EBUSY;
if (vb2_is_streaming(&dev->video_vbq))
return -EBUSY;
*c = sel->r;
if (c->top < b->top)
c->top = b->top;
if (c->top > b->top + b->height)
c->top = b->top + b->height;
if (c->height > b->top - c->top + b->height)
c->height = b->top - c->top + b->height;
if (c->left < b->left)
c->left = b->left;
if (c->left > b->left + b->width)
c->left = b->left + b->width;
if (c->width > b->left - c->left + b->width)
c->width = b->left - c->left + b->width;
sel->r = *c;
return 0;
}
int saa7134_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *t)
{
struct saa7134_dev *dev = video_drvdata(file);
int n;
if (0 != t->index)
return -EINVAL;
memset(t, 0, sizeof(*t));
for (n = 0; n < SAA7134_INPUT_MAX; n++) {
if (card_in(dev, n).type == SAA7134_INPUT_TV ||
card_in(dev, n).type == SAA7134_INPUT_TV_MONO)
break;
}
if (n == SAA7134_INPUT_MAX)
return -EINVAL;
if (card_in(dev, n).type != SAA7134_NO_INPUT) {
strscpy(t->name, "Television", sizeof(t->name));
t->type = V4L2_TUNER_ANALOG_TV;
saa_call_all(dev, tuner, g_tuner, t);
t->capability = V4L2_TUNER_CAP_NORM |
V4L2_TUNER_CAP_STEREO |
V4L2_TUNER_CAP_LANG1 |
V4L2_TUNER_CAP_LANG2;
t->rxsubchans = saa7134_tvaudio_getstereo(dev);
t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans);
}
if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03))
t->signal = 0xffff;
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_g_tuner);
int saa7134_s_tuner(struct file *file, void *priv,
const struct v4l2_tuner *t)
{
struct saa7134_dev *dev = video_drvdata(file);
int rx, mode;
if (0 != t->index)
return -EINVAL;
mode = dev->thread.mode;
if (UNSET == mode) {
rx = saa7134_tvaudio_getstereo(dev);
[media] v4l: fix compiler warnings media_build/v4l/au0828-video.c: In function 'au0828_irq_callback': media_build/v4l/au0828-video.c:123:6: warning: variable 'rc' set but not used [-Wunused-but-set-variable] media_build/v4l/cx23888-ir.c: In function 'pulse_clocks_to_clock_divider': media_build/v4l/cx23888-ir.c:334:6: warning: variable 'rem' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25840-ir.c: In function 'pulse_clocks_to_clock_divider': media_build/v4l/cx25840-ir.c:319:6: warning: variable 'rem' set but not used [-Wunused-but-set-variable] media_build/v4l/cx25840-ir.c: In function 'cx25840_ir_tx_write': media_build/v4l/cx25840-ir.c:863:21: warning: variable 'c' set but not used [-Wunused-but-set-variable] media_build/v4l/em28xx-audio.c: In function 'snd_em28xx_hw_capture_params': media_build/v4l/em28xx-audio.c:346:31: warning: variable 'format' set but not used [-Wunused-but-set-variable] media_build/v4l/em28xx-audio.c:346:25: warning: variable 'rate' set but not used [-Wunused-but-set-variable] media_build/v4l/em28xx-audio.c:346:15: warning: variable 'channels' set but not used [-Wunused-but-set-variable] media_build/v4l/hdpvr-control.c: In function 'get_input_lines_info': media_build/v4l/hdpvr-control.c:98:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/hdpvr-video.c: In function 'hdpvr_try_ctrl': media_build/v4l/hdpvr-video.c:955:6: warning: variable 'ret' set but not used [-Wunused-but-set-variable] media_build/v4l/saa7134-video.c: In function 'saa7134_s_tuner': media_build/v4l/saa7134-video.c:2030:6: warning: variable 'rx' set but not used [-Wunused-but-set-variable] media_build/v4l/sn9c102_core.c: In function 'sn9c102_stream_interrupt': media_build/v4l/sn9c102_core.c:998:7: warning: variable 'timeout' set but not used [-Wunused-but-set-variable] Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Acked-by: Janne Grunau <j@jannau.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2012-05-14 20:17:35 +07:00
mode = saa7134_tvaudio_rx2mode(rx);
}
if (mode != t->audmode)
dev->thread.mode = t->audmode;
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_s_tuner);
int saa7134_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct saa7134_dev *dev = video_drvdata(file);
if (0 != f->tuner)
return -EINVAL;
saa_call_all(dev, tuner, g_frequency, f);
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_g_frequency);
int saa7134_s_frequency(struct file *file, void *priv,
const struct v4l2_frequency *f)
{
struct saa7134_dev *dev = video_drvdata(file);
if (0 != f->tuner)
return -EINVAL;
saa_call_all(dev, tuner, s_frequency, f);
saa7134_tvaudio_do_scan(dev);
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_s_frequency);
static int saa7134_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index >= FORMATS)
return -EINVAL;
f->pixelformat = formats[f->index].fourcc;
return 0;
}
static int saa7134_enum_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (saa7134_no_overlay > 0) {
pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
if ((f->index >= FORMATS) || formats[f->index].planar)
return -EINVAL;
f->pixelformat = formats[f->index].fourcc;
return 0;
}
static int saa7134_g_fbuf(struct file *file, void *f,
struct v4l2_framebuffer *fb)
{
struct saa7134_dev *dev = video_drvdata(file);
*fb = dev->ovbuf;
fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
return 0;
}
static int saa7134_s_fbuf(struct file *file, void *f,
const struct v4l2_framebuffer *fb)
{
struct saa7134_dev *dev = video_drvdata(file);
struct saa7134_format *fmt;
if (!capable(CAP_SYS_ADMIN) &&
!capable(CAP_SYS_RAWIO))
return -EPERM;
/* check args */
fmt = format_by_fourcc(fb->fmt.pixelformat);
if (NULL == fmt)
return -EINVAL;
/* ok, accept it */
dev->ovbuf = *fb;
dev->ovfmt = fmt;
if (0 == dev->ovbuf.fmt.bytesperline)
dev->ovbuf.fmt.bytesperline =
dev->ovbuf.fmt.width*fmt->depth/8;
return 0;
}
static int saa7134_overlay(struct file *file, void *priv, unsigned int on)
{
struct saa7134_dev *dev = video_drvdata(file);
unsigned long flags;
if (on) {
if (saa7134_no_overlay > 0) {
video_dbg("no_overlay\n");
return -EINVAL;
}
if (dev->overlay_owner && priv != dev->overlay_owner)
return -EBUSY;
dev->overlay_owner = priv;
spin_lock_irqsave(&dev->slock, flags);
start_preview(dev);
spin_unlock_irqrestore(&dev->slock, flags);
}
if (!on) {
if (priv != dev->overlay_owner)
return -EINVAL;
spin_lock_irqsave(&dev->slock, flags);
stop_preview(dev);
spin_unlock_irqrestore(&dev->slock, flags);
dev->overlay_owner = NULL;
}
return 0;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int vidioc_g_register (struct file *file, void *priv,
struct v4l2_dbg_register *reg)
{
struct saa7134_dev *dev = video_drvdata(file);
reg->val = saa_readb(reg->reg & 0xffffff);
reg->size = 1;
return 0;
}
static int vidioc_s_register (struct file *file, void *priv,
const struct v4l2_dbg_register *reg)
{
struct saa7134_dev *dev = video_drvdata(file);
saa_writeb(reg->reg & 0xffffff, reg->val);
return 0;
}
#endif
static int radio_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *t)
{
struct saa7134_dev *dev = video_drvdata(file);
if (0 != t->index)
return -EINVAL;
strscpy(t->name, "Radio", sizeof(t->name));
saa_call_all(dev, tuner, g_tuner, t);
t->audmode &= V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO;
if (dev->input->amux == TV) {
t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11);
t->rxsubchans = (saa_readb(0x529) & 0x08) ?
V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
}
return 0;
}
static int radio_s_tuner(struct file *file, void *priv,
const struct v4l2_tuner *t)
{
struct saa7134_dev *dev = video_drvdata(file);
if (0 != t->index)
return -EINVAL;
saa_call_all(dev, tuner, s_tuner, t);
return 0;
}
static const struct v4l2_file_operations video_fops =
{
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
.read = vb2_fop_read,
.poll = vb2_fop_poll,
.mmap = vb2_fop_mmap,
.unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querycap = saa7134_querycap,
.vidioc_enum_fmt_vid_cap = saa7134_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = saa7134_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = saa7134_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = saa7134_s_fmt_vid_cap,
.vidioc_enum_fmt_vid_overlay = saa7134_enum_fmt_vid_overlay,
.vidioc_g_fmt_vid_overlay = saa7134_g_fmt_vid_overlay,
.vidioc_try_fmt_vid_overlay = saa7134_try_fmt_vid_overlay,
.vidioc_s_fmt_vid_overlay = saa7134_s_fmt_vid_overlay,
.vidioc_g_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
.vidioc_try_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
.vidioc_s_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
.vidioc_g_pixelaspect = saa7134_g_pixelaspect,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_s_std = saa7134_s_std,
.vidioc_g_std = saa7134_g_std,
.vidioc_querystd = saa7134_querystd,
.vidioc_enum_input = saa7134_enum_input,
.vidioc_g_input = saa7134_g_input,
.vidioc_s_input = saa7134_s_input,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_g_tuner = saa7134_g_tuner,
.vidioc_s_tuner = saa7134_s_tuner,
.vidioc_g_selection = saa7134_g_selection,
.vidioc_s_selection = saa7134_s_selection,
.vidioc_g_fbuf = saa7134_g_fbuf,
.vidioc_s_fbuf = saa7134_s_fbuf,
.vidioc_overlay = saa7134_overlay,
.vidioc_g_frequency = saa7134_g_frequency,
.vidioc_s_frequency = saa7134_s_frequency,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static const struct v4l2_file_operations radio_fops = {
.owner = THIS_MODULE,
.open = video_open,
.read = radio_read,
.release = video_release,
.unlocked_ioctl = video_ioctl2,
.poll = radio_poll,
};
static const struct v4l2_ioctl_ops radio_ioctl_ops = {
.vidioc_querycap = saa7134_querycap,
.vidioc_g_tuner = radio_g_tuner,
.vidioc_s_tuner = radio_s_tuner,
.vidioc_g_frequency = saa7134_g_frequency,
.vidioc_s_frequency = saa7134_s_frequency,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
/* ----------------------------------------------------------- */
/* exported stuff */
struct video_device saa7134_video_template = {
.name = "saa7134-video",
.fops = &video_fops,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = SAA7134_NORMS,
};
struct video_device saa7134_radio_template = {
.name = "saa7134-radio",
.fops = &radio_fops,
.ioctl_ops = &radio_ioctl_ops,
};
static const struct v4l2_ctrl_ops saa7134_ctrl_ops = {
.s_ctrl = saa7134_s_ctrl,
};
static const struct v4l2_ctrl_config saa7134_ctrl_invert = {
.ops = &saa7134_ctrl_ops,
.id = V4L2_CID_PRIVATE_INVERT,
.name = "Invert",
.type = V4L2_CTRL_TYPE_BOOLEAN,
.min = 0,
.max = 1,
.step = 1,
};
static const struct v4l2_ctrl_config saa7134_ctrl_y_odd = {
.ops = &saa7134_ctrl_ops,
.id = V4L2_CID_PRIVATE_Y_ODD,
.name = "Y Offset Odd Field",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 128,
.step = 1,
};
static const struct v4l2_ctrl_config saa7134_ctrl_y_even = {
.ops = &saa7134_ctrl_ops,
.id = V4L2_CID_PRIVATE_Y_EVEN,
.name = "Y Offset Even Field",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 128,
.step = 1,
};
static const struct v4l2_ctrl_config saa7134_ctrl_automute = {
.ops = &saa7134_ctrl_ops,
.id = V4L2_CID_PRIVATE_AUTOMUTE,
.name = "Automute",
.type = V4L2_CTRL_TYPE_BOOLEAN,
.min = 0,
.max = 1,
.step = 1,
.def = 1,
};
int saa7134_video_init1(struct saa7134_dev *dev)
{
struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
struct vb2_queue *q;
int ret;
/* sanitycheck insmod options */
if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
gbuffers = 2;
if (gbufsize > gbufsize_max)
gbufsize = gbufsize_max;
gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
v4l2_ctrl_handler_init(hdl, 11);
v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
V4L2_CID_CONTRAST, 0, 127, 1, 68);
v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
V4L2_CID_SATURATION, 0, 127, 1, 64);
v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, -15, 15, 1, 0);
v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_invert, NULL);
v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_y_odd, NULL);
v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_y_even, NULL);
v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_automute, NULL);
if (hdl->error)
return hdl->error;
if (card_has_radio(dev)) {
hdl = &dev->radio_ctrl_handler;
v4l2_ctrl_handler_init(hdl, 2);
v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler,
v4l2_ctrl_radio_filter, false);
if (hdl->error)
return hdl->error;
}
dev->ctl_mute = 1;
if (dev->tda9887_conf && saa7134_ctrl_automute.def)
dev->tda9887_conf |= TDA9887_AUTOMUTE;
dev->automute = 0;
INIT_LIST_HEAD(&dev->video_q.queue);
timer_setup(&dev->video_q.timeout, saa7134_buffer_timeout, 0);
dev->video_q.dev = dev;
dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
dev->width = 720;
dev->height = 576;
dev->field = V4L2_FIELD_INTERLACED;
dev->win.w.width = dev->width;
dev->win.w.height = dev->height;
dev->win.field = V4L2_FIELD_INTERLACED;
dev->ovbuf.fmt.width = dev->width;
dev->ovbuf.fmt.height = dev->height;
dev->ovbuf.fmt.pixelformat = dev->fmt->fourcc;
dev->ovbuf.fmt.colorspace = V4L2_COLORSPACE_SMPTE170M;
if (saa7134_boards[dev->board].video_out)
saa7134_videoport_init(dev);
q = &dev->video_vbq;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/*
* Do not add VB2_USERPTR unless explicitly requested: the saa7134 DMA
* engine cannot handle transfers that do not start at the beginning
* of a page. A user-provided pointer can start anywhere in a page, so
* USERPTR support is a no-go unless the application knows about these
* limitations and has special support for this.
*/
q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
if (saa7134_userptr)
q->io_modes |= VB2_USERPTR;
q->drv_priv = &dev->video_q;
q->ops = &vb2_qops;
q->gfp_flags = GFP_DMA32;
q->mem_ops = &vb2_dma_sg_memops;
q->buf_struct_size = sizeof(struct saa7134_buf);
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &dev->lock;
q->dev = &dev->pci->dev;
ret = vb2_queue_init(q);
if (ret)
return ret;
saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt);
q = &dev->vbi_vbq;
q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
/* Don't add VB2_USERPTR, see comment above */
q->io_modes = VB2_MMAP | VB2_READ;
if (saa7134_userptr)
q->io_modes |= VB2_USERPTR;
q->drv_priv = &dev->vbi_q;
q->ops = &saa7134_vbi_qops;
q->gfp_flags = GFP_DMA32;
q->mem_ops = &vb2_dma_sg_memops;
q->buf_struct_size = sizeof(struct saa7134_buf);
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &dev->lock;
q->dev = &dev->pci->dev;
ret = vb2_queue_init(q);
if (ret)
return ret;
saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt);
return 0;
}
void saa7134_video_fini(struct saa7134_dev *dev)
{
/* free stuff */
vb2_queue_release(&dev->video_vbq);
saa7134_pgtable_free(dev->pci, &dev->video_q.pt);
vb2_queue_release(&dev->vbi_vbq);
saa7134_pgtable_free(dev->pci, &dev->vbi_q.pt);
v4l2_ctrl_handler_free(&dev->ctrl_handler);
if (card_has_radio(dev))
v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
}
int saa7134_videoport_init(struct saa7134_dev *dev)
{
/* enable video output */
int vo = saa7134_boards[dev->board].video_out;
int video_reg;
unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts;
/* Configure videoport */
saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]);
video_reg = video_out[vo][1];
if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED)
video_reg &= ~VP_T_CODE_P_INVERTED;
saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg);
saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]);
saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]);
video_reg = video_out[vo][5];
if (vid_port_opts & SET_CLOCK_NOT_DELAYED)
video_reg &= ~VP_CLK_CTRL2_DELAYED;
if (vid_port_opts & SET_CLOCK_INVERTED)
video_reg |= VP_CLK_CTRL1_INVERTED;
saa_writeb(SAA7134_VIDEO_PORT_CTRL5, video_reg);
video_reg = video_out[vo][6];
if (vid_port_opts & SET_VSYNC_OFF) {
video_reg &= ~VP_VS_TYPE_MASK;
video_reg |= VP_VS_TYPE_OFF;
}
saa_writeb(SAA7134_VIDEO_PORT_CTRL6, video_reg);
saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]);
saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]);
/* Start videoport */
saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
return 0;
}
int saa7134_video_init2(struct saa7134_dev *dev)
{
/* init video hw */
set_tvnorm(dev,&tvnorms[0]);
video_mux(dev,0);
v4l2_ctrl_handler_setup(&dev->ctrl_handler);
saa7134_tvaudio_setmute(dev);
saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
return 0;
}
void saa7134_irq_video_signalchange(struct saa7134_dev *dev)
{
static const char *st[] = {
"(no signal)", "NTSC", "PAL", "SECAM" };
u32 st1,st2;
st1 = saa_readb(SAA7134_STATUS_VIDEO1);
st2 = saa_readb(SAA7134_STATUS_VIDEO2);
video_dbg("DCSDT: pll: %s, sync: %s, norm: %s\n",
(st1 & 0x40) ? "not locked" : "locked",
(st2 & 0x40) ? "no" : "yes",
st[st1 & 0x03]);
dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1);
if (dev->nosignal) {
/* no video signal -> mute audio */
if (dev->ctl_automute)
dev->automute = 1;
saa7134_tvaudio_setmute(dev);
} else {
/* wake up tvaudio audio carrier scan thread */
saa7134_tvaudio_do_scan(dev);
}
if ((st2 & 0x80) && !noninterlaced && !dev->nosignal)
saa_clearb(SAA7134_SYNC_CTRL, 0x20);
else
saa_setb(SAA7134_SYNC_CTRL, 0x20);
if (dev->mops && dev->mops->signal_change)
dev->mops->signal_change(dev);
}
void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status)
{
enum v4l2_field field;
spin_lock(&dev->slock);
if (dev->video_q.curr) {
field = dev->field;
if (V4L2_FIELD_HAS_BOTH(field)) {
/* make sure we have seen both fields */
if ((status & 0x10) == 0x00) {
dev->video_q.curr->top_seen = 1;
goto done;
}
if (!dev->video_q.curr->top_seen)
goto done;
} else if (field == V4L2_FIELD_TOP) {
if ((status & 0x10) != 0x10)
goto done;
} else if (field == V4L2_FIELD_BOTTOM) {
if ((status & 0x10) != 0x00)
goto done;
}
saa7134_buffer_finish(dev, &dev->video_q, VB2_BUF_STATE_DONE);
}
saa7134_buffer_next(dev, &dev->video_q);
done:
spin_unlock(&dev->slock);
}