Commit c39d4913 authored by Linus Torvalds's avatar Linus Torvalds

Merge home.osdl.org:/home/torvalds/v2.5/zoran

into home.osdl.org:/home/torvalds/v2.5/linux
parents 51a76834 4f11c88c
DC10/DC10plus/LML33/Buz Driver for Linux Frequently Asked Questions:
========================================= ===========================
subject: unified zoran driver (zr360x7, zoran, buz, dc10(+), dc30(+), lml33)
website: http://mjpeg.sourceforge.net/driver-zoran/
1. What cards are supported
1.1 What the TV decoder can do an what not
1.2 What the TV encoder can do an what not
2. How do I get this damn thing to work
3. What mainboard should I use (or why doesn't my card work)
4. Programming interface
5. Applications
6. Concerning buffer sizes, quality, output size etc.
7. It hangs/crashes/fails/whatevers! Help!
8. Maintainers/Contacting
9. License
===========================
1. What cards are supported
Iomega Buz, Linux Media Labs LML33/LML33R10, Pinnacle/Miro
DC10/DC10+/DC30/DC30+ and related boards (available under various names).
Iomega Buz:
* Zoran zr36067 PCI controller
* Zoran zr36060 MJPEG codec
* Philips saa7111 TV decoder
* Philips saa7185 TV encoder
Drivers to use: videodev, i2c-core, i2c-algo-bit,
videocodec, saa7111, saa7185, zr36060, zoran
Inputs/outputs: Composite and S-video
Norms: PAL, SECAM (720x576 @ 25 fps), NTSC (720x480 @ 29.97 fps)
Card number: 7
Linux Media Labs LML33:
* Zoran zr36067 PCI controller
* Zoran zr36060 MJPEG codec
* Brooktree bt819 TV decoder
* Brooktree bt856 TV encoder
Drivers to use: videodev, i2c-core, i2c-algo-bit,
videocodec, bt819, bt856, zr36060, zoran
Inputs/outputs: Composite and S-video
Norms: PAL (720x576 @ 25 fps), NTSC (720x480 @ 29.97 fps)
Card number: 5
Linux Media Labs LML33R10:
* Zoran zr36067 PCI controller
* Zoran zr36060 MJPEG codec
* Philips saa7114 TV decoder
* Analog Devices adv7170 TV encoder
Drivers to use: videodev, i2c-core, i2c-algo-bit,
videocodec, saa7114, adv7170, zr36060, zoran
Inputs/outputs: Composite and S-video
Norms: PAL (720x576 @ 25 fps), NTSC (720x480 @ 29.97 fps)
Card number: 6
Pinnacle/Miro DC10(new):
* Zoran zr36057 PCI controller
* Zoran zr36060 MJPEG codec
* Philips saa7110a TV decoder
* Analog Devices adv7176 TV encoder
Drivers to use: videodev, i2c-core, i2c-algo-bit,
videocodec, saa7110, adv7175, zr36060, zoran
Inputs/outputs: Composite, S-video and Internal
Norms: PAL, SECAM (768x576 @ 25 fps), NTSC (640x480 @ 29.97 fps)
Card number: 1
Pinnacle/Miro DC10+:
* Zoran zr36067 PCI controller
* Zoran zr36060 MJPEG codec
* Philips saa7110a TV decoder
* Analog Devices adv7176 TV encoder
Drivers to use: videodev, i2c-core, i2c-algo-bit,
videocodec, sa7110, adv7175, zr36060, zoran
Inputs/outputs: Composite, S-video and Internal
Norms: PAL, SECAM (768x576 @ 25 fps), NTSC (640x480 @ 29.97 fps)
Card number: 2
Pinnacle/Miro DC10(old): *
* Zoran zr36057 PCI controller
* Zoran zr36050 MJPEG codec
* Zoran zr36016 Video Front End or Fuji md0211 Video Front End (clone?)
* Micronas vpx3220a TV decoder
* mse3000 TV encoder or Analog Devices adv7176 TV encoder *
Drivers to use: videodev, i2c-core, i2c-algo-bit,
videocodec, vpx3220, mse3000/adv7175, zr36050, zr36016, zoran
Inputs/outputs: Composite, S-video and Internal
Norms: PAL, SECAM (768x576 @ 25 fps), NTSC (640x480 @ 29.97 fps)
Card number: 0
Pinnacle/Miro DC30: *
* Zoran zr36057 PCI controller
* Zoran zr36050 MJPEG codec
* Zoran zr36016 Video Front End
* Micronas vpx3225d/vpx3220a/vpx3216b TV decoder
* Analog Devices adv7176 TV encoder
Drivers to use: videodev, i2c-core, i2c-algo-bit,
videocodec, vpx3220/vpx3224, adv7175, zr36050, zr36016, zoran
Inputs/outputs: Composite, S-video and Internal
Norms: PAL, SECAM (768x576 @ 25 fps), NTSC (640x480 @ 29.97 fps)
Card number: 3
Pinnacle/Miro DC30+: *
* Zoran zr36067 PCI controller
* Zoran zr36050 MJPEG codec
* Zoran zr36016 Video Front End
* Micronas vpx3225d/vpx3220a/vpx3216b TV decoder
* Analog Devices adv7176 TV encoder
Drivers to use: videodev, i2c-core, i2c-algo-bit,
videocodec, vpx3220/vpx3224, adv7175, zr36050, zr36015, zoran
Inputs/outputs: Composite, S-video and Internal
Norms: PAL, SECAM (768x576 @ 25 fps), NTSC (640x480 @ 29.97 fps)
Card number: 4
Note: No module for the mse3000 is available yet
Note: No module for the vpx3224 is available yet
Note: use encoder=X or decoder=X for non-default i2c chips (see i2c-id.h)
===========================
1.1 What the TV decoder can do an what not
The best know TV standards are NTSC/PAL/SECAM. but for decoding a frame that
information is not enough. There are several formats of the TV standards.
And not every TV decoder is able to handle every format. Also the every
combination is supported by the driver. There are currently 11 different
tv broadcast formats all aver the world.
The CCIR defines parameters needed for broadcasting the signal.
The CCIR has defined different standards: A,B,D,E,F,G,D,H,I,K,K1,L,M,N,...
The CCIR says not much about about the colorsystem used !!!
And talking about a colorsystem says not to much about how it is broadcast.
The CCIR standards A,E,F are not used any more.
When you speak about NTSC, you usually mean the standard: CCIR - M using
the NTSC colorsystem which is used in the USA, Japan, Mexico, Canada
and a few others.
When you talk about PAL, you usually mean: CCIR - B/G using the PAL
colorsystem which is used in many Countries.
When you talk about SECAM, you mean: CCIR - L using the SECAM Colorsystem
which is used in France, and a few others.
There the other version of SECAM, CCIR - D/K is used in Bulgaria, China,
Slovakai, Hungary, Korea (Rep.), Poland, Rumania and a others.
The CCIR - H uses the PAL colorsystem (sometimes SECAM) and is used in
Egypt, Libya, Sri Lanka, Syrain Arab. Rep.
The CCIR - I uses the PAL colorsystem, and is used in Great Britain, Hong Kong,
Ireland, Nigeria, South Africa.
The CCIR - N uses the PAL colorsystem and PAL frame size but the NTSC framerate,
and is used in Argentinia, Uruguay, an a few others
We do not talk about how the audio is broadcast !
A rather good sites about the TV standards are:
http://www.sony.jp/ServiceArea/Voltage_map/
http://info.electronicwerkstatt.de/bereiche/fernsehtechnik/frequenzen_und_normen/Fernsehnormen/
and http://www.cabl.com/restaurant/channel.html
Other weird things around: NTSC 4.43 is a modificated NTSC, which is mainly
used in PAL VCR's that are able to play back NTSC. PAL 60 seems to be the same
as NTSC 4.43 . The Datasheets also talk about NTSC 44, It seems as if it would
be the same as NTSC 4.43.
NTSC Combs seems to be a decoder mode where the decoder uses a comb filter
to split coma and luma instead of a Delay line.
But I did not defiantly find out what NTSC Comb is.
Philips saa7111 TV decoder
was introduced in 1997, is used in the BUZ and
can handle: PAL B/G/H/I, PAL N, PAL M, NTSC M, NTSC N, NTSC 4.43 and SECAM
Philips saa7110a TV decoder
was introduced in 1995, is used in the Pinnacle/Miro DC10(new), DC10+ and
can handle: PAL B/G, NTSC M and SECAM
Philips saa7114 TV decoder
was introduced in 2000, is used in the LML33R10 and
can handle: PAL B/G/D/H/I/N, PAL N, PAL M, NTSC M, NTSC 4.43 and SECAM
Brooktree bt819 TV decoder
was introduced in 1996, and is used in the LML33 and
can handle: PAL B/D/G/H/I, NTSC M
Micronas vpx3220a TV decoder
was introduced in 1996, is used in the DC30 and DC30+ and
can handle: PAL B/G/H/I, PAL N, PAL M, NTSC M, NTSC 44, PAL 60, SECAM,NTSC Comb
===========================
1.2 What the TV encoder can do an what not
The TV encoder are doing the "same" as the decoder, but in the oder direction.
You feed them digital data and the generate a Composite or SVHS signal.
For information about the colorsystems and TV norm take a look in the
TV decoder section.
Philips saa7185 TV Encoder
was introduced in 1996, is used in the BUZ
can generate: PAL B/G, NTSC M
Brooktree bt856 TV Encoder
was introduced in 1994, is used in the LML33
can generate: PAL B/D/G/H/I/N, PAL M, NTSC M, PAL-N (Argentina)
Analog Devices adv7170 TV Encoder
was introduced in 2000, is used in the LML300R10
can generate: PAL B/D/G/H/I/N, PAL M, NTSC M, PAL 60
by Rainer Johanni <Rainer@Johanni.de> (for Iomega Buz Driver) Analog Devices adv7175 TV Encoder
was introduced in 1996, is used in the DC10, DC10+, DC10 old, DC30, DC30+
can generate: PAL B/D/G/H/I/N, PAL M, NTSC M
Adapted for DC10/DC10plus by Wolfgang Scherr <scherr@net4you.net> ITT mse3000 TV encoder
was introduced in 1991, is used in the DC10 old
Further changes for DC10/DC10plus and LML33 cards by can generate: PAL , NTSC , SECAM
Serguei Miridonov <mirsev@cicese.mx>
The adv717x, should be able to produce PAL N. But you find nothing PAL N
Current homepage: http://www.cicese.mx/~mirsev/Linux/DC10plus/ specific in the the registers. Seem that you have to reuse a other standard
Current maintainer: Serguei Miridonov <mirsev@cicese.mx> to generate PAL N, maybe it would work if you use the PAL M settings.
This is a driver for DC10plus capture cards from Pinnacle Systems ==========================
Inc., LML33 cards from Linux Media Labs and Buz from Iomega.
It also works with many old Miro DC10 cards with SAA7110A TV decoder 2. How do I get this damn thing to work
and ADV7176 TV encoder (please, make sure that your card has these
chips, otherwise the driver will not work). Load zoran.o. If it can't autodetect your card, use the card=X insmod
option with X being the card number as given in the previous section.
The driver is Video4Linux compliant and contains extensions to To have more than one card, use card=X1[,X2[,X3,[X4[..]]]]
provide hardware support for full motion MJPEG compression and
decompression. Since this driver is a derivative from the driver for To automate this, add the following to your /etc/modules.conf:
Buz Iomega cards written by Dr. Rainer Johanni,
http://www.johanni.de/munich-vision/buz/ they both have compatible options zoran card=X1[,X2[,X3[,X4[..]]]]
API. I hope that this API will become a part of V4L standard. alias char-major-81-0 zoran
Copyright: This driver is free software; you can redistribute it and/or One thing to keep in mind is that this doesn't load zoran.o itself yet. It
modify it under the terms of the GNU General Public License. Please, just automates loading. If you start using xawtv, the device won't load on
check http://www.gnu.org/ for details. some systems, since you're trying to load modules as a user, which is not
allowed ("permission denied"). A quick workaround is to add 'Load "v4l"' to
No warranty: This software is provided on AN "AS-IS" basis WITHOUT XF86Config-4 when you use X by default, or to run 'v4l-conf -c <device>' in
WARRANTY OF ANY KIND. YOU USE IT AT YOUR OWN RISK. one of your startup scripts (normally rc.local) if you don't use X. Both
make sure that the modules are loaded on startup, under the root account.
===========================
CONTENTS
~~~~~~~~ 3. What mainboard should I use (or why doesn't my card work)
Supported Formats <insert lousy disclaimer here>. In short: good=SiS/Intel, bad=VIA.
Hardware compression
Compiling and Loading the Driver Experience tells us that people with a Buz, on average, have more problems
Driver Options than users with a DC10+/LML33. Also, it tells us that people owning a VIA-
Tested applications based mainboard (ktXXX, MVP3) have more problems than users with a mainboard
Programming interface based on a different chipset. Here's some notes from Andrew Stevens:
Features for testing --
Mailing lists Here's my experience of using LML33 and Buz on various motherboards:
Bug Reports
VIA MVP3
Forget it. Pointless. Doesn't work.
Supported Formats Intel 430FX (Pentium 200)
================= LML33 perfect, Buz tolerable (3 or 4 frames dropped per movie)
Intel 440BX (early stepping)
Card: DC10/DC10plus LML33/Buz LML33 tolerable. Buz starting to get annoying (6-10 frames/hour)
Intel 440BX (late stepping)
TV standard: NTSC/PAL/SECAM(*) NTSC/PAL Buz tolerable, LML3 almost perfect (occasional single frame drops)
SiS735
Format: Square pixel CCIR.601 LML33 perfect, Buz tolerable.
640x480 NTSC 720x480 NTSC VIA KT133(*)
768x576 PAL/SECAM(*) 720x576 PAL LML33 starting to get annoying, Buz poor enough that I have up.
Frame rates: 30 frames/60 fields per second NTSC Both 440BX boards were dual CPU versions.
25 frames/50 fields per second PAL/SECAM(*) --
Bernhard Praschinger later added:
(*) - SECAM is supported for input only in DC10/DC10plus cards. The --
output of the recorded SECAM video stream will be in PAL standard. AMD 751
Also, please, note that monitoring of the SECAM input signal at the Buz perfect-tolerable
DC10/DC10plus analog output may not be available. Please, use AMD 760
appropriate application like XawTV to watch full color SECAM video at Buz perfect-tolerable
the card input. --
In general, people on the user mailinglist won't give you much of a chance
Hardware compression if you have a VIA-based motherboard. They may be cheap, but sometimes, you'd
==================== rather want to spend some more money on better boards. In general, VIA
mainboard's IDE/PCI performance will also suck badly compared to others.
Since the card provides hardware compression, even low end machines can You'll noticed the DC10+/DC30+ aren't mentioned anywhere in the overview.
be successfully used for movie capture and playback. I'm testing the Basically, you can assume that if the Buz works, the LML33 will work too. If
driver with with 2.2.16 kernel running on 233 MHz Pentium MMX with 64M the LML33 works, the DC10+/DC30+ will work too. They're most tolerant to
RAM on 430TX motherboard and with 10GB IDE drive from Western Digital different mainboard chipsets from all of the supported cards.
Corp.
If you experience timeouts during capture, buy a better mainboard or lower
On one test run with DC10plus card I've got 0 frames dropped during the quality/buffersize during capture (see 'Concerning buffer sizes, quality,
about 20 minutes of full motion NTSC (I live in Mexico) video capture output size etc.'). If it hangs, there's little we can do as of now. Check
with fully synchronized audio. The command was your IRQs and make sure the card has its own interrupts.
lavrec -fa -in -d1 -l -1 -q30 -w /dos/g/capture/Linux/test%03d.avi ===========================
for recording, and 4. Programming interface
lavplay -n128 /dos/g/capture/Linux/test*.avi This driver conforms to video4linux and video4linux2, both can be used to
use the driver. Since video4linux didn't provide adequate calls to fully
for playback. (See lavtools distribution for more information). use the cards' features, we've introduced several programming extensions,
which are currently officially accepted in the 2.4.x branch of the kernel.
Typical run of similar test can provide as few as 6-8 dropped frames per These extensions are known as the v4l/mjpeg extensions. See zoran.h for
half of an hour. You mileage may vary, though. details (structs/ioctls).
Compiling and Loading the Driver Information - video4linux:
================================
You should run a 2.2.x kernel in order to use this driver. The driver
was also tested with 2.4-test6 kernel, so hopefully it will work
with 2.4 kernels too.
I would recommend to use only official kernels from www.kernel.org and
its mirrors. Kernels supplied with some Linux distributions may be
patched in some way to meet specific needs of particular Linux
distributor and could be incompatible with this driver. As a driver
maintainer, I am not able to follow every unofficial kernel release,
and no unofficial kernels will be supported.
Besides the files in this directory, the driver needs the 'videodev'
and the 'i2c' module from the Linux kernel (i2c-old for 2.4 kernels).
In order to get these modules available, enable module support for
VIDEODEV and BTTV (which implies i2c) in your 2.2.x kernel
configuration. You will find these devices in the menu "Character
Devices" in your Kernel Configuration.
In newer kernels (2.4) instead of BTTV you should enable support for
Iomega Buz cards and for Zoran 36060/36067 chipset. This will include
i2c or i2c-old modules and Buz/LML33 driver. However, instead of
modules for Buz/LML33 driver from the kernel, use modules from _this_
driver.
To compile the driver, just type make.
Before you load the driver you must have a video device at major device
node 81. If you don't have it yet, do the following (as root!):
cd /dev
mknod video0 c 81 0
ln -s video0 video
If you have more than one card, add more nodes in /dev directory:
mknod video1 c 81 1
mknod video2 c 81 2
...
The driver should operate properly with several cards. It was tested
with one DC10plus and one LML33 cards installed together and the driver
correctly identifies both cards and works with both of them.
Currently the driver does not support LML33 and Buz cards installed
together in the same system. This will be fixed in future versions.
Edit the 'update' script if you want to give the driver special options
(see below for options descriptions) and then type (as root)
./update <card_list>
to insert all necessary modules into the kernel. <card_list> is a list of
cards installed in your system separated by white space. Supported cards
are dc10, dc10plus, lml33, and buz. For example, if you have both dc10plus
and lml33 cards, please type
./update dc10 lml33
If you want to make full use of the Video for Linux _uncompressed_
grabbing facilities, you must either
- obtain and install the "big_physarea patch" for your kernel and
set aside the necessary memory during boot time. There seem to be
several versions of this patch against various kernel versions
floating around in the net, you may obtain one e.g. from:
http://www.polyware.nl/~middelin/hob-v4l.html#bigphysarea
You also have to compile your driver AFTER installing that patch in
order to get it working
or
- start your kernel with the mem=xxx option, where xxx is your
real memory minus the memory needed for the buffers.
For doing this add an entry in lilo.conf (if you use lilo):
append "mem=xxxM"
or add a line in your linux.par file (if you use loadlin):
mem=xxxM
The second method is by far easier, however it is dangerous if more
than one driver at a time has the idea to use the memory leftover by
setting the mem=xxx parameter below the actual memory size.
Read also below how to use this memory!
If you use only MJPEG compressed capture provided by the driver, you
should not need large memory areas for DMA. In this case, you will be
able to capture and playback movies with lavtools, however you will
not be able to use capture features of XawTV and other similar
programs (you can still watch video on the screen).
Driver Options
==============
You are able to customize the behavior of the driver by giving
it some options at start time.
default_input, default_norm
---------------------------
As soon as the driver is loaded, the Buz samples video signals
from one of its input ports and displays it on its output.
The driver uses the Composite Input and the video norm PAL for this.
If you want to change this default behavior, set default_input=1
(for S-VHS input) or default_norm=1 for NTSC or default_norm=2
for SECAM (DC10/DC10plus only).
lock_norm
---------
This option was introduced to disable norm (TV standard) change by some
not well behaving programs. For example, if you have some application
which was written by somebody who lives in a country with PAL standard,
this program may not have NTSC option and may always try to set the
driver to PAL. In this case, you may load the driver with
default_norm=1 and lock_norm=1 and the card will be forced to work in
NTSC standard only.
Options:
lock_norm=0 default, TV standard change is enabled;
lock_norm=1 TV standard change is disabled but the driver
will not notify the application about any error;
lock_norm=2 TV standard change is disabled and the driver
will notify the program that TV standards other
than set by default_norm=X option are not
supported.
pass_through
------------
When the driver is not in use (device is not opened by any program) and
pass_through=0 (default) the driver will set the TV encoder to produce
color bar signal at the output. If the driver was loaded with
pass_through=1, the color bar will be disabled and input signal will be
sent to the output even if the driver not in use. If you have LML33 card
and wish the color bar signal at the output, you will also need to set
lml33dpath=1 (please, see next section).
lml33dpath
----------
LML33 card normally (lml33dpath=0) connects its output to the input
using analog switch. Additionally, it also allows real-time monitoring
of digitized video using TV monitor connected to the output. This
"digital path" option can be enabled setting lml33dpath=1. In this
mode, the input is connected only to the TV decoder, digital video data
is sent via internal video bus to the TV encoder and resulting analog
signal is sent to the output. This mode could be very useful for testing and
picture adjustment while watching video at the TV monitor connected to
the output. However, because of lack of 75 ohm terminating resistors at
TV decoder input, the signal will suffer serious distortions.
# These distortions could be eliminated by soldering two 75 ohm resistors
# in LML33 card: in parallel to capacitors C73 and C82 (see schematics of
# H33 board available at www.linuxmedialabs.com and www.zoran.com). Be
# aware, however, that doing so will void card warranty and the card,
# after this change, must always be used with loading option lml33dpath=1.
#
# WARNING: I DID NOT TRY THIS CARD CHANGE YET, THIS IS JUST AN ASSUMPTION
# AND I WILL NOT BE RESPONSIBLE FOR ANY DAMAGE ASSOCIATED WITH THIS
# CHANGE. IF YOU WISH TO TRY IT, DO IT AT YOUR OWN RISK.
Please, note that DC10/DC10plus cards always use "digital path" for
signal monitoring. Its input and output are both properly terminated
and the digitized signal quality does not depend on the connection of
the output load.
v4l_nbufs, v4l_bufsize
----------------------
In order to make to make full use of the Video for Linux uncompressed
picture grabbing facilities of the driver (which are needed by many
Video for Linux applications), the driver needs a set of physically
contiguous buffers for grabbing. These parameters determine how many
buffers of which size the driver will allocate at open (the open will
fail if it is unable to do so!).
These values do not affect the MJPEG grabbing facilities of the driver,
they are needed for uncompressed image grabbing only!!!
v4l_nbufs is the number of buffers to allocate, a value of 2 (the default)
should be sufficient in almost all cases. Only special applications
(streaming captures) will need more buffers and then mostly the
MJPEG capturing features of the Buz will be more appropriate.
So leave this parameter at it's default unless you know what you do.
The things for v4l_bufsize are more complicated: v4l_bufsize is set by
default to 128 [KB] which is the maximum amount of physically
contiguous memory Linux is able to allocate without kernel changes.
This is sufficient for grabbing 24 bit color images up to sizes of
approx. 240x180 pixels (240*180*3 = 129600, 128 KB = 131072).
In order to be able to capture bigger images you have either to
- obtain and install the "big_physarea patch" and set aside
the necessary memory during boot time or
- start your kernel with the mem=xxx option, where xxx is your
real memory minus the memory needed for the buffers.
In that case, useful settings for v4l_bufsize are
- 1296 [Kb] for grabbing 24 bit images of max size 768*576
- 1728 [Kb] for 32bit images of same size (4*768*576 = 1728 Kb!)
You may reduce these numbers accordingly if you know you are only
grabbing 720 pixels wide images or NTSC images (max height 480).
In some cases it may happen that Linux isn't even able to obtain
the default 128 KB buffers. If you don't need uncompressed image
grabbing at all, set v4l_bufsize to an arbitrary small value (e.g. 4)
in order to be able to open the video device.
triton, natoma
--------------
The driver tries to detect if you have a triton or natoma chipset
in order to take special measures for these chipsets.
If this detection fails but you are sure you have such a chipset,
set the corresponding variable to 1.
This is a very special option and may go away in the future.
Tested applications
===================
XawTV to watch video on your computer monitor.
kwintv the same (you might need to use option lock_norm=1).
lavtools To record and playback AVI or Quicktime files. Note: you
will need patched version, lavtools-1.2p2 to support new
features of this driver. Please visit driver homepage for
more info.
Broadcast2000 reportedly (I didn't try that) can accept movies recorded
by lavrec in Quicktime format for editing and then edited
movie can be played back by lavplay program.
MainActor 3.5x also can accept movies recorded by lavrec for editing.
The driver can to be used by two programs at the same time
(please, see warning note below regarding this feature). Using XawTV
you can watch what you are recording or playing back with lavtools.
I've tested the following sequence and it worked for me:
* start xawtv and switch inputs, TV standards, and adjust video
(contrast, saturation, etc.). You may also run your favorite
audio mixer application to adjust audio inputs.
* run lavrec with options:
-i<set your input and norm here> (to choose proper input
and TV standard)
-l -1 (to use audio mixer settings)
Other lavrec option can be added at your choice.
* watch the movie in xawtv window while recording it as AVI or
Quicktime file.
* when recording is finished, run lavplay or xlav and watch your
clip in xawtv window.
* Note: you should not quit xawtv during recording or playing back.
If you quit xawtv during recording or playback, another lavtools
program will stop and may even crash.
I'm not sure that the same will work for you. You can try but,
please, be careful.
WARNING! This is an experimental feature and I'm not sure if it will be
supported in the future. The original driver was not designed to be
used like this and it has no protection against any interference
between two running programs. THEREFORE, IT IS POTENTIALLY DANGEROUS
AND SINCE THE DRIVER OPERATES IN KERNEL SPACE, USING THIS FEATURE MAY
CRASH YOUR ENTIRE SYSTEM.
Programming interface
=====================
This driver should be fully compliant to Video for Linux, so all
tools working with Video for Linux should work with (hopefully)
no problems.
A description of the Video for Linux programming interface can be found at:
http://roadrunner.swansea.linux.org.uk/v4lapi.shtml http://roadrunner.swansea.linux.org.uk/v4lapi.shtml
/usr/src/linux/Documentation/video4linux/API.html
/usr/include/linux/videodev.h
Besides the Video for Linux interface, the driver has a "proprietary" Information - video4linux/mjpeg extensions:
interface for accessing the Buz's MJPEG capture and playback facilities. ./zoran.h
(also see below)
For a full description of all members and ioctls see "zoran.h" (used to Information - video4linux2:
be buz.h or dc10.h in previous versions, so, please, update your http://www.thedirks.org/v4l2/
programs accordingly). /usr/include/linux/videodev2.h
http://www.bytesex.org/v4l/
More information on the video4linux/mjpeg extensions, by Serguei
Miridonovi and Rainer Johanni:
--
The ioctls for that interface are as follows: The ioctls for that interface are as follows:
BUZIOC_G_PARAMS BUZIOC_G_PARAMS
...@@ -434,7 +365,6 @@ data to disk (after BUZIOC_QBUF_CAPT) or for reuse (after BUZIOC_QBUF_PLAY). ...@@ -434,7 +365,6 @@ data to disk (after BUZIOC_QBUF_CAPT) or for reuse (after BUZIOC_QBUF_PLAY).
BUZIOC_G_STATUS BUZIOC_G_STATUS
Get the status of the input lines (video source connected/norm). Get the status of the input lines (video source connected/norm).
This ioctl may be subject to change.
For programming example, please, look at lavrec.c and lavplay.c code in For programming example, please, look at lavrec.c and lavplay.c code in
lavtools-1.2p2 package (URL: http://www.cicese.mx/~mirsev/DC10plus/) lavtools-1.2p2 package (URL: http://www.cicese.mx/~mirsev/DC10plus/)
...@@ -450,68 +380,178 @@ Additional notes for software developers: ...@@ -450,68 +380,178 @@ Additional notes for software developers:
settings of a variety of TV capture cards which may work in ITU or settings of a variety of TV capture cards which may work in ITU or
square pixel format. Remember that users now can lock the norm to square pixel format. Remember that users now can lock the norm to
avoid any ambiguity. avoid any ambiguity.
--
Features for testing Please note that lavplay/lavrec are also included in the MJPEG-tools
==================== (http://mjpeg.sf.net/).
When loaded, the driver creates a /proc/zoranX entry for each card: ===========================
using 'cat /proc/zoran0' for your first card you can see the contents
of ZR36057/67 chip registers. It is also possible to modify the 5. Applications
contents of some registers directly. WARNING: modified contents is not
stored in the driver memory, if you restart any program which uses this Applications known to work with this driver:
driver or even change position or cause redraw of a window of xawtv or
other program, the original registers contents will be restored by the TV viewing:
driver. However, it can be used to change ZR36067 registers on the fly * xawtv
for fine tuning and then to include these changes into driver code. * kwintv
This feature is very limited and still requires some documentation. * probably any TV application that supports video4linux or video4linux2.
However, if you are impatient, look at zoran_procfs.c code and
(IMPORTANT!) read ZR36057/67 manual. To set TopField bit, for example, MJPEG capture/playback:
you need to type as root: * mjpegtools/lavtools (or Linux Video Studio)
* gstreamer
echo TopField=1 > /proc/zoranX # change X to 0 for your first card, * mplayer
# 1 for second and so on...
General raw capture:
If you use this feature and have found some interesting result, please, let * xawtv
me know. * gstreamer
* probably any application that supports video4linux or video4linux2
Mailing lists
============= Video editing:
* Cinelerra
There are two mailing lists available to discuss application issues and * MainActor
suggest driver improvements: * mjpegtools (or Linux Video Studio)
1. A mailing list buz-linux was set up to discuss Iomega Buz driver. ===========================
Since this driver is derivative of that driver, you can also post your
questions and suggestions there. Subscribe with a message (with 6. Concerning buffer sizes, quality, output size etc.
"subscribe" in the subject) to buz-linux-subscribe@webmages.com.
Unsubscribe with a message (with "unsubscribe" in the subject) to The zr36060 can do 1:2 JPEG compression. This is really the theoretical
buz-linux-unsubscribe@webmages.com. The mailing list archive can be maximum that the chipset can reach. The driver can, however, limit compression
found at http://buz.webmages.com/list/. to a maximum (size) of 1:4. The reason for this is that some cards (e.g. Buz)
can't handle 1:2 compression without stopping capture after only a few minutes.
2. Video4Linux mailing list is set for more general discussions related With 1:4, it'll mostly work. If you have a Buz, use 'low_bitrate=1' to go into
to uncompressed video capture, V4L and V4L2 API, many Video4Linux 1:4 max. compression mode.
applications, etc. to subscribe to this mailing list, please, visit
https://listman.redhat.com/mailman/listinfo/video4linux-list 100% JPEG quality is thus 1:2 compression in practice. So for a full PAL frame
(size 720x576). The JPEG fields are stored in YUY2 format, so the size of the
Bug Reports fields are 720x288x16/2 bits/field (2 fields/frame) = 207360 bytes/field x 2 =
=========== 414720 bytes/frame (add some more bytes for headers and DHT (huffman)/DQT
(quantization) tables, and you'll get to something like 512kB per frame for
If you have found a bug, please, do the following: 1:2 compression. For 1:4 compression, you'd have frames of half this size.
1. Edit first line of zoran.c file and set DEBUGLEVEL to 3; Some additional explanation by Martin Samuelsson, which also explains the
2. Recompile the driver and install it running update script importance of buffer sizes:
in the driver directory; --
3. Run the application(s) which you used when you had found a > Hmm, I do not think it is really that way. With the current (downloaded
suspisious behavior; > at 18:00 Monday) driver I get that output sizes for 10 sec:
4. When application stops, look at you /var/log/messages file > -q 50 -b 128 : 24.283.332 Bytes
(or whatever file you use to log kernel messages) and copy > -q 50 -b 256 : 48.442.368
all lines related to the driver activity to a separate file > -q 25 -b 128 : 24.655.992
in the same order of their appearence in your log file. > -q 25 -b 256 : 25.859.820
5. Mail a message to <mirsev@cicese.mx> with a subject
"Linux DC10(plus)/LML33/Buz driver bug report" with a detailed I woke up, and can't go to sleep again. I'll kill some time explaining why
description of your problem, kernel version, application name and this doesn't look strange to me.
attach that file with kernel messages as plain text (please, don't
attach it using base64, uuencode, or any other encoding). Let's do some math using a width of 704 pixels. I'm not sure whether the Buz
actually use that number or not, but that's not too important right now.
If you have a Buz card, please, also mail the same message to
Wolfgang Scherr <scherr@net4you.net> 704x288 pixels, one field, is 202752 pixels. Divided by 64 pixels per block;
3168 blocks per field. Each pixel consist of two bytes; 128 bytes per block;
1024 bits per block. 100% in the new driver mean 1:2 compression; the maximum
output becomes 512 bits per block. Actually 510, but 512 is simpler to use
for calculations.
Let's say that we specify d1q50. We thus want 256 bits per block; times 3168
becomes 811008 bits; 101376 bytes per field. We're talking raw bits and bytes
here, so we don't need to do any fancy corrections for bits-per-pixel or such
things. 101376 bytes per field.
d1 video contains two fields per frame. Those sum up to 202752 bytes per
frame, and one of those frames goes into each buffer.
But wait a second! -b128 gives 128kB buffers! It's not possible to cram
202752 bytes of JPEG data into 128kB!
This is what the driver notice and automatically compensate for in your
examples. Let's do some math using this information:
128kB is 131072 bytes. In this buffer, we want to store two fields, which
leaves 65536 bytes for each field. Using 3168 blocks per field, we get
20.68686868... available bytes per block; 165 bits. We can't allow the
request for 256 bits per block when there's only 165 bits available! The -q50
option is silently overridden, and the -b128 option takes precedence, leaving
us with the equivalence of -q32.
This gives us a data rate of 165 bits per block, which, times 3168, sums up
to 65340 bytes per field, out of the allowed 65536. The current driver has
another level of rate limiting; it won't accept -q values that fill more than
6/8 of the specified buffers. (I'm not sure why. "Playing it safe" seem to be
a safe bet. Personally, I think I would have lowered requested-bits-per-block
by one, or something like that.) We can't use 165 bits per block, but have to
lower it again, to 6/8 of the available buffer space: We end up with 124 bits
per block, the equivalence of -q24. With 128kB buffers, you can't use greater
than -q24 at -d1. (And PAL, and 704 pixels width...)
The third example is limited to -q24 through the same process. The second
example, using very similar calculations, is limited to -q48. The only
example that actually grab at the specified -q value is the last one, which
is clearly visible, looking at the file size.
--
Conclusion: the quality of the resulting movie depends on buffer size, quality,
whether or not you use 'low_bitrate=1' as insmod option for the zr36060.c
module to do 1:4 instead of 1:2 compression, etc.
If you experience timeouts, lowering the quality/buffersize or using
'low_bitrate=1 as insmod option for zr36060.o might actually help, as is
proven by the Buz.
===========================
7. It hangs/crashes/fails/whatevers! Help!
Make sure that the card has its own interrupts (see /proc/interrupts), check
the output of dmesg at high verbosity (load zoran.o/zr36067.o with debug=2,
load all other modules with debug=1). Check that your mainboard is favorable
(see question 2) and if not, test the card in another computer. Also see the
notes given in question 3 and try lowering quality/buffersize/capturesize
if recording fails after a period of time.
If all this doesn't help, give a clear description of the problem including
detailed hardware information (memory+brand, mainboard+chipset+brand, which
MJPEG card, processor, other PCI cards that might be of interest), give the
system PnP information (/proc/interrupts, /proc/dma, /proc/devices), and give
the kernel version, driver version, glibc version, gcc version and any other
information that might possibly be of interest. Also provide the dmesg output
at high verbosity. See 'Contacting' on how to contact the developers.
===========================
8. Maintainers/Contacting
The driver is currently maintained by Laurent Pinchart and Ronald Bultje
(<laurent.pinchart@skynet.be> and <rbultje@ronald.bitfreak.net>). For bug
reports or questions, please contact the mailinglist instead of the developers
individually. For user questions (i.e. bug reports or how-to questions), send
an email to <mjpeg-users@lists.sf.net>, for developers (i.e. if you want to
help programming), send an email to <mjpeg-developer@lists.sf.net>. See
http://www.sf.net/projects/mjpeg/ for subscription information.
For bug reports, be sure to include all the information as described in
the section 'It hangs/crashes/fails/whatevers! Help!'. Please make sure
you're using the latest version (http://mjpeg.sf.net/driver-zoran/).
Previous maintainers/developers of this driver include Serguei Miridonov
<mirsev@cicese.mx>, Wolfgang Scherr <scherr@net4you.net>, Dave Perks
<dperks@ibm.net> and Rainer Johanni <Rainer@Johanni.de>.
===========================
9. License
This driver is distributed under the terms of the General Public License.
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.
See http://www.gnu.org/ for more information.
...@@ -2253,6 +2253,13 @@ M: fuganti@netbank.com.br ...@@ -2253,6 +2253,13 @@ M: fuganti@netbank.com.br
W: http://cvs.conectiva.com.br/drivers/ZFL-watchdog/ W: http://cvs.conectiva.com.br/drivers/ZFL-watchdog/
S: Maintained S: Maintained
ZR36067 VIDEO FOR LINUX DRIVER
P: Ronald Bultje
M: R.S.Bultje@pharm.uu.nl
L: mjpeg-users@lists.sourceforge.net
W: http://mjpeg.sourceforge.net/driver-zoran/
S: Maintained
ZR36120 VIDEO FOR LINUX DRIVER ZR36120 VIDEO FOR LINUX DRIVER
P: Pauline Middelink P: Pauline Middelink
M: middelin@polyware.nl M: middelin@polyware.nl
......
...@@ -160,33 +160,54 @@ config VIDEO_STRADIS ...@@ -160,33 +160,54 @@ config VIDEO_STRADIS
<http://www.stradis.com/decoder.html>. <http://www.stradis.com/decoder.html>.
config VIDEO_ZORAN config VIDEO_ZORAN
tristate "Zoran ZR36057/36060 Video For Linux" tristate "Zoran ZR36057/36067 Video For Linux"
depends on VIDEO_DEV && PCI && I2C depends on VIDEO_DEV && PCI && I2C
help help
Say Y here to include support for video cards based on the Zoran Say Y for support for MJPEG capture cards based on the Zoran
ZR36057/36060 encoder/decoder chip (including the Iomega Buz and the 36057/36067 PCI controller chipset. This includes the Iomega
Miro DC10 and DC30 video capture cards). Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is
a driver homepage at <http://mjpeg.sf.net/driver-zoran/>. For
more information, check <file:Documentation/video4linux/Zoran>.
This driver is available as a module called zr36067 ( = code
which can be inserted in and removed from the running kernel
whenever you want). If you want to compile it as a module, say M
here and read <file:Documentation/modules.txt>.
config VIDEO_ZORAN_BUZ config VIDEO_ZORAN_BUZ
tristate "Iomega Buz support" tristate "Iomega Buz support"
depends on VIDEO_ZORAN depends on VIDEO_ZORAN
help help
Say Y here to include support for the Iomega Buz video card. There Support for the Iomega Buz MJPEG capture/playback card.
is a Buz/Linux homepage at <http://www.lysator.liu.se/~gz/buz/>.
config VIDEO_ZORAN_DC10 config VIDEO_ZORAN_DC10
tristate "Miro DC10(+) support" tristate "Pinnacle/Miro DC10(+) support"
depends on VIDEO_ZORAN depends on VIDEO_ZORAN
help help
Say Y to support the Pinnacle Systems Studio DC10 plus TV/Video Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback
card. Vendor page at <http://www.pinnaclesys.com/>. card.
config VIDEO_ZORAN_DC30
tristate "Pinnacle/Miro DC30(+) support"
depends on VIDEO_ZORAN
help
Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback
card. This also supports really old DC10 cards based on the
zr36050 MJPEG codec and zr36016 VFE.
config VIDEO_ZORAN_LML33 config VIDEO_ZORAN_LML33
tristate "Linux Media Labs LML33 support" tristate "Linux Media Labs LML33 support"
depends on VIDEO_ZORAN depends on VIDEO_ZORAN
help help
Say Y here to support the Linux Media Labs LML33 TV/Video card. Support for the Linux Media Labs LML33 MJPEG capture/playback
Resources page is at <http://www.linuxmedialabs.com/lml33doc.html>. card.
config VIDEO_ZORAN_LML33R10
tristate "Linux Media Labs LML33R10 support"
depends on VIDEO_ZORAN
help
support for the Linux Media Labs LML33R10 MJPEG capture/playback
card.
config VIDEO_ZR36120 config VIDEO_ZR36120
tristate "Zoran ZR36120/36125 Video For Linux" tristate "Zoran ZR36120/36125 Video For Linux"
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
bttv-risc.o bttv-vbi.o bttv-risc.o bttv-vbi.o
zoran-objs := zr36120.o zr36120_i2c.o zr36120_mem.o zoran-objs := zr36120.o zr36120_i2c.o zr36120_mem.o
zr36067-objs := zoran_procfs.o zoran_device.o \
zoran_driver.o zoran_card.o
obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o
...@@ -17,10 +19,13 @@ obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o ...@@ -17,10 +19,13 @@ obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o
obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o
obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o
obj-$(CONFIG_VIDEO_W9966) += w9966.o obj-$(CONFIG_VIDEO_W9966) += w9966.o
obj-$(CONFIG_VIDEO_ZORAN_BUZ) += saa7111.o saa7185.o obj-$(CONFIG_VIDEO_ZORAN_BUZ) += saa7111.o saa7185.o zr36060.o
obj-$(CONFIG_VIDEO_ZORAN_DC10) += saa7110.o adv7175.o obj-$(CONFIG_VIDEO_ZORAN_DC10) += saa7110.o adv7175.o zr36060.o
obj-$(CONFIG_VIDEO_ZORAN_LML33) += bt819.o bt856.o obj-$(CONFIG_VIDEO_ZORAN_DC30) += adv7175.o vpx3220.o zr36050.o \
obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o zr36016.o
obj-$(CONFIG_VIDEO_ZORAN_LML33) += bt819.o bt856.o zr36060.o
obj-$(CONFIG_VIDEO_ZORAN_LML33R10) += saa7114.o adv7170.o zr36060.o
obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o
obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_PMS) += pms.o
obj-$(CONFIG_VIDEO_PLANB) += planb.o obj-$(CONFIG_VIDEO_PLANB) += planb.o
obj-$(CONFIG_VIDEO_VINO) += vino.o obj-$(CONFIG_VIDEO_VINO) += vino.o
......
/*
* adv7170 - adv7170, adv7171 video encoder driver version 0.0.1
*
* Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
*
* Based on adv7176 driver by:
*
* Copyright (C) 1998 Dave Perks <dperks@ibm.net>
* Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
* - some corrections for Pinnacle Systems Inc. DC10plus card.
*
* Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
* - moved over to linux>=2.4.x i2c protocol (1/1/2003)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/signal.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/sched.h>
#include <asm/segment.h>
#include <linux/types.h>
#include <linux/videodev.h>
#include <linux/version.h>
#include <asm/uaccess.h>
MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver");
MODULE_AUTHOR("Maxim Yevtyushkin");
MODULE_LICENSE("GPL");
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#define I2C_NAME(x) (x)->dev.name
#include <linux/video_encoder.h>
static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
/* ----------------------------------------------------------------------- */
struct adv7170 {
unsigned char reg[128];
int norm;
int input;
int enable;
int bright;
int contrast;
int hue;
int sat;
};
#define I2C_ADV7170 0xd4
#define I2C_ADV7171 0x54
static char adv7170_name[] = "adv7170";
static char adv7171_name[] = "adv7171";
static char *inputs[] = { "pass_through", "play_back" };
static char *norms[] = { "PAL", "NTSC" };
/* ----------------------------------------------------------------------- */
static inline int
adv7170_write (struct i2c_client *client,
u8 reg,
u8 value)
{
struct adv7170 *encoder = i2c_get_clientdata(client);
encoder->reg[reg] = value;
return i2c_smbus_write_byte_data(client, reg, value);
}
static inline int
adv7170_read (struct i2c_client *client,
u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static int
adv7170_write_block (struct i2c_client *client,
const u8 *data,
unsigned int len)
{
int ret = -1;
u8 reg;
/* the adv7170 has an autoincrement function, use it if
* the adapter understands raw I2C */
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
/* do raw I2C, not smbus compatible */
struct adv7170 *encoder = i2c_get_clientdata(client);
struct i2c_msg msg;
u8 block_data[32];
msg.addr = client->addr;
msg.flags = client->flags;
while (len >= 2) {
msg.buf = (char *) block_data;
msg.len = 0;
block_data[msg.len++] = reg = data[0];
do {
block_data[msg.len++] =
encoder->reg[reg++] = data[1];
len -= 2;
data += 2;
} while (len >= 2 && data[0] == reg &&
msg.len < 32);
if ((ret =
i2c_transfer(client->adapter, &msg, 1)) < 0)
break;
}
} else {
/* do some slow I2C emulation kind of thing */
while (len >= 2) {
reg = *data++;
if ((ret =
adv7170_write(client, reg, *data++)) < 0)
break;
len -= 2;
}
}
return ret;
}
/* ----------------------------------------------------------------------- */
// Output filter: S-Video Composite
#define MR050 0x11 //0x09
#define MR060 0x14 //0x0c
//---------------------------------------------------------------------------
#define TR0MODE 0x4c
#define TR0RST 0x80
#define TR1CAPT 0x00
#define TR1PLAY 0x00
static const unsigned char init_NTSC[] = {
0x00, 0x10, // MR0
0x01, 0x20, // MR1
0x02, 0x0e, // MR2 RTC control: bits 2 and 1
0x03, 0x80, // MR3
0x04, 0x30, // MR4
0x05, 0x00, // Reserved
0x06, 0x00, // Reserved
0x07, TR0MODE, // TM0
0x08, TR1CAPT, // TM1
0x09, 0x16, // Fsc0
0x0a, 0x7c, // Fsc1
0x0b, 0xf0, // Fsc2
0x0c, 0x21, // Fsc3
0x0d, 0x00, // Subcarrier Phase
0x0e, 0x00, // Closed Capt. Ext 0
0x0f, 0x00, // Closed Capt. Ext 1
0x10, 0x00, // Closed Capt. 0
0x11, 0x00, // Closed Capt. 1
0x12, 0x00, // Pedestal Ctl 0
0x13, 0x00, // Pedestal Ctl 1
0x14, 0x00, // Pedestal Ctl 2
0x15, 0x00, // Pedestal Ctl 3
0x16, 0x00, // CGMS_WSS_0
0x17, 0x00, // CGMS_WSS_1
0x18, 0x00, // CGMS_WSS_2
0x19, 0x00, // Teletext Ctl
};
static const unsigned char init_PAL[] = {
0x00, 0x71, // MR0
0x01, 0x20, // MR1
0x02, 0x0e, // MR2 RTC control: bits 2 and 1
0x03, 0x80, // MR3
0x04, 0x30, // MR4
0x05, 0x00, // Reserved
0x06, 0x00, // Reserved
0x07, TR0MODE, // TM0
0x08, TR1CAPT, // TM1
0x09, 0xcb, // Fsc0
0x0a, 0x8a, // Fsc1
0x0b, 0x09, // Fsc2
0x0c, 0x2a, // Fsc3
0x0d, 0x00, // Subcarrier Phase
0x0e, 0x00, // Closed Capt. Ext 0
0x0f, 0x00, // Closed Capt. Ext 1
0x10, 0x00, // Closed Capt. 0
0x11, 0x00, // Closed Capt. 1
0x12, 0x00, // Pedestal Ctl 0
0x13, 0x00, // Pedestal Ctl 1
0x14, 0x00, // Pedestal Ctl 2
0x15, 0x00, // Pedestal Ctl 3
0x16, 0x00, // CGMS_WSS_0
0x17, 0x00, // CGMS_WSS_1
0x18, 0x00, // CGMS_WSS_2
0x19, 0x00, // Teletext Ctl
};
static int
adv7170_command (struct i2c_client *client,
unsigned int cmd,
void * arg)
{
struct adv7170 *encoder = i2c_get_clientdata(client);
switch (cmd) {
case 0:
#if 0
/* This is just for testing!!! */
adv7170_write_block(client, init_common,
sizeof(init_common));
adv7170_write(client, 0x07, TR0MODE | TR0RST);
adv7170_write(client, 0x07, TR0MODE);
#endif
break;
case ENCODER_GET_CAPABILITIES:
{
struct video_encoder_capability *cap = arg;
cap->flags = VIDEO_ENCODER_PAL |
VIDEO_ENCODER_NTSC;
cap->inputs = 2;
cap->outputs = 1;
}
break;
case ENCODER_SET_NORM:
{
int iarg = *(int *) arg;
dprintk(1, KERN_DEBUG "%s_command: set norm %d",
I2C_NAME(client), iarg);
switch (iarg) {
case VIDEO_MODE_NTSC:
adv7170_write_block(client, init_NTSC,
sizeof(init_NTSC));
if (encoder->input == 0)
adv7170_write(client, 0x02, 0x0e); // Enable genlock
adv7170_write(client, 0x07, TR0MODE | TR0RST);
adv7170_write(client, 0x07, TR0MODE);
break;
case VIDEO_MODE_PAL:
adv7170_write_block(client, init_PAL,
sizeof(init_PAL));
if (encoder->input == 0)
adv7170_write(client, 0x02, 0x0e); // Enable genlock
adv7170_write(client, 0x07, TR0MODE | TR0RST);
adv7170_write(client, 0x07, TR0MODE);
break;
default:
dprintk(1, KERN_ERR "%s: illegal norm: %d\n",
I2C_NAME(client), iarg);
return -EINVAL;
}
dprintk(1, KERN_DEBUG "%s: switched to %s\n", I2C_NAME(client),
norms[iarg]);
encoder->norm = iarg;
}
break;
case ENCODER_SET_INPUT:
{
int iarg = *(int *) arg;
/* RJ: *iarg = 0: input is from decoder
*iarg = 1: input is from ZR36060
*iarg = 2: color bar */
dprintk(1, KERN_DEBUG "%s_command: set input from %s\n",
I2C_NAME(client),
iarg == 0 ? "decoder" : "ZR36060");
switch (iarg) {
case 0:
adv7170_write(client, 0x01, 0x20);
adv7170_write(client, 0x08, TR1CAPT); /* TR1 */
adv7170_write(client, 0x02, 0x0e); // Enable genlock
adv7170_write(client, 0x07, TR0MODE | TR0RST);
adv7170_write(client, 0x07, TR0MODE);
//udelay(10);
break;
case 1:
adv7170_write(client, 0x01, 0x00);
adv7170_write(client, 0x08, TR1PLAY); /* TR1 */
adv7170_write(client, 0x02, 0x08);
adv7170_write(client, 0x07, TR0MODE | TR0RST);
adv7170_write(client, 0x07, TR0MODE);
//udelay(10);
break;
default:
dprintk(1, KERN_ERR "%s: illegal input: %d\n",
I2C_NAME(client), iarg);
return -EINVAL;
}
dprintk(1, KERN_DEBUG "%s: switched to %s\n", I2C_NAME(client),
inputs[iarg]);
encoder->input = iarg;
}
break;
case ENCODER_SET_OUTPUT:
{
int *iarg = arg;
/* not much choice of outputs */
if (*iarg != 0) {
return -EINVAL;
}
}
break;
case ENCODER_ENABLE_OUTPUT:
{
int *iarg = arg;
encoder->enable = !!*iarg;
}
break;
default:
return -EINVAL;
}
return 0;
}
/* ----------------------------------------------------------------------- */
/*
* Generic i2c probe
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
static unsigned short normal_i2c[] =
{ I2C_ADV7170 >> 1, (I2C_ADV7170 >> 1) + 1,
I2C_ADV7171 >> 1, (I2C_ADV7171 >> 1) + 1,
I2C_CLIENT_END
};
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_i2c_range = normal_i2c_range,
.probe = probe,
.probe_range = probe_range,
.ignore = ignore,
.ignore_range = ignore_range,
.force = force
};
static int adv7170_i2c_id = 0;
static struct i2c_driver i2c_driver_adv7170;
static int
adv7170_detect_client (struct i2c_adapter *adapter,
int address,
int kind)
{
int i;
struct i2c_client *client;
struct adv7170 *encoder;
char *dname;
dprintk(1,
KERN_INFO
"adv7170.c: detecting adv7170 client on address 0x%x\n",
address << 1);
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == 0)
return -ENOMEM;
memset(client, 0, sizeof(struct i2c_client));
client->addr = address;
client->adapter = adapter;
client->driver = &i2c_driver_adv7170;
client->flags = I2C_CLIENT_ALLOW_USE;
client->id = adv7170_i2c_id++;
if ((client->addr == I2C_ADV7170 >> 1) ||
(client->addr == (I2C_ADV7170 >> 1) + 1)) {
dname = adv7170_name;
} else if ((client->addr == I2C_ADV7171 >> 1) ||
(client->addr == (I2C_ADV7171 >> 1) + 1)) {
dname = adv7171_name;
} else {
/* We should never get here!!! */
return 0;
}
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"%s[%d]", dname, client->id);
encoder = kmalloc(sizeof(struct adv7170), GFP_KERNEL);
if (encoder == NULL) {
return -ENOMEM;
}
memset(encoder, 0, sizeof(struct adv7170));
encoder->norm = VIDEO_MODE_NTSC;
encoder->input = 0;
encoder->enable = 1;
i2c_set_clientdata(client, encoder);
i = i2c_attach_client(client);
if (i) {
kfree(client);
kfree(encoder);
return i;
}
i = adv7170_write_block(client, init_NTSC, sizeof(init_NTSC));
if (i >= 0) {
i = adv7170_write(client, 0x07, TR0MODE | TR0RST);
i = adv7170_write(client, 0x07, TR0MODE);
i = adv7170_read(client, 0x12);
dprintk(1, KERN_INFO "%s_attach: rev. %d at 0x%02x\n",
I2C_NAME(client), i & 1, client->addr << 1);
}
if (i < 0) {
dprintk(1, KERN_ERR "%s_attach: init error 0x%x\n",
I2C_NAME(client), i);
}
return 0;
}
static int
adv7170_attach_adapter (struct i2c_adapter *adapter)
{
dprintk(1,
KERN_INFO
"adv7170.c: starting probe for adapter %s (0x%x)\n",
I2C_NAME(adapter), adapter->id);
return i2c_probe(adapter, &addr_data, &adv7170_detect_client);
}
static int
adv7170_detach_client (struct i2c_client *client)
{
struct adv7170 *encoder = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
return err;
}
kfree(encoder);
kfree(client);
return 0;
}
/* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_adv7170 = {
.owner = THIS_MODULE,
.name = "adv7170", /* name */
.id = I2C_DRIVERID_ADV7170,
.flags = I2C_DF_NOTIFY,
.attach_adapter = adv7170_attach_adapter,
.detach_client = adv7170_detach_client,
.command = adv7170_command,
};
static int __init
adv7170_init (void)
{
return i2c_add_driver(&i2c_driver_adv7170);
}
static void __exit
adv7170_exit (void)
{
i2c_del_driver(&i2c_driver_adv7170);
}
module_init(adv7170_init);
module_exit(adv7170_exit);
#define DEBUGLEVEL 0
/* /*
adv7175 - adv7175a video encoder driver version 0.0.3 * adv7175 - adv7175a video encoder driver version 0.0.3
*
* Copyright (C) 1998 Dave Perks <dperks@ibm.net>
* Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
* - some corrections for Pinnacle Systems Inc. DC10plus card.
*
* Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
* - moved over to linux>=2.4.x i2c protocol (9/9/2002)
*
* 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.
*/
Copyright (C) 1998 Dave Perks <dperks@ibm.net> #include <linux/version.h>
Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
- some corrections for Pinnacle Systems Inc. DC10plus card.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -38,67 +41,138 @@ ...@@ -38,67 +41,138 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/page.h> #include <asm/page.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <asm/segment.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/videodev.h> #include <linux/videodev.h>
#include <linux/version.h> #include <linux/version.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver");
MODULE_AUTHOR("Dave Perks");
MODULE_LICENSE("GPL");
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c-dev.h>
#define I2C_NAME(s) (s)->dev.name
#include <linux/video_encoder.h> #include <linux/video_encoder.h>
#if (DEBUGLEVEL > 0) static int debug = 0;
#define DEBUG(x...) x /* Debug driver */ MODULE_PARM(debug, "i");
#else MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define DEBUG(x...)
#endif
#define I2C_ADV7175 0xd4 #define dprintk(num, format, args...) \
#define I2C_ADV7176 0x54 do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
#define IF_NAME "adv7175" /* ----------------------------------------------------------------------- */
struct adv7175 {
unsigned char reg[128];
int norm;
int input;
int enable;
int bright;
int contrast;
int hue;
int sat;
};
#define I2C_ADV7175 0xd4
#define I2C_ADV7176 0x54
static char adv7175_name[] = "adv7175"; static char adv7175_name[] = "adv7175";
static char adv7176_name[] = "adv7176"; static char adv7176_name[] = "adv7176";
static char unknown_name[] = "UNKNOWN";
char *dname;
#if (DEBUGLEVEL > 0)
static char *inputs[] = { "pass_through", "play_back", "color_bar" }; static char *inputs[] = { "pass_through", "play_back", "color_bar" };
static char *norms[] = { "PAL", "NTSC", "SECAM->PAL (may not work!)" }; static char *norms[] = { "PAL", "NTSC", "SECAM->PAL (may not work!)" };
#endif
#define I2C_DELAY 10 /* ----------------------------------------------------------------------- */
static unsigned short normal_i2c[] = {I2C_ADV7175, I2C_CLIENT_END}; static inline int
static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; adv7175_write (struct i2c_client *client,
static unsigned short probe[2] = {I2C_CLIENT_END, I2C_CLIENT_END}; u8 reg,
static unsigned short probe_range[2] = {I2C_CLIENT_END, I2C_CLIENT_END}; u8 value)
static unsigned short ignore[2] = {I2C_CLIENT_END, I2C_CLIENT_END}; {
static unsigned short ignore_range[2] = {I2C_CLIENT_END, I2C_CLIENT_END}; struct adv7175 *encoder = i2c_get_clientdata(client);
static unsigned short force[2] = {I2C_CLIENT_END, I2C_CLIENT_END}; encoder->reg[reg] = value;
return i2c_smbus_write_byte_data(client, reg, value);
}
static struct i2c_client_address_data addr_data = { static inline int
normal_i2c, normal_i2c_range, adv7175_read (struct i2c_client *client,
probe, probe_range, u8 reg)
ignore, ignore_range, {
force}; return i2c_smbus_read_byte_data(client, reg);
static struct i2c_client client_template; }
struct adv7175 { static int
struct i2c_client *client; adv7175_write_block (struct i2c_client *client,
int addr; const u8 *data,
unsigned char reg[128]; unsigned int len)
struct semaphore lock; {
int norm; int ret = -1;
int input; u8 reg;
int enable;
int bright; /* the adv7175 has an autoincrement function, use it if
int contrast; * the adapter understands raw I2C */
int hue; if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
int sat; /* do raw I2C, not smbus compatible */
}; struct adv7175 *encoder = i2c_get_clientdata(client);
struct i2c_msg msg;
u8 block_data[32];
msg.addr = client->addr;
msg.flags = client->flags;
while (len >= 2) {
msg.buf = (char *) block_data;
msg.len = 0;
block_data[msg.len++] = reg = data[0];
do {
block_data[msg.len++] =
encoder->reg[reg++] = data[1];
len -= 2;
data += 2;
} while (len >= 2 && data[0] == reg &&
msg.len < 32);
if ((ret =
i2c_transfer(client->adapter, &msg, 1)) < 0)
break;
}
} else {
/* do some slow I2C emulation kind of thing */
while (len >= 2) {
reg = *data++;
if ((ret =
adv7175_write(client, reg, *data++)) < 0)
break;
len -= 2;
}
}
return ret;
}
#ifdef ENCODER_DUMP
static void
dump (struct i2c_client *client)
{
struct adv7175 *encoder = i2c_get_clientdata(client);
int i, j;
printk(KERN_INFO "%s: registry dump\n", I2C_NAME(client));
for (i = 0; i < 182 / 8; i++) {
printk("%s: 0x%02x -", I2C_NAME(client), i * 8);
for (j = 0; j < 8; j++) {
printk(" 0x%02x", encoder->reg[i * 8 + j]);
}
printk("\n");
}
}
#endif
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
// Output filter: S-Video Composite // Output filter: S-Video Composite
...@@ -135,8 +209,6 @@ static const unsigned char init_common[] = { ...@@ -135,8 +209,6 @@ static const unsigned char init_common[] = {
0x0f, 0x00, /* */ 0x0f, 0x00, /* */
0x10, 0x00, /* */ 0x10, 0x00, /* */
0x11, 0x00, /* */ 0x11, 0x00, /* */
0x12, 0x00, /* MR3 */
0x24, 0x00, /* */
}; };
static const unsigned char init_pal[] = { static const unsigned char init_pal[] = {
...@@ -159,284 +231,328 @@ static const unsigned char init_ntsc[] = { ...@@ -159,284 +231,328 @@ static const unsigned char init_ntsc[] = {
0x06, 0x1a, /* subc. phase */ 0x06, 0x1a, /* subc. phase */
}; };
static int adv717x_attach(struct i2c_adapter *adap, int addr, int kind) static int
adv7175_command (struct i2c_client *client,
unsigned int cmd,
void *arg)
{ {
struct adv7175 *encoder; struct adv7175 *encoder = i2c_get_clientdata(client);
struct i2c_client *client;
int rv = 0;
int i, x_common=39; /* x is number entries init_common - 1 */
printk(KERN_INFO "adv717x: video chip found.\n"); switch (cmd) {
client=kmalloc(sizeof(*client), GFP_KERNEL);
if(client == NULL)
return -ENOMEM;
memset(client, 0, sizeof(*client));
client_template.adapter = adap; case 0:
client_template.addr = addr; /* This is just for testing!!! */
memcpy(client, &client_template, sizeof(*client)); adv7175_write_block(client, init_common,
sizeof(init_common));
adv7175_write(client, 0x07, TR0MODE | TR0RST);
adv7175_write(client, 0x07, TR0MODE);
break;
encoder = kmalloc(sizeof(*encoder), GFP_KERNEL); case ENCODER_GET_CAPABILITIES:
if (encoder == NULL) { {
kfree(client); struct video_encoder_capability *cap = arg;
return -ENOMEM;
cap->flags = VIDEO_ENCODER_PAL |
VIDEO_ENCODER_NTSC |
VIDEO_ENCODER_SECAM; /* well, hacky */
cap->inputs = 2;
cap->outputs = 1;
} }
break;
memset(encoder, 0, sizeof(*encoder)); case ENCODER_SET_NORM:
if ((encoder->addr == I2C_ADV7175) || (encoder->addr == (I2C_ADV7175 + 2))) { {
dname = adv7175_name; int iarg = *(int *) arg;
} else if ((encoder->addr == I2C_ADV7176) || (encoder->addr == (I2C_ADV7176 + 2))) {
dname = adv7176_name; switch (iarg) {
} else {
// We should never get here!!! case VIDEO_MODE_NTSC:
dname = unknown_name; adv7175_write_block(client, init_ntsc,
sizeof(init_ntsc));
if (encoder->input == 0)
adv7175_write(client, 0x0d, 0x4f); // Enable genlock
adv7175_write(client, 0x07, TR0MODE | TR0RST);
adv7175_write(client, 0x07, TR0MODE);
break;
case VIDEO_MODE_PAL:
adv7175_write_block(client, init_pal,
sizeof(init_pal));
if (encoder->input == 0)
adv7175_write(client, 0x0d, 0x4f); // Enable genlock
adv7175_write(client, 0x07, TR0MODE | TR0RST);
adv7175_write(client, 0x07, TR0MODE);
break;
case VIDEO_MODE_SECAM: // WARNING! ADV7176 does not support SECAM.
/* This is an attempt to convert
* SECAM->PAL (typically it does not work
* due to genlock: when decoder is in SECAM
* and encoder in in PAL the subcarrier can
* not be syncronized with horizontal
* quency) */
adv7175_write_block(client, init_pal,
sizeof(init_pal));
if (encoder->input == 0)
adv7175_write(client, 0x0d, 0x49); // Disable genlock
adv7175_write(client, 0x07, TR0MODE | TR0RST);
adv7175_write(client, 0x07, TR0MODE);
break;
default:
dprintk(1, KERN_ERR "%s: illegal norm: %d\n",
I2C_NAME(client), iarg);
return -EINVAL;
}
dprintk(1, KERN_INFO "%s: switched to %s\n", I2C_NAME(client),
norms[iarg]);
encoder->norm = iarg;
} }
strlcpy(client->name, dname, DEVICE_NAME_SIZE); break;
init_MUTEX(&encoder->lock);
encoder->client = client; case ENCODER_SET_INPUT:
i2c_set_clientdata(client, encoder); {
encoder->addr = addr; int iarg = *(int *) arg;
encoder->norm = VIDEO_MODE_PAL;
encoder->input = 0; /* RJ: *iarg = 0: input is from SAA7110
encoder->enable = 1; *iarg = 1: input is from ZR36060
*iarg = 2: color bar */
switch (iarg) {
case 0:
adv7175_write(client, 0x01, 0x00);
adv7175_write(client, 0x0c, TR1CAPT); /* TR1 */
if (encoder->norm == VIDEO_MODE_SECAM)
adv7175_write(client, 0x0d, 0x49); // Disable genlock
else
adv7175_write(client, 0x0d, 0x4f); // Enable genlock
adv7175_write(client, 0x07, TR0MODE | TR0RST);
adv7175_write(client, 0x07, TR0MODE);
//udelay(10);
break;
for (i=1; i<x_common; i++) { case 1:
rv = i2c_smbus_write_byte(client,init_common[i]); adv7175_write(client, 0x01, 0x00);
if (rv < 0) { adv7175_write(client, 0x0c, TR1PLAY); /* TR1 */
printk(KERN_ERR "%s_attach: init error %d\n", client->name, rv); adv7175_write(client, 0x0d, 0x49);
adv7175_write(client, 0x07, TR0MODE | TR0RST);
adv7175_write(client, 0x07, TR0MODE);
//udelay(10);
break; break;
case 2:
adv7175_write(client, 0x01, 0x80);
adv7175_write(client, 0x0d, 0x49);
adv7175_write(client, 0x07, TR0MODE | TR0RST);
adv7175_write(client, 0x07, TR0MODE);
//udelay(10);
break;
default:
dprintk(1, KERN_ERR "%s: illegal input: %d\n",
I2C_NAME(client), iarg);
return -EINVAL;
}
dprintk(1, KERN_INFO "%s: switched to %s\n", I2C_NAME(client),
inputs[iarg]);
encoder->input = iarg;
}
break;
case ENCODER_SET_OUTPUT:
{
int *iarg = arg;
/* not much choice of outputs */
if (*iarg != 0) {
return -EINVAL;
} }
} }
break;
case ENCODER_ENABLE_OUTPUT:
{
int *iarg = arg;
if (rv >= 0) { encoder->enable = !!*iarg;
i2c_smbus_write_byte_data(client,0x07, TR0MODE | TR0RST);
i2c_smbus_write_byte_data(client,0x07, TR0MODE);
i2c_smbus_read_byte_data(client,0x12);
printk(KERN_INFO "%s_attach: %s rev. %d at 0x%02x\n",
client->name, dname, rv & 1, client->addr);
} }
break;
i2c_attach_client(client); #ifdef ENCODER_DUMP
case ENCODER_DUMP:
{
dump(client);
}
break;
#endif
default:
return -EINVAL;
}
return 0; return 0;
} }
static /* ----------------------------------------------------------------------- */
int adv717x_probe(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, adv717x_attach);
}
/*
* Generic i2c probe
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
static unsigned short normal_i2c[] =
{ I2C_ADV7175 >> 1, (I2C_ADV7175 >> 1) + 1,
I2C_ADV7176 >> 1, (I2C_ADV7176 >> 1) + 1,
I2C_CLIENT_END
};
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_i2c_range = normal_i2c_range,
.probe = probe,
.probe_range = probe_range,
.ignore = ignore,
.ignore_range = ignore_range,
.force = force
};
static int adv717x_detach(struct i2c_client *client) static int adv7175_i2c_id = 0;
{ static struct i2c_driver i2c_driver_adv7175;
i2c_detach_client(client);
kfree(i2c_get_clientdata(client));
kfree(client);
return 0;
}
static int adv717x_command(struct i2c_client *client, unsigned int cmd, static int
void *arg) adv7175_detect_client (struct i2c_adapter *adapter,
int address,
int kind)
{ {
struct adv7175 *encoder = i2c_get_clientdata(client); int i;
int i, x_ntsc=13, x_pal=13; struct i2c_client *client;
/* x_ntsc is number of entries in init_ntsc -1 */ struct adv7175 *encoder;
/* x_pal is number of entries in init_pal -1 */ char *dname;
switch (cmd) { dprintk(1,
KERN_INFO
"adv7175.c: detecting adv7175 client on address 0x%x\n",
address << 1);
case ENCODER_GET_CAPABILITIES: /* Check if the adapter supports the needed features */
{ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
struct video_encoder_capability *cap = arg; return 0;
cap->flags = VIDEO_ENCODER_PAL | VIDEO_ENCODER_NTSC
// | VIDEO_ENCODER_SECAM
// | VIDEO_ENCODER_CCIR
;
cap->inputs = 2;
cap->outputs = 1;
}
break;
case ENCODER_SET_NORM: client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
{ if (client == 0)
int iarg = *(int *) arg; return -ENOMEM;
memset(client, 0, sizeof(struct i2c_client));
if (encoder->norm != iarg) { client->addr = address;
switch (iarg) { client->adapter = adapter;
client->driver = &i2c_driver_adv7175;
case VIDEO_MODE_NTSC: client->flags = I2C_CLIENT_ALLOW_USE;
for (i=1; i< x_ntsc; i++) client->id = adv7175_i2c_id++;
i2c_smbus_write_byte(client, init_ntsc[i]); if ((client->addr == I2C_ADV7175 >> 1) ||
if (encoder->input == 0) (client->addr == (I2C_ADV7175 >> 1) + 1)) {
i2c_smbus_write_byte_data(client,0x0d, 0x4f); // Enable genlock dname = adv7175_name;
i2c_smbus_write_byte_data(client,0x07, TR0MODE | TR0RST); } else if ((client->addr == I2C_ADV7176 >> 1) ||
i2c_smbus_write_byte_data(client,0x07, TR0MODE); (client->addr == (I2C_ADV7176 >> 1) + 1)) {
break; dname = adv7176_name;
} else {
case VIDEO_MODE_PAL: /* We should never get here!!! */
for (i=1; i< x_pal; i++) return 0;
i2c_smbus_write_byte(client, init_pal[i]); }
if (encoder->input == 0) snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
i2c_smbus_write_byte_data(client,0x0d, 0x4f); // Enable genlock "%s[%d]", dname, client->id);
i2c_smbus_write_byte_data(client,0x07, TR0MODE | TR0RST);
i2c_smbus_write_byte_data(client,0x07, TR0MODE);
break;
case VIDEO_MODE_SECAM: // WARNING! ADV7176 does not support SECAM.
// This is an attempt to convert SECAM->PAL (typically
// it does not work due to genlock: when decoder
// is in SECAM and encoder in in PAL the subcarrier
// can not be syncronized with horizontal frequency)
for (i=1; i< x_pal; i++)
i2c_smbus_write_byte(client, init_pal[i]);
if (encoder->input == 0)
i2c_smbus_write_byte_data(client,0x0d, 0x49); // Disable genlock
i2c_smbus_write_byte_data(client,0x07, TR0MODE | TR0RST);
i2c_smbus_write_byte_data(client,0x07, TR0MODE);
break;
default:
printk(KERN_ERR "%s: illegal norm: %d\n",
client->name, iarg);
return -EINVAL;
}
DEBUG(printk
(KERN_INFO "%s: switched to %s\n",
client->name, norms[iarg]));
encoder->norm = iarg;
}
}
break;
case ENCODER_SET_INPUT: encoder = kmalloc(sizeof(struct adv7175), GFP_KERNEL);
{ if (encoder == NULL) {
int iarg = *(int *) arg; return -ENOMEM;
}
/* RJ: *iarg = 0: input is from SAA7110 memset(encoder, 0, sizeof(struct adv7175));
*iarg = 1: input is from ZR36060 encoder->norm = VIDEO_MODE_PAL;
*iarg = 2: color bar */ encoder->input = 0;
encoder->enable = 1;
if (encoder->input != iarg) { i2c_set_clientdata(client, encoder);
switch (iarg) {
case 0:
i2c_smbus_write_byte_data(client, 0x01, 0x00);
i2c_smbus_write_byte_data(client, 0x0c, TR1CAPT); /* TR1 */
if (encoder->norm ==
VIDEO_MODE_SECAM)
i2c_smbus_write_byte_data(client, 0x0d, 0x49); // Disable genlock
else
i2c_smbus_write_byte_data(client, 0x0d, 0x4f); // Enable genlock
i2c_smbus_write_byte_data(client, 0x07, TR0MODE | TR0RST);
i2c_smbus_write_byte_data(client, 0x07, TR0MODE);
//udelay(10);
break;
case 1:
i2c_smbus_write_byte_data(client, 0x01, 0x00);
i2c_smbus_write_byte_data(client, 0x0c, TR1PLAY); /* TR1 */
i2c_smbus_write_byte_data(client, 0x0d, 0x49);
i2c_smbus_write_byte_data(client, 0x07, TR0MODE | TR0RST);
i2c_smbus_write_byte_data(client, 0x07, TR0MODE);
//udelay(10);
break;
case 2:
i2c_smbus_write_byte_data(client, 0x01, 0x80);
i2c_smbus_write_byte_data(client, 0x0d, 0x49);
i2c_smbus_write_byte_data(client, 0x07, TR0MODE | TR0RST);
i2c_smbus_write_byte_data(client, 0x07, TR0MODE);
//udelay(10);
break;
default:
printk(KERN_ERR "%s: illegal input: %d\n",
client->name, iarg);
return -EINVAL;
}
DEBUG(printk
(KERN_INFO "%s: switched to %s\n",
client->name, inputs[iarg]));
encoder->input = iarg;
}
}
break;
case ENCODER_SET_OUTPUT: i = i2c_attach_client(client);
{ if (i) {
int *iarg = arg; kfree(client);
kfree(encoder);
return i;
}
/* not much choice of outputs */ i = adv7175_write_block(client, init_common, sizeof(init_common));
if (*iarg != 0) { if (i >= 0) {
return -EINVAL; i = adv7175_write(client, 0x07, TR0MODE | TR0RST);
} i = adv7175_write(client, 0x07, TR0MODE);
} i = adv7175_read(client, 0x12);
break; dprintk(1, KERN_INFO "%s_attach: rev. %d at 0x%x\n",
I2C_NAME(client), i & 1, client->addr << 1);
}
if (i < 0) {
dprintk(1, KERN_ERR "%s_attach: init error 0x%x\n",
I2C_NAME(client), i);
}
case ENCODER_ENABLE_OUTPUT: return 0;
{ }
int *iarg = arg;
encoder->enable = !!*iarg;
i2c_smbus_write_byte_data(client, 0x61,
(encoder->
reg[0x61] & 0xbf) | (encoder->
enable ? 0x00 :
0x40));
}
break;
default: static int
return -EINVAL; adv7175_attach_adapter (struct i2c_adapter *adapter)
{
dprintk(1,
KERN_INFO
"adv7175.c: starting probe for adapter %s (0x%x)\n",
I2C_NAME(adapter), adapter->id);
return i2c_probe(adapter, &addr_data, &adv7175_detect_client);
}
static int
adv7175_detach_client (struct i2c_client *client)
{
struct adv7175 *encoder = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
return err;
} }
kfree(encoder);
kfree(client);
return 0; return 0;
} }
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_adv7175 = { static struct i2c_driver i2c_driver_adv7175 = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "adv7175", /* name */ .name = "adv7175", /* name */
.id = I2C_DRIVERID_ADV717x, /* ID */
.flags = I2C_DF_NOTIFY, //I2C_ADV7175, I2C_ADV7175 + 3,
.attach_adapter = adv717x_probe,
.detach_client = adv717x_detach,
.command = adv717x_command,
};
static struct i2c_driver i2c_driver_adv7176 = { .id = I2C_DRIVERID_ADV7175,
.owner = THIS_MODULE, .flags = I2C_DF_NOTIFY,
.name = "adv7176", /* name */
.id = I2C_DRIVERID_ADV717x, /* ID */
.flags = I2C_DF_NOTIFY, //I2C_ADV7176, I2C_ADV7176 + 3,
.attach_adapter = adv717x_probe,
.detach_client = adv717x_detach,
.command = adv717x_command,
};
static struct i2c_client client_template = { .attach_adapter = adv7175_attach_adapter,
.driver = &i2c_driver_adv7175, .detach_client = adv7175_detach_client,
.name = "adv7175_client", .command = adv7175_command,
}; };
static int adv717x_init(void) static int __init
adv7175_init (void)
{ {
int res_adv7175 = 0, res_adv7176 = 0; return i2c_add_driver(&i2c_driver_adv7175);
res_adv7175 = i2c_add_driver(&i2c_driver_adv7175);
res_adv7176 = i2c_add_driver(&i2c_driver_adv7176);
return (res_adv7175 | res_adv7176); // Any idea how to make it better?
} }
static void adv717x_exit(void) static void __exit
adv7175_exit (void)
{ {
i2c_del_driver(&i2c_driver_adv7176);
i2c_del_driver(&i2c_driver_adv7175); i2c_del_driver(&i2c_driver_adv7175);
} }
module_init(adv717x_init); module_init(adv7175_init);
module_exit(adv717x_exit); module_exit(adv7175_exit);
MODULE_LICENSE("GPL");
/* /*
bt819 - BT819A VideoStream Decoder (Rockwell Part) * bt819 - BT819A VideoStream Decoder (Rockwell Part)
*
Copyright (C) 1999 Mike Bernson <mike@mlb.org> * Copyright (C) 1999 Mike Bernson <mike@mlb.org>
Copyright (C) 1998 Dave Perks <dperks@ibm.net> * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
*
Modifications for LML33/DC10plus unified driver * Modifications for LML33/DC10plus unified driver
Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
*
This code was modify/ported from the saa7111 driver written * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
by Dave Perks. * - moved over to linux>=2.4.x i2c protocol (9/9/2002)
*
This program is free software; you can redistribute it and/or modify * This code was modify/ported from the saa7111 driver written
it under the terms of the GNU General Public License as published by * by Dave Perks.
the Free Software Foundation; either version 2 of the License, or *
(at your option) any later version. * 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
This program is distributed in the hope that it will be useful, * the Free Software Foundation; either version 2 of the License, or
but WITHOUT ANY WARRANTY; without even the implied warranty of * (at your option) any later version.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
GNU General Public License for more details. * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
You should have received a copy of the GNU General Public License * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
along with this program; if not, write to the Free Software * GNU General Public License for more details.
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
*/ * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/version.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -31,31 +36,46 @@ ...@@ -31,31 +36,46 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/signal.h> #include <linux/signal.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <asm/segment.h>
#include <linux/types.h>
#include <linux/videodev.h> #include <linux/videodev.h>
#include <linux/version.h>
#include <asm/uaccess.h>
MODULE_DESCRIPTION("Brooktree-819 video decoder driver");
MODULE_AUTHOR("Mike Bernson & Dave Perks");
MODULE_LICENSE("GPL");
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/video_decoder.h> #include <linux/i2c-dev.h>
#define DEBUG(x) x /* Debug driver */ #define I2C_NAME(s) (s)->dev.name
/* ----------------------------------------------------------------------- */ #include <linux/video_decoder.h>
static unsigned short normal_i2c[] = {34>>1, I2C_CLIENT_END }; static int debug = 0;
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-1)");
I2C_CLIENT_INSMOD; #define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
static struct i2c_client client_template; /* ----------------------------------------------------------------------- */
struct bt819 { struct bt819 {
struct i2c_client *client;
int addr;
unsigned char reg[32]; unsigned char reg[32];
int initialized; int initialized;
...@@ -66,7 +86,6 @@ struct bt819 { ...@@ -66,7 +86,6 @@ struct bt819 {
int contrast; int contrast;
int hue; int hue;
int sat; int sat;
struct semaphore lock;
}; };
struct timing { struct timing {
...@@ -78,27 +97,95 @@ struct timing { ...@@ -78,27 +97,95 @@ struct timing {
int vscale; int vscale;
}; };
/* for values, see the bt819 datasheet */
struct timing timing_data[] = { struct timing timing_data[] = {
{864 - 24, 2, 623, 1, 0x0504, 0x0000}, {864 - 24, 20, 625 - 2, 1, 0x0504, 0x0000},
{858 - 24, 2, 523, 1, 0x00f8, 0x0000}, {858 - 24, 20, 525 - 2, 1, 0x00f8, 0x0000},
// { 858-68, 64, 523, 1, 0x00f8, 0x0000 },
}; };
#define I2C_BT819 0x8a #define I2C_BT819 0x8a
#define I2C_DELAY 10
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static inline int
bt819_write (struct i2c_client *client,
u8 reg,
u8 value)
{
struct bt819 *decoder = i2c_get_clientdata(client);
decoder->reg[reg] = value;
return i2c_smbus_write_byte_data(client, reg, value);
}
static int bt819_setbit(struct bt819 *dev, int subaddr, int bit, int data) static inline int
bt819_setbit (struct i2c_client *client,
u8 reg,
u8 bit,
u8 value)
{ {
return i2c_smbus_write_byte_data(dev->client, subaddr, (dev->reg[subaddr] & ~(1 << bit)) | (data ? (1 << bit) : 0)); struct bt819 *decoder = i2c_get_clientdata(client);
return bt819_write(client, reg,
(decoder->
reg[reg] & ~(1 << bit)) |
(value ? (1 << bit) : 0));
} }
static int bt819_init(struct i2c_client *client) static int
bt819_write_block (struct i2c_client *client,
const u8 *data,
unsigned int len)
{ {
struct bt819 *decoder; int ret = -1;
u8 reg;
/* the bt819 has an autoincrement function, use it if
* the adapter understands raw I2C */
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
/* do raw I2C, not smbus compatible */
struct bt819 *decoder = i2c_get_clientdata(client);
struct i2c_msg msg;
u8 block_data[32];
msg.addr = client->addr;
msg.flags = client->flags;
while (len >= 2) {
msg.buf = (char *) block_data;
msg.len = 0;
block_data[msg.len++] = reg = data[0];
do {
block_data[msg.len++] =
decoder->reg[reg++] = data[1];
len -= 2;
data += 2;
} while (len >= 2 && data[0] == reg &&
msg.len < 32);
if ((ret =
i2c_transfer(client->adapter, &msg, 1)) < 0)
break;
}
} else {
/* do some slow I2C emulation kind of thing */
while (len >= 2) {
reg = *data++;
if ((ret = bt819_write(client, reg, *data++)) < 0)
break;
len -= 2;
}
}
return ret;
}
static inline int
bt819_read (struct i2c_client *client,
u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static int
bt819_init (struct i2c_client *client)
{
struct bt819 *decoder = i2c_get_clientdata(client);
static unsigned char init[] = { static unsigned char init[] = {
//0x1f, 0x00, /* Reset */ //0x1f, 0x00, /* Reset */
...@@ -118,107 +205,57 @@ static int bt819_init(struct i2c_client *client) ...@@ -118,107 +205,57 @@ static int bt819_init(struct i2c_client *client)
0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */ 0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */
0x0f, 0x00, /* 0x0f Hue control */ 0x0f, 0x00, /* 0x0f Hue control */
0x12, 0x04, /* 0x12 Output Format */ 0x12, 0x04, /* 0x12 Output Format */
0x13, 0x20, /* 0x13 Vertial Scaling msb 0x60, */ 0x13, 0x20, /* 0x13 Vertial Scaling msb 0x00
chroma comb OFF, line drop scaling, interlace scaling
BUG? Why does turning the chroma comb on fuck up color?
Bug in the bt819 stepping on my board?
*/
0x14, 0x00, /* 0x14 Vertial Scaling lsb */ 0x14, 0x00, /* 0x14 Vertial Scaling lsb */
0x16, 0x04, /* 0x16 Video Timing Polarity 0x02, */ 0x16, 0x07, /* 0x16 Video Timing Polarity
ACTIVE=active low
FIELD: high=odd,
vreset=active high,
hreset=active high */
0x18, 0x68, /* 0x18 AGC Delay */ 0x18, 0x68, /* 0x18 AGC Delay */
0x19, 0x5d, /* 0x19 Burst Gate Delay */ 0x19, 0x5d, /* 0x19 Burst Gate Delay */
0x1a, 0x80, /* 0x1a ADC Interface */ 0x1a, 0x80, /* 0x1a ADC Interface */
}; };
struct timing *timing; struct timing *timing = &timing_data[decoder->norm];
decoder = i2c_get_clientdata(client); init[0x03 * 2 - 1] =
timing = &timing_data[decoder->norm]; (((timing->vdelay >> 8) & 0x03) << 6) | (((timing->
vactive >> 8) &
init[3 * 2 - 1] = (((timing->vdelay >> 8) & 0x03) << 6) | 0x03) << 4) |
(((timing->vactive >> 8) & 0x03) << 4) | (((timing->hdelay >> 8) & 0x03) << 2) | ((timing->
(((timing->hdelay >> 8) & 0x03) << 2) | hactive >> 8) &
((timing->hactive >> 8) & 0x03); 0x03);
init[4 * 2 - 1] = timing->vdelay & 0xff; init[0x04 * 2 - 1] = timing->vdelay & 0xff;
init[5 * 2 - 1] = timing->vactive & 0xff; init[0x05 * 2 - 1] = timing->vactive & 0xff;
init[6 * 2 - 1] = timing->hdelay & 0xff; init[0x06 * 2 - 1] = timing->hdelay & 0xff;
init[7 * 2 - 1] = timing->hactive & 0xff; init[0x07 * 2 - 1] = timing->hactive & 0xff;
init[8 * 2 - 1] = timing->hscale >> 8; init[0x08 * 2 - 1] = timing->hscale >> 8;
init[9 * 2 - 1] = timing->hscale & 0xff; init[0x09 * 2 - 1] = timing->hscale & 0xff;
init[0x19*2-1] = decoder->norm == 0 ? 115 : 93; /* Chroma burst delay */
i2c_smbus_write_byte_data(client, 0x1f, 0x00); /* reset */
bt819_write(client, 0x1f, 0x00);
mdelay(1); mdelay(1);
return i2c_master_send(client, init, sizeof(init));
}
/* ----------------------------------------------------------------------- */
static int bt819_attach(struct i2c_adapter *adap, int addr, int kind)
{
int i;
struct bt819 *decoder;
struct i2c_client *client;
client = kmalloc(sizeof(*client), GFP_KERNEL);
if(client == NULL)
return -ENOMEM;
memset(client, 0, sizeof(*client));
client_template.adapter = adap;
client_template.addr = addr;
memcpy(client, &client_template, sizeof(*client));
decoder = kmalloc(sizeof(struct bt819), GFP_KERNEL);
if (decoder == NULL) {
MOD_DEC_USE_COUNT;
kfree(client);
return -ENOMEM;
}
memset(decoder, 0, sizeof(struct bt819)); /* init */
strlcpy(client->name, "bt819", DEVICE_NAME_SIZE); return bt819_write_block(client, init, sizeof(init));
i2c_set_clientdata(client, decoder);
decoder->client = client;
decoder->addr = addr;
decoder->norm = VIDEO_MODE_NTSC;
decoder->input = 0;
decoder->enable = 1;
decoder->bright = 32768;
decoder->contrast = 32768;
decoder->hue = 32768;
decoder->sat = 32768;
decoder->initialized = 0;
i = bt819_init(client);
if (i < 0) {
printk(KERN_ERR "%s: bt819_attach: init status %d\n",
decoder->client->name, i);
} else {
printk(KERN_INFO "%s: bt819_attach: chip version %x\n",
decoder->client->name, i2c_smbus_read_byte_data(client,
0x17) & 0x0f);
}
init_MUTEX(&decoder->lock);
i2c_attach_client(client);
MOD_INC_USE_COUNT;
return 0;
}
static int bt819_probe(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, bt819_attach);
} }
static int bt819_detach(struct i2c_client *client) /* ----------------------------------------------------------------------- */
{
i2c_detach_client(client);
i2c_get_clientdata(client);
kfree(client);
MOD_DEC_USE_COUNT;
return 0;
}
static int bt819_command(struct i2c_client *client, unsigned int cmd, void *arg) static int
bt819_command (struct i2c_client *client,
unsigned int cmd,
void *arg)
{ {
int temp; int temp;
struct bt819 *decoder = i2c_get_clientdata(client); struct bt819 *decoder = i2c_get_clientdata(client);
//return 0;
if (!decoder->initialized) { // First call to bt819_init could be if (!decoder->initialized) { // First call to bt819_init could be
bt819_init(client); // without #FRST = 0 bt819_init(client); // without #FRST = 0
...@@ -227,207 +264,222 @@ static int bt819_command(struct i2c_client *client, unsigned int cmd, void *arg) ...@@ -227,207 +264,222 @@ static int bt819_command(struct i2c_client *client, unsigned int cmd, void *arg)
switch (cmd) { switch (cmd) {
case 0:
/* This is just for testing!!! */
bt819_init(client);
break;
case DECODER_GET_CAPABILITIES: case DECODER_GET_CAPABILITIES:
{ {
struct video_decoder_capability *cap = arg; struct video_decoder_capability *cap = arg;
cap->flags cap->flags = VIDEO_DECODER_PAL |
= VIDEO_DECODER_PAL VIDEO_DECODER_NTSC |
| VIDEO_DECODER_NTSC | VIDEO_DECODER_CCIR; VIDEO_DECODER_AUTO |
cap->inputs = 8; VIDEO_DECODER_CCIR;
cap->outputs = 1; cap->inputs = 8;
} cap->outputs = 1;
}
break; break;
case DECODER_GET_STATUS: case DECODER_GET_STATUS:
{ {
int *iarg = arg; int *iarg = arg;
int status; int status;
int res; int res;
status = i2c_smbus_read_byte_data(client, 0x00); status = bt819_read(client, 0x00);
res = 0; res = 0;
if ((status & 0x80)) { if ((status & 0x80)) {
res |= DECODER_STATUS_GOOD; res |= DECODER_STATUS_GOOD;
} }
switch (decoder->norm) { switch (decoder->norm) {
case VIDEO_MODE_NTSC: case VIDEO_MODE_NTSC:
res |= DECODER_STATUS_NTSC; res |= DECODER_STATUS_NTSC;
break; break;
case VIDEO_MODE_PAL: case VIDEO_MODE_PAL:
res |= DECODER_STATUS_PAL;
break;
default:
case VIDEO_MODE_AUTO:
if ((status & 0x10)) {
res |= DECODER_STATUS_PAL; res |= DECODER_STATUS_PAL;
break; } else {
default: res |= DECODER_STATUS_NTSC;
case VIDEO_MODE_AUTO:
if ((status & 0x10)) {
res |= DECODER_STATUS_PAL;
} else {
res |= DECODER_STATUS_NTSC;
}
break;
} }
res |= DECODER_STATUS_COLOR; break;
*iarg = res;
DEBUG(printk(KERN_INFO "%s-bt819: get status %x\n",
decoder->client->name, *iarg));
} }
res |= DECODER_STATUS_COLOR;
*iarg = res;
dprintk(1, KERN_INFO "%s: get status %x\n", I2C_NAME(client),
*iarg);
}
break; break;
case DECODER_SET_NORM: case DECODER_SET_NORM:
{ {
int *iarg = arg; int *iarg = arg;
struct timing *timing; struct timing *timing = NULL;
DEBUG(printk(KERN_INFO "%s-bt819: set norm %x\n", dprintk(1, KERN_INFO "%s: set norm %x\n", I2C_NAME(client),
decoder->client->name, *iarg)); *iarg);
if (*iarg == VIDEO_MODE_NTSC) { switch (*iarg) {
bt819_setbit(decoder, 0x01, 0, 1); case VIDEO_MODE_NTSC:
bt819_setbit(decoder, 0x01, 1, 0); bt819_setbit(client, 0x01, 0, 1);
i2c_smbus_write_byte_data(client, 0x18, 0x68); bt819_setbit(client, 0x01, 1, 0);
i2c_smbus_write_byte_data(client, 0x19, 0x5d); bt819_setbit(client, 0x01, 5, 0);
//bt819_setbit(decoder, 0x1a, 5, 1); bt819_write(client, 0x18, 0x68);
timing = &timing_data[VIDEO_MODE_NTSC]; bt819_write(client, 0x19, 0x5d);
} else { //bt819_setbit(client, 0x1a, 5, 1);
bt819_setbit(decoder, 0x01, 0, 1); timing = &timing_data[VIDEO_MODE_NTSC];
bt819_setbit(decoder, 0x01, 1, 1); break;
i2c_smbus_write_byte_data(client, 0x18, 0x7f); case VIDEO_MODE_PAL:
i2c_smbus_write_byte_data(client, 0x19, 0x72); bt819_setbit(client, 0x01, 0, 1);
//bt819_setbit(decoder, 0x1a, 5, 0); bt819_setbit(client, 0x01, 1, 1);
timing = &timing_data[VIDEO_MODE_PAL]; bt819_setbit(client, 0x01, 5, 1);
} bt819_write(client, 0x18, 0x7f);
bt819_write(client, 0x19, 0x72);
//bt819_setbit(client, 0x1a, 5, 0);
timing = &timing_data[VIDEO_MODE_PAL];
break;
case VIDEO_MODE_AUTO:
bt819_setbit(client, 0x01, 0, 0);
bt819_setbit(client, 0x01, 1, 0);
break;
default:
dprintk(1,
KERN_ERR
"%s: unsupported norm %d\n",
I2C_NAME(client), *iarg);
return -EINVAL;
}
i2c_smbus_write_byte_data(client, 0x03, if (timing) {
bt819_write(client, 0x03,
(((timing->vdelay >> 8) & 0x03) << 6) | (((timing->vdelay >> 8) & 0x03) << 6) |
(((timing-> (((timing->vactive >> 8) & 0x03) << 4) |
vactive >> 8) & 0x03) << 4) | (((timing->hdelay >> 8) & 0x03) << 2) |
(((timing-> ((timing->hactive >> 8) & 0x03) );
hdelay >> 8) & 0x03) << 2) | bt819_write(client, 0x04, timing->vdelay & 0xff);
((timing->hactive >> 8) & 0x03)); bt819_write(client, 0x05, timing->vactive & 0xff);
bt819_write(client, 0x06, timing->hdelay & 0xff);
i2c_smbus_write_byte_data(client, 0x04, timing->vdelay & 0xff); bt819_write(client, 0x07, timing->hactive & 0xff);
i2c_smbus_write_byte_data(client, 0x05, timing->vactive & 0xff); bt819_write(client, 0x08, (timing->hscale >> 8) & 0xff);
i2c_smbus_write_byte_data(client, 0x06, timing->hdelay & 0xff); bt819_write(client, 0x09, timing->hscale & 0xff);
i2c_smbus_write_byte_data(client, 0x07, timing->hactive & 0xff);
i2c_smbus_write_byte_data(client, 0x08, timing->hscale >> 8);
i2c_smbus_write_byte_data(client, 0x09, timing->hscale & 0xff);
decoder->norm = *iarg;
} }
decoder->norm = *iarg;
}
break; break;
case DECODER_SET_INPUT: case DECODER_SET_INPUT:
{ {
int *iarg = arg; int *iarg = arg;
DEBUG(printk(KERN_INFO "%s-bt819: set input %x\n", dprintk(1, KERN_INFO "%s: set input %x\n", I2C_NAME(client),
decoder->client->name, *iarg)); *iarg);
if (*iarg < 0 || *iarg > 7) { if (*iarg < 0 || *iarg > 7) {
return -EINVAL; return -EINVAL;
} }
if (decoder->input != *iarg) { if (decoder->input != *iarg) {
decoder->input = *iarg; decoder->input = *iarg;
/* select mode */ /* select mode */
if (decoder->input == 0) { if (decoder->input == 0) {
bt819_setbit(decoder, 0x0b, 6, 0); bt819_setbit(client, 0x0b, 6, 0);
bt819_setbit(decoder, 0x1a, 1, 1); bt819_setbit(client, 0x1a, 1, 1);
} else { } else {
bt819_setbit(decoder, 0x0b, 6, 1); bt819_setbit(client, 0x0b, 6, 1);
bt819_setbit(decoder, 0x1a, 1, 0); bt819_setbit(client, 0x1a, 1, 0);
}
} }
} }
}
break; break;
case DECODER_SET_OUTPUT: case DECODER_SET_OUTPUT:
{ {
int *iarg = arg; int *iarg = arg;
DEBUG(printk(KERN_INFO "%s-bt819: set output %x\n", dprintk(1, KERN_INFO "%s: set output %x\n", I2C_NAME(client),
decoder->client->name, *iarg)); *iarg);
/* not much choice of outputs */ /* not much choice of outputs */
if (*iarg != 0) { if (*iarg != 0) {
return -EINVAL; return -EINVAL;
}
} }
}
break; break;
case DECODER_ENABLE_OUTPUT: case DECODER_ENABLE_OUTPUT:
{ {
int *iarg = arg; int *iarg = arg;
int enable = (*iarg != 0); int enable = (*iarg != 0);
DEBUG(printk dprintk(1, KERN_INFO "%s: enable output %x\n",
(KERN_INFO "%s-bt819: enable output %x\n", I2C_NAME(client), *iarg);
decoder->client->name, *iarg));
if (decoder->enable != enable) {
if (decoder->enable != enable) { decoder->enable = enable;
decoder->enable = enable;
if (decoder->enable) {
if (decoder->enable) { bt819_setbit(client, 0x16, 7, 0);
bt819_setbit(decoder, 0x16, 7, 0); } else {
} else { bt819_setbit(client, 0x16, 7, 1);
bt819_setbit(decoder, 0x16, 7, 1);
}
} }
} }
}
break; break;
case DECODER_SET_PICTURE: case DECODER_SET_PICTURE:
{ {
struct video_picture *pic = arg; struct video_picture *pic = arg;
DEBUG(printk dprintk(1,
(KERN_INFO KERN_INFO
"%s-bt819: set picture brightness %d contrast %d colour %d\n", "%s: set picture brightness %d contrast %d colour %d\n",
decoder->client->name, pic->brightness, I2C_NAME(client), pic->brightness, pic->contrast,
pic->contrast, pic->colour)); pic->colour);
if (decoder->bright != pic->brightness) { if (decoder->bright != pic->brightness) {
/* We want -128 to 127 we get 0-65535 */ /* We want -128 to 127 we get 0-65535 */
decoder->bright = pic->brightness; decoder->bright = pic->brightness;
i2c_smbus_write_byte_data(client, 0x0a, bt819_write(client, 0x0a,
(decoder->bright >> 8) - 128); (decoder->bright >> 8) - 128);
} }
if (decoder->contrast != pic->contrast) { if (decoder->contrast != pic->contrast) {
/* We want 0 to 511 we get 0-65535 */ /* We want 0 to 511 we get 0-65535 */
decoder->contrast = pic->contrast; decoder->contrast = pic->contrast;
i2c_smbus_write_byte_data(client, 0x0c, bt819_write(client, 0x0c,
(decoder-> (decoder->contrast >> 7) & 0xff);
contrast >> 7) & 0xff); bt819_setbit(client, 0x0b, 2,
bt819_setbit(decoder, 0x0b, 2, ((decoder->contrast >> 15) & 0x01));
((decoder-> }
contrast >> 15) & 0x01));
}
if (decoder->sat != pic->colour) { if (decoder->sat != pic->colour) {
/* We want 0 to 511 we get 0-65535 */ /* We want 0 to 511 we get 0-65535 */
decoder->sat = pic->colour; decoder->sat = pic->colour;
i2c_smbus_write_byte_data(client, 0x0d, bt819_write(client, 0x0d,
(decoder->sat >> 7) & 0xff); (decoder->sat >> 7) & 0xff);
bt819_setbit(decoder, 0x0b, 1, bt819_setbit(client, 0x0b, 1,
((decoder-> ((decoder->sat >> 15) & 0x01));
sat >> 15) & 0x01));
temp = (decoder->sat * 201) / 237;
temp = (decoder->sat * 201) / 237; bt819_write(client, 0x0e, (temp >> 7) & 0xff);
i2c_smbus_write_byte_data(client, 0x0e, bt819_setbit(client, 0x0b, 0, (temp >> 15) & 0x01);
(temp >> 7) & 0xff); }
bt819_setbit(decoder, 0x0b, 0,
(temp >> 15) & 0x01);
}
if (decoder->hue != pic->hue) { if (decoder->hue != pic->hue) {
/* We want -128 to 127 we get 0-65535 */ /* We want -128 to 127 we get 0-65535 */
decoder->hue = pic->hue; decoder->hue = pic->hue;
i2c_smbus_write_byte_data(client, 0x0f, bt819_write(client, 0x0f,
128 - (decoder->hue >> 8)); 128 - (decoder->hue >> 8));
}
} }
}
break; break;
default: default:
...@@ -439,32 +491,174 @@ static int bt819_command(struct i2c_client *client, unsigned int cmd, void *arg) ...@@ -439,32 +491,174 @@ static int bt819_command(struct i2c_client *client, unsigned int cmd, void *arg)
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_bt819 = { /*
.name = "bt819", /* name */ * Generic i2c probe
.id = I2C_DRIVERID_BT819, /* ID */ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
.flags = I2C_DF_NOTIFY, */
.attach_adapter = bt819_probe, static unsigned short normal_i2c[] = {
.detach_client = bt819_detach, I2C_BT819 >> 1,
.command = bt819_command I2C_CLIENT_END,
};
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_i2c_range = normal_i2c_range,
.probe = probe,
.probe_range = probe_range,
.ignore = ignore,
.ignore_range = ignore_range,
.force = force
}; };
static struct i2c_client client_template = { static int bt819_i2c_id = 0;
.id = -1, static struct i2c_driver i2c_driver_bt819;
.driver = &i2c_driver_bt819,
.name = "bt819_client", static int
bt819_detect_client (struct i2c_adapter *adapter,
int address,
int kind)
{
int i, id;
struct bt819 *decoder;
struct i2c_client *client;
dprintk(1,
KERN_INFO
"saa7111.c: detecting bt819 client on address 0x%x\n",
address << 1);
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == 0)
return -ENOMEM;
memset(client, 0, sizeof(struct i2c_client));
client->addr = address;
client->adapter = adapter;
client->driver = &i2c_driver_bt819;
client->flags = I2C_CLIENT_ALLOW_USE;
client->id = bt819_i2c_id++;
decoder = kmalloc(sizeof(struct bt819), GFP_KERNEL);
if (decoder == NULL) {
kfree(client);
return -ENOMEM;
}
memset(decoder, 0, sizeof(struct bt819));
decoder->norm = VIDEO_MODE_NTSC;
decoder->input = 0;
decoder->enable = 1;
decoder->bright = 32768;
decoder->contrast = 32768;
decoder->hue = 32768;
decoder->sat = 32768;
decoder->initialized = 0;
i2c_set_clientdata(client, decoder);
id = bt819_read(client, 0x17);
switch (id & 0xf0) {
case 0x70:
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"bt819a[%d]", client->id);
break;
case 0x60:
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"bt817a[%d]", client->id);
break;
case 0x20:
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"bt815a[%d]", client->id);
break;
default:
dprintk(1,
KERN_ERR
"bt819: unknown chip version 0x%x (ver 0x%x)\n",
id & 0xf0, id & 0x0f);
kfree(decoder);
kfree(client);
return 0;
}
i = i2c_attach_client(client);
if (i) {
kfree(client);
kfree(decoder);
return i;
}
i = bt819_init(client);
if (i < 0) {
dprintk(1, KERN_ERR "%s_attach: init status %d\n",
I2C_NAME(client), i);
} else {
dprintk(1,
KERN_INFO
"%s_attach: chip version 0x%x at address 0x%x\n",
I2C_NAME(client), id & 0x0f,
client->addr << 1);
}
return 0;
}
static int
bt819_attach_adapter (struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, &bt819_detect_client);
}
static int
bt819_detach_client (struct i2c_client *client)
{
struct bt819 *decoder = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
return err;
}
kfree(decoder);
kfree(client);
return 0;
}
/* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_bt819 = {
.owner = THIS_MODULE,
.name = "bt819",
.id = I2C_DRIVERID_BT819,
.flags = I2C_DF_NOTIFY,
.attach_adapter = bt819_attach_adapter,
.detach_client = bt819_detach_client,
.command = bt819_command,
}; };
static int bt819_setup(void) static int __init
bt819_init_module (void)
{ {
return i2c_add_driver(&i2c_driver_bt819); return i2c_add_driver(&i2c_driver_bt819);
} }
static void bt819_exit(void) static void __exit
bt819_exit (void)
{ {
i2c_del_driver(&i2c_driver_bt819); i2c_del_driver(&i2c_driver_bt819);
} }
module_init(bt819_setup); module_init(bt819_init_module);
module_exit(bt819_exit); module_exit(bt819_exit);
MODULE_LICENSE("GPL");
/* /*
bt856 - BT856A Digital Video Encoder (Rockwell Part) * bt856 - BT856A Digital Video Encoder (Rockwell Part)
*
* Copyright (C) 1999 Mike Bernson <mike@mlb.org>
* Copyright (C) 1998 Dave Perks <dperks@ibm.net>
*
* Modifications for LML33/DC10plus unified driver
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
*
* This code was modify/ported from the saa7111 driver written
* by Dave Perks.
*
* Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
* - moved over to linux>=2.4.x i2c protocol (9/9/2002)
*
* 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.
*/
Copyright (C) 1999 Mike Bernson <mike@mlb.org> #include <linux/version.h>
Copyright (C) 1998 Dave Perks <dperks@ibm.net>
Modifications for LML33/DC10plus unified driver
Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
This code was modify/ported from the saa7111 driver written
by Dave Perks.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -40,40 +45,40 @@ ...@@ -40,40 +45,40 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/page.h> #include <asm/page.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <asm/segment.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/videodev.h> #include <linux/videodev.h>
#include <linux/version.h> #include <linux/version.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
MODULE_DESCRIPTION("Brooktree-856A video encoder driver");
MODULE_AUTHOR("Mike Bernson & Dave Perks");
MODULE_LICENSE("GPL");
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/video_encoder.h> #include <linux/i2c-dev.h>
#define DEBUG(x) x /* Debug driver */ #define I2C_NAME(s) (s)->dev.name
/* ----------------------------------------------------------------------- */ #include <linux/video_encoder.h>
static unsigned short normal_i2c[] = {34>>1, I2C_CLIENT_END }; static int debug = 0;
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; MODULE_PARM(debug, "i");
static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; MODULE_PARM_DESC(debug, "Debug level (0-1)");
static unsigned short probe_range[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = { #define dprintk(num, format, args...) \
normal_i2c , normal_i2c_range, do { \
probe , probe_range, if (debug >= num) \
ignore , ignore_range, printk(format, ##args); \
force } while (0)
};
/* ----------------------------------------------------------------------- */
static struct i2c_client client_template; #define REG_OFFSET 0xCE
struct bt856 { struct bt856 {
struct i2c_client *client; unsigned char reg[32];
int addr;
unsigned char reg[128];
int norm; int norm;
int enable; int enable;
...@@ -81,212 +86,192 @@ struct bt856 { ...@@ -81,212 +86,192 @@ struct bt856 {
int contrast; int contrast;
int hue; int hue;
int sat; int sat;
struct semaphore lock;
}; };
#define I2C_BT856 0x88 #define I2C_BT856 0x88
#define I2C_DELAY 10
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static int bt856_setbit(struct bt856 *dev, int subaddr, int bit, int data) static inline int
bt856_write (struct i2c_client *client,
u8 reg,
u8 value)
{ {
return i2c_smbus_write_byte_data(dev->client, subaddr,(dev->reg[subaddr] & ~(1 << bit)) | (data ? (1 << bit) : 0)); struct bt856 *encoder = i2c_get_clientdata(client);
encoder->reg[reg - REG_OFFSET] = value;
return i2c_smbus_write_byte_data(client, reg, value);
} }
/* ----------------------------------------------------------------------- */ static inline int
bt856_setbit (struct i2c_client *client,
static int bt856_attach(struct i2c_adapter *adap, int addr, int kind) u8 reg,
u8 bit,
u8 value)
{ {
struct bt856 *encoder; struct bt856 *encoder = i2c_get_clientdata(client);
struct i2c_client *client; return bt856_write(client, reg,
(encoder->
client = kmalloc(sizeof(*client), GFP_KERNEL); reg[reg - REG_OFFSET] & ~(1 << bit)) |
if(client == NULL) (value ? (1 << bit) : 0));
return -ENOMEM; }
memset(client, 0, sizeof(*client));
client_template.adapter = adap;
client_template.addr = addr;
memcpy(client, &client_template, sizeof(*client));
/* This chip is not on the buz card but at the same address saa7185 */
//if (memcmp(device->bus->name, "buz", 3) == 0 || memcmp(device->bus->name, "zr36057", 6) == 0)
// return 1;
encoder = kmalloc(sizeof(struct bt856), GFP_KERNEL); static void
bt856_dump (struct i2c_client *client)
{
int i;
struct bt856 *encoder = i2c_get_clientdata(client);
printk(KERN_INFO "%s: register dump:", I2C_NAME(client));
for (i = 0xd6; i <= 0xde; i += 2)
printk(" %02x", encoder->reg[i - REG_OFFSET]);
printk("\n");
}
if (encoder == NULL) { /* ----------------------------------------------------------------------- */
MOD_DEC_USE_COUNT;
return -ENOMEM;
}
static int
bt856_command (struct i2c_client *client,
unsigned int cmd,
void *arg)
{
struct bt856 *encoder = i2c_get_clientdata(client);
memset(encoder, 0, sizeof(struct bt856)); switch (cmd) {
strlcpy(client->name, "bt856", DEVICE_NAME_SIZE);
encoder->client = client;
i2c_set_clientdata(client, encoder);
encoder->addr = client->addr;
encoder->norm = VIDEO_MODE_NTSC;
encoder->enable = 1;
DEBUG(printk(KERN_INFO "%s-bt856: attach\n", encoder->client->name)); case 0:
/* This is just for testing!!! */
dprintk(1, KERN_INFO "bt856: init\n");
bt856_write(client, 0xdc, 0x18);
bt856_write(client, 0xda, 0);
bt856_write(client, 0xde, 0);
i2c_smbus_write_byte_data(client, 0xdc, 0x18); bt856_setbit(client, 0xdc, 3, 1);
encoder->reg[0xdc] = 0x18; //bt856_setbit(client, 0xdc, 6, 0);
i2c_smbus_write_byte_data(client, 0xda, 0); bt856_setbit(client, 0xdc, 4, 1);
encoder->reg[0xda] = 0;
i2c_smbus_write_byte_data(client, 0xde, 0);
encoder->reg[0xde] = 0;
bt856_setbit(encoder, 0xdc, 3, 1); switch (encoder->norm) {
//bt856_setbit(encoder, 0xdc, 6, 0);
bt856_setbit(encoder, 0xdc, 4, 1);
switch (encoder->norm) { case VIDEO_MODE_NTSC:
bt856_setbit(client, 0xdc, 2, 0);
break;
case VIDEO_MODE_NTSC: case VIDEO_MODE_PAL:
bt856_setbit(encoder, 0xdc, 2, 0); bt856_setbit(client, 0xdc, 2, 1);
break; break;
}
case VIDEO_MODE_PAL: bt856_setbit(client, 0xdc, 1, 1);
bt856_setbit(encoder, 0xdc, 2, 1); bt856_setbit(client, 0xde, 4, 0);
bt856_setbit(client, 0xde, 3, 1);
if (debug != 0)
bt856_dump(client);
break; break;
}
bt856_setbit(encoder, 0xdc, 1, 1);
bt856_setbit(encoder, 0xde, 4, 0);
bt856_setbit(encoder, 0xde, 3, 1);
init_MUTEX(&encoder->lock);
i2c_attach_client(client);
MOD_INC_USE_COUNT;
return 0;
}
static int bt856_probe(struct i2c_adapter *adap) case ENCODER_GET_CAPABILITIES:
{ {
return i2c_probe(adap, &addr_data , bt856_attach); struct video_encoder_capability *cap = arg;
}
static int bt856_detach(struct i2c_client *client)
{
i2c_detach_client(client);
i2c_get_clientdata(client);
kfree(client);
MOD_DEC_USE_COUNT;
return 0;
}
static int bt856_command(struct i2c_client *client, unsigned int cmd,
void *arg)
{
struct bt856 *encoder = i2c_get_clientdata(client);
switch (cmd) { dprintk(1, KERN_INFO "%s: get capabilities\n",
I2C_NAME(client));
case ENCODER_GET_CAPABILITIES: cap->flags = VIDEO_ENCODER_PAL |
{ VIDEO_ENCODER_NTSC |
struct video_encoder_capability *cap = arg; VIDEO_ENCODER_CCIR;
cap->inputs = 2;
DEBUG(printk cap->outputs = 1;
(KERN_INFO "%s-bt856: get capabilities\n", }
encoder->client->name));
cap->flags
= VIDEO_ENCODER_PAL
| VIDEO_ENCODER_NTSC | VIDEO_ENCODER_CCIR;
cap->inputs = 2;
cap->outputs = 1;
}
break; break;
case ENCODER_SET_NORM: case ENCODER_SET_NORM:
{ {
int *iarg = arg; int *iarg = arg;
DEBUG(printk(KERN_INFO "%s-bt856: set norm %d\n", dprintk(1, KERN_INFO "%s: set norm %d\n", I2C_NAME(client),
encoder->client->name, *iarg)); *iarg);
switch (*iarg) { switch (*iarg) {
case VIDEO_MODE_NTSC: case VIDEO_MODE_NTSC:
bt856_setbit(encoder, 0xdc, 2, 0); bt856_setbit(client, 0xdc, 2, 0);
break; break;
case VIDEO_MODE_PAL: case VIDEO_MODE_PAL:
bt856_setbit(encoder, 0xdc, 2, 1); bt856_setbit(client, 0xdc, 2, 1);
bt856_setbit(encoder, 0xda, 0, 0); bt856_setbit(client, 0xda, 0, 0);
//bt856_setbit(encoder, 0xda, 0, 1); //bt856_setbit(client, 0xda, 0, 1);
break; break;
default: default:
return -EINVAL; return -EINVAL;
}
encoder->norm = *iarg;
} }
encoder->norm = *iarg;
if (debug != 0)
bt856_dump(client);
}
break; break;
case ENCODER_SET_INPUT: case ENCODER_SET_INPUT:
{ {
int *iarg = arg; int *iarg = arg;
DEBUG(printk(KERN_INFO "%s-bt856: set input %d\n", dprintk(1, KERN_INFO "%s: set input %d\n", I2C_NAME(client),
encoder->client->name, *iarg)); *iarg);
/* We only have video bus. /* We only have video bus.
*iarg = 0: input is from bt819 * iarg = 0: input is from bt819
*iarg = 1: input is from ZR36060 */ * iarg = 1: input is from ZR36060 */
switch (*iarg) { switch (*iarg) {
case 0: case 0:
bt856_setbit(encoder, 0xde, 4, 0); bt856_setbit(client, 0xde, 4, 0);
bt856_setbit(encoder, 0xde, 3, 1); bt856_setbit(client, 0xde, 3, 1);
bt856_setbit(encoder, 0xdc, 3, 1); bt856_setbit(client, 0xdc, 3, 1);
bt856_setbit(encoder, 0xdc, 6, 0); bt856_setbit(client, 0xdc, 6, 0);
break; break;
case 1: case 1:
bt856_setbit(encoder, 0xde, 4, 0); bt856_setbit(client, 0xde, 4, 0);
bt856_setbit(encoder, 0xde, 3, 1); bt856_setbit(client, 0xde, 3, 1);
bt856_setbit(encoder, 0xdc, 3, 1); bt856_setbit(client, 0xdc, 3, 1);
bt856_setbit(encoder, 0xdc, 6, 1); bt856_setbit(client, 0xdc, 6, 1);
break; break;
case 2: // Color bar case 2: // Color bar
bt856_setbit(encoder, 0xdc, 3, 0); bt856_setbit(client, 0xdc, 3, 0);
bt856_setbit(encoder, 0xde, 4, 1); bt856_setbit(client, 0xde, 4, 1);
break; break;
default: default:
return -EINVAL; return -EINVAL;
}
} }
if (debug != 0)
bt856_dump(client);
}
break; break;
case ENCODER_SET_OUTPUT: case ENCODER_SET_OUTPUT:
{ {
int *iarg = arg; int *iarg = arg;
DEBUG(printk(KERN_INFO "%s-bt856: set output %d\n", dprintk(1, KERN_INFO "%s: set output %d\n", I2C_NAME(client),
encoder->client->name, *iarg)); *iarg);
/* not much choice of outputs */ /* not much choice of outputs */
if (*iarg != 0) { if (*iarg != 0) {
return -EINVAL; return -EINVAL;
}
} }
}
break; break;
case ENCODER_ENABLE_OUTPUT: case ENCODER_ENABLE_OUTPUT:
{ {
int *iarg = arg; int *iarg = arg;
encoder->enable = !!*iarg; encoder->enable = !!*iarg;
DEBUG(printk dprintk(1, KERN_INFO "%s: enable output %d\n",
(KERN_INFO "%s-bt856: enable output %d\n", I2C_NAME(client), encoder->enable);
encoder->client->name, encoder->enable)); }
}
break; break;
default: default:
...@@ -298,32 +283,162 @@ static int bt856_command(struct i2c_client *client, unsigned int cmd, ...@@ -298,32 +283,162 @@ static int bt856_command(struct i2c_client *client, unsigned int cmd,
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
/*
* Generic i2c probe
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
static unsigned short normal_i2c[] = { I2C_BT856 >> 1, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_i2c_range = normal_i2c_range,
.probe = probe,
.probe_range = probe_range,
.ignore = ignore,
.ignore_range = ignore_range,
.force = force
};
static int bt856_i2c_id = 0;
static struct i2c_driver i2c_driver_bt856;
static int
bt856_detect_client (struct i2c_adapter *adapter,
int address,
int kind)
{
int i;
struct i2c_client *client;
struct bt856 *encoder;
dprintk(1,
KERN_INFO
"bt856.c: detecting bt856 client on address 0x%x\n",
address << 1);
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == 0)
return -ENOMEM;
memset(client, 0, sizeof(struct i2c_client));
client->addr = address;
client->adapter = adapter;
client->driver = &i2c_driver_bt856;
client->flags = I2C_CLIENT_ALLOW_USE;
client->id = bt856_i2c_id++;
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"bt856[%d]", client->id);
encoder = kmalloc(sizeof(struct bt856), GFP_KERNEL);
if (encoder == NULL) {
return -ENOMEM;
}
memset(encoder, 0, sizeof(struct bt856));
encoder->norm = VIDEO_MODE_NTSC;
encoder->enable = 1;
i2c_set_clientdata(client, encoder);
i = i2c_attach_client(client);
if (i) {
kfree(client);
kfree(encoder);
return i;
}
bt856_write(client, 0xdc, 0x18);
bt856_write(client, 0xda, 0);
bt856_write(client, 0xde, 0);
bt856_setbit(client, 0xdc, 3, 1);
//bt856_setbit(client, 0xdc, 6, 0);
bt856_setbit(client, 0xdc, 4, 1);
switch (encoder->norm) {
case VIDEO_MODE_NTSC:
bt856_setbit(client, 0xdc, 2, 0);
break;
case VIDEO_MODE_PAL:
bt856_setbit(client, 0xdc, 2, 1);
break;
}
bt856_setbit(client, 0xdc, 1, 1);
bt856_setbit(client, 0xde, 4, 0);
bt856_setbit(client, 0xde, 3, 1);
if (debug != 0)
bt856_dump(client);
dprintk(1, KERN_INFO "%s_attach: at address 0x%x\n", I2C_NAME(client),
client->addr << 1);
return 0;
}
static int
bt856_attach_adapter (struct i2c_adapter *adapter)
{
dprintk(1,
KERN_INFO
"bt856.c: starting probe for adapter %s (0x%x)\n",
I2C_NAME(adapter), adapter->id);
return i2c_probe(adapter, &addr_data, &bt856_detect_client);
}
static int
bt856_detach_client (struct i2c_client *client)
{
struct bt856 *encoder = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
return err;
}
kfree(encoder);
kfree(client);
return 0;
}
/* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_bt856 = { static struct i2c_driver i2c_driver_bt856 = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "bt856", /* name */ .name = "bt856",
.id = I2C_DRIVERID_BT856, /* ID */
.id = I2C_DRIVERID_BT856,
.flags = I2C_DF_NOTIFY, .flags = I2C_DF_NOTIFY,
.attach_adapter = bt856_probe,
.detach_client = bt856_detach,
.command = bt856_command
};
static struct i2c_client client_template = { .attach_adapter = bt856_attach_adapter,
.id = -1, .detach_client = bt856_detach_client,
.driver = &i2c_driver_bt856, .command = bt856_command,
.name = "bt856_client",
}; };
static int bt856_init(void) static int __init
bt856_init (void)
{ {
return i2c_add_driver(&i2c_driver_bt856); return i2c_add_driver(&i2c_driver_bt856);
} }
static void bt856_exit(void) static void __exit
bt856_exit (void)
{ {
i2c_del_driver(&i2c_driver_bt856); i2c_del_driver(&i2c_driver_bt856);
} }
module_init(bt856_init); module_init(bt856_init);
module_exit(bt856_exit); module_exit(bt856_exit);
MODULE_LICENSE("GPL");
/* /*
saa7110 - Philips SAA7110(A) video decoder driver * saa7110 - Philips SAA7110(A) video decoder driver
*
Copyright (C) 1998 Pauline Middelink <middelin@polyware.nl> * Copyright (C) 1998 Pauline Middelink <middelin@polyware.nl>
*
This program is free software; you can redistribute it and/or modify * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
it under the terms of the GNU General Public License as published by * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
the Free Software Foundation; either version 2 of the License, or * - some corrections for Pinnacle Systems Inc. DC10plus card.
(at your option) any later version. *
* Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
This program is distributed in the hope that it will be useful, * - moved over to linux>=2.4.x i2c protocol (1/1/2003)
but WITHOUT ANY WARRANTY; without even the implied warranty of *
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * This program is free software; you can redistribute it and/or modify
GNU General Public License for more details. * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
You should have received a copy of the GNU General Public License * (at your option) any later version.
along with this program; if not, write to the Free Software *
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * This program is distributed in the hope that it will be useful,
*/ * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/version.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -26,353 +35,411 @@ ...@@ -26,353 +35,411 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
MODULE_DESCRIPTION("Philips SAA7110 video decoder driver");
MODULE_AUTHOR("Pauline Middelink");
MODULE_LICENSE("GPL");
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c-dev.h>
#define I2C_NAME(s) (s)->dev.name
#include <linux/videodev.h> #include <linux/videodev.h>
#include "linux/video_decoder.h" #include <linux/video_decoder.h>
#define DEBUG(x...) /* remove when no long debugging */ static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
#define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */ #define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */
#define SAA7110_MAX_OUTPUT 0 /* it's a decoder only */ #define SAA7110_MAX_OUTPUT 0 /* its a decoder only */
#define I2C_SAA7110 0x9C /* or 0x9E */ #define I2C_SAA7110 0x9C /* or 0x9E */
#define IF_NAME "saa7110" struct saa7110 {
#define I2C_DELAY 10 /* 10 us or 100khz */ unsigned char reg[54];
static unsigned short normal_i2c[] = {34>>1, I2C_CLIENT_END }; int norm;
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; int input;
static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; int enable;
static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; int bright;
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; int contrast;
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; int hue;
static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; int sat;
static struct i2c_client_address_data addr_data = { wait_queue_head_t wq;
normal_i2c, normal_i2c_range,
probe, probe_range,
ignore, ignore_range,
force
}; };
static struct i2c_client client_template; /* ----------------------------------------------------------------------- */
/* I2C support functions */
/* ----------------------------------------------------------------------- */
struct saa7110 { static int
struct i2c_client *client; saa7110_write (struct i2c_client *client,
int addr; u8 reg,
unsigned char reg[36]; u8 value)
struct semaphore lock; {
int norm; struct saa7110 *decoder = i2c_get_clientdata(client);
int input; decoder->reg[reg] = value;
int enable; return i2c_smbus_write_byte_data(client, reg, value);
int bright; }
int contrast;
int hue; static int
int sat; saa7110_write_block (struct i2c_client *client,
}; const u8 *data,
unsigned int len)
{
int ret = -1;
u8 reg = *data++;
len--;
/* the saa7110 has an autoincrement function, use it if
* the adapter understands raw I2C */
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
struct saa7110 *decoder = i2c_get_clientdata(client);
struct i2c_msg msg;
u8 block_data[54];
msg.len = 0;
msg.buf = (char *) block_data;
msg.addr = client->addr;
msg.flags = client->flags;
while (len >= 1) {
msg.len = 0;
block_data[msg.len++] = reg;
while (len-- >= 1 && msg.len < 54)
block_data[msg.len++] =
decoder->reg[reg++] = *data++;
ret = i2c_transfer(client->adapter, &msg, 1);
}
} else {
while (len-- >= 1) {
if ((ret =
saa7110_write(client, reg++, *data++)) < 0)
break;
}
}
return ret;
}
static inline int
saa7110_read (struct i2c_client *client)
{
return i2c_smbus_read_byte(client);
}
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
/* SAA7110 functions */ /* SAA7110 functions */
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static
int saa7110_selmux(struct i2c_client *client, int chan) #define FRESP_06H_COMPST 0x03 //0x13
#define FRESP_06H_SVIDEO 0x83 //0xC0
static int
saa7110_selmux (struct i2c_client *client,
int chan)
{ {
static const unsigned char modes[9][8] = { static const unsigned char modes[9][8] = {
/* mode 0 */ { 0x00, 0xD9, 0x17, 0x40, 0x03, 0x44, 0x75, 0x16 }, /* mode 0 */
/* mode 1 */ { 0x00, 0xD8, 0x17, 0x40, 0x03, 0x44, 0x75, 0x16 }, {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03,
/* mode 2 */ { 0x00, 0xBA, 0x07, 0x91, 0x03, 0x60, 0xB5, 0x05 }, 0x44, 0x75, 0x16},
/* mode 3 */ { 0x00, 0xB8, 0x07, 0x91, 0x03, 0x60, 0xB5, 0x05 }, /* mode 1 */
/* mode 4 */ { 0x00, 0x7C, 0x07, 0xD2, 0x83, 0x60, 0xB5, 0x03 }, {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03,
/* mode 5 */ { 0x00, 0x78, 0x07, 0xD2, 0x83, 0x60, 0xB5, 0x03 }, 0x44, 0x75, 0x16},
/* mode 6 */ { 0x80, 0x59, 0x17, 0x42, 0xA3, 0x44, 0x75, 0x12 }, /* mode 2 */
/* mode 7 */ { 0x80, 0x9A, 0x17, 0xB1, 0x13, 0x60, 0xB5, 0x14 }, {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03,
/* mode 8 */ { 0x80, 0x3C, 0x27, 0xC1, 0x23, 0x44, 0x75, 0x21 } }; 0x60, 0xB5, 0x05},
const unsigned char* ptr = modes[chan]; /* mode 3 */
{FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03,
i2c_smbus_write_byte_data(client,0x06,ptr[0]); /* Luminance control */ 0x60, 0xB5, 0x05},
i2c_smbus_write_byte_data(client,0x20,ptr[1]); /* Analog Control #1 */ /* mode 4 */
i2c_smbus_write_byte_data(client,0x21,ptr[2]); /* Analog Control #2 */ {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83,
i2c_smbus_write_byte_data(client,0x22,ptr[3]); /* Mixer Control #1 */ 0x60, 0xB5, 0x03},
i2c_smbus_write_byte_data(client,0x2C,ptr[4]); /* Mixer Control #2 */ /* mode 5 */
i2c_smbus_write_byte_data(client,0x30,ptr[5]); /* ADCs gain control */ {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83,
i2c_smbus_write_byte_data(client,0x31,ptr[6]); /* Mixer Control #3 */ 0x60, 0xB5, 0x03},
i2c_smbus_write_byte_data(client,0x21,ptr[7]); /* Analog Control #2 */ /* mode 6 */
{FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3,
0x44, 0x75, 0x12},
/* mode 7 */
{FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13,
0x60, 0xB5, 0x14},
/* mode 8 */
{FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23,
0x44, 0x75, 0x21}
};
struct saa7110 *decoder = i2c_get_clientdata(client);
const unsigned char *ptr = modes[chan];
saa7110_write(client, 0x06, ptr[0]); /* Luminance control */
saa7110_write(client, 0x20, ptr[1]); /* Analog Control #1 */
saa7110_write(client, 0x21, ptr[2]); /* Analog Control #2 */
saa7110_write(client, 0x22, ptr[3]); /* Mixer Control #1 */
saa7110_write(client, 0x2C, ptr[4]); /* Mixer Control #2 */
saa7110_write(client, 0x30, ptr[5]); /* ADCs gain control */
saa7110_write(client, 0x31, ptr[6]); /* Mixer Control #3 */
saa7110_write(client, 0x21, ptr[7]); /* Analog Control #2 */
decoder->input = chan;
return 0; return 0;
} }
static static const unsigned char initseq[] = {
int determine_norm(struct i2c_client* client) 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00,
/* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90,
/* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA,
/* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F,
/* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C,
/* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02
};
static int
determine_norm (struct i2c_client *client)
{ {
int status; struct saa7110 *decoder = i2c_get_clientdata(client);
int status;
/* mode changed, start automatic detection */ /* mode changed, start automatic detection */
status = i2c_smbus_read_byte(client); saa7110_write_block(client, initseq, sizeof(initseq));
saa7110_selmux(client, decoder->input);
sleep_on_timeout(&decoder->wq, HZ / 4);
status = saa7110_read(client);
if (status & 0x40) {
dprintk(1, KERN_INFO "%s: status=0x%02x (no signal)\n",
I2C_NAME(client), status);
return decoder->norm; // no change
}
if ((status & 3) == 0) { if ((status & 3) == 0) {
i2c_smbus_write_byte_data(client,0x06,0x80); saa7110_write(client, 0x06, 0x83);
if (status & 0x20) { if (status & 0x20) {
DEBUG(printk(KERN_INFO "%s: norm=bw60\n",adp->name)); dprintk(1,
i2c_smbus_write_byte_data(client,0x2E,0x81); KERN_INFO
"%s: status=0x%02x (NTSC/no color)\n",
I2C_NAME(client), status);
//saa7110_write(client,0x2E,0x81);
return VIDEO_MODE_NTSC; return VIDEO_MODE_NTSC;
} }
DEBUG(printk(KERN_INFO "%s: norm=bw50\n",adp->name)); dprintk(1, KERN_INFO "%s: status=0x%02x (PAL/no color)\n",
i2c_smbus_write_byte_data(client,0x2E,0x9A); I2C_NAME(client), status);
//saa7110_write(client,0x2E,0x9A);
return VIDEO_MODE_PAL; return VIDEO_MODE_PAL;
} }
//saa7110_write(client,0x06,0x03);
i2c_smbus_write_byte_data(client,0x06,0x00);
if (status & 0x20) { /* 60Hz */ if (status & 0x20) { /* 60Hz */
DEBUG(printk(KERN_INFO "%s: norm=ntsc\n",adp->name)); dprintk(1, KERN_INFO "%s: status=0x%02x (NTSC)\n",
i2c_smbus_write_byte_data(client,0x0D,0x06); I2C_NAME(client), status);
i2c_smbus_write_byte_data(client,0x11,0x2C); saa7110_write(client, 0x0D, 0x86);
i2c_smbus_write_byte_data(client,0x2E,0x81); saa7110_write(client, 0x0F, 0x50);
saa7110_write(client, 0x11, 0x2C);
//saa7110_write(client,0x2E,0x81);
return VIDEO_MODE_NTSC; return VIDEO_MODE_NTSC;
} }
/* 50Hz -> PAL/SECAM */ /* 50Hz -> PAL/SECAM */
i2c_smbus_write_byte_data(client,0x0D,0x06); saa7110_write(client, 0x0D, 0x86);
i2c_smbus_write_byte_data(client,0x11,0x59); saa7110_write(client, 0x0F, 0x10);
i2c_smbus_write_byte_data(client,0x2E,0x9A); saa7110_write(client, 0x11, 0x59);
//saa7110_write(client,0x2E,0x9A);
mdelay(150); /* pause 150 ms */ sleep_on_timeout(&decoder->wq, HZ / 4);
status = i2c_smbus_read_byte(client); status = saa7110_read(client);
if ((status & 0x03) == 0x01) { if ((status & 0x03) == 0x01) {
DEBUG(printk(KERN_INFO "%s: norm=secam\n",dev->name)); dprintk(1, KERN_INFO "%s: status=0x%02x (SECAM)\n",
i2c_smbus_write_byte_data(client,0x0D,0x07); I2C_NAME(client), status);
saa7110_write(client, 0x0D, 0x87);
return VIDEO_MODE_SECAM; return VIDEO_MODE_SECAM;
} }
DEBUG(printk(KERN_INFO "%s: norm=pal\n",dev->name)); dprintk(1, KERN_INFO "%s: status=0x%02x (PAL)\n", I2C_NAME(client),
status);
return VIDEO_MODE_PAL; return VIDEO_MODE_PAL;
} }
static static int
int saa7110_attach(struct i2c_adapter *adap, int addr, int kind) saa7110_command (struct i2c_client *client,
unsigned int cmd,
void *arg)
{ {
static const unsigned char initseq[] = { struct saa7110 *decoder = i2c_get_clientdata(client);
0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF0, 0x00, 0x00, int v;
0xF8, 0xF8, 0x60, 0x60, 0x00, 0x06, 0x18, 0x90,
0x00, 0x2C, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA,
0xF0, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xD9, 0x17, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F,
0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x81, 0x03,
0x40, 0x75, 0x01, 0x8C, 0x03};
struct saa7110 *decoder;
struct i2c_client *client;
int rv;
client=kmalloc(sizeof(*client), GFP_KERNEL);
if(client == NULL)
return -ENOMEM;
memset(client, 0, sizeof(*client));
client_template.adapter = adap;
client_template.addr = addr;
memcpy(client, &client_template, sizeof(*client));
decoder = kmalloc(sizeof(*decoder), GFP_KERNEL);
if (decoder == NULL) {
kfree(client);
return -ENOMEM;
}
/* clear our private data */ switch (cmd) {
memset(decoder, 0, sizeof(*decoder)); case 0:
strlcpy(client->name, IF_NAME, DEVICE_NAME_SIZE); //saa7110_write_block(client, initseq, sizeof(initseq));
decoder->client = client; break;
i2c_set_clientdata(client, decoder);
decoder->addr = addr;
decoder->norm = VIDEO_MODE_PAL;
decoder->input = 0;
decoder->enable = 1;
decoder->bright = 32768;
decoder->contrast = 32768;
decoder->hue = 32768;
decoder->sat = 32768;
rv = i2c_master_send(client, initseq, sizeof(initseq)); case DECODER_GET_CAPABILITIES:
if (rv < 0) {
printk(KERN_ERR "%s_attach: init status %d\n", client->name, rv); struct video_decoder_capability *dc = arg;
else { dc->flags =
i2c_smbus_write_byte_data(client,0x21,0x16); VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC |
i2c_smbus_write_byte_data(client,0x0D,0x04); VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO;
DEBUG(printk(KERN_INFO "%s_attach: chip version %x\n", client->name, i2c_smbus_read_byte(client))); dc->inputs = SAA7110_MAX_INPUT;
i2c_smbus_write_byte_data(client,0x0D,0x06); dc->outputs = SAA7110_MAX_OUTPUT;
} }
init_MUTEX(&decoder->lock);
i2c_attach_client(client);
MOD_INC_USE_COUNT;
/* setup and implicit mode 0 select has been performed */
return 0;
}
static
int saa7110_probe(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, saa7110_attach);
}
static
int saa7110_detach(struct i2c_client *client)
{
struct saa7110* decoder = i2c_get_clientdata(client);
i2c_detach_client(client);
DEBUG(printk(KERN_INFO "%s_detach\n",client->name));
/* stop further output */
i2c_smbus_write_byte_data(client,0x0E,0x00);
kfree(decoder);
kfree(client);
MOD_DEC_USE_COUNT;
return 0;
}
static
int saa7110_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
struct saa7110* decoder = i2c_get_clientdata(client);
int v;
switch (cmd) {
case DECODER_GET_CAPABILITIES:
{
struct video_decoder_capability *dc = arg;
dc->flags = VIDEO_DECODER_PAL
| VIDEO_DECODER_NTSC
| VIDEO_DECODER_SECAM
| VIDEO_DECODER_AUTO
| VIDEO_DECODER_CCIR;
dc->inputs = SAA7110_MAX_INPUT;
dc->outputs = SAA7110_MAX_OUTPUT;
}
break; break;
case DECODER_GET_STATUS: case DECODER_GET_STATUS:
{ {
struct saa7110* decoder = i2c_get_clientdata(client); int status;
int status; int res = 0;
int res = 0;
status = saa7110_read(client);
status = i2c_smbus_read_byte(client); dprintk(1, KERN_INFO "%s: status=0x%02x norm=%d\n",
if (status & 0x40) I2C_NAME(client), status, decoder->norm);
res |= DECODER_STATUS_GOOD; if (!(status & 0x40))
if (status & 0x03) res |= DECODER_STATUS_GOOD;
res |= DECODER_STATUS_COLOR; if (status & 0x03)
res |= DECODER_STATUS_COLOR;
switch (decoder->norm) {
case VIDEO_MODE_NTSC: switch (decoder->norm) {
res |= DECODER_STATUS_NTSC; case VIDEO_MODE_NTSC:
break; res |= DECODER_STATUS_NTSC;
case VIDEO_MODE_PAL: break;
res |= DECODER_STATUS_PAL; case VIDEO_MODE_PAL:
break; res |= DECODER_STATUS_PAL;
case VIDEO_MODE_SECAM: break;
res |= DECODER_STATUS_SECAM; case VIDEO_MODE_SECAM:
break; res |= DECODER_STATUS_SECAM;
} break;
*(int*)arg = res;
} }
*(int *) arg = res;
}
break; break;
case DECODER_SET_NORM: case DECODER_SET_NORM:
v = *(int*)arg; v = *(int *) arg;
if (decoder->norm != v) { if (decoder->norm != v) {
decoder->norm = v; decoder->norm = v;
i2c_smbus_write_byte_data(client, 0x06, 0x00); //saa7110_write(client, 0x06, 0x03);
switch (v) { switch (v) {
case VIDEO_MODE_NTSC: case VIDEO_MODE_NTSC:
i2c_smbus_write_byte_data(client, 0x0D, 0x06); saa7110_write(client, 0x0D, 0x86);
i2c_smbus_write_byte_data(client, 0x11, 0x2C); saa7110_write(client, 0x0F, 0x50);
i2c_smbus_write_byte_data(client, 0x30, 0x81); saa7110_write(client, 0x11, 0x2C);
i2c_smbus_write_byte_data(client, 0x2A, 0xDF); //saa7110_write(client, 0x2E, 0x81);
dprintk(1,
KERN_INFO "%s: switched to NTSC\n",
I2C_NAME(client));
break; break;
case VIDEO_MODE_PAL: case VIDEO_MODE_PAL:
i2c_smbus_write_byte_data(client, 0x0D, 0x06); saa7110_write(client, 0x0D, 0x86);
i2c_smbus_write_byte_data(client, 0x11, 0x59); saa7110_write(client, 0x0F, 0x10);
i2c_smbus_write_byte_data(client, 0x2E, 0x9A); saa7110_write(client, 0x11, 0x59);
//saa7110_write(client, 0x2E, 0x9A);
dprintk(1,
KERN_INFO "%s: switched to PAL\n",
I2C_NAME(client));
break; break;
case VIDEO_MODE_SECAM: case VIDEO_MODE_SECAM:
i2c_smbus_write_byte_data(client, 0x0D, 0x07); saa7110_write(client, 0x0D, 0x87);
i2c_smbus_write_byte_data(client, 0x11, 0x59); saa7110_write(client, 0x0F, 0x10);
i2c_smbus_write_byte_data(client, 0x2E, 0x9A); saa7110_write(client, 0x11, 0x59);
//saa7110_write(client, 0x2E, 0x9A);
dprintk(1,
KERN_INFO
"%s: switched to SECAM\n",
I2C_NAME(client));
break; break;
case VIDEO_MODE_AUTO: case VIDEO_MODE_AUTO:
*(int*)arg = determine_norm(client); dprintk(1,
KERN_INFO
"%s: TV standard detection...\n",
I2C_NAME(client));
decoder->norm = determine_norm(client);
*(int *) arg = decoder->norm;
break; break;
default: default:
return -EPERM; return -EPERM;
} }
} }
break; break;
case DECODER_SET_INPUT: case DECODER_SET_INPUT:
v = *(int*)arg; v = *(int *) arg;
if (v<0 || v>SAA7110_MAX_INPUT) if (v < 0 || v > SAA7110_MAX_INPUT) {
dprintk(1,
KERN_INFO "%s: input=%d not available\n",
I2C_NAME(client), v);
return -EINVAL; return -EINVAL;
}
if (decoder->input != v) { if (decoder->input != v) {
decoder->input = v;
saa7110_selmux(client, v); saa7110_selmux(client, v);
dprintk(1, KERN_INFO "%s: switched to input=%d\n",
I2C_NAME(client), v);
} }
break; break;
case DECODER_SET_OUTPUT: case DECODER_SET_OUTPUT:
v = *(int*)arg; v = *(int *) arg;
/* not much choice of outputs */ /* not much choice of outputs */
if (v != 0) if (v != 0)
return -EINVAL; return -EINVAL;
break; break;
case DECODER_ENABLE_OUTPUT: case DECODER_ENABLE_OUTPUT:
v = *(int*)arg; v = *(int *) arg;
if (decoder->enable != v) { if (decoder->enable != v) {
decoder->enable = v; decoder->enable = v;
i2c_smbus_write_byte_data(client,0x0E, v ? 0x18 : 0x00); saa7110_write(client, 0x0E, v ? 0x18 : 0x80);
dprintk(1, KERN_INFO "%s: YUV %s\n", I2C_NAME(client),
v ? "on" : "off");
} }
break; break;
case DECODER_SET_PICTURE: case DECODER_SET_PICTURE:
{ {
struct video_picture *pic = arg; struct video_picture *pic = arg;
if (decoder->bright != pic->brightness) { if (decoder->bright != pic->brightness) {
/* We want 0 to 255 we get 0-65535 */ /* We want 0 to 255 we get 0-65535 */
decoder->bright = pic->brightness; decoder->bright = pic->brightness;
i2c_smbus_write_byte_data(client, 0x19, decoder->bright >> 8); saa7110_write(client, 0x19, decoder->bright >> 8);
}
if (decoder->contrast != pic->contrast) {
/* We want 0 to 127 we get 0-65535 */
decoder->contrast = pic->contrast;
i2c_smbus_write_byte_data(client, 0x13, decoder->contrast >> 9);
}
if (decoder->sat != pic->colour) {
/* We want 0 to 127 we get 0-65535 */
decoder->sat = pic->colour;
i2c_smbus_write_byte_data(client, 0x12, decoder->sat >> 9);
}
if (decoder->hue != pic->hue) {
/* We want -128 to 127 we get 0-65535 */
decoder->hue = pic->hue;
i2c_smbus_write_byte_data(client, 0x07, (decoder->hue>>8)-128);
}
} }
if (decoder->contrast != pic->contrast) {
/* We want 0 to 127 we get 0-65535 */
decoder->contrast = pic->contrast;
saa7110_write(client, 0x13,
decoder->contrast >> 9);
}
if (decoder->sat != pic->colour) {
/* We want 0 to 127 we get 0-65535 */
decoder->sat = pic->colour;
saa7110_write(client, 0x12, decoder->sat >> 9);
}
if (decoder->hue != pic->hue) {
/* We want -128 to 127 we get 0-65535 */
decoder->hue = pic->hue;
saa7110_write(client, 0x07,
(decoder->hue >> 8) - 128);
}
}
break; break;
case DECODER_DUMP: case DECODER_DUMP:
for (v=0; v<34; v+=16) { for (v = 0; v < 0x34; v += 16) {
int j; int j;
DEBUG(printk(KERN_INFO "%s: %03x\n",client->name,v)); dprintk(1, KERN_INFO "%s: %03x\n", I2C_NAME(client),
for (j=0; j<16; j++) { v);
DEBUG(printk(KERN_INFO " %02x",decoder->reg[v+j])); for (j = 0; j < 16; j++) {
dprintk(1, KERN_INFO " %02x",
decoder->reg[v + j]);
} }
DEBUG(printk(KERN_INFO "\n")); dprintk(1, KERN_INFO "\n");
} }
break; break;
default: default:
DEBUG(printk(KERN_INFO "unknown saa7110_command??(%d)\n",cmd)); dprintk(1, KERN_INFO "unknown saa7110_command??(%d)\n",
cmd);
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
...@@ -380,33 +447,173 @@ int saa7110_command(struct i2c_client *client, unsigned int cmd, void *arg) ...@@ -380,33 +447,173 @@ int saa7110_command(struct i2c_client *client, unsigned int cmd, void *arg)
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_saa7110 = /*
{ * Generic i2c probe
.owner = THIS_MODULE, * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
.name = IF_NAME, /* name */ */
.id = I2C_DRIVERID_SAA7110, /* in i2c.h */ static unsigned short normal_i2c[] = {
.flags = I2C_DF_NOTIFY, /* Addr range */ I2C_SAA7110 >> 1,
.attach_adapter = saa7110_probe, (I2C_SAA7110 >> 1) + 1,
.detach_client = saa7110_detach, I2C_CLIENT_END
.command = saa7110_command
}; };
static struct i2c_client client_template = { static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
.id = -1,
.driver = &i2c_driver_saa7110, static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
.name = "saa7110_client", static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_i2c_range = normal_i2c_range,
.probe = probe,
.probe_range = probe_range,
.ignore = ignore,
.ignore_range = ignore_range,
.force = force
}; };
static int saa7110_init(void) static int saa7110_i2c_id = 0;
static struct i2c_driver i2c_driver_saa7110;
static int
saa7110_detect_client (struct i2c_adapter *adapter,
int address,
int kind)
{
struct i2c_client *client;
struct saa7110 *decoder;
int rv;
dprintk(1,
KERN_INFO
"saa7110.c: detecting saa7110 client on address 0x%x\n",
address << 1);
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality
(adapter,
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == 0)
return -ENOMEM;
memset(client, 0, sizeof(struct i2c_client));
client->addr = address;
client->adapter = adapter;
client->driver = &i2c_driver_saa7110;
client->flags = I2C_CLIENT_ALLOW_USE;
client->id = saa7110_i2c_id++;
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"saa7110[%d]", client->id);
decoder = kmalloc(sizeof(struct saa7110), GFP_KERNEL);
if (decoder == 0) {
kfree(client);
return -ENOMEM;
}
memset(decoder, 0, sizeof(struct saa7110));
decoder->norm = VIDEO_MODE_PAL;
decoder->input = 0;
decoder->enable = 1;
decoder->bright = 32768;
decoder->contrast = 32768;
decoder->hue = 32768;
decoder->sat = 32768;
init_waitqueue_head(&decoder->wq);
i2c_set_clientdata(client, decoder);
rv = i2c_attach_client(client);
if (rv) {
kfree(client);
kfree(decoder);
return rv;
}
rv = saa7110_write_block(client, initseq, sizeof(initseq));
if (rv < 0)
dprintk(1, KERN_ERR "%s_attach: init status %d\n",
I2C_NAME(client), rv);
else {
int ver, status;
saa7110_write(client, 0x21, 0x10);
saa7110_write(client, 0x0e, 0x18);
saa7110_write(client, 0x0D, 0x04);
ver = saa7110_read(client);
saa7110_write(client, 0x0D, 0x06);
//mdelay(150);
status = saa7110_read(client);
dprintk(1,
KERN_INFO
"%s_attach: SAA7110A version %x at 0x%02x, status=0x%02x\n",
I2C_NAME(client), ver, client->addr << 1, status);
saa7110_write(client, 0x0D, 0x86);
saa7110_write(client, 0x0F, 0x10);
saa7110_write(client, 0x11, 0x59);
//saa7110_write(client, 0x2E, 0x9A);
}
//saa7110_selmux(client,0);
//determine_norm(client);
/* setup and implicit mode 0 select has been performed */
return 0;
}
static int
saa7110_attach_adapter (struct i2c_adapter *adapter)
{
dprintk(1,
KERN_INFO
"saa7110.c: starting probe for adapter %s (0x%x)\n",
I2C_NAME(adapter), adapter->id);
return i2c_probe(adapter, &addr_data, &saa7110_detect_client);
}
static int
saa7110_detach_client (struct i2c_client *client)
{
struct saa7110 *decoder = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
return err;
}
kfree(decoder);
kfree(client);
return 0;
}
/* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_saa7110 = {
.owner = THIS_MODULE,
.name = "saa7110",
.id = I2C_DRIVERID_SAA7110,
.flags = I2C_DF_NOTIFY,
.attach_adapter = saa7110_attach_adapter,
.detach_client = saa7110_detach_client,
.command = saa7110_command,
};
static int __init
saa7110_init (void)
{ {
return i2c_add_driver(&i2c_driver_saa7110); return i2c_add_driver(&i2c_driver_saa7110);
} }
static void saa7110_exit(void) static void __exit
saa7110_exit (void)
{ {
i2c_del_driver(&i2c_driver_saa7110); i2c_del_driver(&i2c_driver_saa7110);
} }
module_init(saa7110_init); module_init(saa7110_init);
module_exit(saa7110_exit); module_exit(saa7110_exit);
MODULE_LICENSE("GPL");
/* /*
saa7111 - Philips SAA7111A video decoder driver version 0.0.3 * saa7111 - Philips SAA7111A video decoder driver version 0.0.3
*
* Copyright (C) 1998 Dave Perks <dperks@ibm.net>
*
* Slight changes for video timing and attachment output by
* Wolfgang Scherr <scherr@net4you.net>
*
* Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
* - moved over to linux>=2.4.x i2c protocol (1/1/2003)
*
* 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.
*/
Copyright (C) 1998 Dave Perks <dperks@ibm.net> #include <linux/version.h>
Slight changes for video timing and attachment output by
Wolfgang Scherr <scherr@net4you.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -30,22 +35,43 @@ ...@@ -30,22 +35,43 @@
#include <linux/major.h> #include <linux/major.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/pci.h>
#include <linux/signal.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <asm/segment.h>
#include <linux/types.h>
#include <linux/videodev.h> #include <linux/videodev.h>
#include <linux/version.h> #include <linux/version.h>
#include <asm/uaccess.h>
MODULE_DESCRIPTION("Philips SAA7111 video decoder driver");
MODULE_AUTHOR("Dave Perks");
MODULE_LICENSE("GPL");
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c-dev.h>
#define I2C_NAME(s) (s)->dev.name
#include <linux/video_decoder.h> #include <linux/video_decoder.h>
#define DEBUG(x) /* Debug driver */ static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
struct saa7111 { struct saa7111 {
struct i2c_client *client;
int addr;
struct semaphore lock;
unsigned char reg[32]; unsigned char reg[32];
int norm; int norm;
...@@ -57,335 +83,344 @@ struct saa7111 { ...@@ -57,335 +83,344 @@ struct saa7111 {
int sat; int sat;
}; };
static unsigned short normal_i2c[] = { 0x24, 0x25, I2C_CLIENT_END }; #define I2C_SAA7111 0x48
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
I2C_CLIENT_INSMOD;
static struct i2c_client client_template;
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static int saa7111_attach(struct i2c_adapter *adap, int addr, int kind) static inline int
saa7111_write (struct i2c_client *client,
u8 reg,
u8 value)
{ {
int i; struct saa7111 *decoder = i2c_get_clientdata(client);
struct saa7111 *decoder; decoder->reg[reg] = value;
struct i2c_client *client; return i2c_smbus_write_byte_data(client, reg, value);
}
/* who wrote this? init[] is used for i2c_master_send() which expects an array that
will be used for the 'buf' part of an i2c message unchanged. so, the first byte
needs to be the subaddress to start with, then follow the data bytes... */
static const unsigned char init[] = {
0x00, /* start address */
0x00, /* 00 - ID byte */
0x00, /* 01 - reserved */
/*front end */
0xd0, /* 02 - FUSE=3, GUDL=2, MODE=0 */
0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
0x00, /* 04 - GAI1=256 */
0x00, /* 05 - GAI2=256 */
/* decoder */
0xf3, /* 06 - HSB at 13(50Hz) / 17(60Hz) pixels after end of last line */
0x13, /* 07 - HSS at 113(50Hz) / 117(60Hz) pixels after end of last line */
0xc8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1, HPLL=0, VNOI=0 */
0x01, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0, UPTCV=0, APER=1 */
0x80, /* 0a - BRIG=128 */
0x47, /* 0b - CONT=1.109 */
0x40, /* 0c - SATN=1.0 */
0x00, /* 0d - HUE=0 */
0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */
0x00, /* 0f - reserved */
0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */
0x00, /* 12 - output control 2 */
0x00, /* 13 - output control 3 */
0x00, /* 14 - reserved */
0x00, /* 15 - VBI */
0x00, /* 16 - VBI */
0x00, /* 17 - VBI */
};
client = kmalloc(sizeof(*client), GFP_KERNEL);
if(client == NULL)
return -ENOMEM;
client_template.adapter = adap;
client_template.addr = addr;
memcpy(client, &client_template, sizeof(*client));
decoder = kmalloc(sizeof(*decoder), GFP_KERNEL);
if (decoder == NULL)
{
kfree(client);
return -ENOMEM;
}
memset(decoder, 0, sizeof(*decoder));
strlcpy(client->name, "saa7111", DEVICE_NAME_SIZE);
decoder->client = client;
i2c_set_clientdata(client, decoder);
decoder->addr = addr;
decoder->norm = VIDEO_MODE_NTSC;
decoder->input = 0;
decoder->enable = 1;
decoder->bright = 32768;
decoder->contrast = 32768;
decoder->hue = 32768;
decoder->sat = 32768;
i = i2c_master_send(client, init, sizeof(init)); static int
if (i < 0) { saa7111_write_block (struct i2c_client *client,
printk(KERN_ERR "%s_attach: init status %d\n", const u8 *data,
client->name, i); unsigned int len)
{
int ret = -1;
u8 reg;
/* the saa7111 has an autoincrement function, use it if
* the adapter understands raw I2C */
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
/* do raw I2C, not smbus compatible */
struct saa7111 *decoder = i2c_get_clientdata(client);
struct i2c_msg msg;
u8 block_data[32];
msg.addr = client->addr;
msg.flags = client->flags;
while (len >= 2) {
msg.buf = (char *) block_data;
msg.len = 0;
block_data[msg.len++] = reg = data[0];
do {
block_data[msg.len++] =
decoder->reg[reg++] = data[1];
len -= 2;
data += 2;
} while (len >= 2 && data[0] == reg &&
msg.len < 32);
if ((ret =
i2c_transfer(client->adapter, &msg, 1)) < 0)
break;
}
} else { } else {
printk(KERN_INFO "%s_attach: chip version %x @ 0x%08x\n", /* do some slow I2C emulation kind of thing */
client->name, i2c_smbus_read_byte_data(client, 0x00) >> 4,addr); while (len >= 2) {
reg = *data++;
if ((ret =
saa7111_write(client, reg, *data++)) < 0)
break;
len -= 2;
}
} }
init_MUTEX(&decoder->lock); return ret;
i2c_attach_client(client);
MOD_INC_USE_COUNT;
return 0;
}
static int saa7111_probe(struct i2c_adapter *adap)
{
/* probing unknown devices on any Matrox i2c-bus takes ages due to the
slow bit banging algorithm used. because of the fact a saa7111(a)
is *never* present on a Matrox gfx card, we can skip such adapters
here */
if( 0 != (adap->id & I2C_HW_B_G400)) {
return -ENODEV;
}
printk("saa7111: probing %s i2c adapter [id=0x%x]\n",
adap->name,adap->id);
return i2c_probe(adap, &addr_data, saa7111_attach);
} }
static int saa7111_detach(struct i2c_client *client) static inline int
saa7111_read (struct i2c_client *client,
u8 reg)
{ {
struct saa7111 *decoder = i2c_get_clientdata(client); return i2c_smbus_read_byte_data(client, reg);
i2c_detach_client(client);
kfree(decoder);
kfree(client);
MOD_DEC_USE_COUNT;
return 0;
} }
static int saa7111_command(struct i2c_client *client, unsigned int cmd, /* ----------------------------------------------------------------------- */
void *arg)
static const unsigned char init[] = {
0x00, 0x00, /* 00 - ID byte */
0x01, 0x00, /* 01 - reserved */
/*front end */
0x02, 0xd0, /* 02 - FUSE=3, GUDL=2, MODE=0 */
0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0,
* HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
0x04, 0x00, /* 04 - GAI1=256 */
0x05, 0x00, /* 05 - GAI2=256 */
/* decoder */
0x06, 0xf3, /* 06 - HSB at 13(50Hz) / 17(60Hz)
* pixels after end of last line */
/*0x07, 0x13, * 07 - HSS at 113(50Hz) / 117(60Hz) pixels
* after end of last line */
0x07, 0xe8, /* 07 - HSS seems to be needed to
* work with NTSC, too */
0x08, 0xc8, /* 08 - AUFD=1, FSEL=1, EXFIL=0,
* VTRC=1, HPLL=0, VNOI=0 */
0x09, 0x01, /* 09 - BYPS=0, PREF=0, BPSS=0,
* VBLB=0, UPTCV=0, APER=1 */
0x0a, 0x80, /* 0a - BRIG=128 */
0x0b, 0x47, /* 0b - CONT=1.109 */
0x0c, 0x40, /* 0c - SATN=1.0 */
0x0d, 0x00, /* 0d - HUE=0 */
0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0,
* FCTC=0, CHBW=1 */
0x0f, 0x00, /* 0f - reserved */
0x10, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
0x11, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1,
* OEYC=1, OEHV=1, VIPB=0, COLO=0 */
0x12, 0x00, /* 12 - output control 2 */
0x13, 0x00, /* 13 - output control 3 */
0x14, 0x00, /* 14 - reserved */
0x15, 0x00, /* 15 - VBI */
0x16, 0x00, /* 16 - VBI */
0x17, 0x00, /* 17 - VBI */
};
static int
saa7111_command (struct i2c_client *client,
unsigned int cmd,
void *arg)
{ {
struct saa7111 *decoder = i2c_get_clientdata(client); struct saa7111 *decoder = i2c_get_clientdata(client);
switch (cmd) { switch (cmd) {
#if defined(DECODER_DUMP) case 0:
//saa7111_write_block(client, init, sizeof(init));
break;
case DECODER_DUMP: case DECODER_DUMP:
{ {
int i; int i;
for (i = 0; i < 32; i += 16) { for (i = 0; i < 32; i += 16) {
int j; int j;
printk("KERN_DEBUG %s: %03x", client->name, i); printk(KERN_DEBUG "%s: %03x", I2C_NAME(client), i);
for (j = 0; j < 16; ++j) { for (j = 0; j < 16; ++j) {
printk(" %02x", printk(" %02x",
i2c_smbus_read_byte_data(client, saa7111_read(client, i + j));
i + j));
}
printk("\n");
} }
printk("\n");
} }
}
break; break;
#endif /* defined(DECODER_DUMP) */
case DECODER_GET_CAPABILITIES: case DECODER_GET_CAPABILITIES:
{ {
struct video_decoder_capability *cap = arg; struct video_decoder_capability *cap = arg;
cap->flags cap->flags = VIDEO_DECODER_PAL |
= VIDEO_DECODER_PAL VIDEO_DECODER_NTSC |
| VIDEO_DECODER_NTSC VIDEO_DECODER_SECAM |
| VIDEO_DECODER_AUTO | VIDEO_DECODER_CCIR; VIDEO_DECODER_AUTO |
cap->inputs = 8; VIDEO_DECODER_CCIR;
cap->outputs = 1; cap->inputs = 8;
} cap->outputs = 1;
}
break; break;
case DECODER_GET_STATUS: case DECODER_GET_STATUS:
{ {
int *iarg = arg; int *iarg = arg;
int status; int status;
int res; int res;
status = i2c_smbus_read_byte_data(client, 0x1f); status = saa7111_read(client, 0x1f);
res = 0; dprintk(1, KERN_DEBUG "%s status: 0x%02x\n", I2C_NAME(client),
if ((status & (1 << 6)) == 0) { status);
res |= DECODER_STATUS_GOOD; res = 0;
} if ((status & (1 << 6)) == 0) {
switch (decoder->norm) { res |= DECODER_STATUS_GOOD;
case VIDEO_MODE_NTSC: }
switch (decoder->norm) {
case VIDEO_MODE_NTSC:
res |= DECODER_STATUS_NTSC;
break;
case VIDEO_MODE_PAL:
res |= DECODER_STATUS_PAL;
break;
case VIDEO_MODE_SECAM:
res |= DECODER_STATUS_SECAM;
break;
default:
case VIDEO_MODE_AUTO:
if ((status & (1 << 5)) != 0) {
res |= DECODER_STATUS_NTSC; res |= DECODER_STATUS_NTSC;
break; } else {
case VIDEO_MODE_PAL:
res |= DECODER_STATUS_PAL; res |= DECODER_STATUS_PAL;
break;
default:
case VIDEO_MODE_AUTO:
if ((status & (1 << 5)) != 0) {
res |= DECODER_STATUS_NTSC;
} else {
res |= DECODER_STATUS_PAL;
}
break;
}
if ((status & (1 << 0)) != 0) {
res |= DECODER_STATUS_COLOR;
} }
*iarg = res; break;
} }
if ((status & (1 << 0)) != 0) {
res |= DECODER_STATUS_COLOR;
}
*iarg = res;
}
break; break;
case DECODER_SET_NORM: case DECODER_SET_NORM:
{ {
int *iarg = arg; int *iarg = arg;
switch (*iarg) { switch (*iarg) {
case VIDEO_MODE_NTSC: case VIDEO_MODE_NTSC:
i2c_smbus_write_byte_data(client, 0x08, saa7111_write(client, 0x08,
(decoder-> (decoder->reg[0x08] & 0x3f) | 0x40);
reg[0x08] & 0x3f) | 0x40); saa7111_write(client, 0x0e,
break; (decoder->reg[0x0e] & 0x8f));
break;
case VIDEO_MODE_PAL:
i2c_smbus_write_byte_data(client, 0x08, case VIDEO_MODE_PAL:
(decoder-> saa7111_write(client, 0x08,
reg[0x08] & 0x3f) | 0x00); (decoder->reg[0x08] & 0x3f) | 0x00);
break; saa7111_write(client, 0x0e,
(decoder->reg[0x0e] & 0x8f));
case VIDEO_MODE_AUTO: break;
i2c_smbus_write_byte_data(client, 0x08,
(decoder-> case VIDEO_MODE_SECAM:
reg[0x08] & 0x3f) | 0x80); saa7111_write(client, 0x08,
break; (decoder->reg[0x0e] & 0x3f) | 0x00);
saa7111_write(client, 0x0e,
default: (decoder->reg[0x0e] & 0x8f) | 0x50);
return -EINVAL; break;
case VIDEO_MODE_AUTO:
saa7111_write(client, 0x08,
(decoder->reg[0x08] & 0x3f) | 0x80);
saa7111_write(client, 0x0e,
(decoder->reg[0x0e] & 0x8f));
break;
default:
return -EINVAL;
}
decoder->norm = *iarg;
} }
decoder->norm = *iarg;
}
break; break;
case DECODER_SET_INPUT: case DECODER_SET_INPUT:
{ {
int *iarg = arg; int *iarg = arg;
if (*iarg < 0 || *iarg > 7) { if (*iarg < 0 || *iarg > 7) {
return -EINVAL; return -EINVAL;
} }
if (decoder->input != *iarg) { if (decoder->input != *iarg) {
decoder->input = *iarg; decoder->input = *iarg;
/* select mode */ /* select mode */
i2c_smbus_write_byte_data(client, 0x02, saa7111_write(client, 0x02,
(decoder-> (decoder->
reg[0x02] & 0xf8) | reg[0x02] & 0xf8) | decoder->input);
decoder->input); /* bypass chrominance trap for modes 4..7 */
/* bypass chrominance trap for modes 4..7 */ saa7111_write(client, 0x09,
i2c_smbus_write_byte_data(client, 0x09, (decoder->
(decoder-> reg[0x09] & 0x7f) | ((decoder->
reg[0x09] & 0x7f) | input >
((decoder->input > 3) ? 0x80 :
3) ? 0x80 : 0)); 0));
}
} }
}
break; break;
case DECODER_SET_OUTPUT: case DECODER_SET_OUTPUT:
{ {
int *iarg = arg; int *iarg = arg;
/* not much choice of outputs */ /* not much choice of outputs */
if (*iarg != 0) { if (*iarg != 0) {
return -EINVAL; return -EINVAL;
}
} }
}
break; break;
case DECODER_ENABLE_OUTPUT: case DECODER_ENABLE_OUTPUT:
{ {
int *iarg = arg; int *iarg = arg;
int enable = (*iarg != 0); int enable = (*iarg != 0);
if (decoder->enable != enable) { if (decoder->enable != enable) {
decoder->enable = enable; decoder->enable = enable;
// RJ: If output should be disabled (for playing videos), we also need a open PLL. /* RJ: If output should be disabled (for
// The input is set to 0 (where no input source is connected), although this * playing videos), we also need a open PLL.
// is not necessary. * The input is set to 0 (where no input
// * source is connected), although this
// If output should be enabled, we have to reverse the above. * is not necessary.
*
if (decoder->enable) { * If output should be enabled, we have to
i2c_smbus_write_byte_data(client, 0x02, * reverse the above.
(decoder-> */
reg[0x02] & 0xf8) |
decoder->input); if (decoder->enable) {
i2c_smbus_write_byte_data(client, 0x08, saa7111_write(client, 0x02,
(decoder-> (decoder->
reg[0x08] & 0xfb)); reg[0x02] & 0xf8) |
i2c_smbus_write_byte_data(client, 0x11, decoder->input);
(decoder-> saa7111_write(client, 0x08,
reg[0x11] & 0xf3) | (decoder->reg[0x08] & 0xfb));
0x0c); saa7111_write(client, 0x11,
} else { (decoder->
i2c_smbus_write_byte_data(client, 0x02, reg[0x11] & 0xf3) | 0x0c);
(decoder-> } else {
reg[0x02] & 0xf8)); saa7111_write(client, 0x02,
i2c_smbus_write_byte_data(client, 0x08, (decoder->reg[0x02] & 0xf8));
(decoder-> saa7111_write(client, 0x08,
reg[0x08] & 0xfb) | (decoder->
0x04); reg[0x08] & 0xfb) | 0x04);
i2c_smbus_write_byte_data(client, 0x11, saa7111_write(client, 0x11,
(decoder-> (decoder->reg[0x11] & 0xf3));
reg[0x11] & 0xf3));
}
} }
} }
}
break; break;
case DECODER_SET_PICTURE: case DECODER_SET_PICTURE:
{ {
struct video_picture *pic = arg; struct video_picture *pic = arg;
if (decoder->bright != pic->brightness) { if (decoder->bright != pic->brightness) {
/* We want 0 to 255 we get 0-65535 */ /* We want 0 to 255 we get 0-65535 */
decoder->bright = pic->brightness; decoder->bright = pic->brightness;
i2c_smbus_write_byte_data(client, 0x0a, saa7111_write(client, 0x0a, decoder->bright >> 8);
decoder->bright >> 8);
}
if (decoder->contrast != pic->contrast) {
/* We want 0 to 127 we get 0-65535 */
decoder->contrast = pic->contrast;
i2c_smbus_write_byte_data(client, 0x0b,
decoder->contrast >> 9);
}
if (decoder->sat != pic->colour) {
/* We want 0 to 127 we get 0-65535 */
decoder->sat = pic->colour;
i2c_smbus_write_byte_data(client, 0x0c,
decoder->sat >> 9);
}
if (decoder->hue != pic->hue) {
/* We want -128 to 127 we get 0-65535 */
decoder->hue = pic->hue;
i2c_smbus_write_byte_data(client, 0x0d,
(decoder->hue - 32768) >> 8);
}
} }
if (decoder->contrast != pic->contrast) {
/* We want 0 to 127 we get 0-65535 */
decoder->contrast = pic->contrast;
saa7111_write(client, 0x0b,
decoder->contrast >> 9);
}
if (decoder->sat != pic->colour) {
/* We want 0 to 127 we get 0-65535 */
decoder->sat = pic->colour;
saa7111_write(client, 0x0c, decoder->sat >> 9);
}
if (decoder->hue != pic->hue) {
/* We want -128 to 127 we get 0-65535 */
decoder->hue = pic->hue;
saa7111_write(client, 0x0d,
(decoder->hue - 32768) >> 8);
}
}
break; break;
default: default:
...@@ -397,34 +432,151 @@ static int saa7111_command(struct i2c_client *client, unsigned int cmd, ...@@ -397,34 +432,151 @@ static int saa7111_command(struct i2c_client *client, unsigned int cmd,
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_saa7111 = { /*
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54) * Generic i2c probe
.owner = THIS_MODULE, * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
#endif */
.name = "saa7111", /* name */ static unsigned short normal_i2c[] = { I2C_SAA7111 >> 1, I2C_CLIENT_END };
.id = I2C_DRIVERID_SAA7111A, /* ID */ static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
.flags = I2C_DF_NOTIFY,
.attach_adapter = saa7111_probe, static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
.detach_client = saa7111_detach, static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
.command = saa7111_command static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_i2c_range = normal_i2c_range,
.probe = probe,
.probe_range = probe_range,
.ignore = ignore,
.ignore_range = ignore_range,
.force = force
}; };
static struct i2c_client client_template = { static int saa7111_i2c_id = 0;
.id = -1, static struct i2c_driver i2c_driver_saa7111;
.driver = &i2c_driver_saa7111,
.name = "saa7111_client", static int
saa7111_detect_client (struct i2c_adapter *adapter,
int address,
int kind)
{
int i;
struct i2c_client *client;
struct saa7111 *decoder;
dprintk(1,
KERN_INFO
"saa7111.c: detecting saa7111 client on address 0x%x\n",
address << 1);
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == 0)
return -ENOMEM;
memset(client, 0, sizeof(struct i2c_client));
client->addr = address;
client->adapter = adapter;
client->driver = &i2c_driver_saa7111;
client->flags = I2C_CLIENT_ALLOW_USE;
client->id = saa7111_i2c_id++;
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"saa7111[%d]", client->id);
decoder = kmalloc(sizeof(struct saa7111), GFP_KERNEL);
if (decoder == NULL) {
kfree(client);
return -ENOMEM;
}
memset(decoder, 0, sizeof(struct saa7111));
decoder->norm = VIDEO_MODE_NTSC;
decoder->input = 0;
decoder->enable = 1;
decoder->bright = 32768;
decoder->contrast = 32768;
decoder->hue = 32768;
decoder->sat = 32768;
i2c_set_clientdata(client, decoder);
i = i2c_attach_client(client);
if (i) {
kfree(client);
kfree(decoder);
return i;
}
i = saa7111_write_block(client, init, sizeof(init));
if (i < 0) {
dprintk(1, KERN_ERR "%s_attach error: init status %d\n",
I2C_NAME(client), i);
} else {
dprintk(1,
KERN_INFO
"%s_attach: chip version %x at address 0x%x\n",
I2C_NAME(client), saa7111_read(client, 0x00) >> 4,
client->addr << 1);
}
return 0;
}
static int
saa7111_attach_adapter (struct i2c_adapter *adapter)
{
dprintk(1,
KERN_INFO
"saa7111.c: starting probe for adapter %s (0x%x)\n",
I2C_NAME(adapter), adapter->id);
return i2c_probe(adapter, &addr_data, &saa7111_detect_client);
}
static int
saa7111_detach_client (struct i2c_client *client)
{
struct saa7111 *decoder = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
return err;
}
kfree(decoder);
kfree(client);
return 0;
}
/* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_saa7111 = {
.owner = THIS_MODULE,
.name = "saa7111",
.id = I2C_DRIVERID_SAA7111A,
.flags = I2C_DF_NOTIFY,
.attach_adapter = saa7111_attach_adapter,
.detach_client = saa7111_detach_client,
.command = saa7111_command,
}; };
static int saa7111_init(void) static int __init
saa7111_init (void)
{ {
return i2c_add_driver(&i2c_driver_saa7111); return i2c_add_driver(&i2c_driver_saa7111);
} }
static void saa7111_exit(void) static void __exit
saa7111_exit (void)
{ {
i2c_del_driver(&i2c_driver_saa7111); i2c_del_driver(&i2c_driver_saa7111);
} }
module_init(saa7111_init); module_init(saa7111_init);
module_exit(saa7111_exit); module_exit(saa7111_exit);
MODULE_LICENSE("GPL");
/*
* saa7114 - Philips SAA7114H video decoder driver version 0.0.1
*
* Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
*
* Based on saa7111 driver by Dave Perks
*
* Copyright (C) 1998 Dave Perks <dperks@ibm.net>
*
* Slight changes for video timing and attachment output by
* Wolfgang Scherr <scherr@net4you.net>
*
* Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
* - moved over to linux>=2.4.x i2c protocol (1/1/2003)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/signal.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/sched.h>
#include <asm/segment.h>
#include <linux/types.h>
#include <linux/videodev.h>
#include <linux/version.h>
#include <asm/uaccess.h>
MODULE_DESCRIPTION("Philips SAA7114H video decoder driver");
MODULE_AUTHOR("Maxim Yevtyushkin");
MODULE_LICENSE("GPL");
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#define I2C_NAME(x) (x)->dev.name
#include <linux/video_decoder.h>
static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
/* ----------------------------------------------------------------------- */
struct saa7114 {
unsigned char reg[0xf0 * 2];
int norm;
int input;
int enable;
int bright;
int contrast;
int hue;
int sat;
int playback;
};
#define I2C_SAA7114 0x42
#define I2C_SAA7114A 0x40
#define I2C_DELAY 10
//#define SAA_7114_NTSC_HSYNC_START (-3)
//#define SAA_7114_NTSC_HSYNC_STOP (-18)
#define SAA_7114_NTSC_HSYNC_START (-17)
#define SAA_7114_NTSC_HSYNC_STOP (-32)
//#define SAA_7114_NTSC_HOFFSET (5)
#define SAA_7114_NTSC_HOFFSET (6)
#define SAA_7114_NTSC_VOFFSET (10)
#define SAA_7114_NTSC_WIDTH (720)
#define SAA_7114_NTSC_HEIGHT (250)
#define SAA_7114_SECAM_HSYNC_START (-17)
#define SAA_7114_SECAM_HSYNC_STOP (-32)
#define SAA_7114_SECAM_HOFFSET (2)
#define SAA_7114_SECAM_VOFFSET (10)
#define SAA_7114_SECAM_WIDTH (720)
#define SAA_7114_SECAM_HEIGHT (300)
#define SAA_7114_PAL_HSYNC_START (-17)
#define SAA_7114_PAL_HSYNC_STOP (-32)
#define SAA_7114_PAL_HOFFSET (2)
#define SAA_7114_PAL_VOFFSET (10)
#define SAA_7114_PAL_WIDTH (720)
#define SAA_7114_PAL_HEIGHT (300)
#define SAA_7114_VERTICAL_CHROMA_OFFSET 0 //0x50504040
#define SAA_7114_VERTICAL_LUMA_OFFSET 0
#define REG_ADDR(x) (((x) << 1) + 1)
#define LOBYTE(x) ((unsigned char)((x) & 0xff))
#define HIBYTE(x) ((unsigned char)(((x) >> 8) & 0xff))
#define LOWORD(x) ((unsigned short int)((x) & 0xffff))
#define HIWORD(x) ((unsigned short int)(((x) >> 16) & 0xffff))
/* ----------------------------------------------------------------------- */
static inline int
saa7114_write (struct i2c_client *client,
u8 reg,
u8 value)
{
/*struct saa7114 *decoder = i2c_get_clientdata(client);*/
/*decoder->reg[reg] = value;*/
return i2c_smbus_write_byte_data(client, reg, value);
}
static int
saa7114_write_block (struct i2c_client *client,
const u8 *data,
unsigned int len)
{
int ret = -1;
u8 reg;
/* the saa7114 has an autoincrement function, use it if
* the adapter understands raw I2C */
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
/* do raw I2C, not smbus compatible */
/*struct saa7114 *decoder = i2c_get_clientdata(client);*/
struct i2c_msg msg;
u8 block_data[32];
msg.addr = client->addr;
msg.flags = client->flags;
while (len >= 2) {
msg.buf = (char *) block_data;
msg.len = 0;
block_data[msg.len++] = reg = data[0];
do {
block_data[msg.len++] =
/*decoder->reg[reg++] =*/ data[1];
len -= 2;
data += 2;
} while (len >= 2 && data[0] == reg &&
msg.len < 32);
if ((ret =
i2c_transfer(client->adapter, &msg, 1)) < 0)
break;
}
} else {
/* do some slow I2C emulation kind of thing */
while (len >= 2) {
reg = *data++;
if ((ret =
saa7114_write(client, reg, *data++)) < 0)
break;
len -= 2;
}
}
return ret;
}
static inline int
saa7114_read (struct i2c_client *client,
u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
/* ----------------------------------------------------------------------- */
// initially set NTSC, composite
static const unsigned char init[] = {
0x00, 0x00, /* 00 - ID byte , chip version,
* read only */
0x01, 0x08, /* 01 - X,X,X,X, IDEL3 to IDEL0 -
* horizontal increment delay,
* recommended position */
0x02, 0x00, /* 02 - FUSE=3, GUDL=2, MODE=0 ;
* input control */
0x03, 0x10, /* 03 - HLNRS=0, VBSL=1, WPOFF=0,
* HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
0x04, 0x90, /* 04 - GAI1=256 */
0x05, 0x90, /* 05 - GAI2=256 */
0x06, SAA_7114_NTSC_HSYNC_START, /* 06 - HSB: hsync start,
* depends on the video standard */
0x07, SAA_7114_NTSC_HSYNC_STOP, /* 07 - HSS: hsync stop, depends
*on the video standard */
0x08, 0xb8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1,
* HPLL: free running in playback, locked
* in capture, VNOI=0 */
0x09, 0x80, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0,
* UPTCV=0, APER=1; depends from input */
0x0a, 0x80, /* 0a - BRIG=128 */
0x0b, 0x44, /* 0b - CONT=1.109 */
0x0c, 0x40, /* 0c - SATN=1.0 */
0x0d, 0x00, /* 0d - HUE=0 */
0x0e, 0x84, /* 0e - CDTO, CSTD2 to 0, DCVF, FCTC,
* CCOMB; depends from video standard */
0x0f, 0x24, /* 0f - ACGC,CGAIN6 to CGAIN0; depends
* from video standard */
0x10, 0x03, /* 10 - OFFU1 to 0, OFFV1 to 0, CHBW,
* LCBW2 to 0 */
0x11, 0x59, /* 11 - COLO, RTP1, HEDL1 to 0, RTP0,
* YDEL2 to 0 */
0x12, 0xc9, /* 12 - RT signal control RTSE13 to 10
* and 03 to 00 */
0x13, 0x80, /* 13 - RT/X port output control */
0x14, 0x00, /* 14 - analog, ADC, compatibility control */
0x15, 0x00, /* 15 - VGATE start FID change */
0x16, 0xfe, /* 16 - VGATE stop */
0x17, 0x00, /* 17 - Misc., VGATE MSBs */
0x18, 0x40, /* RAWG */
0x19, 0x80, /* RAWO */
0x1a, 0x00,
0x1b, 0x00,
0x1c, 0x00,
0x1d, 0x00,
0x1e, 0x00,
0x1f, 0x00, /* status byte, read only */
0x20, 0x00, /* video decoder reserved part */
0x21, 0x00,
0x22, 0x00,
0x23, 0x00,
0x24, 0x00,
0x25, 0x00,
0x26, 0x00,
0x27, 0x00,
0x28, 0x00,
0x29, 0x00,
0x2a, 0x00,
0x2b, 0x00,
0x2c, 0x00,
0x2d, 0x00,
0x2e, 0x00,
0x2f, 0x00,
0x30, 0xbc, /* audio clock generator */
0x31, 0xdf,
0x32, 0x02,
0x33, 0x00,
0x34, 0xcd,
0x35, 0xcc,
0x36, 0x3a,
0x37, 0x00,
0x38, 0x03,
0x39, 0x10,
0x3a, 0x00,
0x3b, 0x00,
0x3c, 0x00,
0x3d, 0x00,
0x3e, 0x00,
0x3f, 0x00,
0x40, 0x00, /* VBI data slicer */
0x41, 0xff,
0x42, 0xff,
0x43, 0xff,
0x44, 0xff,
0x45, 0xff,
0x46, 0xff,
0x47, 0xff,
0x48, 0xff,
0x49, 0xff,
0x4a, 0xff,
0x4b, 0xff,
0x4c, 0xff,
0x4d, 0xff,
0x4e, 0xff,
0x4f, 0xff,
0x50, 0xff,
0x51, 0xff,
0x52, 0xff,
0x53, 0xff,
0x54, 0xff,
0x55, 0xff,
0x56, 0xff,
0x57, 0xff,
0x58, 0x40, // framing code
0x59, 0x47, // horizontal offset
0x5a, 0x06, // vertical offset
0x5b, 0x83, // field offset
0x5c, 0x00, // reserved
0x5d, 0x3e, // header and data
0x5e, 0x00, // sliced data
0x5f, 0x00, // reserved
0x60, 0x00, /* video decoder reserved part */
0x61, 0x00,
0x62, 0x00,
0x63, 0x00,
0x64, 0x00,
0x65, 0x00,
0x66, 0x00,
0x67, 0x00,
0x68, 0x00,
0x69, 0x00,
0x6a, 0x00,
0x6b, 0x00,
0x6c, 0x00,
0x6d, 0x00,
0x6e, 0x00,
0x6f, 0x00,
0x70, 0x00, /* video decoder reserved part */
0x71, 0x00,
0x72, 0x00,
0x73, 0x00,
0x74, 0x00,
0x75, 0x00,
0x76, 0x00,
0x77, 0x00,
0x78, 0x00,
0x79, 0x00,
0x7a, 0x00,
0x7b, 0x00,
0x7c, 0x00,
0x7d, 0x00,
0x7e, 0x00,
0x7f, 0x00,
0x80, 0x00, /* X-port, I-port and scaler */
0x81, 0x00,
0x82, 0x00,
0x83, 0x00,
0x84, 0xc5,
0x85, 0x0d, // hsync and vsync ?
0x86, 0x40,
0x87, 0x01,
0x88, 0x00,
0x89, 0x00,
0x8a, 0x00,
0x8b, 0x00,
0x8c, 0x00,
0x8d, 0x00,
0x8e, 0x00,
0x8f, 0x00,
0x90, 0x03, /* Task A definition */
0x91, 0x08,
0x92, 0x00,
0x93, 0x40,
0x94, 0x00, // window settings
0x95, 0x00,
0x96, 0x00,
0x97, 0x00,
0x98, 0x00,
0x99, 0x00,
0x9a, 0x00,
0x9b, 0x00,
0x9c, 0x00,
0x9d, 0x00,
0x9e, 0x00,
0x9f, 0x00,
0xa0, 0x01, /* horizontal integer prescaling ratio */
0xa1, 0x00, /* horizontal prescaler accumulation
* sequence length */
0xa2, 0x00, /* UV FIR filter, Y FIR filter, prescaler
* DC gain */
0xa3, 0x00,
0xa4, 0x80, // luminance brightness
0xa5, 0x40, // luminance gain
0xa6, 0x40, // chrominance saturation
0xa7, 0x00,
0xa8, 0x00, // horizontal luminance scaling increment
0xa9, 0x04,
0xaa, 0x00, // horizontal luminance phase offset
0xab, 0x00,
0xac, 0x00, // horizontal chrominance scaling increment
0xad, 0x02,
0xae, 0x00, // horizontal chrominance phase offset
0xaf, 0x00,
0xb0, 0x00, // vertical luminance scaling increment
0xb1, 0x04,
0xb2, 0x00, // vertical chrominance scaling increment
0xb3, 0x04,
0xb4, 0x00,
0xb5, 0x00,
0xb6, 0x00,
0xb7, 0x00,
0xb8, 0x00,
0xb9, 0x00,
0xba, 0x00,
0xbb, 0x00,
0xbc, 0x00,
0xbd, 0x00,
0xbe, 0x00,
0xbf, 0x00,
0xc0, 0x02, // Task B definition
0xc1, 0x08,
0xc2, 0x00,
0xc3, 0x40,
0xc4, 0x00, // window settings
0xc5, 0x00,
0xc6, 0x00,
0xc7, 0x00,
0xc8, 0x00,
0xc9, 0x00,
0xca, 0x00,
0xcb, 0x00,
0xcc, 0x00,
0xcd, 0x00,
0xce, 0x00,
0xcf, 0x00,
0xd0, 0x01, // horizontal integer prescaling ratio
0xd1, 0x00, // horizontal prescaler accumulation sequence length
0xd2, 0x00, // UV FIR filter, Y FIR filter, prescaler DC gain
0xd3, 0x00,
0xd4, 0x80, // luminance brightness
0xd5, 0x40, // luminance gain
0xd6, 0x40, // chrominance saturation
0xd7, 0x00,
0xd8, 0x00, // horizontal luminance scaling increment
0xd9, 0x04,
0xda, 0x00, // horizontal luminance phase offset
0xdb, 0x00,
0xdc, 0x00, // horizontal chrominance scaling increment
0xdd, 0x02,
0xde, 0x00, // horizontal chrominance phase offset
0xdf, 0x00,
0xe0, 0x00, // vertical luminance scaling increment
0xe1, 0x04,
0xe2, 0x00, // vertical chrominance scaling increment
0xe3, 0x04,
0xe4, 0x00,
0xe5, 0x00,
0xe6, 0x00,
0xe7, 0x00,
0xe8, 0x00,
0xe9, 0x00,
0xea, 0x00,
0xeb, 0x00,
0xec, 0x00,
0xed, 0x00,
0xee, 0x00,
0xef, 0x00
};
static int
saa7114_command (struct i2c_client *client,
unsigned int cmd,
void *arg)
{
struct saa7114 *decoder = i2c_get_clientdata(client);
switch (cmd) {
case 0:
//dprintk(1, KERN_INFO "%s: writing init\n", I2C_NAME(client));
//saa7114_write_block(client, init, sizeof(init));
break;
case DECODER_DUMP:
{
int i;
dprintk(1, KERN_INFO "%s: decoder dump\n", I2C_NAME(client));
for (i = 0; i < 32; i += 16) {
int j;
printk(KERN_DEBUG "%s: %03x", I2C_NAME(client), i);
for (j = 0; j < 16; ++j) {
printk(" %02x",
saa7114_read(client, i + j));
}
printk("\n");
}
}
break;
case DECODER_GET_CAPABILITIES:
{
struct video_decoder_capability *cap = arg;
dprintk(1, KERN_DEBUG "%s: decoder get capabilities\n",
I2C_NAME(client));
cap->flags = VIDEO_DECODER_PAL |
VIDEO_DECODER_NTSC |
VIDEO_DECODER_AUTO |
VIDEO_DECODER_CCIR;
cap->inputs = 8;
cap->outputs = 1;
}
break;
case DECODER_GET_STATUS:
{
int *iarg = arg;
int status;
int res;
status = saa7114_read(client, 0x1f);
dprintk(1, KERN_DEBUG "%s status: 0x%02x\n", I2C_NAME(client),
status);
res = 0;
if ((status & (1 << 6)) == 0) {
res |= DECODER_STATUS_GOOD;
}
switch (decoder->norm) {
case VIDEO_MODE_NTSC:
res |= DECODER_STATUS_NTSC;
break;
case VIDEO_MODE_PAL:
res |= DECODER_STATUS_PAL;
break;
case VIDEO_MODE_SECAM:
res |= DECODER_STATUS_SECAM;
break;
default:
case VIDEO_MODE_AUTO:
if ((status & (1 << 5)) != 0) {
res |= DECODER_STATUS_NTSC;
} else {
res |= DECODER_STATUS_PAL;
}
break;
}
if ((status & (1 << 0)) != 0) {
res |= DECODER_STATUS_COLOR;
}
*iarg = res;
}
break;
case DECODER_SET_NORM:
{
int *iarg = arg;
short int hoff = 0, voff = 0, w = 0, h = 0;
dprintk(1, KERN_DEBUG "%s: decoder set norm ",
I2C_NAME(client));
switch (*iarg) {
case VIDEO_MODE_NTSC:
dprintk(1, "NTSC\n");
decoder->reg[REG_ADDR(0x06)] =
SAA_7114_NTSC_HSYNC_START;
decoder->reg[REG_ADDR(0x07)] =
SAA_7114_NTSC_HSYNC_STOP;
decoder->reg[REG_ADDR(0x08)] = decoder->playback ? 0x7c : 0xb8; // PLL free when playback, PLL close when capture
decoder->reg[REG_ADDR(0x0e)] = 0x85;
decoder->reg[REG_ADDR(0x0f)] = 0x24;
hoff = SAA_7114_NTSC_HOFFSET;
voff = SAA_7114_NTSC_VOFFSET;
w = SAA_7114_NTSC_WIDTH;
h = SAA_7114_NTSC_HEIGHT;
break;
case VIDEO_MODE_PAL:
dprintk(1, "PAL\n");
decoder->reg[REG_ADDR(0x06)] =
SAA_7114_PAL_HSYNC_START;
decoder->reg[REG_ADDR(0x07)] =
SAA_7114_PAL_HSYNC_STOP;
decoder->reg[REG_ADDR(0x08)] = decoder->playback ? 0x7c : 0xb8; // PLL free when playback, PLL close when capture
decoder->reg[REG_ADDR(0x0e)] = 0x81;
decoder->reg[REG_ADDR(0x0f)] = 0x24;
hoff = SAA_7114_PAL_HOFFSET;
voff = SAA_7114_PAL_VOFFSET;
w = SAA_7114_PAL_WIDTH;
h = SAA_7114_PAL_HEIGHT;
break;
default:
dprintk(1, " Unknown video mode!!!\n");
return -EINVAL;
}
decoder->reg[REG_ADDR(0x94)] = LOBYTE(hoff); // hoffset low
decoder->reg[REG_ADDR(0x95)] = HIBYTE(hoff) & 0x0f; // hoffset high
decoder->reg[REG_ADDR(0x96)] = LOBYTE(w); // width low
decoder->reg[REG_ADDR(0x97)] = HIBYTE(w) & 0x0f; // width high
decoder->reg[REG_ADDR(0x98)] = LOBYTE(voff); // voffset low
decoder->reg[REG_ADDR(0x99)] = HIBYTE(voff) & 0x0f; // voffset high
decoder->reg[REG_ADDR(0x9a)] = LOBYTE(h + 2); // height low
decoder->reg[REG_ADDR(0x9b)] = HIBYTE(h + 2) & 0x0f; // height high
decoder->reg[REG_ADDR(0x9c)] = LOBYTE(w); // out width low
decoder->reg[REG_ADDR(0x9d)] = HIBYTE(w) & 0x0f; // out width high
decoder->reg[REG_ADDR(0x9e)] = LOBYTE(h); // out height low
decoder->reg[REG_ADDR(0x9f)] = HIBYTE(h) & 0x0f; // out height high
decoder->reg[REG_ADDR(0xc4)] = LOBYTE(hoff); // hoffset low
decoder->reg[REG_ADDR(0xc5)] = HIBYTE(hoff) & 0x0f; // hoffset high
decoder->reg[REG_ADDR(0xc6)] = LOBYTE(w); // width low
decoder->reg[REG_ADDR(0xc7)] = HIBYTE(w) & 0x0f; // width high
decoder->reg[REG_ADDR(0xc8)] = LOBYTE(voff); // voffset low
decoder->reg[REG_ADDR(0xc9)] = HIBYTE(voff) & 0x0f; // voffset high
decoder->reg[REG_ADDR(0xca)] = LOBYTE(h + 2); // height low
decoder->reg[REG_ADDR(0xcb)] = HIBYTE(h + 2) & 0x0f; // height high
decoder->reg[REG_ADDR(0xcc)] = LOBYTE(w); // out width low
decoder->reg[REG_ADDR(0xcd)] = HIBYTE(w) & 0x0f; // out width high
decoder->reg[REG_ADDR(0xce)] = LOBYTE(h); // out height low
decoder->reg[REG_ADDR(0xcf)] = HIBYTE(h) & 0x0f; // out height high
saa7114_write(client, 0x80, 0x06); // i-port and scaler back end clock selection, task A&B off
saa7114_write(client, 0x88, 0xd8); // sw reset scaler
saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
saa7114_write_block(client, decoder->reg + (0x06 << 1),
3 << 1);
saa7114_write_block(client, decoder->reg + (0x0e << 1),
2 << 1);
saa7114_write_block(client, decoder->reg + (0x5a << 1),
2 << 1);
saa7114_write_block(client, decoder->reg + (0x94 << 1),
(0x9f + 1 - 0x94) << 1);
saa7114_write_block(client, decoder->reg + (0xc4 << 1),
(0xcf + 1 - 0xc4) << 1);
saa7114_write(client, 0x88, 0xd8); // sw reset scaler
saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
saa7114_write(client, 0x80, 0x36); // i-port and scaler back end clock selection
decoder->norm = *iarg;
}
break;
case DECODER_SET_INPUT:
{
int *iarg = arg;
dprintk(1, KERN_DEBUG "%s: decoder set input (%d)\n",
I2C_NAME(client), *iarg);
if (*iarg < 0 || *iarg > 7) {
return -EINVAL;
}
if (decoder->input != *iarg) {
dprintk(1, KERN_DEBUG "%s: now setting %s input\n",
I2C_NAME(client),
*iarg >= 6 ? "S-Video" : "Composite");
decoder->input = *iarg;
/* select mode */
decoder->reg[REG_ADDR(0x02)] =
(decoder->
reg[REG_ADDR(0x02)] & 0xf0) | (decoder->
input <
6 ? 0x0 : 0x9);
saa7114_write(client, 0x02,
decoder->reg[REG_ADDR(0x02)]);
/* bypass chrominance trap for modes 6..9 */
decoder->reg[REG_ADDR(0x09)] =
(decoder->
reg[REG_ADDR(0x09)] & 0x7f) | (decoder->
input <
6 ? 0x0 :
0x80);
saa7114_write(client, 0x09,
decoder->reg[REG_ADDR(0x09)]);
decoder->reg[REG_ADDR(0x0e)] =
decoder->input <
6 ? decoder->
reg[REG_ADDR(0x0e)] | 1 : decoder->
reg[REG_ADDR(0x0e)] & ~1;
saa7114_write(client, 0x0e,
decoder->reg[REG_ADDR(0x0e)]);
}
}
break;
case DECODER_SET_OUTPUT:
{
int *iarg = arg;
dprintk(1, KERN_DEBUG "%s: decoder set output\n",
I2C_NAME(client));
/* not much choice of outputs */
if (*iarg != 0) {
return -EINVAL;
}
}
break;
case DECODER_ENABLE_OUTPUT:
{
int *iarg = arg;
int enable = (*iarg != 0);
dprintk(1, KERN_DEBUG "%s: decoder %s output\n",
I2C_NAME(client), enable ? "enable" : "disable");
decoder->playback = !enable;
if (decoder->enable != enable) {
decoder->enable = enable;
/* RJ: If output should be disabled (for
* playing videos), we also need a open PLL.
* The input is set to 0 (where no input
* source is connected), although this
* is not necessary.
*
* If output should be enabled, we have to
* reverse the above.
*/
if (decoder->enable) {
decoder->reg[REG_ADDR(0x08)] = 0xb8;
decoder->reg[REG_ADDR(0x12)] = 0xc9;
decoder->reg[REG_ADDR(0x13)] = 0x80;
decoder->reg[REG_ADDR(0x87)] = 0x01;
} else {
decoder->reg[REG_ADDR(0x08)] = 0x7c;
decoder->reg[REG_ADDR(0x12)] = 0x00;
decoder->reg[REG_ADDR(0x13)] = 0x00;
decoder->reg[REG_ADDR(0x87)] = 0x00;
}
saa7114_write_block(client,
decoder->reg + (0x12 << 1),
2 << 1);
saa7114_write(client, 0x08,
decoder->reg[REG_ADDR(0x08)]);
saa7114_write(client, 0x87,
decoder->reg[REG_ADDR(0x87)]);
saa7114_write(client, 0x88, 0xd8); // sw reset scaler
saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
saa7114_write(client, 0x80, 0x36);
}
}
break;
case DECODER_SET_PICTURE:
{
struct video_picture *pic = arg;
dprintk(1,
KERN_DEBUG
"%s: decoder set picture bright=%d contrast=%d saturation=%d hue=%d\n",
I2C_NAME(client), pic->brightness, pic->contrast,
pic->colour, pic->hue);
if (decoder->bright != pic->brightness) {
/* We want 0 to 255 we get 0-65535 */
decoder->bright = pic->brightness;
saa7114_write(client, 0x0a, decoder->bright >> 8);
}
if (decoder->contrast != pic->contrast) {
/* We want 0 to 127 we get 0-65535 */
decoder->contrast = pic->contrast;
saa7114_write(client, 0x0b,
decoder->contrast >> 9);
}
if (decoder->sat != pic->colour) {
/* We want 0 to 127 we get 0-65535 */
decoder->sat = pic->colour;
saa7114_write(client, 0x0c, decoder->sat >> 9);
}
if (decoder->hue != pic->hue) {
/* We want -128 to 127 we get 0-65535 */
decoder->hue = pic->hue;
saa7114_write(client, 0x0d,
(decoder->hue - 32768) >> 8);
}
}
break;
default:
return -EINVAL;
}
return 0;
}
/* ----------------------------------------------------------------------- */
/*
* Generic i2c probe
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
static unsigned short normal_i2c[] =
{ I2C_SAA7114 >> 1, I2C_SAA7114A >> 1, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_i2c_range = normal_i2c_range,
.probe = probe,
.probe_range = probe_range,
.ignore = ignore,
.ignore_range = ignore_range,
.force = force
};
static int saa7114_i2c_id = 0;
static struct i2c_driver i2c_driver_saa7114;
static int
saa7114_detect_client (struct i2c_adapter *adapter,
int address,
int kind)
{
int i, err[30];
short int hoff = SAA_7114_NTSC_HOFFSET;
short int voff = SAA_7114_NTSC_VOFFSET;
short int w = SAA_7114_NTSC_WIDTH;
short int h = SAA_7114_NTSC_HEIGHT;
struct i2c_client *client;
struct saa7114 *decoder;
dprintk(1,
KERN_INFO
"saa7114.c: detecting saa7114 client on address 0x%x\n",
address << 1);
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == 0)
return -ENOMEM;
memset(client, 0, sizeof(struct i2c_client));
client->addr = address;
client->adapter = adapter;
client->driver = &i2c_driver_saa7114;
client->flags = I2C_CLIENT_ALLOW_USE;
client->id = saa7114_i2c_id++;
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"saa7114[%d]", client->id);
decoder = kmalloc(sizeof(struct saa7114), GFP_KERNEL);
if (decoder == NULL) {
kfree(client);
return -ENOMEM;
}
memset(decoder, 0, sizeof(struct saa7114));
decoder->norm = VIDEO_MODE_NTSC;
decoder->input = -1;
decoder->enable = 1;
decoder->bright = 32768;
decoder->contrast = 32768;
decoder->hue = 32768;
decoder->sat = 32768;
decoder->playback = 0; // initially capture mode useda
i2c_set_clientdata(client, decoder);
memcpy(decoder->reg, init, sizeof(init));
decoder->reg[REG_ADDR(0x94)] = LOBYTE(hoff); // hoffset low
decoder->reg[REG_ADDR(0x95)] = HIBYTE(hoff) & 0x0f; // hoffset high
decoder->reg[REG_ADDR(0x96)] = LOBYTE(w); // width low
decoder->reg[REG_ADDR(0x97)] = HIBYTE(w) & 0x0f; // width high
decoder->reg[REG_ADDR(0x98)] = LOBYTE(voff); // voffset low
decoder->reg[REG_ADDR(0x99)] = HIBYTE(voff) & 0x0f; // voffset high
decoder->reg[REG_ADDR(0x9a)] = LOBYTE(h + 2); // height low
decoder->reg[REG_ADDR(0x9b)] = HIBYTE(h + 2) & 0x0f; // height high
decoder->reg[REG_ADDR(0x9c)] = LOBYTE(w); // out width low
decoder->reg[REG_ADDR(0x9d)] = HIBYTE(w) & 0x0f; // out width high
decoder->reg[REG_ADDR(0x9e)] = LOBYTE(h); // out height low
decoder->reg[REG_ADDR(0x9f)] = HIBYTE(h) & 0x0f; // out height high
decoder->reg[REG_ADDR(0xc4)] = LOBYTE(hoff); // hoffset low
decoder->reg[REG_ADDR(0xc5)] = HIBYTE(hoff) & 0x0f; // hoffset high
decoder->reg[REG_ADDR(0xc6)] = LOBYTE(w); // width low
decoder->reg[REG_ADDR(0xc7)] = HIBYTE(w) & 0x0f; // width high
decoder->reg[REG_ADDR(0xc8)] = LOBYTE(voff); // voffset low
decoder->reg[REG_ADDR(0xc9)] = HIBYTE(voff) & 0x0f; // voffset high
decoder->reg[REG_ADDR(0xca)] = LOBYTE(h + 2); // height low
decoder->reg[REG_ADDR(0xcb)] = HIBYTE(h + 2) & 0x0f; // height high
decoder->reg[REG_ADDR(0xcc)] = LOBYTE(w); // out width low
decoder->reg[REG_ADDR(0xcd)] = HIBYTE(w) & 0x0f; // out width high
decoder->reg[REG_ADDR(0xce)] = LOBYTE(h); // out height low
decoder->reg[REG_ADDR(0xcf)] = HIBYTE(h) & 0x0f; // out height high
decoder->reg[REG_ADDR(0xb8)] =
LOBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
decoder->reg[REG_ADDR(0xb9)] =
HIBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
decoder->reg[REG_ADDR(0xba)] =
LOBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
decoder->reg[REG_ADDR(0xbb)] =
HIBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
decoder->reg[REG_ADDR(0xbc)] =
LOBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
decoder->reg[REG_ADDR(0xbd)] =
HIBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
decoder->reg[REG_ADDR(0xbe)] =
LOBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
decoder->reg[REG_ADDR(0xbf)] =
HIBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
decoder->reg[REG_ADDR(0xe8)] =
LOBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
decoder->reg[REG_ADDR(0xe9)] =
HIBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
decoder->reg[REG_ADDR(0xea)] =
LOBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
decoder->reg[REG_ADDR(0xeb)] =
HIBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
decoder->reg[REG_ADDR(0xec)] =
LOBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
decoder->reg[REG_ADDR(0xed)] =
HIBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
decoder->reg[REG_ADDR(0xee)] =
LOBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
decoder->reg[REG_ADDR(0xef)] =
HIBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
decoder->reg[REG_ADDR(0x13)] = 0x80; // RTC0 on
decoder->reg[REG_ADDR(0x87)] = 0x01; // I-Port
decoder->reg[REG_ADDR(0x12)] = 0xc9; // RTS0
decoder->reg[REG_ADDR(0x02)] = 0xc0; // set composite1 input, aveasy
decoder->reg[REG_ADDR(0x09)] = 0x00; // chrominance trap
decoder->reg[REG_ADDR(0x0e)] |= 1; // combfilter on
dprintk(1, KERN_DEBUG "%s_attach: starting decoder init\n",
I2C_NAME(client));
err[0] =
saa7114_write_block(client, decoder->reg + (0x20 << 1),
0x10 << 1);
err[1] =
saa7114_write_block(client, decoder->reg + (0x30 << 1),
0x10 << 1);
err[2] =
saa7114_write_block(client, decoder->reg + (0x63 << 1),
(0x7f + 1 - 0x63) << 1);
err[3] =
saa7114_write_block(client, decoder->reg + (0x89 << 1),
6 << 1);
err[4] =
saa7114_write_block(client, decoder->reg + (0xb8 << 1),
8 << 1);
err[5] =
saa7114_write_block(client, decoder->reg + (0xe8 << 1),
8 << 1);
for (i = 0; i <= 5; i++) {
if (err[i] < 0) {
dprintk(1,
KERN_ERR
"%s_attach: init error %d at stage %d, leaving attach.\n",
I2C_NAME(client), i, err[i]);
return 0;
}
}
for (i = 6; i < 8; i++) {
dprintk(1,
KERN_DEBUG
"%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n",
I2C_NAME(client), i, saa7114_read(client, i),
decoder->reg[REG_ADDR(i)]);
}
dprintk(1,
KERN_DEBUG
"%s_attach: performing decoder reset sequence\n",
I2C_NAME(client));
err[6] = saa7114_write(client, 0x80, 0x06); // i-port and scaler backend clock selection, task A&B off
err[7] = saa7114_write(client, 0x88, 0xd8); // sw reset scaler
err[8] = saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
for (i = 6; i <= 8; i++) {
if (err[i] < 0) {
dprintk(1,
KERN_ERR
"%s_attach: init error %d at stage %d, leaving attach.\n",
I2C_NAME(client), i, err[i]);
return 0;
}
}
dprintk(1, KERN_INFO "%s_attach: performing the rest of init\n",
I2C_NAME(client));
err[9] = saa7114_write(client, 0x01, decoder->reg[REG_ADDR(0x01)]);
err[10] = saa7114_write_block(client, decoder->reg + (0x03 << 1), (0x1e + 1 - 0x03) << 1); // big seq
err[11] = saa7114_write_block(client, decoder->reg + (0x40 << 1), (0x5f + 1 - 0x40) << 1); // slicer
err[12] = saa7114_write_block(client, decoder->reg + (0x81 << 1), 2 << 1); // ?
err[13] = saa7114_write_block(client, decoder->reg + (0x83 << 1), 5 << 1); // ?
err[14] = saa7114_write_block(client, decoder->reg + (0x90 << 1), 4 << 1); // Task A
err[15] =
saa7114_write_block(client, decoder->reg + (0x94 << 1),
12 << 1);
err[16] =
saa7114_write_block(client, decoder->reg + (0xa0 << 1),
8 << 1);
err[17] =
saa7114_write_block(client, decoder->reg + (0xa8 << 1),
8 << 1);
err[18] =
saa7114_write_block(client, decoder->reg + (0xb0 << 1),
8 << 1);
err[19] = saa7114_write_block(client, decoder->reg + (0xc0 << 1), 4 << 1); // Task B
err[15] =
saa7114_write_block(client, decoder->reg + (0xc4 << 1),
12 << 1);
err[16] =
saa7114_write_block(client, decoder->reg + (0xd0 << 1),
8 << 1);
err[17] =
saa7114_write_block(client, decoder->reg + (0xd8 << 1),
8 << 1);
err[18] =
saa7114_write_block(client, decoder->reg + (0xe0 << 1),
8 << 1);
for (i = 9; i <= 18; i++) {
if (err[i] < 0) {
dprintk(1,
KERN_ERR
"%s_attach: init error %d at stage %d, leaving attach.\n",
I2C_NAME(client), i, err[i]);
return 0;
}
}
for (i = 6; i < 8; i++) {
dprintk(1,
KERN_DEBUG
"%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n",
I2C_NAME(client), i, saa7114_read(client, i),
decoder->reg[REG_ADDR(i)]);
}
for (i = 0x11; i <= 0x13; i++) {
dprintk(1,
KERN_DEBUG
"%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n",
I2C_NAME(client), i, saa7114_read(client, i),
decoder->reg[REG_ADDR(i)]);
}
dprintk(1, KERN_DEBUG "%s_attach: setting video input\n",
I2C_NAME(client));
err[19] =
saa7114_write(client, 0x02, decoder->reg[REG_ADDR(0x02)]);
err[20] =
saa7114_write(client, 0x09, decoder->reg[REG_ADDR(0x09)]);
err[21] =
saa7114_write(client, 0x0e, decoder->reg[REG_ADDR(0x0e)]);
for (i = 19; i <= 21; i++) {
if (err[i] < 0) {
dprintk(1,
KERN_ERR
"%s_attach: init error %d at stage %d, leaving attach.\n",
I2C_NAME(client), i, err[i]);
return 0;
}
}
dprintk(1,
KERN_DEBUG
"%s_attach: performing decoder reset sequence\n",
I2C_NAME(client));
err[22] = saa7114_write(client, 0x88, 0xd8); // sw reset scaler
err[23] = saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
err[24] = saa7114_write(client, 0x80, 0x36); // i-port and scaler backend clock selection, task A&B off
for (i = 22; i <= 24; i++) {
if (err[i] < 0) {
dprintk(1,
KERN_ERR
"%s_attach: init error %d at stage %d, leaving attach.\n",
I2C_NAME(client), i, err[i]);
return 0;
}
}
err[25] = saa7114_write(client, 0x06, init[REG_ADDR(0x06)]);
err[26] = saa7114_write(client, 0x07, init[REG_ADDR(0x07)]);
err[27] = saa7114_write(client, 0x10, init[REG_ADDR(0x10)]);
dprintk(1,
KERN_INFO
"%s_attach: chip version %x, decoder status 0x%02x\n",
I2C_NAME(client), saa7114_read(client, 0x00) >> 4,
saa7114_read(client, 0x1f));
dprintk(1,
KERN_DEBUG
"%s_attach: power save control: 0x%02x, scaler status: 0x%02x\n",
I2C_NAME(client), saa7114_read(client, 0x88),
saa7114_read(client, 0x8f));
for (i = 0x94; i < 0x96; i++) {
dprintk(1,
KERN_DEBUG
"%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n",
I2C_NAME(client), i, saa7114_read(client, i),
decoder->reg[REG_ADDR(i)]);
}
i = i2c_attach_client(client);
if (i) {
kfree(client);
kfree(decoder);
return i;
}
//i = saa7114_write_block(client, init, sizeof(init));
i = 0;
if (i < 0) {
dprintk(1, KERN_ERR "%s_attach error: init status %d\n",
I2C_NAME(client), i);
} else {
dprintk(1,
KERN_INFO
"%s_attach: chip version %x at address 0x%x\n",
I2C_NAME(client), saa7114_read(client, 0x00) >> 4,
client->addr << 1);
}
return 0;
}
static int
saa7114_attach_adapter (struct i2c_adapter *adapter)
{
dprintk(1,
KERN_INFO
"saa7114.c: starting probe for adapter %s (0x%x)\n",
I2C_NAME(adapter), adapter->id);
return i2c_probe(adapter, &addr_data, &saa7114_detect_client);
}
static int
saa7114_detach_client (struct i2c_client *client)
{
struct saa7114 *decoder = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
return err;
}
kfree(decoder);
kfree(client);
return 0;
}
/* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_saa7114 = {
.owner = THIS_MODULE,
.name = "saa7114",
.id = I2C_DRIVERID_SAA7114,
.flags = I2C_DF_NOTIFY,
.attach_adapter = saa7114_attach_adapter,
.detach_client = saa7114_detach_client,
.command = saa7114_command,
};
static int __init
saa7114_init (void)
{
return i2c_add_driver(&i2c_driver_saa7114);
}
static void __exit
saa7114_exit (void)
{
i2c_del_driver(&i2c_driver_saa7114);
}
module_init(saa7114_init);
module_exit(saa7114_exit);
/* /*
saa7185 - Philips SAA7185B video encoder driver version 0.0.3 * saa7185 - Philips SAA7185B video encoder driver version 0.0.3
*
* Copyright (C) 1998 Dave Perks <dperks@ibm.net>
*
* Slight changes for video timing and attachment output by
* Wolfgang Scherr <scherr@net4you.net>
*
* Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
* - moved over to linux>=2.4.x i2c protocol (1/1/2003)
*
* 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.
*/
Copyright (C) 1998 Dave Perks <dperks@ibm.net> #include <linux/version.h>
Slight changes for video timing and attachment output by
Wolfgang Scherr <scherr@net4you.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -36,25 +41,38 @@ ...@@ -36,25 +41,38 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/page.h> #include <asm/page.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <asm/segment.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/videodev.h> #include <linux/videodev.h>
#include <linux/version.h> #include <linux/version.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
MODULE_DESCRIPTION("Philips SAA7185 video encoder driver");
MODULE_AUTHOR("Dave Perks");
MODULE_LICENSE("GPL");
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c-dev.h>
#define I2C_NAME(s) (s)->dev.name
#include <linux/video_encoder.h> #include <linux/video_encoder.h>
#define DEBUG(x) /* Debug driver */ static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
struct saa7185 { struct saa7185 {
struct i2c_client *client;
int addr;
unsigned char reg[128]; unsigned char reg[128];
struct semaphore lock;
int norm; int norm;
int enable; int enable;
...@@ -66,31 +84,76 @@ struct saa7185 { ...@@ -66,31 +84,76 @@ struct saa7185 {
#define I2C_SAA7185 0x88 #define I2C_SAA7185 0x88
#define I2C_DELAY 10
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static unsigned short normal_i2c[] = { 34>>1, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned short probe[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static unsigned short probe_range[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = { static inline int
.normal_i2c = normal_i2c, saa7185_read (struct i2c_client *client)
.normal_i2c_range = normal_i2c_range, {
.probe = probe, return i2c_smbus_read_byte(client);
.probe_range = probe_range, }
.ignore = ignore,
.ignore_range = ignore_range, static int
.force = force saa7185_write (struct i2c_client *client,
}; u8 reg,
u8 value)
{
struct saa7185 *encoder = i2c_get_clientdata(client);
dprintk(1, KERN_DEBUG "SAA7185: %02x set to %02x\n", reg, value);
encoder->reg[reg] = value;
return i2c_smbus_write_byte_data(client, reg, value);
}
static struct i2c_client client_template; static int
saa7185_write_block (struct i2c_client *client,
const u8 *data,
unsigned int len)
{
int ret = -1;
u8 reg;
/* the adv7175 has an autoincrement function, use it if
* the adapter understands raw I2C */
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
/* do raw I2C, not smbus compatible */
struct saa7185 *encoder = i2c_get_clientdata(client);
struct i2c_msg msg;
u8 block_data[32];
msg.addr = client->addr;
msg.flags = client->flags;
while (len >= 2) {
msg.buf = (char *) block_data;
msg.len = 0;
block_data[msg.len++] = reg = data[0];
do {
block_data[msg.len++] =
encoder->reg[reg++] = data[1];
len -= 2;
data += 2;
} while (len >= 2 && data[0] == reg &&
msg.len < 32);
if ((ret =
i2c_transfer(client->adapter, &msg, 1)) < 0)
break;
}
} else {
/* do some slow I2C emulation kind of thing */
while (len >= 2) {
reg = *data++;
if ((ret =
saa7185_write(client, reg, *data++)) < 0)
break;
len -= 2;
}
}
return ret;
}
/* ----------------------------------------------------------------------- */
static const unsigned char init_common[] = { static const unsigned char init_common[] = {
0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1, YUV2C=1, MY2C=1, MUV2C=1 */ 0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1,
* YUV2C=1, MY2C=1, MUV2C=1 */
0x42, 0x6b, /* OVLY0=107 */ 0x42, 0x6b, /* OVLY0=107 */
0x43, 0x00, /* OVLU0=0 white */ 0x43, 0x00, /* OVLU0=0 white */
...@@ -125,7 +188,7 @@ static const unsigned char init_common[] = { ...@@ -125,7 +188,7 @@ static const unsigned char init_common[] = {
0x5f, 0x3a, /* CCRS=0, BLNVB=58 */ 0x5f, 0x3a, /* CCRS=0, BLNVB=58 */
0x60, 0x00, /* NULL */ 0x60, 0x00, /* NULL */
/* 0x61 - 0x66 set according to norm */ /* 0x61 - 0x66 set according to norm */
0x67, 0x00, /* 0 : caption 1st byte odd field */ 0x67, 0x00, /* 0 : caption 1st byte odd field */
0x68, 0x00, /* 0 : caption 2nd byte odd field */ 0x68, 0x00, /* 0 : caption 2nd byte odd field */
...@@ -133,14 +196,16 @@ static const unsigned char init_common[] = { ...@@ -133,14 +196,16 @@ static const unsigned char init_common[] = {
0x6a, 0x00, /* 0 : caption 2nd byte even field */ 0x6a, 0x00, /* 0 : caption 2nd byte even field */
0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */ 0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */
0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0, CBLF=0, ORCV2=0, PRCV2=0 */ 0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0,
* CBLF=0, ORCV2=0, PRCV2=0 */
0x6d, 0x00, /* SRCM1=0, CCEN=0 */ 0x6d, 0x00, /* SRCM1=0, CCEN=0 */
0x6e, 0x0e, /* HTRIG=0x005, approx. centered, at least for PAL */ 0x6e, 0x0e, /* HTRIG=0x005, approx. centered, at
* least for PAL */
0x6f, 0x00, /* HTRIG upper bits */ 0x6f, 0x00, /* HTRIG upper bits */
0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */ 0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */
/* The following should not be needed */ /* The following should not be needed */
0x71, 0x15, /* BMRQ=0x115 */ 0x71, 0x15, /* BMRQ=0x115 */
0x72, 0x90, /* EMRQ=0x690 */ 0x72, 0x90, /* EMRQ=0x690 */
...@@ -152,11 +217,11 @@ static const unsigned char init_common[] = { ...@@ -152,11 +217,11 @@ static const unsigned char init_common[] = {
0x78, 0x90, /* ERCV=0x690 */ 0x78, 0x90, /* ERCV=0x690 */
0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */ 0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */
/* Field length controls */ /* Field length controls */
0x7a, 0x70, /* FLC=0 */ 0x7a, 0x70, /* FLC=0 */
/* The following should not be needed if SBLN = 1 */ /* The following should not be needed if SBLN = 1 */
0x7b, 0x16, /* FAL=22 */ 0x7b, 0x16, /* FAL=22 */
0x7c, 0x35, /* LAL=244 */ 0x7c, 0x35, /* LAL=244 */
...@@ -164,7 +229,8 @@ static const unsigned char init_common[] = { ...@@ -164,7 +229,8 @@ static const unsigned char init_common[] = {
}; };
static const unsigned char init_pal[] = { static const unsigned char init_pal[] = {
0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1, YGS=1, INPI=0, DOWN=0 */ 0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1,
* YGS=1, INPI=0, DOWN=0 */
0x62, 0xc8, /* DECTYP=1, BSTA=72 */ 0x62, 0xc8, /* DECTYP=1, BSTA=72 */
0x63, 0xcb, /* FSC0 */ 0x63, 0xcb, /* FSC0 */
0x64, 0x8a, /* FSC1 */ 0x64, 0x8a, /* FSC1 */
...@@ -173,7 +239,8 @@ static const unsigned char init_pal[] = { ...@@ -173,7 +239,8 @@ static const unsigned char init_pal[] = {
}; };
static const unsigned char init_ntsc[] = { static const unsigned char init_ntsc[] = {
0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1, YGS=1, INPI=0, DOWN=0 */ 0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1,
* YGS=1, INPI=0, DOWN=0 */
0x62, 0xe6, /* DECTYP=1, BSTA=102 */ 0x62, 0xe6, /* DECTYP=1, BSTA=102 */
0x63, 0x1f, /* FSC0 */ 0x63, 0x1f, /* FSC0 */
0x64, 0x7c, /* FSC1 */ 0x64, 0x7c, /* FSC1 */
...@@ -181,167 +248,123 @@ static const unsigned char init_ntsc[] = { ...@@ -181,167 +248,123 @@ static const unsigned char init_ntsc[] = {
0x66, 0x21, /* FSC3 */ 0x66, 0x21, /* FSC3 */
}; };
static int saa7185_attach(struct i2c_adapter *adap, int addr, int kind) static int
saa7185_command (struct i2c_client *client,
unsigned int cmd,
void *arg)
{ {
int i; struct saa7185 *encoder = i2c_get_clientdata(client);
struct saa7185 *encoder;
struct i2c_client *client;
client = kmalloc(sizeof(*client), GFP_KERNEL);
if (client == NULL)
return -ENOMEM;
memset(client, 0, sizeof(*client));
client_template.adapter = adap;
client_template.addr = addr;
memcpy(client, &client_template, sizeof(*client));
encoder = kmalloc(sizeof(*encoder), GFP_KERNEL);
if (encoder == NULL) {
kfree(client);
return -ENOMEM;
}
memset(encoder, 0, sizeof(*encoder)); switch (cmd) {
strlcpy(client->name, "saa7185", DEVICE_NAME_SIZE);
encoder->client = client;
i2c_set_clientdata(client, encoder);
encoder->addr = addr;
encoder->norm = VIDEO_MODE_NTSC;
encoder->enable = 1;
i = i2c_master_send(client, init_common, sizeof(init_common)); case 0:
if (i >= 0) { saa7185_write_block(client, init_common,
i = i2c_master_send(client, init_ntsc, sizeof(init_common));
sizeof(init_ntsc)); switch (encoder->norm) {
}
if (i < 0) {
printk(KERN_ERR "%s_attach: init error %d\n", client->name, i);
} else {
printk(KERN_INFO "%s_attach: chip version %d\n",
client->name, i2c_smbus_read_byte(client) >> 5);
}
init_MUTEX(&encoder->lock);
i2c_attach_client(client);
MOD_INC_USE_COUNT;
return 0;
}
static int saa7185_probe(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, saa7185_attach);
}
static int saa7185_detach(struct i2c_client *client) case VIDEO_MODE_NTSC:
{ saa7185_write_block(client, init_ntsc,
struct saa7185 *encoder = i2c_get_clientdata(client); sizeof(init_ntsc));
i2c_detach_client(client); break;
i2c_smbus_write_byte_data(client, 0x61, (encoder->reg[0x61]) | 0x40); /* SW: output off is active */
//i2c_smbus_write_byte_data(client, 0x3a, (encoder->reg[0x3a]) | 0x80); /* SW: color bar */
kfree(encoder);
kfree(client);
MOD_DEC_USE_COUNT;
return 0;
}
static int saa7185_command(struct i2c_client *client, unsigned int cmd, case VIDEO_MODE_PAL:
void *arg) saa7185_write_block(client, init_pal,
{ sizeof(init_pal));
struct saa7185 *encoder = i2c_get_clientdata(client); break;
}
switch (cmd) { break;
case ENCODER_GET_CAPABILITIES: case ENCODER_GET_CAPABILITIES:
{ {
struct video_encoder_capability *cap = arg; struct video_encoder_capability *cap = arg;
cap->flags cap->flags =
= VIDEO_ENCODER_PAL VIDEO_ENCODER_PAL | VIDEO_ENCODER_NTSC |
| VIDEO_ENCODER_NTSC VIDEO_ENCODER_SECAM | VIDEO_ENCODER_CCIR;
| VIDEO_ENCODER_SECAM | VIDEO_ENCODER_CCIR; cap->inputs = 1;
cap->inputs = 1; cap->outputs = 1;
cap->outputs = 1; }
}
break; break;
case ENCODER_SET_NORM: case ENCODER_SET_NORM:
{ {
int *iarg = arg; int *iarg = arg;
switch (*iarg) { //saa7185_write_block(client, init_common, sizeof(init_common));
case VIDEO_MODE_NTSC: switch (*iarg) {
i2c_master_send(client, init_ntsc,
sizeof(init_ntsc));
break;
case VIDEO_MODE_PAL: case VIDEO_MODE_NTSC:
i2c_master_send(client, init_pal, saa7185_write_block(client, init_ntsc,
sizeof(init_pal)); sizeof(init_ntsc));
break; break;
case VIDEO_MODE_SECAM: case VIDEO_MODE_PAL:
default: saa7185_write_block(client, init_pal,
return -EINVAL; sizeof(init_pal));
break;
case VIDEO_MODE_SECAM:
default:
return -EINVAL;
}
encoder->norm = *iarg;
} }
encoder->norm = *iarg;
}
break; break;
case ENCODER_SET_INPUT: case ENCODER_SET_INPUT:
{ {
int *iarg = arg; int *iarg = arg;
/* RJ: *iarg = 0: input is from SA7111 /* RJ: *iarg = 0: input is from SA7111
*iarg = 1: input is from ZR36060 */ *iarg = 1: input is from ZR36060 */
switch (*iarg) { switch (*iarg) {
case 0: case 0:
/* Switch RTCE to 1 */ /* Switch RTCE to 1 */
i2c_smbus_write_byte_data(client, 0x61, saa7185_write(client, 0x61,
(encoder-> (encoder->reg[0x61] & 0xf7) | 0x08);
reg[0x61] & 0xf7) | 0x08); saa7185_write(client, 0x6e, 0x01);
i2c_smbus_write_byte_data(client, 0x6e, 0x01); break;
break;
case 1: case 1:
/* Switch RTCE to 0 */ /* Switch RTCE to 0 */
i2c_smbus_write_byte_data(client, 0x61, saa7185_write(client, 0x61,
(encoder-> (encoder->reg[0x61] & 0xf7) | 0x00);
reg[0x61] & 0xf7) | 0x00); /* SW: a slight sync problem... */
/* SW: a slight sync problem... */ saa7185_write(client, 0x6e, 0x00);
i2c_smbus_write_byte_data(client, 0x6e, 0x00); break;
break;
default: default:
return -EINVAL; return -EINVAL;
}
} }
}
break; break;
case ENCODER_SET_OUTPUT: case ENCODER_SET_OUTPUT:
{ {
int *iarg = arg; int *iarg = arg;
/* not much choice of outputs */ /* not much choice of outputs */
if (*iarg != 0) { if (*iarg != 0) {
return -EINVAL; return -EINVAL;
}
} }
}
break; break;
case ENCODER_ENABLE_OUTPUT: case ENCODER_ENABLE_OUTPUT:
{ {
int *iarg = arg; int *iarg = arg;
encoder->enable = !!*iarg; encoder->enable = !!*iarg;
i2c_smbus_write_byte_data(client, 0x61, saa7185_write(client, 0x61,
(encoder-> (encoder->reg[0x61] & 0xbf) |
reg[0x61] & 0xbf) | (encoder-> (encoder->enable ? 0x00 : 0x40));
enable ? 0x00 : }
0x40));
}
break; break;
default: default:
...@@ -353,32 +376,152 @@ static int saa7185_command(struct i2c_client *client, unsigned int cmd, ...@@ -353,32 +376,152 @@ static int saa7185_command(struct i2c_client *client, unsigned int cmd,
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_saa7185 = { /*
.owner = THIS_MODULE, * Generic i2c probe
.name = "saa7185", /* name */ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
.id = I2C_DRIVERID_SAA7185B, /* ID */ */
.flags = I2C_DF_NOTIFY, static unsigned short normal_i2c[] = { I2C_SAA7185 >> 1, I2C_CLIENT_END };
.attach_adapter = saa7185_probe, static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
.detach_client = saa7185_detach,
.command = saa7185_command static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_i2c_range = normal_i2c_range,
.probe = probe,
.probe_range = probe_range,
.ignore = ignore,
.ignore_range = ignore_range,
.force = force
}; };
static struct i2c_client client_template = { static int saa7185_i2c_id = 0;
.id = -1, static struct i2c_driver i2c_driver_saa7185;
.driver = &i2c_driver_saa7185,
.name = "saa7185_client", static int
saa7185_detect_client (struct i2c_adapter *adapter,
int address,
int kind)
{
int i;
struct i2c_client *client;
struct saa7185 *encoder;
dprintk(1,
KERN_INFO
"saa7185.c: detecting saa7185 client on address 0x%x\n",
address << 1);
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == 0)
return -ENOMEM;
memset(client, 0, sizeof(struct i2c_client));
client->addr = address;
client->adapter = adapter;
client->driver = &i2c_driver_saa7185;
client->flags = I2C_CLIENT_ALLOW_USE;
client->id = saa7185_i2c_id++;
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"saa7185[%d]", client->id);
encoder = kmalloc(sizeof(struct saa7185), GFP_KERNEL);
if (encoder == NULL) {
return -ENOMEM;
}
memset(encoder, 0, sizeof(struct saa7185));
encoder->norm = VIDEO_MODE_NTSC;
encoder->enable = 1;
i2c_set_clientdata(client, encoder);
i = i2c_attach_client(client);
if (i) {
kfree(client);
kfree(encoder);
return i;
}
i = saa7185_write_block(client, init_common, sizeof(init_common));
if (i >= 0) {
i = saa7185_write_block(client, init_ntsc,
sizeof(init_ntsc));
}
if (i < 0) {
dprintk(1, KERN_ERR "%s_attach: init error %d\n",
I2C_NAME(client), i);
} else {
dprintk(1,
KERN_INFO
"%s_attach: chip version %d at address 0x%x\n",
I2C_NAME(client), saa7185_read(client) >> 5,
client->addr << 1);
}
return 0;
}
static int
saa7185_attach_adapter (struct i2c_adapter *adapter)
{
dprintk(1,
KERN_INFO
"saa7185.c: starting probe for adapter %s (0x%x)\n",
I2C_NAME(adapter), adapter->id);
return i2c_probe(adapter, &addr_data, &saa7185_detect_client);
}
static int
saa7185_detach_client (struct i2c_client *client)
{
struct saa7185 *encoder = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
return err;
}
saa7185_write(client, 0x61, (encoder->reg[0x61]) | 0x40); /* SW: output off is active */
//saa7185_write(client, 0x3a, (encoder->reg[0x3a]) | 0x80); /* SW: color bar */
kfree(encoder);
kfree(client);
return 0;
}
/* ----------------------------------------------------------------------- */
static struct i2c_driver i2c_driver_saa7185 = {
.owner = THIS_MODULE,
.name = "saa7185", /* name */
.id = I2C_DRIVERID_SAA7185B,
.flags = I2C_DF_NOTIFY,
.attach_adapter = saa7185_attach_adapter,
.detach_client = saa7185_detach_client,
.command = saa7185_command,
}; };
static int saa7185_init(void) static int __init
saa7185_init (void)
{ {
return i2c_add_driver(&i2c_driver_saa7185); return i2c_add_driver(&i2c_driver_saa7185);
} }
static void saa7185_exit(void) static void __exit
saa7185_exit (void)
{ {
i2c_del_driver(&i2c_driver_saa7185); i2c_del_driver(&i2c_driver_saa7185);
} }
module_init(saa7185_init); module_init(saa7185_init);
module_exit(saa7185_exit); module_exit(saa7185_exit);
MODULE_LICENSE("GPL");
/*
* VIDEO MOTION CODECs internal API for video devices
*
* Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's
* bound to a master device.
*
* (c) 2002 Wolfgang Scherr <scherr@net4you.at>
*
* $Id: videocodec.c,v 1.1.2.8 2003/03/29 07:16:04 rbultje Exp $
*
* ------------------------------------------------------------------------
*
* 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.
*
* ------------------------------------------------------------------------
*/
#define VIDEOCODEC_VERSION "v0.2"
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
// kernel config is here (procfs flag)
#include <linux/config.h>
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#endif
#include <linux/version.h>
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif
#include "videocodec.h"
static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-4)");
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
struct attached_list {
struct videocodec *codec;
struct attached_list *next;
};
struct codec_list {
const struct videocodec *codec;
int attached;
struct attached_list *list;
struct codec_list *next;
};
static struct codec_list *codeclist_top = NULL;
/* ================================================= */
/* function prototypes of the master/slave interface */
/* ================================================= */
struct videocodec *
videocodec_attach (struct videocodec_master *master)
{
struct codec_list *h = codeclist_top;
struct attached_list *a, *ptr;
struct videocodec *codec;
int res;
if (!master) {
dprintk(1, KERN_ERR "videocodec_attach: no data\n");
return NULL;
}
dprintk(2,
"videocodec_attach: '%s', type: %x, flags %lx, magic %lx\n",
master->name, master->type, master->flags, master->magic);
if (!h) {
dprintk(1,
KERN_ERR
"videocodec_attach: no device available\n");
return NULL;
}
while (h) {
// attach only if the slave has at least the flags
// expected by the master
if ((master->flags & h->codec->flags) == master->flags) {
dprintk(4, "videocodec_attach: try '%s'\n",
h->codec->name);
codec =
kmalloc(sizeof(struct videocodec), GFP_KERNEL);
if (!codec) {
dprintk(1,
KERN_ERR
"videocodec_attach: no mem\n");
return NULL;
}
memcpy(codec, h->codec, sizeof(struct videocodec));
snprintf(codec->name, sizeof(codec->name),
"%s[%d]", codec->name, h->attached);
codec->master_data = master;
res = codec->setup(codec);
if (res == 0) {
dprintk(3, "videocodec_attach '%s'\n",
codec->name);
ptr = (struct attached_list *)
kmalloc(sizeof(struct attached_list),
GFP_KERNEL);
if (!ptr) {
dprintk(1,
KERN_ERR
"videocodec_attach: no memory\n");
kfree(codec);
return NULL;
}
memset(ptr, 0,
sizeof(struct attached_list));
ptr->codec = codec;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_INC_USE_COUNT;
#else
if (!try_module_get(THIS_MODULE)) {
dprintk(1,
KERN_ERR
"videocodec: failed to increment usecount\n");
kfree(codec);
kfree(ptr);
return NULL;
}
#endif
a = h->list;
if (!a) {
h->list = ptr;
dprintk(4,
"videocodec: first element\n");
} else {
while (a->next)
a = a->next; // find end
a->next = ptr;
dprintk(4,
"videocodec: in after '%s'\n",
h->codec->name);
}
h->attached += 1;
return codec;
} else {
kfree(codec);
}
}
h = h->next;
}
dprintk(1, KERN_ERR "videocodec_attach: no codec found!\n");
return NULL;
}
int
videocodec_detach (struct videocodec *codec)
{
struct codec_list *h = codeclist_top;
struct attached_list *a, *prev;
int res;
if (!codec) {
dprintk(1, KERN_ERR "videocodec_detach: no data\n");
return -EINVAL;
}
dprintk(2,
"videocodec_detach: '%s', type: %x, flags %lx, magic %lx\n",
codec->name, codec->type, codec->flags, codec->magic);
if (!h) {
dprintk(1,
KERN_ERR "videocodec_detach: no device left...\n");
return -ENXIO;
}
while (h) {
a = h->list;
prev = NULL;
while (a) {
if (codec == a->codec) {
res = a->codec->unset(a->codec);
if (res >= 0) {
dprintk(3,
"videocodec_detach: '%s'\n",
a->codec->name);
a->codec->master_data = NULL;
} else {
dprintk(1,
KERN_ERR
"videocodec_detach: '%s'\n",
a->codec->name);
a->codec->master_data = NULL;
}
if (prev == NULL) {
h->list = a->next;
dprintk(4,
"videocodec: delete first\n");
} else {
prev->next = a->next;
dprintk(4,
"videocodec: delete middle\n");
}
kfree(a->codec);
kfree(a);
h->attached -= 1;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_DEC_USE_COUNT;
#else
module_put(THIS_MODULE);
#endif
return 0;
}
prev = a;
a = a->next;
}
h = h->next;
}
dprintk(1, KERN_ERR "videocodec_detach: given codec not found!\n");
return -EINVAL;
}
int
videocodec_register (const struct videocodec *codec)
{
struct codec_list *ptr, *h = codeclist_top;
if (!codec) {
dprintk(1, KERN_ERR "videocodec_register: no data!\n");
return -EINVAL;
}
dprintk(2,
"videocodec: register '%s', type: %x, flags %lx, magic %lx\n",
codec->name, codec->type, codec->flags, codec->magic);
ptr =
(struct codec_list *) kmalloc(sizeof(struct codec_list),
GFP_KERNEL);
if (!ptr) {
dprintk(1, KERN_ERR "videocodec_register: no memory\n");
return -ENOMEM;
}
memset(ptr, 0, sizeof(struct codec_list));
ptr->codec = codec;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_INC_USE_COUNT;
#else
if (!try_module_get(THIS_MODULE)) {
dprintk(1,
KERN_ERR
"videocodec: failed to increment module count\n");
kfree(ptr);
return -ENODEV;
}
#endif
if (!h) {
codeclist_top = ptr;
dprintk(4, "videocodec: hooked in as first element\n");
} else {
while (h->next)
h = h->next; // find the end
h->next = ptr;
dprintk(4, "videocodec: hooked in after '%s'\n",
h->codec->name);
}
return 0;
}
int
videocodec_unregister (const struct videocodec *codec)
{
struct codec_list *prev = NULL, *h = codeclist_top;
if (!codec) {
dprintk(1, KERN_ERR "videocodec_unregister: no data!\n");
return -EINVAL;
}
dprintk(2,
"videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n",
codec->name, codec->type, codec->flags, codec->magic);
if (!h) {
dprintk(1,
KERN_ERR
"videocodec_unregister: no device left...\n");
return -ENXIO;
}
while (h) {
if (codec == h->codec) {
if (h->attached) {
dprintk(1,
KERN_ERR
"videocodec: '%s' is used\n",
h->codec->name);
return -EBUSY;
}
dprintk(3, "videocodec: unregister '%s' is ok.\n",
h->codec->name);
if (prev == NULL) {
codeclist_top = h->next;
dprintk(4,
"videocodec: delete first element\n");
} else {
prev->next = h->next;
dprintk(4,
"videocodec: delete middle element\n");
}
kfree(h);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_DEC_USE_COUNT;
#else
module_put(THIS_MODULE);
#endif
return 0;
}
prev = h;
h = h->next;
}
dprintk(1,
KERN_ERR
"videocodec_unregister: given codec not found!\n");
return -EINVAL;
}
#ifdef CONFIG_PROC_FS
/* ============ */
/* procfs stuff */
/* ============ */
static char *videocodec_buf = NULL;
static int videocodec_bufsize = 0;
static int
videocodec_build_table (void)
{
struct codec_list *h = codeclist_top;
struct attached_list *a;
int i = 0, size;
// sum up amount of slaves plus their attached masters
while (h) {
i += h->attached + 1;
h = h->next;
}
#define LINESIZE 100
size = LINESIZE * (i + 1);
dprintk(3, "videocodec_build table: %d entries, %d bytes\n", i,
size);
if (videocodec_buf)
kfree(videocodec_buf);
videocodec_buf = (char *) kmalloc(size, GFP_KERNEL);
i = 0;
i += snprintf(videocodec_buf + i, size - 1,
"<S>lave or attached <M>aster name type flags magic ");
i += snprintf(videocodec_buf + i, size - 1, "(connected as)\n");
h = codeclist_top;
while (h) {
if (i > (size - LINESIZE))
break; // security check
i += snprintf(videocodec_buf + i, size,
"S %32s %04x %08lx %08lx (TEMPLATE)\n",
h->codec->name, h->codec->type,
h->codec->flags, h->codec->magic);
a = h->list;
while (a) {
if (i > (size - LINESIZE))
break; // security check
i += snprintf(videocodec_buf + i, size,
"M %32s %04x %08lx %08lx (%s)\n",
a->codec->master_data->name,
a->codec->master_data->type,
a->codec->master_data->flags,
a->codec->master_data->magic,
a->codec->name);
a = a->next;
}
h = h->next;
}
return i;
}
//The definition:
//typedef int (read_proc_t)(char *page, char **start, off_t off,
// int count, int *eof, void *data);
static int
videocodec_info (char *buffer,
char **buffer_location,
off_t offset,
int buffer_length,
int *eof,
void *data)
{
int size;
dprintk(3, "videocodec_info: offset: %ld, len %d / size %d\n",
offset, buffer_length, videocodec_bufsize);
if (offset == 0) {
videocodec_bufsize = videocodec_build_table();
}
if ((offset < 0) || (offset >= videocodec_bufsize)) {
dprintk(4,
"videocodec_info: call delivers no result, return 0\n");
*eof = 1;
return 0;
}
if (buffer_length < (videocodec_bufsize - offset)) {
dprintk(4, "videocodec_info: %ld needed, %d got\n",
videocodec_bufsize - offset, buffer_length);
size = buffer_length;
} else {
dprintk(4, "videocodec_info: last reading of %ld bytes\n",
videocodec_bufsize - offset);
size = videocodec_bufsize - offset;
*eof = 1;
}
memcpy(buffer, videocodec_buf + offset, size);
/* doesn't work... */
/* copy_to_user(buffer, videocodec_buf+offset, size); */
/* *buffer_location = videocodec_buf+offset; */
return size;
}
#endif
/* ===================== */
/* hook in driver module */
/* ===================== */
static int __init
videocodec_init (void)
{
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *videocodec_proc_entry;
#endif
printk(KERN_INFO "Linux video codec intermediate layer: %s\n",
VIDEOCODEC_VERSION);
#ifdef CONFIG_PROC_FS
videocodec_buf = NULL;
videocodec_bufsize = 0;
videocodec_proc_entry = create_proc_entry("videocodecs", 0, 0);
if (videocodec_proc_entry) {
videocodec_proc_entry->read_proc = videocodec_info;
videocodec_proc_entry->write_proc = NULL;
videocodec_proc_entry->data = NULL;
videocodec_proc_entry->owner = THIS_MODULE;
} else {
dprintk(1, KERN_ERR "videocodec: can't init procfs.\n");
}
#endif
return 0;
}
static void __exit
videocodec_exit (void)
{
#ifdef CONFIG_PROC_FS
remove_proc_entry("videocodecs", 0);
if (videocodec_buf)
kfree(videocodec_buf);
#endif
}
EXPORT_SYMBOL(videocodec_attach);
EXPORT_SYMBOL(videocodec_detach);
EXPORT_SYMBOL(videocodec_register);
EXPORT_SYMBOL(videocodec_unregister);
module_init(videocodec_init);
module_exit(videocodec_exit);
MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
MODULE_DESCRIPTION("Intermediate API module for video codecs "
VIDEOCODEC_VERSION);
MODULE_LICENSE("GPL");
/*
* VIDEO MOTION CODECs internal API for video devices
*
* Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's
* bound to a master device.
*
* (c) 2002 Wolfgang Scherr <scherr@net4you.at>
*
* $Id: videocodec.h,v 1.1.2.4 2003/01/14 21:15:03 rbultje Exp $
*
* ------------------------------------------------------------------------
*
* 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.
*
* ------------------------------------------------------------------------
*/
/* =================== */
/* general description */
/* =================== */
/* Should ease the (re-)usage of drivers supporting cards with (different)
video codecs. The codecs register to this module their functionality,
and the processors (masters) can attach to them if they fit.
The codecs are typically have a "strong" binding to their master - so I
don't think it makes sense to have a full blown interfacing as with e.g.
i2c. If you have an other opinion, let's discuss & implement it :-)))
Usage:
The slave has just to setup the videocodec structure and use two functions:
videocodec_register(codecdata);
videocodec_unregister(codecdata);
The best is just calling them at module (de-)initialisation.
The master sets up the structure videocodec_master and calls:
codecdata=videocodec_attach(master_codecdata);
videocodec_detach(codecdata);
The slave is called during attach/detach via functions setup previously
during register. At that time, the master_data pointer is set up
and the slave can access any io registers of the master device (in the case
the slave is bound to it). Otherwise it doesn't need this functions and
therfor they may not be initialized.
The other fuctions are just for convenience, as they are for shure used by
most/all of the codecs. The last ones may be ommited, too.
See the structure declaration below for more information and which data has
to be set up for the master and the slave.
----------------------------------------------------------------------------
The master should have "knowledge" of the slave and vice versa. So the data
structures sent to/from slave via set_data/get_data set_image/get_image are
device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!)
----------------------------------------------------------------------------
*/
/* ========================================== */
/* description of the videocodec_io structure */
/* ========================================== */
/*
==== master setup ====
name -> name of the device structure for reference and debugging
master_data -> data ref. for the master (e.g. the zr36055,57,67)
readreg -> ref. to read-fn from register (setup by master, used by slave)
writereg -> ref. to write-fn to register (setup by master, used by slave)
this two functions do the lowlevel I/O job
==== slave functionality setup ====
slave_data -> data ref. for the slave (e.g. the zr36050,60)
check -> fn-ref. checks availability of an device, returns -EIO on failure or
the type on success
this makes espcecially sense if a driver module supports more than
one codec which may be quite similar to access, nevertheless it
is good for a first functionality check
-- main functions you always need for compression/decompression --
set_mode -> this fn-ref. resets the entire codec, and sets up the mode
with the last defined norm/size (or device default if not
available) - it returns 0 if the mode is possible
set_size -> this fn-ref. sets the norm and image size for
compression/decompression (returns 0 on success)
the norm param is defined in videodev.h (VIDEO_MODE_*)
additional setup may be available, too - but the codec should work with
some default values even without this
set_data -> sets device-specific data (tables, quality etc.)
get_data -> query device-specific data (tables, quality etc.)
if the device delivers interrupts, they may be setup/handled here
setup_interrupt -> codec irq setup (not needed for 36050/60)
handle_interrupt -> codec irq handling (not needed for 36050/60)
if the device delivers pictures, they may be handled here
put_image -> puts image data to the codec (not needed for 36050/60)
get_image -> gets image data from the codec (not needed for 36050/60)
the calls include frame numbers and flags (even/odd/...)
if needed and a flag which allows blocking until its ready
*/
/* ============== */
/* user interface */
/* ============== */
/*
Currently there is only a information display planned, as the layer
is not visible for the user space at all.
Information is available via procfs. The current entry is "/proc/videocodecs"
but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--.
A example for such an output is:
<S>lave or attached <M>aster name type flags magic (connected as)
S zr36050 0002 0000d001 00000000 (TEMPLATE)
M zr36055[0] 0001 0000c001 00000000 (zr36050[0])
M zr36055[1] 0001 0000c001 00000000 (zr36050[1])
*/
/* =============================================== */
/* special defines for the videocodec_io structure */
/* =============================================== */
#ifndef __LINUX_VIDEOCODEC_H
#define __LINUX_VIDEOCODEC_H
#include <linux/videodev.h>
//should be in videodev.h ??? (VID_DO_....)
#define CODEC_DO_COMPRESSION 0
#define CODEC_DO_EXPANSION 1
/* this are the current codec flags I think they are needed */
/* -> type value in structure */
#define CODEC_FLAG_JPEG 0x00000001L // JPEG codec
#define CODEC_FLAG_MPEG 0x00000002L // MPEG1/2/4 codec
#define CODEC_FLAG_DIVX 0x00000004L // DIVX codec
#define CODEC_FLAG_WAVELET 0x00000008L // WAVELET codec
// room for other types
#define CODEC_FLAG_MAGIC 0x00000800L // magic key must match
#define CODEC_FLAG_HARDWARE 0x00001000L // is a hardware codec
#define CODEC_FLAG_VFE 0x00002000L // has direct video frontend
#define CODEC_FLAG_ENCODER 0x00004000L // compression capability
#define CODEC_FLAG_DECODER 0x00008000L // decompression capability
#define CODEC_FLAG_NEEDIRQ 0x00010000L // needs irq handling
#define CODEC_FLAG_RDWRPIC 0x00020000L // handles picture I/O
/* a list of modes, some are just examples (is there any HW?) */
#define CODEC_MODE_BJPG 0x0001 // Baseline JPEG
#define CODEC_MODE_LJPG 0x0002 // Lossless JPEG
#define CODEC_MODE_MPEG1 0x0003 // MPEG 1
#define CODEC_MODE_MPEG2 0x0004 // MPEG 2
#define CODEC_MODE_MPEG4 0x0005 // MPEG 4
#define CODEC_MODE_MSDIVX 0x0006 // MS DivX
#define CODEC_MODE_ODIVX 0x0007 // Open DivX
#define CODEC_MODE_WAVELET 0x0008 // Wavelet
/* this are the current codec types I want to implement */
/* -> type value in structure */
#define CODEC_TYPE_NONE 0
#define CODEC_TYPE_L64702 1
#define CODEC_TYPE_ZR36050 2
#define CODEC_TYPE_ZR36016 3
#define CODEC_TYPE_ZR36060 4
/* the type of data may be enhanced by future implementations (data-fn.'s) */
/* -> used in command */
#define CODEC_G_STATUS 0x0000 /* codec status (query only) */
#define CODEC_S_CODEC_MODE 0x0001 /* codec mode (baseline JPEG, MPEG1,... */
#define CODEC_G_CODEC_MODE 0x8001
#define CODEC_S_VFE 0x0002 /* additional video frontend setup */
#define CODEC_G_VFE 0x8002
#define CODEC_S_MMAP 0x0003 /* MMAP setup (if available) */
#define CODEC_S_JPEG_TDS_BYTE 0x0010 /* target data size in bytes */
#define CODEC_G_JPEG_TDS_BYTE 0x8010
#define CODEC_S_JPEG_SCALE 0x0011 /* scaling factor for quant. tables */
#define CODEC_G_JPEG_SCALE 0x8011
#define CODEC_S_JPEG_HDT_DATA 0x0018 /* huffman-tables */
#define CODEC_G_JPEG_HDT_DATA 0x8018
#define CODEC_S_JPEG_QDT_DATA 0x0019 /* quantizing-tables */
#define CODEC_G_JPEG_QDT_DATA 0x8019
#define CODEC_S_JPEG_APP_DATA 0x001A /* APP marker */
#define CODEC_G_JPEG_APP_DATA 0x801A
#define CODEC_S_JPEG_COM_DATA 0x001B /* COM marker */
#define CODEC_G_JPEG_COM_DATA 0x801B
#define CODEC_S_PRIVATE 0x1000 /* "private" commands start here */
#define CODEC_G_PRIVATE 0x9000
#define CODEC_G_FLAG 0x8000 /* this is how 'get' is detected */
/* types of transfer, directly user space or a kernel buffer (image-fn.'s) */
/* -> used in get_image, put_image */
#define CODEC_TRANSFER_KERNEL 0 /* use "memcopy" */
#define CODEC_TRANSFER_USER 1 /* use "to/from_user" */
/* ========================= */
/* the structures itself ... */
/* ========================= */
struct vfe_polarity {
int vsync_pol:1;
int hsync_pol:1;
int field_pol:1;
int blank_pol:1;
int subimg_pol:1;
int poe_pol:1;
int pvalid_pol:1;
int vclk_pol:1;
};
struct vfe_settings {
__u32 x, y; /* Offsets into image */
__u32 width, height; /* Area to capture */
__u16 decimation; /* Decimation divider */
__u16 flags; /* Flags for capture */
/* flags are the same as in struct video_capture - see videodev.h:
#define VIDEO_CAPTURE_ODD 0
#define VIDEO_CAPTURE_EVEN 1
*/
__u16 quality; /* quality of the video */
};
struct tvnorm {
u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
};
struct videocodec {
/* -- filled in by slave device during register -- */
char name[32];
unsigned long magic; /* may be used for client<->master attaching */
unsigned long flags; /* functionality flags */
unsigned int type; /* codec type */
/* -- these is filled in later during master device attach -- */
struct videocodec_master *master_data;
/* -- these are filled in by the slave device during register -- */
void *data; /* private slave data */
/* attach/detach client functions (indirect call) */
int (*setup) (struct videocodec * codec);
int (*unset) (struct videocodec * codec);
/* main functions, every client needs them for sure! */
// set compression or decompression (or freeze, stop, standby, etc)
int (*set_mode) (struct videocodec * codec,
int mode);
// setup picture size and norm (for the codec's video frontend)
int (*set_video) (struct videocodec * codec,
struct tvnorm * norm,
struct vfe_settings * cap,
struct vfe_polarity * pol);
// other control commands, also mmap setup etc.
int (*control) (struct videocodec * codec,
int type,
int size,
void *data);
/* additional setup/query/processing (may be NULL pointer) */
// interrupt setup / handling (for irq's delivered by master)
int (*setup_interrupt) (struct videocodec * codec,
long mode);
int (*handle_interrupt) (struct videocodec * codec,
int source,
long flag);
// picture interface (if any)
long (*put_image) (struct videocodec * codec,
int tr_type,
int block,
long *fr_num,
long *flag,
long size,
void *buf);
long (*get_image) (struct videocodec * codec,
int tr_type,
int block,
long *fr_num,
long *flag,
long size,
void *buf);
};
struct videocodec_master {
/* -- filled in by master device for registration -- */
char name[32];
unsigned long magic; /* may be used for client<->master attaching */
unsigned long flags; /* functionality flags */
unsigned int type; /* master type */
void *data; /* private master data */
__u32(*readreg) (struct videocodec * codec,
__u16 reg);
void (*writereg) (struct videocodec * codec,
__u16 reg,
__u32 value);
};
/* ================================================= */
/* function prototypes of the master/slave interface */
/* ================================================= */
/* attach and detach commands for the master */
// * master structure needs to be kmalloc'ed before calling attach
// and free'd after calling detach
// * returns pointer on success, NULL on failure
extern struct videocodec *videocodec_attach(struct videocodec_master *);
// * 0 on success, <0 (errno) on failure
extern int videocodec_detach(struct videocodec *);
/* register and unregister commands for the slaves */
// * 0 on success, <0 (errno) on failure
extern int videocodec_register(const struct videocodec *);
// * 0 on success, <0 (errno) on failure
extern int videocodec_unregister(const struct videocodec *);
/* the other calls are directly done via the videocodec structure! */
#endif /*ifndef __LINUX_VIDEOCODEC_H */
/*
* vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1
*
* Copyright (C) 2001 Laurent Pinchart <lpinchart@freegates.be>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/byteorder/swab.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#define I2C_NAME(x) (x)->dev.name
#include <linux/videodev.h>
#include <linux/video_decoder.h>
#define I2C_VPX3220 0x86
#define VPX3220_DEBUG KERN_DEBUG "vpx3220: "
static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
#define VPX_TIMEOUT_COUNT 10
/* ----------------------------------------------------------------------- */
struct vpx3220 {
unsigned char reg[255];
int norm;
int input;
int enable;
int bright;
int contrast;
int hue;
int sat;
};
static char *inputs[] = { "internal", "composite", "svideo" };
/* ----------------------------------------------------------------------- */
static inline int
vpx3220_write (struct i2c_client *client,
u8 reg,
u8 value)
{
struct vpx3220 *decoder = i2c_get_clientdata(client);
decoder->reg[reg] = value;
return i2c_smbus_write_byte_data(client, reg, value);
}
static inline int
vpx3220_read (struct i2c_client *client,
u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static int
vpx3220_fp_status (struct i2c_client *client)
{
unsigned char status;
unsigned int i;
for (i = 0; i < VPX_TIMEOUT_COUNT; i++) {
status = vpx3220_read(client, 0x29);
if (!(status & 4))
return 0;
udelay(10);
if (need_resched())
cond_resched();
}
return -1;
}
static int
vpx3220_fp_write (struct i2c_client *client,
u8 fpaddr,
u16 data)
{
/* Write the 16-bit address to the FPWR register */
if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) {
dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__);
return -1;
}
if (vpx3220_fp_status(client) < 0)
return -1;
/* Write the 16-bit data to the FPDAT register */
if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) {
dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__);
return -1;
}
return 0;
}
static u16
vpx3220_fp_read (struct i2c_client *client,
u16 fpaddr)
{
s16 data;
/* Write the 16-bit address to the FPRD register */
if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) {
dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__);
return -1;
}
if (vpx3220_fp_status(client) < 0)
return -1;
/* Read the 16-bit data from the FPDAT register */
data = i2c_smbus_read_word_data(client, 0x28);
if (data == -1) {
dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__);
return -1;
}
return swab16(data);
}
static int
vpx3220_write_block (struct i2c_client *client,
const u8 *data,
unsigned int len)
{
u8 reg;
int ret = -1;
while (len >= 2) {
reg = *data++;
if ((ret =
vpx3220_write(client, reg, *data++)) < 0)
break;
len -= 2;
}
return ret;
}
static int
vpx3220_write_fp_block (struct i2c_client *client,
const u16 *data,
unsigned int len)
{
u8 reg;
int ret = 0;
while (len > 1) {
reg = *data++;
ret |= vpx3220_fp_write(client, reg, *data++);
len -= 2;
}
return ret;
}
/* ---------------------------------------------------------------------- */
static const unsigned short init_ntsc[] = {
0x1c, 0x00, /* NTSC tint angle */
0x88, 17, /* Window 1 vertical */
0x89, 240, /* Vertical lines in */
0x8a, 240, /* Vertical lines out */
0x8b, 000, /* Horizontal begin */
0x8c, 640, /* Horizontal length */
0x8d, 640, /* Number of pixels */
0x8f, 0xc00, /* Disable window 2 */
0xf0, 0x173, /* 13.5 MHz transport, Forced
* mode, latch windows */
0xf2, 0x13, /* NTSC M, composite input */
0xe7, 0x1e1, /* Enable vertical standard
* locking @ 240 lines */
};
static const unsigned short init_pal[] = {
0x88, 23, /* Window 1 vertical begin */
0x89, 288 + 16, /* Vertical lines in (16 lines
* skipped by the VFE) */
0x8a, 288 + 16, /* Vertical lines out (16 lines
* skipped by the VFE) */
0x8b, 16, /* Horizontal begin */
0x8c, 768, /* Horizontal length */
0x8d, 784, /* Number of pixels
* Must be >= Horizontal begin + Horizontal length */
0x8f, 0xc00, /* Disable window 2 */
0xf0, 0x177, /* 13.5 MHz transport, Forced
* mode, latch windows */
0xf2, 0x3d1, /* PAL B,G,H,I, composite input */
0xe7, 0x261, /* PAL/SECAM set to 288 + 16 lines
* change to 0x241 for 288 lines */
};
static const unsigned short init_secam[] = {
0x88, 23 - 16, /* Window 1 vertical begin */
0x89, 288 + 16, /* Vertical lines in (16 lines
* skipped by the VFE) */
0x8a, 288 + 16, /* Vertical lines out (16 lines
* skipped by the VFE) */
0x8b, 16, /* Horizontal begin */
0x8c, 768, /* Horizontal length */
0x8d, 784, /* Number of pixels
* Must be >= Horizontal begin + Horizontal length */
0x8f, 0xc00, /* Disable window 2 */
0xf0, 0x177, /* 13.5 MHz transport, Forced
* mode, latch windows */
0xf2, 0x3d5, /* SECAM, composite input */
0xe7, 0x261, /* PAL/SECAM set to 288 + 16 lines
* change to 0x241 for 288 lines */
};
static const unsigned char init_common[] = {
0xf2, 0x00, /* Disable all outputs */
0x33, 0x0d, /* Luma : VIN2, Chroma : CIN
* (clamp off) */
0xd8, 0xa8, /* HREF/VREF active high, VREF
* pulse = 2, Odd/Even flag */
0x20, 0x03, /* IF compensation 0dB/oct */
0xe0, 0xff, /* Open up all comparators */
0xe1, 0x00,
0xe2, 0x7f,
0xe3, 0x80,
0xe4, 0x7f,
0xe5, 0x80,
0xe6, 0x00, /* Brightness set to 0 */
0xe7, 0xe0, /* Contrast to 1.0, noise shaping
* 10 to 8 2-bit error diffusion */
0xe8, 0xf8, /* YUV422, CbCr binary offset,
* ... (p.32) */
0xea, 0x18, /* LLC2 connected, output FIFO
* reset with VACTintern */
0xf0, 0x8a, /* Half full level to 10, bus
* shuffler [7:0, 23:16, 15:8] */
0xf1, 0x18, /* Single clock, sync mode, no
* FE delay, no HLEN counter */
0xf8, 0x12, /* Port A, PIXCLK, HF# & FE#
* strength to 2 */
0xf9, 0x24, /* Port B, HREF, VREF, PREF &
* ALPHA strength to 4 */
};
static const unsigned short init_fp[] = {
0x59, 0,
0xa0, 2070, /* ACC reference */
0xa3, 0,
0xa4, 0,
0xa8, 30,
0xb2, 768,
0xbe, 27,
0x58, 0,
0x26, 0,
0x4b, 0x298, /* PLL gain */
};
static void
vpx3220_dump_i2c (struct i2c_client *client)
{
int len = sizeof(init_common);
const unsigned char *data = init_common;
while (len > 1) {
dprintk(1,
KERN_DEBUG "vpx3216b i2c reg 0x%02x data 0x%02x\n",
*data, vpx3220_read(client, *data));
data += 2;
len -= 2;
}
}
static int
vpx3220_command (struct i2c_client *client,
unsigned int cmd,
void *arg)
{
struct vpx3220 *decoder = i2c_get_clientdata(client);
switch (cmd) {
case 0:
{
vpx3220_write_block(client, init_common,
sizeof(init_common));
vpx3220_write_fp_block(client, init_fp,
sizeof(init_fp) >> 1);
switch (decoder->norm) {
case VIDEO_MODE_NTSC:
vpx3220_write_fp_block(client, init_ntsc,
sizeof(init_ntsc) >> 1);
break;
case VIDEO_MODE_PAL:
vpx3220_write_fp_block(client, init_pal,
sizeof(init_pal) >> 1);
break;
case VIDEO_MODE_SECAM:
vpx3220_write_fp_block(client, init_secam,
sizeof(init_secam) >> 1);
break;
default:
vpx3220_write_fp_block(client, init_pal,
sizeof(init_pal) >> 1);
break;
}
}
break;
case DECODER_DUMP:
{
vpx3220_dump_i2c(client);
}
break;
case DECODER_GET_CAPABILITIES:
{
struct video_decoder_capability *cap = arg;
dprintk(1, KERN_DEBUG "%s: DECODER_GET_CAPABILITIES\n",
I2C_NAME(client));
cap->flags = VIDEO_DECODER_PAL |
VIDEO_DECODER_NTSC |
VIDEO_DECODER_SECAM |
VIDEO_DECODER_AUTO |
VIDEO_DECODER_CCIR;
cap->inputs = 3;
cap->outputs = 1;
}
break;
case DECODER_GET_STATUS:
{
int res = 0, status;
dprintk(1, KERN_INFO "%s: DECODER_GET_STATUS\n",
I2C_NAME(client));
status = vpx3220_fp_read(client, 0x0f3);
dprintk(1, KERN_INFO "%s: status: 0x%04x\n", I2C_NAME(client),
status);
if (status < 0)
return status;
if ((status & 0x20) == 0) {
res |= DECODER_STATUS_GOOD | DECODER_STATUS_COLOR;
switch (status & 0x18) {
case 0x00:
case 0x10:
case 0x14:
case 0x18:
res |= DECODER_STATUS_PAL;
break;
case 0x08:
res |= DECODER_STATUS_SECAM;
break;
case 0x04:
case 0x0c:
case 0x1c:
res |= DECODER_STATUS_NTSC;
break;
}
}
*(int *) arg = res;
}
break;
case DECODER_SET_NORM:
{
int *iarg = arg, data;
dprintk(1, KERN_DEBUG "%s: DECODER_SET_NORM %d\n",
I2C_NAME(client), *iarg);
switch (*iarg) {
case VIDEO_MODE_NTSC:
vpx3220_write_fp_block(client, init_ntsc,
sizeof(init_ntsc) >> 1);
dprintk(1, KERN_INFO "%s: norm switched to NTSC\n",
I2C_NAME(client));
break;
case VIDEO_MODE_PAL:
vpx3220_write_fp_block(client, init_pal,
sizeof(init_pal) >> 1);
dprintk(1, KERN_INFO "%s: norm switched to PAL\n",
I2C_NAME(client));
break;
case VIDEO_MODE_SECAM:
vpx3220_write_fp_block(client, init_secam,
sizeof(init_secam) >> 1);
dprintk(1, KERN_INFO "%s: norm switched to SECAM\n",
I2C_NAME(client));
break;
case VIDEO_MODE_AUTO:
/* FIXME This is only preliminary support */
data = vpx3220_fp_read(client, 0xf2) & 0x20;
vpx3220_fp_write(client, 0xf2, 0x00c0 | data);
dprintk(1, KERN_INFO "%s: norm switched to Auto\n",
I2C_NAME(client));
break;
default:
return -EINVAL;
}
decoder->norm = *iarg;
}
break;
case DECODER_SET_INPUT:
{
int *iarg = arg, data;
/* RJ: *iarg = 0: ST8 (PCTV) input
*iarg = 1: COMPOSITE input
*iarg = 2: SVHS input */
const int input[3][2] = {
{0x0c, 0},
{0x0d, 0},
{0x0e, 1}
};
if (*iarg < 0 || *iarg > 2)
return -EINVAL;
dprintk(1, KERN_INFO "%s: input switched to %s\n",
I2C_NAME(client), inputs[*iarg]);
vpx3220_write(client, 0x33, input[*iarg][0]);
data = vpx3220_fp_read(client, 0xf2) & ~(0x0020);
if (data < 0)
return data;
/* 0x0010 is required to latch the setting */
vpx3220_fp_write(client, 0xf2,
data | (input[*iarg][1] << 5) | 0x0010);
udelay(10);
}
break;
case DECODER_SET_OUTPUT:
{
int *iarg = arg;
/* not much choice of outputs */
if (*iarg != 0) {
return -EINVAL;
}
}
break;
case DECODER_ENABLE_OUTPUT:
{
int *iarg = arg;
dprintk(1, KERN_DEBUG "%s: DECODER_ENABLE_OUTPUT %d\n",
I2C_NAME(client), *iarg);
vpx3220_write(client, 0xf2, (*iarg ? 0x1b : 0x00));
}
break;
case DECODER_SET_PICTURE:
{
struct video_picture *pic = arg;
if (decoder->bright != pic->brightness) {
/* We want -128 to 128 we get 0-65535 */
decoder->bright = pic->brightness;
vpx3220_write(client, 0xe6,
(decoder->bright - 32768) >> 8);
}
if (decoder->contrast != pic->contrast) {
/* We want 0 to 64 we get 0-65535 */
/* Bit 7 and 8 is for noise shaping */
decoder->contrast = pic->contrast;
vpx3220_write(client, 0xe7,
(decoder->contrast >> 10) + 192);
}
if (decoder->sat != pic->colour) {
/* We want 0 to 4096 we get 0-65535 */
decoder->sat = pic->colour;
vpx3220_fp_write(client, 0xa0,
decoder->sat >> 4);
}
if (decoder->hue != pic->hue) {
/* We want -512 to 512 we get 0-65535 */
decoder->hue = pic->hue;
vpx3220_fp_write(client, 0x1c,
((decoder->hue - 32768) >> 6) & 0xFFF);
}
}
break;
default:
return -EINVAL;
}
return 0;
}
static int
vpx3220_init_client (struct i2c_client *client)
{
vpx3220_write_block(client, init_common, sizeof(init_common));
vpx3220_write_fp_block(client, init_fp, sizeof(init_fp) >> 1);
/* Default to PAL */
vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1);
return 0;
}
/* -----------------------------------------------------------------------
* Client managment code
*/
/*
* Generic i2c probe
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
static unsigned short normal_i2c[] =
{ I2C_VPX3220 >> 1, (I2C_VPX3220 >> 1) + 4,
I2C_CLIENT_END
};
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_i2c_range = normal_i2c_range,
.probe = probe,
.probe_range = probe_range,
.ignore = ignore,
.ignore_range = ignore_range,
.force = force
};
static int vpx3220_i2c_id = 0;
static struct i2c_driver vpx3220_i2c_driver;
static int
vpx3220_detach_client (struct i2c_client *client)
{
struct vpx3220 *decoder = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
return err;
}
kfree(decoder);
kfree(client);
return 0;
}
static int
vpx3220_detect_client (struct i2c_adapter *adapter,
int address,
int kind)
{
int err;
struct i2c_client *client;
struct vpx3220 *decoder;
dprintk(1, VPX3220_DEBUG "%s\n", __func__);
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality
(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
return 0;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (client == NULL) {
return -ENOMEM;
}
memset(client, 0, sizeof(struct i2c_client));
client->addr = address;
client->adapter = adapter;
client->driver = &vpx3220_i2c_driver;
client->flags = I2C_CLIENT_ALLOW_USE;
client->id = vpx3220_i2c_id++;
/* Check for manufacture ID and part number */
if (kind < 0) {
u8 id;
u16 pn;
id = vpx3220_read(client, 0x00);
if (id != 0xec) {
dprintk(1,
KERN_INFO
"vpx3220_attach: Wrong manufacturer ID (0x%02x)\n",
id);
kfree(client);
return 0;
}
pn = (vpx3220_read(client, 0x02) << 8) +
vpx3220_read(client, 0x01);
switch (pn) {
case 0x4680:
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"vpx3220a[%d]", client->id);
break;
case 0x4260:
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"vpx3216b[%d]", client->id);
break;
case 0x4280:
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"vpx3214c[%d]", client->id);
break;
default:
dprintk(1,
KERN_INFO
"%s: Wrong part number (0x%04x)\n",
__func__, pn);
kfree(client);
return 0;
}
} else {
snprintf(I2C_NAME(client), sizeof(I2C_NAME(client)) - 1,
"forced vpx32xx[%d]",
client->id);
}
decoder = kmalloc(sizeof(struct vpx3220), GFP_KERNEL);
if (decoder == NULL) {
kfree(client);
return -ENOMEM;
}
memset(decoder, 0, sizeof(struct vpx3220));
decoder->norm = VIDEO_MODE_PAL;
decoder->input = 0;
decoder->enable = 1;
decoder->bright = 32768;
decoder->contrast = 32768;
decoder->hue = 32768;
decoder->sat = 32768;
i2c_set_clientdata(client, decoder);
err = i2c_attach_client(client);
if (err) {
kfree(client);
kfree(decoder);
return err;
}
dprintk(1, KERN_INFO "%s: vpx32xx client found at address 0x%02x\n",
I2C_NAME(client), client->addr << 1);
vpx3220_init_client(client);
return 0;
}
static int
vpx3220_attach_adapter (struct i2c_adapter *adapter)
{
int ret;
ret = i2c_probe(adapter, &addr_data, &vpx3220_detect_client);
dprintk(1, VPX3220_DEBUG "%s: i2c_probe returned %d\n",
__func__, ret);
return ret;
}
/* -----------------------------------------------------------------------
* Driver initialization and cleanup code
*/
static struct i2c_driver vpx3220_i2c_driver = {
.owner = THIS_MODULE,
.name = "vpx3220",
.id = I2C_DRIVERID_VPX3220,
.flags = I2C_DF_NOTIFY,
.attach_adapter = vpx3220_attach_adapter,
.detach_client = vpx3220_detach_client,
.command = vpx3220_command,
};
static int __init
vpx3220_init (void)
{
return i2c_add_driver(&vpx3220_i2c_driver);
}
static void __exit
vpx3220_cleanup (void)
{
i2c_del_driver(&vpx3220_i2c_driver);
}
module_init(vpx3220_init);
module_exit(vpx3220_cleanup);
MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video encoder driver");
MODULE_AUTHOR("Laurent Pinchart");
MODULE_LICENSE("GPL");
/* /*
zoran - Iomega Buz driver * zoran - Iomega Buz driver
*
Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de> * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
*
based on * based on
*
zoran.0.0.3 Copyright (C) 1998 Dave Perks <dperks@ibm.net> * zoran.0.0.3 Copyright (C) 1998 Dave Perks <dperks@ibm.net>
*
and * and
*
bttv - Bt848 frame grabber driver * bttv - Bt848 frame grabber driver
Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de) * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
* & Marcus Metzler (mocm@thp.uni-koeln.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 * This program is free software; you can redistribute it and/or modify
the Free Software Foundation; either version 2 of the License, or * it under the terms of the GNU General Public License as published by
(at your option) any later version. * 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 * This program is distributed in the hope that it will be useful,
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details. * 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 * You should have received a copy of the GNU General Public License
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * along with this program; if not, write to the Free Software
*/ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _BUZ_H_ #ifndef _BUZ_H_
#define _BUZ_H_ #define _BUZ_H_
#include <linux/config.h>
/* The Buz only supports a maximum width of 720, but some V4L
applications (e.g. xawtv are more happy with 768).
If XAWTV_HACK is defined, we try to fake a device with bigger width */
//#define XAWTV_HACK
//#ifdef XAWTV_HACK
//#define BUZ_MAX_WIDTH 768 /* never display more than 768 pixels */
#define BUZ_MAX_WIDTH (zr->timing->Wa)
//#else
//#define BUZ_MAX_WIDTH 720 /* never display more than 720 pixels */
//#endif
//#define BUZ_MAX_HEIGHT 576 /* never display more than 576 rows */
#define BUZ_MAX_HEIGHT (zr->timing->Ha)
#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */
#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */
struct zoran_requestbuffers { struct zoran_requestbuffers {
unsigned long count; /* Number of buffers for MJPEG grabbing */ unsigned long count; /* Number of buffers for MJPEG grabbing */
unsigned long size; /* Size PER BUFFER in bytes */ unsigned long size; /* Size PER BUFFER in bytes */
...@@ -80,35 +62,35 @@ struct zoran_params { ...@@ -80,35 +62,35 @@ struct zoran_params {
int input; /* Input channel: 0 = Composite, 1 = S-VHS */ int input; /* Input channel: 0 = Composite, 1 = S-VHS */
int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */
int decimation; /* decimation of captured video, int decimation; /* decimation of captured video,
enlargement of video played back. * enlargement of video played back.
Valid values are 1, 2, 4 or 0. * Valid values are 1, 2, 4 or 0.
0 is a special value where the user * 0 is a special value where the user
has full control over video scaling */ * has full control over video scaling */
/* The following parameters only have to be set if decimation==0, /* The following parameters only have to be set if decimation==0,
for other values of decimation they provide the data how the image is captured */ * for other values of decimation they provide the data how the image is captured */
int HorDcm; /* Horizontal decimation: 1, 2 or 4 */ int HorDcm; /* Horizontal decimation: 1, 2 or 4 */
int VerDcm; /* Vertical decimation: 1 or 2 */ int VerDcm; /* Vertical decimation: 1 or 2 */
int TmpDcm; /* Temporal decimation: 1 or 2, int TmpDcm; /* Temporal decimation: 1 or 2,
if TmpDcm==2 in capture every second frame is dropped, * if TmpDcm==2 in capture every second frame is dropped,
in playback every frame is played twice */ * in playback every frame is played twice */
int field_per_buff; /* Number of fields per buffer: 1 or 2 */ int field_per_buff; /* Number of fields per buffer: 1 or 2 */
int img_x; /* start of image in x direction */ int img_x; /* start of image in x direction */
int img_y; /* start of image in y direction */ int img_y; /* start of image in y direction */
int img_width; /* image width BEFORE decimation, int img_width; /* image width BEFORE decimation,
must be a multiple of HorDcm*16 */ * must be a multiple of HorDcm*16 */
int img_height; /* image height BEFORE decimation, int img_height; /* image height BEFORE decimation,
must be a multiple of VerDcm*8 */ * must be a multiple of VerDcm*8 */
/* --- End of parameters for decimation==0 only --- */ /* --- End of parameters for decimation==0 only --- */
/* JPEG control parameters */ /* JPEG control parameters */
int quality; /* Measure for quality of compressed images. int quality; /* Measure for quality of compressed images.
Scales linearly with the size of the compressed images. * Scales linearly with the size of the compressed images.
Must be beetween 0 and 100, 100 is a compression * Must be beetween 0 and 100, 100 is a compression
ratio of 1:4 */ * ratio of 1:4 */
int odd_even; /* Which field should come first ??? */ int odd_even; /* Which field should come first ??? */
...@@ -120,12 +102,12 @@ struct zoran_params { ...@@ -120,12 +102,12 @@ struct zoran_params {
char COM_data[60]; /* Data in JPEG COM segment */ char COM_data[60]; /* Data in JPEG COM segment */
unsigned long jpeg_markers; /* Which markers should go into the JPEG output. unsigned long jpeg_markers; /* Which markers should go into the JPEG output.
Unless you exactly know what you do, leave them untouched. * Unless you exactly know what you do, leave them untouched.
Inluding less markers will make the resulting code * Inluding less markers will make the resulting code
smaller, but there will be fewer aplications * smaller, but there will be fewer aplications
which can read it. * which can read it.
The presence of the APP and COM marker is * The presence of the APP and COM marker is
influenced by APP0_len and COM_len ONLY! */ * influenced by APP0_len and COM_len ONLY! */
#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */ #define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */
#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */ #define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */
#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */ #define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */
...@@ -133,11 +115,11 @@ struct zoran_params { ...@@ -133,11 +115,11 @@ struct zoran_params {
#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */ #define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */
int VFIFO_FB; /* Flag for enabling Video Fifo Feedback. int VFIFO_FB; /* Flag for enabling Video Fifo Feedback.
If this flag is turned on and JPEG decompressing * If this flag is turned on and JPEG decompressing
is going to the screen, the decompress process * is going to the screen, the decompress process
is stopped every time the Video Fifo is full. * is stopped every time the Video Fifo is full.
This enables a smooth decompress to the screen * This enables a smooth decompress to the screen
but the video output signal will get scrambled */ * but the video output signal will get scrambled */
/* Misc */ /* Misc */
...@@ -158,12 +140,31 @@ Private IOCTL to set up for displaying MJPEG ...@@ -158,12 +140,31 @@ Private IOCTL to set up for displaying MJPEG
#ifdef __KERNEL__ #ifdef __KERNEL__
#define MAJOR_VERSION 0 /* driver major version */
#define MINOR_VERSION 9 /* driver minor version */
#define RELEASE_VERSION 5 /* release version */
#define ZORAN_NAME "ZORAN" /* name of the device */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
#define ZR_DEVNAME(zr) (zr)->name
#else
#define ZR_DEVNAME(zr) (zr)->pci_dev->dev.name
#endif
#define BUZ_MAX_WIDTH (zr->timing->Wa)
#define BUZ_MAX_HEIGHT (zr->timing->Ha)
#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */
#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */
#define BUZ_NUM_STAT_COM 4 #define BUZ_NUM_STAT_COM 4
#define BUZ_MASK_STAT_COM 3 #define BUZ_MASK_STAT_COM 3
#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */ #define BUZ_MAX_FRAME 256 /* Must be a power of 2 */
#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */ #define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */
#define BUZ_MAX_INPUT 8
#if VIDEO_MAX_FRAME <= 32 #if VIDEO_MAX_FRAME <= 32
# define V4L_MAX_FRAME 32 # define V4L_MAX_FRAME 32
#elif VIDEO_MAX_FRAME <= 64 #elif VIDEO_MAX_FRAME <= 64
...@@ -173,15 +174,29 @@ Private IOCTL to set up for displaying MJPEG ...@@ -173,15 +174,29 @@ Private IOCTL to set up for displaying MJPEG
#endif #endif
#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1) #define V4L_MASK_FRAME (V4L_MAX_FRAME - 1)
#define MAX_KMALLOC_MEM (128*1024)
#include "zr36057.h" #include "zr36057.h"
enum card_type { enum card_type {
UNKNOWN = 0, UNKNOWN = -1,
DC10,
DC10plus, /* Pinnacle/Miro */
LML33, DC10_old, /* DC30 like */
BUZ DC10_new, /* DC10plus like */
DC10plus,
DC30,
DC30plus,
/* Linux Media Labs */
LML33,
LML33R10,
/* Iomega */
BUZ,
/* total number of cards */
NUM_CARDS
}; };
enum zoran_codec_mode { enum zoran_codec_mode {
...@@ -199,84 +214,237 @@ enum zoran_buffer_state { ...@@ -199,84 +214,237 @@ enum zoran_buffer_state {
BUZ_STATE_DONE /* buffer is ready to return to application */ BUZ_STATE_DONE /* buffer is ready to return to application */
}; };
struct zoran_gbuffer { enum zoran_map_mode {
ZORAN_MAP_MODE_RAW,
ZORAN_MAP_MODE_JPG_REC,
#define ZORAN_MAP_MODE_JPG ZORAN_MAP_MODE_JPG_REC
ZORAN_MAP_MODE_JPG_PLAY,
};
enum gpio_type {
GPIO_JPEG_SLEEP = 0,
GPIO_JPEG_RESET,
GPIO_JPEG_FRAME,
GPIO_VID_DIR,
GPIO_VID_EN,
GPIO_VID_RESET,
GPIO_CLK_SEL1,
GPIO_CLK_SEL2,
GPIO_MAX,
};
enum gpcs_type {
GPCS_JPEG_RESET = 0,
GPCS_JPEG_START,
GPCS_MAX,
};
struct zoran_format {
char *name;
int palette;
__u32 fourcc;
int colorspace;
int depth;
__u32 flags;
};
/* flags */
#define ZORAN_FORMAT_COMPRESSED 1<<0
#define ZORAN_FORMAT_OVERLAY 1<<1
#define ZORAN_FORMAT_CAPTURE 1<<2
#define ZORAN_FORMAT_PLAYBACK 1<<3
/* overlay-settings */
struct zoran_overlay_settings {
int is_set;
int x, y, width, height; /* position */
int clipcount; /* position and number of clips */
const struct zoran_format *format; /* overlay format */
};
/* v4l-capture settings */
struct zoran_v4l_settings {
int width, height, bytesperline; /* capture size */
const struct zoran_format *format; /* capture format */
};
/* whoops, this one is undeclared if !v4l2 */
#ifndef HAVE_V4L2
struct v4l2_jpegcompression {
int quality;
int APPn;
int APP_len;
char APP_data[60];
int COM_len;
char COM_data[60];
__u32 jpeg_markers;
__u8 reserved[116];
};
#endif
/* jpg-capture/-playback settings */
struct zoran_jpg_settings {
int decimation; /* this bit is used to set everything to default */
int HorDcm, VerDcm, TmpDcm; /* capture decimation settings (TmpDcm=1 means both fields) */
int field_per_buff, odd_even; /* field-settings (odd_even=1 (+TmpDcm=1) means top-field-first) */
int img_x, img_y, img_width, img_height; /* crop settings (subframe capture) */
struct v4l2_jpegcompression jpg_comp; /* JPEG-specific capture settings */
};
struct zoran_mapping {
struct file *file;
int count;
};
struct zoran_jpg_buffer {
struct zoran_mapping *map;
u32 *frag_tab; /* addresses of frag table */ u32 *frag_tab; /* addresses of frag table */
u32 frag_tab_bus; /* same value cached to save time in ISR */ u32 frag_tab_bus; /* same value cached to save time in ISR */
enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */ enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */
struct zoran_sync bs; /* DONE: info to return to application */ struct zoran_sync bs; /* DONE: info to return to application */
}; };
struct v4l_gbuffer { struct zoran_v4l_buffer {
char *fbuffer; /* virtual address of frame buffer */ struct zoran_mapping *map;
char *fbuffer; /* virtual address of frame buffer */
unsigned long fbuffer_phys; /* physical address of frame buffer */ unsigned long fbuffer_phys; /* physical address of frame buffer */
unsigned long fbuffer_bus; /* bus address of frame buffer */ unsigned long fbuffer_bus; /* bus address of frame buffer */
enum zoran_buffer_state state; /* state: unused/pending/done */ enum zoran_buffer_state state; /* state: unused/pending/done */
struct zoran_sync bs; /* DONE: info to return to application */
};
enum zoran_lock_activity {
ZORAN_FREE, /* free for use */
ZORAN_ACTIVE, /* active but unlocked */
ZORAN_LOCKED, /* locked */
};
/* buffer collections */
struct zoran_jpg_struct {
enum zoran_lock_activity active; /* feature currently in use? */
struct zoran_jpg_buffer buffer[BUZ_MAX_FRAME]; /* buffers */
int num_buffers, buffer_size;
u8 allocated; /* Flag if buffers are allocated */
u8 ready_to_be_freed; /* hack - see zoran_driver.c */
u8 need_contiguous; /* Flag if contiguous buffers are needed */
}; };
struct tvnorm { struct zoran_v4l_struct {
u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart; enum zoran_lock_activity active; /* feature currently in use? */
struct zoran_v4l_buffer buffer[VIDEO_MAX_FRAME]; /* buffers */
int num_buffers, buffer_size;
u8 allocated; /* Flag if buffers are allocated */
u8 ready_to_be_freed; /* hack - see zoran_driver.c */
};
struct zoran;
/* zoran_fh contains per-open() settings */
struct zoran_fh {
struct zoran *zr;
enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */
struct zoran_overlay_settings overlay_settings;
u32 *overlay_mask; /* overlay mask */
enum zoran_lock_activity overlay_active; /* feature currently in use? */
struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */
struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */
struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */
struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */
};
struct card_info {
enum card_type type;
char name[32];
u16 i2c_decoder, i2c_encoder; /* I2C types */
u16 video_vfe, video_codec; /* videocodec types */
u16 audio_chip; /* audio type */
u16 vendor_id, device_id; /* subsystem vendor/device ID */
int inputs; /* number of video inputs */
struct input {
int muxsel;
char name[32];
} input[BUZ_MAX_INPUT];
int norms;
struct tvnorm *tvn[3]; /* supported TV norms */
u32 jpeg_int; /* JPEG interrupt */
u32 vsync_int; /* VSYNC interrupt */
s8 gpio[GPIO_MAX];
u8 gpcs[GPCS_MAX];
struct vfe_polarity vfe_pol;
u8 gpio_pol[GPIO_MAX];
/* is the /GWS line conected? */
u8 gws_not_connected;
void (*init) (struct zoran * zr);
}; };
struct zoran { struct zoran {
struct video_device video_dev; struct video_device video_dev;
struct i2c_bus i2c;
int initialized; /* flag if zoran has been correctly initalized */ struct i2c_adapter i2c_adapter; /* */
int user; /* number of current users (0 or 1) */ struct i2c_algo_bit_data i2c_algo; /* */
enum card_type card; u32 i2cbr;
struct tvnorm *timing;
struct i2c_client *decoder; /* video decoder i2c client */
struct i2c_client *encoder; /* video encoder i2c client */
unsigned short id; /* number of this device */ struct videocodec *codec; /* video codec */
char name[32]; /* name of this device */ struct videocodec *vfe; /* video front end */
struct semaphore resource_lock; /* prevent evil stuff */
u8 initialized; /* flag if zoran has been correctly initalized */
int user; /* number of current users */
struct card_info card;
struct tvnorm *timing;
unsigned short id; /* number of this device */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
char name[32]; /* name of this device */
#endif
struct pci_dev *pci_dev; /* PCI device */ struct pci_dev *pci_dev; /* PCI device */
unsigned char revision; /* revision of zr36057 */ unsigned char revision; /* revision of zr36057 */
unsigned int zr36057_adr; /* bus address of IO mem returned by PCI BIOS */ unsigned int zr36057_adr; /* bus address of IO mem returned by PCI BIOS */
unsigned char *zr36057_mem; /* pointer to mapped IO memory */ unsigned char *zr36057_mem; /* pointer to mapped IO memory */
int map_mjpeg_buffers; /* Flag which bufferset will map by next mmap() */ spinlock_t spinlock; /* Spinlock */
spinlock_t lock; /* Spinlock irq and hardware */
struct semaphore sem; /* Guard parallel ioctls and mmap */
/* Video for Linux parameters */ /* Video for Linux parameters */
int input, norm; /* card's norm and input - norm=VIDEO_MODE_* */
struct video_picture picture; /* Current picture params */ int hue, saturation, contrast, brightness; /* Current picture params */
struct video_buffer buffer; /* Current buffer params */ struct video_buffer buffer; /* Current buffer params */
struct video_window window; /* Current window params */ struct zoran_overlay_settings overlay_settings;
int buffer_set, window_set; /* Flags if the above structures are set */ u32 *overlay_mask; /* overlay mask */
int video_interlace; /* Image on screen is interlaced */ enum zoran_lock_activity overlay_active; /* feature currently in use? */
u32 *overlay_mask; wait_queue_head_t v4l_capq;
wait_queue_head_t v4l_capq;
int v4l_overlay_active; /* Overlay grab is activated */ int v4l_overlay_active; /* Overlay grab is activated */
int v4l_memgrab_active; /* Memory grab is activated */ int v4l_memgrab_active; /* Memory grab is activated */
int v4l_grab_frame; /* Frame number being currently grabbed */ int v4l_grab_frame; /* Frame number being currently grabbed */
#define NO_GRAB_ACTIVE (-1) #define NO_GRAB_ACTIVE (-1)
int v4l_grab_seq; /* Number of frames grabbed */ unsigned long v4l_grab_seq; /* Number of frames grabbed */
int gwidth; /* Width of current memory capture */ struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */
int gheight; /* Height of current memory capture */
int gformat; /* Format of ... */
int gbpl; /* byte per line of ... */
/* V4L grab queue of frames pending */ /* V4L grab queue of frames pending */
unsigned long v4l_pend_head;
unsigned v4l_pend_head; unsigned long v4l_pend_tail;
unsigned v4l_pend_tail; unsigned long v4l_sync_tail;
int v4l_pend[V4L_MAX_FRAME]; int v4l_pend[V4L_MAX_FRAME];
struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */
struct v4l_gbuffer v4l_gbuf[VIDEO_MAX_FRAME]; /* V4L buffers' info */
/* Buz MJPEG parameters */ /* Buz MJPEG parameters */
unsigned long jpg_nbufs; /* Number of buffers */
unsigned long jpg_bufsize; /* Size of mjpeg buffers in bytes */
int jpg_buffers_allocated; /* Flag if buffers are allocated */
int need_contiguous; /* Flag if contiguous buffers are needed */
enum zoran_codec_mode codec_mode; /* status of codec */ enum zoran_codec_mode codec_mode; /* status of codec */
struct zoran_params params; /* structure with a lot of things to play with */ struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */
wait_queue_head_t jpg_capq; /* wait here for grab to finish */ wait_queue_head_t jpg_capq; /* wait here for grab to finish */
...@@ -293,17 +461,20 @@ struct zoran { ...@@ -293,17 +461,20 @@ struct zoran {
unsigned long jpg_queued_num; /* count of frames queued since grab/play started */ unsigned long jpg_queued_num; /* count of frames queued since grab/play started */
/* zr36057's code buffer table */ /* zr36057's code buffer table */
u32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ u32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */
/* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */ /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */
int jpg_pend[BUZ_MAX_FRAME]; int jpg_pend[BUZ_MAX_FRAME];
/* array indexed by frame number */ /* array indexed by frame number */
struct zoran_gbuffer jpg_gbuf[BUZ_MAX_FRAME]; /* MJPEG buffers' info */ struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */
/* Additional stuff for testing */ /* Additional stuff for testing */
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *zoran_proc; struct proc_dir_entry *zoran_proc;
#else
void *zoran_proc;
#endif
int testing; int testing;
int jpeg_error; int jpeg_error;
int intr_counter_GIRQ1; int intr_counter_GIRQ1;
...@@ -330,8 +501,6 @@ struct zoran { ...@@ -330,8 +501,6 @@ struct zoran {
wait_queue_head_t test_q; wait_queue_head_t test_q;
}; };
#endif
/*The following should be done in more portable way. It depends on define /*The following should be done in more portable way. It depends on define
of _ALPHA_BUZ in the Makefile.*/ of _ALPHA_BUZ in the Makefile.*/
...@@ -347,22 +516,6 @@ struct zoran { ...@@ -347,22 +516,6 @@ struct zoran {
#define btor(dat,adr) btwrite((dat) | btread(adr), adr) #define btor(dat,adr) btwrite((dat) | btread(adr), adr)
#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) #define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
#define I2C_TSA5522 0xc2 #endif /* __kernel__ */
#define I2C_TDA9850 0xb6
#define I2C_HAUPEE 0xa0
#define I2C_STBEE 0xae
#define I2C_SAA7111 0x48
#define I2C_SAA7110 0x9c
#define I2C_SAA7185 0x88
//#define I2C_ADV7175 0xd4
#define I2C_ADV7175 0x54
#define TDA9850_CON1 0x04
#define TDA9850_CON2 0x05
#define TDA9850_CON3 0x06
#define TDA9850_CON4 0x07
#define TDA9850_ALI1 0x08
#define TDA9850_ALI2 0x09
#define TDA9850_ALI3 0x0a
#endif #endif
/*
* Zoran zr36057/zr36067 PCI controller driver, for the
* Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
* Media Labs LML33/LML33R10.
*
* This part handles card-specific data and detection
*
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
*
* Currently maintained by:
* Ronald Bultje <rbultje@ronald.bitfreak.net>
* Laurent Pinchart <laurent.pinchart@skynet.be>
* Mailinglist <mjpeg-users@lists.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/videodev.h>
#include <linux/spinlock.h>
#include <linux/sem.h>
#include <linux/kmod.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/video_decoder.h>
#include <linux/video_encoder.h>
#include "videocodec.h"
#include "zoran.h"
#include "zoran_card.h"
#include "zoran_device.h"
#include "zoran_procfs.h"
#define I2C_NAME(x) (x)->dev.name
extern const struct zoran_format zoran_formats[];
extern const int zoran_num_formats;
static int card[BUZ_MAX] = { -1, -1, -1, -1 };
MODULE_PARM(card, "1-" __stringify(BUZ_MAX) "i");
MODULE_PARM_DESC(card, "The type of card");
static int encoder[BUZ_MAX] = { -1, -1, -1, -1 };
MODULE_PARM(encoder, "1-" __stringify(BUZ_MAX) "i");
MODULE_PARM_DESC(encoder, "i2c TV encoder");
static int decoder[BUZ_MAX] = { -1, -1, -1, -1 };
MODULE_PARM(decoder, "1-" __stringify(BUZ_MAX) "i");
MODULE_PARM_DESC(decoder, "i2c TV decoder");
/*
The video mem address of the video card.
The driver has a little database for some videocards
to determine it from there. If your video card is not in there
you have either to give it to the driver as a parameter
or set in in a VIDIOCSFBUF ioctl
*/
static unsigned long vidmem = 0; /* Video memory base address */
MODULE_PARM(vidmem, "i");
/*
Default input and video norm at startup of the driver.
*/
static int default_input = 0; /* 0=Composite, 1=S-Video */
MODULE_PARM(default_input, "i");
MODULE_PARM_DESC(default_input,
"Default input (0=Composite, 1=S-Video, 2=Internal)");
static int default_norm = 0; /* 0=PAL, 1=NTSC 2=SECAM */
MODULE_PARM(default_norm, "i");
MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)");
static int video_nr = -1; /* /dev/videoN, -1 for autodetect */
MODULE_PARM(video_nr, "i");
MODULE_PARM_DESC(video_nr, "video device number");
/*
Number and size of grab buffers for Video 4 Linux
The vast majority of applications should not need more than 2,
the very popular BTTV driver actually does ONLY have 2.
Time sensitive applications might need more, the maximum
is VIDEO_MAX_FRAME (defined in <linux/videodev.h>).
The size is set so that the maximum possible request
can be satisfied. Decrease it, if bigphys_area alloc'd
memory is low. If you don't have the bigphys_area patch,
set it to 128 KB. Will you allow only to grab small
images with V4L, but that's better than nothing.
v4l_bufsize has to be given in KB !
*/
int v4l_nbufs = 2;
int v4l_bufsize = 128; /* Everybody should be able to work with this setting */
MODULE_PARM(v4l_nbufs, "i");
MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use");
MODULE_PARM(v4l_bufsize, "i");
MODULE_PARM_DESC(v4l_bufsize, "Maximum size per V4L buffer (in kB)");
int jpg_nbufs = 32;
int jpg_bufsize = 512; /* max size for 100% quality full-PAL frame */
MODULE_PARM(jpg_nbufs, "i");
MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use");
MODULE_PARM(jpg_bufsize, "i");
MODULE_PARM_DESC(jpg_bufsize, "Maximum size per JPG buffer (in kB)");
int pass_through = 0; /* 1=Pass through TV signal when device is not used */
/* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */
MODULE_PARM(pass_through, "i");
MODULE_PARM_DESC(pass_through,
"Pass TV signal through to TV-out when idling");
int debug = 1;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-4)");
MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver");
MODULE_AUTHOR("Serguei Miridonov");
MODULE_LICENSE("GPL");
static struct pci_device_id zr36067_pci_tbl[] = {
{PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0}
};
MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl);
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
int zoran_num; /* number of Buzs in use */
struct zoran zoran[BUZ_MAX];
/* videocodec bus functions ZR36060 */
static u32
zr36060_read (struct videocodec *codec,
u16 reg)
{
struct zoran *zr = (struct zoran *) codec->master_data->data;
__u32 data;
if (post_office_wait(zr)
|| post_office_write(zr, 0, 1, reg >> 8)
|| post_office_write(zr, 0, 2, reg & 0xff)) {
return -1;
}
data = post_office_read(zr, 0, 3) & 0xff;
return data;
}
static void
zr36060_write (struct videocodec *codec,
u16 reg,
u32 val)
{
struct zoran *zr = (struct zoran *) codec->master_data->data;
if (post_office_wait(zr)
|| post_office_write(zr, 0, 1, reg >> 8)
|| post_office_write(zr, 0, 2, reg & 0xff)) {
return;
}
post_office_write(zr, 0, 3, val & 0xff);
}
/* videocodec bus functions ZR36050 */
static u32
zr36050_read (struct videocodec *codec,
u16 reg)
{
struct zoran *zr = (struct zoran *) codec->master_data->data;
__u32 data;
if (post_office_wait(zr)
|| post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
return -1;
}
data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read
return data;
}
static void
zr36050_write (struct videocodec *codec,
u16 reg,
u32 val)
{
struct zoran *zr = (struct zoran *) codec->master_data->data;
if (post_office_wait(zr)
|| post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
return;
}
post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data
}
/* videocodec bus functions ZR36016 */
static u32
zr36016_read (struct videocodec *codec,
u16 reg)
{
struct zoran *zr = (struct zoran *) codec->master_data->data;
__u32 data;
if (post_office_wait(zr)) {
return -1;
}
data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read
return data;
}
/* hack for in zoran_device.c */
void
zr36016_write (struct videocodec *codec,
u16 reg,
u32 val)
{
struct zoran *zr = (struct zoran *) codec->master_data->data;
if (post_office_wait(zr)) {
return;
}
post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data
}
/*
* Board specific information
*/
static void
dc10_init (struct zoran *zr)
{
dprintk(3, KERN_DEBUG "%s: dc10_init()\n", ZR_DEVNAME(zr));
/* Pixel clock selection */
GPIO(zr, 4, 0);
GPIO(zr, 5, 1);
/* Enable the video bus sync signals */
GPIO(zr, 7, 0);
}
static void
dc10plus_init (struct zoran *zr)
{
dprintk(3, KERN_DEBUG "%s: dc10plus_init()\n", ZR_DEVNAME(zr));
}
static void
buz_init (struct zoran *zr)
{
dprintk(3, KERN_DEBUG "%s: buz_init()\n", ZR_DEVNAME(zr));
/* some stuff from Iomega */
pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15);
pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020);
pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000);
}
static void
lml33_init (struct zoran *zr)
{
dprintk(3, KERN_DEBUG "%s: lml33_init()\n", ZR_DEVNAME(zr));
GPIO(zr, 2, 1); // Set Composite input/output
}
static char *
i2cid_to_modulename (u16 i2c_id)
{
char *name = NULL;
switch (i2c_id) {
case I2C_DRIVERID_SAA7110:
name = "saa7110";
break;
case I2C_DRIVERID_SAA7111A:
name = "saa7111";
break;
case I2C_DRIVERID_SAA7114:
name = "saa7114";
break;
case I2C_DRIVERID_SAA7185B:
name = "saa7185";
break;
case I2C_DRIVERID_ADV7170:
name = "adv7170";
break;
case I2C_DRIVERID_ADV7175:
name = "adv7175";
break;
case I2C_DRIVERID_BT819:
name = "bt819";
break;
case I2C_DRIVERID_BT856:
name = "bt856";
break;
case I2C_DRIVERID_VPX3220:
name = "vpx3220";
break;
/* case I2C_DRIVERID_VPX3224:
name = "vpx3224";
break;
case I2C_DRIVERID_MSE3000:
name = "mse3000";
break;*/
default:
break;
}
return name;
}
static char *
codecid_to_modulename (u16 codecid)
{
char *name = NULL;
switch (codecid) {
case CODEC_TYPE_ZR36060:
name = "zr36060";
break;
case CODEC_TYPE_ZR36050:
name = "zr36050";
break;
case CODEC_TYPE_ZR36016:
name = "zr36016";
break;
default:
break;
}
return name;
}
// struct tvnorm {
// u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
// };
static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 };
static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 };
static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 };
static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 };
static struct tvnorm f50ccir601_lml33 = { 864, 720, 75+34, 804, 625, 576, 18 };
static struct tvnorm f60ccir601_lml33 = { 858, 720, 57+34, 788, 525, 480, 16 };
/* The DC10 (57/16/50) uses VActive as HSync, so HStart must be 0 */
static struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 };
static struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 };
/* FIXME: I cannot swap U and V in saa7114, so i do one
* pixel left shift in zoran (75 -> 74)
* (Maxim Yevtyushkin <max@linuxmedialabs.com>) */
static struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74+54, 804, 625, 576, 18 };
static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 };
static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
{
.type = DC10_old,
.name = "DC10(old)",
.i2c_decoder = I2C_DRIVERID_VPX3220,
/*.i2c_encoder = I2C_DRIVERID_MSE3000,*/
.video_codec = CODEC_TYPE_ZR36050,
.video_vfe = CODEC_TYPE_ZR36016,
.inputs = 3,
.input = {
{ 1, "Composite" },
{ 2, "S-Video" },
{ 0, "Internal/comp" }
},
.norms = 3,
.tvn = {
&f50sqpixel_dc10,
&f60sqpixel_dc10,
&f50sqpixel_dc10
},
.jpeg_int = 0,
.vsync_int = ZR36057_ISR_GIRQ1,
.gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
.gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
.gpcs = { -1, 0 },
.vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
.gws_not_connected = 0,
.init = &dc10_init,
}, {
.type = DC10_new,
.name = "DC10(new)",
.i2c_decoder = I2C_DRIVERID_SAA7110,
.i2c_encoder = I2C_DRIVERID_ADV7175,
.video_codec = CODEC_TYPE_ZR36060,
.inputs = 3,
.input = {
{ 0, "Composite" },
{ 7, "S-Video" },
{ 5, "Internal/comp" }
},
.norms = 3,
.tvn = {
&f50sqpixel,
&f60sqpixel,
&f50sqpixel},
.jpeg_int = ZR36057_ISR_GIRQ0,
.vsync_int = ZR36057_ISR_GIRQ1,
.gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
.gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
.gpcs = { -1, 1},
.vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
.gws_not_connected = 0,
.init = &dc10plus_init,
}, {
.type = DC10plus,
.name = "DC10plus",
.vendor_id = PCI_VENDOR_ID_MIRO,
.device_id = PCI_DEVICE_ID_MIRO_DC10PLUS,
.i2c_decoder = I2C_DRIVERID_SAA7110,
.i2c_encoder = I2C_DRIVERID_ADV7175,
.video_codec = CODEC_TYPE_ZR36060,
.inputs = 3,
.input = {
{ 0, "Composite" },
{ 7, "S-Video" },
{ 5, "Internal/comp" }
},
.norms = 3,
.tvn = {
&f50sqpixel,
&f60sqpixel,
&f50sqpixel
},
.jpeg_int = ZR36057_ISR_GIRQ0,
.vsync_int = ZR36057_ISR_GIRQ1,
.gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
.gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
.gpcs = { -1, 1 },
.vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
.gws_not_connected = 0,
.init = &dc10plus_init,
}, {
.type = DC30,
.name = "DC30",
.i2c_decoder = I2C_DRIVERID_VPX3220,
.i2c_encoder = I2C_DRIVERID_ADV7175,
.video_codec = CODEC_TYPE_ZR36050,
.video_vfe = CODEC_TYPE_ZR36016,
.inputs = 3,
.input = {
{ 1, "Composite" },
{ 2, "S-Video" },
{ 0, "Internal/comp" }
},
.norms = 3,
.tvn = {
&f50sqpixel_dc10,
&f60sqpixel_dc10,
&f50sqpixel_dc10
},
.jpeg_int = 0,
.vsync_int = ZR36057_ISR_GIRQ1,
.gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
.gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
.gpcs = { -1, 0 },
.vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
.gws_not_connected = 0,
.init = &dc10_init,
}, {
.type = DC30plus,
.name = "DC30plus",
.vendor_id = PCI_VENDOR_ID_MIRO,
.device_id = PCI_DEVICE_ID_MIRO_DC30PLUS,
.i2c_decoder = I2C_DRIVERID_VPX3220,
.i2c_encoder = I2C_DRIVERID_ADV7175,
.video_codec = CODEC_TYPE_ZR36050,
.video_vfe = CODEC_TYPE_ZR36016,
.inputs = 3,
.input = {
{ 1, "Composite" },
{ 2, "S-Video" },
{ 0, "Internal/comp" }
},
.norms = 3,
.tvn = {
&f50sqpixel_dc10,
&f60sqpixel_dc10,
&f50sqpixel_dc10
},
.jpeg_int = 0,
.vsync_int = ZR36057_ISR_GIRQ1,
.gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
.gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
.gpcs = { -1, 0 },
.vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
.gws_not_connected = 0,
.init = &dc10_init,
}, {
.type = LML33,
.name = "LML33",
.i2c_decoder = I2C_DRIVERID_BT819,
.i2c_encoder = I2C_DRIVERID_BT856,
.video_codec = CODEC_TYPE_ZR36060,
.inputs = 2,
.input = {
{ 0, "Composite" },
{ 7, "S-Video" }
},
.norms = 2,
.tvn = {
&f50ccir601_lml33,
&f60ccir601_lml33,
NULL
},
.jpeg_int = ZR36057_ISR_GIRQ1,
.vsync_int = ZR36057_ISR_GIRQ0,
.gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
.gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
.gpcs = { 3, 1 },
.vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
.gws_not_connected = 1,
.init = &lml33_init,
}, {
.type = LML33R10,
.name = "LML33R10",
.vendor_id = PCI_VENDOR_ID_ELECTRONICDESIGNGMBH,
.device_id = PCI_DEVICE_ID_LML_33R10,
.i2c_decoder = I2C_DRIVERID_SAA7114,
.i2c_encoder = I2C_DRIVERID_ADV7170,
.video_codec = CODEC_TYPE_ZR36060,
.inputs = 2,
.input = {
{ 0, "Composite" },
{ 7, "S-Video" }
},
.norms = 2,
.tvn = {
&f50ccir601_lm33r10,
&f60ccir601_lm33r10,
NULL
},
.jpeg_int = ZR36057_ISR_GIRQ1,
.vsync_int = ZR36057_ISR_GIRQ0,
.gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
.gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
.gpcs = { 3, 1 },
.vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
.gws_not_connected = 1,
.init = &lml33_init,
}, {
.type = BUZ,
.name = "Buz",
.vendor_id = PCI_VENDOR_ID_IOMEGA,
.device_id = PCI_DEVICE_ID_IOMEGA_BUZ,
.i2c_decoder = I2C_DRIVERID_SAA7111A,
.i2c_encoder = I2C_DRIVERID_SAA7185B,
.video_codec = CODEC_TYPE_ZR36060,
.inputs = 2,
.input = {
{ 3, "Composite" },
{ 7, "S-Video" }
},
.norms = 3,
.tvn = {
&f50ccir601,
&f60ccir601,
&f50ccir601
},
.jpeg_int = ZR36057_ISR_GIRQ1,
.vsync_int = ZR36057_ISR_GIRQ0,
.gpio = { 1, -1, 3, -1, -1, -1, -1, -1 },
.gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
.gpcs = { 3, 1 },
.vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
.gws_not_connected = 1,
.init = &buz_init,
}
};
/*
* I2C functions
*/
/* software I2C functions */
static int
zoran_i2c_getsda (void *data)
{
struct zoran *zr = (struct zoran *) data;
return (btread(ZR36057_I2CBR) >> 1) & 1;
}
static int
zoran_i2c_getscl (void *data)
{
struct zoran *zr = (struct zoran *) data;
return btread(ZR36057_I2CBR) & 1;
}
static void
zoran_i2c_setsda (void *data,
int state)
{
struct zoran *zr = (struct zoran *) data;
if (state)
zr->i2cbr |= 2;
else
zr->i2cbr &= ~2;
btwrite(zr->i2cbr, ZR36057_I2CBR);
}
static void
zoran_i2c_setscl (void *data,
int state)
{
struct zoran *zr = (struct zoran *) data;
if (state)
zr->i2cbr |= 1;
else
zr->i2cbr &= ~1;
btwrite(zr->i2cbr, ZR36057_I2CBR);
}
static int
zoran_i2c_client_register (struct i2c_client *client)
{
struct zoran *zr = (struct zoran *) i2c_get_adapdata(client->adapter);
int res = 0;
dprintk(2,
KERN_DEBUG "%s: i2c_client_register() - driver id = %d\n",
ZR_DEVNAME(zr), client->driver->id);
down(&zr->resource_lock);
if (zr->user > 0) {
/* we're already busy, so we keep a reference to
* them... Could do a lot of stuff here, but this
* is easiest. (Did I ever mention I'm a lazy ass?)
*/
res = -EBUSY;
goto clientreg_unlock_and_return;
}
if (client->driver->id == zr->card.i2c_decoder)
zr->decoder = client;
else if (client->driver->id == zr->card.i2c_encoder)
zr->encoder = client;
else {
res = -ENODEV;
goto clientreg_unlock_and_return;
}
clientreg_unlock_and_return:
up(&zr->resource_lock);
return res;
}
static int
zoran_i2c_client_unregister (struct i2c_client *client)
{
struct zoran *zr = (struct zoran *) i2c_get_adapdata(client->adapter);
int res = 0;
dprintk(2, KERN_DEBUG "%s: i2c_client_unregister()\n", ZR_DEVNAME(zr));
down(&zr->resource_lock);
if (zr->user > 0) {
res = -EBUSY;
goto clientunreg_unlock_and_return;
}
/* try to locate it */
if (client == zr->encoder) {
zr->encoder = NULL;
} else if (client == zr->decoder) {
zr->decoder = NULL;
snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%d]", zr->id);
}
clientunreg_unlock_and_return:
up(&zr->resource_lock);
return res;
}
static struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
.setsda = zoran_i2c_setsda,
.setscl = zoran_i2c_setscl,
.getsda = zoran_i2c_getsda,
.getscl = zoran_i2c_getscl,
.udelay = 10,
.mdelay = 0,
.timeout = 100,
};
static struct i2c_adapter zoran_i2c_adapter_template = {
I2C_DEVNAME("zr36057"),
.id = I2C_HW_B_ZR36067,
.algo = NULL,
.client_register = zoran_i2c_client_register,
.client_unregister = zoran_i2c_client_unregister,
};
static int
zoran_register_i2c (struct zoran *zr)
{
memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template,
sizeof(struct i2c_algo_bit_data));
zr->i2c_algo.data = zr;
memcpy(&zr->i2c_adapter, &zoran_i2c_adapter_template,
sizeof(struct i2c_adapter));
strncpy(I2C_NAME(&zr->i2c_adapter), ZR_DEVNAME(zr),
sizeof(I2C_NAME(&zr->i2c_adapter)) - 1);
i2c_set_adapdata(&zr->i2c_adapter, zr);
zr->i2c_adapter.algo_data = &zr->i2c_algo;
return i2c_bit_add_bus(&zr->i2c_adapter);
}
static void
zoran_unregister_i2c (struct zoran *zr)
{
i2c_bit_del_bus((&zr->i2c_adapter));
}
/* Check a zoran_params struct for correctness, insert default params */
int
zoran_check_jpg_settings (struct zoran *zr,
struct zoran_jpg_settings *settings)
{
int err = 0, err0 = 0;
dprintk(4,
KERN_DEBUG
"%s: check_jpg_settings() - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n",
ZR_DEVNAME(zr), settings->decimation, settings->HorDcm,
settings->VerDcm, settings->TmpDcm);
dprintk(4,
KERN_DEBUG
"%s: check_jpg_settings() - x: %d, y: %d, w: %d, y: %d\n",
ZR_DEVNAME(zr), settings->img_x, settings->img_y,
settings->img_width, settings->img_height);
/* Check decimation, set default values for decimation = 1, 2, 4 */
switch (settings->decimation) {
case 1:
settings->HorDcm = 1;
settings->VerDcm = 1;
settings->TmpDcm = 1;
settings->field_per_buff = 2;
settings->img_x = 0;
settings->img_y = 0;
settings->img_width = BUZ_MAX_WIDTH;
settings->img_height = BUZ_MAX_HEIGHT / 2;
break;
case 2:
settings->HorDcm = 2;
settings->VerDcm = 1;
settings->TmpDcm = 2;
settings->field_per_buff = 1;
settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
settings->img_y = 0;
settings->img_width =
(BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
settings->img_height = BUZ_MAX_HEIGHT / 2;
break;
case 4:
if (zr->card.type == DC10_new) {
dprintk(1,
KERN_DEBUG
"%s: check_jpg_settings() - HDec by 4 is not supported on the DC10\n",
ZR_DEVNAME(zr));
err0++;
break;
}
settings->HorDcm = 4;
settings->VerDcm = 2;
settings->TmpDcm = 2;
settings->field_per_buff = 1;
settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
settings->img_y = 0;
settings->img_width =
(BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
settings->img_height = BUZ_MAX_HEIGHT / 2;
break;
case 0:
/* We have to check the data the user has set */
if (settings->HorDcm != 1 && settings->HorDcm != 2 &&
(zr->card.type == DC10_new || settings->HorDcm != 4))
err0++;
if (settings->VerDcm != 1 && settings->VerDcm != 2)
err0++;
if (settings->TmpDcm != 1 && settings->TmpDcm != 2)
err0++;
if (settings->field_per_buff != 1 &&
settings->field_per_buff != 2)
err0++;
if (settings->img_x < 0)
err0++;
if (settings->img_y < 0)
err0++;
if (settings->img_width < 0)
err0++;
if (settings->img_height < 0)
err0++;
if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH)
err0++;
if (settings->img_y + settings->img_height >
BUZ_MAX_HEIGHT / 2)
err0++;
if (settings->HorDcm && settings->VerDcm) {
if (settings->img_width %
(16 * settings->HorDcm) != 0)
err0++;
if (settings->img_height %
(8 * settings->VerDcm) != 0)
err0++;
}
if (err0) {
dprintk(1,
KERN_ERR
"%s: check_jpg_settings() - error in params for decimation = 0\n",
ZR_DEVNAME(zr));
err++;
}
break;
default:
dprintk(1,
KERN_ERR
"%s: check_jpg_settings() - decimation = %d, must be 0, 1, 2 or 4\n",
ZR_DEVNAME(zr), settings->decimation);
err++;
break;
}
if (settings->jpg_comp.quality > 100)
settings->jpg_comp.quality = 100;
if (settings->jpg_comp.quality < 5)
settings->jpg_comp.quality = 5;
if (settings->jpg_comp.APPn < 0)
settings->jpg_comp.APPn = 0;
if (settings->jpg_comp.APPn > 15)
settings->jpg_comp.APPn = 15;
if (settings->jpg_comp.APP_len < 0)
settings->jpg_comp.APP_len = 0;
if (settings->jpg_comp.APP_len > 60)
settings->jpg_comp.APP_len = 60;
if (settings->jpg_comp.COM_len < 0)
settings->jpg_comp.COM_len = 0;
if (settings->jpg_comp.COM_len > 60)
settings->jpg_comp.COM_len = 60;
if (err)
return -EINVAL;
return 0;
}
void
zoran_open_init_params (struct zoran *zr)
{
int i;
/* User must explicitly set a window */
zr->overlay_settings.is_set = 0;
zr->overlay_mask = NULL;
zr->overlay_active = ZORAN_FREE;
zr->v4l_memgrab_active = 0;
zr->v4l_overlay_active = 0;
zr->v4l_grab_frame = NO_GRAB_ACTIVE;
zr->v4l_grab_seq = 0;
zr->v4l_settings.width = 192;
zr->v4l_settings.height = 144;
zr->v4l_settings.format = &zoran_formats[4]; /* YUY2 - YUV-4:2:2 packed */
zr->v4l_settings.bytesperline =
zr->v4l_settings.width *
((zr->v4l_settings.format->depth + 7) / 8);
/* DMA ring stuff for V4L */
zr->v4l_pend_tail = 0;
zr->v4l_pend_head = 0;
zr->v4l_sync_tail = 0;
zr->v4l_buffers.active = ZORAN_FREE;
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
}
zr->v4l_buffers.allocated = 0;
for (i = 0; i < BUZ_MAX_FRAME; i++) {
zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
}
zr->jpg_buffers.active = ZORAN_FREE;
zr->jpg_buffers.allocated = 0;
/* Set necessary params and call zoran_check_jpg_settings to set the defaults */
zr->jpg_settings.decimation = 1;
zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */
if (zr->card.type != BUZ)
zr->jpg_settings.odd_even = 1;
else
zr->jpg_settings.odd_even = 0;
zr->jpg_settings.jpg_comp.APPn = 0;
zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */
memset(zr->jpg_settings.jpg_comp.APP_data, 0,
sizeof(zr->jpg_settings.jpg_comp.APP_data));
zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */
memset(zr->jpg_settings.jpg_comp.COM_data, 0,
sizeof(zr->jpg_settings.jpg_comp.COM_data));
zr->jpg_settings.jpg_comp.jpeg_markers =
JPEG_MARKER_DHT | JPEG_MARKER_DQT;
i = zoran_check_jpg_settings(zr, &zr->jpg_settings);
if (i)
dprintk(1,
KERN_ERR
"%s: zoran_open_init_params() internal error\n",
ZR_DEVNAME(zr));
clear_interrupt_counters(zr);
zr->testing = 0;
}
static void __devinit
test_interrupts (struct zoran *zr)
{
int timeout, icr;
clear_interrupt_counters(zr);
zr->testing = 1;
icr = btread(ZR36057_ICR);
btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR);
timeout = interruptible_sleep_on_timeout(&zr->test_q, 1 * HZ);
btwrite(0, ZR36057_ICR);
btwrite(0x78000000, ZR36057_ISR);
zr->testing = 0;
dprintk(5, KERN_INFO "%s: Testing interrupts...\n", ZR_DEVNAME(zr));
if (timeout) {
dprintk(1, ": time spent: %d\n", 1 * HZ - timeout);
}
if (debug > 1)
print_interrupts(zr);
btwrite(icr, ZR36057_ICR);
}
static int __devinit
zr36057_init (struct zoran *zr)
{
unsigned long mem;
unsigned mem_needed;
int j;
int two = 2;
int zero = 0;
dprintk(1,
KERN_INFO
"%s: zr36057_init() - initializing card[%d], zr=%p\n",
ZR_DEVNAME(zr), zr->id, zr);
/* default setup of all parameters which will persist between opens */
zr->user = 0;
init_waitqueue_head(&zr->v4l_capq);
init_waitqueue_head(&zr->jpg_capq);
init_waitqueue_head(&zr->test_q);
zr->jpg_buffers.allocated = 0;
zr->v4l_buffers.allocated = 0;
zr->buffer.base = (void *) vidmem;
zr->buffer.width = 0;
zr->buffer.height = 0;
zr->buffer.depth = 0;
zr->buffer.bytesperline = 0;
/* Avoid nonsense settings from user for default input/norm */
if (default_norm < VIDEO_MODE_PAL &&
default_norm > VIDEO_MODE_SECAM)
default_norm = VIDEO_MODE_PAL;
zr->norm = default_norm;
if (!(zr->timing = zr->card.tvn[zr->norm])) {
dprintk(1,
KERN_WARNING
"%s: zr36057_init() - default TV standard not supported by hardware. PAL will be used.\n",
ZR_DEVNAME(zr));
zr->norm = VIDEO_MODE_PAL;
zr->timing = zr->card.tvn[zr->norm];
}
zr->input = default_input = (default_input ? 1 : 0);
/* Should the following be reset at every open ? */
zr->hue = 32768;
zr->contrast = 32768;
zr->saturation = 32768;
zr->brightness = 32768;
/* default setup (will be repeated at every open) */
zoran_open_init_params(zr);
/* allocate memory *before* doing anything to the hardware
* in case allocation fails */
mem_needed = BUZ_NUM_STAT_COM * 4;
mem = (unsigned long) kmalloc(mem_needed, GFP_KERNEL);
if (!mem) {
dprintk(1,
KERN_ERR
"%s: zr36057_init() - kmalloc (STAT_COM) failed\n",
ZR_DEVNAME(zr));
return -ENOMEM;
}
memset((void *) mem, 0, mem_needed);
zr->stat_com = (u32 *) mem;
for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
zr->stat_com[j] = 1; /* mark as unavailable to zr36057 */
}
/*
* Now add the template and register the device unit.
*/
memcpy(&zr->video_dev, &zoran_template, sizeof(zoran_template));
strcpy(zr->video_dev.name, ZR_DEVNAME(zr));
if (video_register_device
(&zr->video_dev, VFL_TYPE_GRABBER, video_nr) < 0) {
zoran_unregister_i2c(zr);
kfree((void *) zr->stat_com);
return -1;
}
zoran_init_hardware(zr);
if (debug > 2)
detect_guest_activity(zr);
test_interrupts(zr);
if (!pass_through) {
decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero);
encoder_command(zr, ENCODER_SET_INPUT, &two);
}
zr->zoran_proc = NULL;
zr->initialized = 1;
return 0;
}
static void
zoran_release (struct zoran *zr)
{
if (!zr->initialized)
return;
/* unregister videocodec bus */
if (zr->codec) {
struct videocodec_master *master = zr->codec->master_data;
videocodec_detach(zr->codec);
if (master)
kfree(master);
}
if (zr->vfe) {
struct videocodec_master *master = zr->vfe->master_data;
videocodec_detach(zr->vfe);
if (master)
kfree(master);
}
/* unregister i2c bus */
zoran_unregister_i2c(zr);
/* disable PCI bus-mastering */
zoran_set_pci_master(zr, 0);
/* put chip into reset */
btwrite(0, ZR36057_SPGPPCR);
free_irq(zr->pci_dev->irq, zr);
/* unmap and free memory */
kfree((void *) zr->stat_com);
zoran_proc_cleanup(zr);
iounmap(zr->zr36057_mem);
video_unregister_device(&zr->video_dev);
}
static struct videocodec_master * __devinit
zoran_setup_videocodec (struct zoran *zr,
int type)
{
struct videocodec_master *m = NULL;
m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL);
if (!m) {
dprintk(1,
KERN_ERR
"%s: zoran_setup_videocodec() - no memory\n",
ZR_DEVNAME(zr));
return m;
}
m->magic = 0L; /* magic not used */
m->type = VID_HARDWARE_ZR36067;
m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER;
strncpy(m->name, ZR_DEVNAME(zr), sizeof(m->name));
m->data = zr;
switch (type)
{
case CODEC_TYPE_ZR36060:
m->readreg = zr36060_read;
m->writereg = zr36060_write;
m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE;
break;
case CODEC_TYPE_ZR36050:
m->readreg = zr36050_read;
m->writereg = zr36050_write;
m->flags |= CODEC_FLAG_JPEG;
break;
case CODEC_TYPE_ZR36016:
m->readreg = zr36016_read;
m->writereg = zr36016_write;
m->flags |= CODEC_FLAG_VFE;
break;
}
return m;
}
/*
* Scan for a Buz card (actually for the PCI contoler ZR36057),
* request the irq and map the io memory
*/
static int __devinit
find_zr36057 (void)
{
unsigned char latency, need_latency;
struct zoran *zr;
struct pci_dev *dev = NULL;
int result;
struct videocodec_master *master_vfe = NULL;
struct videocodec_master *master_codec = NULL;
int card_num;
char *i2c_enc_name, *i2c_dec_name, *codec_name, *vfe_name;
zoran_num = 0;
while (zoran_num < BUZ_MAX &&
(dev =
pci_find_device(PCI_VENDOR_ID_ZORAN,
PCI_DEVICE_ID_ZORAN_36057, dev)) != NULL) {
card_num = card[zoran_num];
zr = &zoran[zoran_num];
memset(zr, 0, sizeof(struct zoran)); // Just in case if previous cycle failed
zr->pci_dev = dev;
//zr->zr36057_mem = NULL;
zr->id = zoran_num;
snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id);
spin_lock_init(&zr->spinlock);
init_MUTEX(&zr->resource_lock);
if (pci_enable_device(dev))
continue;
zr->zr36057_adr = pci_resource_start(zr->pci_dev, 0);
pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION,
&zr->revision);
if (zr->revision < 2) {
dprintk(1,
KERN_INFO
"%s: Zoran ZR36057 (rev %d) irq: %d, memory: 0x%08x.\n",
ZR_DEVNAME(zr), zr->revision, zr->pci_dev->irq,
zr->zr36057_adr);
if (card_num == -1) {
dprintk(1,
KERN_ERR
"%s: find_zr36057() - no card specified, please use the card=X insmod option\n",
ZR_DEVNAME(zr));
continue;
}
} else {
int i;
unsigned short ss_vendor, ss_device;
ss_vendor = zr->pci_dev->subsystem_vendor;
ss_device = zr->pci_dev->subsystem_device;
dprintk(1,
KERN_INFO
"%s: Zoran ZR36067 (rev %d) irq: %d, memory: 0x%08x\n",
ZR_DEVNAME(zr), zr->revision, zr->pci_dev->irq,
zr->zr36057_adr);
dprintk(1,
KERN_INFO
"%s: subsystem vendor=0x%04x id=0x%04x\n",
ZR_DEVNAME(zr), ss_vendor, ss_device);
if (card_num == -1) {
dprintk(3,
KERN_DEBUG
"%s: find_zr36057() - trying to autodetect card type\n",
ZR_DEVNAME(zr));
for (i=0;i<NUM_CARDS;i++) {
if (ss_vendor == zoran_cards[i].vendor_id &&
ss_device == zoran_cards[i].device_id) {
dprintk(3,
KERN_DEBUG
"%s: find_zr36057() - card %s detected\n",
ZR_DEVNAME(zr),
zoran_cards[i].name);
card_num = i;
break;
}
}
if (i == NUM_CARDS) {
dprintk(1,
KERN_ERR
"%s: find_zr36057() - unknown card\n",
ZR_DEVNAME(zr));
continue;
}
}
}
if (card_num < 0 || card_num >= NUM_CARDS) {
dprintk(2,
KERN_ERR
"%s: find_zr36057() - invalid cardnum %d\n",
ZR_DEVNAME(zr), card_num);
continue;
}
/* even though we make this a non pointer and thus
* theoretically allow for making changes to this struct
* on a per-individual card basis at runtime, this is
* strongly discouraged. This structure is intended to
* keep general card information, no settings or anything */
zr->card = zoran_cards[card_num];
snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)),
"%s[%u]", zr->card.name, zr->id);
zr->zr36057_mem = ioremap_nocache(zr->zr36057_adr, 0x1000);
if (!zr->zr36057_mem) {
dprintk(1,
KERN_ERR
"%s: find_zr36057() - ioremap failed\n",
ZR_DEVNAME(zr));
continue;
}
result = request_irq(zr->pci_dev->irq,
zoran_irq,
SA_SHIRQ | SA_INTERRUPT,
ZR_DEVNAME(zr),
(void *) zr);
if (result < 0) {
if (result == -EINVAL) {
dprintk(1,
KERN_ERR
"%s: find_zr36057() - bad irq number or handler\n",
ZR_DEVNAME(zr));
} else if (result == -EBUSY) {
dprintk(1,
KERN_ERR
"%s: find_zr36057() - IRQ %d busy, change your PnP config in BIOS\n",
ZR_DEVNAME(zr), zr->pci_dev->irq);
} else {
dprintk(1,
KERN_ERR
"%s: find_zr36057() - can't assign irq, error code %d\n",
ZR_DEVNAME(zr), result);
}
goto zr_unmap;
}
/* set PCI latency timer */
pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
&latency);
need_latency = zr->revision > 1 ? 32 : 48;
if (latency != need_latency) {
dprintk(2,
KERN_INFO
"%s: Changing PCI latency from %d to %d.\n",
ZR_DEVNAME(zr), latency, need_latency);
pci_write_config_byte(zr->pci_dev,
PCI_LATENCY_TIMER,
need_latency);
}
zr36057_restart(zr);
/* i2c */
dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n",
ZR_DEVNAME(zr));
/* i2c decoder */
if (decoder[zr->id] != -1) {
i2c_dec_name = i2cid_to_modulename(decoder[zr->id]);
zr->card.i2c_decoder = decoder[zr->id];
} else if (zr->card.i2c_decoder != 0) {
i2c_dec_name =
i2cid_to_modulename(zr->card.i2c_decoder);
} else {
i2c_dec_name = NULL;
}
if (i2c_dec_name) {
if ((result = request_module(i2c_dec_name)) < 0) {
dprintk(1,
KERN_ERR
"%s: failed to load module %s: %d\n",
ZR_DEVNAME(zr), i2c_dec_name, result);
}
}
/* i2c encoder */
if (encoder[zr->id] != -1) {
i2c_enc_name = i2cid_to_modulename(encoder[zr->id]);
zr->card.i2c_encoder = encoder[zr->id];
} else if (zr->card.i2c_encoder != 0) {
i2c_enc_name =
i2cid_to_modulename(zr->card.i2c_encoder);
} else {
i2c_enc_name = NULL;
}
if (i2c_enc_name) {
if ((result = request_module(i2c_enc_name)) < 0) {
dprintk(1,
KERN_ERR
"%s: failed to load module %s: %d\n",
ZR_DEVNAME(zr), i2c_enc_name, result);
}
}
if (zoran_register_i2c(zr) < 0) {
dprintk(1,
KERN_ERR
"%s: find_zr36057() - can't initialize i2c bus\n",
ZR_DEVNAME(zr));
goto zr_free_irq;
}
dprintk(2,
KERN_INFO "%s: Initializing videocodec bus...\n",
ZR_DEVNAME(zr));
if (zr->card.video_codec != 0 &&
(codec_name =
codecid_to_modulename(zr->card.video_codec)) != NULL) {
if ((result = request_module(codec_name)) < 0) {
dprintk(1,
KERN_ERR
"%s: failed to load modules %s: %d\n",
ZR_DEVNAME(zr), codec_name, result);
}
}
if (zr->card.video_vfe != 0 &&
(vfe_name =
codecid_to_modulename(zr->card.video_vfe)) != NULL) {
if ((result = request_module(vfe_name)) < 0) {
dprintk(1,
KERN_ERR
"%s: failed to load modules %s: %d\n",
ZR_DEVNAME(zr), vfe_name, result);
}
}
/* reset JPEG codec */
jpeg_codec_sleep(zr, 1);
jpeg_codec_reset(zr);
/* video bus enabled */
/* display codec revision */
if (zr->card.video_codec != 0) {
master_codec = zoran_setup_videocodec(zr,
zr->card.video_codec);
if (!master_codec)
goto zr_unreg_i2c;
zr->codec = videocodec_attach(master_codec);
if (!zr->codec) {
dprintk(1,
KERN_ERR
"%s: find_zr36057() - no codec found\n",
ZR_DEVNAME(zr));
goto zr_free_codec;
}
if (zr->codec->type != zr->card.video_codec) {
dprintk(1,
KERN_ERR
"%s: find_zr36057() - wrong codec\n",
ZR_DEVNAME(zr));
goto zr_detach_codec;
}
}
if (zr->card.video_vfe != 0) {
master_vfe = zoran_setup_videocodec(zr,
zr->card.video_vfe);
if (!master_vfe)
goto zr_detach_codec;
zr->vfe = videocodec_attach(master_vfe);
if (!zr->vfe) {
dprintk(1,
KERN_ERR
"%s: find_zr36057() - no VFE found\n",
ZR_DEVNAME(zr));
goto zr_free_vfe;
}
if (zr->vfe->type != zr->card.video_vfe) {
dprintk(1,
KERN_ERR
"%s: find_zr36057() = wrong VFE\n",
ZR_DEVNAME(zr));
goto zr_detach_vfe;
}
}
zoran_num++;
continue;
// Init errors
zr_detach_vfe:
videocodec_detach(zr->vfe);
zr_free_vfe:
kfree(master_vfe);
zr_detach_codec:
videocodec_detach(zr->codec);
zr_free_codec:
kfree(master_codec);
zr_unreg_i2c:
zoran_unregister_i2c(zr);
zr_free_irq:
btwrite(0, ZR36057_SPGPPCR);
free_irq(zr->pci_dev->irq, zr);
zr_unmap:
iounmap(zr->zr36057_mem);
continue;
}
if (zoran_num == 0) {
dprintk(1, KERN_INFO "No known MJPEG cards found.\n");
}
return zoran_num;
}
static int __init
init_dc10_cards (void)
{
int i;
memset(zoran, 0, sizeof(zoran));
printk(KERN_INFO "Zoran MJPEG board driver version %d.%d.%d\n",
MAJOR_VERSION, MINOR_VERSION, RELEASE_VERSION);
/* Look for cards */
if (find_zr36057() < 0) {
return -EIO;
}
if (zoran_num == 0)
return -ENODEV;
dprintk(1, KERN_INFO "%s: %d card(s) found\n", ZORAN_NAME,
zoran_num);
/* check the parameters we have been given, adjust if necessary */
if (v4l_nbufs < 2)
v4l_nbufs = 2;
if (v4l_nbufs > VIDEO_MAX_FRAME)
v4l_nbufs = VIDEO_MAX_FRAME;
/* The user specfies the in KB, we want them in byte
* (and page aligned) */
v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024);
if (v4l_bufsize < 32768)
v4l_bufsize = 32768;
/* 2 MB is arbitrary but sufficient for the maximum possible images */
if (v4l_bufsize > 2048 * 1024)
v4l_bufsize = 2048 * 1024;
if (jpg_nbufs < 4)
jpg_nbufs = 4;
if (jpg_nbufs > BUZ_MAX_FRAME)
jpg_nbufs = BUZ_MAX_FRAME;
jpg_bufsize = PAGE_ALIGN(jpg_bufsize * 1024);
if (jpg_bufsize < 8192)
jpg_bufsize = 8192;
if (jpg_bufsize > (512 * 1024))
jpg_bufsize = 512 * 1024;
/* Use parameter for vidmem or try to find a video card */
if (vidmem) {
dprintk(1,
KERN_INFO
"%s: Using supplied video memory base address @ 0x%lx\n",
ZORAN_NAME, vidmem);
}
/* random nonsense */
dprintk(5, KERN_DEBUG "Jotti is een held!\n");
/* some mainboards might not do PCI-PCI data transfer well */
if (pci_pci_problems & PCIPCI_FAIL) {
dprintk(1,
KERN_WARNING
"%s: chipset may not support reliable PCI-PCI DMA\n",
ZORAN_NAME);
}
/* take care of Natoma chipset and a revision 1 zr36057 */
for (i = 0; i < zoran_num; i++) {
struct zoran *zr = &zoran[i];
if (pci_pci_problems & PCIPCI_NATOMA && zr->revision <= 1) {
zr->jpg_buffers.need_contiguous = 1;
dprintk(1,
KERN_INFO
"%s: ZR36057/Natoma bug, max. buffer size is 128K\n",
ZR_DEVNAME(zr));
}
if (zr36057_init(zr) < 0) {
for (i = 0; i < zoran_num; i++)
zoran_release(&zoran[i]);
return -EIO;
}
zoran_proc_init(zr);
}
return 0;
}
static void __exit
unload_dc10_cards (void)
{
int i;
for (i = 0; i < zoran_num; i++)
zoran_release(&zoran[i]);
}
module_init(init_dc10_cards);
module_exit(unload_dc10_cards);
/*
* Zoran zr36057/zr36067 PCI controller driver, for the
* Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
* Media Labs LML33/LML33R10.
*
* This part handles card-specific data and detection
*
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
*
* Currently maintained by:
* Ronald Bultje <rbultje@ronald.bitfreak.net>
* Laurent Pinchart <laurent.pinchart@skynet.be>
* Mailinglist <mjpeg-users@lists.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ZORAN_CARD_H__
#define __ZORAN_CARD_H__
/* Anybody who uses more than four? */
#define BUZ_MAX 4
extern int zoran_num;
extern struct zoran zoran[BUZ_MAX];
extern struct video_device zoran_template;
extern int zoran_check_jpg_settings(struct zoran *zr,
struct zoran_jpg_settings *settings);
extern void zoran_open_init_params(struct zoran *zr);
#endif /* __ZORAN_CARD_H__ */
/*
* Zoran zr36057/zr36067 PCI controller driver, for the
* Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
* Media Labs LML33/LML33R10.
*
* This part handles device access (PCI/I2C/codec/...)
*
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
*
* Currently maintained by:
* Ronald Bultje <rbultje@ronald.bitfreak.net>
* Laurent Pinchart <laurent.pinchart@skynet.be>
* Mailinglist <mjpeg-users@lists.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/videodev.h>
#include <linux/spinlock.h>
#include <linux/sem.h>
#include <linux/pci.h>
#include <linux/video_decoder.h>
#include <linux/video_encoder.h>
#include <linux/delay.h>
#include "videocodec.h"
#include "zoran.h"
#include "zoran_device.h"
#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | \
ZR36057_ISR_GIRQ1 | \
ZR36057_ISR_JPEGRepIRQ )
extern const struct zoran_format zoran_formats[];
extern const int zoran_num_formats;
extern int debug;
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
static int lml33dpath = 0; /* 1 will use digital path in capture
* mode instead of analog. It can be
* used for picture adjustments using
* tool like xawtv while watching image
* on TV monitor connected to the output.
* However, due to absence of 75 Ohm
* load on Bt819 input, there will be
* some image imperfections */
MODULE_PARM(lml33dpath, "i");
MODULE_PARM_DESC(lml33dpath,
"Use digital path capture mode (on LML33 cards)");
/*
* General Purpose I/O and Guest bus access
*/
/*
* This is a bit tricky. When a board lacks a GPIO function, the corresponding
* GPIO bit number in the card_info structure is set to 0.
*/
void
GPIO (struct zoran *zr,
int bit,
unsigned int value)
{
u32 reg;
u32 mask;
/* Make sure the bit number is legal
* A bit number of -1 (lacking) gives a mask of 0,
* making it harmless */
mask = (1 << (24 + bit)) & 0xff000000;
reg = btread(ZR36057_GPPGCR1) & ~mask;
if (value) {
reg |= mask;
}
btwrite(reg, ZR36057_GPPGCR1);
udelay(1);
}
/*
* Wait til post office is no longer busy
*/
int
post_office_wait (struct zoran *zr)
{
u32 por;
// while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) {
while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) {
/* wait for something to happen */
}
if ((por & ZR36057_POR_POTime) && !zr->card.gws_not_connected) {
/* In LML33/BUZ \GWS line is not connected, so it has always timeout set */
dprintk(1, KERN_INFO "%s: pop timeout %08x\n", ZR_DEVNAME(zr),
por);
return -1;
}
return 0;
}
int
post_office_write (struct zoran *zr,
unsigned int guest,
unsigned int reg,
unsigned int value)
{
u32 por;
por =
ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) |
((reg & 7) << 16) | (value & 0xFF);
btwrite(por, ZR36057_POR);
return post_office_wait(zr);
}
int
post_office_read (struct zoran *zr,
unsigned int guest,
unsigned int reg)
{
u32 por;
por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16);
btwrite(por, ZR36057_POR);
if (post_office_wait(zr) < 0) {
return -1;
}
return btread(ZR36057_POR) & 0xFF;
}
/*
* detect guests
*/
static void
dump_guests (struct zoran *zr)
{
if (debug > 2) {
int i, guest[8];
for (i = 1; i < 8; i++) { // Don't read jpeg codec here
guest[i] = post_office_read(zr, i, 0);
}
printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr));
for (i = 1; i < 8; i++) {
printk(" 0x%02x", guest[i]);
}
printk("\n");
}
}
static inline unsigned long
get_time (void)
{
struct timeval tv;
do_gettimeofday(&tv);
return (1000000 * tv.tv_sec + tv.tv_usec);
}
void
detect_guest_activity (struct zoran *zr)
{
int timeout, i, j, res, guest[8], guest0[8], change[8][3];
unsigned long t0, t1;
dump_guests(zr);
printk(KERN_INFO "%s: Detecting guests activity, please wait...\n",
ZR_DEVNAME(zr));
for (i = 1; i < 8; i++) { // Don't read jpeg codec here
guest0[i] = guest[i] = post_office_read(zr, i, 0);
}
timeout = 0;
j = 0;
t0 = get_time();
while (timeout < 10000) {
udelay(10);
timeout++;
for (i = 1; (i < 8) && (j < 8); i++) {
res = post_office_read(zr, i, 0);
if (res != guest[i]) {
t1 = get_time();
change[j][0] = (t1 - t0);
t0 = t1;
change[j][1] = i;
change[j][2] = res;
j++;
guest[i] = res;
}
}
if (j >= 8)
break;
}
printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr));
for (i = 1; i < 8; i++) {
printk(" 0x%02x", guest0[i]);
}
printk("\n");
if (j == 0) {
printk(KERN_INFO "%s: No activity detected.\n", ZR_DEVNAME(zr));
return;
}
for (i = 0; i < j; i++) {
printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", ZR_DEVNAME(zr),
change[i][0], change[i][1], change[i][2]);
}
}
/*
* JPEG Codec access
*/
void
jpeg_codec_sleep (struct zoran *zr,
int sleep)
{
GPIO(zr, zr->card.gpio[GPIO_JPEG_SLEEP], !sleep);
if (!sleep) {
dprintk(3,
KERN_DEBUG
"%s: jpeg_codec_sleep() - wake GPIO=0x%08x\n",
ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1));
udelay(500);
} else {
dprintk(3,
KERN_DEBUG
"%s: jpeg_codec_sleep() - sleep GPIO=0x%08x\n",
ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1));
udelay(2);
}
}
int
jpeg_codec_reset (struct zoran *zr)
{
/* Take the codec out of sleep */
jpeg_codec_sleep(zr, 0);
if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) {
post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0,
0);
udelay(2);
} else {
GPIO(zr, zr->card.gpio[GPIO_JPEG_RESET], 0);
udelay(2);
GPIO(zr, zr->card.gpio[GPIO_JPEG_RESET], 1);
udelay(2);
}
return 0;
}
/*
* Set the registers for the size we have specified. Don't bother
* trying to understand this without the ZR36057 manual in front of
* you [AC].
*
* PS: The manual is free for download in .pdf format from
* www.zoran.com - nicely done those folks.
*/
static void
zr36057_adjust_vfe (struct zoran *zr,
enum zoran_codec_mode mode)
{
u32 reg;
switch (mode) {
case BUZ_MODE_MOTION_DECOMPRESS:
btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
reg = btread(ZR36057_VFEHCR);
if ((reg & (1 << 10)) && zr->card.type != LML33R10) {
reg += ((1 << 10) | 1);
}
btwrite(reg, ZR36057_VFEHCR);
break;
case BUZ_MODE_MOTION_COMPRESS:
case BUZ_MODE_IDLE:
default:
if (zr->norm == VIDEO_MODE_NTSC ||
(zr->card.type == LML33R10 &&
zr->norm == VIDEO_MODE_PAL))
btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
else
btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
reg = btread(ZR36057_VFEHCR);
if (!(reg & (1 << 10)) && zr->card.type != LML33R10) {
reg -= ((1 << 10) | 1);
}
btwrite(reg, ZR36057_VFEHCR);
break;
}
}
/*
* set geometry
*/
static void
zr36057_set_vfe (struct zoran *zr,
int video_width,
int video_height,
const struct zoran_format *format)
{
struct tvnorm *tvn;
unsigned HStart, HEnd, VStart, VEnd;
unsigned DispMode;
unsigned VidWinWid, VidWinHt;
unsigned hcrop1, hcrop2, vcrop1, vcrop2;
unsigned Wa, We, Ha, He;
unsigned X, Y, HorDcm, VerDcm;
u32 reg;
unsigned mask_line_size;
tvn = zr->timing;
Wa = tvn->Wa;
Ha = tvn->Ha;
dprintk(2, KERN_INFO "%s: set_vfe() - width = %d, height = %d\n",
ZR_DEVNAME(zr), video_width, video_height);
if (zr->norm != VIDEO_MODE_PAL &&
zr->norm != VIDEO_MODE_NTSC &&
zr->norm != VIDEO_MODE_SECAM) {
dprintk(1,
KERN_ERR "%s: set_vfe() - norm = %d not valid\n",
ZR_DEVNAME(zr), zr->norm);
return;
}
if (video_width < BUZ_MIN_WIDTH ||
video_height < BUZ_MIN_HEIGHT ||
video_width > Wa || video_height > Ha) {
dprintk(1, KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n",
ZR_DEVNAME(zr), video_width, video_height);
return;
}
/**** zr36057 ****/
/* horizontal */
VidWinWid = video_width;
X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa;
We = (VidWinWid * 64) / X;
HorDcm = 64 - X;
hcrop1 = 2 * ((tvn->Wa - We) / 4);
hcrop2 = tvn->Wa - We - hcrop1;
HStart = tvn->HStart ? tvn->HStart : 1;
/* (Ronald) Original comment:
* "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+"
* this is false. It inverses chroma values on the LML33R10 (so Cr
* suddenly is shown as Cb and reverse, really cool effect if you
* want to see blue faces, not useful otherwise). So don't use |1.
* However, the DC10 has '0' as HStart, but does need |1, so we
* use a dirty check...
*/
HEnd = HStart + tvn->Wa - 1;
HStart += hcrop1;
HEnd -= hcrop2;
reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart)
| ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd);
if (zr->card.vfe_pol.hsync_pol)
reg |= ZR36057_VFEHCR_HSPol;
btwrite(reg, ZR36057_VFEHCR);
/* Vertical */
DispMode = !(video_height > BUZ_MAX_HEIGHT / 2);
VidWinHt = DispMode ? video_height : video_height / 2;
Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha;
He = (VidWinHt * 64) / Y;
VerDcm = 64 - Y;
vcrop1 = (tvn->Ha / 2 - He) / 2;
vcrop2 = tvn->Ha / 2 - He - vcrop1;
VStart = tvn->VStart;
VEnd = VStart + tvn->Ha / 2; // - 1; FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP
VStart += vcrop1;
VEnd -= vcrop2;
reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart)
| ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd);
if (zr->card.vfe_pol.vsync_pol)
reg |= ZR36057_VFEVCR_VSPol;
btwrite(reg, ZR36057_VFEVCR);
/* scaler and pixel format */
reg = 0;
reg |= (HorDcm << ZR36057_VFESPFR_HorDcm);
reg |= (VerDcm << ZR36057_VFESPFR_VerDcm);
reg |= (DispMode << ZR36057_VFESPFR_DispMode);
if (format->palette != VIDEO_PALETTE_YUV422)
reg |= ZR36057_VFESPFR_LittleEndian;
/* RJ: I don't know, why the following has to be the opposite
* of the corresponding ZR36060 setting, but only this way
* we get the correct colors when uncompressing to the screen */
//reg |= ZR36057_VFESPFR_VCLKPol; /**/
/* RJ: Don't know if that is needed for NTSC also */
if (zr->norm != VIDEO_MODE_NTSC)
reg |= ZR36057_VFESPFR_ExtFl; // NEEDED!!!!!!! Wolfgang
reg |= ZR36057_VFESPFR_TopField;
switch (format->palette) {
case VIDEO_PALETTE_YUV422:
reg |= ZR36057_VFESPFR_YUV422;
break;
case VIDEO_PALETTE_RGB555:
reg |= ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ErrDif;
break;
case VIDEO_PALETTE_RGB565:
reg |= ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ErrDif;
break;
case VIDEO_PALETTE_RGB24:
reg |= ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_Pack24;
break;
case VIDEO_PALETTE_RGB32:
reg |= ZR36057_VFESPFR_RGB888;
break;
default:
dprintk(1,
KERN_INFO "%s: set_vfe() - unknown color_fmt=%x\n",
ZR_DEVNAME(zr), format->palette);
return;
}
if (HorDcm >= 48) {
reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */
} else if (HorDcm >= 32) {
reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */
} else if (HorDcm >= 16) {
reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */
}
btwrite(reg, ZR36057_VFESPFR);
/* display configuration */
reg = (16 << ZR36057_VDCR_MinPix)
| (VidWinHt << ZR36057_VDCR_VidWinHt)
| (VidWinWid << ZR36057_VDCR_VidWinWid);
if (pci_pci_problems & PCIPCI_TRITON)
// || zr->revision < 1) // Revision 1 has also Triton support
reg &= ~ZR36057_VDCR_Triton;
else
reg |= ZR36057_VDCR_Triton;
btwrite(reg, ZR36057_VDCR);
/* (Ronald) don't write this if overlay_mask = NULL */
if (zr->overlay_mask) {
/* Write overlay clipping mask data, but don't enable overlay clipping */
/* RJ: since this makes only sense on the screen, we use
* zr->overlay_settings.width instead of video_width */
mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
reg = virt_to_bus(zr->overlay_mask);
btwrite(reg, ZR36057_MMTR);
reg = virt_to_bus(zr->overlay_mask + mask_line_size);
btwrite(reg, ZR36057_MMBR);
reg =
mask_line_size - (zr->overlay_settings.width +
31) / 32;
if (DispMode == 0)
reg += mask_line_size;
reg <<= ZR36057_OCR_MaskStride;
btwrite(reg, ZR36057_OCR);
}
zr36057_adjust_vfe(zr, zr->codec_mode);
}
/*
* Switch overlay on or off
*/
void
zr36057_overlay (struct zoran *zr,
int on)
{
u32 reg;
if (on) {
/* do the necessary settings ... */
btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */
zr36057_set_vfe(zr,
zr->overlay_settings.width,
zr->overlay_settings.height,
zr->overlay_settings.format);
/* Start and length of each line MUST be 4-byte aligned.
* This should be allready checked before the call to this routine.
* All error messages are internal driver checking only! */
/* video display top and bottom registers */
reg = (u32) zr->buffer.base +
zr->overlay_settings.x *
((zr->overlay_settings.format->depth + 7) / 8) +
zr->overlay_settings.y *
zr->buffer.bytesperline;
btwrite(reg, ZR36057_VDTR);
if (reg & 3)
dprintk(1,
KERN_ERR
"%s: zr36057_overlay() - video_address not aligned\n",
ZR_DEVNAME(zr));
if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
reg += zr->buffer.bytesperline;
btwrite(reg, ZR36057_VDBR);
/* video stride, status, and frame grab register */
reg = zr->buffer.bytesperline -
zr->overlay_settings.width *
((zr->overlay_settings.format->depth + 7) / 8);
if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
reg += zr->buffer.bytesperline;
if (reg & 3)
dprintk(1,
KERN_ERR
"%s: zr36057_overlay() - video_stride not aligned\n",
ZR_DEVNAME(zr));
reg = (reg << ZR36057_VSSFGR_DispStride);
reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */
btwrite(reg, ZR36057_VSSFGR);
/* Set overlay clipping */
if (zr->overlay_settings.clipcount > 0)
btor(ZR36057_OCR_OvlEnable, ZR36057_OCR);
/* ... and switch it on */
btor(ZR36057_VDCR_VidEn, ZR36057_VDCR);
} else {
/* Switch it off */
btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
}
}
/*
* The overlay mask has one bit for each pixel on a scan line,
* and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels.
*/
void
write_overlay_mask (struct file *file,
struct video_clip *vp,
int count)
{
struct zoran_fh *fh = file->private_data;
struct zoran *zr = fh->zr;
unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
u32 *mask;
int x, y, width, height;
unsigned i, j, k;
u32 reg;
/* fill mask with one bits */
memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT);
reg = 0;
for (i = 0; i < count; ++i) {
/* pick up local copy of clip */
x = vp[i].x;
y = vp[i].y;
width = vp[i].width;
height = vp[i].height;
/* trim clips that extend beyond the window */
if (x < 0) {
width += x;
x = 0;
}
if (y < 0) {
height += y;
y = 0;
}
if (x + width > fh->overlay_settings.width) {
width = fh->overlay_settings.width - x;
}
if (y + height > fh->overlay_settings.height) {
height = fh->overlay_settings.height - y;
}
/* ignore degenerate clips */
if (height <= 0) {
continue;
}
if (width <= 0) {
continue;
}
/* apply clip for each scan line */
for (j = 0; j < height; ++j) {
/* reset bit for each pixel */
/* this can be optimized later if need be */
mask = fh->overlay_mask + (y + j) * mask_line_size;
for (k = 0; k < width; ++k) {
mask[(x + k) / 32] &=
~((u32) 1 << (x + k) % 32);
}
}
}
}
/* Enable/Disable uncompressed memory grabbing of the 36057 */
void
zr36057_set_memgrab (struct zoran *zr,
int mode)
{
if (mode) {
if (btread(ZR36057_VSSFGR) &
(ZR36057_VSSFGR_SnapShot | ZR36057_VSSFGR_FrameGrab))
dprintk(1,
KERN_WARNING
"%s: zr36057_set_memgrab(1) with SnapShot or FrameGrab on!?\n",
ZR_DEVNAME(zr));
/* switch on VSync interrupts */
btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts
btor(zr->card.vsync_int, ZR36057_ICR); // SW
/* enable SnapShot */
btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
/* Set zr36057 video front end and enable video */
zr36057_set_vfe(zr, zr->v4l_settings.width,
zr->v4l_settings.height,
zr->v4l_settings.format);
zr->v4l_memgrab_active = 1;
} else {
zr->v4l_memgrab_active = 0;
/* switch off VSync interrupts */
btand(~zr->card.vsync_int, ZR36057_ICR); // SW
/* reenable grabbing to screen if it was running */
if (zr->v4l_overlay_active) {
zr36057_overlay(zr, 1);
} else {
btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
}
}
}
int
wait_grab_pending (struct zoran *zr)
{
unsigned long flags;
/* wait until all pending grabs are finished */
if (!zr->v4l_memgrab_active)
return 0;
while (zr->v4l_pend_tail != zr->v4l_pend_head) {
interruptible_sleep_on(&zr->v4l_capq);
if (signal_pending(current))
return -ERESTARTSYS;
}
spin_lock_irqsave(&zr->spinlock, flags);
zr36057_set_memgrab(zr, 0);
spin_unlock_irqrestore(&zr->spinlock, flags);
return 0;
}
/*****************************************************************************
* *
* Set up the Buz-specific MJPEG part *
* *
*****************************************************************************/
static inline void
set_frame (struct zoran *zr,
int val)
{
GPIO(zr, zr->card.gpio[GPIO_JPEG_FRAME], val);
}
static void
set_videobus_dir (struct zoran *zr,
int val)
{
switch (zr->card.type) {
case LML33:
case LML33R10:
if (lml33dpath == 0)
GPIO(zr, 5, val);
else
GPIO(zr, 5, 1);
break;
default:
GPIO(zr, zr->card.gpio[GPIO_VID_DIR],
zr->card.gpio_pol[GPIO_VID_DIR] ? !val : val);
break;
}
}
static void
init_jpeg_queue (struct zoran *zr)
{
int i;
/* re-initialize DMA ring stuff */
zr->jpg_que_head = 0;
zr->jpg_dma_head = 0;
zr->jpg_dma_tail = 0;
zr->jpg_que_tail = 0;
zr->jpg_seq_num = 0;
zr->JPEG_error = 0;
zr->num_errors = 0;
zr->jpg_err_seq = 0;
zr->jpg_err_shift = 0;
zr->jpg_queued_num = 0;
for (i = 0; i < zr->jpg_buffers.num_buffers; i++) {
zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
}
for (i = 0; i < BUZ_NUM_STAT_COM; i++) {
zr->stat_com[i] = 1; /* mark as unavailable to zr36057 */
}
}
static void
zr36057_set_jpg (struct zoran *zr,
enum zoran_codec_mode mode)
{
struct tvnorm *tvn;
u32 reg;
tvn = zr->timing;
/* assert P_Reset, disable code transfer, deassert Active */
btwrite(0, ZR36057_JPC);
/* MJPEG compression mode */
switch (mode) {
case BUZ_MODE_MOTION_COMPRESS:
default:
reg = ZR36057_JMC_MJPGCmpMode;
break;
case BUZ_MODE_MOTION_DECOMPRESS:
reg = ZR36057_JMC_MJPGExpMode;
reg |= ZR36057_JMC_SyncMstr;
/* RJ: The following is experimental - improves the output to screen */
//if(zr->jpg_settings.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM
break;
case BUZ_MODE_STILL_COMPRESS:
reg = ZR36057_JMC_JPGCmpMode;
break;
case BUZ_MODE_STILL_DECOMPRESS:
reg = ZR36057_JMC_JPGExpMode;
break;
}
reg |= ZR36057_JMC_JPG;
if (zr->jpg_settings.field_per_buff == 1)
reg |= ZR36057_JMC_Fld_per_buff;
btwrite(reg, ZR36057_JMC);
/* vertical */
btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR);
reg = (6 << ZR36057_VSP_VsyncSize) |
(tvn->Ht << ZR36057_VSP_FrmTot);
btwrite(reg, ZR36057_VSP);
reg = ((zr->jpg_settings.img_y + tvn->VStart) << ZR36057_FVAP_NAY) |
(zr->jpg_settings.img_height << ZR36057_FVAP_PAY);
btwrite(reg, ZR36057_FVAP);
/* horizontal */
if (zr->card.vfe_pol.hsync_pol)
btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
else
btand(~ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
reg = ((tvn->HSyncStart) << ZR36057_HSP_HsyncStart) |
(tvn->Wt << ZR36057_HSP_LineTot);
btwrite(reg, ZR36057_HSP);
reg = ((zr->jpg_settings.img_x +
tvn->HStart + 4) << ZR36057_FHAP_NAX) |
(zr->jpg_settings.img_width << ZR36057_FHAP_PAX);
btwrite(reg, ZR36057_FHAP);
/* field process parameters */
if (zr->jpg_settings.odd_even)
reg = ZR36057_FPP_Odd_Even;
else
reg = 0;
btwrite(reg, ZR36057_FPP);
/* Set proper VCLK Polarity, else colors will be wrong during playback */
//btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR);
/* code base address */
reg = virt_to_bus(zr->stat_com);
btwrite(reg, ZR36057_JCBA);
/* FIFO threshold (FIFO is 160. double words) */
/* NOTE: decimal values here */
switch (mode) {
case BUZ_MODE_STILL_COMPRESS:
case BUZ_MODE_MOTION_COMPRESS:
if (zr->card.type != BUZ)
reg = 140;
else
reg = 60;
break;
case BUZ_MODE_STILL_DECOMPRESS:
case BUZ_MODE_MOTION_DECOMPRESS:
reg = 20;
break;
default:
reg = 80;
break;
}
btwrite(reg, ZR36057_JCFT);
zr36057_adjust_vfe(zr, mode);
}
void
print_interrupts (struct zoran *zr)
{
int res, noerr;
noerr = 0;
printk(KERN_INFO "%s: interrupts received:", ZR_DEVNAME(zr));
if ((res = zr->field_counter) < -1 || res > 1) {
printk(" FD:%d", res);
}
if ((res = zr->intr_counter_GIRQ1) != 0) {
printk(" GIRQ1:%d", res);
noerr++;
}
if ((res = zr->intr_counter_GIRQ0) != 0) {
printk(" GIRQ0:%d", res);
noerr++;
}
if ((res = zr->intr_counter_CodRepIRQ) != 0) {
printk(" CodRepIRQ:%d", res);
noerr++;
}
if ((res = zr->intr_counter_JPEGRepIRQ) != 0) {
printk(" JPEGRepIRQ:%d", res);
noerr++;
}
if (zr->JPEG_max_missed) {
printk(" JPEG delays: max=%d min=%d", zr->JPEG_max_missed,
zr->JPEG_min_missed);
}
if (zr->END_event_missed) {
printk(" ENDs missed: %d", zr->END_event_missed);
}
//if (zr->jpg_queued_num) {
printk(" queue_state=%ld/%ld/%ld/%ld", zr->jpg_que_tail,
zr->jpg_dma_tail, zr->jpg_dma_head, zr->jpg_que_head);
//}
if (!noerr) {
printk(": no interrupts detected.");
}
printk("\n");
}
void
clear_interrupt_counters (struct zoran *zr)
{
zr->intr_counter_GIRQ1 = 0;
zr->intr_counter_GIRQ0 = 0;
zr->intr_counter_CodRepIRQ = 0;
zr->intr_counter_JPEGRepIRQ = 0;
zr->field_counter = 0;
zr->IRQ1_in = 0;
zr->IRQ1_out = 0;
zr->JPEG_in = 0;
zr->JPEG_out = 0;
zr->JPEG_0 = 0;
zr->JPEG_1 = 0;
zr->END_event_missed = 0;
zr->JPEG_missed = 0;
zr->JPEG_max_missed = 0;
zr->JPEG_min_missed = 0x7fffffff;
}
static u32
count_reset_interrupt (struct zoran *zr)
{
u32 isr;
if ((isr = btread(ZR36057_ISR) & 0x78000000)) {
if (isr & ZR36057_ISR_GIRQ1) {
btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR);
zr->intr_counter_GIRQ1++;
}
if (isr & ZR36057_ISR_GIRQ0) {
btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR);
zr->intr_counter_GIRQ0++;
}
if (isr & ZR36057_ISR_CodRepIRQ) {
btwrite(ZR36057_ISR_CodRepIRQ, ZR36057_ISR);
zr->intr_counter_CodRepIRQ++;
}
if (isr & ZR36057_ISR_JPEGRepIRQ) {
btwrite(ZR36057_ISR_JPEGRepIRQ, ZR36057_ISR);
zr->intr_counter_JPEGRepIRQ++;
}
}
return isr;
}
/* hack */
extern void zr36016_write (struct videocodec *codec,
u16 reg,
u32 val);
void
jpeg_start (struct zoran *zr)
{
int reg;
zr->frame_num = 0;
/* deassert P_reset, disable code transfer, deassert Active */
btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC);
/* stop flushing the internal code buffer */
btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
/* enable code transfer */
btor(ZR36057_JPC_CodTrnsEn, ZR36057_JPC);
/* clear IRQs */
btwrite(IRQ_MASK, ZR36057_ISR);
/* enable the JPEG IRQs */
btwrite(zr->card.jpeg_int |
ZR36057_ICR_JPEGRepIRQ |
ZR36057_ICR_IntPinEn,
ZR36057_ICR);
set_frame(zr, 0); // \FRAME
/* set the JPEG codec guest ID */
reg = (zr->card.gpcs[1] << ZR36057_JCGI_JPEGuestID) |
(0 << ZR36057_JCGI_JPEGuestReg);
btwrite(reg, ZR36057_JCGI);
if (zr->card.video_vfe == CODEC_TYPE_ZR36016 &&
zr->card.video_codec == CODEC_TYPE_ZR36050) {
/* Enable processing on the ZR36016 */
if (zr->vfe)
zr36016_write(zr->vfe, 0, 1);
/* load the address of the GO register in the ZR36050 latch */
post_office_write(zr, 0, 0, 0);
}
/* assert Active */
btor(ZR36057_JPC_Active, ZR36057_JPC);
/* enable the Go generation */
btor(ZR36057_JMC_Go_en, ZR36057_JMC);
udelay(30);
set_frame(zr, 1); // /FRAME
dprintk(3, KERN_DEBUG "%s: jpeg_start\n", ZR_DEVNAME(zr));
}
void
zr36057_enable_jpg (struct zoran *zr,
enum zoran_codec_mode mode)
{
static int zero = 0;
static int one = 1;
struct vfe_settings cap;
int field_size =
zr->jpg_buffers.buffer_size / zr->jpg_settings.field_per_buff;
zr->codec_mode = mode;
cap.x = zr->jpg_settings.img_x;
cap.y = zr->jpg_settings.img_y;
cap.width = zr->jpg_settings.img_width;
cap.height = zr->jpg_settings.img_height;
cap.decimation =
zr->jpg_settings.HorDcm | (zr->jpg_settings.VerDcm << 8);
cap.quality = zr->jpg_settings.jpg_comp.quality;
switch (mode) {
case BUZ_MODE_MOTION_COMPRESS:
/* In motion compress mode, the decoder output must be enabled, and
* the video bus direction set to input.
*/
set_videobus_dir(zr, 0);
decoder_command(zr, DECODER_ENABLE_OUTPUT, &one);
encoder_command(zr, ENCODER_SET_INPUT, &zero);
/* Take the JPEG codec and the VFE out of sleep */
jpeg_codec_sleep(zr, 0);
/* Setup the JPEG codec */
zr->codec->control(zr->codec, CODEC_S_JPEG_TDS_BYTE,
sizeof(int), &field_size);
zr->codec->set_video(zr->codec, zr->timing, &cap,
&zr->card.vfe_pol);
zr->codec->set_mode(zr->codec, CODEC_DO_COMPRESSION);
/* Setup the VFE */
if (zr->vfe) {
zr->vfe->control(zr->vfe, CODEC_S_JPEG_TDS_BYTE,
sizeof(int), &field_size);
zr->vfe->set_video(zr->vfe, zr->timing, &cap,
&zr->card.vfe_pol);
zr->vfe->set_mode(zr->vfe, CODEC_DO_COMPRESSION);
}
init_jpeg_queue(zr);
zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO
clear_interrupt_counters(zr);
dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_COMPRESS)\n",
ZR_DEVNAME(zr));
break;
case BUZ_MODE_MOTION_DECOMPRESS:
/* In motion decompression mode, the decoder output must be disabled, and
* the video bus direction set to output.
*/
decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero);
set_videobus_dir(zr, 1);
encoder_command(zr, ENCODER_SET_INPUT, &one);
/* Take the JPEG codec and the VFE out of sleep */
jpeg_codec_sleep(zr, 0);
/* Setup the VFE */
if (zr->vfe) {
zr->vfe->set_video(zr->vfe, zr->timing, &cap,
&zr->card.vfe_pol);
zr->vfe->set_mode(zr->vfe, CODEC_DO_EXPANSION);
}
/* Setup the JPEG codec */
zr->codec->set_video(zr->codec, zr->timing, &cap,
&zr->card.vfe_pol);
zr->codec->set_mode(zr->codec, CODEC_DO_EXPANSION);
init_jpeg_queue(zr);
zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO
clear_interrupt_counters(zr);
dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_DECOMPRESS)\n",
ZR_DEVNAME(zr));
break;
case BUZ_MODE_IDLE:
default:
/* shut down processing */
btand(~(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ),
ZR36057_ICR);
btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ,
ZR36057_ISR);
btand(~ZR36057_JMC_Go_en, ZR36057_JMC); // \Go_en
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ / 20);
set_videobus_dir(zr, 0);
set_frame(zr, 1); // /FRAME
btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); // /CFlush
btwrite(0, ZR36057_JPC); // \P_Reset,\CodTrnsEn,\Active
btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC);
btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC);
jpeg_codec_reset(zr);
jpeg_codec_sleep(zr, 1);
zr36057_adjust_vfe(zr, mode);
decoder_command(zr, DECODER_ENABLE_OUTPUT, &one);
encoder_command(zr, ENCODER_SET_INPUT, &zero);
dprintk(2, KERN_INFO "%s: enable_jpg(IDLE)\n", ZR_DEVNAME(zr));
break;
}
}
/* when this is called the spinlock must be held */
void
zoran_feed_stat_com (struct zoran *zr)
{
/* move frames from pending queue to DMA */
int frame, i, max_stat_com;
max_stat_com =
(zr->jpg_settings.TmpDcm ==
1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1);
while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com &&
zr->jpg_dma_head < zr->jpg_que_head) {
frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME];
if (zr->jpg_settings.TmpDcm == 1) {
/* fill 1 stat_com entry */
i = (zr->jpg_dma_head -
zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
if (!(zr->stat_com[i] & 1))
break;
zr->stat_com[i] =
zr->jpg_buffers.buffer[frame].frag_tab_bus;
} else {
/* fill 2 stat_com entries */
i = ((zr->jpg_dma_head -
zr->jpg_err_shift) & 1) * 2;
if (!(zr->stat_com[i] & 1))
break;
zr->stat_com[i] =
zr->jpg_buffers.buffer[frame].frag_tab_bus;
zr->stat_com[i + 1] =
zr->jpg_buffers.buffer[frame].frag_tab_bus;
}
zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA;
zr->jpg_dma_head++;
}
if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS)
zr->jpg_queued_num++;
}
/* when this is called the spinlock must be held */
static void
zoran_reap_stat_com (struct zoran *zr)
{
/* move frames from DMA queue to done queue */
int i;
u32 stat_com;
unsigned int seq;
unsigned int dif;
struct zoran_jpg_buffer *buffer;
int frame;
/* In motion decompress we don't have a hardware frame counter,
* we just count the interrupts here */
if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
zr->jpg_seq_num++;
}
while (zr->jpg_dma_tail < zr->jpg_dma_head) {
if (zr->jpg_settings.TmpDcm == 1)
i = (zr->jpg_dma_tail -
zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
else
i = ((zr->jpg_dma_tail -
zr->jpg_err_shift) & 1) * 2 + 1;
stat_com = zr->stat_com[i];
if ((stat_com & 1) == 0) {
return;
}
frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
buffer = &zr->jpg_buffers.buffer[frame];
do_gettimeofday(&buffer->bs.timestamp);
if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
buffer->bs.length = (stat_com & 0x7fffff) >> 1;
/* update sequence number with the help of the counter in stat_com */
seq = ((stat_com >> 24) + zr->jpg_err_seq) & 0xff;
dif = (seq - zr->jpg_seq_num) & 0xff;
zr->jpg_seq_num += dif;
} else {
buffer->bs.length = 0;
}
buffer->bs.seq =
zr->jpg_settings.TmpDcm ==
2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num;
buffer->state = BUZ_STATE_DONE;
zr->jpg_dma_tail++;
}
}
static void
error_handler (struct zoran *zr,
u32 astat,
u32 stat)
{
/* This is JPEG error handling part */
if ((zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) &&
(zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS)) {
//dprintk(1, KERN_ERR "%s: Internal error: error handling request in mode %d\n", ZR_DEVNAME(zr), zr->codec_mode);
return;
}
if ((stat & 1) == 0 &&
zr->codec_mode == BUZ_MODE_MOTION_COMPRESS &&
zr->jpg_dma_tail - zr->jpg_que_tail >=
zr->jpg_buffers.num_buffers) {
/* No free buffers... */
zoran_reap_stat_com(zr);
zoran_feed_stat_com(zr);
wake_up_interruptible(&zr->jpg_capq);
zr->JPEG_missed = 0;
return;
}
if (zr->JPEG_error != 1) {
/*
* First entry: error just happened during normal operation
*
* In BUZ_MODE_MOTION_COMPRESS:
*
* Possible glitch in TV signal. In this case we should
* stop the codec and wait for good quality signal before
* restarting it to avoid further problems
*
* In BUZ_MODE_MOTION_DECOMPRESS:
*
* Bad JPEG frame: we have to mark it as processed (codec crashed
* and was not able to do it itself), and to remove it from queue.
*/
btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
udelay(1);
stat = stat | (post_office_read(zr, 7, 0) & 3) << 8;
btwrite(0, ZR36057_JPC);
btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
jpeg_codec_reset(zr);
jpeg_codec_sleep(zr, 1);
zr->JPEG_error = 1;
zr->num_errors++;
/* Report error */
if (debug > 1 && zr->num_errors <= 8) {
long frame;
frame =
zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
printk(KERN_ERR
"%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ",
ZR_DEVNAME(zr), stat, zr->last_isr,
zr->jpg_que_tail, zr->jpg_dma_tail,
zr->jpg_dma_head, zr->jpg_que_head,
zr->jpg_seq_num, frame);
printk("stat_com frames:");
{
int i, j;
for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
for (i = 0;
i < zr->jpg_buffers.num_buffers;
i++) {
if (zr->stat_com[j] ==
zr->jpg_buffers.
buffer[i].
frag_tab_bus) {
printk("% d->%d",
j, i);
}
}
}
printk("\n");
}
}
/* Find an entry in stat_com and rotate contents */
{
int i;
if (zr->jpg_settings.TmpDcm == 1)
i = (zr->jpg_dma_tail -
zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
else
i = ((zr->jpg_dma_tail -
zr->jpg_err_shift) & 1) * 2;
if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
/* Mimic zr36067 operation */
zr->stat_com[i] |= 1;
if (zr->jpg_settings.TmpDcm != 1)
zr->stat_com[i + 1] |= 1;
/* Refill */
zoran_reap_stat_com(zr);
zoran_feed_stat_com(zr);
wake_up_interruptible(&zr->jpg_capq);
/* Find an entry in stat_com again after refill */
if (zr->jpg_settings.TmpDcm == 1)
i = (zr->jpg_dma_tail -
zr->jpg_err_shift) &
BUZ_MASK_STAT_COM;
else
i = ((zr->jpg_dma_tail -
zr->jpg_err_shift) & 1) * 2;
}
if (i) {
/* Rotate stat_comm entries to make current entry first */
int j;
u32 bus_addr[BUZ_NUM_STAT_COM];
memcpy(bus_addr, zr->stat_com,
sizeof(bus_addr));
for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
zr->stat_com[j] =
bus_addr[(i + j) &
BUZ_MASK_STAT_COM];
}
zr->jpg_err_shift += i;
zr->jpg_err_shift &= BUZ_MASK_STAT_COM;
}
if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)
zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */
}
}
/* Now the stat_comm buffer is ready for restart */
do {
int status, mode;
if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
decoder_command(zr, DECODER_GET_STATUS, &status);
mode = CODEC_DO_COMPRESSION;
} else {
status = 0;
mode = CODEC_DO_EXPANSION;
}
if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
(status & DECODER_STATUS_GOOD)) {
/********** RESTART code *************/
jpeg_codec_reset(zr);
zr->codec->set_mode(zr->codec, mode);
zr36057_set_jpg(zr, zr->codec_mode);
jpeg_start(zr);
if (zr->num_errors <= 8)
dprintk(2, KERN_INFO "%s: Restart\n",
ZR_DEVNAME(zr));
zr->JPEG_missed = 0;
zr->JPEG_error = 2;
/********** End RESTART code ***********/
}
} while (0);
}
irqreturn_t
zoran_irq (int irq,
void *dev_id,
struct pt_regs *regs)
{
u32 stat, astat;
int count;
struct zoran *zr;
unsigned long flags;
zr = (struct zoran *) dev_id;
count = 0;
if (zr->testing) {
/* Testing interrupts */
spin_lock_irqsave(&zr->spinlock, flags);
while ((stat = count_reset_interrupt(zr))) {
if (count++ > 100) {
btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
dprintk(1,
KERN_ERR
"%s: IRQ lockup while testing, isr=0x%08x, cleared int mask\n",
ZR_DEVNAME(zr), stat);
wake_up_interruptible(&zr->test_q);
}
}
zr->last_isr = stat;
spin_unlock_irqrestore(&zr->spinlock, flags);
return IRQ_HANDLED;
}
spin_lock_irqsave(&zr->spinlock, flags);
while (1) {
/* get/clear interrupt status bits */
stat = count_reset_interrupt(zr);
astat = stat & IRQ_MASK;
if (!astat) {
break;
}
dprintk(4,
KERN_DEBUG
"zoran_irq: astat: 0x%08x, mask: 0x%08x\n",
astat, btread(ZR36057_ICR));
if (astat & zr->card.vsync_int) { // SW
if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
/* count missed interrupts */
zr->JPEG_missed++;
}
//post_office_read(zr,1,0);
/* Interrupts may still happen when
* zr->v4l_memgrab_active is switched off.
* We simply ignore them */
if (zr->v4l_memgrab_active) {
/* A lot more checks should be here ... */
if ((btread(ZR36057_VSSFGR) &
ZR36057_VSSFGR_SnapShot) == 0)
dprintk(1,
KERN_WARNING
"%s: BuzIRQ with SnapShot off ???\n",
ZR_DEVNAME(zr));
if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) {
/* There is a grab on a frame going on, check if it has finished */
if ((btread(ZR36057_VSSFGR) &
ZR36057_VSSFGR_FrameGrab) ==
0) {
/* it is finished, notify the user */
zr->v4l_buffers.buffer[zr->
v4l_grab_frame].
state = BUZ_STATE_DONE;
zr->v4l_buffers.buffer[zr->
v4l_grab_frame].
bs.seq =
zr->v4l_grab_seq;
do_gettimeofday(&zr->
v4l_buffers.
buffer[zr->
v4l_grab_frame].
bs.
timestamp);
zr->v4l_grab_frame =
NO_GRAB_ACTIVE;
zr->v4l_pend_tail++;
}
}
if (zr->v4l_grab_frame == NO_GRAB_ACTIVE)
wake_up_interruptible(&zr->
v4l_capq);
/* Check if there is another grab queued */
if (zr->v4l_grab_frame == NO_GRAB_ACTIVE &&
zr->v4l_pend_tail !=
zr->v4l_pend_head) {
int frame =
zr->v4l_pend[zr->
v4l_pend_tail &
V4L_MASK_FRAME];
u32 reg;
zr->v4l_grab_frame = frame;
/* Set zr36057 video front end and enable video */
/* Buffer address */
reg =
zr->v4l_buffers.buffer[frame].
fbuffer_bus;
btwrite(reg, ZR36057_VDTR);
if (zr->v4l_settings.height >
BUZ_MAX_HEIGHT / 2)
reg +=
zr->v4l_settings.
bytesperline;
btwrite(reg, ZR36057_VDBR);
/* video stride, status, and frame grab register */
reg = 0;
if (zr->v4l_settings.height >
BUZ_MAX_HEIGHT / 2)
reg +=
zr->v4l_settings.
bytesperline;
reg =
(reg <<
ZR36057_VSSFGR_DispStride);
reg |= ZR36057_VSSFGR_VidOvf;
reg |= ZR36057_VSSFGR_SnapShot;
reg |= ZR36057_VSSFGR_FrameGrab;
btwrite(reg, ZR36057_VSSFGR);
btor(ZR36057_VDCR_VidEn,
ZR36057_VDCR);
}
}
/* even if we don't grab, we do want to increment
* the sequence counter to see lost frames */
zr->v4l_grab_seq++;
}
#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ)
if (astat & ZR36057_ISR_CodRepIRQ) {
zr->intr_counter_CodRepIRQ++;
IDEBUG(printk
(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n",
ZR_DEVNAME(zr)));
btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR);
}
#endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */
#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ)
if (astat & ZR36057_ISR_JPEGRepIRQ) {
if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
if (debug > 1 &&
(!zr->frame_num || zr->JPEG_error)) {
printk(KERN_INFO
"%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n",
ZR_DEVNAME(zr), stat,
zr->jpg_settings.odd_even,
zr->jpg_settings.
field_per_buff,
zr->JPEG_missed);
{
char sc[] = "0000";
char sv[5];
int i;
strcpy(sv, sc);
for (i = 0; i < 4; i++) {
if (zr->
stat_com[i] &
1)
sv[i] =
'1';
}
sv[4] = 0;
printk(KERN_INFO
"%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n",
ZR_DEVNAME(zr), sv,
zr->jpg_que_tail,
zr->jpg_dma_tail,
zr->jpg_dma_head,
zr->jpg_que_head);
}
} else {
if (zr->JPEG_missed > zr->JPEG_max_missed) // Get statistics
zr->JPEG_max_missed =
zr->JPEG_missed;
if (zr->JPEG_missed <
zr->JPEG_min_missed)
zr->JPEG_min_missed =
zr->JPEG_missed;
}
if (debug > 2 && zr->frame_num < 6) {
int i;
printk("%s: seq=%ld stat_com:",
ZR_DEVNAME(zr), zr->jpg_seq_num);
for (i = 0; i < 4; i++) {
printk(" %08x",
zr->stat_com[i]);
}
printk("\n");
}
zr->frame_num++;
zr->JPEG_missed = 0;
zr->JPEG_error = 0;
zoran_reap_stat_com(zr);
zoran_feed_stat_com(zr);
wake_up_interruptible(&zr->jpg_capq);
} /*else {
dprintk(1,
KERN_ERR
"%s: JPEG interrupt while not in motion (de)compress mode!\n",
ZR_DEVNAME(zr));
}*/
}
#endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */
/* DATERR, too many fields missed, error processing */
if ((astat & zr->card.jpeg_int) ||
zr->JPEG_missed > 25 ||
zr->JPEG_error == 1 ||
((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) &&
(zr->frame_num & (zr->JPEG_missed >
zr->jpg_settings.field_per_buff)))) {
error_handler(zr, astat, stat);
}
count++;
if (count > 10) {
dprintk(2, KERN_WARNING "%s: irq loop %d\n",
ZR_DEVNAME(zr), count);
if (count > 20) {
btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
dprintk(2,
KERN_ERR
"%s: IRQ lockup, cleared int mask\n",
ZR_DEVNAME(zr));
break;
}
}
zr->last_isr = stat;
}
spin_unlock_irqrestore(&zr->spinlock, flags);
return IRQ_HANDLED;
}
void
zoran_set_pci_master (struct zoran *zr,
int set_master)
{
u16 command;
if (set_master) {
pci_set_master(zr->pci_dev);
} else {
pci_read_config_word(zr->pci_dev, PCI_COMMAND, &command);
command &= ~PCI_COMMAND_MASTER;
pci_write_config_word(zr->pci_dev, PCI_COMMAND, command);
}
}
void
zoran_init_hardware (struct zoran *zr)
{
int j, zero = 0;
/* Enable bus-mastering */
zoran_set_pci_master(zr, 1);
/* Initialize the board */
if (zr->card.init) {
zr->card.init(zr);
}
j = zr->card.input[zr->input].muxsel;
decoder_command(zr, 0, NULL);
decoder_command(zr, DECODER_SET_NORM, &zr->norm);
decoder_command(zr, DECODER_SET_INPUT, &j);
encoder_command(zr, 0, NULL);
encoder_command(zr, ENCODER_SET_NORM, &zr->norm);
encoder_command(zr, ENCODER_SET_INPUT, &zero);
/* toggle JPEG codec sleep to sync PLL */
jpeg_codec_sleep(zr, 1);
jpeg_codec_sleep(zr, 0);
/* set individual interrupt enables (without GIRQ1)
* but don't global enable until zoran_open() */
//btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ1, ZR36057_ICR); // SW
// It looks like using only JPEGRepIRQEn is not always reliable,
// may be when JPEG codec crashes it won't generate IRQ? So,
/*CP*/ // btwrite(IRQ_MASK, ZR36057_ICR); // Enable Vsync interrupts too. SM WHY ? LP
zr36057_init_vfe(zr);
zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts
}
void
zr36057_restart (struct zoran *zr)
{
btwrite(0, ZR36057_SPGPPCR);
mdelay(1);
btor(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR);
mdelay(1);
/* assert P_Reset */
btwrite(0, ZR36057_JPC);
/* set up GPIO direction - all output */
btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR);
/* set up GPIO pins and guest bus timing */
btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1);
}
/*
* initialize video front end
*/
void
zr36057_init_vfe (struct zoran *zr)
{
u32 reg;
reg = btread(ZR36057_VFESPFR);
reg |= ZR36057_VFESPFR_LittleEndian;
reg &= ~ZR36057_VFESPFR_VCLKPol;
reg |= ZR36057_VFESPFR_ExtFl;
reg |= ZR36057_VFESPFR_TopField;
btwrite(reg, ZR36057_VFESPFR);
reg = btread(ZR36057_VDCR);
if (pci_pci_problems & PCIPCI_TRITON)
// || zr->revision < 1) // Revision 1 has also Triton support
reg &= ~ZR36057_VDCR_Triton;
else
reg |= ZR36057_VDCR_Triton;
btwrite(reg, ZR36057_VDCR);
}
/*
* Interface to decoder and encoder chips using i2c bus
*/
int
decoder_command (struct zoran *zr,
int cmd,
void *data)
{
if (zr->decoder == NULL)
return -EIO;
if (zr->card.type == LML33 &&
(cmd == DECODER_SET_NORM || DECODER_SET_INPUT)) {
int res;
// Bt819 needs to reset its FIFO buffer using #FRST pin and
// LML33 card uses GPIO(7) for that.
GPIO(zr, 7, 0);
res = zr->decoder->driver->command(zr->decoder, cmd, data);
// Pull #FRST high.
GPIO(zr, 7, 1);
return res;
} else
return zr->decoder->driver->command(zr->decoder, cmd,
data);
}
int
encoder_command (struct zoran *zr,
int cmd,
void *data)
{
if (zr->encoder == NULL)
return -1;
return zr->encoder->driver->command(zr->encoder, cmd, data);
}
/*
* Zoran zr36057/zr36067 PCI controller driver, for the
* Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
* Media Labs LML33/LML33R10.
*
* This part handles card-specific data and detection
*
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
*
* Currently maintained by:
* Ronald Bultje <rbultje@ronald.bitfreak.net>
* Laurent Pinchart <laurent.pinchart@skynet.be>
* Mailinglist <mjpeg-users@lists.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ZORAN_DEVICE_H__
#define __ZORAN_DEVICE_H__
/* general purpose I/O */
extern void GPIO(struct zoran *zr,
int bit,
unsigned int value);
/* codec (or actually: guest bus) access */
extern int post_office_wait(struct zoran *zr);
extern int post_office_write(struct zoran *zr,
unsigned guest,
unsigned reg,
unsigned value);
extern int post_office_read(struct zoran *zr,
unsigned guest,
unsigned reg);
extern void detect_guest_activity(struct zoran *zr);
extern void jpeg_codec_sleep(struct zoran *zr,
int sleep);
extern int jpeg_codec_reset(struct zoran *zr);
/* zr360x7 access to raw capture */
extern void zr36057_overlay(struct zoran *zr,
int on);
extern void write_overlay_mask(struct file *file,
struct video_clip *vp,
int count);
extern void zr36057_set_memgrab(struct zoran *zr,
int mode);
extern int wait_grab_pending(struct zoran *zr);
/* interrupts */
extern void print_interrupts(struct zoran *zr);
extern void clear_interrupt_counters(struct zoran *zr);
extern irqreturn_t zoran_irq(int irq,
void *dev_id,
struct pt_regs *regs);
/* JPEG codec access */
extern void jpeg_start(struct zoran *zr);
extern void zr36057_enable_jpg(struct zoran *zr,
enum zoran_codec_mode mode);
extern void zoran_feed_stat_com(struct zoran *zr);
/* general */
extern void zoran_set_pci_master(struct zoran *zr,
int set_master);
extern void zoran_init_hardware(struct zoran *zr);
extern void zr36057_restart(struct zoran *zr);
extern void zr36057_init_vfe(struct zoran *zr);
/* i2c */
extern int decoder_command(struct zoran *zr,
int cmd,
void *data);
extern int encoder_command(struct zoran *zr,
int cmd,
void *data);
#endif /* __ZORAN_DEVICE_H__ */
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* Zoran zr36057/zr36067 PCI controller driver, for the
* Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
* Media Labs LML33/LML33R10.
*
* This part handles the procFS entries (/proc/ZORAN[%d])
*
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
*
* Currently maintained by:
* Ronald Bultje <rbultje@ronald.bitfreak.net>
* Laurent Pinchart <laurent.pinchart@skynet.be>
* Mailinglist <mjpeg-users@lists.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h> #include <linux/config.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/videodev.h>
#include <linux/spinlock.h>
#include <linux/sem.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <asm/io.h>
#include "videocodec.h"
#include "zoran.h"
#include "zoran_procfs.h"
extern int debug;
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
#ifdef CONFIG_PROC_FS
struct procfs_params_zr36067 { struct procfs_params_zr36067 {
char *name; char *name;
short reg; short reg;
...@@ -8,7 +65,7 @@ struct procfs_params_zr36067 { ...@@ -8,7 +65,7 @@ struct procfs_params_zr36067 {
short bit; short bit;
}; };
static struct procfs_params_zr36067 zr67[] = { static const struct procfs_params_zr36067 zr67[] = {
{"HSPol", 0x000, 1, 30}, {"HSPol", 0x000, 1, 30},
{"HStart", 0x000, 0x3ff, 10}, {"HStart", 0x000, 0x3ff, 10},
{"HEnd", 0x000, 0x3ff, 0}, {"HEnd", 0x000, 0x3ff, 0},
...@@ -31,15 +88,27 @@ static struct procfs_params_zr36067 zr67[] = { ...@@ -31,15 +88,27 @@ static struct procfs_params_zr36067 zr67[] = {
{"NAY", 0x114, 0xffff, 16}, {"NAY", 0x114, 0xffff, 16},
{"PAY", 0x114, 0xffff, 0}, {"PAY", 0x114, 0xffff, 0},
/* {"",,,}, */
/* {"",,,}, */
{NULL, 0, 0, 0}, {NULL, 0, 0, 0},
}; };
static void setparam(struct zoran *zr, char *name, char *sval) struct procfs_io {
char *buffer;
char *end;
int neof;
int count;
int count_current;
};
static void
setparam (struct zoran *zr,
char *name,
char *sval)
{ {
int i, reg0, reg, val; int i = 0, reg0, reg, val;
i = 0;
while (zr67[i].name != NULL) { while (zr67[i].name != NULL) {
if (!strncmp(name, zr67[i].name, strlen(zr67[i].name))) { if (!strncmp(name, zr67[i].name, strlen(zr67[i].name))) {
reg = reg0 = btread(zr67[i].reg); reg = reg0 = btread(zr67[i].reg);
...@@ -50,8 +119,11 @@ static void setparam(struct zoran *zr, char *name, char *sval) ...@@ -50,8 +119,11 @@ static void setparam(struct zoran *zr, char *name, char *sval)
if ((val & ~zr67[i].mask)) if ((val & ~zr67[i].mask))
break; break;
reg |= (val & zr67[i].mask) << zr67[i].bit; reg |= (val & zr67[i].mask) << zr67[i].bit;
printk(KERN_INFO "%s: setparam: setting ZR36067 register 0x%03x: 0x%08x=>0x%08x %s=%d\n", dprintk(4,
zr->name, zr67[i].reg, reg0, reg, zr67[i].name, val); KERN_INFO
"%s: setparam: setting ZR36067 register 0x%03x: 0x%08x=>0x%08x %s=%d\n",
ZR_DEVNAME(zr), zr67[i].reg, reg0, reg,
zr67[i].name, val);
btwrite(reg, zr67[i].reg); btwrite(reg, zr67[i].reg);
break; break;
} }
...@@ -59,72 +131,97 @@ static void setparam(struct zoran *zr, char *name, char *sval) ...@@ -59,72 +131,97 @@ static void setparam(struct zoran *zr, char *name, char *sval)
} }
} }
/* This macro was stolen from /usr/src/drivers/char/nvram.c and modified */ static int
#define PRINT_PROC(args...) \ print_procfs (struct procfs_io *io,
do { \ const char *fmt,
if (begin + len > offset + size) { \ ...)
*eof = 0; \
break; \
} \
len += sprintf( buffer+len, ##args ); \
if (begin + len < offset) { \
begin += len; \
len = 0; \
} \
} while(0)
static int zoran_read_proc(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
{ {
#ifdef CONFIG_PROC_FS va_list args;
int len = 0; int i;
off_t begin = 0;
if (io->buffer >= io->end) {
io->neof++;
return 0;
}
if (io->count > io->count_current++)
return 0;
va_start(args, fmt);
i = vsprintf(io->buffer, fmt, args);
io->buffer += i;
va_end(args);
return i;
}
static void
zoran_procfs_output (struct procfs_io *io,
void *data)
{
int i; int i;
struct zoran *zr; struct zoran *zr;
zr = (struct zoran *) data; zr = (struct zoran *) data;
DEBUG2(printk(KERN_INFO "%s: read_proc: buffer=%x, offset=%d, size=%d, data=%x\n", zr->name, (int) buffer, (int) offset, size, (int) data));
*eof = 1; print_procfs(io, "ZR36067 registers:");
PRINT_PROC("ZR36067 registers:");
for (i = 0; i < 0x130; i += 4) { for (i = 0; i < 0x130; i += 4) {
if (!(i % 16)) { if (!(i % 16)) {
PRINT_PROC("\n%03X", i); print_procfs(io, "\n%03X", i);
} };
PRINT_PROC(" %08X ", btread(i)); print_procfs(io, " %08X ", btread(i));
} };
PRINT_PROC("\n"); print_procfs(io, "\n");
if (offset >= len + begin) { }
return 0;
} static int
*start = buffer + begin - offset; zoran_read_proc (char *buffer,
return ((size < begin + len - offset) ? size : begin + len - offset); char **start,
#endif off_t offset,
int size,
int *eof,
void *data)
{
struct procfs_io io;
int nbytes;
io.buffer = buffer;
io.end = buffer + size - 128; // Just to make it a little bit safer
io.count = offset;
io.count_current = 0;
io.neof = 0;
zoran_procfs_output(&io, data);
*start = (char *) (io.count_current - io.count);
nbytes = (int) (io.buffer - buffer);
*eof = !io.neof;
return nbytes;
return 0; return 0;
} }
static int zoran_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) static int
zoran_write_proc (struct file *file,
const char *buffer,
unsigned long count,
void *data)
{ {
#ifdef CONFIG_PROC_FS
char *string, *sp; char *string, *sp;
char *line, *ldelim, *varname, *svar, *tdelim; char *line, *ldelim, *varname, *svar, *tdelim;
struct zoran *zr; struct zoran *zr;
zr = (struct zoran *) data; zr = (struct zoran *) data;
if(count > 32768) /* Stupidity filter */
count = 32768;
string = sp = vmalloc(count + 1); string = sp = vmalloc(count + 1);
if (!string) { if (!string) {
printk(KERN_ERR "%s: write_proc: can not allocate memory\n", zr->name); dprintk(1,
KERN_ERR
"%s: write_proc: can not allocate memory\n",
ZR_DEVNAME(zr));
return -ENOMEM; return -ENOMEM;
} }
if (copy_from_user(string, buffer, count)) { if (copy_from_user(string, buffer, count)) {
vfree(string); vfree (string);
return -EFAULT; return -EFAULT;
} }
string[count] = 0; string[count] = 0;
DEBUG2(printk(KERN_INFO "%s: write_proc: name=%s count=%lu data=%x\n", zr->name, file->f_dentry->d_name.name, count, (int) data)); dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%lu data=%x\n",
ZR_DEVNAME(zr), file->f_dentry->d_name.name, count, (int) data);
ldelim = " \t\n"; ldelim = " \t\n";
tdelim = "="; tdelim = "=";
line = strpbrk(sp, ldelim); line = strpbrk(sp, ldelim);
...@@ -141,36 +238,44 @@ static int zoran_write_proc(struct file *file, const char *buffer, unsigned long ...@@ -141,36 +238,44 @@ static int zoran_write_proc(struct file *file, const char *buffer, unsigned long
line = strpbrk(sp, ldelim); line = strpbrk(sp, ldelim);
} }
vfree(string); vfree(string);
#endif
return count; return count;
} }
#endif
static int zoran_proc_init(int i) int
zoran_proc_init (struct zoran *zr)
{ {
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
char name[8]; char name[8];
sprintf(name, "zoran%d", i); snprintf(name, 7, "zoran%d", zr->id);
if ((zoran[i].zoran_proc = create_proc_entry(name, 0, 0))) { if ((zr->zoran_proc = create_proc_entry(name, 0, 0))) {
zoran[i].zoran_proc->read_proc = zoran_read_proc; zr->zoran_proc->read_proc = zoran_read_proc;
zoran[i].zoran_proc->write_proc = zoran_write_proc; zr->zoran_proc->write_proc = zoran_write_proc;
zoran[i].zoran_proc->data = &zoran[i]; zr->zoran_proc->data = zr;
printk(KERN_INFO "%s: procfs entry /proc/%s allocated. data=%x\n", zoran[i].name, name, (int) zoran[i].zoran_proc->data); zr->zoran_proc->owner = THIS_MODULE;
dprintk(2,
KERN_INFO
"%s: procfs entry /proc/%s allocated. data=%p\n",
ZR_DEVNAME(zr), name, zr->zoran_proc->data);
} else { } else {
printk(KERN_ERR "%s: Unable to initialise /proc/%s\n", zoran[i].name, name); dprintk(1, KERN_ERR "%s: Unable to initialise /proc/%s\n",
ZR_DEVNAME(zr), name);
return 1; return 1;
} }
#endif #endif
return 0; return 0;
} }
static void zoran_proc_cleanup(int i) void
zoran_proc_cleanup (struct zoran *zr)
{ {
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
char name[8]; char name[8];
sprintf(name, "zoran%d", i); snprintf(name, 7, "zoran%d", zr->id);
if (zoran[i].zoran_proc) { if (zr->zoran_proc) {
remove_proc_entry(name, 0); remove_proc_entry(name, 0);
} }
zoran[i].zoran_proc = NULL; zr->zoran_proc = NULL;
#endif #endif
} }
/*
* Zoran zr36057/zr36067 PCI controller driver, for the
* Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
* Media Labs LML33/LML33R10.
*
* This part handles card-specific data and detection
*
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
*
* Currently maintained by:
* Ronald Bultje <rbultje@ronald.bitfreak.net>
* Laurent Pinchart <laurent.pinchart@skynet.be>
* Mailinglist <mjpeg-users@lists.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ZORAN_PROCFS_H__
#define __ZORAN_PROCFS_H__
extern int zoran_proc_init(struct zoran *zr);
extern void zoran_proc_cleanup(struct zoran *zr);
#endif /* __ZORAN_PROCFS_H__ */
/*
* Zoran ZR36016 basic configuration functions
*
* Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
*
* $Id: zr36016.c,v 1.1.2.14 2003/08/20 19:46:55 rbultje Exp $
*
* ------------------------------------------------------------------------
*
* 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.
*
* ------------------------------------------------------------------------
*/
#define ZR016_VERSION "v0.7"
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/wait.h>
/* includes for structures and defines regarding video
#include<linux/videodev.h> */
/* I/O commands, error codes */
#include<asm/io.h>
//#include<errno.h>
/* v4l API */
#include<linux/videodev.h>
/* headerfile of this module */
#include"zr36016.h"
/* codec io API */
#include"videocodec.h"
/* it doesn't make sense to have more than 20 or so,
just to prevent some unwanted loops */
#define MAX_CODECS 20
/* amount of chips attached via this driver */
static int zr36016_codecs = 0;
/* debugging is available via module parameter */
static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-4)");
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
/* =========================================================================
Local hardware I/O functions:
read/write via codec layer (registers are located in the master device)
========================================================================= */
/* read and write functions */
static u8
zr36016_read (struct zr36016 *ptr,
u16 reg)
{
u8 value = 0;
// just in case something is wrong...
if (ptr->codec->master_data->readreg)
value =
(ptr->codec->master_data->
readreg(ptr->codec, reg)) & 0xFF;
else
dprintk(1,
KERN_ERR "%s: invalid I/O setup, nothing read!\n",
ptr->name);
dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg,
value);
return value;
}
static void
zr36016_write (struct zr36016 *ptr,
u16 reg,
u8 value)
{
dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value,
reg);
// just in case something is wrong...
if (ptr->codec->master_data->writereg) {
ptr->codec->master_data->writereg(ptr->codec, reg, value);
} else
dprintk(1,
KERN_ERR
"%s: invalid I/O setup, nothing written!\n",
ptr->name);
}
/* indirect read and write functions */
/* the 016 supports auto-addr-increment, but
* writing it all time cost not much and is safer... */
static u8
zr36016_readi (struct zr36016 *ptr,
u16 reg)
{
u8 value = 0;
// just in case something is wrong...
if ((ptr->codec->master_data->writereg) &&
(ptr->codec->master_data->readreg)) {
ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR
value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; // DATA
} else
dprintk(1,
KERN_ERR
"%s: invalid I/O setup, nothing read (i)!\n",
ptr->name);
dprintk(4, "%s: reading indirect from 0x%04x: %02x\n", ptr->name,
reg, value);
return value;
}
static void
zr36016_writei (struct zr36016 *ptr,
u16 reg,
u8 value)
{
dprintk(4, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name,
value, reg);
// just in case something is wrong...
if (ptr->codec->master_data->writereg) {
ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR
ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); // DATA
} else
dprintk(1,
KERN_ERR
"%s: invalid I/O setup, nothing written (i)!\n",
ptr->name);
}
/* =========================================================================
Local helper function:
version read
========================================================================= */
/* version kept in datastructure */
static u8
zr36016_read_version (struct zr36016 *ptr)
{
ptr->version = zr36016_read(ptr, 0) >> 4;
return ptr->version;
}
/* =========================================================================
Local helper function:
basic test of "connectivity", writes/reads to/from PAX-Lo register
========================================================================= */
static int
zr36016_basic_test (struct zr36016 *ptr)
{
if (debug) {
int i;
zr36016_writei(ptr, ZR016I_PAX_LO, 0x55);
dprintk(1, KERN_INFO "%s: registers: ", ptr->name);
for (i = 0; i <= 0x0b; i++)
dprintk(1, "%02x ", zr36016_readi(ptr, i));
dprintk(1, "\n");
}
// for testing just write 0, then the default value to a register and read
// it back in both cases
zr36016_writei(ptr, ZR016I_PAX_LO, 0x00);
if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) {
dprintk(1,
KERN_ERR
"%s: attach failed, can't connect to vfe processor!\n",
ptr->name);
return -ENXIO;
}
zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0);
if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) {
dprintk(1,
KERN_ERR
"%s: attach failed, can't connect to vfe processor!\n",
ptr->name);
return -ENXIO;
}
// we allow version numbers from 0-3, should be enough, though
zr36016_read_version(ptr);
if (ptr->version & 0x0c) {
dprintk(1,
KERN_ERR
"%s: attach failed, suspicious version %d found...\n",
ptr->name, ptr->version);
return -ENXIO;
}
return 0; /* looks good! */
}
/* =========================================================================
Local helper function:
simple loop for pushing the init datasets - NO USE --
========================================================================= */
#if 0
static int zr36016_pushit (struct zr36016 *ptr,
u16 startreg,
u16 len,
const char *data)
{
int i=0;
dprintk(4, "%s: write data block to 0x%04x (len=%d)\n",
ptr->name, startreg,len);
while (i<len) {
zr36016_writei(ptr, startreg++, data[i++]);
}
return i;
}
#endif
/* =========================================================================
Basic datasets & init:
//TODO//
========================================================================= */
// needed offset values PAL NTSC SECAM
static const int zr016_xoff[] = { 20, 20, 20 };
static const int zr016_yoff[] = { 8, 9, 7 };
static void
zr36016_init (struct zr36016 *ptr)
{
// stop any processing
zr36016_write(ptr, ZR016_GOSTOP, 0);
// mode setup (yuv422 in and out, compression/expansuon due to mode)
zr36016_write(ptr, ZR016_MODE,
ZR016_YUV422 | ZR016_YUV422_YUV422 |
(ptr->mode == CODEC_DO_COMPRESSION ?
ZR016_COMPRESSION : ZR016_EXPANSION));
// misc setup
zr36016_writei(ptr, ZR016I_SETUP1,
(ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) |
(ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI);
zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR);
// Window setup
// (no extra offset for now, norm defines offset, default width height)
zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8);
zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF);
zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8);
zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF);
zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8);
zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF);
zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8);
zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF);
/* shall we continue now, please? */
zr36016_write(ptr, ZR016_GOSTOP, 1);
}
/* =========================================================================
CODEC API FUNCTIONS
this functions are accessed by the master via the API structure
========================================================================= */
/* set compression/expansion mode and launches codec -
this should be the last call from the master before starting processing */
static int
zr36016_set_mode (struct videocodec *codec,
int mode)
{
struct zr36016 *ptr = (struct zr36016 *) codec->data;
dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
return -EINVAL;
ptr->mode = mode;
zr36016_init(ptr);
return 0;
}
/* set picture size */
static int
zr36016_set_video (struct videocodec *codec,
struct tvnorm *norm,
struct vfe_settings *cap,
struct vfe_polarity *pol)
{
struct zr36016 *ptr = (struct zr36016 *) codec->data;
dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n",
ptr->name, norm->HStart, norm->VStart,
cap->x, cap->y, cap->width, cap->height,
cap->decimation);
/* if () return -EINVAL;
* trust the master driver that it knows what it does - so
* we allow invalid startx/y for now ... */
ptr->width = cap->width;
ptr->height = cap->height;
/* (Ronald) This is ugly. zoran_device.c, line 387
* already mentions what happens if HStart is even
* (blue faces, etc., cr/cb inversed). There's probably
* some good reason why HStart is 0 instead of 1, so I'm
* leaving it to this for now, but really... This can be
* done a lot simpler */
ptr->xoff = (norm->HStart ? norm->HStart : 1) + cap->x;
/* Something to note here (I don't understand it), setting
* VStart too high will cause the codec to 'not work'. I
* really don't get it. values of 16 (VStart) already break
* it here. Just '0' seems to work. More testing needed! */
ptr->yoff = norm->VStart + cap->y;
/* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */
ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1;
ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1;
return 0;
}
/* additional control functions */
static int
zr36016_control (struct videocodec *codec,
int type,
int size,
void *data)
{
struct zr36016 *ptr = (struct zr36016 *) codec->data;
int *ival = (int *) data;
dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
size);
switch (type) {
case CODEC_G_STATUS: /* get last status - we don't know it ... */
if (size != sizeof(int))
return -EFAULT;
*ival = 0;
break;
case CODEC_G_CODEC_MODE:
if (size != sizeof(int))
return -EFAULT;
*ival = 0;
break;
case CODEC_S_CODEC_MODE:
if (size != sizeof(int))
return -EFAULT;
if (*ival != 0)
return -EINVAL;
/* not needed, do nothing */
return 0;
case CODEC_G_VFE:
case CODEC_S_VFE:
return 0;
case CODEC_S_MMAP:
/* not available, give an error */
return -ENXIO;
default:
return -EINVAL;
}
return size;
}
/* =========================================================================
Exit and unregister function:
Deinitializes Zoran's JPEG processor
========================================================================= */
static int
zr36016_unset (struct videocodec *codec)
{
struct zr36016 *ptr = codec->data;
if (ptr) {
/* do wee need some codec deinit here, too ???? */
dprintk(1, "%s: finished codec #%d\n", ptr->name,
ptr->num);
kfree(ptr);
codec->data = NULL;
zr36016_codecs--;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_DEC_USE_COUNT;
#else
module_put(THIS_MODULE);
#endif
return 0;
}
return -EFAULT;
}
/* =========================================================================
Setup and registry function:
Initializes Zoran's JPEG processor
Also sets pixel size, average code size, mode (compr./decompr.)
(the given size is determined by the processor with the video interface)
========================================================================= */
static int
zr36016_setup (struct videocodec *codec)
{
struct zr36016 *ptr;
int res;
dprintk(2, "zr36016: initializing VFE subsystem #%d.\n",
zr36016_codecs);
if (zr36016_codecs == MAX_CODECS) {
dprintk(1,
KERN_ERR "zr36016: Can't attach more codecs!\n");
return -ENOSPC;
}
//mem structure init
codec->data = ptr = kmalloc(sizeof(struct zr36016), GFP_KERNEL);
if (NULL == ptr) {
dprintk(1, KERN_ERR "zr36016: Can't get enough memory!\n");
return -ENOMEM;
}
memset(ptr, 0, sizeof(struct zr36016));
snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]",
zr36016_codecs);
ptr->num = zr36016_codecs++;
ptr->codec = codec;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_INC_USE_COUNT;
#else
if (!try_module_get(THIS_MODULE)) {
dprintk(1,
KERN_ERR
"zr36016: failed to increase module use count\n");
kfree(ptr);
zr36016_codecs--;
return -ENODEV;
}
#endif
//testing
res = zr36016_basic_test(ptr);
if (res < 0) {
zr36016_unset(codec);
return res;
}
//final setup
ptr->mode = CODEC_DO_COMPRESSION;
ptr->width = 768;
ptr->height = 288;
ptr->xdec = 1;
ptr->ydec = 0;
zr36016_init(ptr);
dprintk(1, KERN_INFO "%s: codec v%d attached and running\n",
ptr->name, ptr->version);
return 0;
}
static const struct videocodec zr36016_codec = {
.name = "zr36016",
.magic = 0L, // magic not used
.flags =
CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER |
CODEC_FLAG_DECODER,
.type = CODEC_TYPE_ZR36016,
.setup = zr36016_setup, // functionality
.unset = zr36016_unset,
.set_mode = zr36016_set_mode,
.set_video = zr36016_set_video,
.control = zr36016_control,
// others are not used
};
/* =========================================================================
HOOK IN DRIVER AS KERNEL MODULE
========================================================================= */
static int __init
zr36016_init_module (void)
{
//dprintk(1, "ZR36016 driver %s\n",ZR016_VERSION);
zr36016_codecs = 0;
return videocodec_register(&zr36016_codec);
}
static void __exit
zr36016_cleanup_module (void)
{
if (zr36016_codecs) {
dprintk(1,
"zr36016: something's wrong - %d codecs left somehow.\n",
zr36016_codecs);
}
videocodec_unregister(&zr36016_codec);
}
module_init(zr36016_init_module);
module_exit(zr36016_cleanup_module);
MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
MODULE_DESCRIPTION("Driver module for ZR36016 video frontends "
ZR016_VERSION);
MODULE_LICENSE("GPL");
/*
* Zoran ZR36016 basic configuration functions - header file
*
* Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
*
* $Id: zr36016.h,v 1.1.2.3 2003/01/14 21:18:07 rbultje Exp $
*
* ------------------------------------------------------------------------
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* ------------------------------------------------------------------------
*/
#ifndef ZR36016_H
#define ZR36016_H
/* data stored for each zoran jpeg codec chip */
struct zr36016 {
char name[32];
int num;
/* io datastructure */
struct videocodec *codec;
// coder status
__u8 version;
// actual coder setup
int mode;
__u16 xoff;
__u16 yoff;
__u16 width;
__u16 height;
__u16 xdec;
__u16 ydec;
};
/* direct register addresses */
#define ZR016_GOSTOP 0x00
#define ZR016_MODE 0x01
#define ZR016_IADDR 0x02
#define ZR016_IDATA 0x03
/* indirect register addresses */
#define ZR016I_SETUP1 0x00
#define ZR016I_SETUP2 0x01
#define ZR016I_NAX_LO 0x02
#define ZR016I_NAX_HI 0x03
#define ZR016I_PAX_LO 0x04
#define ZR016I_PAX_HI 0x05
#define ZR016I_NAY_LO 0x06
#define ZR016I_NAY_HI 0x07
#define ZR016I_PAY_LO 0x08
#define ZR016I_PAY_HI 0x09
#define ZR016I_NOL_LO 0x0a
#define ZR016I_NOL_HI 0x0b
/* possible values for mode register */
#define ZR016_RGB444_YUV444 0x00
#define ZR016_RGB444_YUV422 0x01
#define ZR016_RGB444_YUV411 0x02
#define ZR016_RGB444_Y400 0x03
#define ZR016_RGB444_RGB444 0x04
#define ZR016_YUV444_YUV444 0x08
#define ZR016_YUV444_YUV422 0x09
#define ZR016_YUV444_YUV411 0x0a
#define ZR016_YUV444_Y400 0x0b
#define ZR016_YUV444_RGB444 0x0c
#define ZR016_YUV422_YUV422 0x11
#define ZR016_YUV422_YUV411 0x12
#define ZR016_YUV422_Y400 0x13
#define ZR016_YUV411_YUV411 0x16
#define ZR016_YUV411_Y400 0x17
#define ZR016_4444_4444 0x19
#define ZR016_100_100 0x1b
#define ZR016_RGB444 0x00
#define ZR016_YUV444 0x20
#define ZR016_YUV422 0x40
#define ZR016_COMPRESSION 0x80
#define ZR016_EXPANSION 0x80
/* possible values for setup 1 register */
#define ZR016_CKRT 0x80
#define ZR016_VERT 0x40
#define ZR016_HORZ 0x20
#define ZR016_HRFL 0x10
#define ZR016_DSFL 0x08
#define ZR016_SBFL 0x04
#define ZR016_RSTR 0x02
#define ZR016_CNTI 0x01
/* possible values for setup 2 register */
#define ZR016_SYEN 0x40
#define ZR016_CCIR 0x04
#define ZR016_SIGN 0x02
#define ZR016_YMCS 0x01
#endif /*fndef ZR36016_H */
/*
* Zoran ZR36050 basic configuration functions
*
* Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
*
* $Id: zr36050.c,v 1.1.2.11 2003/08/03 14:54:53 rbultje Exp $
*
* ------------------------------------------------------------------------
*
* 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.
*
* ------------------------------------------------------------------------
*/
#define ZR050_VERSION "v0.7"
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/wait.h>
/* includes for structures and defines regarding video
#include<linux/videodev.h> */
/* I/O commands, error codes */
#include<asm/io.h>
//#include<errno.h>
/* headerfile of this module */
#include"zr36050.h"
/* codec io API */
#include"videocodec.h"
/* it doesn't make sense to have more than 20 or so,
just to prevent some unwanted loops */
#define MAX_CODECS 20
/* amount of chips attached via this driver */
static int zr36050_codecs = 0;
/* debugging is available via module parameter */
static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-4)");
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
/* =========================================================================
Local hardware I/O functions:
read/write via codec layer (registers are located in the master device)
========================================================================= */
/* read and write functions */
static u8
zr36050_read (struct zr36050 *ptr,
u16 reg)
{
u8 value = 0;
// just in case something is wrong...
if (ptr->codec->master_data->readreg)
value = (ptr->codec->master_data->readreg(ptr->codec,
reg)) & 0xFF;
else
dprintk(1,
KERN_ERR "%s: invalid I/O setup, nothing read!\n",
ptr->name);
dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg,
value);
return value;
}
static void
zr36050_write (struct zr36050 *ptr,
u16 reg,
u8 value)
{
dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value,
reg);
// just in case something is wrong...
if (ptr->codec->master_data->writereg)
ptr->codec->master_data->writereg(ptr->codec, reg, value);
else
dprintk(1,
KERN_ERR
"%s: invalid I/O setup, nothing written!\n",
ptr->name);
}
/* =========================================================================
Local helper function:
status read
========================================================================= */
/* status is kept in datastructure */
static u8
zr36050_read_status1 (struct zr36050 *ptr)
{
ptr->status1 = zr36050_read(ptr, ZR050_STATUS_1);
zr36050_read(ptr, 0);
return ptr->status1;
}
/* =========================================================================
Local helper function:
scale factor read
========================================================================= */
/* scale factor is kept in datastructure */
static u16
zr36050_read_scalefactor (struct zr36050 *ptr)
{
ptr->scalefact = (zr36050_read(ptr, ZR050_SF_HI) << 8) |
(zr36050_read(ptr, ZR050_SF_LO) & 0xFF);
/* leave 0 selected for an eventually GO from master */
zr36050_read(ptr, 0);
return ptr->scalefact;
}
/* =========================================================================
Local helper function:
wait if codec is ready to proceed (end of processing) or time is over
========================================================================= */
static void
zr36050_wait_end (struct zr36050 *ptr)
{
int i = 0;
while (!(zr36050_read_status1(ptr) & 0x4)) {
udelay(1);
if (i++ > 200000) { // 200ms, there is for shure something wrong!!!
dprintk(1,
"%s: timout at wait_end (last status: 0x%02x)\n",
ptr->name, ptr->status1);
break;
}
}
}
/* =========================================================================
Local helper function:
basic test of "connectivity", writes/reads to/from memory the SOF marker
========================================================================= */
static int
zr36050_basic_test (struct zr36050 *ptr)
{
zr36050_write(ptr, ZR050_SOF_IDX, 0x00);
zr36050_write(ptr, ZR050_SOF_IDX + 1, 0x00);
if ((zr36050_read(ptr, ZR050_SOF_IDX) |
zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0x0000) {
dprintk(1,
KERN_ERR
"%s: attach failed, can't connect to jpeg processor!\n",
ptr->name);
return -ENXIO;
}
zr36050_write(ptr, ZR050_SOF_IDX, 0xff);
zr36050_write(ptr, ZR050_SOF_IDX + 1, 0xc0);
if (((zr36050_read(ptr, ZR050_SOF_IDX) << 8) |
zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0xffc0) {
dprintk(1,
KERN_ERR
"%s: attach failed, can't connect to jpeg processor!\n",
ptr->name);
return -ENXIO;
}
zr36050_wait_end(ptr);
if ((ptr->status1 & 0x4) == 0) {
dprintk(1,
KERN_ERR
"%s: attach failed, jpeg processor failed (end flag)!\n",
ptr->name);
return -EBUSY;
}
return 0; /* looks good! */
}
/* =========================================================================
Local helper function:
simple loop for pushing the init datasets
========================================================================= */
static int
zr36050_pushit (struct zr36050 *ptr,
u16 startreg,
u16 len,
const char *data)
{
int i = 0;
dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name,
startreg, len);
while (i < len) {
zr36050_write(ptr, startreg++, data[i++]);
}
return i;
}
/* =========================================================================
Basic datasets:
jpeg baseline setup data (you find it on lots places in internet, or just
extract it from any regular .jpg image...)
Could be variable, but until it's not needed it they are just fixed to save
memory. Otherwise expand zr36050 structure with arrays, push the values to
it and initalize from there, as e.g. the linux zr36057/60 driver does it.
========================================================================= */
static const char zr36050_dqt[0x86] = {
0xff, 0xdb, //Marker: DQT
0x00, 0x84, //Length: 2*65+2
0x00, //Pq,Tq first table
0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
0x01, //Pq,Tq second table
0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
};
static const char zr36050_dht[0x1a4] = {
0xff, 0xc4, //Marker: DHT
0x01, 0xa2, //Length: 2*AC, 2*DC
0x00, //DC first table
0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
0x01, //DC second table
0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
0x10, //AC first table
0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11,
0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24,
0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
0xF8, 0xF9, 0xFA,
0x11, //AC second table
0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
0x07, 0x05, 0x04, 0x04, 0x00, 0x01,
0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25,
0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
0xF9, 0xFA
};
static const char zr36050_app[0x40] = {
0xff, 0xe0, //Marker: APP0
0x00, 0x3e, //Length: 60+2
' ', 'A', 'V', 'I', '1', 0, 0, 0, // 'AVI' field
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
static const char zr36050_com[0x40] = {
0xff, 0xfe, //Marker: COM
0x00, 0x3e, //Length: 60+2
' ', 'C', 'O', 'M', 0, 0, 0, 0, // 'COM' field
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */
#define NO_OF_COMPONENTS 0x3 //Y,U,V
#define BASELINE_PRECISION 0x8 //MCU size (?)
static const char zr36050_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT
static const char zr36050_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC
static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC
/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */
static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 };
static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 };
/* =========================================================================
Local helper functions:
calculation and setup of parameter-dependent JPEG baseline segments
(needed for compression only)
========================================================================= */
/* ------------------------------------------------------------------------- */
/* SOF (start of frame) segment depends on width, height and sampling ratio
of each color component */
static int
zr36050_set_sof (struct zr36050 *ptr)
{
char sof_data[34]; // max. size of register set
int i;
dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name,
ptr->width, ptr->height, NO_OF_COMPONENTS);
sof_data[0] = 0xff;
sof_data[1] = 0xc0;
sof_data[2] = 0x00;
sof_data[3] = (3 * NO_OF_COMPONENTS) + 8;
sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36050
sof_data[5] = (ptr->height) >> 8;
sof_data[6] = (ptr->height) & 0xff;
sof_data[7] = (ptr->width) >> 8;
sof_data[8] = (ptr->width) & 0xff;
sof_data[9] = NO_OF_COMPONENTS;
for (i = 0; i < NO_OF_COMPONENTS; i++) {
sof_data[10 + (i * 3)] = i; // index identifier
sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | (ptr->v_samp_ratio[i]); // sampling ratios
sof_data[12 + (i * 3)] = zr36050_tq[i]; // Q table selection
}
return zr36050_pushit(ptr, ZR050_SOF_IDX,
(3 * NO_OF_COMPONENTS) + 10, sof_data);
}
/* ------------------------------------------------------------------------- */
/* SOS (start of scan) segment depends on the used scan components
of each color component */
static int
zr36050_set_sos (struct zr36050 *ptr)
{
char sos_data[16]; // max. size of register set
int i;
dprintk(3, "%s: write SOS\n", ptr->name);
sos_data[0] = 0xff;
sos_data[1] = 0xda;
sos_data[2] = 0x00;
sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3;
sos_data[4] = NO_OF_COMPONENTS;
for (i = 0; i < NO_OF_COMPONENTS; i++) {
sos_data[5 + (i * 2)] = i; // index
sos_data[6 + (i * 2)] = (zr36050_td[i] << 4) | zr36050_ta[i]; // AC/DC tbl.sel.
}
sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start
sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3F;
sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00;
return zr36050_pushit(ptr, ZR050_SOS1_IDX,
4 + 1 + (2 * NO_OF_COMPONENTS) + 3,
sos_data);
}
/* ------------------------------------------------------------------------- */
/* DRI (define restart interval) */
static int
zr36050_set_dri (struct zr36050 *ptr)
{
char dri_data[6]; // max. size of register set
dprintk(3, "%s: write DRI\n", ptr->name);
dri_data[0] = 0xff;
dri_data[1] = 0xdd;
dri_data[2] = 0x00;
dri_data[3] = 0x04;
dri_data[4] = ptr->dri >> 8;
dri_data[5] = ptr->dri * 0xff;
return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data);
}
/* =========================================================================
Setup function:
Setup compression/decompression of Zoran's JPEG processor
( see also zoran 36050 manual )
... sorry for the spaghetti code ...
========================================================================= */
static void
zr36050_init (struct zr36050 *ptr)
{
int sum = 0;
long bitcnt, tmp;
if (ptr->mode == CODEC_DO_COMPRESSION) {
dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name);
/* 050 communicates with 057 in master mode */
zr36050_write(ptr, ZR050_HARDWARE, ZR050_HW_MSTR);
/* encoding table preload for compression */
zr36050_write(ptr, ZR050_MODE,
ZR050_MO_COMP | ZR050_MO_TLM);
zr36050_write(ptr, ZR050_OPTIONS, 0);
/* disable all IRQs */
zr36050_write(ptr, ZR050_INT_REQ_0, 0);
zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1
/* volume control settings */
zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol >> 1);
zr36050_write(ptr, ZR050_SF_HI, ptr->scalefact >> 8);
zr36050_write(ptr, ZR050_SF_LO, ptr->scalefact & 0xff);
zr36050_write(ptr, ZR050_AF_HI, 0xff);
zr36050_write(ptr, ZR050_AF_M, 0xff);
zr36050_write(ptr, ZR050_AF_LO, 0xff);
/* setup the variable jpeg tables */
sum += zr36050_set_sof(ptr);
sum += zr36050_set_sos(ptr);
sum += zr36050_set_dri(ptr);
/* setup the fixed jpeg tables - maybe variable, though -
* (see table init section above) */
dprintk(3, "%s: write DQT, DHT, APP\n", ptr->name);
sum += zr36050_pushit(ptr, ZR050_DQT_IDX,
sizeof(zr36050_dqt), zr36050_dqt);
sum += zr36050_pushit(ptr, ZR050_DHT_IDX,
sizeof(zr36050_dht), zr36050_dht);
sum += zr36050_pushit(ptr, ZR050_APP_IDX,
sizeof(zr36050_app), zr36050_app);
sum += zr36050_pushit(ptr, ZR050_COM_IDX,
sizeof(zr36050_com), zr36050_com);
/* do the internal huffman table preload */
zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI);
zr36050_write(ptr, ZR050_GO, 1); // launch codec
zr36050_wait_end(ptr);
dprintk(2, "%s: Status after table preload: 0x%02x\n",
ptr->name, ptr->status1);
if ((ptr->status1 & 0x4) == 0) {
dprintk(1, KERN_ERR "%s: init aborted!\n",
ptr->name);
return; // something is wrong, its timed out!!!!
}
/* setup misc. data for compression (target code sizes) */
/* size of compressed code to reach without header data */
sum = ptr->total_code_vol - sum;
bitcnt = sum << 3; /* need the size in bits */
tmp = bitcnt >> 16;
dprintk(3,
"%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n",
ptr->name, sum, ptr->total_code_vol, bitcnt, tmp);
zr36050_write(ptr, ZR050_TCV_NET_HI, tmp >> 8);
zr36050_write(ptr, ZR050_TCV_NET_MH, tmp & 0xff);
tmp = bitcnt & 0xffff;
zr36050_write(ptr, ZR050_TCV_NET_ML, tmp >> 8);
zr36050_write(ptr, ZR050_TCV_NET_LO, tmp & 0xff);
bitcnt -= bitcnt >> 7; // bits without stuffing
bitcnt -= ((bitcnt * 5) >> 6); // bits without eob
tmp = bitcnt >> 16;
dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n",
ptr->name, bitcnt, tmp);
zr36050_write(ptr, ZR050_TCV_DATA_HI, tmp >> 8);
zr36050_write(ptr, ZR050_TCV_DATA_MH, tmp & 0xff);
tmp = bitcnt & 0xffff;
zr36050_write(ptr, ZR050_TCV_DATA_ML, tmp >> 8);
zr36050_write(ptr, ZR050_TCV_DATA_LO, tmp & 0xff);
/* compression setup with or without bitrate control */
zr36050_write(ptr, ZR050_MODE,
ZR050_MO_COMP | ZR050_MO_PASS2 |
(ptr->bitrate_ctrl ? ZR050_MO_BRC : 0));
/* this headers seem to deliver "valid AVI" jpeg frames */
zr36050_write(ptr, ZR050_MARKERS_EN,
ZR050_ME_APP | ZR050_ME_DQT | ZR050_ME_DHT |
ZR050_ME_COM);
} else {
dprintk(2, "%s: EXPANSION SETUP\n", ptr->name);
/* 050 communicates with 055 in master mode */
zr36050_write(ptr, ZR050_HARDWARE,
ZR050_HW_MSTR | ZR050_HW_CFIS_2_CLK);
/* encoding table preload */
zr36050_write(ptr, ZR050_MODE, ZR050_MO_TLM);
/* disable all IRQs */
zr36050_write(ptr, ZR050_INT_REQ_0, 0);
zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1
dprintk(3, "%s: write DHT\n", ptr->name);
zr36050_pushit(ptr, ZR050_DHT_IDX, sizeof(zr36050_dht),
zr36050_dht);
/* do the internal huffman table preload */
zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI);
zr36050_write(ptr, ZR050_GO, 1); // launch codec
zr36050_wait_end(ptr);
dprintk(2, "%s: Status after table preload: 0x%02x\n",
ptr->name, ptr->status1);
if ((ptr->status1 & 0x4) == 0) {
dprintk(1, KERN_ERR "%s: init aborted!\n",
ptr->name);
return; // something is wrong, its timed out!!!!
}
/* setup misc. data for expansion */
zr36050_write(ptr, ZR050_MODE, 0);
zr36050_write(ptr, ZR050_MARKERS_EN, 0);
}
/* adr on selected, to allow GO from master */
zr36050_read(ptr, 0);
}
/* =========================================================================
CODEC API FUNCTIONS
this functions are accessed by the master via the API structure
========================================================================= */
/* set compression/expansion mode and launches codec -
this should be the last call from the master before starting processing */
static int
zr36050_set_mode (struct videocodec *codec,
int mode)
{
struct zr36050 *ptr = (struct zr36050 *) codec->data;
dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
return -EINVAL;
ptr->mode = mode;
zr36050_init(ptr);
return 0;
}
/* set picture size (norm is ignored as the codec doesn't know about it) */
static int
zr36050_set_video (struct videocodec *codec,
struct tvnorm *norm,
struct vfe_settings *cap,
struct vfe_polarity *pol)
{
struct zr36050 *ptr = (struct zr36050 *) codec->data;
dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n",
ptr->name, norm->HStart, norm->VStart,
cap->x, cap->y, cap->width, cap->height,
cap->decimation);
/* if () return -EINVAL;
* trust the master driver that it knows what it does - so
* we allow invalid startx/y and norm for now ... */
ptr->width = cap->width / (cap->decimation & 0xff);
ptr->height = cap->height / ((cap->decimation >> 8) & 0xff);
return 0;
}
/* additional control functions */
static int
zr36050_control (struct videocodec *codec,
int type,
int size,
void *data)
{
struct zr36050 *ptr = (struct zr36050 *) codec->data;
int *ival = (int *) data;
dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
size);
switch (type) {
case CODEC_G_STATUS: /* get last status */
if (size != sizeof(int))
return -EFAULT;
zr36050_read_status1(ptr);
*ival = ptr->status1;
break;
case CODEC_G_CODEC_MODE:
if (size != sizeof(int))
return -EFAULT;
*ival = CODEC_MODE_BJPG;
break;
case CODEC_S_CODEC_MODE:
if (size != sizeof(int))
return -EFAULT;
if (*ival != CODEC_MODE_BJPG)
return -EINVAL;
/* not needed, do nothing */
return 0;
case CODEC_G_VFE:
case CODEC_S_VFE:
/* not needed, do nothing */
return 0;
case CODEC_S_MMAP:
/* not available, give an error */
return -ENXIO;
case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */
if (size != sizeof(int))
return -EFAULT;
*ival = ptr->total_code_vol;
break;
case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */
if (size != sizeof(int))
return -EFAULT;
ptr->total_code_vol = *ival;
break;
case CODEC_G_JPEG_SCALE: /* get scaling factor */
if (size != sizeof(int))
return -EFAULT;
*ival = zr36050_read_scalefactor(ptr);
break;
case CODEC_S_JPEG_SCALE: /* set scaling factor */
if (size != sizeof(int))
return -EFAULT;
ptr->scalefact = *ival;
break;
default:
return -EINVAL;
}
return size;
}
/* =========================================================================
Exit and unregister function:
Deinitializes Zoran's JPEG processor
========================================================================= */
static int
zr36050_unset (struct videocodec *codec)
{
struct zr36050 *ptr = codec->data;
if (ptr) {
/* do wee need some codec deinit here, too ???? */
dprintk(1, "%s: finished codec #%d\n", ptr->name,
ptr->num);
kfree(ptr);
codec->data = NULL;
zr36050_codecs--;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_DEC_USE_COUNT;
#else
module_put(THIS_MODULE);
#endif
return 0;
}
return -EFAULT;
}
/* =========================================================================
Setup and registry function:
Initializes Zoran's JPEG processor
Also sets pixel size, average code size, mode (compr./decompr.)
(the given size is determined by the processor with the video interface)
========================================================================= */
static int
zr36050_setup (struct videocodec *codec)
{
struct zr36050 *ptr;
int res;
dprintk(2, "zr36050: initializing MJPEG subsystem #%d.\n",
zr36050_codecs);
if (zr36050_codecs == MAX_CODECS) {
dprintk(1,
KERN_ERR "zr36050: Can't attach more codecs!\n");
return -ENOSPC;
}
//mem structure init
codec->data = ptr = kmalloc(sizeof(struct zr36050), GFP_KERNEL);
if (NULL == ptr) {
dprintk(1, KERN_ERR "zr36050: Can't get enough memory!\n");
return -ENOMEM;
}
memset(ptr, 0, sizeof(struct zr36050));
snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]",
zr36050_codecs);
ptr->num = zr36050_codecs++;
ptr->codec = codec;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_INC_USE_COUNT;
#else
if (!try_module_get(THIS_MODULE)) {
dprintk(1,
KERN_ERR
"zr36050: failed to increase module use count\n");
kfree(ptr);
zr36050_codecs--;
return -ENODEV;
}
#endif
//testing
res = zr36050_basic_test(ptr);
if (res < 0) {
zr36050_unset(codec);
return res;
}
//final setup
memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8);
memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8);
ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag
* (what is the difference?) */
ptr->mode = CODEC_DO_COMPRESSION;
ptr->width = 384;
ptr->height = 288;
ptr->total_code_vol = 16000;
ptr->max_block_vol = 240;
ptr->scalefact = 0x100;
ptr->dri = 1;
zr36050_init(ptr);
dprintk(1, KERN_INFO "%s: codec attached and running\n",
ptr->name);
return 0;
}
static const struct videocodec zr36050_codec = {
.name = "zr36050",
.magic = 0L, // magic not used
.flags =
CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER |
CODEC_FLAG_DECODER,
.type = CODEC_TYPE_ZR36050,
.setup = zr36050_setup, // functionality
.unset = zr36050_unset,
.set_mode = zr36050_set_mode,
.set_video = zr36050_set_video,
.control = zr36050_control,
// others are not used
};
/* =========================================================================
HOOK IN DRIVER AS KERNEL MODULE
========================================================================= */
static int __init
zr36050_init_module (void)
{
//dprintk(1, "ZR36050 driver %s\n",ZR050_VERSION);
zr36050_codecs = 0;
return videocodec_register(&zr36050_codec);
}
static void __exit
zr36050_cleanup_module (void)
{
if (zr36050_codecs) {
dprintk(1,
"zr36050: something's wrong - %d codecs left somehow.\n",
zr36050_codecs);
}
videocodec_unregister(&zr36050_codec);
}
module_init(zr36050_init_module);
module_exit(zr36050_cleanup_module);
MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
MODULE_DESCRIPTION("Driver module for ZR36050 jpeg processors "
ZR050_VERSION);
MODULE_LICENSE("GPL");
/*
* Zoran ZR36050 basic configuration functions - header file
*
* Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
*
* $Id: zr36050.h,v 1.1.2.2 2003/01/14 21:18:22 rbultje Exp $
*
* ------------------------------------------------------------------------
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* ------------------------------------------------------------------------
*/
#ifndef ZR36050_H
#define ZR36050_H
/* data stored for each zoran jpeg codec chip */
struct zr36050 {
char name[32];
int num;
/* io datastructure */
struct videocodec *codec;
// last coder status
__u8 status1;
// actual coder setup
int mode;
__u16 width;
__u16 height;
__u16 bitrate_ctrl;
__u32 total_code_vol;
__u16 max_block_vol;
__u8 h_samp_ratio[8];
__u8 v_samp_ratio[8];
__u16 scalefact;
__u16 dri;
};
/* zr36050 register addresses */
#define ZR050_GO 0x000
#define ZR050_HARDWARE 0x002
#define ZR050_MODE 0x003
#define ZR050_OPTIONS 0x004
#define ZR050_MBCV 0x005
#define ZR050_MARKERS_EN 0x006
#define ZR050_INT_REQ_0 0x007
#define ZR050_INT_REQ_1 0x008
#define ZR050_TCV_NET_HI 0x009
#define ZR050_TCV_NET_MH 0x00a
#define ZR050_TCV_NET_ML 0x00b
#define ZR050_TCV_NET_LO 0x00c
#define ZR050_TCV_DATA_HI 0x00d
#define ZR050_TCV_DATA_MH 0x00e
#define ZR050_TCV_DATA_ML 0x00f
#define ZR050_TCV_DATA_LO 0x010
#define ZR050_SF_HI 0x011
#define ZR050_SF_LO 0x012
#define ZR050_AF_HI 0x013
#define ZR050_AF_M 0x014
#define ZR050_AF_LO 0x015
#define ZR050_ACV_HI 0x016
#define ZR050_ACV_MH 0x017
#define ZR050_ACV_ML 0x018
#define ZR050_ACV_LO 0x019
#define ZR050_ACT_HI 0x01a
#define ZR050_ACT_MH 0x01b
#define ZR050_ACT_ML 0x01c
#define ZR050_ACT_LO 0x01d
#define ZR050_ACV_TRUN_HI 0x01e
#define ZR050_ACV_TRUN_MH 0x01f
#define ZR050_ACV_TRUN_ML 0x020
#define ZR050_ACV_TRUN_LO 0x021
#define ZR050_STATUS_0 0x02e
#define ZR050_STATUS_1 0x02f
#define ZR050_SOF_IDX 0x040
#define ZR050_SOS1_IDX 0x07a
#define ZR050_SOS2_IDX 0x08a
#define ZR050_SOS3_IDX 0x09a
#define ZR050_SOS4_IDX 0x0aa
#define ZR050_DRI_IDX 0x0c0
#define ZR050_DNL_IDX 0x0c6
#define ZR050_DQT_IDX 0x0cc
#define ZR050_DHT_IDX 0x1d4
#define ZR050_APP_IDX 0x380
#define ZR050_COM_IDX 0x3c0
/* zr36050 hardware register bits */
#define ZR050_HW_BSWD 0x80
#define ZR050_HW_MSTR 0x40
#define ZR050_HW_DMA 0x20
#define ZR050_HW_CFIS_1_CLK 0x00
#define ZR050_HW_CFIS_2_CLK 0x04
#define ZR050_HW_CFIS_3_CLK 0x08
#define ZR050_HW_CFIS_4_CLK 0x0C
#define ZR050_HW_CFIS_5_CLK 0x10
#define ZR050_HW_CFIS_6_CLK 0x14
#define ZR050_HW_CFIS_7_CLK 0x18
#define ZR050_HW_CFIS_8_CLK 0x1C
#define ZR050_HW_BELE 0x01
/* zr36050 mode register bits */
#define ZR050_MO_COMP 0x80
#define ZR050_MO_COMP 0x80
#define ZR050_MO_ATP 0x40
#define ZR050_MO_PASS2 0x20
#define ZR050_MO_TLM 0x10
#define ZR050_MO_DCONLY 0x08
#define ZR050_MO_BRC 0x04
#define ZR050_MO_ATP 0x40
#define ZR050_MO_PASS2 0x20
#define ZR050_MO_TLM 0x10
#define ZR050_MO_DCONLY 0x08
/* zr36050 option register bits */
#define ZR050_OP_NSCN_1 0x00
#define ZR050_OP_NSCN_2 0x20
#define ZR050_OP_NSCN_3 0x40
#define ZR050_OP_NSCN_4 0x60
#define ZR050_OP_NSCN_5 0x80
#define ZR050_OP_NSCN_6 0xA0
#define ZR050_OP_NSCN_7 0xC0
#define ZR050_OP_NSCN_8 0xE0
#define ZR050_OP_OVF 0x10
/* zr36050 markers-enable register bits */
#define ZR050_ME_APP 0x80
#define ZR050_ME_COM 0x40
#define ZR050_ME_DRI 0x20
#define ZR050_ME_DQT 0x10
#define ZR050_ME_DHT 0x08
#define ZR050_ME_DNL 0x04
#define ZR050_ME_DQTI 0x02
#define ZR050_ME_DHTI 0x01
/* zr36050 status0/1 register bit masks */
#define ZR050_ST_RST_MASK 0x20
#define ZR050_ST_SOF_MASK 0x02
#define ZR050_ST_SOS_MASK 0x02
#define ZR050_ST_DATRDY_MASK 0x80
#define ZR050_ST_MRKDET_MASK 0x40
#define ZR050_ST_RFM_MASK 0x10
#define ZR050_ST_RFD_MASK 0x08
#define ZR050_ST_END_MASK 0x04
#define ZR050_ST_TCVOVF_MASK 0x02
#define ZR050_ST_DATOVF_MASK 0x01
/* pixel component idx */
#define ZR050_Y_COMPONENT 0
#define ZR050_U_COMPONENT 1
#define ZR050_V_COMPONENT 2
#endif /*fndef ZR36050_H */
/* /*
zr36057.h - zr36057 register offsets * zr36057.h - zr36057 register offsets
*
Copyright (C) 1998 Dave Perks <dperks@ibm.net> * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
*
This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. * (at your option) any later version.
*
This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. * GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#ifndef _ZR36057_H_ #ifndef _ZR36057_H_
#define _ZR36057_H_ #define _ZR36057_H_
...@@ -24,19 +24,19 @@ ...@@ -24,19 +24,19 @@
/* Zoran ZR36057 registers */ /* Zoran ZR36057 registers */
#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ #define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */
#define ZR36057_VFEHCR_HSPol (1<<30) #define ZR36057_VFEHCR_HSPol (1<<30)
#define ZR36057_VFEHCR_HStart 10 #define ZR36057_VFEHCR_HStart 10
#define ZR36057_VFEHCR_HEnd 0 #define ZR36057_VFEHCR_HEnd 0
#define ZR36057_VFEHCR_Hmask 0x3ff #define ZR36057_VFEHCR_Hmask 0x3ff
#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ #define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */
#define ZR36057_VFEVCR_VSPol (1<<30) #define ZR36057_VFEVCR_VSPol (1<<30)
#define ZR36057_VFEVCR_VStart 10 #define ZR36057_VFEVCR_VStart 10
#define ZR36057_VFEVCR_VEnd 0 #define ZR36057_VFEVCR_VEnd 0
#define ZR36057_VFEVCR_Vmask 0x3ff #define ZR36057_VFEVCR_Vmask 0x3ff
#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ #define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */
#define ZR36057_VFESPFR_ExtFl (1<<26) #define ZR36057_VFESPFR_ExtFl (1<<26)
#define ZR36057_VFESPFR_TopField (1<<25) #define ZR36057_VFESPFR_TopField (1<<25)
#define ZR36057_VFESPFR_VCLKPol (1<<24) #define ZR36057_VFESPFR_VCLKPol (1<<24)
...@@ -52,65 +52,65 @@ ...@@ -52,65 +52,65 @@
#define ZR36057_VFESPFR_Pack24 (1<<1) #define ZR36057_VFESPFR_Pack24 (1<<1)
#define ZR36057_VFESPFR_LittleEndian (1<<0) #define ZR36057_VFESPFR_LittleEndian (1<<0)
#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ #define ZR36057_VDTR 0x00c /* Video Display "Top" Register */
#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ #define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */
#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ #define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */
#define ZR36057_VSSFGR_DispStride 16 #define ZR36057_VSSFGR_DispStride 16
#define ZR36057_VSSFGR_VidOvf (1<<8) #define ZR36057_VSSFGR_VidOvf (1<<8)
#define ZR36057_VSSFGR_SnapShot (1<<1) #define ZR36057_VSSFGR_SnapShot (1<<1)
#define ZR36057_VSSFGR_FrameGrab (1<<0) #define ZR36057_VSSFGR_FrameGrab (1<<0)
#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ #define ZR36057_VDCR 0x018 /* Video Display Configuration Register */
#define ZR36057_VDCR_VidEn (1<<31) #define ZR36057_VDCR_VidEn (1<<31)
#define ZR36057_VDCR_MinPix 24 #define ZR36057_VDCR_MinPix 24
#define ZR36057_VDCR_Triton (1<<24) #define ZR36057_VDCR_Triton (1<<24)
#define ZR36057_VDCR_VidWinHt 12 #define ZR36057_VDCR_VidWinHt 12
#define ZR36057_VDCR_VidWinWid 0 #define ZR36057_VDCR_VidWinWid 0
#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ #define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */
#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ #define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */
#define ZR36057_OCR 0x024 /* Overlay Control Register */ #define ZR36057_OCR 0x024 /* Overlay Control Register */
#define ZR36057_OCR_OvlEnable (1 << 15) #define ZR36057_OCR_OvlEnable (1 << 15)
#define ZR36057_OCR_MaskStride 0 #define ZR36057_OCR_MaskStride 0
#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ #define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */
#define ZR36057_SPGPPCR_SoftReset (1<<24) #define ZR36057_SPGPPCR_SoftReset (1<<24)
#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ #define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */
#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ #define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */
#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ #define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */
#define ZR36057_MCTCR_CodTime (1 << 30) #define ZR36057_MCTCR_CodTime (1 << 30)
#define ZR36057_MCTCR_CEmpty (1 << 29) #define ZR36057_MCTCR_CEmpty (1 << 29)
#define ZR36057_MCTCR_CFlush (1 << 28) #define ZR36057_MCTCR_CFlush (1 << 28)
#define ZR36057_MCTCR_CodGuestID 20 #define ZR36057_MCTCR_CodGuestID 20
#define ZR36057_MCTCR_CodGuestReg 16 #define ZR36057_MCTCR_CodGuestReg 16
#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ #define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */
#define ZR36057_ISR 0x03c /* Interrupt Status Register */ #define ZR36057_ISR 0x03c /* Interrupt Status Register */
#define ZR36057_ISR_GIRQ1 (1<<30) #define ZR36057_ISR_GIRQ1 (1<<30)
#define ZR36057_ISR_GIRQ0 (1<<29) #define ZR36057_ISR_GIRQ0 (1<<29)
#define ZR36057_ISR_CodRepIRQ (1<<28) #define ZR36057_ISR_CodRepIRQ (1<<28)
#define ZR36057_ISR_JPEGRepIRQ (1<<27) #define ZR36057_ISR_JPEGRepIRQ (1<<27)
#define ZR36057_ICR 0x040 /* Interrupt Control Register */ #define ZR36057_ICR 0x040 /* Interrupt Control Register */
#define ZR36057_ICR_GIRQ1 (1<<30) #define ZR36057_ICR_GIRQ1 (1<<30)
#define ZR36057_ICR_GIRQ0 (1<<29) #define ZR36057_ICR_GIRQ0 (1<<29)
#define ZR36057_ICR_CodRepIRQ (1<<28) #define ZR36057_ICR_CodRepIRQ (1<<28)
#define ZR36057_ICR_JPEGRepIRQ (1<<27) #define ZR36057_ICR_JPEGRepIRQ (1<<27)
#define ZR36057_ICR_IntPinEn (1<<24) #define ZR36057_ICR_IntPinEn (1<<24)
#define ZR36057_I2CBR 0x044 /* I2C Bus Register */ #define ZR36057_I2CBR 0x044 /* I2C Bus Register */
#define ZR36057_I2CBR_SDA (1<<1) #define ZR36057_I2CBR_SDA (1<<1)
#define ZR36057_I2CBR_SCL (1<<0) #define ZR36057_I2CBR_SCL (1<<0)
#define ZR36057_JMC 0x100 /* JPEG Mode and Control */ #define ZR36057_JMC 0x100 /* JPEG Mode and Control */
#define ZR36057_JMC_JPG (1 << 31) #define ZR36057_JMC_JPG (1 << 31)
#define ZR36057_JMC_JPGExpMode (0 << 29) #define ZR36057_JMC_JPGExpMode (0 << 29)
#define ZR36057_JMC_JPGCmpMode (1 << 29) #define ZR36057_JMC_JPGCmpMode (1 << 29)
...@@ -124,45 +124,45 @@ ...@@ -124,45 +124,45 @@
#define ZR36057_JMC_CFIFO_FB (1 << 1) #define ZR36057_JMC_CFIFO_FB (1 << 1)
#define ZR36057_JMC_Stll_LitEndian (1 << 0) #define ZR36057_JMC_Stll_LitEndian (1 << 0)
#define ZR36057_JPC 0x104 /* JPEG Process Control */ #define ZR36057_JPC 0x104 /* JPEG Process Control */
#define ZR36057_JPC_P_Reset (1 << 7) #define ZR36057_JPC_P_Reset (1 << 7)
#define ZR36057_JPC_CodTrnsEn (1 << 5) #define ZR36057_JPC_CodTrnsEn (1 << 5)
#define ZR36057_JPC_Active (1 << 0) #define ZR36057_JPC_Active (1 << 0)
#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ #define ZR36057_VSP 0x108 /* Vertical Sync Parameters */
#define ZR36057_VSP_VsyncSize 16 #define ZR36057_VSP_VsyncSize 16
#define ZR36057_VSP_FrmTot 0 #define ZR36057_VSP_FrmTot 0
#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ #define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */
#define ZR36057_HSP_HsyncStart 16 #define ZR36057_HSP_HsyncStart 16
#define ZR36057_HSP_LineTot 0 #define ZR36057_HSP_LineTot 0
#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ #define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */
#define ZR36057_FHAP_NAX 16 #define ZR36057_FHAP_NAX 16
#define ZR36057_FHAP_PAX 0 #define ZR36057_FHAP_PAX 0
#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */ #define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */
#define ZR36057_FVAP_NAY 16 #define ZR36057_FVAP_NAY 16
#define ZR36057_FVAP_PAY 0 #define ZR36057_FVAP_PAY 0
#define ZR36057_FPP 0x118 /* Field Process Parameters */ #define ZR36057_FPP 0x118 /* Field Process Parameters */
#define ZR36057_FPP_Odd_Even (1 << 0) #define ZR36057_FPP_Odd_Even (1 << 0)
#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ #define ZR36057_JCBA 0x11c /* JPEG Code Base Address */
#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ #define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */
#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ #define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */
#define ZR36057_JCGI_JPEGuestID 4 #define ZR36057_JCGI_JPEGuestID 4
#define ZR36057_JCGI_JPEGuestReg 0 #define ZR36057_JCGI_JPEGuestReg 0
#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ #define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */
#define ZR36057_POR 0x200 /* Post Office Register */ #define ZR36057_POR 0x200 /* Post Office Register */
#define ZR36057_POR_POPen (1<<25) #define ZR36057_POR_POPen (1<<25)
#define ZR36057_POR_POTime (1<<24) #define ZR36057_POR_POTime (1<<24)
#define ZR36057_POR_PODir (1<<23) #define ZR36057_POR_PODir (1<<23)
#define ZR36057_STR 0x300 /* "Still" Transfer Register */ #define ZR36057_STR 0x300 /* "Still" Transfer Register */
#endif #endif
/*
* Zoran ZR36060 basic configuration functions
*
* Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be>
*
* $Id: zr36060.c,v 1.1.2.22 2003/05/06 09:35:36 rbultje Exp $
*
* ------------------------------------------------------------------------
*
* 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.
*
* ------------------------------------------------------------------------
*/
#define ZR060_VERSION "v0.7"
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/wait.h>
/* includes for structures and defines regarding video
#include<linux/videodev.h> */
/* I/O commands, error codes */
#include<asm/io.h>
//#include<errno.h>
/* headerfile of this module */
#include"zr36060.h"
/* codec io API */
#include"videocodec.h"
/* it doesn't make sense to have more than 20 or so,
just to prevent some unwanted loops */
#define MAX_CODECS 20
/* amount of chips attached via this driver */
static int zr36060_codecs = 0;
static int low_bitrate = 0;
MODULE_PARM(low_bitrate, "i");
MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate");
/* debugging is available via module parameter */
static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug level (0-4)");
#define dprintk(num, format, args...) \
do { \
if (debug >= num) \
printk(format, ##args); \
} while (0)
/* =========================================================================
Local hardware I/O functions:
read/write via codec layer (registers are located in the master device)
========================================================================= */
/* read and write functions */
static u8
zr36060_read (struct zr36060 *ptr,
u16 reg)
{
u8 value = 0;
// just in case something is wrong...
if (ptr->codec->master_data->readreg)
value = (ptr->codec->master_data->readreg(ptr->codec,
reg)) & 0xff;
else
dprintk(1,
KERN_ERR "%s: invalid I/O setup, nothing read!\n",
ptr->name);
//dprintk(4, "%s: reading from 0x%04x: %02x\n",ptr->name,reg,value);
return value;
}
static void
zr36060_write(struct zr36060 *ptr,
u16 reg,
u8 value)
{
//dprintk(4, "%s: writing 0x%02x to 0x%04x\n",ptr->name,value,reg);
dprintk(4, "0x%02x @0x%04x\n", value, reg);
// just in case something is wrong...
if (ptr->codec->master_data->writereg)
ptr->codec->master_data->writereg(ptr->codec, reg, value);
else
dprintk(1,
KERN_ERR
"%s: invalid I/O setup, nothing written!\n",
ptr->name);
}
/* =========================================================================
Local helper function:
status read
========================================================================= */
/* status is kept in datastructure */
static u8
zr36060_read_status (struct zr36060 *ptr)
{
ptr->status = zr36060_read(ptr, ZR060_CFSR);
zr36060_read(ptr, 0);
return ptr->status;
}
/* =========================================================================
Local helper function:
scale factor read
========================================================================= */
/* scale factor is kept in datastructure */
static u16
zr36060_read_scalefactor (struct zr36060 *ptr)
{
ptr->scalefact = (zr36060_read(ptr, ZR060_SF_HI) << 8) |
(zr36060_read(ptr, ZR060_SF_LO) & 0xFF);
/* leave 0 selected for an eventually GO from master */
zr36060_read(ptr, 0);
return ptr->scalefact;
}
/* =========================================================================
Local helper function:
wait if codec is ready to proceed (end of processing) or time is over
========================================================================= */
static void
zr36060_wait_end (struct zr36060 *ptr)
{
int i = 0;
while (zr36060_read_status(ptr) & ZR060_CFSR_Busy) {
udelay(1);
if (i++ > 200000) { // 200ms, there is for shure something wrong!!!
dprintk(1,
"%s: timout at wait_end (last status: 0x%02x)\n",
ptr->name, ptr->status);
break;
}
}
}
/* =========================================================================
Local helper function:
basic test of "connectivity", writes/reads to/from memory the SOF marker
========================================================================= */
static int
zr36060_basic_test (struct zr36060 *ptr)
{
if ((zr36060_read(ptr, ZR060_IDR_DEV) != 0x33) &&
(zr36060_read(ptr, ZR060_IDR_REV) != 0x01)) {
dprintk(1,
KERN_ERR
"%s: attach failed, can't connect to jpeg processor!\n",
ptr->name);
return -ENXIO;
}
zr36060_wait_end(ptr);
if (ptr->status & ZR060_CFSR_Busy) {
dprintk(1,
KERN_ERR
"%s: attach failed, jpeg processor failed (end flag)!\n",
ptr->name);
return -EBUSY;
}
return 0; /* looks good! */
}
/* =========================================================================
Local helper function:
simple loop for pushing the init datasets
========================================================================= */
static int
zr36060_pushit (struct zr36060 *ptr,
u16 startreg,
u16 len,
const char *data)
{
int i = 0;
dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name,
startreg, len);
while (i < len) {
zr36060_write(ptr, startreg++, data[i++]);
}
return i;
}
/* =========================================================================
Basic datasets:
jpeg baseline setup data (you find it on lots places in internet, or just
extract it from any regular .jpg image...)
Could be variable, but until it's not needed it they are just fixed to save
memory. Otherwise expand zr36060 structure with arrays, push the values to
it and initalize from there, as e.g. the linux zr36057/60 driver does it.
========================================================================= */
static const char zr36060_dqt[0x86] = {
0xff, 0xdb, //Marker: DQT
0x00, 0x84, //Length: 2*65+2
0x00, //Pq,Tq first table
0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
0x01, //Pq,Tq second table
0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
};
static const char zr36060_dht[0x1a4] = {
0xff, 0xc4, //Marker: DHT
0x01, 0xa2, //Length: 2*AC, 2*DC
0x00, //DC first table
0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
0x01, //DC second table
0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
0x10, //AC first table
0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11,
0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24,
0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
0xF8, 0xF9, 0xFA,
0x11, //AC second table
0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
0x07, 0x05, 0x04, 0x04, 0x00, 0x01,
0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25,
0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
0xF9, 0xFA
};
static const char zr36060_app[0x40] = {
0xff, 0xe0, //Marker: APP0
0x00, 0x07, //Length: 7
' ', 'A', 'V', 'I', '1', 0, 0, 0, // 'AVI' field
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
static const char zr36060_com[0x40] = {
0xff, 0xfe, //Marker: COM
0x00, 0x06, //Length: 6
' ', 'C', 'O', 'M', 0, 0, 0, 0, // 'COM' field
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */
#define NO_OF_COMPONENTS 0x3 //Y,U,V
#define BASELINE_PRECISION 0x8 //MCU size (?)
static const char zr36060_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT
static const char zr36060_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC
static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC
/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */
static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 };
static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 };
/* =========================================================================
Local helper functions:
calculation and setup of parameter-dependent JPEG baseline segments
(needed for compression only)
========================================================================= */
/* ------------------------------------------------------------------------- */
/* SOF (start of frame) segment depends on width, height and sampling ratio
of each color component */
static int
zr36060_set_sof (struct zr36060 *ptr)
{
char sof_data[34]; // max. size of register set
int i;
dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name,
ptr->width, ptr->height, NO_OF_COMPONENTS);
sof_data[0] = 0xff;
sof_data[1] = 0xc0;
sof_data[2] = 0x00;
sof_data[3] = (3 * NO_OF_COMPONENTS) + 8;
sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36060
sof_data[5] = (ptr->height) >> 8;
sof_data[6] = (ptr->height) & 0xff;
sof_data[7] = (ptr->width) >> 8;
sof_data[8] = (ptr->width) & 0xff;
sof_data[9] = NO_OF_COMPONENTS;
for (i = 0; i < NO_OF_COMPONENTS; i++) {
sof_data[10 + (i * 3)] = i; // index identifier
sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) |
(ptr->v_samp_ratio[i]); // sampling ratios
sof_data[12 + (i * 3)] = zr36060_tq[i]; // Q table selection
}
return zr36060_pushit(ptr, ZR060_SOF_IDX,
(3 * NO_OF_COMPONENTS) + 10, sof_data);
}
/* ------------------------------------------------------------------------- */
/* SOS (start of scan) segment depends on the used scan components
of each color component */
static int
zr36060_set_sos (struct zr36060 *ptr)
{
char sos_data[16]; // max. size of register set
int i;
dprintk(3, "%s: write SOS\n", ptr->name);
sos_data[0] = 0xff;
sos_data[1] = 0xda;
sos_data[2] = 0x00;
sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3;
sos_data[4] = NO_OF_COMPONENTS;
for (i = 0; i < NO_OF_COMPONENTS; i++) {
sos_data[5 + (i * 2)] = i; // index
sos_data[6 + (i * 2)] = (zr36060_td[i] << 4) |
zr36060_ta[i]; // AC/DC tbl.sel.
}
sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start
sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3f;
sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00;
return zr36060_pushit(ptr, ZR060_SOS_IDX,
4 + 1 + (2 * NO_OF_COMPONENTS) + 3,
sos_data);
}
/* ------------------------------------------------------------------------- */
/* DRI (define restart interval) */
static int
zr36060_set_dri (struct zr36060 *ptr)
{
char dri_data[6]; // max. size of register set
dprintk(3, "%s: write DRI\n", ptr->name);
dri_data[0] = 0xff;
dri_data[1] = 0xdd;
dri_data[2] = 0x00;
dri_data[3] = 0x04;
dri_data[4] = (ptr->dri) >> 8;
dri_data[5] = (ptr->dri) & 0xff;
return zr36060_pushit(ptr, ZR060_DRI_IDX, 6, dri_data);
}
/* =========================================================================
Setup function:
Setup compression/decompression of Zoran's JPEG processor
( see also zoran 36060 manual )
... sorry for the spaghetti code ...
========================================================================= */
static void
zr36060_init (struct zr36060 *ptr)
{
int sum = 0;
long bitcnt, tmp;
if (ptr->mode == CODEC_DO_COMPRESSION) {
dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name);
zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
/* 060 communicates with 067 in master mode */
zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr);
/* Compression with or without variable scale factor */
/*FIXME: What about ptr->bitrate_ctrl? */
zr36060_write(ptr, ZR060_CMR,
ZR060_CMR_Comp | ZR060_CMR_Pass2 |
ZR060_CMR_BRB);
/* Must be zero */
zr36060_write(ptr, ZR060_MBZ, 0x00);
zr36060_write(ptr, ZR060_TCR_HI, 0x00);
zr36060_write(ptr, ZR060_TCR_LO, 0x00);
/* Disable all IRQs - no DataErr means autoreset */
zr36060_write(ptr, ZR060_IMR, 0);
/* volume control settings */
zr36060_write(ptr, ZR060_SF_HI, ptr->scalefact >> 8);
zr36060_write(ptr, ZR060_SF_LO, ptr->scalefact & 0xff);
zr36060_write(ptr, ZR060_AF_HI, 0xff);
zr36060_write(ptr, ZR060_AF_M, 0xff);
zr36060_write(ptr, ZR060_AF_LO, 0xff);
/* setup the variable jpeg tables */
sum += zr36060_set_sof(ptr);
sum += zr36060_set_sos(ptr);
sum += zr36060_set_dri(ptr);
/* setup the fixed jpeg tables - maybe variable, though -
* (see table init section above) */
sum +=
zr36060_pushit(ptr, ZR060_DQT_IDX, sizeof(zr36060_dqt),
zr36060_dqt);
sum +=
zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht),
zr36060_dht);
sum +=
zr36060_pushit(ptr, ZR060_APP_IDX, sizeof(zr36060_app),
zr36060_app);
sum +=
zr36060_pushit(ptr, ZR060_COM_IDX, sizeof(zr36060_com),
zr36060_com);
/* setup misc. data for compression (target code sizes) */
/* size of compressed code to reach without header data */
sum = ptr->real_code_vol - sum;
bitcnt = sum << 3; /* need the size in bits */
tmp = bitcnt >> 16;
dprintk(3,
"%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n",
ptr->name, sum, ptr->real_code_vol, bitcnt, tmp);
zr36060_write(ptr, ZR060_TCV_NET_HI, tmp >> 8);
zr36060_write(ptr, ZR060_TCV_NET_MH, tmp & 0xff);
tmp = bitcnt & 0xffff;
zr36060_write(ptr, ZR060_TCV_NET_ML, tmp >> 8);
zr36060_write(ptr, ZR060_TCV_NET_LO, tmp & 0xff);
bitcnt -= bitcnt >> 7; // bits without stuffing
bitcnt -= ((bitcnt * 5) >> 6); // bits without eob
tmp = bitcnt >> 16;
dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n",
ptr->name, bitcnt, tmp);
zr36060_write(ptr, ZR060_TCV_DATA_HI, tmp >> 8);
zr36060_write(ptr, ZR060_TCV_DATA_MH, tmp & 0xff);
tmp = bitcnt & 0xffff;
zr36060_write(ptr, ZR060_TCV_DATA_ML, tmp >> 8);
zr36060_write(ptr, ZR060_TCV_DATA_LO, tmp & 0xff);
/* JPEG markers to be included in the compressed stream */
zr36060_write(ptr, ZR060_MER,
ZR060_MER_App | ZR060_MER_Com | ZR060_MER_DQT
| ZR060_MER_DHT);
/* Setup the Video Frontend */
/* Limit pixel range to 16..235 as per CCIR-601 */
zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range);
} else {
dprintk(2, "%s: EXPANSION SETUP\n", ptr->name);
zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
/* 060 communicates with 067 in master mode */
zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr);
/* Decompression */
zr36060_write(ptr, ZR060_CMR, 0);
/* Must be zero */
zr36060_write(ptr, ZR060_MBZ, 0x00);
zr36060_write(ptr, ZR060_TCR_HI, 0x00);
zr36060_write(ptr, ZR060_TCR_LO, 0x00);
/* Disable all IRQs - no DataErr means autoreset */
zr36060_write(ptr, ZR060_IMR, 0);
/* setup misc. data for expansion */
zr36060_write(ptr, ZR060_MER, 0);
/* setup the fixed jpeg tables - maybe variable, though -
* (see table init section above) */
zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht),
zr36060_dht);
/* Setup the Video Frontend */
//zr36060_write(ptr, ZR060_VCR, ZR060_VCR_FIExt);
//this doesn't seem right and doesn't work...
zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range);
}
/* Load the tables */
zr36060_write(ptr, ZR060_LOAD,
ZR060_LOAD_SyncRst | ZR060_LOAD_Load);
zr36060_wait_end(ptr);
dprintk(2, "%s: Status after table preload: 0x%02x\n", ptr->name,
ptr->status);
if (ptr->status & ZR060_CFSR_Busy) {
dprintk(1, KERN_ERR "%s: init aborted!\n", ptr->name);
return; // something is wrong, its timed out!!!!
}
}
/* =========================================================================
CODEC API FUNCTIONS
this functions are accessed by the master via the API structure
========================================================================= */
/* set compression/expansion mode and launches codec -
this should be the last call from the master before starting processing */
static int
zr36060_set_mode (struct videocodec *codec,
int mode)
{
struct zr36060 *ptr = (struct zr36060 *) codec->data;
dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
return -EINVAL;
ptr->mode = mode;
zr36060_init(ptr);
return 0;
}
/* set picture size (norm is ignored as the codec doesn't know about it) */
static int
zr36060_set_video (struct videocodec *codec,
struct tvnorm *norm,
struct vfe_settings *cap,
struct vfe_polarity *pol)
{
struct zr36060 *ptr = (struct zr36060 *) codec->data;
u32 reg;
int size;
dprintk(2, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name,
cap->x, cap->y, cap->width, cap->height, cap->decimation);
/* if () return -EINVAL;
* trust the master driver that it knows what it does - so
* we allow invalid startx/y and norm for now ... */
ptr->width = cap->width / (cap->decimation & 0xff);
ptr->height = cap->height / (cap->decimation >> 8);
zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
/* Note that VSPol/HSPol bits in zr36060 have the opposite
* meaning of their zr360x7 counterparts with the same names
* N.b. for VSPol this is only true if FIVEdge = 0 (default,
* left unchanged here - in accordance with datasheet).
*/
reg = (!pol->vsync_pol ? ZR060_VPR_VSPol : 0)
| (!pol->hsync_pol ? ZR060_VPR_HSPol : 0)
| (pol->field_pol ? ZR060_VPR_FIPol : 0)
| (pol->blank_pol ? ZR060_VPR_BLPol : 0)
| (pol->subimg_pol ? ZR060_VPR_SImgPol : 0)
| (pol->poe_pol ? ZR060_VPR_PoePol : 0)
| (pol->pvalid_pol ? ZR060_VPR_PValPol : 0)
| (pol->vclk_pol ? ZR060_VPR_VCLKPol : 0);
zr36060_write(ptr, ZR060_VPR, reg);
reg = 0;
switch (cap->decimation & 0xff) {
default:
case 1:
break;
case 2:
reg |= ZR060_SR_HScale2;
break;
case 4:
reg |= ZR060_SR_HScale4;
break;
}
switch (cap->decimation >> 8) {
default:
case 1:
break;
case 2:
reg |= ZR060_SR_VScale;
break;
}
zr36060_write(ptr, ZR060_SR, reg);
zr36060_write(ptr, ZR060_BCR_Y, 0x00);
zr36060_write(ptr, ZR060_BCR_U, 0x80);
zr36060_write(ptr, ZR060_BCR_V, 0x80);
/* sync generator */
reg = norm->Ht - 1; /* Vtotal */
zr36060_write(ptr, ZR060_SGR_VTOTAL_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_SGR_VTOTAL_LO, (reg >> 0) & 0xff);
reg = norm->Wt - 1; /* Htotal */
zr36060_write(ptr, ZR060_SGR_HTOTAL_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_SGR_HTOTAL_LO, (reg >> 0) & 0xff);
reg = 6 - 1; /* VsyncSize */
zr36060_write(ptr, ZR060_SGR_VSYNC, reg);
//reg = 30 - 1; /* HsyncSize */
///*CP*/ reg = (zr->params.norm == 1 ? 57 : 68);
reg = 68;
zr36060_write(ptr, ZR060_SGR_HSYNC, reg);
reg = norm->VStart - 1; /* BVstart */
zr36060_write(ptr, ZR060_SGR_BVSTART, reg);
reg += norm->Ha / 2; /* BVend */
zr36060_write(ptr, ZR060_SGR_BVEND_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_SGR_BVEND_LO, (reg >> 0) & 0xff);
reg = norm->HStart - 1; /* BHstart */
zr36060_write(ptr, ZR060_SGR_BHSTART, reg);
reg += norm->Wa; /* BHend */
zr36060_write(ptr, ZR060_SGR_BHEND_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_SGR_BHEND_LO, (reg >> 0) & 0xff);
/* active area */
reg = cap->y + norm->VStart; /* Vstart */
zr36060_write(ptr, ZR060_AAR_VSTART_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_AAR_VSTART_LO, (reg >> 0) & 0xff);
reg += cap->height; /* Vend */
zr36060_write(ptr, ZR060_AAR_VEND_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_AAR_VEND_LO, (reg >> 0) & 0xff);
reg = cap->x + norm->HStart; /* Hstart */
zr36060_write(ptr, ZR060_AAR_HSTART_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_AAR_HSTART_LO, (reg >> 0) & 0xff);
reg += cap->width; /* Hend */
zr36060_write(ptr, ZR060_AAR_HEND_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_AAR_HEND_LO, (reg >> 0) & 0xff);
/* subimage area */
reg = norm->VStart - 4; /* SVstart */
zr36060_write(ptr, ZR060_SWR_VSTART_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_SWR_VSTART_LO, (reg >> 0) & 0xff);
reg += norm->Ha / 2 + 8; /* SVend */
zr36060_write(ptr, ZR060_SWR_VEND_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_SWR_VEND_LO, (reg >> 0) & 0xff);
reg = norm->HStart /*+ 64 */ - 4; /* SHstart */
zr36060_write(ptr, ZR060_SWR_HSTART_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_SWR_HSTART_LO, (reg >> 0) & 0xff);
reg += norm->Wa + 8; /* SHend */
zr36060_write(ptr, ZR060_SWR_HEND_HI, (reg >> 8) & 0xff);
zr36060_write(ptr, ZR060_SWR_HEND_LO, (reg >> 0) & 0xff);
size = ptr->width * ptr->height;
/* Target compressed field size in bits: */
size = size * 16; /* uncompressed size in bits */
/* (Ronald) by default, quality = 100 is a compression
* ratio 1:2. Setting low_bitrate (insmod option) sets
* it to 1:4 (instead of 1:2, zr36060 max) as limit because the
* buz can't handle more at decimation=1... Use low_bitrate if
* you have a Buz, unless you know what you're doing */
size = size * cap->quality / (low_bitrate ? 400 : 200);
/* Lower limit (arbitrary, 1 KB) */
if (size < 8192)
size = 8192;
/* Upper limit: 7/8 of the code buffers */
if (size > ptr->total_code_vol * 7)
size = ptr->total_code_vol * 7;
ptr->real_code_vol = size >> 3; /* in bytes */
/* the MBCVR is the *maximum* block volume, according to the
* JPEG ISO specs, this shouldn't be used, since that allows
* for the best encoding quality. So set it to it's max value */
reg = ptr->max_block_vol;
zr36060_write(ptr, ZR060_MBCVR, reg);
return 0;
}
/* additional control functions */
static int
zr36060_control (struct videocodec *codec,
int type,
int size,
void *data)
{
struct zr36060 *ptr = (struct zr36060 *) codec->data;
int *ival = (int *) data;
dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
size);
switch (type) {
case CODEC_G_STATUS: /* get last status */
if (size != sizeof(int))
return -EFAULT;
zr36060_read_status(ptr);
*ival = ptr->status;
break;
case CODEC_G_CODEC_MODE:
if (size != sizeof(int))
return -EFAULT;
*ival = CODEC_MODE_BJPG;
break;
case CODEC_S_CODEC_MODE:
if (size != sizeof(int))
return -EFAULT;
if (*ival != CODEC_MODE_BJPG)
return -EINVAL;
/* not needed, do nothing */
return 0;
case CODEC_G_VFE:
case CODEC_S_VFE:
/* not needed, do nothing */
return 0;
case CODEC_S_MMAP:
/* not available, give an error */
return -ENXIO;
case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */
if (size != sizeof(int))
return -EFAULT;
*ival = ptr->total_code_vol;
break;
case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */
if (size != sizeof(int))
return -EFAULT;
ptr->total_code_vol = *ival;
ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
break;
case CODEC_G_JPEG_SCALE: /* get scaling factor */
if (size != sizeof(int))
return -EFAULT;
*ival = zr36060_read_scalefactor(ptr);
break;
case CODEC_S_JPEG_SCALE: /* set scaling factor */
if (size != sizeof(int))
return -EFAULT;
ptr->scalefact = *ival;
break;
default:
return -EINVAL;
}
return size;
}
/* =========================================================================
Exit and unregister function:
Deinitializes Zoran's JPEG processor
========================================================================= */
static int
zr36060_unset (struct videocodec *codec)
{
struct zr36060 *ptr = codec->data;
if (ptr) {
/* do wee need some codec deinit here, too ???? */
dprintk(1, "%s: finished codec #%d\n", ptr->name,
ptr->num);
kfree(ptr);
codec->data = NULL;
zr36060_codecs--;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_DEC_USE_COUNT;
#else
module_put(THIS_MODULE);
#endif
return 0;
}
return -EFAULT;
}
/* =========================================================================
Setup and registry function:
Initializes Zoran's JPEG processor
Also sets pixel size, average code size, mode (compr./decompr.)
(the given size is determined by the processor with the video interface)
========================================================================= */
static int
zr36060_setup (struct videocodec *codec)
{
struct zr36060 *ptr;
int res;
dprintk(2, "zr36060: initializing MJPEG subsystem #%d.\n",
zr36060_codecs);
if (zr36060_codecs == MAX_CODECS) {
dprintk(1,
KERN_ERR "zr36060: Can't attach more codecs!\n");
return -ENOSPC;
}
//mem structure init
codec->data = ptr = kmalloc(sizeof(struct zr36060), GFP_KERNEL);
if (NULL == ptr) {
dprintk(1, KERN_ERR "zr36060: Can't get enough memory!\n");
return -ENOMEM;
}
memset(ptr, 0, sizeof(struct zr36060));
snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]",
zr36060_codecs);
ptr->num = zr36060_codecs++;
ptr->codec = codec;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_INC_USE_COUNT;
#else
if (!try_module_get(THIS_MODULE)) {
dprintk(1,
KERN_ERR
"zr36060: failed to increase module use count\n");
kfree(ptr);
zr36060_codecs--;
return -ENODEV;
}
#endif
//testing
res = zr36060_basic_test(ptr);
if (res < 0) {
zr36060_unset(codec);
return res;
}
//final setup
memcpy(ptr->h_samp_ratio, zr36060_decimation_h, 8);
memcpy(ptr->v_samp_ratio, zr36060_decimation_v, 8);
ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag
* (what is the difference?) */
ptr->mode = CODEC_DO_COMPRESSION;
ptr->width = 384;
ptr->height = 288;
ptr->total_code_vol = 16000; /* CHECKME */
ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
ptr->max_block_vol = 240; /* CHECKME, was 120 is 240 */
ptr->scalefact = 0x100;
ptr->dri = 1; /* CHECKME, was 8 is 1 */
zr36060_init(ptr);
dprintk(1, KERN_INFO "%s: codec attached and running\n",
ptr->name);
return 0;
}
static const struct videocodec zr36060_codec = {
.name = "zr36060",
.magic = 0L, // magic not used
.flags =
CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER |
CODEC_FLAG_DECODER | CODEC_FLAG_VFE,
.type = CODEC_TYPE_ZR36060,
.setup = zr36060_setup, // functionality
.unset = zr36060_unset,
.set_mode = zr36060_set_mode,
.set_video = zr36060_set_video,
.control = zr36060_control,
// others are not used
};
/* =========================================================================
HOOK IN DRIVER AS KERNEL MODULE
========================================================================= */
static int __init
zr36060_init_module (void)
{
//dprintk(1, "zr36060 driver %s\n",ZR060_VERSION);
zr36060_codecs = 0;
return videocodec_register(&zr36060_codec);
}
static void __exit
zr36060_cleanup_module (void)
{
if (zr36060_codecs) {
dprintk(1,
"zr36060: something's wrong - %d codecs left somehow.\n",
zr36060_codecs);
}
/* however, we can't just stay alive */
videocodec_unregister(&zr36060_codec);
}
module_init(zr36060_init_module);
module_exit(zr36060_cleanup_module);
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@skynet.be>");
MODULE_DESCRIPTION("Driver module for ZR36060 jpeg processors "
ZR060_VERSION);
MODULE_LICENSE("GPL");
/* /*
zr36060.h - zr36060 register offsets * Zoran ZR36060 basic configuration functions - header file
*
* Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be>
*
* $Id: zr36060.h,v 1.1.1.1.2.3 2003/01/14 21:18:47 rbultje Exp $
*
* ------------------------------------------------------------------------
*
* 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.
*
* ------------------------------------------------------------------------
*/
Copyright (C) 1998 Dave Perks <dperks@ibm.net> #ifndef ZR36060_H
#define ZR36060_H
This program is free software; you can redistribute it and/or modify /* data stored for each zoran jpeg codec chip */
it under the terms of the GNU General Public License as published by struct zr36060 {
the Free Software Foundation; either version 2 of the License, or char name[32];
(at your option) any later version. int num;
/* io datastructure */
struct videocodec *codec;
// last coder status
__u8 status;
// actual coder setup
int mode;
This program is distributed in the hope that it will be useful, __u16 width;
but WITHOUT ANY WARRANTY; without even the implied warranty of __u16 height;
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 __u16 bitrate_ctrl;
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ZR36060_H_ __u32 total_code_vol;
#define _ZR36060_H_ __u32 real_code_vol;
__u16 max_block_vol;
__u8 h_samp_ratio[8];
__u8 v_samp_ratio[8];
__u16 scalefact;
__u16 dri;
};
/* Zoran ZR36060 registers */ /* ZR36060 register addresses */
#define ZR060_LOAD 0x000
#define ZR060_CFSR 0x001
#define ZR060_CIR 0x002
#define ZR060_CMR 0x003
#define ZR060_MBZ 0x004
#define ZR060_MBCVR 0x005
#define ZR060_MER 0x006
#define ZR060_IMR 0x007
#define ZR060_ISR 0x008
#define ZR060_TCV_NET_HI 0x009
#define ZR060_TCV_NET_MH 0x00a
#define ZR060_TCV_NET_ML 0x00b
#define ZR060_TCV_NET_LO 0x00c
#define ZR060_TCV_DATA_HI 0x00d
#define ZR060_TCV_DATA_MH 0x00e
#define ZR060_TCV_DATA_ML 0x00f
#define ZR060_TCV_DATA_LO 0x010
#define ZR060_SF_HI 0x011
#define ZR060_SF_LO 0x012
#define ZR060_AF_HI 0x013
#define ZR060_AF_M 0x014
#define ZR060_AF_LO 0x015
#define ZR060_ACV_HI 0x016
#define ZR060_ACV_MH 0x017
#define ZR060_ACV_ML 0x018
#define ZR060_ACV_LO 0x019
#define ZR060_ACT_HI 0x01a
#define ZR060_ACT_MH 0x01b
#define ZR060_ACT_ML 0x01c
#define ZR060_ACT_LO 0x01d
#define ZR060_ACV_TRUN_HI 0x01e
#define ZR060_ACV_TRUN_MH 0x01f
#define ZR060_ACV_TRUN_ML 0x020
#define ZR060_ACV_TRUN_LO 0x021
#define ZR060_IDR_DEV 0x022
#define ZR060_IDR_REV 0x023
#define ZR060_TCR_HI 0x024
#define ZR060_TCR_LO 0x025
#define ZR060_VCR 0x030
#define ZR060_VPR 0x031
#define ZR060_SR 0x032
#define ZR060_BCR_Y 0x033
#define ZR060_BCR_U 0x034
#define ZR060_BCR_V 0x035
#define ZR060_SGR_VTOTAL_HI 0x036
#define ZR060_SGR_VTOTAL_LO 0x037
#define ZR060_SGR_HTOTAL_HI 0x038
#define ZR060_SGR_HTOTAL_LO 0x039
#define ZR060_SGR_VSYNC 0x03a
#define ZR060_SGR_HSYNC 0x03b
#define ZR060_SGR_BVSTART 0x03c
#define ZR060_SGR_BHSTART 0x03d
#define ZR060_SGR_BVEND_HI 0x03e
#define ZR060_SGR_BVEND_LO 0x03f
#define ZR060_SGR_BHEND_HI 0x040
#define ZR060_SGR_BHEND_LO 0x041
#define ZR060_AAR_VSTART_HI 0x042
#define ZR060_AAR_VSTART_LO 0x043
#define ZR060_AAR_VEND_HI 0x044
#define ZR060_AAR_VEND_LO 0x045
#define ZR060_AAR_HSTART_HI 0x046
#define ZR060_AAR_HSTART_LO 0x047
#define ZR060_AAR_HEND_HI 0x048
#define ZR060_AAR_HEND_LO 0x049
#define ZR060_SWR_VSTART_HI 0x04a
#define ZR060_SWR_VSTART_LO 0x04b
#define ZR060_SWR_VEND_HI 0x04c
#define ZR060_SWR_VEND_LO 0x04d
#define ZR060_SWR_HSTART_HI 0x04e
#define ZR060_SWR_HSTART_LO 0x04f
#define ZR060_SWR_HEND_HI 0x050
#define ZR060_SWR_HEND_LO 0x051
#define ZR36060_LoadParameters 0x000 #define ZR060_SOF_IDX 0x060
#define ZR36060_Load (1<<7) #define ZR060_SOS_IDX 0x07a
#define ZR36060_SyncRst (1<<0) #define ZR060_DRI_IDX 0x0c0
#define ZR060_DQT_IDX 0x0cc
#define ZR060_DHT_IDX 0x1d4
#define ZR060_APP_IDX 0x380
#define ZR060_COM_IDX 0x3c0
#define ZR36060_CodeFifoStatus 0x001 /* ZR36060 LOAD register bits */
#define ZR36060_Load (1<<7)
#define ZR36060_SyncRst (1<<0)
#endif #define ZR060_LOAD_Load (1 << 7)
#define ZR060_LOAD_SyncRst (1 << 0)
/* ZR36060 Code FIFO Status register bits */
#define ZR060_CFSR_Busy (1 << 7)
#define ZR060_CFSR_CBusy (1 << 2)
#define ZR060_CFSR_CFIFO (3 << 0)
/* ZR36060 Code Interface register */
#define ZR060_CIR_Code16 (1 << 7)
#define ZR060_CIR_Endian (1 << 6)
#define ZR060_CIR_CFIS (1 << 2)
#define ZR060_CIR_CodeMstr (1 << 0)
/* ZR36060 Codec Mode register */
#define ZR060_CMR_Comp (1 << 7)
#define ZR060_CMR_ATP (1 << 6)
#define ZR060_CMR_Pass2 (1 << 5)
#define ZR060_CMR_TLM (1 << 4)
#define ZR060_CMR_BRB (1 << 2)
#define ZR060_CMR_FSF (1 << 1)
/* ZR36060 Markers Enable register */
#define ZR060_MER_App (1 << 7)
#define ZR060_MER_Com (1 << 6)
#define ZR060_MER_DRI (1 << 5)
#define ZR060_MER_DQT (1 << 4)
#define ZR060_MER_DHT (1 << 3)
/* ZR36060 Interrupt Mask register */
#define ZR060_IMR_EOAV (1 << 3)
#define ZR060_IMR_EOI (1 << 2)
#define ZR060_IMR_End (1 << 1)
#define ZR060_IMR_DataErr (1 << 0)
/* ZR36060 Interrupt Status register */
#define ZR060_ISR_ProCnt (3 << 6)
#define ZR060_ISR_EOAV (1 << 3)
#define ZR060_ISR_EOI (1 << 2)
#define ZR060_ISR_End (1 << 1)
#define ZR060_ISR_DataErr (1 << 0)
/* ZR36060 Video Control register */
#define ZR060_VCR_Video8 (1 << 7)
#define ZR060_VCR_Range (1 << 6)
#define ZR060_VCR_FIDet (1 << 3)
#define ZR060_VCR_FIVedge (1 << 2)
#define ZR060_VCR_FIExt (1 << 1)
#define ZR060_VCR_SyncMstr (1 << 0)
/* ZR36060 Video Polarity register */
#define ZR060_VPR_VCLKPol (1 << 7)
#define ZR060_VPR_PValPol (1 << 6)
#define ZR060_VPR_PoePol (1 << 5)
#define ZR060_VPR_SImgPol (1 << 4)
#define ZR060_VPR_BLPol (1 << 3)
#define ZR060_VPR_FIPol (1 << 2)
#define ZR060_VPR_HSPol (1 << 1)
#define ZR060_VPR_VSPol (1 << 0)
/* ZR36060 Scaling register */
#define ZR060_SR_VScale (1 << 2)
#define ZR060_SR_HScale2 (1 << 0)
#define ZR060_SR_HScale4 (2 << 0)
#endif /*fndef ZR36060_H */
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -87,18 +87,19 @@ ...@@ -87,18 +87,19 @@
#define I2C_DRIVERID_TDA8444 39 /* octuple 6-bit DAC */ #define I2C_DRIVERID_TDA8444 39 /* octuple 6-bit DAC */
#define I2C_DRIVERID_BT819 40 /* video decoder */ #define I2C_DRIVERID_BT819 40 /* video decoder */
#define I2C_DRIVERID_BT856 41 /* video encoder */ #define I2C_DRIVERID_BT856 41 /* video encoder */
#define I2C_DRIVERID_VPX32XX 42 /* video decoder+vbi/vtxt */ #define I2C_DRIVERID_VPX3220 42 /* video decoder+vbi/vtxt */
#define I2C_DRIVERID_DRP3510 43 /* ADR decoder (Astra Radio) */ #define I2C_DRIVERID_DRP3510 43 /* ADR decoder (Astra Radio) */
#define I2C_DRIVERID_SP5055 44 /* Satellite tuner */ #define I2C_DRIVERID_SP5055 44 /* Satellite tuner */
#define I2C_DRIVERID_STV0030 45 /* Multipurpose switch */ #define I2C_DRIVERID_STV0030 45 /* Multipurpose switch */
#define I2C_DRIVERID_SAA7108 46 /* video decoder, image scaler */ #define I2C_DRIVERID_SAA7108 46 /* video decoder, image scaler */
#define I2C_DRIVERID_DS1307 47 /* DS1307 real time clock */ #define I2C_DRIVERID_DS1307 47 /* DS1307 real time clock */
#define I2C_DRIVERID_ADV717x 48 /* ADV 7175/7176 video encoder */ #define I2C_DRIVERID_ADV7175 48 /* ADV 7175/7176 video encoder */
#define I2C_DRIVERID_ZR36067 49 /* Zoran 36067 video encoder */ #define I2C_DRIVERID_SAA7114 49 /* video decoder */
#define I2C_DRIVERID_ZR36120 50 /* Zoran 36120 video encoder */ #define I2C_DRIVERID_ZR36120 50 /* Zoran 36120 video encoder */
#define I2C_DRIVERID_24LC32A 51 /* Microchip 24LC32A 32k EEPROM */ #define I2C_DRIVERID_24LC32A 51 /* Microchip 24LC32A 32k EEPROM */
#define I2C_DRIVERID_STM41T00 52 /* real time clock */ #define I2C_DRIVERID_STM41T00 52 /* real time clock */
#define I2C_DRIVERID_UDA1342 53 /* UDA1342 audio codec */ #define I2C_DRIVERID_UDA1342 53 /* UDA1342 audio codec */
#define I2C_DRIVERID_ADV7170 54 /* video encoder */
...@@ -218,6 +219,7 @@ ...@@ -218,6 +219,7 @@
#define I2C_HW_B_IXP2000 0x16 /* GPIO on IXP2000 systems */ #define I2C_HW_B_IXP2000 0x16 /* GPIO on IXP2000 systems */
#define I2C_HW_B_IXP425 0x17 /* GPIO on IXP425 systems */ #define I2C_HW_B_IXP425 0x17 /* GPIO on IXP425 systems */
#define I2C_HW_B_S3VIA 0x18 /* S3Via ProSavage adapter */ #define I2C_HW_B_S3VIA 0x18 /* S3Via ProSavage adapter */
#define I2C_HW_B_ZR36067 0x19 /* Zoran-36057/36067 based boards */
/* --- PCF 8584 based algorithms */ /* --- PCF 8584 based algorithms */
#define I2C_HW_P_LP 0x00 /* Parallel port interface */ #define I2C_HW_P_LP 0x00 /* Parallel port interface */
......
...@@ -522,6 +522,8 @@ ...@@ -522,6 +522,8 @@
#define PCI_VENDOR_ID_MIRO 0x1031 #define PCI_VENDOR_ID_MIRO 0x1031
#define PCI_DEVICE_ID_MIRO_36050 0x5601 #define PCI_DEVICE_ID_MIRO_36050 0x5601
#define PCI_DEVICE_ID_MIRO_DC10PLUS 0x7efe
#define PCI_DEVICE_ID_MIRO_DC30PLUS 0xd801
#define PCI_VENDOR_ID_NEC 0x1033 #define PCI_VENDOR_ID_NEC 0x1033
#define PCI_DEVICE_ID_NEC_CBUS_1 0x0001 /* PCI-Cbus Bridge */ #define PCI_DEVICE_ID_NEC_CBUS_1 0x0001 /* PCI-Cbus Bridge */
...@@ -1574,6 +1576,9 @@ ...@@ -1574,6 +1576,9 @@
#define PCI_DEVICE_ID_AUREAL_VORTEX_1 0x0001 #define PCI_DEVICE_ID_AUREAL_VORTEX_1 0x0001
#define PCI_DEVICE_ID_AUREAL_VORTEX_2 0x0002 #define PCI_DEVICE_ID_AUREAL_VORTEX_2 0x0002
#define PCI_VENDOR_ID_ELECTRONICDESIGNGMBH 0x12f8
#define PCI_DEVICE_ID_LML_33R10 0x8a02
#define PCI_VENDOR_ID_CBOARDS 0x1307 #define PCI_VENDOR_ID_CBOARDS 0x1307
#define PCI_DEVICE_ID_CBOARDS_DAS1602_16 0x0001 #define PCI_DEVICE_ID_CBOARDS_DAS1602_16 0x0001
...@@ -1683,6 +1688,9 @@ ...@@ -1683,6 +1688,9 @@
#define PCI_VENDOR_ID_3WARE 0x13C1 #define PCI_VENDOR_ID_3WARE 0x13C1
#define PCI_DEVICE_ID_3WARE_1000 0x1000 #define PCI_DEVICE_ID_3WARE_1000 0x1000
#define PCI_VENDOR_ID_IOMEGA 0x13ca
#define PCI_DEVICE_ID_IOMEGA_BUZ 0x4231
#define PCI_VENDOR_ID_ABOCOM 0x13D1 #define PCI_VENDOR_ID_ABOCOM 0x13D1
#define PCI_DEVICE_ID_ABOCOM_2BD1 0x2BD1 #define PCI_DEVICE_ID_ABOCOM_2BD1 0x2BD1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment