Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
c8639e86
Commit
c8639e86
authored
Apr 07, 2003
by
Alan Cox
Committed by
Linus Torvalds
Apr 07, 2003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] add drivers/media/common for mixed dvb/analog device stuff
(Martin Hunold)
parent
8826bb90
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
4355 additions
and
0 deletions
+4355
-0
drivers/media/common/Kconfig
drivers/media/common/Kconfig
+11
-0
drivers/media/common/Makefile
drivers/media/common/Makefile
+5
-0
drivers/media/common/saa7146_core.c
drivers/media/common/saa7146_core.c
+524
-0
drivers/media/common/saa7146_fops.c
drivers/media/common/saa7146_fops.c
+493
-0
drivers/media/common/saa7146_hlp.c
drivers/media/common/saa7146_hlp.c
+1043
-0
drivers/media/common/saa7146_i2c.c
drivers/media/common/saa7146_i2c.c
+424
-0
drivers/media/common/saa7146_vbi.c
drivers/media/common/saa7146_vbi.c
+468
-0
drivers/media/common/saa7146_video.c
drivers/media/common/saa7146_video.c
+1387
-0
No files found.
drivers/media/common/Kconfig
0 → 100644
View file @
c8639e86
config VIDEO_SAA7146
tristate
default y if DVB_AV7110=y || DVB_BUDGET=y || DVB_BUDGET_AV=y || VIDEO_MXB=y || VIDEO_DPC=y
default m if DVB_AV7110=m || DVB_BUDGET=m || DVB_BUDGET_AV=m || VIDEO_MXB=m || VIDEO_DPC=m
depends on VIDEO_DEV && PCI
config VIDEO_VIDEOBUF
tristate
default y if VIDEO_SAA7134=y || VIDEO_BT848=y || VIDEO_SAA7146=y
default m if VIDEO_SAA7134=m || VIDEO_BT848=m || VIDEO_SAA7146=m
depends on VIDEO_DEV
drivers/media/common/Makefile
0 → 100644
View file @
c8639e86
saa7146-objs
:=
saa7146_i2c.o saa7146_core.o
saa7146_vv-objs
:=
saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
obj-$(CONFIG_VIDEO_SAA7146)
+=
saa7146.o saa7146_vv.o
drivers/media/common/saa7146_core.c
0 → 100644
View file @
c8639e86
/*
saa7146.o - driver for generic saa7146-based hardware
Copyright (C) 1998-2003 Michael Hunold <michael@mihu.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 <media/saa7146.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
#define KBUILD_MODNAME saa7146
#endif
/* global variables */
struct
list_head
saa7146_devices
;
struct
semaphore
saa7146_devices_lock
;
static
int
initialized
=
0
;
int
saa7146_num
=
0
;
unsigned
int
saa7146_debug
=
0
;
MODULE_PARM
(
saa7146_debug
,
"i"
);
MODULE_PARM_DESC
(
saa7146_debug
,
"debug level (default: 0)"
);
#if 0
static void dump_registers(struct saa7146_dev* dev)
{
int i = 0;
INFO((" @ %li jiffies:\n",jiffies));
for(i = 0; i <= 0x148; i+=4) {
printk("0x%03x: 0x%08x\n",i,saa7146_read(dev,i));
}
}
#endif
/****************************************************************************
* general helper functions
****************************************************************************/
/* this is videobuf_vmalloc_to_sg() from video-buf.c */
static
struct
scatterlist
*
vmalloc_to_sg
(
unsigned
char
*
virt
,
int
nr_pages
)
{
struct
scatterlist
*
sglist
;
struct
page
*
pg
;
int
i
;
sglist
=
kmalloc
(
sizeof
(
struct
scatterlist
)
*
nr_pages
,
GFP_KERNEL
);
if
(
NULL
==
sglist
)
return
NULL
;
memset
(
sglist
,
0
,
sizeof
(
struct
scatterlist
)
*
nr_pages
);
for
(
i
=
0
;
i
<
nr_pages
;
i
++
,
virt
+=
PAGE_SIZE
)
{
pg
=
vmalloc_to_page
(
virt
);
if
(
NULL
==
pg
)
goto
err
;
if
(
PageHighMem
(
pg
))
BUG
();
sglist
[
i
].
page
=
pg
;
sglist
[
i
].
length
=
PAGE_SIZE
;
}
return
sglist
;
err:
kfree
(
sglist
);
return
NULL
;
}
/********************************************************************************/
/* common page table functions */
#define SAA7146_PGTABLE_SIZE 4096
char
*
saa7146_vmalloc_build_pgtable
(
struct
pci_dev
*
pci
,
long
length
,
struct
saa7146_pgtable
*
pt
)
{
struct
scatterlist
*
slist
=
NULL
;
int
pages
=
(
length
+
PAGE_SIZE
-
1
)
/
PAGE_SIZE
;
char
*
mem
=
vmalloc
(
length
);
int
slen
=
0
;
if
(
NULL
==
mem
)
{
return
NULL
;
}
if
(
!
(
slist
=
vmalloc_to_sg
(
mem
,
pages
)))
{
vfree
(
mem
);
return
NULL
;
}
if
(
saa7146_pgtable_alloc
(
pci
,
pt
))
{
kfree
(
slist
);
vfree
(
mem
);
return
NULL
;
}
slen
=
pci_map_sg
(
pci
,
slist
,
pages
,
PCI_DMA_FROMDEVICE
);
saa7146_pgtable_build_single
(
pci
,
pt
,
slist
,
slen
);
/* fixme: here's a memory leak: slist never gets freed by any other
function ...*/
return
mem
;
}
void
saa7146_pgtable_free
(
struct
pci_dev
*
pci
,
struct
saa7146_pgtable
*
pt
)
{
//fm DEB_EE(("pci:%p, pt:%p\n",pci,pt));
if
(
NULL
==
pt
->
cpu
)
return
;
pci_free_consistent
(
pci
,
pt
->
size
,
pt
->
cpu
,
pt
->
dma
);
pt
->
cpu
=
NULL
;
}
int
saa7146_pgtable_alloc
(
struct
pci_dev
*
pci
,
struct
saa7146_pgtable
*
pt
)
{
u32
*
cpu
;
dma_addr_t
dma_addr
;
//fm DEB_EE(("pci:%p, pt:%p\n",pci,pt));
cpu
=
pci_alloc_consistent
(
pci
,
SAA7146_PGTABLE_SIZE
,
&
dma_addr
);
if
(
NULL
==
cpu
)
{
//fm ERR(("pci_alloc_consistent() failed."));
return
-
ENOMEM
;
}
pt
->
size
=
SAA7146_PGTABLE_SIZE
;
pt
->
cpu
=
cpu
;
pt
->
dma
=
dma_addr
;
return
0
;
}
void
saa7146_pgtable_build_single
(
struct
pci_dev
*
pci
,
struct
saa7146_pgtable
*
pt
,
struct
scatterlist
*
list
,
int
length
)
{
u32
*
ptr
,
fill
;
int
i
,
p
;
//fm DEB_EE(("pci:%p, pt:%p, sl:%p, len:%d\n",pci,pt,list,length));
/* if we have a user buffer, the first page may not be
aligned to a page boundary. */
pt
->
offset
=
list
->
offset
;
ptr
=
pt
->
cpu
;
for
(
i
=
0
;
i
<
length
;
i
++
,
list
++
)
{
for
(
p
=
0
;
p
*
4096
<
list
->
length
;
p
++
,
ptr
++
)
{
*
ptr
=
sg_dma_address
(
list
)
-
list
->
offset
;
}
}
/* safety; fill the page table up with the last valid page */
fill
=
*
(
ptr
-
1
);
for
(;
i
<
1024
;
i
++
)
{
*
ptr
++
=
fill
;
}
/*
ptr = pt->cpu;
for(j=0;j<60;j++) {
printk("ptr1 %d: 0x%08x\n",j,ptr[j]);
}
*/
}
/********************************************************************************/
/* gpio functions */
void
saa7146_setgpio
(
struct
saa7146_dev
*
dev
,
int
port
,
u32
data
)
{
u32
val
=
0
;
val
=
saa7146_read
(
dev
,
GPIO_CTRL
);
val
&=~
(
0xff
<<
(
8
*
(
port
)));
val
|=
(
data
)
<<
(
8
*
(
port
));
saa7146_write
(
dev
,
GPIO_CTRL
,
val
);
}
/********************************************************************************/
/* interrupt handler */
static
void
interrupt_hw
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
struct
saa7146_dev
*
dev
=
(
struct
saa7146_dev
*
)
dev_id
;
u32
isr
=
0
;
/* read out the interrupt status register */
isr
=
saa7146_read
(
dev
,
ISR
);
/* is this our interrupt? */
if
(
0
==
isr
)
{
/* nope, some other device */
return
;
}
saa7146_write
(
dev
,
ISR
,
isr
);
// DEB_INT(("0x%08x\n",isr));
if
(
0
!=
(
dev
->
ext
))
{
if
(
0
!=
(
dev
->
ext
->
irq_mask
&
isr
))
{
if
(
0
!=
dev
->
ext
->
irq_func
)
{
dev
->
ext
->
irq_func
(
dev
,
&
isr
);
}
isr
&=
~
dev
->
ext
->
irq_mask
;
}
}
if
(
0
!=
(
isr
&
(
MASK_27
)))
{
DEB_INT
((
"irq: RPS0 (0x%08x).
\n
"
,
isr
));
if
(
0
!=
dev
->
vv_data
&&
0
!=
dev
->
vv_callback
)
{
dev
->
vv_callback
(
dev
,
isr
);
}
isr
&=
~
MASK_27
;
}
if
(
0
!=
(
isr
&
(
MASK_28
)))
{
if
(
0
!=
dev
->
vv_data
&&
0
!=
dev
->
vv_callback
)
{
dev
->
vv_callback
(
dev
,
isr
);
}
isr
&=
~
MASK_28
;
}
if
(
0
!=
(
isr
&
(
MASK_16
|
MASK_17
)))
{
u32
status
=
saa7146_read
(
dev
,
I2C_STATUS
);
if
(
(
0x3
==
(
status
&
0x3
))
||
(
0
==
(
status
&
0x1
))
)
{
IER_DISABLE
(
dev
,
MASK_16
|
MASK_17
);
/* only wake up if we expect something */
if
(
0
!=
dev
->
i2c_op
)
{
u32
psr
=
(
saa7146_read
(
dev
,
PSR
)
>>
16
)
&
0x2
;
u32
ssr
=
(
saa7146_read
(
dev
,
SSR
)
>>
17
)
&
0x1f
;
DEB_I2C
((
"irq: i2c, status: 0x%08x, psr:0x%02x, ssr:0x%02x).
\n
"
,
status
,
psr
,
ssr
));
dev
->
i2c_op
=
0
;
wake_up
(
&
dev
->
i2c_wq
);
}
else
{
DEB_I2C
((
"unexpected irq: i2c, status: 0x%08x, isr %#x
\n
"
,
status
,
isr
));
}
}
else
{
DEB_I2C
((
"unhandled irq: i2c, status: 0x%08x, isr %#x
\n
"
,
status
,
isr
));
}
isr
&=
~
(
MASK_16
|
MASK_17
);
}
if
(
0
!=
isr
)
{
ERR
((
"warning: interrupt enabled, but not handled properly.(0x%08x)
\n
"
,
isr
));
ERR
((
"disabling interrupt source(s)!
\n
"
));
IER_DISABLE
(
dev
,
isr
);
}
}
/*********************************************************************************/
/* configuration-functions */
static
int
saa7146_init_one
(
struct
pci_dev
*
pci
,
const
struct
pci_device_id
*
ent
)
{
unsigned
long
adr
=
0
,
len
=
0
;
struct
saa7146_dev
*
dev
=
kmalloc
(
sizeof
(
struct
saa7146_dev
),
GFP_KERNEL
);
struct
saa7146_pci_extension_data
*
pci_ext
=
(
struct
saa7146_pci_extension_data
*
)
ent
->
driver_data
;
struct
saa7146_extension
*
ext
=
pci_ext
->
ext
;
int
err
=
0
;
if
(
!
(
dev
=
kmalloc
(
sizeof
(
struct
saa7146_dev
),
GFP_KERNEL
)))
{
ERR
((
"out of memory.
\n
"
));
return
-
ENOMEM
;
}
/* clear out mem for sure */
memset
(
dev
,
0x0
,
sizeof
(
struct
saa7146_dev
));
DEB_EE
((
"pci:%p
\n
"
,
pci
));
if
(
pci_enable_device
(
pci
))
{
ERR
((
"pci_enable_device() failed.
\n
"
));
err
=
-
EIO
;
goto
pci_error
;
}
/* enable bus-mastering */
pci_set_master
(
pci
);
dev
->
pci
=
pci
;
/* get chip-revision; this is needed to enable bug-fixes */
if
(
0
>
pci_read_config_dword
(
dev
->
pci
,
PCI_CLASS_REVISION
,
&
dev
->
revision
))
{
ERR
((
"pci_read_config_dword() failed.
\n
"
));
err
=
-
ENODEV
;
goto
pci_error
;
}
dev
->
revision
&=
0xf
;
/* remap the memory from virtual to physical adress */
adr
=
pci_resource_start
(
pci
,
0
);
len
=
pci_resource_len
(
pci
,
0
);
if
(
!
request_mem_region
(
pci_resource_start
(
pci
,
0
),
pci_resource_len
(
pci
,
0
),
"saa7146"
))
{
ERR
((
"request_mem_region() failed.
\n
"
));
err
=
-
ENODEV
;
goto
pci_error
;
}
if
(
!
(
dev
->
mem
=
ioremap
(
adr
,
len
)))
{
ERR
((
"ioremap() failed.
\n
"
));
err
=
-
ENODEV
;
goto
ioremap_error
;
}
/* we don't do a master reset here anymore, it screws up
some boards that don't have an i2c-eeprom for configuration
values */
/*
saa7146_write(dev, MC1, MASK_31);
*/
/* disable alle irqs */
saa7146_write
(
dev
,
IER
,
0
);
/* shut down all dma transfers */
saa7146_write
(
dev
,
MC1
,
0x00ff0000
);
/* clear out any rps-signals pending */
saa7146_write
(
dev
,
MC2
,
0xf8000000
);
/* request an interrupt for the saa7146 */
if
(
request_irq
(
dev
->
pci
->
irq
,
interrupt_hw
,
SA_SHIRQ
|
SA_INTERRUPT
,
dev
->
name
,
dev
))
{
ERR
((
"request_irq() failed.
\n
"
));
err
=
-
ENODEV
;
goto
irq_error
;
}
/* get memory for various stuff */
dev
->
rps0
=
(
u32
*
)
kmalloc
(
SAA7146_RPS_MEM
,
GFP_KERNEL
);
if
(
NULL
==
dev
->
rps0
)
{
err
=
-
ENOMEM
;
goto
kmalloc_error_1
;
}
memset
(
dev
->
rps0
,
0x0
,
SAA7146_RPS_MEM
);
dev
->
rps1
=
(
u32
*
)
kmalloc
(
SAA7146_RPS_MEM
,
GFP_KERNEL
);
if
(
NULL
==
dev
->
rps1
)
{
err
=
-
ENOMEM
;
goto
kmalloc_error_2
;
}
memset
(
dev
->
rps1
,
0x0
,
SAA7146_RPS_MEM
);
dev
->
i2c_mem
=
(
u32
*
)
kmalloc
(
SAA7146_I2C_MEM
,
GFP_KERNEL
);
if
(
NULL
==
dev
->
i2c_mem
)
{
err
=
-
ENOMEM
;
goto
kmalloc_error_3
;
}
memset
(
dev
->
i2c_mem
,
0x00
,
SAA7146_I2C_MEM
);
/* the rest + print status message */
/* create a nice device name */
sprintf
(
&
dev
->
name
[
0
],
"saa7146 (%d)"
,
saa7146_num
);
INFO
((
"found saa7146 @ mem 0x%08x (revision %d, irq %d) (0x%04x,0x%04x).
\n
"
,
(
unsigned
int
)
dev
->
mem
,
dev
->
revision
,
dev
->
pci
->
irq
,
dev
->
pci
->
subsystem_vendor
,
dev
->
pci
->
subsystem_device
));
dev
->
ext
=
ext
;
pci_set_drvdata
(
pci
,
dev
);
init_MUTEX
(
&
dev
->
lock
);
dev
->
int_slock
=
SPIN_LOCK_UNLOCKED
;
dev
->
slock
=
SPIN_LOCK_UNLOCKED
;
init_MUTEX
(
&
dev
->
i2c_lock
);
dev
->
module
=
THIS_MODULE
;
init_waitqueue_head
(
&
dev
->
i2c_wq
);
if
(
0
!=
ext
->
probe
)
{
if
(
0
!=
ext
->
probe
(
dev
)
)
{
DEB_D
((
"ext->probe() failed for %p. skipping device.
\n
"
,
dev
));
err
=
-
ENODEV
;
goto
probe_error
;
}
}
if
(
0
!=
ext
->
attach
(
dev
,
pci_ext
)
)
{
DEB_D
((
"ext->attach() failed for %p. skipping device.
\n
"
,
dev
));
err
=
-
ENODEV
;
goto
attach_error
;
}
INIT_LIST_HEAD
(
&
dev
->
item
);
list_add_tail
(
&
dev
->
item
,
&
saa7146_devices
);
saa7146_num
++
;
/* set some default values */
saa7146_write
(
dev
,
BCS_CTRL
,
0x80400040
);
err
=
0
;
goto
out
;
attach_error:
probe_error:
pci_set_drvdata
(
pci
,
NULL
);
kfree
(
dev
->
i2c_mem
);
kmalloc_error_3:
kfree
(
dev
->
rps1
);
kmalloc_error_2:
kfree
(
dev
->
rps0
);
kmalloc_error_1:
free_irq
(
dev
->
pci
->
irq
,
(
void
*
)
dev
);
irq_error:
iounmap
(
dev
->
mem
);
ioremap_error:
release_mem_region
(
adr
,
len
);
pci_error:
kfree
(
dev
);
out:
return
err
;
}
static
void
saa7146_remove_one
(
struct
pci_dev
*
pdev
)
{
struct
saa7146_dev
*
dev
=
(
struct
saa7146_dev
*
)
pci_get_drvdata
(
pdev
);
DEB_EE
((
"dev:%p
\n
"
,
dev
));
dev
->
ext
->
detach
(
dev
);
/* shut down all video dma transfers */
saa7146_write
(
dev
,
MC1
,
0x00ff0000
);
/* disable all irqs, release irq-routine */
saa7146_write
(
dev
,
IER
,
0
);
free_irq
(
dev
->
pci
->
irq
,
(
void
*
)
dev
);
/* free kernel memory */
kfree
(
dev
->
rps0
);
kfree
(
dev
->
rps1
);
kfree
(
dev
->
i2c_mem
);
iounmap
(
dev
->
mem
);
release_mem_region
(
pci_resource_start
(
dev
->
pci
,
0
),
pci_resource_len
(
dev
->
pci
,
0
));
list_del
(
&
dev
->
item
);
kfree
(
dev
);
saa7146_num
--
;
}
/*********************************************************************************/
/* extension handling functions */
int
saa7146_register_extension
(
struct
saa7146_extension
*
ext
)
{
DEB_EE
((
"ext:%p
\n
"
,
ext
));
if
(
0
==
initialized
)
{
INIT_LIST_HEAD
(
&
saa7146_devices
);
init_MUTEX
(
&
saa7146_devices_lock
);
initialized
=
1
;
}
ext
->
driver
.
name
=
ext
->
name
;
ext
->
driver
.
id_table
=
ext
->
pci_tbl
;
ext
->
driver
.
probe
=
saa7146_init_one
;
ext
->
driver
.
remove
=
saa7146_remove_one
;
printk
(
"saa7146: register extension '%s'.
\n
"
,
ext
->
name
);
return
pci_module_init
(
&
ext
->
driver
);
}
int
saa7146_unregister_extension
(
struct
saa7146_extension
*
ext
)
{
DEB_EE
((
"ext:%p
\n
"
,
ext
));
printk
(
"saa7146: unregister extension '%s'.
\n
"
,
ext
->
name
);
pci_unregister_driver
(
&
ext
->
driver
);
return
0
;
}
static
int
__init
saa7146_init_module
(
void
)
{
if
(
0
==
initialized
)
{
INIT_LIST_HEAD
(
&
saa7146_devices
);
init_MUTEX
(
&
saa7146_devices_lock
);
initialized
=
1
;
}
return
0
;
}
static
void
__exit
saa7146_cleanup_module
(
void
)
{
}
module_init
(
saa7146_init_module
);
module_exit
(
saa7146_cleanup_module
);
EXPORT_SYMBOL_GPL
(
saa7146_register_extension
);
EXPORT_SYMBOL_GPL
(
saa7146_unregister_extension
);
/* misc functions used by extension modules */
EXPORT_SYMBOL_GPL
(
saa7146_pgtable_alloc
);
EXPORT_SYMBOL_GPL
(
saa7146_pgtable_free
);
EXPORT_SYMBOL_GPL
(
saa7146_pgtable_build_single
);
EXPORT_SYMBOL_GPL
(
saa7146_vmalloc_build_pgtable
);
EXPORT_SYMBOL_GPL
(
saa7146_setgpio
);
EXPORT_SYMBOL_GPL
(
saa7146_i2c_transfer
);
EXPORT_SYMBOL_GPL
(
saa7146_i2c_adapter_prepare
);
EXPORT_SYMBOL_GPL
(
saa7146_debug
);
EXPORT_SYMBOL_GPL
(
saa7146_devices
);
EXPORT_SYMBOL_GPL
(
saa7146_devices_lock
);
MODULE_AUTHOR
(
"Michael Hunold <michael@mihu.de>"
);
MODULE_DESCRIPTION
(
"driver for generic saa7146-based hardware"
);
MODULE_LICENSE
(
"GPL"
);
drivers/media/common/saa7146_fops.c
0 → 100644
View file @
c8639e86
#include <media/saa7146_vv.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
#define KBUILD_MODNAME saa7146
#endif
#define BOARD_CAN_DO_VBI(dev) (dev->revision != 0 && dev->vv_data->vbi_minor != -1)
/********************************************************************************/
/* common dma functions */
void
saa7146_dma_free
(
struct
saa7146_dev
*
dev
,
struct
saa7146_buf
*
buf
)
{
DEB_EE
((
"dev:%p, buf:%p
\n
"
,
dev
,
buf
));
if
(
in_interrupt
())
BUG
();
videobuf_waiton
(
&
buf
->
vb
,
0
,
0
);
videobuf_dma_pci_unmap
(
dev
->
pci
,
&
buf
->
vb
.
dma
);
videobuf_dma_free
(
&
buf
->
vb
.
dma
);
buf
->
vb
.
state
=
STATE_NEEDS_INIT
;
}
/********************************************************************************/
/* common buffer functions */
int
saa7146_buffer_queue
(
struct
saa7146_dev
*
dev
,
struct
saa7146_dmaqueue
*
q
,
struct
saa7146_buf
*
buf
)
{
#if DEBUG_SPINLOCKS
BUG_ON
(
!
spin_is_locked
(
&
dev
->
slock
));
#endif
DEB_EE
((
"dev:%p, dmaq:%p, buf:%p
\n
"
,
dev
,
q
,
buf
));
if
(
NULL
==
q
)
{
ERR
((
"internal error: fatal NULL pointer for q.
\n
"
));
return
0
;
}
if
(
NULL
==
q
->
curr
)
{
q
->
curr
=
buf
;
DEB_D
((
"immediately activating buffer %p
\n
"
,
buf
));
buf
->
activate
(
dev
,
buf
,
NULL
);
}
else
{
list_add_tail
(
&
buf
->
vb
.
queue
,
&
q
->
queue
);
buf
->
vb
.
state
=
STATE_QUEUED
;
DEB_D
((
"adding buffer %p to queue. (active buffer present)
\n
"
,
buf
));
}
return
0
;
}
void
saa7146_buffer_finish
(
struct
saa7146_dev
*
dev
,
struct
saa7146_dmaqueue
*
q
,
int
state
)
{
#if DEBUG_SPINLOCKS
BUG_ON
(
!
spin_is_locked
(
&
dev
->
slock
));
#endif
if
(
NULL
==
q
->
curr
)
{
ERR
((
"internal error: fatal NULL pointer for q->curr.
\n
"
));
return
;
}
DEB_EE
((
"dev:%p, dmaq:%p, state:%d
\n
"
,
dev
,
q
,
state
));
/* finish current buffer */
q
->
curr
->
vb
.
state
=
state
;
do_gettimeofday
(
&
q
->
curr
->
vb
.
ts
);
wake_up
(
&
q
->
curr
->
vb
.
done
);
q
->
curr
=
NULL
;
}
void
saa7146_buffer_next
(
struct
saa7146_dev
*
dev
,
struct
saa7146_dmaqueue
*
q
,
int
vbi
)
{
struct
saa7146_buf
*
buf
,
*
next
=
NULL
;
if
(
NULL
==
q
)
{
ERR
((
"internal error: fatal NULL pointer for q.
\n
"
));
return
;
}
DEB_EE
((
"dev:%p, dmaq:%p, vbi:%d
\n
"
,
dev
,
q
,
vbi
));
#if DEBUG_SPINLOCKS
BUG_ON
(
!
spin_is_locked
(
&
dev
->
slock
));
#endif
if
(
!
list_empty
(
&
q
->
queue
))
{
/* activate next one from queue */
buf
=
list_entry
(
q
->
queue
.
next
,
struct
saa7146_buf
,
vb
.
queue
);
list_del
(
&
buf
->
vb
.
queue
);
if
(
!
list_empty
(
&
q
->
queue
))
next
=
list_entry
(
q
->
queue
.
next
,
struct
saa7146_buf
,
vb
.
queue
);
q
->
curr
=
buf
;
DEB_D
((
"next buffer: buf:%p, prev:%p, next:%p
\n
"
,
buf
,
q
->
queue
.
prev
,
q
->
queue
.
next
));
buf
->
activate
(
dev
,
buf
,
next
);
}
else
{
DEB_D
((
"no next buffer. stopping.
\n
"
));
if
(
0
!=
vbi
)
{
/* turn off video-dma3 */
saa7146_write
(
dev
,
MC1
,
MASK_20
);
}
else
{
/* nothing to do -- just prevent next video-dma1 transfer
by lowering the protection address */
// fixme: fix this for vflip != 0
saa7146_write
(
dev
,
PROT_ADDR1
,
0
);
/* write the address of the rps-program */
saa7146_write
(
dev
,
RPS_ADDR0
,
virt_to_bus
(
&
dev
->
rps0
[
0
]));
/* turn on rps */
saa7146_write
(
dev
,
MC1
,
(
MASK_12
|
MASK_28
));
}
del_timer
(
&
q
->
timeout
);
}
}
void
saa7146_buffer_timeout
(
unsigned
long
data
)
{
struct
saa7146_dmaqueue
*
q
=
(
struct
saa7146_dmaqueue
*
)
data
;
struct
saa7146_dev
*
dev
=
q
->
dev
;
unsigned
long
flags
;
DEB_EE
((
"dev:%p, dmaq:%p
\n
"
,
dev
,
q
));
spin_lock_irqsave
(
&
dev
->
slock
,
flags
);
if
(
q
->
curr
)
{
DEB_D
((
"timeout on %p
\n
"
,
q
->
curr
));
saa7146_buffer_finish
(
dev
,
q
,
STATE_ERROR
);
}
/* we don't restart the transfer here like other drivers do. when
a streaming capture is disabled, the timeout function will be
called for the current buffer. if we activate the next buffer now,
we mess up our capture logic. if a timeout occurs on another buffer,
then something is seriously broken before, so no need to buffer the
next capture IMHO... */
/*
saa7146_buffer_next(dev,q);
*/
spin_unlock_irqrestore
(
&
dev
->
slock
,
flags
);
}
/********************************************************************************/
/* file operations */
static
int
fops_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
unsigned
int
minor
=
minor
(
inode
->
i_rdev
);
struct
saa7146_dev
*
h
=
NULL
,
*
dev
=
NULL
;
struct
list_head
*
list
;
struct
saa7146_fh
*
fh
=
NULL
;
int
result
=
0
;
enum
v4l2_buf_type
type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE
;
DEB_EE
((
"inode:%p, file:%p, minor:%d
\n
"
,
inode
,
file
,
minor
));
if
(
down_interruptible
(
&
saa7146_devices_lock
))
return
-
ERESTARTSYS
;
list_for_each
(
list
,
&
saa7146_devices
)
{
h
=
list_entry
(
list
,
struct
saa7146_dev
,
item
);
if
(
NULL
==
h
->
vv_data
)
{
DEB_D
((
"device %p has not registered video devices.
\n
"
,
h
));
continue
;
}
DEB_D
((
"trying: %p @ major %d,%d
\n
"
,
h
,
h
->
vv_data
->
video_minor
,
h
->
vv_data
->
vbi_minor
));
if
(
h
->
vv_data
->
video_minor
==
minor
)
{
dev
=
h
;
}
if
(
h
->
vv_data
->
vbi_minor
==
minor
)
{
type
=
V4L2_BUF_TYPE_VBI_CAPTURE
;
dev
=
h
;
}
}
if
(
NULL
==
dev
)
{
DEB_S
((
"no such video device.
\n
"
));
result
=
-
ENODEV
;
goto
out
;
}
DEB_D
((
"using: %p
\n
"
,
dev
));
/* check if an extension is registered */
if
(
NULL
==
dev
->
ext
)
{
DEB_S
((
"no extension registered for this device.
\n
"
));
result
=
-
ENODEV
;
goto
out
;
}
/* allocate per open data */
fh
=
kmalloc
(
sizeof
(
*
fh
),
GFP_KERNEL
);
if
(
NULL
==
fh
)
{
DEB_S
((
"cannot allocate memory for per open data.
\n
"
));
result
=
-
ENOMEM
;
goto
out
;
}
memset
(
fh
,
0
,
sizeof
(
*
fh
));
// FIXME: do we need to increase *our* usage count?
if
(
0
==
try_module_get
(
dev
->
ext
->
module
))
{
result
=
-
EINVAL
;
goto
out
;
}
file
->
private_data
=
fh
;
fh
->
dev
=
dev
;
fh
->
type
=
type
;
saa7146_video_uops
.
open
(
dev
,
fh
);
if
(
0
!=
BOARD_CAN_DO_VBI
(
dev
)
)
{
saa7146_vbi_uops
.
open
(
dev
,
fh
);
}
result
=
0
;
out:
if
(
fh
!=
0
&&
result
!=
0
)
{
kfree
(
fh
);
}
up
(
&
saa7146_devices_lock
);
return
result
;
}
static
int
fops_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
saa7146_dev
*
dev
=
fh
->
dev
;
DEB_EE
((
"inode:%p, file:%p
\n
"
,
inode
,
file
));
if
(
down_interruptible
(
&
saa7146_devices_lock
))
return
-
ERESTARTSYS
;
saa7146_video_uops
.
release
(
dev
,
fh
,
file
);
if
(
0
!=
BOARD_CAN_DO_VBI
(
dev
)
)
{
saa7146_vbi_uops
.
release
(
dev
,
fh
,
file
);
}
module_put
(
dev
->
ext
->
module
);
file
->
private_data
=
NULL
;
kfree
(
fh
);
up
(
&
saa7146_devices_lock
);
return
0
;
}
int
saa7146_video_do_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
void
*
arg
);
static
int
fops_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
/*
DEB_EE(("inode:%p, file:%p, cmd:%d, arg:%li\n",inode, file, cmd, arg));
*/
return
video_usercopy
(
inode
,
file
,
cmd
,
arg
,
saa7146_video_do_ioctl
);
}
static
int
fops_mmap
(
struct
file
*
file
,
struct
vm_area_struct
*
vma
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
videobuf_queue
*
q
;
switch
(
fh
->
type
)
{
case
V4L2_BUF_TYPE_VIDEO_CAPTURE
:
{
DEB_EE
((
"V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p
\n
"
,
file
,
vma
));
q
=
&
fh
->
video_q
;
break
;
}
case
V4L2_BUF_TYPE_VBI_CAPTURE
:
{
DEB_EE
((
"V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p
\n
"
,
file
,
vma
));
q
=
&
fh
->
vbi_q
;
break
;
}
default:
BUG
();
return
0
;
}
return
videobuf_mmap_mapper
(
vma
,
q
);
}
static
unsigned
int
fops_poll
(
struct
file
*
file
,
struct
poll_table_struct
*
wait
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
videobuf_buffer
*
buf
=
NULL
;
struct
videobuf_queue
*
q
;
DEB_EE
((
"file:%p, poll:%p
\n
"
,
file
,
wait
));
if
(
V4L2_BUF_TYPE_VBI_CAPTURE
==
fh
->
type
)
{
if
(
0
==
fh
->
vbi_q
.
streaming
)
return
videobuf_poll_stream
(
file
,
&
fh
->
vbi_q
,
wait
);
q
=
&
fh
->
vbi_q
;
}
else
{
q
=
&
fh
->
video_q
;
}
if
(
!
list_empty
(
&
q
->
stream
))
buf
=
list_entry
(
q
->
stream
.
next
,
struct
videobuf_buffer
,
stream
);
if
(
!
buf
)
{
return
POLLERR
;
}
poll_wait
(
file
,
&
buf
->
done
,
wait
);
if
(
buf
->
state
==
STATE_DONE
||
buf
->
state
==
STATE_ERROR
)
{
return
POLLIN
|
POLLRDNORM
;
}
return
0
;
}
static
ssize_t
fops_read
(
struct
file
*
file
,
char
*
data
,
size_t
count
,
loff_t
*
ppos
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
switch
(
fh
->
type
)
{
case
V4L2_BUF_TYPE_VIDEO_CAPTURE
:
{
DEB_EE
((
"V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%d
\n
"
,
file
,
data
,
count
));
return
saa7146_video_uops
.
read
(
file
,
data
,
count
,
ppos
);
}
case
V4L2_BUF_TYPE_VBI_CAPTURE
:
{
DEB_EE
((
"V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%d
\n
"
,
file
,
data
,
count
));
return
saa7146_vbi_uops
.
read
(
file
,
data
,
count
,
ppos
);
}
break
;
default:
BUG
();
return
0
;
}
}
static
struct
file_operations
video_fops
=
{
.
owner
=
THIS_MODULE
,
.
open
=
fops_open
,
.
release
=
fops_release
,
.
read
=
fops_read
,
.
poll
=
fops_poll
,
.
mmap
=
fops_mmap
,
.
ioctl
=
fops_ioctl
,
.
llseek
=
no_llseek
,
};
void
vv_callback
(
struct
saa7146_dev
*
dev
,
unsigned
long
status
)
{
u32
isr
=
status
;
DEB_EE
((
"dev:%p, isr:0x%08x
\n
"
,
dev
,(
u32
)
status
));
if
(
0
!=
(
isr
&
(
MASK_27
)))
{
DEB_INT
((
"irq: RPS0 (0x%08x).
\n
"
,
isr
));
saa7146_video_uops
.
irq_done
(
dev
,
isr
);
}
if
(
0
!=
(
isr
&
(
MASK_28
)))
{
u32
mc2
=
saa7146_read
(
dev
,
MC2
);
if
(
0
!=
(
mc2
&
MASK_15
))
{
DEB_INT
((
"irq: RPS1 vbi workaround (0x%08x).
\n
"
,
isr
));
wake_up
(
&
dev
->
vv_data
->
vbi_wq
);
saa7146_write
(
dev
,
MC2
,
MASK_31
);
return
;
}
DEB_INT
((
"irq: RPS1 (0x%08x).
\n
"
,
isr
));
saa7146_vbi_uops
.
irq_done
(
dev
,
isr
);
}
}
static
struct
video_device
device_template
=
{
.
hardware
=
VID_HARDWARE_SAA7146
,
.
fops
=
&
video_fops
,
.
minor
=
-
1
,
};
int
saa7146_vv_init
(
struct
saa7146_dev
*
dev
)
{
struct
saa7146_vv
*
vv
=
kmalloc
(
sizeof
(
struct
saa7146_vv
),
GFP_KERNEL
);
if
(
NULL
==
vv
)
{
ERR
((
"out of memory. aborting.
\n
"
));
return
-
1
;
}
memset
(
vv
,
0x0
,
sizeof
(
*
vv
));
DEB_EE
((
"dev:%p
\n
"
,
dev
));
vv
->
video_minor
=
-
1
;
vv
->
vbi_minor
=
-
1
;
vv
->
clipping
=
(
u32
*
)
kmalloc
(
SAA7146_CLIPPING_MEM
,
GFP_KERNEL
);
if
(
NULL
==
vv
->
clipping
)
{
ERR
((
"out of memory. aborting.
\n
"
));
kfree
(
vv
);
return
-
1
;
}
memset
(
vv
->
clipping
,
0x0
,
SAA7146_CLIPPING_MEM
);
saa7146_video_uops
.
init
(
dev
,
vv
);
saa7146_vbi_uops
.
init
(
dev
,
vv
);
dev
->
vv_data
=
vv
;
dev
->
vv_callback
=
&
vv_callback
;
return
0
;
}
int
saa7146_vv_release
(
struct
saa7146_dev
*
dev
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
DEB_EE
((
"dev:%p
\n
"
,
dev
));
kfree
(
vv
);
dev
->
vv_data
=
NULL
;
dev
->
vv_callback
=
NULL
;
return
0
;
}
int
saa7146_register_device
(
struct
video_device
*
vid
,
struct
saa7146_dev
*
dev
,
char
*
name
,
int
type
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
DEB_EE
((
"dev:%p, name:'%s'
\n
"
,
dev
,
name
));
*
vid
=
device_template
;
strncpy
(
vid
->
name
,
name
,
32
);
vid
->
priv
=
dev
;
// fixme: -1 should be an insmod parameter *for the extension* (like "video_nr");
if
(
video_register_device
(
vid
,
type
,
-
1
)
<
0
)
{
ERR
((
"cannot register vbi v4l2 device. skipping.
\n
"
));
return
-
1
;
}
if
(
VFL_TYPE_GRABBER
==
type
)
{
vv
->
video_minor
=
vid
->
minor
;
INFO
((
"%s: registered device video%d [v4l2]
\n
"
,
dev
->
name
,
vid
->
minor
&
0x1f
));
}
else
{
vv
->
vbi_minor
=
vid
->
minor
;
INFO
((
"%s: registered device vbi%d [v4l2]
\n
"
,
dev
->
name
,
vid
->
minor
&
0x1f
));
}
return
0
;
}
int
saa7146_unregister_device
(
struct
video_device
*
vid
,
struct
saa7146_dev
*
dev
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
DEB_EE
((
"dev:%p
\n
"
,
dev
));
if
(
VFL_TYPE_GRABBER
==
vid
->
type
)
{
vv
->
video_minor
=
-
1
;
}
else
{
vv
->
vbi_minor
=
-
1
;
}
video_unregister_device
(
vid
);
return
0
;
}
static
int
__init
saa7146_vv_init_module
(
void
)
{
return
0
;
}
static
void
__exit
saa7146_vv_cleanup_module
(
void
)
{
}
module_init
(
saa7146_vv_init_module
);
module_exit
(
saa7146_vv_cleanup_module
);
EXPORT_SYMBOL_GPL
(
saa7146_set_hps_source_and_sync
);
EXPORT_SYMBOL_GPL
(
saa7146_register_device
);
EXPORT_SYMBOL_GPL
(
saa7146_unregister_device
);
EXPORT_SYMBOL_GPL
(
saa7146_vv_init
);
EXPORT_SYMBOL_GPL
(
saa7146_vv_release
);
MODULE_AUTHOR
(
"Michael Hunold <michael@mihu.de>"
);
MODULE_DESCRIPTION
(
"video4linux driver for saa7146-based hardware"
);
MODULE_LICENSE
(
"GPL"
);
drivers/media/common/saa7146_hlp.c
0 → 100644
View file @
c8639e86
#include <media/saa7146_vv.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
#define KBUILD_MODNAME saa7146
#endif
#define my_min(type,x,y) \
({ type __x = (x), __y = (y); __x < __y ? __x: __y; })
#define my_max(type,x,y) \
({ type __x = (x), __y = (y); __x > __y ? __x: __y; })
static
void
calculate_output_format_register
(
struct
saa7146_dev
*
saa
,
u32
palette
,
u32
*
clip_format
)
{
/* clear out the necessary bits */
*
clip_format
&=
0x0000ffff
;
/* set these bits new */
*
clip_format
|=
((
((
palette
&
0xf00
)
>>
8
)
<<
30
)
|
((
palette
&
0x00f
)
<<
24
)
|
(((
palette
&
0x0f0
)
>>
4
)
<<
16
));
}
static
void
calculate_bcs_ctrl_register
(
struct
saa7146_dev
*
dev
,
int
brightness
,
int
contrast
,
int
colour
,
u32
*
bcs_ctrl
)
{
*
bcs_ctrl
=
((
brightness
<<
24
)
|
(
contrast
<<
16
)
|
(
colour
<<
0
));
}
static
void
calculate_hps_source_and_sync
(
struct
saa7146_dev
*
dev
,
int
source
,
int
sync
,
u32
*
hps_ctrl
)
{
*
hps_ctrl
&=
~
(
MASK_30
|
MASK_31
|
MASK_28
);
*
hps_ctrl
|=
(
source
<<
30
)
|
(
sync
<<
28
);
}
static
void
calculate_hxo_and_hyo
(
struct
saa7146_vv
*
vv
,
u32
*
hps_h_scale
,
u32
*
hps_ctrl
)
{
int
hyo
=
0
,
hxo
=
0
;
hyo
=
vv
->
standard
->
v_offset
;
hxo
=
vv
->
standard
->
h_offset
;
*
hps_h_scale
&=
~
(
MASK_B0
|
0xf00
);
*
hps_h_scale
|=
(
hxo
<<
0
);
*
hps_ctrl
&=
~
(
MASK_W0
|
MASK_B2
);
*
hps_ctrl
|=
(
hyo
<<
12
);
}
/* helper functions for the calculation of the horizontal- and vertical
scaling registers, clip-format-register etc ...
these functions take pointers to the (most-likely read-out
original-values) and manipulate them according to the requested
changes.
*/
/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */
static
struct
{
u16
hps_coeff
;
u16
weight_sum
;
}
hps_h_coeff_tab
[]
=
{
{
0x00
,
2
},
{
0x02
,
4
},
{
0x00
,
4
},
{
0x06
,
8
},
{
0x02
,
8
},
{
0x08
,
8
},
{
0x00
,
8
},
{
0x1E
,
16
},
{
0x0E
,
8
},
{
0x26
,
8
},
{
0x06
,
8
},
{
0x42
,
8
},
{
0x02
,
8
},
{
0x80
,
8
},
{
0x00
,
8
},
{
0xFE
,
16
},
{
0xFE
,
8
},
{
0x7E
,
8
},
{
0x7E
,
8
},
{
0x3E
,
8
},
{
0x3E
,
8
},
{
0x1E
,
8
},
{
0x1E
,
8
},
{
0x0E
,
8
},
{
0x0E
,
8
},
{
0x06
,
8
},
{
0x06
,
8
},
{
0x02
,
8
},
{
0x02
,
8
},
{
0x00
,
8
},
{
0x00
,
8
},
{
0xFE
,
16
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0xFE
,
8
},
{
0x7E
,
8
},
{
0x7E
,
8
},
{
0x3E
,
8
},
{
0x3E
,
8
},
{
0x1E
,
8
},
{
0x1E
,
8
},
{
0x0E
,
8
},
{
0x0E
,
8
},
{
0x06
,
8
},
{
0x06
,
8
},
{
0x02
,
8
},
{
0x02
,
8
},
{
0x00
,
8
},
{
0x00
,
8
},
{
0xFE
,
16
}
};
/* table of attenuation values for horizontal scaling */
u8
h_attenuation
[]
=
{
1
,
2
,
4
,
8
,
2
,
4
,
8
,
16
,
0
};
/* calculate horizontal scale registers */
static
int
calculate_h_scale_registers
(
struct
saa7146_dev
*
dev
,
int
in_x
,
int
out_x
,
int
flip_lr
,
u32
*
hps_ctrl
,
u32
*
hps_v_gain
,
u32
*
hps_h_prescale
,
u32
*
hps_h_scale
)
{
/* horizontal prescaler */
u32
dcgx
=
0
,
xpsc
=
0
,
xacm
=
0
,
cxy
=
0
,
cxuv
=
0
;
/* horizontal scaler */
u32
xim
=
0
,
xp
=
0
,
xsci
=
0
;
/* vertical scale & gain */
u32
pfuv
=
0
;
/* helper variables */
u32
h_atten
=
0
,
i
=
0
;
if
(
0
==
out_x
)
{
return
-
EINVAL
;
}
/* mask out vanity-bit */
*
hps_ctrl
&=
~
MASK_29
;
/* calculate prescale-(xspc)-value: [n .. 1/2) : 1
[1/2 .. 1/3) : 2
[1/3 .. 1/4) : 3
... */
if
(
in_x
>
out_x
)
{
xpsc
=
in_x
/
out_x
;
}
else
{
/* zooming */
xpsc
=
1
;
}
/* if flip_lr-bit is set, number of pixels after
horizontal prescaling must be < 384 */
if
(
0
!=
flip_lr
)
{
/* set vanity bit */
*
hps_ctrl
|=
MASK_29
;
while
(
in_x
/
xpsc
>=
384
)
xpsc
++
;
}
/* if zooming is wanted, number of pixels after
horizontal prescaling must be < 768 */
else
{
while
(
in_x
/
xpsc
>=
768
)
xpsc
++
;
}
/* maximum prescale is 64 (p.69) */
if
(
xpsc
>
64
)
xpsc
=
64
;
/* keep xacm clear*/
xacm
=
0
;
/* set horizontal filter parameters (CXY = CXUV) */
cxy
=
hps_h_coeff_tab
[(
(
xpsc
-
1
)
<
63
?
(
xpsc
-
1
)
:
63
)].
hps_coeff
;
cxuv
=
cxy
;
/* calculate and set horizontal fine scale (xsci) */
/* bypass the horizontal scaler ? */
if
(
(
in_x
==
out_x
)
&&
(
1
==
xpsc
)
)
xsci
=
0x400
;
else
xsci
=
(
(
1024
*
in_x
)
/
(
out_x
*
xpsc
)
)
+
xpsc
;
/* set start phase for horizontal fine scale (xp) to 0 */
xp
=
0
;
/* set xim, if we bypass the horizontal scaler */
if
(
0x400
==
xsci
)
xim
=
1
;
else
xim
=
0
;
/* if the prescaler is bypassed, enable horizontal
accumulation mode (xacm) and clear dcgx */
if
(
1
==
xpsc
)
{
xacm
=
1
;
dcgx
=
0
;
}
else
{
xacm
=
0
;
/* get best match in the table of attenuations
for horizontal scaling */
h_atten
=
hps_h_coeff_tab
[(
(
xpsc
-
1
)
<
63
?
(
xpsc
-
1
)
:
63
)].
weight_sum
;
for
(
i
=
0
;
h_attenuation
[
i
]
!=
0
;
i
++
)
{
if
(
h_attenuation
[
i
]
>=
h_atten
)
break
;
}
dcgx
=
i
;
}
/* the horizontal scaling increment controls the UV filter
to reduce the bandwith to improve the display quality,
so set it ... */
if
(
xsci
==
0x400
)
pfuv
=
0x00
;
else
if
(
xsci
<
0x600
)
pfuv
=
0x01
;
else
if
(
xsci
<
0x680
)
pfuv
=
0x11
;
else
if
(
xsci
<
0x700
)
pfuv
=
0x22
;
else
pfuv
=
0x33
;
*
hps_v_gain
&=
MASK_W0
|
MASK_B2
;
*
hps_v_gain
|=
(
pfuv
<<
24
);
*
hps_h_scale
&=
~
(
MASK_W1
|
0xf000
);
*
hps_h_scale
|=
(
xim
<<
31
)
|
(
xp
<<
24
)
|
(
xsci
<<
12
);
*
hps_h_prescale
|=
(
dcgx
<<
27
)
|
((
xpsc
-
1
)
<<
18
)
|
(
xacm
<<
17
)
|
(
cxy
<<
8
)
|
(
cxuv
<<
0
);
return
0
;
}
static
struct
{
u16
hps_coeff
;
u16
weight_sum
;
}
hps_v_coeff_tab
[]
=
{
{
0x0100
,
2
},
{
0x0102
,
4
},
{
0x0300
,
4
},
{
0x0106
,
8
},
{
0x0502
,
8
},
{
0x0708
,
8
},
{
0x0F00
,
8
},
{
0x011E
,
16
},
{
0x110E
,
16
},
{
0x1926
,
16
},
{
0x3906
,
16
},
{
0x3D42
,
16
},
{
0x7D02
,
16
},
{
0x7F80
,
16
},
{
0xFF00
,
16
},
{
0x01FE
,
32
},
{
0x01FE
,
32
},
{
0x817E
,
32
},
{
0x817E
,
32
},
{
0xC13E
,
32
},
{
0xC13E
,
32
},
{
0xE11E
,
32
},
{
0xE11E
,
32
},
{
0xF10E
,
32
},
{
0xF10E
,
32
},
{
0xF906
,
32
},
{
0xF906
,
32
},
{
0xFD02
,
32
},
{
0xFD02
,
32
},
{
0xFF00
,
32
},
{
0xFF00
,
32
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x01FE
,
64
},
{
0x817E
,
64
},
{
0x817E
,
64
},
{
0xC13E
,
64
},
{
0xC13E
,
64
},
{
0xE11E
,
64
},
{
0xE11E
,
64
},
{
0xF10E
,
64
},
{
0xF10E
,
64
},
{
0xF906
,
64
},
{
0xF906
,
64
},
{
0xFD02
,
64
},
{
0xFD02
,
64
},
{
0xFF00
,
64
},
{
0xFF00
,
64
},
{
0x01FE
,
128
}
};
/* table of attenuation values for vertical scaling */
u16
v_attenuation
[]
=
{
2
,
4
,
8
,
16
,
32
,
64
,
128
,
256
,
0
};
/* calculate vertical scale registers */
static
int
calculate_v_scale_registers
(
struct
saa7146_dev
*
dev
,
enum
v4l2_field
field
,
int
in_y
,
int
out_y
,
u32
*
hps_v_scale
,
u32
*
hps_v_gain
)
{
int
lpi
=
0
;
/* vertical scaling */
u32
yacm
=
0
,
ysci
=
0
,
yacl
=
0
,
ypo
=
0
,
ype
=
0
;
/* vertical scale & gain */
u32
dcgy
=
0
,
cya_cyb
=
0
;
/* helper variables */
u32
v_atten
=
0
,
i
=
0
;
/* error, if vertical zooming */
if
(
in_y
<
out_y
)
{
return
-
EINVAL
;
}
/* linear phase interpolation may be used
if scaling is between 1 and 1/2 (both fields used)
or scaling is between 1/2 and 1/4 (if only one field is used) */
if
(
V4L2_FIELD_HAS_BOTH
(
field
))
{
if
(
2
*
out_y
>=
in_y
)
{
lpi
=
1
;
}
}
else
if
(
field
==
V4L2_FIELD_TOP
||
field
==
V4L2_FIELD_BOTTOM
)
{
if
(
4
*
out_y
>=
in_y
)
{
lpi
=
1
;
}
out_y
*=
2
;
}
if
(
0
!=
lpi
)
{
yacm
=
0
;
yacl
=
0
;
cya_cyb
=
0x00ff
;
/* calculate scaling increment */
if
(
in_y
>
out_y
)
ysci
=
((
1024
*
in_y
)
/
(
out_y
+
1
))
-
1024
;
else
ysci
=
0
;
dcgy
=
0
;
/* calculate ype and ypo */
ype
=
ysci
/
16
;
ypo
=
ype
+
(
ysci
/
64
);
}
else
{
yacm
=
1
;
/* calculate scaling increment */
ysci
=
(((
10
*
1024
*
(
in_y
-
out_y
-
1
))
/
in_y
)
+
9
)
/
10
;
/* calculate ype and ypo */
ypo
=
ype
=
((
ysci
+
15
)
/
16
);
/* the sequence length interval (yacl) has to be set according
to the prescale value, e.g. [n .. 1/2) : 0
[1/2 .. 1/3) : 1
[1/3 .. 1/4) : 2
... */
if
(
ysci
<
512
)
{
yacl
=
0
;
}
else
{
yacl
=
(
ysci
/
(
1024
-
ysci
)
);
}
/* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
cya_cyb
=
hps_v_coeff_tab
[
(
yacl
<
63
?
yacl
:
63
)
].
hps_coeff
;
/* get best match in the table of attenuations for vertical scaling */
v_atten
=
hps_v_coeff_tab
[
(
yacl
<
63
?
yacl
:
63
)
].
weight_sum
;
for
(
i
=
0
;
v_attenuation
[
i
]
!=
0
;
i
++
)
{
if
(
v_attenuation
[
i
]
>=
v_atten
)
break
;
}
dcgy
=
i
;
}
/* ypo and ype swapped in spec ? */
*
hps_v_scale
|=
(
yacm
<<
31
)
|
(
ysci
<<
21
)
|
(
yacl
<<
15
)
|
(
ypo
<<
8
)
|
(
ype
<<
1
);
*
hps_v_gain
&=
~
(
MASK_W0
|
MASK_B2
);
*
hps_v_gain
|=
(
dcgy
<<
16
)
|
(
cya_cyb
<<
0
);
return
0
;
}
/* simple bubble-sort algorithm with duplicate elimination */
static
int
sort_and_eliminate
(
u32
*
values
,
int
*
count
)
{
int
low
=
0
,
high
=
0
,
top
=
0
,
temp
=
0
;
int
cur
=
0
,
next
=
0
;
/* sanity checks */
if
(
(
0
>
*
count
)
||
(
NULL
==
values
)
)
{
return
-
EINVAL
;
}
/* bubble sort the first count items of the array values */
for
(
top
=
*
count
;
top
>
0
;
top
--
)
{
for
(
low
=
0
,
high
=
1
;
high
<
top
;
low
++
,
high
++
)
{
if
(
values
[
low
]
>
values
[
high
]
)
{
temp
=
values
[
low
];
values
[
low
]
=
values
[
high
];
values
[
high
]
=
temp
;
}
}
}
/* remove duplicate items */
for
(
cur
=
0
,
next
=
1
;
next
<
*
count
;
next
++
)
{
if
(
values
[
cur
]
!=
values
[
next
])
values
[
++
cur
]
=
values
[
next
];
}
*
count
=
cur
+
1
;
return
0
;
}
static
void
calculate_clipping_registers_rect
(
struct
saa7146_dev
*
dev
,
struct
saa7146_fh
*
fh
,
struct
saa7146_video_dma
*
vdma2
,
u32
*
clip_format
,
u32
*
arbtr_ctrl
,
enum
v4l2_field
field
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
u32
*
clipping
=
vv
->
clipping
;
int
width
=
fh
->
ov
.
win
.
w
.
width
;
int
height
=
fh
->
ov
.
win
.
w
.
height
;
int
clipcount
=
fh
->
ov
.
nclips
;
u32
line_list
[
32
];
u32
pixel_list
[
32
];
int
numdwords
=
0
;
int
i
=
0
,
j
=
0
;
int
cnt_line
=
0
,
cnt_pixel
=
0
;
int
x
[
32
],
y
[
32
],
w
[
32
],
h
[
32
];
/* clear out memory */
memset
(
&
line_list
[
0
],
0x00
,
sizeof
(
u32
)
*
32
);
memset
(
&
pixel_list
[
0
],
0x00
,
sizeof
(
u32
)
*
32
);
memset
(
clipping
,
0x00
,
SAA7146_CLIPPING_MEM
);
/* fill the line and pixel-lists */
for
(
i
=
0
;
i
<
clipcount
;
i
++
)
{
int
l
=
0
,
r
=
0
,
t
=
0
,
b
=
0
;
x
[
i
]
=
fh
->
ov
.
clips
[
i
].
c
.
left
;
y
[
i
]
=
fh
->
ov
.
clips
[
i
].
c
.
top
;
w
[
i
]
=
fh
->
ov
.
clips
[
i
].
c
.
width
;
h
[
i
]
=
fh
->
ov
.
clips
[
i
].
c
.
height
;
if
(
w
[
i
]
<
0
)
{
x
[
i
]
+=
w
[
i
];
w
[
i
]
=
-
w
[
i
];
}
if
(
h
[
i
]
<
0
)
{
y
[
i
]
+=
h
[
i
];
h
[
i
]
=
-
h
[
i
];
}
if
(
x
[
i
]
<
0
)
{
w
[
i
]
+=
x
[
i
];
x
[
i
]
=
0
;
}
if
(
y
[
i
]
<
0
)
{
h
[
i
]
+=
y
[
i
];
y
[
i
]
=
0
;
}
if
(
0
!=
vv
->
vflip
)
{
y
[
i
]
=
height
-
y
[
i
]
-
h
[
i
];
}
l
=
x
[
i
];
r
=
x
[
i
]
+
w
[
i
];
t
=
y
[
i
];
b
=
y
[
i
]
+
h
[
i
];
/* insert left/right coordinates */
pixel_list
[
2
*
i
]
=
my_min
(
int
,
l
,
width
);
pixel_list
[(
2
*
i
)
+
1
]
=
my_min
(
int
,
r
,
width
);
/* insert top/bottom coordinates */
line_list
[
2
*
i
]
=
my_min
(
int
,
t
,
height
);
line_list
[(
2
*
i
)
+
1
]
=
my_min
(
int
,
b
,
height
);
}
/* sort and eliminate lists */
cnt_line
=
cnt_pixel
=
2
*
clipcount
;
sort_and_eliminate
(
&
pixel_list
[
0
],
&
cnt_pixel
);
sort_and_eliminate
(
&
line_list
[
0
],
&
cnt_line
);
/* calculate the number of used u32s */
numdwords
=
my_max
(
int
,
(
cnt_line
+
1
),
(
cnt_pixel
+
1
))
*
2
;
numdwords
=
my_max
(
int
,
4
,
numdwords
);
numdwords
=
my_min
(
int
,
64
,
numdwords
);
/* fill up cliptable */
for
(
i
=
0
;
i
<
cnt_pixel
;
i
++
)
{
clipping
[
2
*
i
]
|=
(
pixel_list
[
i
]
<<
16
);
}
for
(
i
=
0
;
i
<
cnt_line
;
i
++
)
{
clipping
[(
2
*
i
)
+
1
]
|=
(
line_list
[
i
]
<<
16
);
}
/* fill up cliptable with the display infos */
for
(
j
=
0
;
j
<
clipcount
;
j
++
)
{
for
(
i
=
0
;
i
<
cnt_pixel
;
i
++
)
{
if
(
x
[
j
]
<
0
)
x
[
j
]
=
0
;
if
(
pixel_list
[
i
]
<
(
x
[
j
]
+
w
[
j
]))
{
if
(
pixel_list
[
i
]
>=
x
[
j
]
)
{
clipping
[
2
*
i
]
|=
(
1
<<
j
);
}
}
}
for
(
i
=
0
;
i
<
cnt_line
;
i
++
)
{
if
(
y
[
j
]
<
0
)
y
[
j
]
=
0
;
if
(
line_list
[
i
]
<
(
y
[
j
]
+
h
[
j
])
)
{
if
(
line_list
[
i
]
>=
y
[
j
]
)
{
clipping
[(
2
*
i
)
+
1
]
|=
(
1
<<
j
);
}
}
}
}
/* adjust arbitration control register */
*
arbtr_ctrl
&=
0xffff00ff
;
*
arbtr_ctrl
|=
0x00001c00
;
vdma2
->
base_even
=
virt_to_bus
(
clipping
);
vdma2
->
base_odd
=
virt_to_bus
(
clipping
);
vdma2
->
prot_addr
=
virt_to_bus
(
clipping
)
+
((
sizeof
(
u32
))
*
(
numdwords
));
vdma2
->
base_page
=
0x04
;
vdma2
->
pitch
=
0x00
;
vdma2
->
num_line_byte
=
(
0
<<
16
|
(
sizeof
(
u32
))
*
(
numdwords
-
1
)
);
/* set clipping-mode. this depends on the field(s) used */
*
clip_format
&=
0xfffffff7
;
if
(
V4L2_FIELD_HAS_BOTH
(
field
))
{
*
clip_format
|=
0x00000008
;
}
else
if
(
field
==
V4L2_FIELD_TOP
)
{
*
clip_format
|=
0x00000000
;
}
else
if
(
field
==
V4L2_FIELD_BOTTOM
)
{
*
clip_format
|=
0x00000000
;
}
}
/* disable clipping */
static
void
saa7146_disable_clipping
(
struct
saa7146_dev
*
dev
)
{
u32
clip_format
=
saa7146_read
(
dev
,
CLIP_FORMAT_CTRL
);
/* mask out relevant bits (=lower word)*/
clip_format
&=
MASK_W1
;
/* upload clipping-registers*/
saa7146_write
(
dev
,
CLIP_FORMAT_CTRL
,
clip_format
);
saa7146_write
(
dev
,
MC2
,
(
MASK_05
|
MASK_21
));
/* disable video dma2 */
saa7146_write
(
dev
,
MC1
,
(
MASK_21
));
}
static
void
saa7146_set_clipping_rect
(
struct
saa7146_dev
*
dev
,
struct
saa7146_fh
*
fh
)
{
enum
v4l2_field
field
=
fh
->
ov
.
win
.
field
;
int
clipcount
=
fh
->
ov
.
nclips
;
struct
saa7146_video_dma
vdma2
;
u32
clip_format
=
saa7146_read
(
dev
,
CLIP_FORMAT_CTRL
);
u32
arbtr_ctrl
=
saa7146_read
(
dev
,
PCI_BT_V1
);
// fixme: is this used at all? SAA7146_CLIPPING_RECT_INVERTED;
u32
type
=
SAA7146_CLIPPING_RECT
;
/* check clipcount, disable clipping if clipcount == 0*/
if
(
clipcount
==
0
)
{
saa7146_disable_clipping
(
dev
);
return
;
}
calculate_clipping_registers_rect
(
dev
,
fh
,
&
vdma2
,
&
clip_format
,
&
arbtr_ctrl
,
field
);
/* set clipping format */
clip_format
&=
0xffff0008
;
clip_format
|=
(
type
<<
4
);
/* prepare video dma2 */
saa7146_write
(
dev
,
BASE_EVEN2
,
vdma2
.
base_even
);
saa7146_write
(
dev
,
BASE_ODD2
,
vdma2
.
base_odd
);
saa7146_write
(
dev
,
PROT_ADDR2
,
vdma2
.
prot_addr
);
saa7146_write
(
dev
,
BASE_PAGE2
,
vdma2
.
base_page
);
saa7146_write
(
dev
,
PITCH2
,
vdma2
.
pitch
);
saa7146_write
(
dev
,
NUM_LINE_BYTE2
,
vdma2
.
num_line_byte
);
/* prepare the rest */
saa7146_write
(
dev
,
CLIP_FORMAT_CTRL
,
clip_format
);
saa7146_write
(
dev
,
PCI_BT_V1
,
arbtr_ctrl
);
/* upload clip_control-register, clipping-registers, enable video dma2 */
saa7146_write
(
dev
,
MC2
,
(
MASK_05
|
MASK_21
|
MASK_03
|
MASK_19
));
saa7146_write
(
dev
,
MC1
,
(
MASK_05
|
MASK_21
));
}
static
void
saa7146_set_window
(
struct
saa7146_dev
*
dev
,
int
width
,
int
height
,
enum
v4l2_field
field
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
int
source
=
vv
->
current_hps_source
;
int
sync
=
vv
->
current_hps_sync
;
u32
hps_v_scale
=
0
,
hps_v_gain
=
0
,
hps_ctrl
=
0
,
hps_h_prescale
=
0
,
hps_h_scale
=
0
;
/* set vertical scale */
hps_v_scale
=
0
;
/* all bits get set by the function-call */
hps_v_gain
=
0
;
/* fixme: saa7146_read(dev, HPS_V_GAIN);*/
calculate_v_scale_registers
(
dev
,
field
,
vv
->
standard
->
v_calc
,
height
,
&
hps_v_scale
,
&
hps_v_gain
);
/* set horizontal scale */
hps_ctrl
=
0
;
hps_h_prescale
=
0
;
/* all bits get set in the function */
hps_h_scale
=
0
;
calculate_h_scale_registers
(
dev
,
vv
->
standard
->
h_calc
,
width
,
vv
->
hflip
,
&
hps_ctrl
,
&
hps_v_gain
,
&
hps_h_prescale
,
&
hps_h_scale
);
/* set hyo and hxo */
calculate_hxo_and_hyo
(
vv
,
&
hps_h_scale
,
&
hps_ctrl
);
calculate_hps_source_and_sync
(
dev
,
source
,
sync
,
&
hps_ctrl
);
/* write out new register contents */
saa7146_write
(
dev
,
HPS_V_SCALE
,
hps_v_scale
);
saa7146_write
(
dev
,
HPS_V_GAIN
,
hps_v_gain
);
saa7146_write
(
dev
,
HPS_CTRL
,
hps_ctrl
);
saa7146_write
(
dev
,
HPS_H_PRESCALE
,
hps_h_prescale
);
saa7146_write
(
dev
,
HPS_H_SCALE
,
hps_h_scale
);
/* upload shadow-ram registers */
saa7146_write
(
dev
,
MC2
,
(
MASK_05
|
MASK_06
|
MASK_21
|
MASK_22
)
);
}
/* calculate the new memory offsets for a desired position */
static
void
saa7146_set_position
(
struct
saa7146_dev
*
dev
,
int
w_x
,
int
w_y
,
int
w_height
,
enum
v4l2_field
field
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
int
b_depth
=
vv
->
ov_fmt
->
depth
;
int
b_bpl
=
vv
->
ov_fb
.
fmt
.
bytesperline
;
u32
base
=
(
u32
)
vv
->
ov_fb
.
base
;
struct
saa7146_video_dma
vdma1
;
/* calculate memory offsets for picture, look if we shall top-down-flip */
vdma1
.
pitch
=
2
*
b_bpl
;
if
(
0
==
vv
->
vflip
)
{
vdma1
.
base_even
=
(
u32
)
base
+
(
w_y
*
(
vdma1
.
pitch
/
2
))
+
(
w_x
*
(
b_depth
/
8
));
vdma1
.
base_odd
=
vdma1
.
base_even
+
(
vdma1
.
pitch
/
2
);
vdma1
.
prot_addr
=
vdma1
.
base_even
+
(
w_height
*
(
vdma1
.
pitch
/
2
));
}
else
{
vdma1
.
base_even
=
(
u32
)
base
+
((
w_y
+
w_height
)
*
(
vdma1
.
pitch
/
2
))
+
(
w_x
*
(
b_depth
/
8
));
vdma1
.
base_odd
=
vdma1
.
base_even
-
(
vdma1
.
pitch
/
2
);
vdma1
.
prot_addr
=
vdma1
.
base_odd
-
(
w_height
*
(
vdma1
.
pitch
/
2
));
}
if
(
V4L2_FIELD_HAS_BOTH
(
field
))
{
}
else
if
(
field
==
V4L2_FIELD_TOP
)
{
vdma1
.
base_odd
=
vdma1
.
prot_addr
;
vdma1
.
pitch
/=
2
;
}
else
if
(
field
==
V4L2_FIELD_BOTTOM
)
{
vdma1
.
base_odd
=
vdma1
.
base_even
;
vdma1
.
base_even
=
vdma1
.
prot_addr
;
vdma1
.
pitch
/=
2
;
}
if
(
0
!=
vv
->
vflip
)
{
vdma1
.
pitch
*=
-
1
;
}
vdma1
.
base_page
=
0
;
vdma1
.
num_line_byte
=
(
vv
->
standard
->
v_field
<<
16
)
+
vv
->
standard
->
h_pixels
;
saa7146_write_out_dma
(
dev
,
1
,
&
vdma1
);
}
static
void
saa7146_set_output_format
(
struct
saa7146_dev
*
dev
,
unsigned
long
palette
)
{
u32
clip_format
=
saa7146_read
(
dev
,
CLIP_FORMAT_CTRL
);
/* call helper function */
calculate_output_format_register
(
dev
,
palette
,
&
clip_format
);
/* update the hps registers */
saa7146_write
(
dev
,
CLIP_FORMAT_CTRL
,
clip_format
);
saa7146_write
(
dev
,
MC2
,
(
MASK_05
|
MASK_21
));
}
void
saa7146_set_picture_prop
(
struct
saa7146_dev
*
dev
,
int
brightness
,
int
contrast
,
int
colour
)
{
u32
bcs_ctrl
=
0
;
calculate_bcs_ctrl_register
(
dev
,
brightness
,
contrast
,
colour
,
&
bcs_ctrl
);
saa7146_write
(
dev
,
BCS_CTRL
,
bcs_ctrl
);
/* update the bcs register */
saa7146_write
(
dev
,
MC2
,
(
MASK_06
|
MASK_22
));
}
/* select input-source */
void
saa7146_set_hps_source_and_sync
(
struct
saa7146_dev
*
dev
,
int
source
,
int
sync
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
u32
hps_ctrl
=
0
;
/* read old state */
hps_ctrl
=
saa7146_read
(
dev
,
HPS_CTRL
);
hps_ctrl
&=
~
(
MASK_31
|
MASK_30
|
MASK_28
);
hps_ctrl
|=
(
source
<<
30
)
|
(
sync
<<
28
);
/* write back & upload register */
saa7146_write
(
dev
,
HPS_CTRL
,
hps_ctrl
);
saa7146_write
(
dev
,
MC2
,
(
MASK_05
|
MASK_21
));
vv
->
current_hps_source
=
source
;
vv
->
current_hps_sync
=
sync
;
}
/* write "data" to the gpio-pin "pin" */
void
saa7146_set_gpio
(
struct
saa7146_dev
*
dev
,
u8
pin
,
u8
data
)
{
u32
value
=
0
;
/* sanity check */
if
(
pin
>
3
)
return
;
/* read old register contents */
value
=
saa7146_read
(
dev
,
GPIO_CTRL
);
value
&=
~
(
0xff
<<
(
8
*
pin
));
value
|=
(
data
<<
(
8
*
pin
));
saa7146_write
(
dev
,
GPIO_CTRL
,
value
);
}
/* reprogram hps, enable(1) / disable(0) video */
void
saa7146_set_overlay
(
struct
saa7146_dev
*
dev
,
struct
saa7146_fh
*
fh
,
int
v
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
/* enable ? */
if
(
0
==
v
)
{
/* disable video dma1 */
saa7146_write
(
dev
,
MC1
,
MASK_22
);
return
;
}
saa7146_set_window
(
dev
,
fh
->
ov
.
win
.
w
.
width
,
fh
->
ov
.
win
.
w
.
height
,
fh
->
ov
.
win
.
field
);
saa7146_set_position
(
dev
,
fh
->
ov
.
win
.
w
.
left
,
fh
->
ov
.
win
.
w
.
top
,
fh
->
ov
.
win
.
w
.
height
,
fh
->
ov
.
win
.
field
);
saa7146_set_output_format
(
dev
,
vv
->
ov_fmt
->
trans
);
saa7146_set_clipping_rect
(
dev
,
fh
);
/* enable video dma1 */
saa7146_write
(
dev
,
MC1
,
(
MASK_06
|
MASK_22
));
}
void
saa7146_write_out_dma
(
struct
saa7146_dev
*
dev
,
int
which
,
struct
saa7146_video_dma
*
vdma
)
{
int
where
=
0
;
if
(
which
<
1
||
which
>
3
)
{
return
;
}
/* calculate starting address */
where
=
(
which
-
1
)
*
0x18
;
if
(
0
!=
(
dev
->
ext
->
ext_vv_data
->
flags
&
SAA7146_EXT_SWAP_ODD_EVEN
))
{
saa7146_write
(
dev
,
where
,
vdma
->
base_even
);
saa7146_write
(
dev
,
where
+
0x04
,
vdma
->
base_odd
);
}
else
{
saa7146_write
(
dev
,
where
,
vdma
->
base_odd
);
saa7146_write
(
dev
,
where
+
0x04
,
vdma
->
base_even
);
}
saa7146_write
(
dev
,
where
+
0x08
,
vdma
->
prot_addr
);
saa7146_write
(
dev
,
where
+
0x0c
,
vdma
->
pitch
);
saa7146_write
(
dev
,
where
+
0x10
,
vdma
->
base_page
);
saa7146_write
(
dev
,
where
+
0x14
,
vdma
->
num_line_byte
);
/* upload */
saa7146_write
(
dev
,
MC2
,
(
MASK_02
<<
(
which
-
1
))
|
(
MASK_18
<<
(
which
-
1
)));
/*
printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even);
printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd);
printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr);
printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page);
printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch);
printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte);
*/
}
static
int
calculate_video_dma_grab_packed
(
struct
saa7146_dev
*
dev
,
struct
saa7146_buf
*
buf
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
struct
saa7146_video_dma
vdma1
;
struct
saa7146_format
*
sfmt
=
format_by_fourcc
(
dev
,
buf
->
fmt
->
pixelformat
);
int
width
=
buf
->
fmt
->
width
;
int
height
=
buf
->
fmt
->
height
;
enum
v4l2_field
field
=
buf
->
fmt
->
field
;
int
depth
=
sfmt
->
depth
;
DEB_CAP
((
"[size=%dx%d,fields=%s]
\n
"
,
width
,
height
,
v4l2_field_names
[
field
]));
vdma1
.
pitch
=
(
width
*
depth
*
2
)
/
8
;
vdma1
.
num_line_byte
=
((
vv
->
standard
->
v_field
<<
16
)
+
vv
->
standard
->
h_pixels
);
vdma1
.
base_page
=
buf
->
pt
[
0
].
dma
|
ME1
;
if
(
0
!=
vv
->
vflip
)
{
vdma1
.
prot_addr
=
buf
->
pt
[
0
].
offset
;
vdma1
.
base_even
=
buf
->
pt
[
0
].
offset
+
(
vdma1
.
pitch
/
2
)
*
height
;
vdma1
.
base_odd
=
vdma1
.
base_even
-
(
vdma1
.
pitch
/
2
);
}
else
{
vdma1
.
base_even
=
buf
->
pt
[
0
].
offset
;
vdma1
.
base_odd
=
vdma1
.
base_even
+
(
vdma1
.
pitch
/
2
);
vdma1
.
prot_addr
=
buf
->
pt
[
0
].
offset
+
(
vdma1
.
pitch
/
2
)
*
height
;
}
if
(
V4L2_FIELD_HAS_BOTH
(
field
))
{
}
else
if
(
field
==
V4L2_FIELD_TOP
)
{
vdma1
.
base_odd
=
vdma1
.
prot_addr
;
vdma1
.
pitch
/=
2
;
}
else
if
(
field
==
V4L2_FIELD_BOTTOM
)
{
vdma1
.
base_odd
=
vdma1
.
base_even
;
vdma1
.
base_even
=
vdma1
.
prot_addr
;
vdma1
.
pitch
/=
2
;
}
if
(
0
!=
vv
->
vflip
)
{
vdma1
.
pitch
*=
-
1
;
}
saa7146_write_out_dma
(
dev
,
1
,
&
vdma1
);
return
0
;
}
static
int
calc_planar_422
(
struct
saa7146_vv
*
vv
,
struct
saa7146_buf
*
buf
,
struct
saa7146_video_dma
*
vdma2
,
struct
saa7146_video_dma
*
vdma3
)
{
int
height
=
buf
->
fmt
->
height
;
int
width
=
buf
->
fmt
->
width
;
vdma2
->
pitch
=
width
;
vdma3
->
pitch
=
width
;
if
(
0
!=
vv
->
vflip
)
{
vdma2
->
prot_addr
=
buf
->
pt
[
1
].
offset
;
vdma2
->
base_even
=
((
vdma2
->
pitch
/
2
)
*
height
)
+
buf
->
pt
[
1
].
offset
;
vdma2
->
base_odd
=
vdma2
->
base_even
-
(
vdma2
->
pitch
/
2
);
vdma3
->
prot_addr
=
buf
->
pt
[
2
].
offset
;
vdma3
->
base_even
=
((
vdma3
->
pitch
/
2
)
*
height
)
+
buf
->
pt
[
2
].
offset
;
vdma3
->
base_odd
=
vdma3
->
base_even
-
(
vdma3
->
pitch
/
2
);
}
else
{
vdma3
->
base_even
=
buf
->
pt
[
2
].
offset
;
vdma3
->
base_odd
=
vdma3
->
base_even
+
(
vdma3
->
pitch
/
2
);
vdma3
->
prot_addr
=
(
vdma3
->
pitch
/
2
)
*
height
+
buf
->
pt
[
2
].
offset
;
vdma2
->
base_even
=
buf
->
pt
[
1
].
offset
;
vdma2
->
base_odd
=
vdma2
->
base_even
+
(
vdma2
->
pitch
/
2
);
vdma2
->
prot_addr
=
(
vdma2
->
pitch
/
2
)
*
height
+
buf
->
pt
[
1
].
offset
;
}
return
0
;
}
static
int
calc_planar_420
(
struct
saa7146_vv
*
vv
,
struct
saa7146_buf
*
buf
,
struct
saa7146_video_dma
*
vdma2
,
struct
saa7146_video_dma
*
vdma3
)
{
int
height
=
buf
->
fmt
->
height
;
int
width
=
buf
->
fmt
->
width
;
vdma2
->
pitch
=
width
/
2
;
vdma3
->
pitch
=
width
/
2
;
if
(
0
!=
vv
->
vflip
)
{
vdma2
->
prot_addr
=
buf
->
pt
[
2
].
offset
;
vdma2
->
base_even
=
((
vdma2
->
pitch
/
2
)
*
height
)
+
buf
->
pt
[
2
].
offset
;
vdma2
->
base_odd
=
vdma2
->
base_even
-
(
vdma2
->
pitch
/
2
);
vdma3
->
prot_addr
=
buf
->
pt
[
1
].
offset
;
vdma3
->
base_even
=
((
vdma3
->
pitch
/
2
)
*
height
)
+
buf
->
pt
[
1
].
offset
;
vdma3
->
base_odd
=
vdma3
->
base_even
-
(
vdma3
->
pitch
/
2
);
}
else
{
vdma3
->
base_even
=
buf
->
pt
[
2
].
offset
;
vdma3
->
base_odd
=
vdma3
->
base_even
+
(
vdma3
->
pitch
);
vdma3
->
prot_addr
=
(
vdma3
->
pitch
/
2
)
*
height
+
buf
->
pt
[
2
].
offset
;
vdma2
->
base_even
=
buf
->
pt
[
1
].
offset
;
vdma2
->
base_odd
=
vdma2
->
base_even
+
(
vdma2
->
pitch
);
vdma2
->
prot_addr
=
(
vdma2
->
pitch
/
2
)
*
height
+
buf
->
pt
[
1
].
offset
;
}
return
0
;
}
static
int
calculate_video_dma_grab_planar
(
struct
saa7146_dev
*
dev
,
struct
saa7146_buf
*
buf
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
struct
saa7146_video_dma
vdma1
;
struct
saa7146_video_dma
vdma2
;
struct
saa7146_video_dma
vdma3
;
struct
saa7146_format
*
sfmt
=
format_by_fourcc
(
dev
,
buf
->
fmt
->
pixelformat
);
int
width
=
buf
->
fmt
->
width
;
int
height
=
buf
->
fmt
->
height
;
enum
v4l2_field
field
=
buf
->
fmt
->
field
;
BUG_ON
(
0
==
buf
->
pt
[
0
].
dma
);
BUG_ON
(
0
==
buf
->
pt
[
1
].
dma
);
BUG_ON
(
0
==
buf
->
pt
[
2
].
dma
);
DEB_CAP
((
"[size=%dx%d,fields=%s]
\n
"
,
width
,
height
,
v4l2_field_names
[
field
]));
/* fixme: what happens for user space buffers here?. The offsets are
most likely wrong, this version here only works for page-aligned
buffers, modifications to the pagetable-functions are necessary...*/
vdma1
.
pitch
=
width
*
2
;
vdma1
.
num_line_byte
=
((
vv
->
standard
->
v_field
<<
16
)
+
vv
->
standard
->
h_pixels
);
vdma1
.
base_page
=
buf
->
pt
[
0
].
dma
|
ME1
;
if
(
0
!=
vv
->
vflip
)
{
vdma1
.
prot_addr
=
buf
->
pt
[
0
].
offset
;
vdma1
.
base_even
=
((
vdma1
.
pitch
/
2
)
*
height
)
+
buf
->
pt
[
0
].
offset
;
vdma1
.
base_odd
=
vdma1
.
base_even
-
(
vdma1
.
pitch
/
2
);
}
else
{
vdma1
.
base_even
=
buf
->
pt
[
0
].
offset
;
vdma1
.
base_odd
=
vdma1
.
base_even
+
(
vdma1
.
pitch
/
2
);
vdma1
.
prot_addr
=
(
vdma1
.
pitch
/
2
)
*
height
+
buf
->
pt
[
0
].
offset
;
}
vdma2
.
num_line_byte
=
0
;
/* unused */
vdma2
.
base_page
=
buf
->
pt
[
1
].
dma
|
ME1
;
vdma3
.
num_line_byte
=
0
;
/* unused */
vdma3
.
base_page
=
buf
->
pt
[
2
].
dma
|
ME1
;
switch
(
sfmt
->
depth
)
{
case
12
:
{
calc_planar_420
(
vv
,
buf
,
&
vdma2
,
&
vdma3
);
break
;
}
case
16
:
{
calc_planar_422
(
vv
,
buf
,
&
vdma2
,
&
vdma3
);
break
;
}
default:
{
return
-
1
;
}
}
if
(
V4L2_FIELD_HAS_BOTH
(
field
))
{
}
else
if
(
field
==
V4L2_FIELD_TOP
)
{
vdma1
.
base_odd
=
vdma1
.
prot_addr
;
vdma1
.
pitch
/=
2
;
vdma2
.
base_odd
=
vdma2
.
prot_addr
;
vdma2
.
pitch
/=
2
;
vdma3
.
base_odd
=
vdma3
.
prot_addr
;
vdma3
.
pitch
/=
2
;
}
else
if
(
field
==
V4L2_FIELD_BOTTOM
)
{
vdma1
.
base_odd
=
vdma1
.
base_even
;
vdma1
.
base_even
=
vdma1
.
prot_addr
;
vdma1
.
pitch
/=
2
;
vdma2
.
base_odd
=
vdma2
.
base_even
;
vdma2
.
base_even
=
vdma2
.
prot_addr
;
vdma2
.
pitch
/=
2
;
vdma3
.
base_odd
=
vdma3
.
base_even
;
vdma3
.
base_even
=
vdma3
.
prot_addr
;
vdma3
.
pitch
/=
2
;
}
if
(
0
!=
vv
->
vflip
)
{
vdma1
.
pitch
*=
-
1
;
vdma2
.
pitch
*=
-
1
;
vdma3
.
pitch
*=
-
1
;
}
saa7146_write_out_dma
(
dev
,
1
,
&
vdma1
);
if
(
sfmt
->
swap
!=
0
)
{
saa7146_write_out_dma
(
dev
,
3
,
&
vdma2
);
saa7146_write_out_dma
(
dev
,
2
,
&
vdma3
);
}
else
{
saa7146_write_out_dma
(
dev
,
2
,
&
vdma2
);
saa7146_write_out_dma
(
dev
,
3
,
&
vdma3
);
}
return
0
;
}
static
void
program_capture_engine
(
struct
saa7146_dev
*
dev
,
int
planar
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
int
count
=
0
;
unsigned
long
e_wait
=
vv
->
current_hps_sync
==
SAA7146_HPS_SYNC_PORT_A
?
CMD_E_FID_A
:
CMD_E_FID_B
;
unsigned
long
o_wait
=
vv
->
current_hps_sync
==
SAA7146_HPS_SYNC_PORT_A
?
CMD_O_FID_A
:
CMD_O_FID_B
;
/* write beginning of rps-program */
count
=
0
;
/* wait for o_fid_a/b / e_fid_a/b toggle only if bit 0 is not set*/
dev
->
rps0
[
count
++
]
=
CMD_PAUSE
|
CMD_OAN
|
CMD_SIG0
|
e_wait
;
dev
->
rps0
[
count
++
]
=
CMD_PAUSE
|
CMD_OAN
|
CMD_SIG0
|
o_wait
;
/* set bit 0 */
dev
->
rps0
[
count
++
]
=
CMD_WR_REG
|
(
1
<<
8
)
|
(
MC2
/
4
);
dev
->
rps0
[
count
++
]
=
MASK_27
|
MASK_11
;
/* turn on video-dma1 */
dev
->
rps0
[
count
++
]
=
CMD_WR_REG_MASK
|
(
MC1
/
4
);
dev
->
rps0
[
count
++
]
=
MASK_06
|
MASK_22
;
/* => mask */
dev
->
rps0
[
count
++
]
=
MASK_06
|
MASK_22
;
/* => values */
if
(
0
!=
planar
)
{
/* turn on video-dma2 */
dev
->
rps0
[
count
++
]
=
CMD_WR_REG_MASK
|
(
MC1
/
4
);
dev
->
rps0
[
count
++
]
=
MASK_05
|
MASK_21
;
/* => mask */
dev
->
rps0
[
count
++
]
=
MASK_05
|
MASK_21
;
/* => values */
/* turn on video-dma3 */
dev
->
rps0
[
count
++
]
=
CMD_WR_REG_MASK
|
(
MC1
/
4
);
dev
->
rps0
[
count
++
]
=
MASK_04
|
MASK_20
;
/* => mask */
dev
->
rps0
[
count
++
]
=
MASK_04
|
MASK_20
;
/* => values */
}
/* wait for o_fid_a/b / e_fid_a/b toggle */
dev
->
rps0
[
count
++
]
=
CMD_PAUSE
|
e_wait
;
dev
->
rps0
[
count
++
]
=
CMD_PAUSE
|
o_wait
;
/* turn off video-dma1 */
dev
->
rps0
[
count
++
]
=
CMD_WR_REG_MASK
|
(
MC1
/
4
);
dev
->
rps0
[
count
++
]
=
MASK_22
|
MASK_06
;
/* => mask */
dev
->
rps0
[
count
++
]
=
MASK_22
;
/* => values */
if
(
0
!=
planar
)
{
/* turn off video-dma2 */
dev
->
rps0
[
count
++
]
=
CMD_WR_REG_MASK
|
(
MC1
/
4
);
dev
->
rps0
[
count
++
]
=
MASK_05
|
MASK_21
;
/* => mask */
dev
->
rps0
[
count
++
]
=
MASK_21
;
/* => values */
/* turn off video-dma3 */
dev
->
rps0
[
count
++
]
=
CMD_WR_REG_MASK
|
(
MC1
/
4
);
dev
->
rps0
[
count
++
]
=
MASK_04
|
MASK_20
;
/* => mask */
dev
->
rps0
[
count
++
]
=
MASK_20
;
/* => values */
}
/* generate interrupt */
dev
->
rps0
[
count
++
]
=
CMD_INTERRUPT
;
/* stop */
dev
->
rps0
[
count
++
]
=
CMD_STOP
;
}
void
saa7146_set_capture
(
struct
saa7146_dev
*
dev
,
struct
saa7146_buf
*
buf
,
struct
saa7146_buf
*
next
)
{
struct
saa7146_format
*
sfmt
=
format_by_fourcc
(
dev
,
buf
->
fmt
->
pixelformat
);
DEB_CAP
((
"buf:%p, next:%p
\n
"
,
buf
,
next
));
saa7146_set_window
(
dev
,
buf
->
fmt
->
width
,
buf
->
fmt
->
height
,
buf
->
fmt
->
field
);
saa7146_set_output_format
(
dev
,
sfmt
->
trans
);
saa7146_disable_clipping
(
dev
);
if
(
0
!=
IS_PLANAR
(
sfmt
->
trans
))
{
calculate_video_dma_grab_planar
(
dev
,
buf
);
program_capture_engine
(
dev
,
1
);
}
else
{
calculate_video_dma_grab_packed
(
dev
,
buf
);
program_capture_engine
(
dev
,
0
);
}
/* write the address of the rps-program */
saa7146_write
(
dev
,
RPS_ADDR0
,
virt_to_bus
(
&
dev
->
rps0
[
0
]));
/* turn on rps */
saa7146_write
(
dev
,
MC1
,
(
MASK_12
|
MASK_28
));
}
drivers/media/common/saa7146_i2c.c
0 → 100644
View file @
c8639e86
#include <media/saa7146_vv.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
#define KBUILD_MODNAME saa7146
#endif
/* helper function */
static
void
my_wait
(
struct
saa7146_dev
*
dev
,
long
ms
)
{
set_current_state
(
TASK_INTERRUPTIBLE
);
schedule_timeout
((((
ms
+
10
)
/
10
)
*
HZ
)
/
1000
);
}
u32
saa7146_i2c_func
(
struct
i2c_adapter
*
adapter
)
{
//fm DEB_I2C(("'%s'.\n", adapter->name));
return
I2C_FUNC_I2C
|
I2C_FUNC_SMBUS_QUICK
|
I2C_FUNC_SMBUS_READ_BYTE
|
I2C_FUNC_SMBUS_WRITE_BYTE
|
I2C_FUNC_SMBUS_READ_BYTE_DATA
|
I2C_FUNC_SMBUS_WRITE_BYTE_DATA
;
}
/* this function returns the status-register of our i2c-device */
static
inline
u32
saa7146_i2c_status
(
struct
saa7146_dev
*
dev
)
{
u32
iicsta
=
saa7146_read
(
dev
,
I2C_STATUS
);
/*
DEB_I2C(("status: 0x%08x\n",iicsta));
*/
return
iicsta
;
}
/* this function runs through the i2c-messages and prepares the data to be
sent through the saa7146. have a look at the specifications p. 122 ff
to understand this. it returns the number of u32s to send, or -1
in case of an error. */
static
int
saa7146_i2c_msg_prepare
(
const
struct
i2c_msg
m
[],
int
num
,
u32
*
op
)
{
int
h1
,
h2
;
int
i
,
j
,
addr
;
int
mem
=
0
,
op_count
=
0
;
/* first determine size of needed memory */
for
(
i
=
0
;
i
<
num
;
i
++
)
{
mem
+=
m
[
i
].
len
+
1
;
}
/* worst case: we need one u32 for three bytes to be send
plus one extra byte to address the device */
mem
=
1
+
((
mem
-
1
)
/
3
);
/* we assume that op points to a memory of at least SAA7146_I2C_MEM bytes
size. if we exceed this limit... */
if
(
(
4
*
mem
)
>
SAA7146_I2C_MEM
)
{
//fm DEB_I2C(("cannot prepare i2c-message.\n"));
return
-
ENOMEM
;
}
/* be careful: clear out the i2c-mem first */
memset
(
op
,
0
,
sizeof
(
u32
)
*
mem
);
/* loop through all messages */
for
(
i
=
0
;
i
<
num
;
i
++
)
{
/* insert the address of the i2c-slave.
note: we get 7 bit i2c-addresses,
so we have to perform a translation */
addr
=
(
m
[
i
].
addr
*
2
)
+
(
(
0
!=
(
m
[
i
].
flags
&
I2C_M_RD
))
?
1
:
0
);
h1
=
op_count
/
3
;
h2
=
op_count
%
3
;
op
[
h1
]
|=
(
(
u8
)
addr
<<
((
3
-
h2
)
*
8
));
op
[
h1
]
|=
(
SAA7146_I2C_START
<<
((
3
-
h2
)
*
2
));
op_count
++
;
/* loop through all bytes of message i */
for
(
j
=
0
;
j
<
m
[
i
].
len
;
j
++
)
{
/* insert the data bytes */
h1
=
op_count
/
3
;
h2
=
op_count
%
3
;
op
[
h1
]
|=
(
(
u32
)((
u8
)
m
[
i
].
buf
[
j
])
<<
((
3
-
h2
)
*
8
));
op
[
h1
]
|=
(
SAA7146_I2C_CONT
<<
((
3
-
h2
)
*
2
));
op_count
++
;
}
}
/* have a look at the last byte inserted:
if it was: ...CONT change it to ...STOP */
h1
=
(
op_count
-
1
)
/
3
;
h2
=
(
op_count
-
1
)
%
3
;
if
(
SAA7146_I2C_CONT
==
(
0x3
&
(
op
[
h1
]
>>
((
3
-
h2
)
*
2
)))
)
{
op
[
h1
]
&=
~
(
0x2
<<
((
3
-
h2
)
*
2
));
op
[
h1
]
|=
(
SAA7146_I2C_STOP
<<
((
3
-
h2
)
*
2
));
}
/* return the number of u32s to send */
return
mem
;
}
/* this functions loops through all i2c-messages. normally, it should determine
which bytes were read through the adapter and write them back to the corresponding
i2c-message. but instead, we simply write back all bytes.
fixme: this could be improved. */
static
int
saa7146_i2c_msg_cleanup
(
const
struct
i2c_msg
m
[],
int
num
,
u32
*
op
)
{
int
i
,
j
;
int
op_count
=
0
;
/* loop through all messages */
for
(
i
=
0
;
i
<
num
;
i
++
)
{
op_count
++
;
/* loop throgh all bytes of message i */
for
(
j
=
0
;
j
<
m
[
i
].
len
;
j
++
)
{
/* write back all bytes that could have been read */
m
[
i
].
buf
[
j
]
=
(
op
[
op_count
/
3
]
>>
((
3
-
(
op_count
%
3
))
*
8
));
op_count
++
;
}
}
return
0
;
}
/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */
static
int
saa7146_i2c_reset
(
struct
saa7146_dev
*
dev
)
{
/* get current status */
u32
status
=
saa7146_i2c_status
(
dev
);
/* clear registers for sure */
saa7146_write
(
dev
,
I2C_STATUS
,
dev
->
i2c_bitrate
);
saa7146_write
(
dev
,
I2C_TRANSFER
,
0
);
/* check if any operation is still in progress */
if
(
0
!=
(
status
&
SAA7146_I2C_BUSY
)
)
{
/* yes, kill ongoing operation */
DEB_I2C
((
"busy_state detected.
\n
"
));
/* set "ABORT-OPERATION"-bit (bit 7)*/
saa7146_write
(
dev
,
I2C_STATUS
,
(
dev
->
i2c_bitrate
|
MASK_07
));
saa7146_write
(
dev
,
MC2
,
(
MASK_00
|
MASK_16
));
my_wait
(
dev
,
SAA7146_I2C_DELAY
);
/* clear all error-bits pending; this is needed because p.123, note 1 */
saa7146_write
(
dev
,
I2C_STATUS
,
dev
->
i2c_bitrate
);
saa7146_write
(
dev
,
MC2
,
(
MASK_00
|
MASK_16
));
my_wait
(
dev
,
SAA7146_I2C_DELAY
);
}
/* check if any error is (still) present. (this can be necessary because p.123, note 1) */
status
=
saa7146_i2c_status
(
dev
);
if
(
dev
->
i2c_bitrate
!=
status
)
{
DEB_I2C
((
"error_state detected. status:0x%08x
\n
"
,
status
));
/* Repeat the abort operation. This seems to be necessary
after serious protocol errors caused by e.g. the SAA7740 */
saa7146_write
(
dev
,
I2C_STATUS
,
(
dev
->
i2c_bitrate
|
MASK_07
));
saa7146_write
(
dev
,
MC2
,
(
MASK_00
|
MASK_16
));
my_wait
(
dev
,
SAA7146_I2C_DELAY
);
/* clear all error-bits pending */
saa7146_write
(
dev
,
I2C_STATUS
,
dev
->
i2c_bitrate
);
saa7146_write
(
dev
,
MC2
,
(
MASK_00
|
MASK_16
));
my_wait
(
dev
,
SAA7146_I2C_DELAY
);
/* the data sheet says it might be necessary to clear the status
twice after an abort */
saa7146_write
(
dev
,
I2C_STATUS
,
dev
->
i2c_bitrate
);
saa7146_write
(
dev
,
MC2
,
(
MASK_00
|
MASK_16
));
my_wait
(
dev
,
SAA7146_I2C_DELAY
);
}
/* if any error is still present, a fatal error has occured ... */
status
=
saa7146_i2c_status
(
dev
);
if
(
dev
->
i2c_bitrate
!=
status
)
{
DEB_I2C
((
"fatal error. status:0x%08x
\n
"
,
status
));
return
-
1
;
}
return
0
;
}
/* this functions writes out the data-byte 'dword' to the i2c-device.
it returns 0 if ok, -1 if the transfer failed, -2 if the transfer
failed badly (e.g. address error) */
static
int
saa7146_i2c_writeout
(
struct
saa7146_dev
*
dev
,
u32
*
dword
)
{
u32
status
=
0
,
mc2
=
0
;
int
timeout
;
/* write out i2c-command */
DEB_I2C
((
"before: 0x%08x (status: 0x%08x), %d
\n
"
,
*
dword
,
saa7146_read
(
dev
,
I2C_STATUS
),
dev
->
i2c_op
));
if
(
0
!=
(
SAA7146_USE_I2C_IRQ
&
dev
->
ext
->
flags
))
{
saa7146_write
(
dev
,
I2C_STATUS
,
dev
->
i2c_bitrate
);
saa7146_write
(
dev
,
I2C_TRANSFER
,
*
dword
);
dev
->
i2c_op
=
1
;
IER_ENABLE
(
dev
,
MASK_16
|
MASK_17
);
saa7146_write
(
dev
,
MC2
,
(
MASK_00
|
MASK_16
));
wait_event_interruptible
(
dev
->
i2c_wq
,
dev
->
i2c_op
==
0
);
if
(
signal_pending
(
current
))
{
/* a signal arrived */
return
-
ERESTARTSYS
;
}
status
=
saa7146_read
(
dev
,
I2C_STATUS
);
}
else
{
saa7146_write
(
dev
,
I2C_STATUS
,
dev
->
i2c_bitrate
);
saa7146_write
(
dev
,
I2C_TRANSFER
,
*
dword
);
saa7146_write
(
dev
,
MC2
,
(
MASK_00
|
MASK_16
));
/* do not poll for i2c-status before upload is complete */
timeout
=
jiffies
+
HZ
/
100
+
1
;
/* 10ms */
while
(
1
)
{
mc2
=
(
saa7146_read
(
dev
,
MC2
)
&
0x1
);
if
(
0
!=
mc2
)
{
break
;
}
if
(
jiffies
>
timeout
)
{
printk
(
KERN_WARNING
"saa7146_i2c_writeout: timed out waiting for MC2
\n
"
);
return
-
EIO
;
}
}
/* wait until we get a transfer done or error */
timeout
=
jiffies
+
HZ
/
100
+
1
;
/* 10ms */
while
(
1
)
{
status
=
saa7146_i2c_status
(
dev
);
if
(
(
0x3
==
(
status
&
0x3
))
||
(
0
==
(
status
&
0x1
))
)
{
break
;
}
if
(
jiffies
>
timeout
)
{
/* this is normal when probing the bus
* (no answer from nonexisistant device...)
*/
DEB_I2C
((
"saa7146_i2c_writeout: timed out waiting for end of xfer
\n
"
));
return
-
EIO
;
}
my_wait
(
dev
,
1
);
}
}
/* give a detailed status report */
if
(
0
!=
(
status
&
SAA7146_I2C_ERR
))
{
if
(
0
!=
(
status
&
SAA7146_I2C_SPERR
)
)
{
DEB_I2C
((
"error due to invalid start/stop condition.
\n
"
));
}
if
(
0
!=
(
status
&
SAA7146_I2C_DTERR
)
)
{
DEB_I2C
((
"error in data transmission.
\n
"
));
}
if
(
0
!=
(
status
&
SAA7146_I2C_DRERR
)
)
{
DEB_I2C
((
"error when receiving data.
\n
"
));
}
if
(
0
!=
(
status
&
SAA7146_I2C_AL
)
)
{
DEB_I2C
((
"error because arbitration lost.
\n
"
));
}
/* we handle address-errors here */
if
(
0
!=
(
status
&
SAA7146_I2C_APERR
)
)
{
DEB_I2C
((
"error in address phase.
\n
"
));
return
-
EREMOTEIO
;
}
return
-
EIO
;
}
/* read back data, just in case we were reading ... */
*
dword
=
saa7146_read
(
dev
,
I2C_TRANSFER
);
DEB_I2C
((
"after: 0x%08x
\n
"
,
*
dword
));
return
0
;
}
int
saa7146_i2c_transfer
(
struct
saa7146_dev
*
dev
,
const
struct
i2c_msg
msgs
[],
int
num
,
int
retries
)
{
int
i
=
0
,
count
=
0
;
u32
*
buffer
=
dev
->
i2c_mem
;
int
err
=
0
;
int
address_err
=
0
;
if
(
down_interruptible
(
&
dev
->
i2c_lock
))
return
-
ERESTARTSYS
;
for
(
i
=
0
;
i
<
num
;
i
++
)
{
DEB_I2C
((
"msg:%d/%d
\n
"
,
i
+
1
,
num
));
}
/* prepare the message(s), get number of u32s to transfer */
count
=
saa7146_i2c_msg_prepare
(
msgs
,
num
,
buffer
);
if
(
0
>
count
)
{
err
=
-
1
;
goto
out
;
}
do
{
/* reset the i2c-device if necessary */
err
=
saa7146_i2c_reset
(
dev
);
if
(
0
>
err
)
{
DEB_I2C
((
"could not reset i2c-device.
\n
"
));
goto
out
;
}
/* write out the u32s one after another */
for
(
i
=
0
;
i
<
count
;
i
++
)
{
err
=
saa7146_i2c_writeout
(
dev
,
&
buffer
[
i
]
);
if
(
0
!=
err
)
{
/* this one is unsatisfying: some i2c slaves on some
dvb cards don't acknowledge correctly, so the saa7146
thinks that an address error occured. in that case, the
transaction should be retrying, even if an address error
occured. analog saa7146 based cards extensively rely on
i2c address probing, however, and address errors indicate that a
device is really *not* there. retrying in that case
increases the time the device needs to probe greatly, so
it should be avoided. because of the fact, that only
analog based cards use irq based i2c transactions (for dvb
cards, this screwes up other interrupt sources), we bail out
completely for analog cards after an address error and trust
the saa7146 address error detection. */
if
(
-
EREMOTEIO
==
err
)
{
if
(
0
!=
(
SAA7146_USE_I2C_IRQ
&
dev
->
ext
->
flags
))
{
goto
out
;
}
address_err
++
;
}
DEB_I2C
((
"error while sending message(s). starting again.
\n
"
));
break
;
}
}
if
(
0
==
err
)
{
err
=
num
;
break
;
}
/* delay a bit before retrying */
my_wait
(
dev
,
10
);
}
while
(
err
!=
num
&&
retries
--
);
/* if every retry had an address error, exit right away */
if
(
address_err
==
retries
)
{
goto
out
;
}
/* if any things had to be read, get the results */
if
(
0
!=
saa7146_i2c_msg_cleanup
(
msgs
,
num
,
buffer
))
{
DEB_I2C
((
"could not cleanup i2c-message.
\n
"
));
err
=
-
1
;
goto
out
;
}
/* return the number of delivered messages */
DEB_I2C
((
"transmission successful. (msg:%d).
\n
"
,
err
));
out:
/* another bug in revision 0: the i2c-registers get uploaded randomly by other
uploads, so we better clear them out before continueing */
if
(
0
==
dev
->
revision
)
{
u32
zero
=
0
;
saa7146_i2c_reset
(
dev
);
if
(
0
!=
saa7146_i2c_writeout
(
dev
,
&
zero
))
{
INFO
((
"revision 0 error. this should never happen.
\n
"
));
}
}
up
(
&
dev
->
i2c_lock
);
return
err
;
}
/* utility functions */
static
int
saa7146_i2c_xfer
(
struct
i2c_adapter
*
adapter
,
struct
i2c_msg
msg
[],
int
num
)
{
struct
saa7146_dev
*
dev
=
i2c_get_adapdata
(
adapter
);
DEB_I2C
((
"adapter: '%s'.
\n
"
,
adapter
->
dev
.
name
));
/* use helper function to transfer data */
return
saa7146_i2c_transfer
(
dev
,
msg
,
num
,
adapter
->
retries
);
}
/*****************************************************************************/
/* i2c-adapter helper functions */
#include <linux/i2c-id.h>
/* exported algorithm data */
static
struct
i2c_algorithm
saa7146_algo
=
{
.
name
=
"saa7146 i2c algorithm"
,
.
id
=
I2C_ALGO_SAA7146
,
.
master_xfer
=
saa7146_i2c_xfer
,
.
functionality
=
saa7146_i2c_func
,
};
int
saa7146_i2c_adapter_prepare
(
struct
saa7146_dev
*
dev
,
struct
i2c_adapter
*
i2c_adapter
,
u32
bitrate
)
{
DEB_EE
((
"bitrate: 0x%08x
\n
"
,
bitrate
));
dev
->
i2c_bitrate
=
bitrate
;
saa7146_i2c_reset
(
dev
);
if
(
NULL
!=
i2c_adapter
)
{
memset
(
i2c_adapter
,
0
,
sizeof
(
struct
i2c_adapter
));
strcpy
(
i2c_adapter
->
dev
.
name
,
dev
->
name
);
i2c_set_adapdata
(
i2c_adapter
,
dev
);
i2c_adapter
->
algo
=
&
saa7146_algo
;
i2c_adapter
->
algo_data
=
NULL
;
i2c_adapter
->
id
=
I2C_ALGO_SAA7146
;
i2c_adapter
->
timeout
=
SAA7146_I2C_TIMEOUT
;
i2c_adapter
->
retries
=
SAA7146_I2C_RETRIES
;
}
return
0
;
}
drivers/media/common/saa7146_vbi.c
0 → 100644
View file @
c8639e86
#include <media/saa7146_vv.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
#define KBUILD_MODNAME saa7146
#endif
static
int
vbi_pixel_to_capture
=
720
*
2
;
static
int
vbi_workaround
(
struct
saa7146_dev
*
dev
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
u32
*
cpu
;
dma_addr_t
dma_addr
;
int
i
,
index
;
DECLARE_WAITQUEUE
(
wait
,
current
);
DEB_VBI
((
"dev:%p
\n
"
,
dev
));
/* once again, a bug in the saa7146: the brs acquisition
is buggy and especially the BXO-counter does not work
as specified. there is this workaround, but please
don't let me explain it. ;-) */
cpu
=
pci_alloc_consistent
(
dev
->
pci
,
4096
,
&
dma_addr
);
if
(
NULL
==
cpu
)
return
-
ENOMEM
;
/* setup some basic programming, just for the workaround */
saa7146_write
(
dev
,
BASE_EVEN3
,
dma_addr
);
saa7146_write
(
dev
,
BASE_ODD3
,
dma_addr
+
vbi_pixel_to_capture
);
saa7146_write
(
dev
,
PROT_ADDR3
,
dma_addr
+
4096
);
saa7146_write
(
dev
,
PITCH3
,
vbi_pixel_to_capture
);
saa7146_write
(
dev
,
BASE_PAGE3
,
0x0
);
saa7146_write
(
dev
,
NUM_LINE_BYTE3
,
(
2
<<
16
)
|
((
vbi_pixel_to_capture
)
<<
0
));
saa7146_write
(
dev
,
MC2
,
MASK_04
|
MASK_20
);
/* we have to do the workaround two times to be sure that
everything is ok */
for
(
i
=
0
;
i
<
2
;
i
++
)
{
/* indicate to the irq handler that we do the workaround */
saa7146_write
(
dev
,
MC2
,
MASK_31
|
MASK_15
);
saa7146_write
(
dev
,
NUM_LINE_BYTE3
,
(
1
<<
16
)
|
(
2
<<
0
));
saa7146_write
(
dev
,
MC2
,
MASK_04
|
MASK_20
);
index
=
0
;
/* load brs-control register */
dev
->
rps1
[
index
++
]
=
CMD_WR_REG
|
(
1
<<
8
)
|
(
BRS_CTRL
/
4
);
/* BXO = 1h, BRS to outbound */
dev
->
rps1
[
index
++
]
=
0xc000008c
;
/* wait for vbi_a */
dev
->
rps1
[
index
++
]
=
CMD_PAUSE
|
MASK_10
;
/* upload brs */
dev
->
rps1
[
index
++
]
=
CMD_UPLOAD
|
MASK_08
;
/* load brs-control register */
dev
->
rps1
[
index
++
]
=
CMD_WR_REG
|
(
1
<<
8
)
|
(
BRS_CTRL
/
4
);
/* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */
dev
->
rps1
[
index
++
]
=
((
1728
-
(
vbi_pixel_to_capture
))
<<
7
)
|
MASK_19
;
/* wait for brs_done */
dev
->
rps1
[
index
++
]
=
CMD_PAUSE
|
MASK_08
;
/* upload brs */
dev
->
rps1
[
index
++
]
=
CMD_UPLOAD
|
MASK_08
;
/* load video-dma3 NumLines3 and NumBytes3 */
dev
->
rps1
[
index
++
]
=
CMD_WR_REG
|
(
1
<<
8
)
|
(
NUM_LINE_BYTE3
/
4
);
/* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */
dev
->
rps1
[
index
++
]
=
(
2
<<
16
)
|
(
vbi_pixel_to_capture
);
/* load brs-control register */
dev
->
rps1
[
index
++
]
=
CMD_WR_REG
|
(
1
<<
8
)
|
(
BRS_CTRL
/
4
);
/* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */
dev
->
rps1
[
index
++
]
=
(
540
<<
7
)
|
(
5
<<
19
);
// 5 == vbi_start
/* wait for brs_done */
dev
->
rps1
[
index
++
]
=
CMD_PAUSE
|
MASK_08
;
/* upload brs and video-dma3*/
dev
->
rps1
[
index
++
]
=
CMD_UPLOAD
|
MASK_08
|
MASK_04
;
/* load mc2 register: enable dma3 */
dev
->
rps1
[
index
++
]
=
CMD_WR_REG
|
(
1
<<
8
)
|
(
MC1
/
4
);
dev
->
rps1
[
index
++
]
=
MASK_20
|
MASK_04
;
/* generate interrupt */
dev
->
rps1
[
index
++
]
=
CMD_INTERRUPT
;
/* stop rps1 */
dev
->
rps1
[
index
++
]
=
CMD_STOP
;
/* enable rps1 irqs */
IER_ENABLE
(
dev
,
MASK_28
);
/* prepare to wait to be woken up by the irq-handler */
add_wait_queue
(
&
vv
->
vbi_wq
,
&
wait
);
current
->
state
=
TASK_INTERRUPTIBLE
;
/* start rps1 to enable workaround */
saa7146_write
(
dev
,
RPS_ADDR1
,
virt_to_bus
(
&
dev
->
rps1
[
0
]));
saa7146_write
(
dev
,
MC1
,
(
MASK_13
|
MASK_29
));
schedule
();
DEB_VBI
((
"brs bug workaround %d/1.
\n
"
,
i
));
remove_wait_queue
(
&
vv
->
vbi_wq
,
&
wait
);
current
->
state
=
TASK_RUNNING
;
/* disable rps1 irqs */
IER_DISABLE
(
dev
,
MASK_28
);
/* stop video-dma3 */
saa7146_write
(
dev
,
MC1
,
MASK_20
);
if
(
signal_pending
(
current
))
{
DEB_VBI
((
"aborted.
\n
"
));
/* stop rps1 for sure */
saa7146_write
(
dev
,
MC1
,
MASK_29
);
pci_free_consistent
(
dev
->
pci
,
4096
,
cpu
,
dma_addr
);
return
-
EINTR
;
}
}
pci_free_consistent
(
dev
->
pci
,
4096
,
cpu
,
dma_addr
);
return
0
;
}
void
saa7146_set_vbi_capture
(
struct
saa7146_dev
*
dev
,
struct
saa7146_buf
*
buf
,
struct
saa7146_buf
*
next
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
struct
saa7146_video_dma
vdma3
;
int
count
=
0
;
unsigned
long
e_wait
=
vv
->
current_hps_sync
==
SAA7146_HPS_SYNC_PORT_A
?
CMD_E_FID_A
:
CMD_E_FID_B
;
unsigned
long
o_wait
=
vv
->
current_hps_sync
==
SAA7146_HPS_SYNC_PORT_A
?
CMD_O_FID_A
:
CMD_O_FID_B
;
/*
vdma3.base_even = (u32)dev->ov_fb.base+2048*70;
vdma3.base_odd = (u32)dev->ov_fb.base;
vdma3.prot_addr = (u32)dev->ov_fb.base+2048*164;
vdma3.pitch = 2048;
vdma3.base_page = 0;
vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above!
*/
vdma3
.
base_even
=
buf
->
pt
[
2
].
offset
;
vdma3
.
base_odd
=
buf
->
pt
[
2
].
offset
+
16
*
vbi_pixel_to_capture
;
vdma3
.
prot_addr
=
buf
->
pt
[
2
].
offset
+
16
*
2
*
vbi_pixel_to_capture
;
vdma3
.
pitch
=
vbi_pixel_to_capture
;
vdma3
.
base_page
=
buf
->
pt
[
2
].
dma
|
ME1
;
vdma3
.
num_line_byte
=
(
16
<<
16
)
|
vbi_pixel_to_capture
;
saa7146_write_out_dma
(
dev
,
3
,
&
vdma3
);
/* write beginning of rps-program */
count
=
0
;
/* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */
/* we don't wait here for the first field anymore. this is different from the video
capture and might cause that the first buffer is only half filled (with only
one field). but since this is some sort of streaming data, this is not that negative.
but by doing this, we can use the whole engine from video-buf.c... */
/*
dev->rps1[ count++ ] = CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait;
dev->rps1[ count++ ] = CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait;
*/
/* set bit 1 */
dev
->
rps1
[
count
++
]
=
CMD_WR_REG
|
(
1
<<
8
)
|
(
MC2
/
4
);
dev
->
rps1
[
count
++
]
=
MASK_28
|
MASK_12
;
/* turn on video-dma3 */
dev
->
rps1
[
count
++
]
=
CMD_WR_REG_MASK
|
(
MC1
/
4
);
dev
->
rps1
[
count
++
]
=
MASK_04
|
MASK_20
;
/* => mask */
dev
->
rps1
[
count
++
]
=
MASK_04
|
MASK_20
;
/* => values */
/* wait for o_fid_a/b / e_fid_a/b toggle */
dev
->
rps1
[
count
++
]
=
CMD_PAUSE
|
o_wait
;
dev
->
rps1
[
count
++
]
=
CMD_PAUSE
|
e_wait
;
/* generate interrupt */
dev
->
rps1
[
count
++
]
=
CMD_INTERRUPT
;
/* stop */
dev
->
rps1
[
count
++
]
=
CMD_STOP
;
/* enable rps1 irqs */
IER_ENABLE
(
dev
,
MASK_28
);
/* write the address of the rps-program */
saa7146_write
(
dev
,
RPS_ADDR1
,
virt_to_bus
(
&
dev
->
rps1
[
0
]));
/* turn on rps */
saa7146_write
(
dev
,
MC1
,
(
MASK_13
|
MASK_29
));
}
static
int
buffer_activate
(
struct
saa7146_dev
*
dev
,
struct
saa7146_buf
*
buf
,
struct
saa7146_buf
*
next
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
buf
->
vb
.
state
=
STATE_ACTIVE
;
DEB_VBI
((
"dev:%p, buf:%p, next:%p
\n
"
,
dev
,
buf
,
next
));
saa7146_set_vbi_capture
(
dev
,
buf
,
next
);
mod_timer
(
&
vv
->
vbi_q
.
timeout
,
jiffies
+
BUFFER_TIMEOUT
);
return
0
;
}
static
int
buffer_prepare
(
struct
file
*
file
,
struct
videobuf_buffer
*
vb
,
enum
v4l2_field
field
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_buf
*
buf
=
(
struct
saa7146_buf
*
)
vb
;
int
err
=
0
;
int
lines
,
llength
,
size
;
lines
=
16
*
2
;
/* 2 fields */
llength
=
vbi_pixel_to_capture
;
size
=
lines
*
llength
;
DEB_VBI
((
"vb:%p
\n
"
,
vb
));
if
(
0
!=
buf
->
vb
.
baddr
&&
buf
->
vb
.
bsize
<
size
)
{
DEB_VBI
((
"size mismatch.
\n
"
));
return
-
EINVAL
;
}
if
(
buf
->
vb
.
size
!=
size
)
saa7146_dma_free
(
dev
,
buf
);
if
(
STATE_NEEDS_INIT
==
buf
->
vb
.
state
)
{
buf
->
vb
.
width
=
llength
;
buf
->
vb
.
height
=
lines
;
buf
->
vb
.
size
=
size
;
buf
->
vb
.
field
=
field
;
// FIXME: check this
saa7146_pgtable_free
(
dev
->
pci
,
&
buf
->
pt
[
2
]);
saa7146_pgtable_alloc
(
dev
->
pci
,
&
buf
->
pt
[
2
]);
err
=
videobuf_iolock
(
dev
->
pci
,
&
buf
->
vb
);
if
(
err
)
goto
oops
;
saa7146_pgtable_build_single
(
dev
->
pci
,
&
buf
->
pt
[
2
],
buf
->
vb
.
dma
.
sglist
,
buf
->
vb
.
dma
.
sglen
);
}
buf
->
vb
.
state
=
STATE_PREPARED
;
buf
->
activate
=
buffer_activate
;
return
0
;
oops:
DEB_VBI
((
"error out.
\n
"
));
saa7146_dma_free
(
dev
,
buf
);
return
err
;
}
static
int
buffer_setup
(
struct
file
*
file
,
unsigned
int
*
count
,
unsigned
int
*
size
)
{
int
llength
,
lines
;
lines
=
16
*
2
;
/* 2 fields */
llength
=
vbi_pixel_to_capture
;
*
size
=
lines
*
llength
;
*
count
=
2
;
DEB_VBI
((
"count:%d, size:%d
\n
"
,
*
count
,
*
size
));
return
0
;
}
static
void
buffer_queue
(
struct
file
*
file
,
struct
videobuf_buffer
*
vb
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
struct
saa7146_buf
*
buf
=
(
struct
saa7146_buf
*
)
vb
;
DEB_VBI
((
"vb:%p
\n
"
,
vb
));
saa7146_buffer_queue
(
dev
,
&
vv
->
vbi_q
,
buf
);
}
static
void
buffer_release
(
struct
file
*
file
,
struct
videobuf_buffer
*
vb
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_buf
*
buf
=
(
struct
saa7146_buf
*
)
vb
;
DEB_VBI
((
"vb:%p
\n
"
,
vb
));
saa7146_dma_free
(
dev
,
buf
);
}
static
struct
videobuf_queue_ops
vbi_qops
=
{
.
buf_setup
=
buffer_setup
,
.
buf_prepare
=
buffer_prepare
,
.
buf_queue
=
buffer_queue
,
.
buf_release
=
buffer_release
,
};
/* ------------------------------------------------------------------ */
static
void
vbi_stop
(
struct
saa7146_fh
*
fh
)
{
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
unsigned
long
flags
;
DEB_VBI
((
"dev:%p, fh:%p
\n
"
,
dev
,
fh
));
spin_lock_irqsave
(
&
dev
->
slock
,
flags
);
/* disable rps1 */
saa7146_write
(
dev
,
MC1
,
MASK_29
);
/* disable rps1 irqs */
IER_DISABLE
(
dev
,
MASK_28
);
/* shut down dma 3 transfers */
saa7146_write
(
dev
,
MC1
,
MASK_20
);
vv
->
vbi_streaming
=
NULL
;
spin_unlock_irqrestore
(
&
dev
->
slock
,
flags
);
}
static
void
vbi_read_timeout
(
unsigned
long
data
)
{
struct
saa7146_fh
*
fh
=
(
struct
saa7146_fh
*
)
data
;
struct
saa7146_dev
*
dev
=
fh
->
dev
;
DEB_VBI
((
"dev:%p, fh:%p
\n
"
,
dev
,
fh
));
vbi_stop
(
fh
);
}
static
void
vbi_init
(
struct
saa7146_dev
*
dev
,
struct
saa7146_vv
*
vv
)
{
DEB_VBI
((
"dev:%p
\n
"
,
dev
));
INIT_LIST_HEAD
(
&
vv
->
vbi_q
.
queue
);
init_timer
(
&
vv
->
vbi_q
.
timeout
);
vv
->
vbi_q
.
timeout
.
function
=
saa7146_buffer_timeout
;
vv
->
vbi_q
.
timeout
.
data
=
(
unsigned
long
)(
&
vv
->
vbi_q
);
vv
->
vbi_q
.
dev
=
dev
;
init_waitqueue_head
(
&
vv
->
vbi_wq
);
}
static
void
vbi_open
(
struct
saa7146_dev
*
dev
,
struct
saa7146_fh
*
fh
)
{
DEB_VBI
((
"dev:%p, fh:%p
\n
"
,
dev
,
fh
));
memset
(
&
fh
->
vbi_fmt
,
0
,
sizeof
(
fh
->
vbi_fmt
));
fh
->
vbi_fmt
.
sampling_rate
=
27000000
;
fh
->
vbi_fmt
.
offset
=
248
;
/* todo */
fh
->
vbi_fmt
.
samples_per_line
=
vbi_pixel_to_capture
;
fh
->
vbi_fmt
.
sample_format
=
V4L2_PIX_FMT_GREY
;
/* fixme: this only works for PAL */
fh
->
vbi_fmt
.
start
[
0
]
=
5
;
fh
->
vbi_fmt
.
count
[
0
]
=
16
;
fh
->
vbi_fmt
.
start
[
1
]
=
312
;
fh
->
vbi_fmt
.
count
[
1
]
=
16
;
videobuf_queue_init
(
&
fh
->
vbi_q
,
&
vbi_qops
,
dev
->
pci
,
&
dev
->
slock
,
V4L2_BUF_TYPE_VBI_CAPTURE
,
V4L2_FIELD_SEQ_TB
,
// FIXME: does this really work?
sizeof
(
struct
saa7146_buf
));
init_MUTEX
(
&
fh
->
vbi_q
.
lock
);
init_timer
(
&
fh
->
vbi_read_timeout
);
fh
->
vbi_read_timeout
.
function
=
vbi_read_timeout
;
fh
->
vbi_read_timeout
.
data
=
(
unsigned
long
)
fh
;
vbi_workaround
(
dev
);
}
static
void
vbi_close
(
struct
saa7146_dev
*
dev
,
struct
saa7146_fh
*
fh
,
struct
file
*
file
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
DEB_VBI
((
"dev:%p, fh:%p
\n
"
,
dev
,
fh
));
if
(
fh
==
vv
->
vbi_streaming
)
{
vbi_stop
(
fh
);
}
}
static
void
vbi_irq_done
(
struct
saa7146_dev
*
dev
,
unsigned
long
status
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
spin_lock
(
&
dev
->
slock
);
if
(
vv
->
vbi_q
.
curr
)
{
DEB_VBI
((
"dev:%p, curr:%p
\n
"
,
dev
,
vv
->
vbi_q
.
curr
));
/* this must be += 2, one count for each field */
vv
->
vbi_fieldcount
+=
2
;
vv
->
vbi_q
.
curr
->
vb
.
field_count
=
vv
->
vbi_fieldcount
;
saa7146_buffer_finish
(
dev
,
&
vv
->
vbi_q
,
STATE_DONE
);
}
else
{
DEB_VBI
((
"dev:%p
\n
"
,
dev
));
}
saa7146_buffer_next
(
dev
,
&
vv
->
vbi_q
,
1
);
spin_unlock
(
&
dev
->
slock
);
}
static
ssize_t
vbi_read
(
struct
file
*
file
,
char
*
data
,
size_t
count
,
loff_t
*
ppos
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
ssize_t
ret
=
0
;
DEB_VBI
((
"dev:%p, fh:%p
\n
"
,
dev
,
fh
));
if
(
NULL
==
vv
->
vbi_streaming
)
{
// fixme: check if dma3 is available
// fixme: activate vbi engine here if necessary. (really?)
vv
->
vbi_streaming
=
fh
;
}
if
(
fh
!=
vv
->
vbi_streaming
)
{
DEB_VBI
((
"open %p is already using vbi capture."
,
vv
->
vbi_streaming
));
return
-
EBUSY
;
}
mod_timer
(
&
fh
->
vbi_read_timeout
,
jiffies
+
BUFFER_TIMEOUT
);
ret
=
videobuf_read_stream
(
file
,
&
fh
->
vbi_q
,
data
,
count
,
ppos
,
1
);
/*
printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3));
printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3));
printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3));
printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3));
printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3));
printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3));
printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL));
*/
return
ret
;
}
struct
saa7146_use_ops
saa7146_vbi_uops
=
{
.
init
=
vbi_init
,
.
open
=
vbi_open
,
.
release
=
vbi_close
,
.
irq_done
=
vbi_irq_done
,
.
read
=
vbi_read
,
};
EXPORT_SYMBOL_GPL
(
saa7146_vbi_uops
);
drivers/media/common/saa7146_video.c
0 → 100644
View file @
c8639e86
#include <media/saa7146_vv.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
#define KBUILD_MODNAME saa7146
#endif
static
int
memory
=
32
;
MODULE_PARM
(
memory
,
"i"
);
MODULE_PARM_DESC
(
memory
,
"maximum memory usage for capture buffers (default: 32Mb)"
);
/* format descriptions for capture and preview */
static
struct
saa7146_format
formats
[]
=
{
{
.
name
=
"RGB-8 (3-3-2)"
,
.
pixelformat
=
V4L2_PIX_FMT_RGB332
,
.
trans
=
RGB08_COMPOSED
,
.
depth
=
8
,
},
{
.
name
=
"RGB-16 (5/B-6/G-5/R)"
,
/* really? */
.
pixelformat
=
V4L2_PIX_FMT_RGB565
,
.
trans
=
RGB16_COMPOSED
,
.
depth
=
16
,
},
{
.
name
=
"RGB-24 (B-G-R)"
,
.
pixelformat
=
V4L2_PIX_FMT_BGR24
,
.
trans
=
RGB24_COMPOSED
,
.
depth
=
24
,
},
{
.
name
=
"RGB-32 (B-G-R)"
,
.
pixelformat
=
V4L2_PIX_FMT_BGR32
,
.
trans
=
RGB32_COMPOSED
,
.
depth
=
32
,
},
{
.
name
=
"Greyscale-8"
,
.
pixelformat
=
V4L2_PIX_FMT_GREY
,
.
trans
=
Y8
,
.
depth
=
8
,
},
{
.
name
=
"YUV 4:2:2 planar (Y-Cb-Cr)"
,
.
pixelformat
=
V4L2_PIX_FMT_YUV422P
,
.
trans
=
YUV422_DECOMPOSED
,
.
depth
=
16
,
.
swap
=
1
,
},
{
.
name
=
"YVU 4:2:0 planar (Y-Cb-Cr)"
,
.
pixelformat
=
V4L2_PIX_FMT_YVU420
,
.
trans
=
YUV420_DECOMPOSED
,
.
depth
=
12
,
.
swap
=
1
,
},
{
.
name
=
"YUV 4:2:0 planar (Y-Cb-Cr)"
,
.
pixelformat
=
V4L2_PIX_FMT_YUV420
,
.
trans
=
YUV420_DECOMPOSED
,
.
depth
=
12
,
},
{
.
name
=
"YUV 4:2:2 (U-Y-V-Y)"
,
.
pixelformat
=
V4L2_PIX_FMT_UYVY
,
.
trans
=
YUV422_COMPOSED
,
.
depth
=
16
,
}
};
/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps.
due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped
(like V4L2_PIX_FMT_YUYV) ... 8-( */
static
int
NUM_FORMATS
=
sizeof
(
formats
)
/
sizeof
(
struct
saa7146_format
);
struct
saa7146_format
*
format_by_fourcc
(
struct
saa7146_dev
*
dev
,
int
fourcc
)
{
int
i
,
j
=
NUM_FORMATS
;
for
(
i
=
0
;
i
<
j
;
i
++
)
{
if
(
formats
[
i
].
pixelformat
==
fourcc
)
{
return
formats
+
i
;
}
}
DEB_D
((
"unknown pixelformat:'%4.4s'
\n
"
,(
char
*
)
&
fourcc
));
return
NULL
;
}
static
int
g_fmt
(
struct
saa7146_fh
*
fh
,
struct
v4l2_format
*
f
)
{
struct
saa7146_dev
*
dev
=
fh
->
dev
;
DEB_EE
((
"dev:%p, fh:%p
\n
"
,
dev
,
fh
));
switch
(
f
->
type
)
{
case
V4L2_BUF_TYPE_VIDEO_CAPTURE
:
f
->
fmt
.
pix
=
fh
->
video_fmt
;
return
0
;
case
V4L2_BUF_TYPE_VIDEO_OVERLAY
:
f
->
fmt
.
win
=
fh
->
ov
.
win
;
return
0
;
case
V4L2_BUF_TYPE_VBI_CAPTURE
:
{
f
->
fmt
.
vbi
=
fh
->
vbi_fmt
;
return
0
;
}
default:
DEB_D
((
"invalid format type '%d'.
\n
"
,
f
->
type
));
return
-
EINVAL
;
}
}
static
int
try_win
(
struct
saa7146_dev
*
dev
,
struct
v4l2_window
*
win
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
enum
v4l2_field
field
;
int
maxw
,
maxh
;
DEB_EE
((
"dev:%p
\n
"
,
dev
));
if
(
NULL
==
vv
->
ov_fb
.
base
)
{
DEB_D
((
"no fb base set.
\n
"
));
return
-
EINVAL
;
}
if
(
NULL
==
vv
->
ov_fmt
)
{
DEB_D
((
"no fb fmt set.
\n
"
));
return
-
EINVAL
;
}
if
(
win
->
w
.
width
<
64
||
win
->
w
.
height
<
64
)
{
DEB_D
((
"min width/height. (%d,%d)
\n
"
,
win
->
w
.
width
,
win
->
w
.
height
));
return
-
EINVAL
;
}
if
(
win
->
clipcount
>
16
)
{
DEB_D
((
"clipcount too big.
\n
"
));
return
-
EINVAL
;
}
field
=
win
->
field
;
maxw
=
vv
->
standard
->
h_max_out
;
maxh
=
vv
->
standard
->
v_max_out
;
if
(
V4L2_FIELD_ANY
==
field
)
{
field
=
(
win
->
w
.
height
>
maxh
/
2
)
?
V4L2_FIELD_INTERLACED
:
V4L2_FIELD_TOP
;
}
switch
(
field
)
{
case
V4L2_FIELD_TOP
:
case
V4L2_FIELD_BOTTOM
:
maxh
=
maxh
/
2
;
break
;
case
V4L2_FIELD_INTERLACED
:
break
;
default:
{
DEB_D
((
"no known field mode '%d'.
\n
"
,
field
));
return
-
EINVAL
;
}
}
win
->
field
=
field
;
if
(
win
->
w
.
width
>
maxw
)
win
->
w
.
width
=
maxw
;
if
(
win
->
w
.
height
>
maxh
)
win
->
w
.
height
=
maxh
;
return
0
;
}
static
int
try_fmt
(
struct
saa7146_fh
*
fh
,
struct
v4l2_format
*
f
)
{
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
int
err
;
switch
(
f
->
type
)
{
case
V4L2_BUF_TYPE_VIDEO_CAPTURE
:
{
struct
saa7146_format
*
fmt
;
enum
v4l2_field
field
;
int
maxw
,
maxh
;
DEB_EE
((
"V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p
\n
"
,
dev
,
fh
));
fmt
=
format_by_fourcc
(
dev
,
f
->
fmt
.
pix
.
pixelformat
);
if
(
NULL
==
fmt
)
{
return
-
EINVAL
;
}
field
=
f
->
fmt
.
pix
.
field
;
maxw
=
vv
->
standard
->
h_max_out
;
maxh
=
vv
->
standard
->
v_max_out
;
if
(
V4L2_FIELD_ANY
==
field
)
{
field
=
(
f
->
fmt
.
pix
.
height
>
maxh
/
2
)
?
V4L2_FIELD_INTERLACED
:
V4L2_FIELD_BOTTOM
;
}
switch
(
field
)
{
case
V4L2_FIELD_TOP
:
case
V4L2_FIELD_BOTTOM
:
maxh
=
maxh
/
2
;
break
;
case
V4L2_FIELD_INTERLACED
:
break
;
default:
{
DEB_D
((
"no known field mode '%d'.
\n
"
,
field
));
return
-
EINVAL
;
}
}
f
->
fmt
.
pix
.
field
=
field
;
if
(
f
->
fmt
.
pix
.
width
>
maxw
)
f
->
fmt
.
pix
.
width
=
maxw
;
if
(
f
->
fmt
.
pix
.
height
>
maxh
)
f
->
fmt
.
pix
.
height
=
maxh
;
f
->
fmt
.
pix
.
sizeimage
=
(
f
->
fmt
.
pix
.
width
*
f
->
fmt
.
pix
.
height
*
fmt
->
depth
)
/
8
;
return
0
;
}
case
V4L2_BUF_TYPE_VIDEO_OVERLAY
:
DEB_EE
((
"V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p
\n
"
,
dev
,
fh
));
err
=
try_win
(
dev
,
&
f
->
fmt
.
win
);
if
(
0
!=
err
)
{
return
err
;
}
return
0
;
default:
DEB_EE
((
"unknown format type '%d'
\n
"
,
f
->
type
));
return
-
EINVAL
;
}
}
static
int
start_preview
(
struct
saa7146_fh
*
fh
)
{
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
int
err
=
0
;
DEB_EE
((
"dev:%p, fh:%p
\n
"
,
dev
,
fh
));
/* check if we have overlay informations */
if
(
NULL
==
fh
->
ov
.
fh
)
{
DEB_D
((
"not overlay data available. try S_FMT first.
\n
"
));
return
-
EAGAIN
;
}
/* check if overlay is running */
if
(
0
!=
vv
->
ov_data
)
{
if
(
fh
!=
vv
->
ov_data
->
fh
)
{
DEB_D
((
"overlay is running in another open.
\n
"
));
return
-
EAGAIN
;
}
DEB_D
((
"overlay is already active.
\n
"
));
return
0
;
}
if
(
0
!=
vv
->
streaming
)
{
DEB_D
((
"streaming capture is active.
\n
"
));
return
-
EBUSY
;
}
err
=
try_win
(
dev
,
&
fh
->
ov
.
win
);
if
(
0
!=
err
)
{
return
err
;
}
vv
->
ov_data
=
&
fh
->
ov
;
DEB_D
((
"%dx%d+%d+%d %s field=%s
\n
"
,
fh
->
ov
.
win
.
w
.
width
,
fh
->
ov
.
win
.
w
.
height
,
fh
->
ov
.
win
.
w
.
left
,
fh
->
ov
.
win
.
w
.
top
,
vv
->
ov_fmt
->
name
,
v4l2_field_names
[
fh
->
ov
.
win
.
field
]));
saa7146_set_overlay
(
dev
,
fh
,
1
);
return
0
;
}
static
int
stop_preview
(
struct
saa7146_fh
*
fh
)
{
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
DEB_EE
((
"saa7146.o: stop_preview()
\n
"
));
/* check if overlay is running */
if
(
0
==
vv
->
ov_data
)
{
DEB_D
((
"overlay is not active.
\n
"
));
return
0
;
}
if
(
fh
!=
vv
->
ov_data
->
fh
)
{
DEB_D
((
"overlay is active, but for another open.
\n
"
));
return
-
EBUSY
;
}
saa7146_set_overlay
(
dev
,
fh
,
0
);
vv
->
ov_data
=
NULL
;
return
0
;
}
static
int
s_fmt
(
struct
saa7146_fh
*
fh
,
struct
v4l2_format
*
f
)
{
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
unsigned
long
flags
;
int
err
;
switch
(
f
->
type
)
{
case
V4L2_BUF_TYPE_VIDEO_CAPTURE
:
DEB_EE
((
"V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p
\n
"
,
dev
,
fh
));
if
(
fh
==
vv
->
streaming
)
{
DEB_EE
((
"streaming capture is active"
));
return
-
EAGAIN
;
}
err
=
try_fmt
(
fh
,
f
);
if
(
0
!=
err
)
return
err
;
fh
->
video_fmt
=
f
->
fmt
.
pix
;
DEB_EE
((
"set to pixelformat '%4.4s'
\n
"
,(
char
*
)
&
fh
->
video_fmt
.
pixelformat
));
return
0
;
case
V4L2_BUF_TYPE_VIDEO_OVERLAY
:
DEB_EE
((
"V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p
\n
"
,
dev
,
fh
));
err
=
try_win
(
dev
,
&
f
->
fmt
.
win
);
if
(
0
!=
err
)
return
err
;
down
(
&
dev
->
lock
);
fh
->
ov
.
win
=
f
->
fmt
.
win
;
fh
->
ov
.
nclips
=
f
->
fmt
.
win
.
clipcount
;
if
(
fh
->
ov
.
nclips
>
16
)
fh
->
ov
.
nclips
=
16
;
if
(
copy_from_user
(
fh
->
ov
.
clips
,
f
->
fmt
.
win
.
clips
,
sizeof
(
struct
v4l2_clip
)
*
fh
->
ov
.
nclips
))
{
up
(
&
dev
->
lock
);
return
-
EFAULT
;
}
/* fh->ov.fh is used to indicate that we have valid overlay informations, too */
fh
->
ov
.
fh
=
fh
;
/* check if we have an active overlay */
if
(
vv
->
ov_data
!=
NULL
)
{
if
(
fh
==
vv
->
ov_data
->
fh
)
{
spin_lock_irqsave
(
&
dev
->
slock
,
flags
);
stop_preview
(
fh
);
start_preview
(
fh
);
spin_unlock_irqrestore
(
&
dev
->
slock
,
flags
);
}
}
up
(
&
dev
->
lock
);
return
0
;
default:
DEB_D
((
"unknown format type '%d'
\n
"
,
f
->
type
));
return
-
EINVAL
;
}
}
/********************************************************************************/
/* device controls */
static
struct
v4l2_queryctrl
controls
[]
=
{
{
id:
V4L2_CID_BRIGHTNESS
,
name:
"Brightness"
,
minimum:
0
,
maximum:
255
,
step:
1
,
default_value:
128
,
type:
V4L2_CTRL_TYPE_INTEGER
,
},{
id:
V4L2_CID_CONTRAST
,
name:
"Contrast"
,
minimum:
0
,
maximum:
127
,
step:
1
,
default_value:
64
,
type:
V4L2_CTRL_TYPE_INTEGER
,
},{
id:
V4L2_CID_SATURATION
,
name:
"Saturation"
,
minimum:
0
,
maximum:
127
,
step:
1
,
default_value:
64
,
type:
V4L2_CTRL_TYPE_INTEGER
,
},{
id:
V4L2_CID_VFLIP
,
name:
"Vertical flip"
,
minimum:
0
,
maximum:
1
,
type:
V4L2_CTRL_TYPE_BOOLEAN
,
},{
id:
V4L2_CID_HFLIP
,
name:
"Horizontal flip"
,
minimum:
0
,
maximum:
1
,
type:
V4L2_CTRL_TYPE_BOOLEAN
,
},
};
static
int
NUM_CONTROLS
=
sizeof
(
controls
)
/
sizeof
(
struct
v4l2_queryctrl
);
#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0)
static
struct
v4l2_queryctrl
*
ctrl_by_id
(
int
id
)
{
int
i
;
for
(
i
=
0
;
i
<
NUM_CONTROLS
;
i
++
)
if
(
controls
[
i
].
id
==
id
)
return
controls
+
i
;
return
NULL
;
}
static
int
get_control
(
struct
saa7146_fh
*
fh
,
struct
v4l2_control
*
c
)
{
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
const
struct
v4l2_queryctrl
*
ctrl
;
u32
value
=
0
;
ctrl
=
ctrl_by_id
(
c
->
id
);
if
(
NULL
==
ctrl
)
return
-
EINVAL
;
switch
(
c
->
id
)
{
case
V4L2_CID_BRIGHTNESS
:
value
=
saa7146_read
(
dev
,
BCS_CTRL
);
c
->
value
=
0xff
&
(
value
>>
24
);
break
;
case
V4L2_CID_CONTRAST
:
value
=
saa7146_read
(
dev
,
BCS_CTRL
);
c
->
value
=
0x7f
&
(
value
>>
16
);
break
;
case
V4L2_CID_SATURATION
:
value
=
saa7146_read
(
dev
,
BCS_CTRL
);
c
->
value
=
0x7f
&
(
value
>>
0
);
break
;
case
V4L2_CID_VFLIP
:
c
->
value
=
vv
->
vflip
;
break
;
case
V4L2_CID_HFLIP
:
c
->
value
=
vv
->
hflip
;
break
;
default:
return
-
EINVAL
;
}
return
0
;
}
static
int
set_control
(
struct
saa7146_fh
*
fh
,
struct
v4l2_control
*
c
)
{
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
const
struct
v4l2_queryctrl
*
ctrl
;
unsigned
long
flags
;
int
restart_overlay
=
0
;
ctrl
=
ctrl_by_id
(
c
->
id
);
if
(
NULL
==
ctrl
)
{
DEB_D
((
"unknown control %d
\n
"
,
c
->
id
));
return
-
EINVAL
;
}
switch
(
ctrl
->
type
)
{
case
V4L2_CTRL_TYPE_BOOLEAN
:
case
V4L2_CTRL_TYPE_MENU
:
case
V4L2_CTRL_TYPE_INTEGER
:
if
(
c
->
value
<
ctrl
->
minimum
)
c
->
value
=
ctrl
->
minimum
;
if
(
c
->
value
>
ctrl
->
maximum
)
c
->
value
=
ctrl
->
maximum
;
break
;
default:
/* nothing */
;
};
switch
(
c
->
id
)
{
case
V4L2_CID_BRIGHTNESS
:
{
u32
value
=
saa7146_read
(
dev
,
BCS_CTRL
);
value
&=
0x00ffffff
;
value
|=
(
c
->
value
<<
24
);
saa7146_write
(
dev
,
BCS_CTRL
,
value
);
saa7146_write
(
dev
,
MC2
,
MASK_22
|
MASK_06
);
break
;
}
case
V4L2_CID_CONTRAST
:
{
u32
value
=
saa7146_read
(
dev
,
BCS_CTRL
);
value
&=
0xff00ffff
;
value
|=
(
c
->
value
<<
16
);
saa7146_write
(
dev
,
BCS_CTRL
,
value
);
saa7146_write
(
dev
,
MC2
,
MASK_22
|
MASK_06
);
break
;
}
case
V4L2_CID_SATURATION
:
{
u32
value
=
saa7146_read
(
dev
,
BCS_CTRL
);
value
&=
0xffffff00
;
value
|=
(
c
->
value
<<
0
);
saa7146_write
(
dev
,
BCS_CTRL
,
value
);
saa7146_write
(
dev
,
MC2
,
MASK_22
|
MASK_06
);
break
;
}
case
V4L2_CID_HFLIP
:
/* fixme: we can supfhrt changing VFLIP and HFLIP here... */
if
(
0
!=
vv
->
streaming
)
{
DEB_D
((
"V4L2_CID_HFLIP while active capture.
\n
"
));
return
-
EINVAL
;
}
vv
->
hflip
=
c
->
value
;
restart_overlay
=
1
;
break
;
case
V4L2_CID_VFLIP
:
if
(
0
!=
vv
->
streaming
)
{
DEB_D
((
"V4L2_CID_VFLIP while active capture.
\n
"
));
return
-
EINVAL
;
}
vv
->
vflip
=
c
->
value
;
restart_overlay
=
1
;
break
;
default:
{
return
-
EINVAL
;
}
}
if
(
0
!=
restart_overlay
)
{
if
(
0
!=
vv
->
ov_data
)
{
if
(
fh
==
vv
->
ov_data
->
fh
)
{
spin_lock_irqsave
(
&
dev
->
slock
,
flags
);
stop_preview
(
fh
);
start_preview
(
fh
);
spin_unlock_irqrestore
(
&
dev
->
slock
,
flags
);
}
}
}
return
0
;
}
/********************************************************************************/
/* common pagetable functions */
static
int
saa7146_pgtable_build
(
struct
saa7146_dev
*
dev
,
struct
saa7146_buf
*
buf
)
{
struct
pci_dev
*
pci
=
dev
->
pci
;
struct
scatterlist
*
list
=
buf
->
vb
.
dma
.
sglist
;
int
length
=
buf
->
vb
.
dma
.
sglen
;
struct
saa7146_format
*
sfmt
=
format_by_fourcc
(
dev
,
buf
->
fmt
->
pixelformat
);
DEB_EE
((
"dev:%p, buf:%p
\n
"
,
dev
,
buf
));
if
(
0
!=
IS_PLANAR
(
sfmt
->
trans
))
{
struct
saa7146_pgtable
*
pt1
=
&
buf
->
pt
[
0
];
struct
saa7146_pgtable
*
pt2
=
&
buf
->
pt
[
1
];
struct
saa7146_pgtable
*
pt3
=
&
buf
->
pt
[
2
];
u32
*
ptr1
,
*
ptr2
,
*
ptr3
;
u32
fill
;
int
size
=
buf
->
fmt
->
width
*
buf
->
fmt
->
height
;
int
i
,
p
,
m1
,
m2
,
m3
,
o1
,
o2
;
switch
(
sfmt
->
depth
)
{
case
12
:
{
/* create some offsets inside the page table */
m1
=
((
size
+
PAGE_SIZE
)
/
PAGE_SIZE
)
-
1
;
m2
=
((
size
+
(
size
/
4
)
+
PAGE_SIZE
)
/
PAGE_SIZE
)
-
1
;
m3
=
((
size
+
(
size
/
2
)
+
PAGE_SIZE
)
/
PAGE_SIZE
)
-
1
;
o1
=
size
%
PAGE_SIZE
;
o2
=
(
size
+
(
size
/
4
))
%
PAGE_SIZE
;
printk
(
"size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d
\n
"
,
size
,
m1
,
m2
,
m3
,
o1
,
o2
);
break
;
}
case
16
:
{
/* create some offsets inside the page table */
m1
=
((
size
+
PAGE_SIZE
)
/
PAGE_SIZE
)
-
1
;
m2
=
((
size
+
(
size
/
2
)
+
PAGE_SIZE
)
/
PAGE_SIZE
)
-
1
;
m3
=
((
2
*
size
+
PAGE_SIZE
)
/
PAGE_SIZE
)
-
1
;
o1
=
size
%
PAGE_SIZE
;
o2
=
(
size
+
(
size
/
2
))
%
PAGE_SIZE
;
printk
(
"size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d
\n
"
,
size
,
m1
,
m2
,
m3
,
o1
,
o2
);
break
;
}
default:
{
return
-
1
;
}
}
ptr1
=
pt1
->
cpu
;
ptr2
=
pt2
->
cpu
;
ptr3
=
pt3
->
cpu
;
/* walk all pages, copy all page addresses to ptr1 */
for
(
i
=
0
;
i
<
length
;
i
++
,
list
++
)
{
for
(
p
=
0
;
p
*
4096
<
list
->
length
;
p
++
,
ptr1
++
)
{
*
ptr1
=
sg_dma_address
(
list
)
-
list
->
offset
;
}
}
/*
ptr1 = pt1->cpu;
for(j=0;j<40;j++) {
printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
}
*/
/* if we have a user buffer, the first page may not be
aligned to a page boundary. */
pt1
->
offset
=
buf
->
vb
.
dma
.
sglist
->
offset
;
pt2
->
offset
=
pt1
->
offset
+
o1
;
pt3
->
offset
=
pt1
->
offset
+
o2
;
/* create video-dma2 page table */
ptr1
=
pt1
->
cpu
;
for
(
i
=
m1
;
i
<=
m2
;
i
++
,
ptr2
++
)
{
*
ptr2
=
ptr1
[
i
];
}
fill
=
*
(
ptr2
-
1
);
for
(;
i
<
1024
;
i
++
,
ptr2
++
)
{
*
ptr2
=
fill
;
}
/* create video-dma3 page table */
ptr1
=
pt1
->
cpu
;
for
(
i
=
m2
;
i
<=
m3
;
i
++
,
ptr3
++
)
{
*
ptr3
=
ptr1
[
i
];
}
fill
=
*
(
ptr3
-
1
);
for
(;
i
<
1024
;
i
++
,
ptr3
++
)
{
*
ptr3
=
fill
;
}
/* finally: finish up video-dma1 page table */
ptr1
=
pt1
->
cpu
+
m1
;
fill
=
pt1
->
cpu
[
m1
];
for
(
i
=
m1
;
i
<
1024
;
i
++
,
ptr1
++
)
{
*
ptr1
=
fill
;
}
/*
ptr1 = pt1->cpu;
ptr2 = pt2->cpu;
ptr3 = pt3->cpu;
for(j=0;j<40;j++) {
printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
}
for(j=0;j<40;j++) {
printk("ptr2 %d: 0x%08x\n",j,ptr2[j]);
}
for(j=0;j<40;j++) {
printk("ptr3 %d: 0x%08x\n",j,ptr3[j]);
}
*/
}
else
{
struct
saa7146_pgtable
*
pt
=
&
buf
->
pt
[
0
];
saa7146_pgtable_build_single
(
pci
,
pt
,
list
,
length
);
}
return
0
;
}
/********************************************************************************/
/* file operations */
static
int
video_begin
(
struct
saa7146_fh
*
fh
)
{
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
unsigned
long
flags
;
DEB_EE
((
"dev:%p, fh:%p
\n
"
,
dev
,
fh
));
if
(
fh
==
vv
->
streaming
)
{
DEB_S
((
"already capturing.
\n
"
));
return
0
;
}
if
(
vv
->
streaming
!=
0
)
{
DEB_S
((
"already capturing, but in another open.
\n
"
));
return
-
EBUSY
;
}
/* fixme: check for planar formats here, if we will interfere with
vbi capture for example */
spin_lock_irqsave
(
&
dev
->
slock
,
flags
);
/* clear out beginning of streaming bit */
saa7146_write
(
dev
,
MC2
,
MASK_27
);
/* enable rps0 irqs */
IER_ENABLE
(
dev
,
MASK_27
);
vv
->
streaming
=
fh
;
spin_unlock_irqrestore
(
&
dev
->
slock
,
flags
);
return
0
;
}
static
int
video_end
(
struct
saa7146_fh
*
fh
)
{
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
unsigned
long
flags
;
DEB_EE
((
"dev:%p, fh:%p
\n
"
,
dev
,
fh
));
if
(
vv
->
streaming
!=
fh
)
{
DEB_S
((
"not capturing.
\n
"
));
return
-
EINVAL
;
}
spin_lock_irqsave
(
&
dev
->
slock
,
flags
);
/* disable rps0 */
saa7146_write
(
dev
,
MC1
,
MASK_28
);
/* disable rps0 irqs */
IER_DISABLE
(
dev
,
MASK_27
);
// fixme: only used formats here!
/* fixme: look at planar formats here, especially at the
shutdown of planar formats! */
/* shut down all used video dma transfers */
/* fixme: what about the budget-dvb cards? they use
video-dma3, but video_end should not get called anyway ...*/
saa7146_write
(
dev
,
MC1
,
0x00700000
);
vv
->
streaming
=
NULL
;
spin_unlock_irqrestore
(
&
dev
->
slock
,
flags
);
return
0
;
}
/*
* This function is _not_ called directly, but from
* video_generic_ioctl (and maybe others). userspace
* copying is done already, arg is a kernel fhinter.
*/
int
saa7146_video_do_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
void
*
arg
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
unsigned
long
flags
;
int
err
=
0
,
result
=
0
,
ee
=
0
;
struct
saa7146_use_ops
*
ops
;
struct
videobuf_queue
*
q
;
/* check if extension handles the command */
for
(
ee
=
0
;
dev
->
ext
->
ext_vv_data
->
ioctls
[
ee
].
flags
!=
0
;
ee
++
)
{
if
(
cmd
==
dev
->
ext
->
ext_vv_data
->
ioctls
[
ee
].
cmd
)
break
;
}
if
(
0
!=
(
dev
->
ext
->
ext_vv_data
->
ioctls
[
ee
].
flags
&
SAA7146_EXCLUSIVE
)
)
{
DEB_D
((
"extension handles ioctl exclusive.
\n
"
));
result
=
dev
->
ext
->
ext_vv_data
->
ioctl
(
dev
,
cmd
,
arg
);
return
result
;
}
if
(
0
!=
(
dev
->
ext
->
ext_vv_data
->
ioctls
[
ee
].
flags
&
SAA7146_BEFORE
)
)
{
DEB_D
((
"extension handles ioctl before.
\n
"
));
result
=
dev
->
ext
->
ext_vv_data
->
ioctl
(
dev
,
cmd
,
arg
);
if
(
-
EAGAIN
!=
result
)
{
return
result
;
}
}
/* fixme: add handle "after" case (is it still needed?) */
switch
(
fh
->
type
)
{
case
V4L2_BUF_TYPE_VIDEO_CAPTURE
:
{
ops
=
&
saa7146_video_uops
;
q
=
&
fh
->
video_q
;
break
;
}
case
V4L2_BUF_TYPE_VBI_CAPTURE
:
{
ops
=
&
saa7146_vbi_uops
;
q
=
&
fh
->
vbi_q
;
break
;
}
default:
BUG
();
return
0
;
}
switch
(
cmd
)
{
case
VIDIOC_QUERYCAP
:
{
struct
v4l2_capability
*
cap
=
arg
;
memset
(
cap
,
0
,
sizeof
(
*
cap
));
DEB_EE
((
"VIDIOC_QUERYCAP
\n
"
));
strcpy
(
cap
->
driver
,
"saa7146 v4l2"
);
strncpy
(
cap
->
card
,
dev
->
ext
->
name
,
sizeof
(
cap
->
card
));
sprintf
(
cap
->
bus_info
,
"PCI:%s"
,
dev
->
pci
->
slot_name
);
cap
->
version
=
SAA7146_VERSION_CODE
;
cap
->
capabilities
=
V4L2_CAP_VIDEO_CAPTURE
|
V4L2_CAP_VIDEO_OVERLAY
|
V4L2_CAP_READWRITE
|
V4L2_CAP_STREAMING
;
cap
->
capabilities
|=
dev
->
ext
->
ext_vv_data
->
capabilities
;
return
0
;
}
case
VIDIOC_G_FBUF
:
{
struct
v4l2_framebuffer
*
fb
=
arg
;
DEB_EE
((
"VIDIOC_G_FBUF
\n
"
));
*
fb
=
vv
->
ov_fb
;
fb
->
capability
=
V4L2_FBUF_CAP_LIST_CLIPPING
;
return
0
;
}
case
VIDIOC_S_FBUF
:
{
struct
v4l2_framebuffer
*
fb
=
arg
;
struct
saa7146_format
*
fmt
;
DEB_EE
((
"VIDIOC_S_FBUF
\n
"
));
/*
if(!capable(CAP_SYS_ADMIN)) { // && !capable(CAP_SYS_RAWIO)) {
DEB_D(("VIDIOC_S_FBUF: not CAP_SYS_ADMIN or CAP_SYS_RAWIO.\n"));
return -EPERM;
}
*/
if
(
0
!=
vv
->
ov_data
)
{
DEB_D
((
"VIDIOC_S_FBUF: overlay is active.
\n
"
));
return
-
EPERM
;
}
/* check args */
fmt
=
format_by_fourcc
(
dev
,
fb
->
fmt
.
pixelformat
);
if
(
NULL
==
fmt
)
{
return
-
EINVAL
;
}
/* ok, accept it */
vv
->
ov_fb
=
*
fb
;
vv
->
ov_fmt
=
fmt
;
if
(
0
==
vv
->
ov_fb
.
fmt
.
bytesperline
)
vv
->
ov_fb
.
fmt
.
bytesperline
=
vv
->
ov_fb
.
fmt
.
width
*
fmt
->
depth
/
8
;
return
0
;
}
case
VIDIOC_ENUM_FMT
:
{
struct
v4l2_fmtdesc
*
f
=
arg
;
int
index
;
switch
(
f
->
type
)
{
case
V4L2_BUF_TYPE_VIDEO_CAPTURE
:
case
V4L2_BUF_TYPE_VIDEO_OVERLAY
:
{
index
=
f
->
index
;
if
(
index
<
0
||
index
>=
NUM_FORMATS
)
{
return
-
EINVAL
;
}
memset
(
f
,
0
,
sizeof
(
*
f
));
f
->
index
=
index
;
strncpy
(
f
->
description
,
formats
[
index
].
name
,
31
);
f
->
pixelformat
=
formats
[
index
].
pixelformat
;
break
;
}
default:
return
-
EINVAL
;
}
DEB_EE
((
"VIDIOC_ENUMSTD: type:%d, index:%d
\n
"
,
f
->
type
,
f
->
index
));
return
0
;
}
case
VIDIOC_QUERYCTRL
:
{
const
struct
v4l2_queryctrl
*
ctrl
;
struct
v4l2_queryctrl
*
c
=
arg
;
if
((
c
->
id
<
V4L2_CID_BASE
||
c
->
id
>=
V4L2_CID_LASTP1
)
&&
(
c
->
id
<
V4L2_CID_PRIVATE_BASE
||
c
->
id
>=
V4L2_CID_PRIVATE_LASTP1
))
return
-
EINVAL
;
ctrl
=
ctrl_by_id
(
c
->
id
);
if
(
NULL
==
ctrl
)
{
c
->
flags
=
V4L2_CTRL_FLAG_DISABLED
;
return
0
;
}
DEB_EE
((
"VIDIOC_QUERYCTRL: id:%d
\n
"
,
c
->
id
));
*
c
=
*
ctrl
;
return
0
;
}
case
VIDIOC_G_CTRL
:
{
DEB_EE
((
"VIDIOC_G_CTRL
\n
"
));
return
get_control
(
fh
,
arg
);
}
case
VIDIOC_S_CTRL
:
{
DEB_EE
((
"VIDIOC_S_CTRL
\n
"
));
down
(
&
dev
->
lock
);
err
=
set_control
(
fh
,
arg
);
up
(
&
dev
->
lock
);
return
err
;
}
case
VIDIOC_G_PARM
:
{
struct
v4l2_streamparm
*
parm
=
arg
;
if
(
parm
->
type
!=
V4L2_BUF_TYPE_VIDEO_CAPTURE
)
{
return
-
EINVAL
;
}
memset
(
&
parm
->
parm
.
capture
,
0
,
sizeof
(
struct
v4l2_captureparm
));
parm
->
parm
.
capture
.
readbuffers
=
1
;
// fixme: only for PAL!
parm
->
parm
.
capture
.
timeperframe
.
numerator
=
1
;
parm
->
parm
.
capture
.
timeperframe
.
denominator
=
25
;
return
0
;
}
case
VIDIOC_G_FMT
:
{
struct
v4l2_format
*
f
=
arg
;
DEB_EE
((
"VIDIOC_G_FMT
\n
"
));
return
g_fmt
(
fh
,
f
);
}
case
VIDIOC_S_FMT
:
{
struct
v4l2_format
*
f
=
arg
;
DEB_EE
((
"VIDIOC_S_FMT
\n
"
));
return
s_fmt
(
fh
,
f
);
}
case
VIDIOC_TRY_FMT
:
{
struct
v4l2_format
*
f
=
arg
;
DEB_EE
((
"VIDIOC_TRY_FMT
\n
"
));
return
try_fmt
(
fh
,
f
);
}
case
VIDIOC_G_STD
:
{
v4l2_std_id
*
id
=
arg
;
DEB_EE
((
"VIDIOC_G_STD
\n
"
));
*
id
=
vv
->
standard
->
id
;
return
0
;
}
/* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
PAL / NTSC / SECAM. if your hardware does not (or does more)
-- override this function in your extension */
case
VIDIOC_ENUMSTD
:
{
struct
v4l2_standard
*
e
=
arg
;
if
(
e
->
index
<
0
)
return
-
EINVAL
;
if
(
e
->
index
<
dev
->
ext
->
ext_vv_data
->
num_stds
)
{
DEB_EE
((
"VIDIOC_ENUMSTD: index:%d
\n
"
,
e
->
index
));
return
v4l2_video_std_construct
(
e
,
dev
->
ext
->
ext_vv_data
->
stds
[
e
->
index
].
id
,
dev
->
ext
->
ext_vv_data
->
stds
[
e
->
index
].
name
);
}
return
-
EINVAL
;
}
case
VIDIOC_S_STD
:
{
v4l2_std_id
*
id
=
arg
;
int
i
;
int
restart_overlay
=
0
;
int
found
=
0
;
struct
saa7146_fh
*
ov_fh
=
NULL
;
if
(
0
!=
vv
->
streaming
)
{
return
-
EBUSY
;
}
down
(
&
dev
->
lock
);
if
(
vv
->
ov_data
!=
NULL
)
{
ov_fh
=
vv
->
ov_data
->
fh
;
stop_preview
(
ov_fh
);
restart_overlay
=
1
;
}
for
(
i
=
0
;
i
<
dev
->
ext
->
ext_vv_data
->
num_stds
;
i
++
)
if
(
*
id
&
dev
->
ext
->
ext_vv_data
->
stds
[
i
].
id
)
break
;
if
(
i
!=
dev
->
ext
->
ext_vv_data
->
num_stds
)
{
vv
->
standard
=
&
dev
->
ext
->
ext_vv_data
->
stds
[
i
];
if
(
NULL
!=
dev
->
ext
->
ext_vv_data
->
std_callback
)
dev
->
ext
->
ext_vv_data
->
std_callback
(
dev
,
vv
->
standard
);
found
=
1
;
}
if
(
0
!=
restart_overlay
)
{
start_preview
(
ov_fh
);
}
up
(
&
dev
->
lock
);
if
(
0
==
found
)
{
DEB_EE
((
"VIDIOC_S_STD: standard not found.
\n
"
));
return
-
EINVAL
;
}
DEB_EE
((
"VIDIOC_S_STD: set to standard to '%s'
\n
"
,
vv
->
standard
->
name
));
return
0
;
}
case
VIDIOC_OVERLAY
:
{
int
on
=
*
(
int
*
)
arg
;
int
err
=
0
;
if
(
NULL
==
vv
->
ov_fmt
)
{
DEB_D
((
"VIDIOC_OVERLAY: no framebuffer informations. call S_FBUF first!
\n
"
));
return
-
EAGAIN
;
}
DEB_D
((
"VIDIOC_OVERLAY on:%d
\n
"
,
on
));
if
(
0
!=
on
)
{
if
(
vv
->
ov_data
!=
NULL
)
{
if
(
fh
!=
vv
->
ov_data
->
fh
)
{
return
-
EAGAIN
;
}
}
spin_lock_irqsave
(
&
dev
->
slock
,
flags
);
err
=
start_preview
(
fh
);
spin_unlock_irqrestore
(
&
dev
->
slock
,
flags
);
}
else
{
if
(
vv
->
ov_data
!=
NULL
)
{
if
(
fh
!=
vv
->
ov_data
->
fh
)
{
return
-
EAGAIN
;
}
}
spin_lock_irqsave
(
&
dev
->
slock
,
flags
);
err
=
stop_preview
(
fh
);
spin_unlock_irqrestore
(
&
dev
->
slock
,
flags
);
}
return
err
;
}
case
VIDIOC_REQBUFS
:
{
DEB_D
((
"VIDIOC_REQBUFS
\n
"
));
return
videobuf_reqbufs
(
file
,
q
,
arg
);
}
case
VIDIOC_QUERYBUF
:
{
DEB_D
((
"VIDIOC_QUERYBUF
\n
"
));
return
videobuf_querybuf
(
q
,
arg
);
}
case
VIDIOC_QBUF
:
{
DEB_D
((
"VIDIOC_QBUF
\n
"
));
return
videobuf_qbuf
(
file
,
q
,
arg
);
}
case
VIDIOC_DQBUF
:
{
DEB_D
((
"VIDIOC_DQBUF
\n
"
));
return
videobuf_dqbuf
(
file
,
q
,
arg
);
}
case
VIDIOC_STREAMON
:
{
DEB_D
((
"VIDIOC_STREAMON
\n
"
));
if
(
0
!=
ops
->
capture_begin
)
{
if
(
0
!=
(
err
=
ops
->
capture_begin
(
fh
)))
{
return
err
;
}
}
err
=
videobuf_streamon
(
file
,
q
);
return
err
;
}
case
VIDIOC_STREAMOFF
:
{
DEB_D
((
"VIDIOC_STREAMOFF
\n
"
));
if
(
0
!=
ops
->
capture_end
)
{
ops
->
capture_end
(
fh
);
}
err
=
videobuf_streamoff
(
file
,
q
);
return
0
;
}
case
VIDIOCGMBUF
:
{
struct
video_mbuf
*
mbuf
=
arg
;
struct
videobuf_queue
*
q
;
int
i
;
/* fixme: number of capture buffers and sizes for v4l apps */
int
gbuffers
=
2
;
int
gbufsize
=
768
*
576
*
4
;
DEB_D
((
"VIDIOCGMBUF
\n
"
));
q
=
&
fh
->
video_q
;
down
(
&
q
->
lock
);
err
=
videobuf_mmap_setup
(
file
,
q
,
gbuffers
,
gbufsize
);
if
(
err
<
0
)
{
up
(
&
q
->
lock
);
return
err
;
}
memset
(
mbuf
,
0
,
sizeof
(
*
mbuf
));
mbuf
->
frames
=
gbuffers
;
mbuf
->
size
=
gbuffers
*
gbufsize
;
for
(
i
=
0
;
i
<
gbuffers
;
i
++
)
mbuf
->
offsets
[
i
]
=
i
*
gbufsize
;
up
(
&
q
->
lock
);
return
0
;
}
default:
return
v4l_compat_translate_ioctl
(
inode
,
file
,
cmd
,
arg
,
saa7146_video_do_ioctl
);
}
return
0
;
}
/*********************************************************************************/
/* buffer handling functions */
static
int
buffer_activate
(
struct
saa7146_dev
*
dev
,
struct
saa7146_buf
*
buf
,
struct
saa7146_buf
*
next
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
buf
->
vb
.
state
=
STATE_ACTIVE
;
saa7146_set_capture
(
dev
,
buf
,
next
);
mod_timer
(
&
vv
->
video_q
.
timeout
,
jiffies
+
BUFFER_TIMEOUT
);
return
0
;
}
static
int
buffer_prepare
(
struct
file
*
file
,
struct
videobuf_buffer
*
vb
,
enum
v4l2_field
field
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
struct
saa7146_buf
*
buf
=
(
struct
saa7146_buf
*
)
vb
;
int
size
,
err
=
0
;
/* sanity checks */
if
(
fh
->
video_fmt
.
width
<
64
||
fh
->
video_fmt
.
height
<
64
||
fh
->
video_fmt
.
width
>
vv
->
standard
->
h_max_out
||
fh
->
video_fmt
.
height
>
vv
->
standard
->
v_max_out
)
{
DEB_D
((
"w (%d) / h (%d) out of bounds.
\n
"
,
fh
->
video_fmt
.
width
,
fh
->
video_fmt
.
height
));
return
-
EINVAL
;
}
size
=
fh
->
video_fmt
.
sizeimage
;
if
(
0
!=
buf
->
vb
.
baddr
&&
buf
->
vb
.
bsize
<
size
)
{
DEB_D
((
"size mismatch.
\n
"
));
return
-
EINVAL
;
}
DEB_CAP
((
"buffer_prepare [size=%dx%d,bytes=%d,fields=%s]
\n
"
,
fh
->
video_fmt
.
width
,
fh
->
video_fmt
.
height
,
size
,
v4l2_field_names
[
fh
->
video_fmt
.
field
]));
if
(
buf
->
vb
.
width
!=
fh
->
video_fmt
.
width
||
buf
->
vb
.
height
!=
fh
->
video_fmt
.
height
||
buf
->
vb
.
size
!=
size
||
buf
->
vb
.
field
!=
field
||
buf
->
vb
.
field
!=
fh
->
video_fmt
.
field
||
buf
->
fmt
!=
&
fh
->
video_fmt
)
{
saa7146_dma_free
(
dev
,
buf
);
}
if
(
STATE_NEEDS_INIT
==
buf
->
vb
.
state
)
{
struct
saa7146_format
*
sfmt
;
buf
->
vb
.
width
=
fh
->
video_fmt
.
width
;
buf
->
vb
.
height
=
fh
->
video_fmt
.
height
;
buf
->
vb
.
size
=
size
;
buf
->
vb
.
field
=
field
;
buf
->
fmt
=
&
fh
->
video_fmt
;
buf
->
vb
.
field
=
fh
->
video_fmt
.
field
;
sfmt
=
format_by_fourcc
(
dev
,
buf
->
fmt
->
pixelformat
);
if
(
0
!=
IS_PLANAR
(
sfmt
->
trans
))
{
saa7146_pgtable_free
(
dev
->
pci
,
&
buf
->
pt
[
0
]);
saa7146_pgtable_free
(
dev
->
pci
,
&
buf
->
pt
[
1
]);
saa7146_pgtable_free
(
dev
->
pci
,
&
buf
->
pt
[
2
]);
saa7146_pgtable_alloc
(
dev
->
pci
,
&
buf
->
pt
[
0
]);
saa7146_pgtable_alloc
(
dev
->
pci
,
&
buf
->
pt
[
1
]);
saa7146_pgtable_alloc
(
dev
->
pci
,
&
buf
->
pt
[
2
]);
}
else
{
saa7146_pgtable_free
(
dev
->
pci
,
&
buf
->
pt
[
0
]);
saa7146_pgtable_alloc
(
dev
->
pci
,
&
buf
->
pt
[
0
]);
}
err
=
videobuf_iolock
(
dev
->
pci
,
&
buf
->
vb
);
if
(
err
)
goto
oops
;
err
=
saa7146_pgtable_build
(
dev
,
buf
);
if
(
err
)
goto
oops
;
}
buf
->
vb
.
state
=
STATE_PREPARED
;
buf
->
activate
=
buffer_activate
;
return
0
;
oops:
DEB_D
((
"error out.
\n
"
));
saa7146_dma_free
(
dev
,
buf
);
return
err
;
}
static
int
buffer_setup
(
struct
file
*
file
,
unsigned
int
*
count
,
unsigned
int
*
size
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
if
(
0
==
*
count
||
*
count
>
MAX_SAA7146_CAPTURE_BUFFERS
)
*
count
=
MAX_SAA7146_CAPTURE_BUFFERS
;
*
size
=
fh
->
video_fmt
.
sizeimage
;
/* check if we exceed the "memory" parameter */
if
(
(
*
count
*
*
size
)
>
(
memory
*
1048576
)
)
{
*
count
=
(
memory
*
1048576
)
/
*
size
;
}
DEB_CAP
((
"%d buffers, %d bytes each.
\n
"
,
*
count
,
*
size
));
return
0
;
}
static
void
buffer_queue
(
struct
file
*
file
,
struct
videobuf_buffer
*
vb
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
struct
saa7146_buf
*
buf
=
(
struct
saa7146_buf
*
)
vb
;
DEB_CAP
((
"vbuf:%p
\n
"
,
vb
));
saa7146_buffer_queue
(
fh
->
dev
,
&
vv
->
video_q
,
buf
);
}
static
void
buffer_release
(
struct
file
*
file
,
struct
videobuf_buffer
*
vb
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_buf
*
buf
=
(
struct
saa7146_buf
*
)
vb
;
DEB_CAP
((
"vbuf:%p
\n
"
,
vb
));
saa7146_dma_free
(
dev
,
buf
);
}
static
struct
videobuf_queue_ops
video_qops
=
{
.
buf_setup
=
buffer_setup
,
.
buf_prepare
=
buffer_prepare
,
.
buf_queue
=
buffer_queue
,
.
buf_release
=
buffer_release
,
};
/********************************************************************************/
/* file operations */
static
void
video_init
(
struct
saa7146_dev
*
dev
,
struct
saa7146_vv
*
vv
)
{
INIT_LIST_HEAD
(
&
vv
->
video_q
.
queue
);
init_timer
(
&
vv
->
video_q
.
timeout
);
vv
->
video_q
.
timeout
.
function
=
saa7146_buffer_timeout
;
vv
->
video_q
.
timeout
.
data
=
(
unsigned
long
)(
&
vv
->
video_q
);
vv
->
video_q
.
dev
=
dev
;
/* set some default values */
vv
->
standard
=
&
dev
->
ext
->
ext_vv_data
->
stds
[
0
];
/* FIXME: what's this? */
vv
->
current_hps_source
=
SAA7146_HPS_SOURCE_PORT_A
;
vv
->
current_hps_sync
=
SAA7146_HPS_SYNC_PORT_A
;
}
static
void
video_open
(
struct
saa7146_dev
*
dev
,
struct
saa7146_fh
*
fh
)
{
struct
saa7146_format
*
sfmt
;
fh
->
video_fmt
.
width
=
384
;
fh
->
video_fmt
.
height
=
288
;
fh
->
video_fmt
.
pixelformat
=
V4L2_PIX_FMT_BGR24
;
fh
->
video_fmt
.
field
=
V4L2_FIELD_ANY
;
sfmt
=
format_by_fourcc
(
dev
,
fh
->
video_fmt
.
pixelformat
);
fh
->
video_fmt
.
sizeimage
=
(
fh
->
video_fmt
.
width
*
fh
->
video_fmt
.
height
*
sfmt
->
depth
)
/
8
;
videobuf_queue_init
(
&
fh
->
video_q
,
&
video_qops
,
dev
->
pci
,
&
dev
->
slock
,
V4L2_BUF_TYPE_VIDEO_CAPTURE
,
V4L2_FIELD_INTERLACED
,
sizeof
(
struct
saa7146_buf
));
init_MUTEX
(
&
fh
->
video_q
.
lock
);
}
static
void
video_close
(
struct
saa7146_dev
*
dev
,
struct
saa7146_fh
*
fh
,
struct
file
*
file
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
unsigned
long
flags
;
if
(
0
!=
vv
->
ov_data
)
{
if
(
fh
==
vv
->
ov_data
->
fh
)
{
spin_lock_irqsave
(
&
dev
->
slock
,
flags
);
stop_preview
(
fh
);
spin_unlock_irqrestore
(
&
dev
->
slock
,
flags
);
}
}
if
(
fh
==
vv
->
streaming
)
{
video_end
(
fh
);
}
videobuf_queue_cancel
(
file
,
&
fh
->
video_q
);
}
static
void
video_irq_done
(
struct
saa7146_dev
*
dev
,
unsigned
long
st
)
{
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
struct
saa7146_dmaqueue
*
q
=
&
vv
->
video_q
;
spin_lock
(
&
dev
->
slock
);
DEB_CAP
((
"called.
\n
"
));
/* only finish the buffer if we have one... */
if
(
NULL
!=
q
->
curr
)
{
saa7146_buffer_finish
(
dev
,
q
,
STATE_DONE
);
}
saa7146_buffer_next
(
dev
,
q
,
0
);
spin_unlock
(
&
dev
->
slock
);
}
static
ssize_t
video_read
(
struct
file
*
file
,
char
*
data
,
size_t
count
,
loff_t
*
ppos
)
{
struct
saa7146_fh
*
fh
=
file
->
private_data
;
struct
saa7146_dev
*
dev
=
fh
->
dev
;
struct
saa7146_vv
*
vv
=
dev
->
vv_data
;
ssize_t
ret
=
0
;
int
restart_overlay
=
0
;
struct
saa7146_fh
*
ov_fh
=
NULL
;
DEB_EE
((
"called.
\n
"
));
if
(
vv
->
ov_data
!=
NULL
)
{
ov_fh
=
vv
->
ov_data
->
fh
;
stop_preview
(
ov_fh
);
restart_overlay
=
1
;
}
if
(
0
!=
video_begin
(
fh
))
{
return
-
EAGAIN
;
}
ret
=
videobuf_read_one
(
file
,
&
fh
->
video_q
,
data
,
count
,
ppos
);
video_end
(
fh
);
/* restart overlay if it was active before */
if
(
0
!=
restart_overlay
)
{
start_preview
(
ov_fh
);
}
return
ret
;
}
struct
saa7146_use_ops
saa7146_video_uops
=
{
.
init
=
video_init
,
.
open
=
video_open
,
.
release
=
video_close
,
.
irq_done
=
video_irq_done
,
.
read
=
video_read
,
.
capture_begin
=
video_begin
,
.
capture_end
=
video_end
,
};
EXPORT_SYMBOL_GPL
(
saa7146_video_uops
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment