[media] rename most media/video usb drivers to media/usb
Rename all USB drivers with their own directory under drivers/media/video into drivers/media/usb and update the building system. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
58
drivers/media/usb/em28xx/Kconfig
Normal file
58
drivers/media/usb/em28xx/Kconfig
Normal file
@@ -0,0 +1,58 @@
|
||||
config VIDEO_EM28XX
|
||||
tristate "Empia EM28xx USB video capture support"
|
||||
depends on VIDEO_DEV && I2C
|
||||
select VIDEO_TUNER
|
||||
select VIDEO_TVEEPROM
|
||||
select VIDEOBUF_VMALLOC
|
||||
select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
|
||||
select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO
|
||||
select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
|
||||
select VIDEO_MT9V011 if VIDEO_HELPER_CHIPS_AUTO
|
||||
|
||||
---help---
|
||||
This is a video4linux driver for Empia 28xx based TV cards.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called em28xx
|
||||
|
||||
config VIDEO_EM28XX_ALSA
|
||||
depends on VIDEO_EM28XX && SND
|
||||
select SND_PCM
|
||||
tristate "Empia EM28xx ALSA audio module"
|
||||
---help---
|
||||
This is an ALSA driver for some Empia 28xx based TV cards.
|
||||
|
||||
This is not required for em2800/em2820/em2821 boards. However,
|
||||
newer em28xx devices uses Vendor Class for audio, instead of
|
||||
implementing the USB Audio Class. For those chips, this module
|
||||
will enable digital audio.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called em28xx-alsa
|
||||
|
||||
config VIDEO_EM28XX_DVB
|
||||
tristate "DVB/ATSC Support for em28xx based TV cards"
|
||||
depends on VIDEO_EM28XX && DVB_CORE
|
||||
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
|
||||
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
|
||||
select DVB_TDA10023 if !DVB_FE_CUSTOMISE
|
||||
select DVB_S921 if !DVB_FE_CUSTOMISE
|
||||
select DVB_DRXD if !DVB_FE_CUSTOMISE
|
||||
select DVB_CXD2820R if !DVB_FE_CUSTOMISE
|
||||
select DVB_DRXK if !DVB_FE_CUSTOMISE
|
||||
select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
|
||||
select DVB_TDA10071 if !DVB_FE_CUSTOMISE
|
||||
select DVB_A8293 if !DVB_FE_CUSTOMISE
|
||||
select VIDEOBUF_DVB
|
||||
---help---
|
||||
This adds support for DVB cards based on the
|
||||
Empiatech em28xx chips.
|
||||
|
||||
config VIDEO_EM28XX_RC
|
||||
tristate "EM28XX Remote Controller support"
|
||||
depends on RC_CORE
|
||||
depends on VIDEO_EM28XX
|
||||
depends on !(RC_CORE=m && VIDEO_EM28XX=y)
|
||||
default VIDEO_EM28XX
|
||||
---help---
|
||||
Enables Remote Controller support on em28xx driver.
|
15
drivers/media/usb/em28xx/Makefile
Normal file
15
drivers/media/usb/em28xx/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o
|
||||
em28xx-y += em28xx-core.o em28xx-vbi.o
|
||||
|
||||
em28xx-alsa-objs := em28xx-audio.o
|
||||
em28xx-rc-objs := em28xx-input.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o
|
||||
obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
|
||||
obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o
|
||||
obj-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-rc.o
|
||||
|
||||
ccflags-y += -Idrivers/media/video
|
||||
ccflags-y += -Idrivers/media/tuners
|
||||
ccflags-y += -Idrivers/media/dvb-core
|
||||
ccflags-y += -Idrivers/media/dvb-frontends
|
751
drivers/media/usb/em28xx/em28xx-audio.c
Normal file
751
drivers/media/usb/em28xx/em28xx-audio.c
Normal file
@@ -0,0 +1,751 @@
|
||||
/*
|
||||
* Empiatech em28x1 audio extension
|
||||
*
|
||||
* Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2007-2011 Mauro Carvalho Chehab <mchehab@redhat.com>
|
||||
* - Port to work with the in-kernel driver
|
||||
* - Cleanups, fixes, alsa-controls, etc.
|
||||
*
|
||||
* This driver is based on my previous au600 usb pstn audio driver
|
||||
* and inherits all the copyrights
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sound.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/soundcard.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include "em28xx.h"
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "activates debug info");
|
||||
|
||||
#define dprintk(fmt, arg...) do { \
|
||||
if (debug) \
|
||||
printk(KERN_INFO "em28xx-audio %s: " fmt, \
|
||||
__func__, ##arg); \
|
||||
} while (0)
|
||||
|
||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
|
||||
|
||||
static int em28xx_deinit_isoc_audio(struct em28xx *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
dprintk("Stopping isoc\n");
|
||||
for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
|
||||
if (!irqs_disabled())
|
||||
usb_kill_urb(dev->adev.urb[i]);
|
||||
else
|
||||
usb_unlink_urb(dev->adev.urb[i]);
|
||||
|
||||
usb_free_urb(dev->adev.urb[i]);
|
||||
dev->adev.urb[i] = NULL;
|
||||
|
||||
kfree(dev->adev.transfer_buffer[i]);
|
||||
dev->adev.transfer_buffer[i] = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void em28xx_audio_isocirq(struct urb *urb)
|
||||
{
|
||||
struct em28xx *dev = urb->context;
|
||||
int i;
|
||||
unsigned int oldptr;
|
||||
int period_elapsed = 0;
|
||||
int status;
|
||||
unsigned char *cp;
|
||||
unsigned int stride;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
case -ETIMEDOUT: /* NAK */
|
||||
break;
|
||||
case -ECONNRESET: /* kill */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
default: /* error */
|
||||
dprintk("urb completition error %d.\n", urb->status);
|
||||
break;
|
||||
}
|
||||
|
||||
if (atomic_read(&dev->stream_started) == 0)
|
||||
return;
|
||||
|
||||
if (dev->adev.capture_pcm_substream) {
|
||||
substream = dev->adev.capture_pcm_substream;
|
||||
runtime = substream->runtime;
|
||||
stride = runtime->frame_bits >> 3;
|
||||
|
||||
for (i = 0; i < urb->number_of_packets; i++) {
|
||||
int length =
|
||||
urb->iso_frame_desc[i].actual_length / stride;
|
||||
cp = (unsigned char *)urb->transfer_buffer +
|
||||
urb->iso_frame_desc[i].offset;
|
||||
|
||||
if (!length)
|
||||
continue;
|
||||
|
||||
oldptr = dev->adev.hwptr_done_capture;
|
||||
if (oldptr + length >= runtime->buffer_size) {
|
||||
unsigned int cnt =
|
||||
runtime->buffer_size - oldptr;
|
||||
memcpy(runtime->dma_area + oldptr * stride, cp,
|
||||
cnt * stride);
|
||||
memcpy(runtime->dma_area, cp + cnt * stride,
|
||||
length * stride - cnt * stride);
|
||||
} else {
|
||||
memcpy(runtime->dma_area + oldptr * stride, cp,
|
||||
length * stride);
|
||||
}
|
||||
|
||||
snd_pcm_stream_lock(substream);
|
||||
|
||||
dev->adev.hwptr_done_capture += length;
|
||||
if (dev->adev.hwptr_done_capture >=
|
||||
runtime->buffer_size)
|
||||
dev->adev.hwptr_done_capture -=
|
||||
runtime->buffer_size;
|
||||
|
||||
dev->adev.capture_transfer_done += length;
|
||||
if (dev->adev.capture_transfer_done >=
|
||||
runtime->period_size) {
|
||||
dev->adev.capture_transfer_done -=
|
||||
runtime->period_size;
|
||||
period_elapsed = 1;
|
||||
}
|
||||
|
||||
snd_pcm_stream_unlock(substream);
|
||||
}
|
||||
if (period_elapsed)
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
urb->status = 0;
|
||||
|
||||
status = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (status < 0) {
|
||||
em28xx_errdev("resubmit of audio urb failed (error=%i)\n",
|
||||
status);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int em28xx_init_audio_isoc(struct em28xx *dev)
|
||||
{
|
||||
int i, errCode;
|
||||
const int sb_size = EM28XX_NUM_AUDIO_PACKETS *
|
||||
EM28XX_AUDIO_MAX_PACKET_SIZE;
|
||||
|
||||
dprintk("Starting isoc transfers\n");
|
||||
|
||||
for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
|
||||
struct urb *urb;
|
||||
int j, k;
|
||||
|
||||
dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
|
||||
if (!dev->adev.transfer_buffer[i])
|
||||
return -ENOMEM;
|
||||
|
||||
memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
|
||||
urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
|
||||
if (!urb) {
|
||||
em28xx_errdev("usb_alloc_urb failed!\n");
|
||||
for (j = 0; j < i; j++) {
|
||||
usb_free_urb(dev->adev.urb[j]);
|
||||
kfree(dev->adev.transfer_buffer[j]);
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
urb->dev = dev->udev;
|
||||
urb->context = dev;
|
||||
urb->pipe = usb_rcvisocpipe(dev->udev, EM28XX_EP_AUDIO);
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
urb->transfer_buffer = dev->adev.transfer_buffer[i];
|
||||
urb->interval = 1;
|
||||
urb->complete = em28xx_audio_isocirq;
|
||||
urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS;
|
||||
urb->transfer_buffer_length = sb_size;
|
||||
|
||||
for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS;
|
||||
j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) {
|
||||
urb->iso_frame_desc[j].offset = k;
|
||||
urb->iso_frame_desc[j].length =
|
||||
EM28XX_AUDIO_MAX_PACKET_SIZE;
|
||||
}
|
||||
dev->adev.urb[i] = urb;
|
||||
}
|
||||
|
||||
for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
|
||||
errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
|
||||
if (errCode) {
|
||||
em28xx_errdev("submit of audio urb failed\n");
|
||||
em28xx_deinit_isoc_audio(dev);
|
||||
atomic_set(&dev->stream_started, 0);
|
||||
return errCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
|
||||
size_t size)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = subs->runtime;
|
||||
|
||||
dprintk("Allocating vbuffer\n");
|
||||
if (runtime->dma_area) {
|
||||
if (runtime->dma_bytes > size)
|
||||
return 0;
|
||||
|
||||
vfree(runtime->dma_area);
|
||||
}
|
||||
runtime->dma_area = vmalloc(size);
|
||||
if (!runtime->dma_area)
|
||||
return -ENOMEM;
|
||||
|
||||
runtime->dma_bytes = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_hardware snd_em28xx_hw_capture = {
|
||||
.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BATCH |
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
|
||||
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
|
||||
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
|
||||
.period_bytes_min = 64, /* 12544/2, */
|
||||
.period_bytes_max = 12544,
|
||||
.periods_min = 2,
|
||||
.periods_max = 98, /* 12544, */
|
||||
};
|
||||
|
||||
static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct em28xx *dev = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int ret = 0;
|
||||
|
||||
dprintk("opening device and trying to acquire exclusive lock\n");
|
||||
|
||||
if (!dev) {
|
||||
em28xx_err("BUG: em28xx can't find device struct."
|
||||
" Can't proceed with open\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
runtime->hw = snd_em28xx_hw_capture;
|
||||
if ((dev->alt == 0 || dev->audio_ifnum) && dev->adev.users == 0) {
|
||||
if (dev->audio_ifnum)
|
||||
dev->alt = 1;
|
||||
else
|
||||
dev->alt = 7;
|
||||
|
||||
dprintk("changing alternate number on interface %d to %d\n",
|
||||
dev->audio_ifnum, dev->alt);
|
||||
usb_set_interface(dev->udev, dev->audio_ifnum, dev->alt);
|
||||
|
||||
/* Sets volume, mute, etc */
|
||||
dev->mute = 0;
|
||||
mutex_lock(&dev->lock);
|
||||
ret = em28xx_audio_analog_set(dev);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
dev->adev.users++;
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
dev->adev.capture_pcm_substream = substream;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
em28xx_err("Error while configuring em28xx mixer\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct em28xx *dev = snd_pcm_substream_chip(substream);
|
||||
|
||||
dprintk("closing device\n");
|
||||
|
||||
dev->mute = 1;
|
||||
mutex_lock(&dev->lock);
|
||||
dev->adev.users--;
|
||||
if (atomic_read(&dev->stream_started) > 0) {
|
||||
atomic_set(&dev->stream_started, 0);
|
||||
schedule_work(&dev->wq_trigger);
|
||||
}
|
||||
|
||||
em28xx_audio_analog_set(dev);
|
||||
if (substream->runtime->dma_area) {
|
||||
dprintk("freeing\n");
|
||||
vfree(substream->runtime->dma_area);
|
||||
substream->runtime->dma_area = NULL;
|
||||
}
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dprintk("Setting capture parameters\n");
|
||||
|
||||
ret = snd_pcm_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#if 0
|
||||
/* TODO: set up em28xx audio chip to deliver the correct audio format,
|
||||
current default is 48000hz multiplexed => 96000hz mono
|
||||
which shouldn't matter since analogue TV only supports mono */
|
||||
unsigned int channels, rate, format;
|
||||
|
||||
format = params_format(hw_params);
|
||||
rate = params_rate(hw_params);
|
||||
channels = params_channels(hw_params);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct em28xx *dev = snd_pcm_substream_chip(substream);
|
||||
|
||||
dprintk("Stop capture, if needed\n");
|
||||
|
||||
if (atomic_read(&dev->stream_started) > 0) {
|
||||
atomic_set(&dev->stream_started, 0);
|
||||
schedule_work(&dev->wq_trigger);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct em28xx *dev = snd_pcm_substream_chip(substream);
|
||||
|
||||
dev->adev.hwptr_done_capture = 0;
|
||||
dev->adev.capture_transfer_done = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void audio_trigger(struct work_struct *work)
|
||||
{
|
||||
struct em28xx *dev = container_of(work, struct em28xx, wq_trigger);
|
||||
|
||||
if (atomic_read(&dev->stream_started)) {
|
||||
dprintk("starting capture");
|
||||
em28xx_init_audio_isoc(dev);
|
||||
} else {
|
||||
dprintk("stopping capture");
|
||||
em28xx_deinit_isoc_audio(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct em28xx *dev = snd_pcm_substream_chip(substream);
|
||||
int retval = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
atomic_set(&dev->stream_started, 1);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
atomic_set(&dev->stream_started, 0);
|
||||
break;
|
||||
default:
|
||||
retval = -EINVAL;
|
||||
}
|
||||
schedule_work(&dev->wq_trigger);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
|
||||
*substream)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct em28xx *dev;
|
||||
snd_pcm_uframes_t hwptr_done;
|
||||
|
||||
dev = snd_pcm_substream_chip(substream);
|
||||
spin_lock_irqsave(&dev->adev.slock, flags);
|
||||
hwptr_done = dev->adev.hwptr_done_capture;
|
||||
spin_unlock_irqrestore(&dev->adev.slock, flags);
|
||||
|
||||
return hwptr_done;
|
||||
}
|
||||
|
||||
static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
|
||||
unsigned long offset)
|
||||
{
|
||||
void *pageptr = subs->runtime->dma_area + offset;
|
||||
|
||||
return vmalloc_to_page(pageptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* AC97 volume control support
|
||||
*/
|
||||
static int em28xx_vol_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
info->count = 2;
|
||||
info->value.integer.min = 0;
|
||||
info->value.integer.max = 0x1f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int em28xx_vol_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct em28xx *dev = snd_kcontrol_chip(kcontrol);
|
||||
u16 val = (0x1f - (value->value.integer.value[0] & 0x1f)) |
|
||||
(0x1f - (value->value.integer.value[1] & 0x1f)) << 8;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
rc = em28xx_read_ac97(dev, kcontrol->private_value);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
val |= rc & 0x8000; /* Preserve the mute flag */
|
||||
|
||||
rc = em28xx_write_ac97(dev, kcontrol->private_value, val);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
|
||||
(val & 0x8000) ? "muted " : "",
|
||||
0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
|
||||
val, (int)kcontrol->private_value);
|
||||
|
||||
err:
|
||||
mutex_unlock(&dev->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int em28xx_vol_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct em28xx *dev = snd_kcontrol_chip(kcontrol);
|
||||
int val;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
val = em28xx_read_ac97(dev, kcontrol->private_value);
|
||||
mutex_unlock(&dev->lock);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
|
||||
(val & 0x8000) ? "muted " : "",
|
||||
0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
|
||||
val, (int)kcontrol->private_value);
|
||||
|
||||
value->value.integer.value[0] = 0x1f - (val & 0x1f);
|
||||
value->value.integer.value[1] = 0x1f - ((val << 8) & 0x1f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct em28xx *dev = snd_kcontrol_chip(kcontrol);
|
||||
u16 val = value->value.integer.value[0];
|
||||
int rc;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
rc = em28xx_read_ac97(dev, kcontrol->private_value);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
if (val)
|
||||
rc &= 0x1f1f;
|
||||
else
|
||||
rc |= 0x8000;
|
||||
|
||||
rc = em28xx_write_ac97(dev, kcontrol->private_value, rc);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
|
||||
(val & 0x8000) ? "muted " : "",
|
||||
0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
|
||||
val, (int)kcontrol->private_value);
|
||||
|
||||
err:
|
||||
mutex_unlock(&dev->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct em28xx *dev = snd_kcontrol_chip(kcontrol);
|
||||
int val;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
val = em28xx_read_ac97(dev, kcontrol->private_value);
|
||||
mutex_unlock(&dev->lock);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val & 0x8000)
|
||||
value->value.integer.value[0] = 0;
|
||||
else
|
||||
value->value.integer.value[0] = 1;
|
||||
|
||||
dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
|
||||
(val & 0x8000) ? "muted " : "",
|
||||
0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
|
||||
val, (int)kcontrol->private_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(em28xx_db_scale, -3450, 150, 0);
|
||||
|
||||
static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev,
|
||||
char *name, int id)
|
||||
{
|
||||
int err;
|
||||
char ctl_name[44];
|
||||
struct snd_kcontrol *kctl;
|
||||
struct snd_kcontrol_new tmp;
|
||||
|
||||
memset (&tmp, 0, sizeof(tmp));
|
||||
tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
tmp.private_value = id,
|
||||
tmp.name = ctl_name,
|
||||
|
||||
/* Add Mute Control */
|
||||
sprintf(ctl_name, "%s Switch", name);
|
||||
tmp.get = em28xx_vol_get_mute;
|
||||
tmp.put = em28xx_vol_put_mute;
|
||||
tmp.info = snd_ctl_boolean_mono_info;
|
||||
kctl = snd_ctl_new1(&tmp, dev);
|
||||
err = snd_ctl_add(card, kctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
dprintk("Added control %s for ac97 volume control 0x%04x\n",
|
||||
ctl_name, id);
|
||||
|
||||
memset (&tmp, 0, sizeof(tmp));
|
||||
tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
tmp.private_value = id,
|
||||
tmp.name = ctl_name,
|
||||
|
||||
/* Add Volume Control */
|
||||
sprintf(ctl_name, "%s Volume", name);
|
||||
tmp.get = em28xx_vol_get;
|
||||
tmp.put = em28xx_vol_put;
|
||||
tmp.info = em28xx_vol_info;
|
||||
tmp.tlv.p = em28xx_db_scale,
|
||||
kctl = snd_ctl_new1(&tmp, dev);
|
||||
err = snd_ctl_add(card, kctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
dprintk("Added control %s for ac97 volume control 0x%04x\n",
|
||||
ctl_name, id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* register/unregister code and data
|
||||
*/
|
||||
static struct snd_pcm_ops snd_em28xx_pcm_capture = {
|
||||
.open = snd_em28xx_capture_open,
|
||||
.close = snd_em28xx_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_em28xx_hw_capture_params,
|
||||
.hw_free = snd_em28xx_hw_capture_free,
|
||||
.prepare = snd_em28xx_prepare,
|
||||
.trigger = snd_em28xx_capture_trigger,
|
||||
.pointer = snd_em28xx_capture_pointer,
|
||||
.page = snd_pcm_get_vmalloc_page,
|
||||
};
|
||||
|
||||
static int em28xx_audio_init(struct em28xx *dev)
|
||||
{
|
||||
struct em28xx_audio *adev = &dev->adev;
|
||||
struct snd_pcm *pcm;
|
||||
struct snd_card *card;
|
||||
static int devnr;
|
||||
int err;
|
||||
|
||||
if (!dev->has_alsa_audio || dev->audio_ifnum < 0) {
|
||||
/* This device does not support the extension (in this case
|
||||
the device is expecting the snd-usb-audio module or
|
||||
doesn't have analog audio support at all) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "em28xx-audio.c: probing for em28xx Audio Vendor Class\n");
|
||||
printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
|
||||
"Rechberger\n");
|
||||
printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab\n");
|
||||
|
||||
err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0,
|
||||
&card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
spin_lock_init(&adev->slock);
|
||||
err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture);
|
||||
pcm->info_flags = 0;
|
||||
pcm->private_data = dev;
|
||||
strcpy(pcm->name, "Empia 28xx Capture");
|
||||
|
||||
snd_card_set_dev(card, &dev->udev->dev);
|
||||
strcpy(card->driver, "Em28xx-Audio");
|
||||
strcpy(card->shortname, "Em28xx Audio");
|
||||
strcpy(card->longname, "Empia Em28xx Audio");
|
||||
|
||||
INIT_WORK(&dev->wq_trigger, audio_trigger);
|
||||
|
||||
if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
|
||||
em28xx_cvol_new(card, dev, "Video", AC97_VIDEO);
|
||||
em28xx_cvol_new(card, dev, "Line In", AC97_LINE);
|
||||
em28xx_cvol_new(card, dev, "Phone", AC97_PHONE);
|
||||
em28xx_cvol_new(card, dev, "Microphone", AC97_MIC);
|
||||
em28xx_cvol_new(card, dev, "CD", AC97_CD);
|
||||
em28xx_cvol_new(card, dev, "AUX", AC97_AUX);
|
||||
em28xx_cvol_new(card, dev, "PCM", AC97_PCM);
|
||||
|
||||
em28xx_cvol_new(card, dev, "Master", AC97_MASTER);
|
||||
em28xx_cvol_new(card, dev, "Line", AC97_HEADPHONE);
|
||||
em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO);
|
||||
em28xx_cvol_new(card, dev, "LFE", AC97_CENTER_LFE_MASTER);
|
||||
em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER);
|
||||
}
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
adev->sndcard = card;
|
||||
adev->udev = dev->udev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int em28xx_audio_fini(struct em28xx *dev)
|
||||
{
|
||||
if (dev == NULL)
|
||||
return 0;
|
||||
|
||||
if (dev->has_alsa_audio != 1) {
|
||||
/* This device does not support the extension (in this case
|
||||
the device is expecting the snd-usb-audio module or
|
||||
doesn't have analog audio support at all) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dev->adev.sndcard) {
|
||||
snd_card_free(dev->adev.sndcard);
|
||||
dev->adev.sndcard = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct em28xx_ops audio_ops = {
|
||||
.id = EM28XX_AUDIO,
|
||||
.name = "Em28xx Audio Extension",
|
||||
.init = em28xx_audio_init,
|
||||
.fini = em28xx_audio_fini,
|
||||
};
|
||||
|
||||
static int __init em28xx_alsa_register(void)
|
||||
{
|
||||
return em28xx_register_extension(&audio_ops);
|
||||
}
|
||||
|
||||
static void __exit em28xx_alsa_unregister(void)
|
||||
{
|
||||
em28xx_unregister_extension(&audio_ops);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
|
||||
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
|
||||
MODULE_DESCRIPTION("Em28xx Audio driver");
|
||||
|
||||
module_init(em28xx_alsa_register);
|
||||
module_exit(em28xx_alsa_unregister);
|
3417
drivers/media/usb/em28xx/em28xx-cards.c
Normal file
3417
drivers/media/usb/em28xx/em28xx-cards.c
Normal file
File diff suppressed because it is too large
Load Diff
1260
drivers/media/usb/em28xx/em28xx-core.c
Normal file
1260
drivers/media/usb/em28xx/em28xx-core.c
Normal file
File diff suppressed because it is too large
Load Diff
1197
drivers/media/usb/em28xx/em28xx-dvb.c
Normal file
1197
drivers/media/usb/em28xx/em28xx-dvb.c
Normal file
File diff suppressed because it is too large
Load Diff
568
drivers/media/usb/em28xx/em28xx-i2c.c
Normal file
568
drivers/media/usb/em28xx/em28xx-i2c.c
Normal file
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
em28xx-i2c.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
|
||||
|
||||
Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
|
||||
Markus Rechberger <mrechberger@gmail.com>
|
||||
Mauro Carvalho Chehab <mchehab@infradead.org>
|
||||
Sascha Sommer <saschasommer@freenet.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include "em28xx.h"
|
||||
#include "tuner-xc2028.h"
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/tuner.h>
|
||||
|
||||
/* ----------------------------------------------------------- */
|
||||
|
||||
static unsigned int i2c_scan;
|
||||
module_param(i2c_scan, int, 0444);
|
||||
MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
|
||||
|
||||
static unsigned int i2c_debug;
|
||||
module_param(i2c_debug, int, 0644);
|
||||
MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
|
||||
|
||||
#define dprintk2(lvl, fmt, args...) \
|
||||
do { \
|
||||
if (i2c_debug >= lvl) { \
|
||||
printk(KERN_DEBUG "%s at %s: " fmt, \
|
||||
dev->name, __func__ , ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* em2800_i2c_send_max4()
|
||||
* send up to 4 bytes to the i2c device
|
||||
*/
|
||||
static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr,
|
||||
char *buf, int len)
|
||||
{
|
||||
int ret;
|
||||
int write_timeout;
|
||||
unsigned char b2[6];
|
||||
BUG_ON(len < 1 || len > 4);
|
||||
b2[5] = 0x80 + len - 1;
|
||||
b2[4] = addr;
|
||||
b2[3] = buf[0];
|
||||
if (len > 1)
|
||||
b2[2] = buf[1];
|
||||
if (len > 2)
|
||||
b2[1] = buf[2];
|
||||
if (len > 3)
|
||||
b2[0] = buf[3];
|
||||
|
||||
ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len);
|
||||
if (ret != 2 + len) {
|
||||
em28xx_warn("writing to i2c device failed (error=%i)\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0;
|
||||
write_timeout -= 5) {
|
||||
ret = dev->em28xx_read_reg(dev, 0x05);
|
||||
if (ret == 0x80 + len - 1)
|
||||
return len;
|
||||
msleep(5);
|
||||
}
|
||||
em28xx_warn("i2c write timed out\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* em2800_i2c_send_bytes()
|
||||
*/
|
||||
static int em2800_i2c_send_bytes(void *data, unsigned char addr, char *buf,
|
||||
short len)
|
||||
{
|
||||
char *bufPtr = buf;
|
||||
int ret;
|
||||
int wrcount = 0;
|
||||
int count;
|
||||
int maxLen = 4;
|
||||
struct em28xx *dev = (struct em28xx *)data;
|
||||
while (len > 0) {
|
||||
count = (len > maxLen) ? maxLen : len;
|
||||
ret = em2800_i2c_send_max4(dev, addr, bufPtr, count);
|
||||
if (ret > 0) {
|
||||
len -= count;
|
||||
bufPtr += count;
|
||||
wrcount += count;
|
||||
} else
|
||||
return (ret < 0) ? ret : -EFAULT;
|
||||
}
|
||||
return wrcount;
|
||||
}
|
||||
|
||||
/*
|
||||
* em2800_i2c_check_for_device()
|
||||
* check if there is a i2c_device at the supplied address
|
||||
*/
|
||||
static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr)
|
||||
{
|
||||
char msg;
|
||||
int ret;
|
||||
int write_timeout;
|
||||
msg = addr;
|
||||
ret = dev->em28xx_write_regs(dev, 0x04, &msg, 1);
|
||||
if (ret < 0) {
|
||||
em28xx_warn("setting i2c device address failed (error=%i)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
msg = 0x84;
|
||||
ret = dev->em28xx_write_regs(dev, 0x05, &msg, 1);
|
||||
if (ret < 0) {
|
||||
em28xx_warn("preparing i2c read failed (error=%i)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0;
|
||||
write_timeout -= 5) {
|
||||
unsigned reg = dev->em28xx_read_reg(dev, 0x5);
|
||||
|
||||
if (reg == 0x94)
|
||||
return -ENODEV;
|
||||
else if (reg == 0x84)
|
||||
return 0;
|
||||
msleep(5);
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* em2800_i2c_recv_bytes()
|
||||
* read from the i2c device
|
||||
*/
|
||||
static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr,
|
||||
char *buf, int len)
|
||||
{
|
||||
int ret;
|
||||
/* check for the device and set i2c read address */
|
||||
ret = em2800_i2c_check_for_device(dev, addr);
|
||||
if (ret) {
|
||||
em28xx_warn
|
||||
("preparing read at i2c address 0x%x failed (error=%i)\n",
|
||||
addr, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = dev->em28xx_read_reg_req_len(dev, 0x0, 0x3, buf, len);
|
||||
if (ret < 0) {
|
||||
em28xx_warn("reading from i2c device at 0x%x failed (error=%i)",
|
||||
addr, ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* em28xx_i2c_send_bytes()
|
||||
*/
|
||||
static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf,
|
||||
short len, int stop)
|
||||
{
|
||||
int wrcount = 0;
|
||||
struct em28xx *dev = (struct em28xx *)data;
|
||||
int write_timeout, ret;
|
||||
|
||||
wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len);
|
||||
|
||||
/* Seems to be required after a write */
|
||||
for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0;
|
||||
write_timeout -= 5) {
|
||||
ret = dev->em28xx_read_reg(dev, 0x05);
|
||||
if (!ret)
|
||||
break;
|
||||
msleep(5);
|
||||
}
|
||||
|
||||
return wrcount;
|
||||
}
|
||||
|
||||
/*
|
||||
* em28xx_i2c_recv_bytes()
|
||||
* read a byte from the i2c device
|
||||
*/
|
||||
static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr,
|
||||
char *buf, int len)
|
||||
{
|
||||
int ret;
|
||||
ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len);
|
||||
if (ret < 0) {
|
||||
em28xx_warn("reading i2c device failed (error=%i)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (dev->em28xx_read_reg(dev, 0x5) != 0)
|
||||
return -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* em28xx_i2c_check_for_device()
|
||||
* check if there is a i2c_device at the supplied address
|
||||
*/
|
||||
static int em28xx_i2c_check_for_device(struct em28xx *dev, unsigned char addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dev->em28xx_read_reg_req(dev, 2, addr);
|
||||
if (ret < 0) {
|
||||
em28xx_warn("reading from i2c device failed (error=%i)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (dev->em28xx_read_reg(dev, 0x5) != 0)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* em28xx_i2c_xfer()
|
||||
* the main i2c transfer function
|
||||
*/
|
||||
static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||
struct i2c_msg msgs[], int num)
|
||||
{
|
||||
struct em28xx *dev = i2c_adap->algo_data;
|
||||
int addr, rc, i, byte;
|
||||
|
||||
if (num <= 0)
|
||||
return 0;
|
||||
for (i = 0; i < num; i++) {
|
||||
addr = msgs[i].addr << 1;
|
||||
dprintk2(2, "%s %s addr=%x len=%d:",
|
||||
(msgs[i].flags & I2C_M_RD) ? "read" : "write",
|
||||
i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
|
||||
if (!msgs[i].len) { /* no len: check only for device presence */
|
||||
if (dev->board.is_em2800)
|
||||
rc = em2800_i2c_check_for_device(dev, addr);
|
||||
else
|
||||
rc = em28xx_i2c_check_for_device(dev, addr);
|
||||
if (rc < 0) {
|
||||
dprintk2(2, " no device\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
} else if (msgs[i].flags & I2C_M_RD) {
|
||||
/* read bytes */
|
||||
if (dev->board.is_em2800)
|
||||
rc = em2800_i2c_recv_bytes(dev, addr,
|
||||
msgs[i].buf,
|
||||
msgs[i].len);
|
||||
else
|
||||
rc = em28xx_i2c_recv_bytes(dev, addr,
|
||||
msgs[i].buf,
|
||||
msgs[i].len);
|
||||
if (i2c_debug >= 2) {
|
||||
for (byte = 0; byte < msgs[i].len; byte++)
|
||||
printk(" %02x", msgs[i].buf[byte]);
|
||||
}
|
||||
} else {
|
||||
/* write bytes */
|
||||
if (i2c_debug >= 2) {
|
||||
for (byte = 0; byte < msgs[i].len; byte++)
|
||||
printk(" %02x", msgs[i].buf[byte]);
|
||||
}
|
||||
if (dev->board.is_em2800)
|
||||
rc = em2800_i2c_send_bytes(dev, addr,
|
||||
msgs[i].buf,
|
||||
msgs[i].len);
|
||||
else
|
||||
rc = em28xx_i2c_send_bytes(dev, addr,
|
||||
msgs[i].buf,
|
||||
msgs[i].len,
|
||||
i == num - 1);
|
||||
}
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
if (i2c_debug >= 2)
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
return num;
|
||||
err:
|
||||
dprintk2(2, " ERROR: %i\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* based on linux/sunrpc/svcauth.h and linux/hash.h
|
||||
* The original hash function returns a different value, if arch is x86_64
|
||||
* or i386.
|
||||
*/
|
||||
static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits)
|
||||
{
|
||||
unsigned long hash = 0;
|
||||
unsigned long l = 0;
|
||||
int len = 0;
|
||||
unsigned char c;
|
||||
do {
|
||||
if (len == length) {
|
||||
c = (char)len;
|
||||
len = -1;
|
||||
} else
|
||||
c = *buf++;
|
||||
l = (l << 8) | c;
|
||||
len++;
|
||||
if ((len & (32 / 8 - 1)) == 0)
|
||||
hash = ((hash^l) * 0x9e370001UL);
|
||||
} while (len);
|
||||
|
||||
return (hash >> (32 - bits)) & 0xffffffffUL;
|
||||
}
|
||||
|
||||
static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
|
||||
{
|
||||
unsigned char buf, *p = eedata;
|
||||
struct em28xx_eeprom *em_eeprom = (void *)eedata;
|
||||
int i, err, size = len, block;
|
||||
|
||||
if (dev->chip_id == CHIP_ID_EM2874 ||
|
||||
dev->chip_id == CHIP_ID_EM28174 ||
|
||||
dev->chip_id == CHIP_ID_EM2884) {
|
||||
/* Empia switched to a 16-bit addressable eeprom in newer
|
||||
devices. While we could certainly write a routine to read
|
||||
the eeprom, there is nothing of use in there that cannot be
|
||||
accessed through registers, and there is the risk that we
|
||||
could corrupt the eeprom (since a 16-bit read call is
|
||||
interpreted as a write call by 8-bit eeproms).
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev->i2c_client.addr = 0xa0 >> 1;
|
||||
|
||||
/* Check if board has eeprom */
|
||||
err = i2c_master_recv(&dev->i2c_client, &buf, 0);
|
||||
if (err < 0) {
|
||||
em28xx_errdev("board has no eeprom\n");
|
||||
memset(eedata, 0, len);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
buf = 0;
|
||||
|
||||
err = i2c_master_send(&dev->i2c_client, &buf, 1);
|
||||
if (err != 1) {
|
||||
printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n",
|
||||
dev->name, err);
|
||||
return err;
|
||||
}
|
||||
while (size > 0) {
|
||||
if (size > 16)
|
||||
block = 16;
|
||||
else
|
||||
block = size;
|
||||
|
||||
if (block !=
|
||||
(err = i2c_master_recv(&dev->i2c_client, p, block))) {
|
||||
printk(KERN_WARNING
|
||||
"%s: i2c eeprom read error (err=%d)\n",
|
||||
dev->name, err);
|
||||
return err;
|
||||
}
|
||||
size -= block;
|
||||
p += block;
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
if (0 == (i % 16))
|
||||
printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i);
|
||||
printk(" %02x", eedata[i]);
|
||||
if (15 == (i % 16))
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
if (em_eeprom->id == 0x9567eb1a)
|
||||
dev->hash = em28xx_hash_mem(eedata, len, 32);
|
||||
|
||||
printk(KERN_INFO "%s: EEPROM ID= 0x%08x, EEPROM hash = 0x%08lx\n",
|
||||
dev->name, em_eeprom->id, dev->hash);
|
||||
|
||||
printk(KERN_INFO "%s: EEPROM info:\n", dev->name);
|
||||
|
||||
switch (em_eeprom->chip_conf >> 4 & 0x3) {
|
||||
case 0:
|
||||
printk(KERN_INFO "%s:\tNo audio on board.\n", dev->name);
|
||||
break;
|
||||
case 1:
|
||||
printk(KERN_INFO "%s:\tAC97 audio (5 sample rates)\n",
|
||||
dev->name);
|
||||
break;
|
||||
case 2:
|
||||
printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n",
|
||||
dev->name);
|
||||
break;
|
||||
case 3:
|
||||
printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n",
|
||||
dev->name);
|
||||
break;
|
||||
}
|
||||
|
||||
if (em_eeprom->chip_conf & 1 << 3)
|
||||
printk(KERN_INFO "%s:\tUSB Remote wakeup capable\n", dev->name);
|
||||
|
||||
if (em_eeprom->chip_conf & 1 << 2)
|
||||
printk(KERN_INFO "%s:\tUSB Self power capable\n", dev->name);
|
||||
|
||||
switch (em_eeprom->chip_conf & 0x3) {
|
||||
case 0:
|
||||
printk(KERN_INFO "%s:\t500mA max power\n", dev->name);
|
||||
break;
|
||||
case 1:
|
||||
printk(KERN_INFO "%s:\t400mA max power\n", dev->name);
|
||||
break;
|
||||
case 2:
|
||||
printk(KERN_INFO "%s:\t300mA max power\n", dev->name);
|
||||
break;
|
||||
case 3:
|
||||
printk(KERN_INFO "%s:\t200mA max power\n", dev->name);
|
||||
break;
|
||||
}
|
||||
printk(KERN_INFO "%s:\tTable at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n",
|
||||
dev->name,
|
||||
em_eeprom->string_idx_table,
|
||||
em_eeprom->string1,
|
||||
em_eeprom->string2,
|
||||
em_eeprom->string3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* functionality()
|
||||
*/
|
||||
static u32 functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static struct i2c_algorithm em28xx_algo = {
|
||||
.master_xfer = em28xx_i2c_xfer,
|
||||
.functionality = functionality,
|
||||
};
|
||||
|
||||
static struct i2c_adapter em28xx_adap_template = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "em28xx",
|
||||
.algo = &em28xx_algo,
|
||||
};
|
||||
|
||||
static struct i2c_client em28xx_client_template = {
|
||||
.name = "em28xx internal",
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* i2c_devs
|
||||
* incomplete list of known devices
|
||||
*/
|
||||
static char *i2c_devs[128] = {
|
||||
[0x4a >> 1] = "saa7113h",
|
||||
[0x52 >> 1] = "drxk",
|
||||
[0x60 >> 1] = "remote IR sensor",
|
||||
[0x8e >> 1] = "remote IR sensor",
|
||||
[0x86 >> 1] = "tda9887",
|
||||
[0x80 >> 1] = "msp34xx",
|
||||
[0x88 >> 1] = "msp34xx",
|
||||
[0xa0 >> 1] = "eeprom",
|
||||
[0xb0 >> 1] = "tda9874",
|
||||
[0xb8 >> 1] = "tvp5150a",
|
||||
[0xba >> 1] = "webcam sensor or tvp5150a",
|
||||
[0xc0 >> 1] = "tuner (analog)",
|
||||
[0xc2 >> 1] = "tuner (analog)",
|
||||
[0xc4 >> 1] = "tuner (analog)",
|
||||
[0xc6 >> 1] = "tuner (analog)",
|
||||
};
|
||||
|
||||
/*
|
||||
* do_i2c_scan()
|
||||
* check i2c address range for devices
|
||||
*/
|
||||
void em28xx_do_i2c_scan(struct em28xx *dev)
|
||||
{
|
||||
u8 i2c_devicelist[128];
|
||||
unsigned char buf;
|
||||
int i, rc;
|
||||
|
||||
memset(i2c_devicelist, 0, ARRAY_SIZE(i2c_devicelist));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
|
||||
dev->i2c_client.addr = i;
|
||||
rc = i2c_master_recv(&dev->i2c_client, &buf, 0);
|
||||
if (rc < 0)
|
||||
continue;
|
||||
i2c_devicelist[i] = i;
|
||||
printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n",
|
||||
dev->name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
|
||||
}
|
||||
|
||||
dev->i2c_hash = em28xx_hash_mem(i2c_devicelist,
|
||||
ARRAY_SIZE(i2c_devicelist), 32);
|
||||
}
|
||||
|
||||
/*
|
||||
* em28xx_i2c_register()
|
||||
* register i2c bus
|
||||
*/
|
||||
int em28xx_i2c_register(struct em28xx *dev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg);
|
||||
BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req);
|
||||
dev->i2c_adap = em28xx_adap_template;
|
||||
dev->i2c_adap.dev.parent = &dev->udev->dev;
|
||||
strcpy(dev->i2c_adap.name, dev->name);
|
||||
dev->i2c_adap.algo_data = dev;
|
||||
i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
|
||||
|
||||
retval = i2c_add_adapter(&dev->i2c_adap);
|
||||
if (retval < 0) {
|
||||
em28xx_errdev("%s: i2c_add_adapter failed! retval [%d]\n",
|
||||
__func__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
dev->i2c_client = em28xx_client_template;
|
||||
dev->i2c_client.adapter = &dev->i2c_adap;
|
||||
|
||||
retval = em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata));
|
||||
if ((retval < 0) && (retval != -ENODEV)) {
|
||||
em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n",
|
||||
__func__, retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (i2c_scan)
|
||||
em28xx_do_i2c_scan(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* em28xx_i2c_unregister()
|
||||
* unregister i2c_bus
|
||||
*/
|
||||
int em28xx_i2c_unregister(struct em28xx *dev)
|
||||
{
|
||||
i2c_del_adapter(&dev->i2c_adap);
|
||||
return 0;
|
||||
}
|
645
drivers/media/usb/em28xx/em28xx-input.c
Normal file
645
drivers/media/usb/em28xx/em28xx-input.c
Normal file
@@ -0,0 +1,645 @@
|
||||
/*
|
||||
handle em28xx IR remotes via linux kernel input layer.
|
||||
|
||||
Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
|
||||
Markus Rechberger <mrechberger@gmail.com>
|
||||
Mauro Carvalho Chehab <mchehab@infradead.org>
|
||||
Sascha Sommer <saschasommer@freenet.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "em28xx.h"
|
||||
|
||||
#define EM28XX_SNAPSHOT_KEY KEY_CAMERA
|
||||
#define EM28XX_SBUTTON_QUERY_INTERVAL 500
|
||||
#define EM28XX_R0C_USBSUSP_SNAPSHOT 0x20
|
||||
|
||||
static unsigned int ir_debug;
|
||||
module_param(ir_debug, int, 0644);
|
||||
MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
|
||||
|
||||
#define MODULE_NAME "em28xx"
|
||||
|
||||
#define i2cdprintk(fmt, arg...) \
|
||||
if (ir_debug) { \
|
||||
printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
|
||||
}
|
||||
|
||||
#define dprintk(fmt, arg...) \
|
||||
if (ir_debug) { \
|
||||
printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
Polling structure used by em28xx IR's
|
||||
**********************************************************/
|
||||
|
||||
struct em28xx_ir_poll_result {
|
||||
unsigned int toggle_bit:1;
|
||||
unsigned int read_count:7;
|
||||
u8 rc_address;
|
||||
u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */
|
||||
};
|
||||
|
||||
struct em28xx_IR {
|
||||
struct em28xx *dev;
|
||||
struct rc_dev *rc;
|
||||
char name[32];
|
||||
char phys[32];
|
||||
|
||||
/* poll external decoder */
|
||||
int polling;
|
||||
struct delayed_work work;
|
||||
unsigned int full_code:1;
|
||||
unsigned int last_readcount;
|
||||
|
||||
int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *);
|
||||
};
|
||||
|
||||
/**********************************************************
|
||||
I2C IR based get keycodes - should be used with ir-kbd-i2c
|
||||
**********************************************************/
|
||||
|
||||
static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
|
||||
{
|
||||
unsigned char b;
|
||||
|
||||
/* poll IR chip */
|
||||
if (1 != i2c_master_recv(ir->c, &b, 1)) {
|
||||
i2cdprintk("read error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* it seems that 0xFE indicates that a button is still hold
|
||||
down, while 0xff indicates that no button is hold
|
||||
down. 0xfe sequences are sometimes interrupted by 0xFF */
|
||||
|
||||
i2cdprintk("key %02x\n", b);
|
||||
|
||||
if (b == 0xff)
|
||||
return 0;
|
||||
|
||||
if (b == 0xfe)
|
||||
/* keep old data */
|
||||
return 1;
|
||||
|
||||
*ir_key = b;
|
||||
*ir_raw = b;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
|
||||
{
|
||||
unsigned char buf[2];
|
||||
u16 code;
|
||||
int size;
|
||||
|
||||
/* poll IR chip */
|
||||
size = i2c_master_recv(ir->c, buf, sizeof(buf));
|
||||
|
||||
if (size != 2)
|
||||
return -EIO;
|
||||
|
||||
/* Does eliminate repeated parity code */
|
||||
if (buf[1] == 0xff)
|
||||
return 0;
|
||||
|
||||
ir->old = buf[1];
|
||||
|
||||
/*
|
||||
* Rearranges bits to the right order.
|
||||
* The bit order were determined experimentally by using
|
||||
* The original Hauppauge Grey IR and another RC5 that uses addr=0x08
|
||||
* The RC5 code has 14 bits, but we've experimentally determined
|
||||
* the meaning for only 11 bits.
|
||||
* So, the code translation is not complete. Yet, it is enough to
|
||||
* work with the provided RC5 IR.
|
||||
*/
|
||||
code =
|
||||
((buf[0] & 0x01) ? 0x0020 : 0) | /* 0010 0000 */
|
||||
((buf[0] & 0x02) ? 0x0010 : 0) | /* 0001 0000 */
|
||||
((buf[0] & 0x04) ? 0x0008 : 0) | /* 0000 1000 */
|
||||
((buf[0] & 0x08) ? 0x0004 : 0) | /* 0000 0100 */
|
||||
((buf[0] & 0x10) ? 0x0002 : 0) | /* 0000 0010 */
|
||||
((buf[0] & 0x20) ? 0x0001 : 0) | /* 0000 0001 */
|
||||
((buf[1] & 0x08) ? 0x1000 : 0) | /* 0001 0000 */
|
||||
((buf[1] & 0x10) ? 0x0800 : 0) | /* 0000 1000 */
|
||||
((buf[1] & 0x20) ? 0x0400 : 0) | /* 0000 0100 */
|
||||
((buf[1] & 0x40) ? 0x0200 : 0) | /* 0000 0010 */
|
||||
((buf[1] & 0x80) ? 0x0100 : 0); /* 0000 0001 */
|
||||
|
||||
i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x%02x)\n",
|
||||
code, buf[1], buf[0]);
|
||||
|
||||
/* return key */
|
||||
*ir_key = code;
|
||||
*ir_raw = code;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
|
||||
u32 *ir_raw)
|
||||
{
|
||||
unsigned char buf[3];
|
||||
|
||||
/* poll IR chip */
|
||||
|
||||
if (3 != i2c_master_recv(ir->c, buf, 3)) {
|
||||
i2cdprintk("read error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
i2cdprintk("key %02x\n", buf[2]&0x3f);
|
||||
if (buf[0] != 0x00)
|
||||
return 0;
|
||||
|
||||
*ir_key = buf[2]&0x3f;
|
||||
*ir_raw = buf[2]&0x3f;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key,
|
||||
u32 *ir_raw)
|
||||
{
|
||||
unsigned char subaddr, keydetect, key;
|
||||
|
||||
struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, .buf = &subaddr, .len = 1},
|
||||
|
||||
{ .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} };
|
||||
|
||||
subaddr = 0x10;
|
||||
if (2 != i2c_transfer(ir->c->adapter, msg, 2)) {
|
||||
i2cdprintk("read error\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (keydetect == 0x00)
|
||||
return 0;
|
||||
|
||||
subaddr = 0x00;
|
||||
msg[1].buf = &key;
|
||||
if (2 != i2c_transfer(ir->c->adapter, msg, 2)) {
|
||||
i2cdprintk("read error\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (key == 0x00)
|
||||
return 0;
|
||||
|
||||
*ir_key = key;
|
||||
*ir_raw = key;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
Poll based get keycode functions
|
||||
**********************************************************/
|
||||
|
||||
/* This is for the em2860/em2880 */
|
||||
static int default_polling_getkey(struct em28xx_IR *ir,
|
||||
struct em28xx_ir_poll_result *poll_result)
|
||||
{
|
||||
struct em28xx *dev = ir->dev;
|
||||
int rc;
|
||||
u8 msg[3] = { 0, 0, 0 };
|
||||
|
||||
/* Read key toggle, brand, and key code
|
||||
on registers 0x45, 0x46 and 0x47
|
||||
*/
|
||||
rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR,
|
||||
msg, sizeof(msg));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Infrared toggle (Reg 0x45[7]) */
|
||||
poll_result->toggle_bit = (msg[0] >> 7);
|
||||
|
||||
/* Infrared read count (Reg 0x45[6:0] */
|
||||
poll_result->read_count = (msg[0] & 0x7f);
|
||||
|
||||
/* Remote Control Address (Reg 0x46) */
|
||||
poll_result->rc_address = msg[1];
|
||||
|
||||
/* Remote Control Data (Reg 0x47) */
|
||||
poll_result->rc_data[0] = msg[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int em2874_polling_getkey(struct em28xx_IR *ir,
|
||||
struct em28xx_ir_poll_result *poll_result)
|
||||
{
|
||||
struct em28xx *dev = ir->dev;
|
||||
int rc;
|
||||
u8 msg[5] = { 0, 0, 0, 0, 0 };
|
||||
|
||||
/* Read key toggle, brand, and key code
|
||||
on registers 0x51-55
|
||||
*/
|
||||
rc = dev->em28xx_read_reg_req_len(dev, 0, EM2874_R51_IR,
|
||||
msg, sizeof(msg));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Infrared toggle (Reg 0x51[7]) */
|
||||
poll_result->toggle_bit = (msg[0] >> 7);
|
||||
|
||||
/* Infrared read count (Reg 0x51[6:0] */
|
||||
poll_result->read_count = (msg[0] & 0x7f);
|
||||
|
||||
/* Remote Control Address (Reg 0x52) */
|
||||
poll_result->rc_address = msg[1];
|
||||
|
||||
/* Remote Control Data (Reg 0x53-55) */
|
||||
poll_result->rc_data[0] = msg[2];
|
||||
poll_result->rc_data[1] = msg[3];
|
||||
poll_result->rc_data[2] = msg[4];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
Polling code for em28xx
|
||||
**********************************************************/
|
||||
|
||||
static void em28xx_ir_handle_key(struct em28xx_IR *ir)
|
||||
{
|
||||
int result;
|
||||
struct em28xx_ir_poll_result poll_result;
|
||||
|
||||
/* read the registers containing the IR status */
|
||||
result = ir->get_key(ir, &poll_result);
|
||||
if (unlikely(result < 0)) {
|
||||
dprintk("ir->get_key() failed %d\n", result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlikely(poll_result.read_count != ir->last_readcount)) {
|
||||
dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__,
|
||||
poll_result.toggle_bit, poll_result.read_count,
|
||||
poll_result.rc_address, poll_result.rc_data[0]);
|
||||
if (ir->full_code)
|
||||
rc_keydown(ir->rc,
|
||||
poll_result.rc_address << 8 |
|
||||
poll_result.rc_data[0],
|
||||
poll_result.toggle_bit);
|
||||
else
|
||||
rc_keydown(ir->rc,
|
||||
poll_result.rc_data[0],
|
||||
poll_result.toggle_bit);
|
||||
|
||||
if (ir->dev->chip_id == CHIP_ID_EM2874 ||
|
||||
ir->dev->chip_id == CHIP_ID_EM2884)
|
||||
/* The em2874 clears the readcount field every time the
|
||||
register is read. The em2860/2880 datasheet says that it
|
||||
is supposed to clear the readcount, but it doesn't. So with
|
||||
the em2874, we are looking for a non-zero read count as
|
||||
opposed to a readcount that is incrementing */
|
||||
ir->last_readcount = 0;
|
||||
else
|
||||
ir->last_readcount = poll_result.read_count;
|
||||
}
|
||||
}
|
||||
|
||||
static void em28xx_ir_work(struct work_struct *work)
|
||||
{
|
||||
struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work);
|
||||
|
||||
em28xx_ir_handle_key(ir);
|
||||
schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
|
||||
}
|
||||
|
||||
static int em28xx_ir_start(struct rc_dev *rc)
|
||||
{
|
||||
struct em28xx_IR *ir = rc->priv;
|
||||
|
||||
INIT_DELAYED_WORK(&ir->work, em28xx_ir_work);
|
||||
schedule_delayed_work(&ir->work, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void em28xx_ir_stop(struct rc_dev *rc)
|
||||
{
|
||||
struct em28xx_IR *ir = rc->priv;
|
||||
|
||||
cancel_delayed_work_sync(&ir->work);
|
||||
}
|
||||
|
||||
static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type)
|
||||
{
|
||||
int rc = 0;
|
||||
struct em28xx_IR *ir = rc_dev->priv;
|
||||
struct em28xx *dev = ir->dev;
|
||||
u8 ir_config = EM2874_IR_RC5;
|
||||
|
||||
/* Adjust xclk based o IR table for RC5/NEC tables */
|
||||
|
||||
if (rc_type == RC_TYPE_RC5) {
|
||||
dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE;
|
||||
ir->full_code = 1;
|
||||
} else if (rc_type == RC_TYPE_NEC) {
|
||||
dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE;
|
||||
ir_config = EM2874_IR_NEC;
|
||||
ir->full_code = 1;
|
||||
} else if (rc_type != RC_TYPE_UNKNOWN)
|
||||
rc = -EINVAL;
|
||||
|
||||
em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk,
|
||||
EM28XX_XCLK_IR_RC5_MODE);
|
||||
|
||||
/* Setup the proper handler based on the chip */
|
||||
switch (dev->chip_id) {
|
||||
case CHIP_ID_EM2860:
|
||||
case CHIP_ID_EM2883:
|
||||
ir->get_key = default_polling_getkey;
|
||||
break;
|
||||
case CHIP_ID_EM2884:
|
||||
case CHIP_ID_EM2874:
|
||||
case CHIP_ID_EM28174:
|
||||
ir->get_key = em2874_polling_getkey;
|
||||
em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1);
|
||||
break;
|
||||
default:
|
||||
printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n",
|
||||
dev->chip_id);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void em28xx_register_i2c_ir(struct em28xx *dev)
|
||||
{
|
||||
/* Leadtek winfast tv USBII deluxe can find a non working IR-device */
|
||||
/* at address 0x18, so if that address is needed for another board in */
|
||||
/* the future, please put it after 0x1f. */
|
||||
struct i2c_board_info info;
|
||||
const unsigned short addr_list[] = {
|
||||
0x1f, 0x30, 0x47, I2C_CLIENT_END
|
||||
};
|
||||
|
||||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
memset(&dev->init_data, 0, sizeof(dev->init_data));
|
||||
strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
|
||||
|
||||
/* detect & configure */
|
||||
switch (dev->model) {
|
||||
case EM2800_BOARD_TERRATEC_CINERGY_200:
|
||||
case EM2820_BOARD_TERRATEC_CINERGY_250:
|
||||
dev->init_data.ir_codes = RC_MAP_EM_TERRATEC;
|
||||
dev->init_data.get_key = em28xx_get_key_terratec;
|
||||
dev->init_data.name = "i2c IR (EM28XX Terratec)";
|
||||
break;
|
||||
case EM2820_BOARD_PINNACLE_USB_2:
|
||||
dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY;
|
||||
dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey;
|
||||
dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)";
|
||||
break;
|
||||
case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
|
||||
dev->init_data.ir_codes = RC_MAP_HAUPPAUGE;
|
||||
dev->init_data.get_key = em28xx_get_key_em_haup;
|
||||
dev->init_data.name = "i2c IR (EM2840 Hauppauge)";
|
||||
break;
|
||||
case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE:
|
||||
dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE;
|
||||
dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe;
|
||||
dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)";
|
||||
break;
|
||||
}
|
||||
|
||||
if (dev->init_data.name)
|
||||
info.platform_data = &dev->init_data;
|
||||
i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL);
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
Handle Webcam snapshot button
|
||||
**********************************************************/
|
||||
|
||||
static void em28xx_query_sbutton(struct work_struct *work)
|
||||
{
|
||||
/* Poll the register and see if the button is depressed */
|
||||
struct em28xx *dev =
|
||||
container_of(work, struct em28xx, sbutton_query_work.work);
|
||||
int ret;
|
||||
|
||||
ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP);
|
||||
|
||||
if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) {
|
||||
u8 cleared;
|
||||
/* Button is depressed, clear the register */
|
||||
cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT;
|
||||
em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1);
|
||||
|
||||
/* Not emulate the keypress */
|
||||
input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
|
||||
1);
|
||||
/* Now unpress the key */
|
||||
input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
|
||||
0);
|
||||
}
|
||||
|
||||
/* Schedule next poll */
|
||||
schedule_delayed_work(&dev->sbutton_query_work,
|
||||
msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
|
||||
}
|
||||
|
||||
static void em28xx_register_snapshot_button(struct em28xx *dev)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
em28xx_info("Registering snapshot button...\n");
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
em28xx_errdev("input_allocate_device failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
usb_make_path(dev->udev, dev->snapshot_button_path,
|
||||
sizeof(dev->snapshot_button_path));
|
||||
strlcat(dev->snapshot_button_path, "/sbutton",
|
||||
sizeof(dev->snapshot_button_path));
|
||||
INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton);
|
||||
|
||||
input_dev->name = "em28xx snapshot button";
|
||||
input_dev->phys = dev->snapshot_button_path;
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
|
||||
set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit);
|
||||
input_dev->keycodesize = 0;
|
||||
input_dev->keycodemax = 0;
|
||||
input_dev->id.bustype = BUS_USB;
|
||||
input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
|
||||
input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
|
||||
input_dev->id.version = 1;
|
||||
input_dev->dev.parent = &dev->udev->dev;
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err) {
|
||||
em28xx_errdev("input_register_device failed\n");
|
||||
input_free_device(input_dev);
|
||||
return;
|
||||
}
|
||||
|
||||
dev->sbutton_input_dev = input_dev;
|
||||
schedule_delayed_work(&dev->sbutton_query_work,
|
||||
msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
static void em28xx_deregister_snapshot_button(struct em28xx *dev)
|
||||
{
|
||||
if (dev->sbutton_input_dev != NULL) {
|
||||
em28xx_info("Deregistering snapshot button\n");
|
||||
cancel_delayed_work_sync(&dev->sbutton_query_work);
|
||||
input_unregister_device(dev->sbutton_input_dev);
|
||||
dev->sbutton_input_dev = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int em28xx_ir_init(struct em28xx *dev)
|
||||
{
|
||||
struct em28xx_IR *ir;
|
||||
struct rc_dev *rc;
|
||||
int err = -ENOMEM;
|
||||
|
||||
if (dev->board.ir_codes == NULL) {
|
||||
/* No remote control support */
|
||||
em28xx_warn("Remote control support is not available for "
|
||||
"this card.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
|
||||
rc = rc_allocate_device();
|
||||
if (!ir || !rc)
|
||||
goto err_out_free;
|
||||
|
||||
/* record handles to ourself */
|
||||
ir->dev = dev;
|
||||
dev->ir = ir;
|
||||
ir->rc = rc;
|
||||
|
||||
/*
|
||||
* em2874 supports more protocols. For now, let's just announce
|
||||
* the two protocols that were already tested
|
||||
*/
|
||||
rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC;
|
||||
rc->priv = ir;
|
||||
rc->change_protocol = em28xx_ir_change_protocol;
|
||||
rc->open = em28xx_ir_start;
|
||||
rc->close = em28xx_ir_stop;
|
||||
|
||||
/* By default, keep protocol field untouched */
|
||||
err = em28xx_ir_change_protocol(rc, RC_TYPE_UNKNOWN);
|
||||
if (err)
|
||||
goto err_out_free;
|
||||
|
||||
/* This is how often we ask the chip for IR information */
|
||||
ir->polling = 100; /* ms */
|
||||
|
||||
/* init input device */
|
||||
snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)",
|
||||
dev->name);
|
||||
|
||||
usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
|
||||
strlcat(ir->phys, "/input0", sizeof(ir->phys));
|
||||
|
||||
rc->input_name = ir->name;
|
||||
rc->input_phys = ir->phys;
|
||||
rc->input_id.bustype = BUS_USB;
|
||||
rc->input_id.version = 1;
|
||||
rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
|
||||
rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
|
||||
rc->dev.parent = &dev->udev->dev;
|
||||
rc->map_name = dev->board.ir_codes;
|
||||
rc->driver_name = MODULE_NAME;
|
||||
|
||||
/* all done */
|
||||
err = rc_register_device(rc);
|
||||
if (err)
|
||||
goto err_out_stop;
|
||||
|
||||
em28xx_register_i2c_ir(dev);
|
||||
|
||||
#if defined(CONFIG_MODULES) && defined(MODULE)
|
||||
if (dev->board.has_ir_i2c)
|
||||
request_module("ir-kbd-i2c");
|
||||
#endif
|
||||
if (dev->board.has_snapshot_button)
|
||||
em28xx_register_snapshot_button(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_out_stop:
|
||||
dev->ir = NULL;
|
||||
err_out_free:
|
||||
rc_free_device(rc);
|
||||
kfree(ir);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int em28xx_ir_fini(struct em28xx *dev)
|
||||
{
|
||||
struct em28xx_IR *ir = dev->ir;
|
||||
|
||||
em28xx_deregister_snapshot_button(dev);
|
||||
|
||||
/* skip detach on non attached boards */
|
||||
if (!ir)
|
||||
return 0;
|
||||
|
||||
if (ir->rc)
|
||||
rc_unregister_device(ir->rc);
|
||||
|
||||
/* done */
|
||||
kfree(ir);
|
||||
dev->ir = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct em28xx_ops rc_ops = {
|
||||
.id = EM28XX_RC,
|
||||
.name = "Em28xx Input Extension",
|
||||
.init = em28xx_ir_init,
|
||||
.fini = em28xx_ir_fini,
|
||||
};
|
||||
|
||||
static int __init em28xx_rc_register(void)
|
||||
{
|
||||
return em28xx_register_extension(&rc_ops);
|
||||
}
|
||||
|
||||
static void __exit em28xx_rc_unregister(void)
|
||||
{
|
||||
em28xx_unregister_extension(&rc_ops);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
|
||||
MODULE_DESCRIPTION("Em28xx Input driver");
|
||||
|
||||
module_init(em28xx_rc_register);
|
||||
module_exit(em28xx_rc_unregister);
|
226
drivers/media/usb/em28xx/em28xx-reg.h
Normal file
226
drivers/media/usb/em28xx/em28xx-reg.h
Normal file
@@ -0,0 +1,226 @@
|
||||
#define EM_GPIO_0 (1 << 0)
|
||||
#define EM_GPIO_1 (1 << 1)
|
||||
#define EM_GPIO_2 (1 << 2)
|
||||
#define EM_GPIO_3 (1 << 3)
|
||||
#define EM_GPIO_4 (1 << 4)
|
||||
#define EM_GPIO_5 (1 << 5)
|
||||
#define EM_GPIO_6 (1 << 6)
|
||||
#define EM_GPIO_7 (1 << 7)
|
||||
|
||||
#define EM_GPO_0 (1 << 0)
|
||||
#define EM_GPO_1 (1 << 1)
|
||||
#define EM_GPO_2 (1 << 2)
|
||||
#define EM_GPO_3 (1 << 3)
|
||||
|
||||
/* em28xx endpoints */
|
||||
#define EM28XX_EP_ANALOG 0x82
|
||||
#define EM28XX_EP_AUDIO 0x83
|
||||
#define EM28XX_EP_DIGITAL 0x84
|
||||
|
||||
/* em2800 registers */
|
||||
#define EM2800_R08_AUDIOSRC 0x08
|
||||
|
||||
/* em28xx registers */
|
||||
|
||||
#define EM28XX_R00_CHIPCFG 0x00
|
||||
|
||||
/* em28xx Chip Configuration 0x00 */
|
||||
#define EM28XX_CHIPCFG_VENDOR_AUDIO 0x80
|
||||
#define EM28XX_CHIPCFG_I2S_VOLUME_CAPABLE 0x40
|
||||
#define EM28XX_CHIPCFG_I2S_5_SAMPRATES 0x30
|
||||
#define EM28XX_CHIPCFG_I2S_3_SAMPRATES 0x20
|
||||
#define EM28XX_CHIPCFG_AC97 0x10
|
||||
#define EM28XX_CHIPCFG_AUDIOMASK 0x30
|
||||
|
||||
#define EM28XX_R01_CHIPCFG2 0x01
|
||||
|
||||
/* em28xx Chip Configuration 2 0x01 */
|
||||
#define EM28XX_CHIPCFG2_TS_PRESENT 0x10
|
||||
#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_MASK 0x0c /* bits 3-2 */
|
||||
#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_1MF 0x00
|
||||
#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_2MF 0x04
|
||||
#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_4MF 0x08
|
||||
#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_8MF 0x0c
|
||||
#define EM28XX_CHIPCFG2_TS_PACKETSIZE_MASK 0x03 /* bits 0-1 */
|
||||
#define EM28XX_CHIPCFG2_TS_PACKETSIZE_188 0x00
|
||||
#define EM28XX_CHIPCFG2_TS_PACKETSIZE_376 0x01
|
||||
#define EM28XX_CHIPCFG2_TS_PACKETSIZE_564 0x02
|
||||
#define EM28XX_CHIPCFG2_TS_PACKETSIZE_752 0x03
|
||||
|
||||
|
||||
/* GPIO/GPO registers */
|
||||
#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */
|
||||
#define EM28XX_R08_GPIO 0x08 /* em2820 or upper */
|
||||
|
||||
#define EM28XX_R06_I2C_CLK 0x06
|
||||
|
||||
/* em28xx I2C Clock Register (0x06) */
|
||||
#define EM28XX_I2C_CLK_ACK_LAST_READ 0x80
|
||||
#define EM28XX_I2C_CLK_WAIT_ENABLE 0x40
|
||||
#define EM28XX_I2C_EEPROM_ON_BOARD 0x08
|
||||
#define EM28XX_I2C_EEPROM_KEY_VALID 0x04
|
||||
#define EM2874_I2C_SECONDARY_BUS_SELECT 0x04 /* em2874 has two i2c busses */
|
||||
#define EM28XX_I2C_FREQ_1_5_MHZ 0x03 /* bus frequency (bits [1-0]) */
|
||||
#define EM28XX_I2C_FREQ_25_KHZ 0x02
|
||||
#define EM28XX_I2C_FREQ_400_KHZ 0x01
|
||||
#define EM28XX_I2C_FREQ_100_KHZ 0x00
|
||||
|
||||
|
||||
#define EM28XX_R0A_CHIPID 0x0a
|
||||
#define EM28XX_R0C_USBSUSP 0x0c /* */
|
||||
|
||||
#define EM28XX_R0E_AUDIOSRC 0x0e
|
||||
#define EM28XX_R0F_XCLK 0x0f
|
||||
|
||||
/* em28xx XCLK Register (0x0f) */
|
||||
#define EM28XX_XCLK_AUDIO_UNMUTE 0x80 /* otherwise audio muted */
|
||||
#define EM28XX_XCLK_I2S_MSB_TIMING 0x40 /* otherwise standard timing */
|
||||
#define EM28XX_XCLK_IR_RC5_MODE 0x20 /* otherwise NEC mode */
|
||||
#define EM28XX_XCLK_IR_NEC_CHK_PARITY 0x10
|
||||
#define EM28XX_XCLK_FREQUENCY_30MHZ 0x00 /* Freq. select (bits [3-0]) */
|
||||
#define EM28XX_XCLK_FREQUENCY_15MHZ 0x01
|
||||
#define EM28XX_XCLK_FREQUENCY_10MHZ 0x02
|
||||
#define EM28XX_XCLK_FREQUENCY_7_5MHZ 0x03
|
||||
#define EM28XX_XCLK_FREQUENCY_6MHZ 0x04
|
||||
#define EM28XX_XCLK_FREQUENCY_5MHZ 0x05
|
||||
#define EM28XX_XCLK_FREQUENCY_4_3MHZ 0x06
|
||||
#define EM28XX_XCLK_FREQUENCY_12MHZ 0x07
|
||||
#define EM28XX_XCLK_FREQUENCY_20MHZ 0x08
|
||||
#define EM28XX_XCLK_FREQUENCY_20MHZ_2 0x09
|
||||
#define EM28XX_XCLK_FREQUENCY_48MHZ 0x0a
|
||||
#define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b
|
||||
|
||||
#define EM28XX_R10_VINMODE 0x10
|
||||
|
||||
#define EM28XX_R11_VINCTRL 0x11
|
||||
|
||||
/* em28xx Video Input Control Register 0x11 */
|
||||
#define EM28XX_VINCTRL_VBI_SLICED 0x80
|
||||
#define EM28XX_VINCTRL_VBI_RAW 0x40
|
||||
#define EM28XX_VINCTRL_VOUT_MODE_IN 0x20 /* HREF,VREF,VACT in output */
|
||||
#define EM28XX_VINCTRL_CCIR656_ENABLE 0x10
|
||||
#define EM28XX_VINCTRL_VBI_16BIT_RAW 0x08 /* otherwise 8-bit raw */
|
||||
#define EM28XX_VINCTRL_FID_ON_HREF 0x04
|
||||
#define EM28XX_VINCTRL_DUAL_EDGE_STROBE 0x02
|
||||
#define EM28XX_VINCTRL_INTERLACED 0x01
|
||||
|
||||
#define EM28XX_R12_VINENABLE 0x12 /* */
|
||||
|
||||
#define EM28XX_R14_GAMMA 0x14
|
||||
#define EM28XX_R15_RGAIN 0x15
|
||||
#define EM28XX_R16_GGAIN 0x16
|
||||
#define EM28XX_R17_BGAIN 0x17
|
||||
#define EM28XX_R18_ROFFSET 0x18
|
||||
#define EM28XX_R19_GOFFSET 0x19
|
||||
#define EM28XX_R1A_BOFFSET 0x1a
|
||||
|
||||
#define EM28XX_R1B_OFLOW 0x1b
|
||||
#define EM28XX_R1C_HSTART 0x1c
|
||||
#define EM28XX_R1D_VSTART 0x1d
|
||||
#define EM28XX_R1E_CWIDTH 0x1e
|
||||
#define EM28XX_R1F_CHEIGHT 0x1f
|
||||
|
||||
#define EM28XX_R20_YGAIN 0x20
|
||||
#define EM28XX_R21_YOFFSET 0x21
|
||||
#define EM28XX_R22_UVGAIN 0x22
|
||||
#define EM28XX_R23_UOFFSET 0x23
|
||||
#define EM28XX_R24_VOFFSET 0x24
|
||||
#define EM28XX_R25_SHARPNESS 0x25
|
||||
|
||||
#define EM28XX_R26_COMPR 0x26
|
||||
#define EM28XX_R27_OUTFMT 0x27
|
||||
|
||||
/* em28xx Output Format Register (0x27) */
|
||||
#define EM28XX_OUTFMT_RGB_8_RGRG 0x00
|
||||
#define EM28XX_OUTFMT_RGB_8_GRGR 0x01
|
||||
#define EM28XX_OUTFMT_RGB_8_GBGB 0x02
|
||||
#define EM28XX_OUTFMT_RGB_8_BGBG 0x03
|
||||
#define EM28XX_OUTFMT_RGB_16_656 0x04
|
||||
#define EM28XX_OUTFMT_RGB_8_BAYER 0x08 /* Pattern in Reg 0x10[1-0] */
|
||||
#define EM28XX_OUTFMT_YUV211 0x10
|
||||
#define EM28XX_OUTFMT_YUV422_Y0UY1V 0x14
|
||||
#define EM28XX_OUTFMT_YUV422_Y1UY0V 0x15
|
||||
#define EM28XX_OUTFMT_YUV411 0x18
|
||||
|
||||
|
||||
#define EM28XX_R28_XMIN 0x28
|
||||
#define EM28XX_R29_XMAX 0x29
|
||||
#define EM28XX_R2A_YMIN 0x2a
|
||||
#define EM28XX_R2B_YMAX 0x2b
|
||||
|
||||
#define EM28XX_R30_HSCALELOW 0x30
|
||||
#define EM28XX_R31_HSCALEHIGH 0x31
|
||||
#define EM28XX_R32_VSCALELOW 0x32
|
||||
#define EM28XX_R33_VSCALEHIGH 0x33
|
||||
#define EM28XX_R34_VBI_START_H 0x34
|
||||
#define EM28XX_R35_VBI_START_V 0x35
|
||||
#define EM28XX_R36_VBI_WIDTH 0x36
|
||||
#define EM28XX_R37_VBI_HEIGHT 0x37
|
||||
|
||||
#define EM28XX_R40_AC97LSB 0x40
|
||||
#define EM28XX_R41_AC97MSB 0x41
|
||||
#define EM28XX_R42_AC97ADDR 0x42
|
||||
#define EM28XX_R43_AC97BUSY 0x43
|
||||
|
||||
#define EM28XX_R45_IR 0x45
|
||||
/* 0x45 bit 7 - parity bit
|
||||
bits 6-0 - count
|
||||
0x46 IR brand
|
||||
0x47 IR data
|
||||
*/
|
||||
|
||||
/* em2874 registers */
|
||||
#define EM2874_R50_IR_CONFIG 0x50
|
||||
#define EM2874_R51_IR 0x51
|
||||
#define EM2874_R5F_TS_ENABLE 0x5f
|
||||
#define EM2874_R80_GPIO 0x80
|
||||
|
||||
/* em2874 IR config register (0x50) */
|
||||
#define EM2874_IR_NEC 0x00
|
||||
#define EM2874_IR_RC5 0x04
|
||||
#define EM2874_IR_RC6_MODE_0 0x08
|
||||
#define EM2874_IR_RC6_MODE_6A 0x0b
|
||||
|
||||
/* em2874 Transport Stream Enable Register (0x5f) */
|
||||
#define EM2874_TS1_CAPTURE_ENABLE (1 << 0)
|
||||
#define EM2874_TS1_FILTER_ENABLE (1 << 1)
|
||||
#define EM2874_TS1_NULL_DISCARD (1 << 2)
|
||||
#define EM2874_TS2_CAPTURE_ENABLE (1 << 4)
|
||||
#define EM2874_TS2_FILTER_ENABLE (1 << 5)
|
||||
#define EM2874_TS2_NULL_DISCARD (1 << 6)
|
||||
|
||||
/* register settings */
|
||||
#define EM2800_AUDIO_SRC_TUNER 0x0d
|
||||
#define EM2800_AUDIO_SRC_LINE 0x0c
|
||||
#define EM28XX_AUDIO_SRC_TUNER 0xc0
|
||||
#define EM28XX_AUDIO_SRC_LINE 0x80
|
||||
|
||||
/* FIXME: Need to be populated with the other chip ID's */
|
||||
enum em28xx_chip_id {
|
||||
CHIP_ID_EM2800 = 7,
|
||||
CHIP_ID_EM2710 = 17,
|
||||
CHIP_ID_EM2820 = 18, /* Also used by some em2710 */
|
||||
CHIP_ID_EM2840 = 20,
|
||||
CHIP_ID_EM2750 = 33,
|
||||
CHIP_ID_EM2860 = 34,
|
||||
CHIP_ID_EM2870 = 35,
|
||||
CHIP_ID_EM2883 = 36,
|
||||
CHIP_ID_EM2874 = 65,
|
||||
CHIP_ID_EM2884 = 68,
|
||||
CHIP_ID_EM28174 = 113,
|
||||
};
|
||||
|
||||
/*
|
||||
* Registers used by em202
|
||||
*/
|
||||
|
||||
/* EMP202 vendor registers */
|
||||
#define EM202_EXT_MODEM_CTRL 0x3e
|
||||
#define EM202_GPIO_CONF 0x4c
|
||||
#define EM202_GPIO_POLARITY 0x4e
|
||||
#define EM202_GPIO_STICKY 0x50
|
||||
#define EM202_GPIO_MASK 0x52
|
||||
#define EM202_GPIO_STATUS 0x54
|
||||
#define EM202_SPDIF_OUT_SEL 0x6a
|
||||
#define EM202_ANTIPOP 0x72
|
||||
#define EM202_EAPD_GPIO_ACCESS 0x74
|
145
drivers/media/usb/em28xx/em28xx-vbi.c
Normal file
145
drivers/media/usb/em28xx/em28xx-vbi.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
em28xx-vbi.c - VBI driver for em28xx
|
||||
|
||||
Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
|
||||
|
||||
This work was sponsored by EyeMagnet Limited.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include "em28xx.h"
|
||||
|
||||
static unsigned int vbibufs = 5;
|
||||
module_param(vbibufs, int, 0644);
|
||||
MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
|
||||
|
||||
static unsigned int vbi_debug;
|
||||
module_param(vbi_debug, int, 0644);
|
||||
MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
|
||||
|
||||
#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \
|
||||
printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg)
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void
|
||||
free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
|
||||
{
|
||||
struct em28xx_fh *fh = vq->priv_data;
|
||||
struct em28xx *dev = fh->dev;
|
||||
unsigned long flags = 0;
|
||||
if (in_interrupt())
|
||||
BUG();
|
||||
|
||||
/* We used to wait for the buffer to finish here, but this didn't work
|
||||
because, as we were keeping the state as VIDEOBUF_QUEUED,
|
||||
videobuf_queue_cancel marked it as finished for us.
|
||||
(Also, it could wedge forever if the hardware was misconfigured.)
|
||||
|
||||
This should be safe; by the time we get here, the buffer isn't
|
||||
queued anymore. If we ever start marking the buffers as
|
||||
VIDEOBUF_ACTIVE, it won't be, though.
|
||||
*/
|
||||
spin_lock_irqsave(&dev->slock, flags);
|
||||
if (dev->isoc_ctl.vbi_buf == buf)
|
||||
dev->isoc_ctl.vbi_buf = NULL;
|
||||
spin_unlock_irqrestore(&dev->slock, flags);
|
||||
|
||||
videobuf_vmalloc_free(&buf->vb);
|
||||
buf->vb.state = VIDEOBUF_NEEDS_INIT;
|
||||
}
|
||||
|
||||
static int
|
||||
vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
|
||||
{
|
||||
struct em28xx_fh *fh = q->priv_data;
|
||||
struct em28xx *dev = fh->dev;
|
||||
|
||||
*size = dev->vbi_width * dev->vbi_height * 2;
|
||||
|
||||
if (0 == *count)
|
||||
*count = vbibufs;
|
||||
if (*count < 2)
|
||||
*count = 2;
|
||||
if (*count > 32)
|
||||
*count = 32;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
|
||||
enum v4l2_field field)
|
||||
{
|
||||
struct em28xx_fh *fh = q->priv_data;
|
||||
struct em28xx *dev = fh->dev;
|
||||
struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
|
||||
int rc = 0;
|
||||
|
||||
buf->vb.size = dev->vbi_width * dev->vbi_height * 2;
|
||||
|
||||
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
|
||||
return -EINVAL;
|
||||
|
||||
buf->vb.width = dev->vbi_width;
|
||||
buf->vb.height = dev->vbi_height;
|
||||
buf->vb.field = field;
|
||||
|
||||
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
|
||||
rc = videobuf_iolock(q, &buf->vb, NULL);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
buf->vb.state = VIDEOBUF_PREPARED;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
free_buffer(q, buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
|
||||
{
|
||||
struct em28xx_buffer *buf = container_of(vb,
|
||||
struct em28xx_buffer,
|
||||
vb);
|
||||
struct em28xx_fh *fh = vq->priv_data;
|
||||
struct em28xx *dev = fh->dev;
|
||||
struct em28xx_dmaqueue *vbiq = &dev->vbiq;
|
||||
|
||||
buf->vb.state = VIDEOBUF_QUEUED;
|
||||
list_add_tail(&buf->vb.queue, &vbiq->active);
|
||||
}
|
||||
|
||||
static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
|
||||
{
|
||||
struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
|
||||
free_buffer(q, buf);
|
||||
}
|
||||
|
||||
struct videobuf_queue_ops em28xx_vbi_qops = {
|
||||
.buf_setup = vbi_setup,
|
||||
.buf_prepare = vbi_prepare,
|
||||
.buf_queue = vbi_queue,
|
||||
.buf_release = vbi_release,
|
||||
};
|
2624
drivers/media/usb/em28xx/em28xx-video.c
Normal file
2624
drivers/media/usb/em28xx/em28xx-video.c
Normal file
File diff suppressed because it is too large
Load Diff
809
drivers/media/usb/em28xx/em28xx.h
Normal file
809
drivers/media/usb/em28xx/em28xx.h
Normal file
@@ -0,0 +1,809 @@
|
||||
/*
|
||||
em28xx.h - driver for Empia EM2800/EM2820/2840 USB video capture devices
|
||||
|
||||
Copyright (C) 2005 Markus Rechberger <mrechberger@gmail.com>
|
||||
Ludovico Cavedon <cavedon@sssup.it>
|
||||
Mauro Carvalho Chehab <mchehab@infradead.org>
|
||||
|
||||
Based on the em2800 driver from Sascha Sommer <saschasommer@freenet.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _EM28XX_H
|
||||
#define _EM28XX_H
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <media/videobuf-vmalloc.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/ir-kbd-i2c.h>
|
||||
#include <media/rc-core.h>
|
||||
#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE)
|
||||
#include <media/videobuf-dvb.h>
|
||||
#endif
|
||||
#include "tuner-xc2028.h"
|
||||
#include "xc5000.h"
|
||||
#include "em28xx-reg.h"
|
||||
|
||||
/* Boards supported by driver */
|
||||
#define EM2800_BOARD_UNKNOWN 0
|
||||
#define EM2820_BOARD_UNKNOWN 1
|
||||
#define EM2820_BOARD_TERRATEC_CINERGY_250 2
|
||||
#define EM2820_BOARD_PINNACLE_USB_2 3
|
||||
#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4
|
||||
#define EM2820_BOARD_MSI_VOX_USB_2 5
|
||||
#define EM2800_BOARD_TERRATEC_CINERGY_200 6
|
||||
#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7
|
||||
#define EM2800_BOARD_KWORLD_USB2800 8
|
||||
#define EM2820_BOARD_PINNACLE_DVC_90 9
|
||||
#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10
|
||||
#define EM2880_BOARD_TERRATEC_HYBRID_XS 11
|
||||
#define EM2820_BOARD_KWORLD_PVRTV2800RF 12
|
||||
#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13
|
||||
#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14
|
||||
#define EM2800_BOARD_VGEAR_POCKETTV 15
|
||||
#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 16
|
||||
#define EM2880_BOARD_PINNACLE_PCTV_HD_PRO 17
|
||||
#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 18
|
||||
#define EM2860_BOARD_SAA711X_REFERENCE_DESIGN 19
|
||||
#define EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 20
|
||||
#define EM2800_BOARD_GRABBEEX_USB2800 21
|
||||
#define EM2750_BOARD_UNKNOWN 22
|
||||
#define EM2750_BOARD_DLCW_130 23
|
||||
#define EM2820_BOARD_DLINK_USB_TV 24
|
||||
#define EM2820_BOARD_GADMEI_UTV310 25
|
||||
#define EM2820_BOARD_HERCULES_SMART_TV_USB2 26
|
||||
#define EM2820_BOARD_PINNACLE_USB_2_FM1216ME 27
|
||||
#define EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE 28
|
||||
#define EM2860_BOARD_TVP5150_REFERENCE_DESIGN 29
|
||||
#define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30
|
||||
#define EM2821_BOARD_USBGEAR_VD204 31
|
||||
#define EM2821_BOARD_SUPERCOMP_USB_2 32
|
||||
#define EM2860_BOARD_ELGATO_VIDEO_CAPTURE 33
|
||||
#define EM2860_BOARD_TERRATEC_HYBRID_XS 34
|
||||
#define EM2860_BOARD_TYPHOON_DVD_MAKER 35
|
||||
#define EM2860_BOARD_NETGMBH_CAM 36
|
||||
#define EM2860_BOARD_GADMEI_UTV330 37
|
||||
#define EM2861_BOARD_YAKUMO_MOVIE_MIXER 38
|
||||
#define EM2861_BOARD_KWORLD_PVRTV_300U 39
|
||||
#define EM2861_BOARD_PLEXTOR_PX_TV100U 40
|
||||
#define EM2870_BOARD_KWORLD_350U 41
|
||||
#define EM2870_BOARD_KWORLD_355U 42
|
||||
#define EM2870_BOARD_TERRATEC_XS 43
|
||||
#define EM2870_BOARD_TERRATEC_XS_MT2060 44
|
||||
#define EM2870_BOARD_PINNACLE_PCTV_DVB 45
|
||||
#define EM2870_BOARD_COMPRO_VIDEOMATE 46
|
||||
#define EM2880_BOARD_KWORLD_DVB_305U 47
|
||||
#define EM2880_BOARD_KWORLD_DVB_310U 48
|
||||
#define EM2880_BOARD_MSI_DIGIVOX_AD 49
|
||||
#define EM2880_BOARD_MSI_DIGIVOX_AD_II 50
|
||||
#define EM2880_BOARD_TERRATEC_HYBRID_XS_FR 51
|
||||
#define EM2881_BOARD_DNT_DA2_HYBRID 52
|
||||
#define EM2881_BOARD_PINNACLE_HYBRID_PRO 53
|
||||
#define EM2882_BOARD_KWORLD_VS_DVBT 54
|
||||
#define EM2882_BOARD_TERRATEC_HYBRID_XS 55
|
||||
#define EM2882_BOARD_PINNACLE_HYBRID_PRO_330E 56
|
||||
#define EM2883_BOARD_KWORLD_HYBRID_330U 57
|
||||
#define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58
|
||||
#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60
|
||||
#define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61
|
||||
#define EM2820_BOARD_GADMEI_TVR200 62
|
||||
#define EM2860_BOARD_KAIOMY_TVNPC_U2 63
|
||||
#define EM2860_BOARD_EASYCAP 64
|
||||
#define EM2820_BOARD_IODATA_GVMVP_SZ 65
|
||||
#define EM2880_BOARD_EMPIRE_DUAL_TV 66
|
||||
#define EM2860_BOARD_TERRATEC_GRABBY 67
|
||||
#define EM2860_BOARD_TERRATEC_AV350 68
|
||||
#define EM2882_BOARD_KWORLD_ATSC_315U 69
|
||||
#define EM2882_BOARD_EVGA_INDTUBE 70
|
||||
#define EM2820_BOARD_SILVERCREST_WEBCAM 71
|
||||
#define EM2861_BOARD_GADMEI_UTV330PLUS 72
|
||||
#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73
|
||||
#define EM2800_BOARD_VC211A 74
|
||||
#define EM2882_BOARD_DIKOM_DK300 75
|
||||
#define EM2870_BOARD_KWORLD_A340 76
|
||||
#define EM2874_BOARD_LEADERSHIP_ISDBT 77
|
||||
#define EM28174_BOARD_PCTV_290E 78
|
||||
#define EM2884_BOARD_TERRATEC_H5 79
|
||||
#define EM28174_BOARD_PCTV_460E 80
|
||||
#define EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C 81
|
||||
#define EM2884_BOARD_CINERGY_HTC_STICK 82
|
||||
#define EM2860_BOARD_HT_VIDBOX_NW03 83
|
||||
#define EM2874_BOARD_MAXMEDIA_UB425_TC 84
|
||||
#define EM2884_BOARD_PCTV_510E 85
|
||||
#define EM2884_BOARD_PCTV_520E 86
|
||||
|
||||
/* Limits minimum and default number of buffers */
|
||||
#define EM28XX_MIN_BUF 4
|
||||
#define EM28XX_DEF_BUF 8
|
||||
|
||||
/*Limits the max URB message size */
|
||||
#define URB_MAX_CTRL_SIZE 80
|
||||
|
||||
/* Params for validated field */
|
||||
#define EM28XX_BOARD_NOT_VALIDATED 1
|
||||
#define EM28XX_BOARD_VALIDATED 0
|
||||
|
||||
/* Params for em28xx_cmd() audio */
|
||||
#define EM28XX_START_AUDIO 1
|
||||
#define EM28XX_STOP_AUDIO 0
|
||||
|
||||
/* maximum number of em28xx boards */
|
||||
#define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */
|
||||
|
||||
/* maximum number of frames that can be queued */
|
||||
#define EM28XX_NUM_FRAMES 5
|
||||
/* number of frames that get used for v4l2_read() */
|
||||
#define EM28XX_NUM_READ_FRAMES 2
|
||||
|
||||
/* number of buffers for isoc transfers */
|
||||
#define EM28XX_NUM_BUFS 5
|
||||
#define EM28XX_DVB_NUM_BUFS 5
|
||||
|
||||
/* number of packets for each buffer
|
||||
windows requests only 64 packets .. so we better do the same
|
||||
this is what I found out for all alternate numbers there!
|
||||
*/
|
||||
#define EM28XX_NUM_PACKETS 64
|
||||
#define EM28XX_DVB_MAX_PACKETS 64
|
||||
|
||||
#define EM28XX_INTERLACED_DEFAULT 1
|
||||
|
||||
/*
|
||||
#define (use usbview if you want to get the other alternate number infos)
|
||||
#define
|
||||
#define alternate number 2
|
||||
#define Endpoint Address: 82
|
||||
Direction: in
|
||||
Attribute: 1
|
||||
Type: Isoc
|
||||
Max Packet Size: 1448
|
||||
Interval: 125us
|
||||
|
||||
alternate number 7
|
||||
|
||||
Endpoint Address: 82
|
||||
Direction: in
|
||||
Attribute: 1
|
||||
Type: Isoc
|
||||
Max Packet Size: 3072
|
||||
Interval: 125us
|
||||
*/
|
||||
|
||||
/* time to wait when stopping the isoc transfer */
|
||||
#define EM28XX_URB_TIMEOUT \
|
||||
msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS)
|
||||
|
||||
/* time in msecs to wait for i2c writes to finish */
|
||||
#define EM2800_I2C_WRITE_TIMEOUT 20
|
||||
|
||||
enum em28xx_mode {
|
||||
EM28XX_SUSPEND,
|
||||
EM28XX_ANALOG_MODE,
|
||||
EM28XX_DIGITAL_MODE,
|
||||
};
|
||||
|
||||
|
||||
struct em28xx;
|
||||
|
||||
struct em28xx_usb_isoc_bufs {
|
||||
/* max packet size of isoc transaction */
|
||||
int max_pkt_size;
|
||||
|
||||
/* number of packets in each buffer */
|
||||
int num_packets;
|
||||
|
||||
/* number of allocated urbs */
|
||||
int num_bufs;
|
||||
|
||||
/* urb for isoc transfers */
|
||||
struct urb **urb;
|
||||
|
||||
/* transfer buffers for isoc transfer */
|
||||
char **transfer_buffer;
|
||||
};
|
||||
|
||||
struct em28xx_usb_isoc_ctl {
|
||||
/* isoc transfer buffers for analog mode */
|
||||
struct em28xx_usb_isoc_bufs analog_bufs;
|
||||
|
||||
/* isoc transfer buffers for digital mode */
|
||||
struct em28xx_usb_isoc_bufs digital_bufs;
|
||||
|
||||
/* Stores already requested buffers */
|
||||
struct em28xx_buffer *vid_buf;
|
||||
struct em28xx_buffer *vbi_buf;
|
||||
|
||||
/* isoc urb callback */
|
||||
int (*isoc_copy) (struct em28xx *dev, struct urb *urb);
|
||||
|
||||
};
|
||||
|
||||
/* Struct to enumberate video formats */
|
||||
struct em28xx_fmt {
|
||||
char *name;
|
||||
u32 fourcc; /* v4l2 format id */
|
||||
int depth;
|
||||
int reg;
|
||||
};
|
||||
|
||||
/* buffer for one video frame */
|
||||
struct em28xx_buffer {
|
||||
/* common v4l buffer stuff -- must be first */
|
||||
struct videobuf_buffer vb;
|
||||
|
||||
struct list_head frame;
|
||||
int top_field;
|
||||
};
|
||||
|
||||
struct em28xx_dmaqueue {
|
||||
struct list_head active;
|
||||
|
||||
wait_queue_head_t wq;
|
||||
|
||||
/* Counters to control buffer fill */
|
||||
int pos;
|
||||
};
|
||||
|
||||
/* inputs */
|
||||
|
||||
#define MAX_EM28XX_INPUT 4
|
||||
enum enum28xx_itype {
|
||||
EM28XX_VMUX_COMPOSITE1 = 1,
|
||||
EM28XX_VMUX_COMPOSITE2,
|
||||
EM28XX_VMUX_COMPOSITE3,
|
||||
EM28XX_VMUX_COMPOSITE4,
|
||||
EM28XX_VMUX_SVIDEO,
|
||||
EM28XX_VMUX_TELEVISION,
|
||||
EM28XX_VMUX_CABLE,
|
||||
EM28XX_VMUX_DVB,
|
||||
EM28XX_VMUX_DEBUG,
|
||||
EM28XX_RADIO,
|
||||
};
|
||||
|
||||
enum em28xx_ac97_mode {
|
||||
EM28XX_NO_AC97 = 0,
|
||||
EM28XX_AC97_EM202,
|
||||
EM28XX_AC97_SIGMATEL,
|
||||
EM28XX_AC97_OTHER,
|
||||
};
|
||||
|
||||
struct em28xx_audio_mode {
|
||||
enum em28xx_ac97_mode ac97;
|
||||
|
||||
u16 ac97_feat;
|
||||
u32 ac97_vendor_id;
|
||||
|
||||
unsigned int has_audio:1;
|
||||
|
||||
unsigned int i2s_3rates:1;
|
||||
unsigned int i2s_5rates:1;
|
||||
};
|
||||
|
||||
/* em28xx has two audio inputs: tuner and line in.
|
||||
However, on most devices, an auxiliary AC97 codec device is used.
|
||||
The AC97 device may have several different inputs and outputs,
|
||||
depending on their model. So, it is possible to use AC97 mixer to
|
||||
address more than two different entries.
|
||||
*/
|
||||
enum em28xx_amux {
|
||||
/* This is the only entry for em28xx tuner input */
|
||||
EM28XX_AMUX_VIDEO, /* em28xx tuner, AC97 mixer Video */
|
||||
|
||||
EM28XX_AMUX_LINE_IN, /* AC97 mixer Line In */
|
||||
|
||||
/* Some less-common mixer setups */
|
||||
EM28XX_AMUX_VIDEO2, /* em28xx Line in, AC97 mixer Video */
|
||||
EM28XX_AMUX_PHONE,
|
||||
EM28XX_AMUX_MIC,
|
||||
EM28XX_AMUX_CD,
|
||||
EM28XX_AMUX_AUX,
|
||||
EM28XX_AMUX_PCM_OUT,
|
||||
};
|
||||
|
||||
enum em28xx_aout {
|
||||
/* AC97 outputs */
|
||||
EM28XX_AOUT_MASTER = 1 << 0,
|
||||
EM28XX_AOUT_LINE = 1 << 1,
|
||||
EM28XX_AOUT_MONO = 1 << 2,
|
||||
EM28XX_AOUT_LFE = 1 << 3,
|
||||
EM28XX_AOUT_SURR = 1 << 4,
|
||||
|
||||
/* PCM IN Mixer - used by AC97_RECORD_SELECT register */
|
||||
EM28XX_AOUT_PCM_IN = 1 << 7,
|
||||
|
||||
/* Bits 10-8 are used to indicate the PCM IN record select */
|
||||
EM28XX_AOUT_PCM_MIC_PCM = 0 << 8,
|
||||
EM28XX_AOUT_PCM_CD = 1 << 8,
|
||||
EM28XX_AOUT_PCM_VIDEO = 2 << 8,
|
||||
EM28XX_AOUT_PCM_AUX = 3 << 8,
|
||||
EM28XX_AOUT_PCM_LINE = 4 << 8,
|
||||
EM28XX_AOUT_PCM_STEREO = 5 << 8,
|
||||
EM28XX_AOUT_PCM_MONO = 6 << 8,
|
||||
EM28XX_AOUT_PCM_PHONE = 7 << 8,
|
||||
};
|
||||
|
||||
static inline int ac97_return_record_select(int a_out)
|
||||
{
|
||||
return (a_out & 0x700) >> 8;
|
||||
}
|
||||
|
||||
struct em28xx_reg_seq {
|
||||
int reg;
|
||||
unsigned char val, mask;
|
||||
int sleep;
|
||||
};
|
||||
|
||||
struct em28xx_input {
|
||||
enum enum28xx_itype type;
|
||||
unsigned int vmux;
|
||||
enum em28xx_amux amux;
|
||||
enum em28xx_aout aout;
|
||||
struct em28xx_reg_seq *gpio;
|
||||
};
|
||||
|
||||
#define INPUT(nr) (&em28xx_boards[dev->model].input[nr])
|
||||
|
||||
enum em28xx_decoder {
|
||||
EM28XX_NODECODER = 0,
|
||||
EM28XX_TVP5150,
|
||||
EM28XX_SAA711X,
|
||||
};
|
||||
|
||||
enum em28xx_sensor {
|
||||
EM28XX_NOSENSOR = 0,
|
||||
EM28XX_MT9V011,
|
||||
EM28XX_MT9M001,
|
||||
EM28XX_MT9M111,
|
||||
};
|
||||
|
||||
enum em28xx_adecoder {
|
||||
EM28XX_NOADECODER = 0,
|
||||
EM28XX_TVAUDIO,
|
||||
};
|
||||
|
||||
struct em28xx_board {
|
||||
char *name;
|
||||
int vchannels;
|
||||
int tuner_type;
|
||||
int tuner_addr;
|
||||
|
||||
/* i2c flags */
|
||||
unsigned int tda9887_conf;
|
||||
|
||||
/* GPIO sequences */
|
||||
struct em28xx_reg_seq *dvb_gpio;
|
||||
struct em28xx_reg_seq *suspend_gpio;
|
||||
struct em28xx_reg_seq *tuner_gpio;
|
||||
struct em28xx_reg_seq *mute_gpio;
|
||||
|
||||
unsigned int is_em2800:1;
|
||||
unsigned int has_msp34xx:1;
|
||||
unsigned int mts_firmware:1;
|
||||
unsigned int max_range_640_480:1;
|
||||
unsigned int has_dvb:1;
|
||||
unsigned int has_snapshot_button:1;
|
||||
unsigned int is_webcam:1;
|
||||
unsigned int valid:1;
|
||||
unsigned int has_ir_i2c:1;
|
||||
|
||||
unsigned char xclk, i2c_speed;
|
||||
unsigned char radio_addr;
|
||||
unsigned short tvaudio_addr;
|
||||
|
||||
enum em28xx_decoder decoder;
|
||||
enum em28xx_adecoder adecoder;
|
||||
|
||||
struct em28xx_input input[MAX_EM28XX_INPUT];
|
||||
struct em28xx_input radio;
|
||||
char *ir_codes;
|
||||
};
|
||||
|
||||
struct em28xx_eeprom {
|
||||
u32 id; /* 0x9567eb1a */
|
||||
u16 vendor_ID;
|
||||
u16 product_ID;
|
||||
|
||||
u16 chip_conf;
|
||||
|
||||
u16 board_conf;
|
||||
|
||||
u16 string1, string2, string3;
|
||||
|
||||
u8 string_idx_table;
|
||||
};
|
||||
|
||||
/* device states */
|
||||
enum em28xx_dev_state {
|
||||
DEV_INITIALIZED = 0x01,
|
||||
DEV_DISCONNECTED = 0x02,
|
||||
DEV_MISCONFIGURED = 0x04,
|
||||
};
|
||||
|
||||
#define EM28XX_AUDIO_BUFS 5
|
||||
#define EM28XX_NUM_AUDIO_PACKETS 64
|
||||
#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */
|
||||
#define EM28XX_CAPTURE_STREAM_EN 1
|
||||
|
||||
/* em28xx extensions */
|
||||
#define EM28XX_AUDIO 0x10
|
||||
#define EM28XX_DVB 0x20
|
||||
#define EM28XX_RC 0x30
|
||||
|
||||
/* em28xx resource types (used for res_get/res_lock etc */
|
||||
#define EM28XX_RESOURCE_VIDEO 0x01
|
||||
#define EM28XX_RESOURCE_VBI 0x02
|
||||
|
||||
struct em28xx_audio {
|
||||
char name[50];
|
||||
char *transfer_buffer[EM28XX_AUDIO_BUFS];
|
||||
struct urb *urb[EM28XX_AUDIO_BUFS];
|
||||
struct usb_device *udev;
|
||||
unsigned int capture_transfer_done;
|
||||
struct snd_pcm_substream *capture_pcm_substream;
|
||||
|
||||
unsigned int hwptr_done_capture;
|
||||
struct snd_card *sndcard;
|
||||
|
||||
int users;
|
||||
spinlock_t slock;
|
||||
};
|
||||
|
||||
struct em28xx;
|
||||
|
||||
struct em28xx_fh {
|
||||
struct em28xx *dev;
|
||||
int radio;
|
||||
unsigned int resources;
|
||||
|
||||
struct videobuf_queue vb_vidq;
|
||||
struct videobuf_queue vb_vbiq;
|
||||
|
||||
enum v4l2_buf_type type;
|
||||
};
|
||||
|
||||
/* main device struct */
|
||||
struct em28xx {
|
||||
/* generic device properties */
|
||||
char name[30]; /* name (including minor) of the device */
|
||||
int model; /* index in the device_data struct */
|
||||
int devno; /* marks the number of this device */
|
||||
enum em28xx_chip_id chip_id;
|
||||
|
||||
int audio_ifnum;
|
||||
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct em28xx_board board;
|
||||
|
||||
/* Webcam specific fields */
|
||||
enum em28xx_sensor em28xx_sensor;
|
||||
int sensor_xres, sensor_yres;
|
||||
int sensor_xtal;
|
||||
|
||||
/* Allows progressive (e. g. non-interlaced) mode */
|
||||
int progressive;
|
||||
|
||||
/* Vinmode/Vinctl used at the driver */
|
||||
int vinmode, vinctl;
|
||||
|
||||
unsigned int has_audio_class:1;
|
||||
unsigned int has_alsa_audio:1;
|
||||
unsigned int is_audio_only:1;
|
||||
|
||||
/* Controls audio streaming */
|
||||
struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */
|
||||
atomic_t stream_started; /* stream should be running if true */
|
||||
|
||||
struct em28xx_fmt *format;
|
||||
|
||||
struct em28xx_IR *ir;
|
||||
|
||||
/* Some older em28xx chips needs a waiting time after writing */
|
||||
unsigned int wait_after_write;
|
||||
|
||||
struct list_head devlist;
|
||||
|
||||
u32 i2s_speed; /* I2S speed for audio digital stream */
|
||||
|
||||
struct em28xx_audio_mode audio_mode;
|
||||
|
||||
int tuner_type; /* type of the tuner */
|
||||
int tuner_addr; /* tuner address */
|
||||
int tda9887_conf;
|
||||
/* i2c i/o */
|
||||
struct i2c_adapter i2c_adap;
|
||||
struct i2c_client i2c_client;
|
||||
/* video for linux */
|
||||
int users; /* user count for exclusive use */
|
||||
struct video_device *vdev; /* video for linux device struct */
|
||||
v4l2_std_id norm; /* selected tv norm */
|
||||
int ctl_freq; /* selected frequency */
|
||||
unsigned int ctl_input; /* selected input */
|
||||
unsigned int ctl_ainput;/* selected audio input */
|
||||
unsigned int ctl_aoutput;/* selected audio output */
|
||||
int mute;
|
||||
int volume;
|
||||
/* frame properties */
|
||||
int width; /* current frame width */
|
||||
int height; /* current frame height */
|
||||
unsigned hscale; /* horizontal scale factor (see datasheet) */
|
||||
unsigned vscale; /* vertical scale factor (see datasheet) */
|
||||
int interlaced; /* 1=interlace fileds, 0=just top fileds */
|
||||
unsigned int video_bytesread; /* Number of bytes read */
|
||||
|
||||
unsigned long hash; /* eeprom hash - for boards with generic ID */
|
||||
unsigned long i2c_hash; /* i2c devicelist hash -
|
||||
for boards with generic ID */
|
||||
|
||||
struct em28xx_audio adev;
|
||||
|
||||
/* states */
|
||||
enum em28xx_dev_state state;
|
||||
|
||||
/* vbi related state tracking */
|
||||
int capture_type;
|
||||
int vbi_read;
|
||||
unsigned char cur_field;
|
||||
unsigned int vbi_width;
|
||||
unsigned int vbi_height; /* lines per field */
|
||||
|
||||
struct work_struct request_module_wk;
|
||||
|
||||
/* locks */
|
||||
struct mutex lock;
|
||||
struct mutex ctrl_urb_lock; /* protects urb_buf */
|
||||
/* spinlock_t queue_lock; */
|
||||
struct list_head inqueue, outqueue;
|
||||
struct video_device *vbi_dev;
|
||||
struct video_device *radio_dev;
|
||||
|
||||
/* resources in use */
|
||||
unsigned int resources;
|
||||
|
||||
unsigned char eedata[256];
|
||||
|
||||
/* Isoc control struct */
|
||||
struct em28xx_dmaqueue vidq;
|
||||
struct em28xx_dmaqueue vbiq;
|
||||
struct em28xx_usb_isoc_ctl isoc_ctl;
|
||||
spinlock_t slock;
|
||||
|
||||
/* usb transfer */
|
||||
struct usb_device *udev; /* the usb device */
|
||||
int alt; /* alternate */
|
||||
int max_pkt_size; /* max packet size of isoc transaction */
|
||||
int num_alt; /* Number of alternative settings */
|
||||
unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
|
||||
int dvb_alt; /* alternate for DVB */
|
||||
unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */
|
||||
char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
|
||||
|
||||
/* helper funcs that call usb_control_msg */
|
||||
int (*em28xx_write_regs) (struct em28xx *dev, u16 reg,
|
||||
char *buf, int len);
|
||||
int (*em28xx_read_reg) (struct em28xx *dev, u16 reg);
|
||||
int (*em28xx_read_reg_req_len) (struct em28xx *dev, u8 req, u16 reg,
|
||||
char *buf, int len);
|
||||
int (*em28xx_write_regs_req) (struct em28xx *dev, u8 req, u16 reg,
|
||||
char *buf, int len);
|
||||
int (*em28xx_read_reg_req) (struct em28xx *dev, u8 req, u16 reg);
|
||||
|
||||
enum em28xx_mode mode;
|
||||
|
||||
/* register numbers for GPO/GPIO registers */
|
||||
u16 reg_gpo_num, reg_gpio_num;
|
||||
|
||||
/* Caches GPO and GPIO registers */
|
||||
unsigned char reg_gpo, reg_gpio;
|
||||
|
||||
/* Snapshot button */
|
||||
char snapshot_button_path[30]; /* path of the input dev */
|
||||
struct input_dev *sbutton_input_dev;
|
||||
struct delayed_work sbutton_query_work;
|
||||
|
||||
struct em28xx_dvb *dvb;
|
||||
|
||||
/* I2C keyboard data */
|
||||
struct IR_i2c_init_data init_data;
|
||||
};
|
||||
|
||||
struct em28xx_ops {
|
||||
struct list_head next;
|
||||
char *name;
|
||||
int id;
|
||||
int (*init)(struct em28xx *);
|
||||
int (*fini)(struct em28xx *);
|
||||
};
|
||||
|
||||
/* Provided by em28xx-i2c.c */
|
||||
void em28xx_do_i2c_scan(struct em28xx *dev);
|
||||
int em28xx_i2c_register(struct em28xx *dev);
|
||||
int em28xx_i2c_unregister(struct em28xx *dev);
|
||||
|
||||
/* Provided by em28xx-core.c */
|
||||
|
||||
u32 em28xx_request_buffers(struct em28xx *dev, u32 count);
|
||||
void em28xx_queue_unusedframes(struct em28xx *dev);
|
||||
void em28xx_release_buffers(struct em28xx *dev);
|
||||
|
||||
int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
|
||||
char *buf, int len);
|
||||
int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg);
|
||||
int em28xx_read_reg(struct em28xx *dev, u16 reg);
|
||||
int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
|
||||
int len);
|
||||
int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len);
|
||||
int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val);
|
||||
int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
|
||||
u8 bitmask);
|
||||
|
||||
int em28xx_read_ac97(struct em28xx *dev, u8 reg);
|
||||
int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val);
|
||||
|
||||
int em28xx_audio_analog_set(struct em28xx *dev);
|
||||
int em28xx_audio_setup(struct em28xx *dev);
|
||||
|
||||
int em28xx_colorlevels_set_default(struct em28xx *dev);
|
||||
int em28xx_capture_start(struct em28xx *dev, int start);
|
||||
int em28xx_vbi_supported(struct em28xx *dev);
|
||||
int em28xx_set_outfmt(struct em28xx *dev);
|
||||
int em28xx_resolution_set(struct em28xx *dev);
|
||||
int em28xx_set_alternate(struct em28xx *dev);
|
||||
int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode,
|
||||
int max_packets, int num_bufs, int max_pkt_size);
|
||||
int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode,
|
||||
int max_packets, int num_bufs, int max_pkt_size,
|
||||
int (*isoc_copy) (struct em28xx *dev, struct urb *urb));
|
||||
void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode);
|
||||
void em28xx_stop_urbs(struct em28xx *dev);
|
||||
int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev);
|
||||
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
|
||||
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
|
||||
void em28xx_wake_i2c(struct em28xx *dev);
|
||||
int em28xx_register_extension(struct em28xx_ops *dev);
|
||||
void em28xx_unregister_extension(struct em28xx_ops *dev);
|
||||
void em28xx_init_extension(struct em28xx *dev);
|
||||
void em28xx_close_extension(struct em28xx *dev);
|
||||
|
||||
/* Provided by em28xx-video.c */
|
||||
int em28xx_register_analog_devices(struct em28xx *dev);
|
||||
void em28xx_release_analog_resources(struct em28xx *dev);
|
||||
|
||||
/* Provided by em28xx-cards.c */
|
||||
extern int em2800_variant_detect(struct usb_device *udev, int model);
|
||||
extern struct em28xx_board em28xx_boards[];
|
||||
extern struct usb_device_id em28xx_id_table[];
|
||||
extern const unsigned int em28xx_bcount;
|
||||
int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
|
||||
void em28xx_release_resources(struct em28xx *dev);
|
||||
|
||||
/* Provided by em28xx-vbi.c */
|
||||
extern struct videobuf_queue_ops em28xx_vbi_qops;
|
||||
|
||||
/* printk macros */
|
||||
|
||||
#define em28xx_err(fmt, arg...) do {\
|
||||
printk(KERN_ERR fmt , ##arg); } while (0)
|
||||
|
||||
#define em28xx_errdev(fmt, arg...) do {\
|
||||
printk(KERN_ERR "%s: "fmt,\
|
||||
dev->name , ##arg); } while (0)
|
||||
|
||||
#define em28xx_info(fmt, arg...) do {\
|
||||
printk(KERN_INFO "%s: "fmt,\
|
||||
dev->name , ##arg); } while (0)
|
||||
#define em28xx_warn(fmt, arg...) do {\
|
||||
printk(KERN_WARNING "%s: "fmt,\
|
||||
dev->name , ##arg); } while (0)
|
||||
|
||||
static inline int em28xx_compression_disable(struct em28xx *dev)
|
||||
{
|
||||
/* side effect of disabling scaler and mixer */
|
||||
return em28xx_write_reg(dev, EM28XX_R26_COMPR, 0x00);
|
||||
}
|
||||
|
||||
static inline int em28xx_contrast_get(struct em28xx *dev)
|
||||
{
|
||||
return em28xx_read_reg(dev, EM28XX_R20_YGAIN) & 0x1f;
|
||||
}
|
||||
|
||||
static inline int em28xx_brightness_get(struct em28xx *dev)
|
||||
{
|
||||
return em28xx_read_reg(dev, EM28XX_R21_YOFFSET);
|
||||
}
|
||||
|
||||
static inline int em28xx_saturation_get(struct em28xx *dev)
|
||||
{
|
||||
return em28xx_read_reg(dev, EM28XX_R22_UVGAIN) & 0x1f;
|
||||
}
|
||||
|
||||
static inline int em28xx_u_balance_get(struct em28xx *dev)
|
||||
{
|
||||
return em28xx_read_reg(dev, EM28XX_R23_UOFFSET);
|
||||
}
|
||||
|
||||
static inline int em28xx_v_balance_get(struct em28xx *dev)
|
||||
{
|
||||
return em28xx_read_reg(dev, EM28XX_R24_VOFFSET);
|
||||
}
|
||||
|
||||
static inline int em28xx_gamma_get(struct em28xx *dev)
|
||||
{
|
||||
return em28xx_read_reg(dev, EM28XX_R14_GAMMA) & 0x3f;
|
||||
}
|
||||
|
||||
static inline int em28xx_contrast_set(struct em28xx *dev, s32 val)
|
||||
{
|
||||
u8 tmp = (u8) val;
|
||||
return em28xx_write_regs(dev, EM28XX_R20_YGAIN, &tmp, 1);
|
||||
}
|
||||
|
||||
static inline int em28xx_brightness_set(struct em28xx *dev, s32 val)
|
||||
{
|
||||
u8 tmp = (u8) val;
|
||||
return em28xx_write_regs(dev, EM28XX_R21_YOFFSET, &tmp, 1);
|
||||
}
|
||||
|
||||
static inline int em28xx_saturation_set(struct em28xx *dev, s32 val)
|
||||
{
|
||||
u8 tmp = (u8) val;
|
||||
return em28xx_write_regs(dev, EM28XX_R22_UVGAIN, &tmp, 1);
|
||||
}
|
||||
|
||||
static inline int em28xx_u_balance_set(struct em28xx *dev, s32 val)
|
||||
{
|
||||
u8 tmp = (u8) val;
|
||||
return em28xx_write_regs(dev, EM28XX_R23_UOFFSET, &tmp, 1);
|
||||
}
|
||||
|
||||
static inline int em28xx_v_balance_set(struct em28xx *dev, s32 val)
|
||||
{
|
||||
u8 tmp = (u8) val;
|
||||
return em28xx_write_regs(dev, EM28XX_R24_VOFFSET, &tmp, 1);
|
||||
}
|
||||
|
||||
static inline int em28xx_gamma_set(struct em28xx *dev, s32 val)
|
||||
{
|
||||
u8 tmp = (u8) val;
|
||||
return em28xx_write_regs(dev, EM28XX_R14_GAMMA, &tmp, 1);
|
||||
}
|
||||
|
||||
/*FIXME: maxw should be dependent of alt mode */
|
||||
static inline unsigned int norm_maxw(struct em28xx *dev)
|
||||
{
|
||||
if (dev->board.is_webcam)
|
||||
return dev->sensor_xres;
|
||||
|
||||
if (dev->board.max_range_640_480)
|
||||
return 640;
|
||||
|
||||
return 720;
|
||||
}
|
||||
|
||||
static inline unsigned int norm_maxh(struct em28xx *dev)
|
||||
{
|
||||
if (dev->board.is_webcam)
|
||||
return dev->sensor_yres;
|
||||
|
||||
if (dev->board.max_range_640_480)
|
||||
return 480;
|
||||
|
||||
return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user