GIT fdd92b679d12232fdb58fed6f81bb3cdcc1f8962 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb.git#devel commit fdd92b679d12232fdb58fed6f81bb3cdcc1f8962 Author: Michael Krufky Date: Sat Jul 28 20:02:55 2007 -0300 V4L/DVB (5950): whitespace cleanup for mt2131 and s5h1409 - trivial whitespace cleanups - add "c-basic-offset: 8" to enforce tabbing style in emacs Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 244861a87426f520247332b7cae1b4fd44fcd40e Author: Michael Krufky Date: Wed Mar 21 12:03:23 2007 -0300 V4L/DVB (5949): s5h1409: use ARRAY_SIZE macro when appropriate Use ARRAY_SIZE macro already defined in kernel.h Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 8a4b42f5ccc41cc83ccc5c88b02be0d747148a9b Author: Steven Toth Date: Sat Jul 28 19:34:52 2007 -0300 V4L/DVB (5948): Adding support for the S5H1409/CX24227 8VSB/QAM demodulator. This patch adds support for the Samsung S5H1409 demodulator, also known as the Conexant CX24227 demodulator. 8VSB mode has been tested and QAM has been implemented based on the spec, although it's untested. The S5H1409 / CX24227 appears on various Hauppauge boards. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab commit ab2d7777a4eac7b124921555528644de5f449f98 Author: Steven Toth Date: Sat Jul 28 19:17:39 2007 -0300 V4L/DVB (5947): Adding support for the MT2131 tuner. This adds support for the Microtune MT2131 tuner. 8VSB mode has been tested but QAM support will likely require more register work. Hauppauge have not announced any QAM devices using the MT2131 so QAM remains undone. For legal reasons, Microtune allowed us to write a GPL driver providing we did not document in significant detail any of the registers. This explains the lack of comments or defined on register names. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab commit 2a9b2ceb94c73ffddbf12936537f1254b0f4b096 Author: Matthias Kaehlcke Date: Mon Jul 30 14:58:10 2007 -0300 V4L/DVB (5946): Use mutex instead of semaphore in the DVB frontend tuning interface The DVB frontend tuning interface uses a semaphore as mutex. Use the mutex API instead of the (binary) semaphore. Signed-off-by: Matthias Kaehlcke Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 2b830a8ca360452ccaeb6d83ae5e9b541719219b Author: Andi Drebes Date: Mon Jul 30 11:49:51 2007 -0300 V4L/DVB (5942): Usb/vp7045.c: ARRAY_SIZE() This patch replaces an array size calculation done using sizeof with an invocation of the ARRAY_SIZE macro. Tested by compilation on an i386 box using "allyesconfig". Diffed against Linus' git-tree. Signed-off-by: Andi Drebes Signed-off-by: Mauro Carvalho Chehab commit 7cb7fd970d9fa18ae24daabad7db813bee138e76 Author: Andi Drebes Date: Mon Jul 30 11:48:10 2007 -0300 V4L/DVB (5941): Ttpci/budget-av.c: ARRAY_SIZE() This patch replaces an array size calculation done using sizeof with an invocation of the ARRAY_SIZE macro. Tested by compilation on an i386 box using "allyesconfig". Diffed against Linus' git-tree. Signed-off-by: Andi Drebes Signed-off-by: Mauro Carvalho Chehab commit 431ebc9cfa66b0f6ed112def7e1e6152b5957f07 Author: Adrian Bunk Date: Mon Jul 30 11:43:55 2007 -0300 V4L/DVB (5940): Export v4l2_int_device_{, un}register This patch fixes the following build error: <-- snip --> ... MODPOST 2135 modules make[2]: *** [__modpost] Error 1 <-- snip --> Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab commit 3ae49e3190dd1236d63c3d9d0be82685b0ff630d Author: Adrian Bunk Date: Sun Jul 29 12:40:48 2007 -0300 V4L/DVB (5939): dvb-pll: make struct dvb_pll_fcv1236d static The fcv1236d support patch was created before the "dvb: remove static dependencies on dvb-pll" patch was applied, but the fcv1236d patch didn't get merged until after the fact. struct dvb_pll_fcv1236d can become static. Signed-off-by: Adrian Bunk Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 1d68ed6a02a1ab561ad75e56266c46b1fe6193d9 Author: Oliver Endriss Date: Mon Jul 23 21:14:10 2007 -0300 V4L/DVB (5936): tda10023: Remove range check of symbol rate Remove incorrect range check of symbol rate, spotted by the coverity checker and reported by Adrian Bunk. These range checks are performed by dvb_core now. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit e34a0cee5a9d8a0776af443dd6b41d9f1f170ac6 Author: Oliver Endriss Date: Mon Jul 23 21:00:36 2007 -0300 V4L/DVB (5935): dvb_frontend: Range check of frequency and symbol rate Add range check of frequency and symbol rate to the FE_SET_FRONTEND ioctl. This will also avoid a divide-by zero exception in the stv0297 driver, if symbol rate is set to 0. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit 1ce58c08f00d62aae09e81839e466c841e17c7d4 Author: Oliver Endriss Date: Mon Jul 23 13:59:55 2007 -0300 V4L/DVB (5934): dvb-ttpci/saa7146: Replace saa7146_i2c_transfer by generic i2c_transfer Convert av7110_v4l.c to use i2c_transfer() instead of saa7146_i2c_transfer(). Make saa7146_i2c_transfer() static. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit 729b41e07621ad77b081704db8fddf06272fe826 Author: Adrian Bunk Date: Fri Jul 27 11:09:57 2007 -0300 V4L/DVB (5933): Dvb-usb/af9005-fe.c: error check fixes This patch: - adds a missing error check and - removes an error check that could never be true Both spotted by the Coverity checker. Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Acked-by: Luca Olivetti Signed-off-by: Mauro Carvalho Chehab commit dd1c63e59d1ab983a54f254a1c6c9602bcb4917f Author: Luca Olivetti Date: Fri Jul 27 10:27:47 2007 -0300 V4L/DVB (5932): Af9005 fix tuner module unload This patch removes the useless tuner field and avoids a double free of the tuner (either mt2060 or qt1010). Signed-off-by: Luca Olivetti Signed-off-by: Mauro Carvalho Chehab commit 0eae48bbf985ee8b79dde2b01799fa70acfa5389 Author: Hans Verkuil Date: Fri Jul 27 06:56:50 2007 -0300 V4L/DVB (5929): Add vp27smpx driver This device is internal to the Panasonic VP27S tuner and is used to set the mono/stereo/bilingual setting of the tuner. It is used by two Japanese cx23416-based cards. Signed-off-by: Takahiro Adachi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit e2e813cc864835514d7739ff1e65a7ac9a30f38e Author: Hans Verkuil Date: Fri Jul 20 06:53:23 2007 -0300 V4L/DVB (5928): tuner: fix TOP values for the Panasonic VP27 tuner. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 17c4794e1297b75b34ce52a6c84444054c35d856 Author: Hans Verkuil Date: Fri Jul 20 06:51:58 2007 -0300 V4L/DVB (5927): ivtv: set correct crystal frequency of the GVMVPRX cards Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 14a8021f594abdf34ad1060b0f811ddaf1a022bc Author: Hans Verkuil Date: Wed Jul 25 12:55:52 2007 -0300 V4L/DVB (5924): ivtv-fb: initializing the fb should trigger ivtv firmware load ivtv-fb: initializing the framebuffer should trigger ivtv firmware load Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 991069a3734f38666da4f190216ebe0b20cbaa09 Author: Hans Verkuil Date: Sun Jul 22 12:52:40 2007 -0300 V4L/DVB (5922): ivtv, cx25840: postpone fw load until first use The firmware is now loaded when the driver is actually used for the first time. This allows the driver to be compiled in-kernel instead of as a module. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 486ed1843d519b14e5a326a287bd3b01f542c2e9 Author: Hans Verkuil Date: Sun Jul 22 12:13:08 2007 -0300 V4L/DVB (5921): ivtv: add missing composite input line for ivtv_pci_pg600v2 Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 252763691b5af33a6604dce53ee75c7616e4a0b1 Author: Hans Verkuil Date: Sun Jul 22 11:12:26 2007 -0300 V4L/DVB (5920): ivtv: fix incorrect fw size report. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit af9e439a0bddd4446cd06d311994e11f0ffe3dec Author: Hans Verkuil Date: Sun Jul 22 09:42:09 2007 -0300 V4L/DVB (5919): ivtv: remove dead code Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 875ec2fcb6b984293a6ae2e8a0293c049ce7e4b3 Author: Hans Verkuil Date: Sun Jul 22 09:39:56 2007 -0300 V4L/DVB (5918): ivtv: fix TV-out VBI handling, only reset on last close. While decoding (MPEG or YUV) is active or when VBI output is in use, then do not clear the VBI output of the saa7127. Only after the last user is gone can we clear it. This fixes the case where playback was stopped, another channel was chosen and playback was restarted, while /dev/vbi16 was used to set the WSS (widescreen) setting. Without this fix the WSS was reset on every stop instead of just keeping the last value. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit dffa449ac7867ee24764d3023dce7406a54c68e9 Author: Hans Verkuil Date: Sun Jul 22 08:49:32 2007 -0300 V4L/DVB (5917): ivtv: improve mailbox responsiveness. First try polling for the result of a mailbox command, then switch to a test/sleep loop. Also reduce the sleep time from 10 ms to 1 ms. Improves the responsiveness of the mailbox handling. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 85cf37ce56ef8401ef1216c1b136e59f9238a2c1 Author: Hans Verkuil Date: Sun Jul 22 08:46:38 2007 -0300 V4L/DVB (5916): ivtv: fix pause/continue/play handling Pausing a decoder followed by a Play command would do nothing. Fixed. Pausing a decoder running at non-standard speed following by a Continue would reset the speed to 100%. Fixed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 6c809273872ed417f3aa0ad175f658defe550caf Author: Olivier DANET Date: Wed Jul 25 14:42:54 2007 -0300 V4L/DVB (5914): Add initial support for Dual-DVB-T stick Add initial support for Dual-DVB-T stick based on DiB7700 and MT2266 - Microtune MT2266 driver. - Preliminary support for these dual tuner devices : - Pinnacle Dual DVB-T diversity - Terratec Cinergy DT USB XS diversity - Hauppauge Nova TD USB Signed-off-by: Olivier DANET Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit 5b1421bd6fe85e8be33925d134e6a942b1e4ddca Author: Jesper Juhl Date: Mon Jul 23 11:34:34 2007 -0300 V4L/DVB (5913): Clean up duplicate includes in include/media/ This patch cleans up duplicate includes in include/media/ Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab commit 63581d8be5641236ac282d4d57b30ac235b80d76 Author: Jesper Juhl Date: Mon Jul 23 11:33:26 2007 -0300 V4L/DVB (5912): Clean up duplicate includes in drivers/media/ This patch cleans up duplicate includes in drivers/media/ Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab commit f60cb6e727420d5d862d2b147dd6e8ad84c14077 Author: Hans Verkuil Date: Sun Jul 22 15:44:41 2007 -0300 V4L/DVB (5910): ivtv-fb: improve debug message Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 1f50935c3609c97780ccce83c1258f8089d03b19 Author: Hans Verkuil Date: Sun Jul 22 08:39:43 2007 -0300 V4L/DVB (5909): ivtv: update version to 1.1 to mark ivtv-fb support Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 720ebf9f0cade0f5457ccaf71ad38ef00034c4f5 Author: Ian Armstrong Date: Sat Jul 21 16:43:36 2007 -0300 V4L/DVB (5908): ivtv-fb: cleanups, prevent fw calls in some cases Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit edffc221b461c5e348218b54852a8b9ea5d8a7bf Author: Hans Verkuil Date: Fri Jul 20 18:17:18 2007 -0300 V4L/DVB (5906): ivtv-fb: replace HZ with msecs_to_jiffies Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 5ddadbc698006a15693381e26629e31a70b7d176 Author: Hans Verkuil Date: Fri Jul 20 10:30:32 2007 -0300 V4L/DVB (5905): ivtv-fb: Use proper ioctl value Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 453886cc376c768b180b4ec350d2778b0861e263 Author: Hans Verkuil Date: Fri Jul 20 10:16:03 2007 -0300 V4L/DVB (5904): ivtv-fb: cleanups Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit c595a6b4db79f5d9b91d2bfc7e57f02f61db6de2 Author: Hans Verkuil Date: Fri Jul 20 09:29:43 2007 -0300 V4L/DVB (5902): Add ivtv-fb framebuffer driver. Add the ivtv-fb framebuffer driver for cx23415 devices (currently only the Hauppauge PVR-350 cards). This makes it possible to use the On-Screen Display functionality of these cards, either for menus during MPEG playback, or as a console or X display. Signed-off-by: Kevin Thayer Signed-off-by: Chris Kennedy Signed-off-by: Hans Verkuil Signed-off-by: John P Harvey Signed-off-by: Ian Armstrong Signed-off-by: Mauro Carvalho Chehab commit af631eb1f3eb1e29df7140d96d59072910bf1087 Author: Trent Piepho Date: Mon Jul 23 06:58:31 2007 -0300 V4L/DVB (5900): usbvision: fix bugs [sg]_register functions s_register was assigning the return code to (unsigned)reg->val, rather than errCode, which it what it would return. Except reg->val can't be < 0, so it would never actually return an error. g_register never actually put the value it read into reg->val. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit aa397edb0f52f8821588e4ec83287dfb19e92e96 Author: Trent Piepho Date: Sat Jul 21 21:26:40 2007 -0300 V4L/DVB (5899): bttv: Fix Viewcast Osprey 440 support Various gpio and mux settings for the Osprey 440 weren't correct. Fix them and provide some documentation about how the gpios work. The osprey eeprom routine wasn't run for the 440, add it. It was also crap, re-written to be better. Add the Osprey 440 to the Bt878 ALSA driver's whitelist. Currently the sample rate is fixed at 32kHz, as the driver doesn't support different rates for digital input mode, though the card can select the rate from 32, 44.1, or 48 kHz via gpio. Setting the audio gain via ALSA isn't supported yet; a userspace tool that programs the X9221 via i2c-dev must be used. The Bt878 digital audio format isn't programmed correctly for the CS5331A ADC used, resulting in extremely garbled sound. That is fixed in a followup patch. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit ed8e38387f3e5ed4bf671ea2f3512762fdfbf86a Author: Rasmus Rohde Date: Sat Jul 21 15:37:35 2007 -0300 V4L/DVB (5897): dtt200u: add support for the Miglia TVMini USB DVB-T adapter add code for autodetection and firmware download to the Miglia TVMini USB DVB-T adapter. After firmware download, the device re-registers using the WT220U_ZL0353_WARM usb id. Signed-off-by: Rasmus Rohde Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 38443db868b9ed02c00d09e21dae10c5f0f198c7 Author: Anssi Hannula Date: Fri Jul 20 17:18:10 2007 -0300 V4L/DVB (5893): DVB: fix includes of video.h when __KERNEL__ is undefined linux/dvb/video.h uses types __u32, __s32, etc., but does not include any header defining those when __KERNEL__ is not defined. Fix this by including asm/types.h when __KERNEL__ is not defined. Signed-off-by: Anssi Hannula Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit afcbee2a5cb21f0b0559d04cfe94373e84c58bfd Author: Trent Piepho Date: Tue Jul 17 18:29:45 2007 -0300 V4L/DVB (5891): zr36067: Turn off raw capture properly When raw capture was turned off, the current capturing frame (v4l_grab_frame) wasn't reset to NO_GRAB_ACTIVE. If capture was turned back on, the driver would think this frame was currently being captured, and wait for it to complete before starting a new frame. The hardware on the other hand would not be actively capturing a frame. The result was the driver would wait forever for v4l_grab_frame to be captured. Some calls to zr36057_set_memgrab(0) were missing spin-locks, which have been added. Signed-off-by: Trent Piepho Acked-by: Ronald S. Bultje Signed-off-by: Mauro Carvalho Chehab commit fabe3fd3827d610b8f7e6cd05f163e6041a2a1d8 Author: Trent Piepho Date: Tue Jul 17 18:29:44 2007 -0300 V4L/DVB (5890): zr36067: Add UYVY, RGB555X, RGB565X, and RGB32 formats Add support for the UYVY and the other big endian output formats. The driver was naming formats based on the host endianess. This is different that all the other drivers appear to work and not what software appears to expect. Use ARRAY_SIZE() to find the the size of the zoran_formats array. Change the way the driver handles setting the video format register. Rather than use some if and switch statements to set to register by looking at the format id, the format list simply has a field with the proper bits to set. Adds a bit of ifdef to make a driver without V4L1 support more possible. Also create a macro for defining formats that handles vl41 and/or vl42 support to avoid repeated ifdefs in the format list. Signed-off-by: Trent Piepho Acked-by: Ronald S. Bultje Signed-off-by: Mauro Carvalho Chehab commit fb0336b7916057d8584a6ac14e894209d1aa89e9 Author: Trent Piepho Date: Tue Jul 17 18:29:43 2007 -0300 V4L/DVB (5888): zr36067: Driver was not returning correct image size The driver was returning the size of the (fixed) buffer it allocated as the sizeimage field in the v4l2 pixel format, rather than the actual size of the image. For example, a 192x128 YUYV image is 49152 bytes but the driver would always return 131072 bytes since if that was the size of the v4l buffer. This violates the v4l2 spec, which says that sizeimage should be the actual size of the image for uncompressed formats. It also caused mplayer to crash. Signed-off-by: Trent Piepho Acked-by: Ronald S. Bultje Signed-off-by: Mauro Carvalho Chehab commit 37e6ef79d637f34747c2eddcd41b216f76d4820d Author: Trent Piepho Date: Tue Jul 17 18:29:43 2007 -0300 V4L/DVB (5887): zr36067: Fix poll() operation During uncompressed capture, the poll() function was looking the wrong frame. It was using the frame the driver was going to capture into next (pend_tail), when it should have been looking at the next frame to be de-queued with DQBUF/SYNC (sync_tail). It also wasn't looking in the right spot. It was looking at the file handle's copy of the buffer status, rather than the driver core copy. The interrupt routine marks frames as done in the driver core copy, the file handle copy isn't updated. So even if poll() looked at the right frame, it would never see it transition to done and return POLLIN. The compressed capture code has this same problem, looking in fh->jpg_buffers when it should have used zr->jpg_buffers. There was some logic to detect when there was no current capture in process nor any frames queued and try to return an error, which ends up being a bad idea. It's possible to call select() from one thread while no capture is in process, or no frames queued, and then start a capture or queue frames from another thread. The buffer state variables are protected by a spin lock, which the code wasn't acquiring. That is fixed too. Signed-off-by: Trent Piepho Acked-by: Ronald S. Bultje Signed-off-by: Mauro Carvalho Chehab commit b0126110e4cc5088d6d27f406213f3b7311e5701 Author: Trent Piepho Date: Tue Jul 17 18:29:42 2007 -0300 V4L/DVB (5886): zr36067: Fix problem setting norms The zr36067 driver doesn't make a distinction between the different sub-types of NTSC, PAL, or SECAM norms. For example, when the enum std ioctl returns the PAL standard it returns PAL_BG|PAL_DK|PAL_H|PAL_I. When setting the norm, it required the bitmask to match exactly the set of norms used during the enumeration. If just one norm was specified, for example PAL_BG or NTSC_M, it would fail. This violates the V4L2 spec, "VIDIOC_S_STD accepts *one* or more flags..." The key thing to realize is that V4L2_STD_PAL is not one bit, it is multiple bits. It's ok to call S_STD with any *one* of those bits, but the driver was requiring *all* of them. This fixes the S_STD function so that it will accept any set of one or more PAL norms as PAL, and the same for NTSC and SECAM. Signed-off-by: Trent Piepho Acked-by: Ronald S. Bultje Signed-off-by: Mauro Carvalho Chehab commit 74d8bbf80baafb4cbf5adefa965237fcbf407ac3 Author: Trent Piepho Date: Tue Jul 17 18:29:42 2007 -0300 V4L/DVB (5885): zr36067: Fix problems with module parameters Add permissions to all the module parameters so they can be queried and set (when possible) via sysfs. Add description for the vidmem parameter. Change the video_nr parameter to an array, so that the video number can be specified when a user has more than one card. The driver would try to give all cards the same number otherwise, which will fail for all cards after the first. The default_input option would only allow values of 0 or 1, contrary to the description. Allow values up to the number of inputs defined for the card. Add description of lock_norm's different behavior for 1 and >1. Signed-off-by: Trent Piepho Acked-by: Ronald S. Bultje Signed-off-by: Mauro Carvalho Chehab commit 812a7433ec250723c64d6671d4241b251932444e Author: Jean Delvare Date: Tue Jul 17 18:29:41 2007 -0300 V4L/DVB (5884): zr36067: clean up debug function Debugging cleanups to the zr36067 driver: * Use module_param_named() to declare the debug parameter, so we can use a single global variable to handle the debug level. This makes the driver a bit smaller (by 648 bytes on x86_64), thanks to one less level of indirection on every use. * Change the debug parameter sysfs permissions, so that the debug level can be adjusted at runtime, as is done in many other media/video drivers. * The debug level is between 0 and 5, not 0 and 4. * Move the zr_debug export and dprintk macro definition to a header file so that we don't have to define them in each source file. * Simplify a duplicate test on zr_debug. Note that zr_debug was subsequently renamed to debug_zr36067 to avoid possible conflicts with other Zoran device drivers, on a suggestion by Trent Piepho. Signed-off-by: Jean Delvare Signed-off-by: Trent Piepho Acked-by: Ronald S. Bultje Signed-off-by: Mauro Carvalho Chehab commit b0c8aa95bd6dc7167a43c2d42ad0ddf1e0bfbe8f Author: Sakari Ailus Date: Fri Jul 20 13:12:51 2007 -0300 V4L/DVB (5883): V4L: Fix a compile warning on non-32-bit machines. Fix a compile warning on non-32-bit machines in v4l2-int-device.h. Add internal ioctl interface fallback function for ioctls with one argument. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab commit ff24eb5a722e0605a9934c53a3cdc19b9d644f0b Author: Hans Verkuil Date: Fri Jul 20 06:26:34 2007 -0300 V4L/DVB (5881): ivtv: init channel for NTSC_M_JP standard. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 505184d5e75da10e39fd25937586c380d6bf3712 Author: Sakari Ailus Date: Wed Jul 18 18:04:17 2007 -0300 V4L/DVB (5863): TCM825x: Add driver. Add a driver for Toshiba TCM825x VGA camera sensor. This sensor is used e.g. in Nokia N800 internet tablet. This driver uses the new V4L2 internal ioctl interface. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab commit fd5fcbad09d0a9f2d91d7093be977e74abd65b4c Author: Sakari Ailus Date: Wed Jul 18 17:59:15 2007 -0300 V4L/DVB (5862): V4L: Add internal ioctl-like interface. This patch adds an internal ioctl-like interface which can be used in situations where a single Video4Linux device is implemented by multiple device drivers. One master device controls one or more slave devices. The slaves provide Video4Linux ioctl-like interface for the use of the master. Only a handful of ioctls are implemented at the moment. More can (and should) be added as more functionality is required. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Andrew Morton --- drivers/media/common/saa7146_core.c | 1 drivers/media/common/saa7146_i2c.c | 2 drivers/media/dvb/dvb-core/dvb_frontend.c | 56 drivers/media/dvb/dvb-core/dvb_frontend.h | 3 drivers/media/dvb/dvb-usb/Kconfig | 1 drivers/media/dvb/dvb-usb/dib0700.h | 4 drivers/media/dvb/dvb-usb/dib0700_core.c | 2 drivers/media/dvb/dvb-usb/dib0700_devices.c | 262 +++ drivers/media/dvb/dvb-usb/dtt200u.c | 28 drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 6 drivers/media/dvb/dvb-usb/vp7045.c | 2 drivers/media/dvb/frontends/Kconfig | 22 drivers/media/dvb/frontends/Makefile | 3 drivers/media/dvb/frontends/dib7000p.c | 5 drivers/media/dvb/frontends/dib7000p.h | 2 drivers/media/dvb/frontends/mt2131.c | 326 ++++ drivers/media/dvb/frontends/mt2131.h | 54 drivers/media/dvb/frontends/mt2131_priv.h | 49 drivers/media/dvb/frontends/mt2266.c | 288 ++++ drivers/media/dvb/frontends/mt2266.h | 37 drivers/media/dvb/frontends/s5h1409.c | 729 ++++++++++ drivers/media/dvb/frontends/s5h1409.h | 73 + drivers/media/dvb/frontends/tda10023.c | 6 drivers/media/dvb/ttpci/av7110_v4l.c | 6 drivers/media/dvb/ttpci/budget-av.c | 2 drivers/media/video/Kconfig | 16 drivers/media/video/Makefile | 9 drivers/media/video/arv.c | 1 drivers/media/video/cx25840/cx25840-core.c | 21 drivers/media/video/cx25840/cx25840-core.h | 1 drivers/media/video/cx88/cx88-mpeg.c | 1 drivers/media/video/cx88/cx88-video.c | 1 drivers/media/video/ivtv/Kconfig | 17 drivers/media/video/ivtv/Makefile | 1 drivers/media/video/ivtv/ivtv-cards.c | 5 drivers/media/video/ivtv/ivtv-cards.h | 3 drivers/media/video/ivtv/ivtv-driver.c | 225 +-- drivers/media/video/ivtv/ivtv-driver.h | 35 drivers/media/video/ivtv/ivtv-fb.c | 1199 ++++++++++++++++++ drivers/media/video/ivtv/ivtv-fileops.c | 7 drivers/media/video/ivtv/ivtv-i2c.c | 2 drivers/media/video/ivtv/ivtv-streams.c | 9 drivers/media/video/ivtv/ivtv-version.h | 2 drivers/media/video/tcm825x.c | 942 ++++++++++++++ drivers/media/video/tcm825x.h | 195 ++ drivers/media/video/tuner-types.c | 3 drivers/media/video/v4l2-int-device.c | 166 ++ drivers/media/video/vp27smpx.c | 211 +++ drivers/media/video/zoran_card.c | 64 drivers/media/video/zoran_card.h | 8 drivers/media/video/zoran_device.c | 19 drivers/media/video/zoran_driver.c | 61 drivers/media/video/zoran_procfs.c | 9 include/media/ivtv-fb.h | 34 include/media/saa7146.h | 1 include/media/v4l2-chip-ident.h | 3 include/media/v4l2-dev.h | 2 include/media/v4l2-int-device.h | 210 +++ 58 files changed, 5213 insertions(+), 239 deletions(-) diff -puN drivers/media/common/saa7146_core.c~git-dvb drivers/media/common/saa7146_core.c --- a/drivers/media/common/saa7146_core.c~git-dvb +++ a/drivers/media/common/saa7146_core.c @@ -548,7 +548,6 @@ EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_ EXPORT_SYMBOL_GPL(saa7146_setgpio); -EXPORT_SYMBOL_GPL(saa7146_i2c_transfer); EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare); EXPORT_SYMBOL_GPL(saa7146_debug); diff -puN drivers/media/common/saa7146_i2c.c~git-dvb drivers/media/common/saa7146_i2c.c --- a/drivers/media/common/saa7146_i2c.c~git-dvb +++ a/drivers/media/common/saa7146_i2c.c @@ -277,7 +277,7 @@ static int saa7146_i2c_writeout(struct s return 0; } -int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries) +static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries) { int i = 0, count = 0; u32* buffer = dev->d_i2c.cpu_addr; diff -puN drivers/media/dvb/dvb-core/dvb_frontend.c~git-dvb drivers/media/dvb/dvb-core/dvb_frontend.c --- a/drivers/media/dvb/dvb-core/dvb_frontend.c~git-dvb +++ a/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -138,7 +138,7 @@ static void dvb_frontend_add_event(struc dprintk ("%s\n", __FUNCTION__); - if (down_interruptible (&events->sem)) + if (mutex_lock_interruptible (&events->mtx)) return; wp = (events->eventw + 1) % MAX_EVENT; @@ -159,7 +159,7 @@ static void dvb_frontend_add_event(struc events->eventw = wp; - up (&events->sem); + mutex_unlock(&events->mtx); e->status = status; @@ -197,7 +197,7 @@ static int dvb_frontend_get_event(struct return ret; } - if (down_interruptible (&events->sem)) + if (mutex_lock_interruptible (&events->mtx)) return -ERESTARTSYS; memcpy (event, &events->events[events->eventr], @@ -205,7 +205,7 @@ static int dvb_frontend_get_event(struct events->eventr = (events->eventr + 1) % MAX_EVENT; - up (&events->sem); + mutex_unlock(&events->mtx); return 0; } @@ -697,6 +697,47 @@ static int dvb_frontend_start(struct dvb return 0; } +static int dvb_frontend_check_parameters(struct dvb_frontend *fe, + struct dvb_frontend_parameters *parms) +{ + /* range check: frequency */ + if ((fe->ops.info.frequency_min && + parms->frequency < fe->ops.info.frequency_min) || + (fe->ops.info.frequency_max && + parms->frequency > fe->ops.info.frequency_max)) { + printk(KERN_WARNING "DVB: frontend %u frequency %u out of range (%u..%u)\n", + fe->dvb->num, parms->frequency, + fe->ops.info.frequency_min, fe->ops.info.frequency_max); + return -EINVAL; + } + + /* range check: symbol rate */ + if (fe->ops.info.type == FE_QPSK) { + if ((fe->ops.info.symbol_rate_min && + parms->u.qpsk.symbol_rate < fe->ops.info.symbol_rate_min) || + (fe->ops.info.symbol_rate_max && + parms->u.qpsk.symbol_rate > fe->ops.info.symbol_rate_max)) { + printk(KERN_WARNING "DVB: frontend %u symbol rate %u out of range (%u..%u)\n", + fe->dvb->num, parms->u.qpsk.symbol_rate, + fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max); + return -EINVAL; + } + + } else if (fe->ops.info.type == FE_QAM) { + if ((fe->ops.info.symbol_rate_min && + parms->u.qam.symbol_rate < fe->ops.info.symbol_rate_min) || + (fe->ops.info.symbol_rate_max && + parms->u.qam.symbol_rate > fe->ops.info.symbol_rate_max)) { + printk(KERN_WARNING "DVB: frontend %u symbol rate %u out of range (%u..%u)\n", + fe->dvb->num, parms->u.qam.symbol_rate, + fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max); + return -EINVAL; + } + } + + return 0; +} + static int dvb_frontend_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) { @@ -883,6 +924,11 @@ static int dvb_frontend_ioctl(struct ino case FE_SET_FRONTEND: { struct dvb_frontend_tune_settings fetunesettings; + if (dvb_frontend_check_parameters(fe, parg) < 0) { + err = -EINVAL; + break; + } + memcpy (&fepriv->parameters, parg, sizeof (struct dvb_frontend_parameters)); @@ -1080,7 +1126,7 @@ int dvb_register_frontend(struct dvb_ada init_MUTEX (&fepriv->sem); init_waitqueue_head (&fepriv->wait_queue); init_waitqueue_head (&fepriv->events.wait_queue); - init_MUTEX (&fepriv->events.sem); + mutex_init(&fepriv->events.mtx); fe->dvb = dvb; fepriv->inversion = INVERSION_OFF; diff -puN drivers/media/dvb/dvb-core/dvb_frontend.h~git-dvb drivers/media/dvb/dvb-core/dvb_frontend.h --- a/drivers/media/dvb/dvb-core/dvb_frontend.h~git-dvb +++ a/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -142,7 +143,7 @@ struct dvb_fe_events { int eventr; int overflow; wait_queue_head_t wait_queue; - struct semaphore sem; + struct mutex mtx; }; struct dvb_frontend { diff -puN drivers/media/dvb/dvb-usb/Kconfig~git-dvb drivers/media/dvb/dvb-usb/Kconfig --- a/drivers/media/dvb/dvb-usb/Kconfig~git-dvb +++ a/drivers/media/dvb/dvb-usb/Kconfig @@ -74,6 +74,7 @@ config DVB_USB_DIB0700 select DVB_DIB7000M select DVB_DIB3000MC select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select DVB_TUNER_MT2266 if !DVB_FE_CUSTOMISE help Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The USB bridge is also present in devices having the DiB7700 DVB-T-USB diff -puN drivers/media/dvb/dvb-usb/dib0700.h~git-dvb drivers/media/dvb/dvb-usb/dib0700.h --- a/drivers/media/dvb/dvb-usb/dib0700.h~git-dvb +++ a/drivers/media/dvb/dvb-usb/dib0700.h @@ -35,12 +35,13 @@ extern int dvb_usb_dib0700_debug; struct dib0700_state { u8 channel_state; u16 mt2060_if1[2]; - + u8 rc_toggle; u8 is_dib7000pc; }; extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val); extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3); +extern int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen); extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw); extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); extern struct i2c_algorithm dib0700_i2c_algo; @@ -50,5 +51,4 @@ extern int dib0700_identify_state(struct extern int dib0700_device_count; extern struct dvb_usb_device_properties dib0700_devices[]; extern struct usb_device_id dib0700_usb_id_table[]; - #endif diff -puN drivers/media/dvb/dvb-usb/dib0700_core.c~git-dvb drivers/media/dvb/dvb-usb/dib0700_core.c --- a/drivers/media/dvb/dvb-usb/dib0700_core.c~git-dvb +++ a/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -32,7 +32,7 @@ static int dib0700_ctrl_wr(struct dvb_us } /* expecting tx buffer: request data[0] ... data[n] (n <= 4) */ -static int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen) +int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen) { u16 index, value; int status; diff -puN drivers/media/dvb/dvb-usb/dib0700_devices.c~git-dvb drivers/media/dvb/dvb-usb/dib0700_devices.c --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c~git-dvb +++ a/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -12,6 +12,7 @@ #include "dib7000m.h" #include "dib7000p.h" #include "mt2060.h" +#include "mt2266.h" static int force_lna_activation; module_param(force_lna_activation, int, 0644); @@ -96,6 +97,228 @@ static int bristol_tuner_attach(struct d st->mt2060_if1[adap->id]) == NULL ? -ENODEV : 0; } +/* STK7700D: Pinnacle Dual DVB-T Diversity */ + +static struct dibx000_agc_config stk7700d_7000p_mt2266_agc_config = { + BAND_UHF/* | BAND_VHF*/, + 0xE64, // setup + 2372, // inv_gain + 21, // time_stabiliz + + 0, // alpha_level + 118, // thlock + + 0, // wbd_inv + 0, // wbd_ref + 0, // wbd_sel + 0, // wbd_alpha + + 65535, // agc1_max + 0, // agc1_min + 65535, // agc2_max + 23592, // agc2_min + 0, // agc1_pt1 + 128, // agc1_pt2 + 128, // agc1_pt3 + 128, // agc1_slope1 + 0, // agc1_slope2 + 128, // agc2_pt1 + 253, // agc2_pt2 + 81, // agc2_slope1 + 0, // agc2_slope2 + + 17, // alpha_mant + 27, // alpha_exp + + 23, // beta_mant + 51, // beta_exp + + 0, // perform_agc_softsplit : 1 en vrai! +}; + +static struct dibx000_bandwidth_config stk7700d_mt2266_pll_config = { + 60000, 30000, // internal, sampling + 1, 8, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass + 0, 0, 1, 1, 2, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo + (3 << 14) | (1 << 12) | (524 << 0), // sad_cfg: refsel, sel, freq_15k + 0, // ifreq + 20452225, // timf +}; + +static struct dib7000p_config stk7700d_dib7000p_mt2266_config[] = { + { .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + + .agc = &stk7700d_7000p_mt2266_agc_config, + .bw = &stk7700d_mt2266_pll_config, + + .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, + }, + { .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + + .agc = &stk7700d_7000p_mt2266_agc_config, + .bw = &stk7700d_mt2266_pll_config, + + .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, + } +}; + +static struct mt2266_config stk7700d_mt2266_config[2] = { + { .i2c_address = 0x60 + }, + { .i2c_address = 0x60 + } +}; + +static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (adap->id == 0) { + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(10); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + dib7000p_i2c_enumeration(&adap->dev->i2c_adap,2,18,stk7700d_dib7000p_mt2266_config); + } + + adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,0x80+(adap->id << 1), + &stk7700d_dib7000p_mt2266_config[adap->id]); + + return adap->fe == NULL ? -ENODEV : 0; +} + +static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct i2c_adapter *tun_i2c; + tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + return dvb_attach(mt2266_attach, adap->fe, tun_i2c, + &stk7700d_mt2266_config[adap->id]) == NULL ? -ENODEV : 0;; +} + +#define DEFAULT_RC_INTERVAL 150 + +static u8 rc_request[] = { REQUEST_POLL_RC, 0 }; + +int stk7700d_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 key[4]; + int i; + struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + struct dib0700_state *st = d->priv; + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + i=dib0700_ctrl_rd(d,rc_request,2,key,4); + if (i<=0) { + err("stk7700d:RC Query Failed\n"); + return 0; + } + if (key[0]==0 && key[1]==0 && key[2]==0 && key[3]==0) return 0; + if (key[1]!=st->rc_toggle) { + for (i=0;iprops.rc_key_map_size; i++) { + if (keymap[i].custom == key[2] && keymap[i].data == key[3]) { + *event = keymap[i].event; + *state = REMOTE_KEY_PRESSED; + st->rc_toggle=key[1]; + return 0; + } + } + err("stk7700d:Unknown remote controller key : %2X %2X\n",(int)key[2],(int)key[3]); + } + return 0; +} + +#define KEY_MAP_SIZE (25+48) + +struct dvb_usb_rc_key stk7700d_rc_keys[] = { + /* Key codes for the tiny Pinnacle remote*/ + { 0x07, 0x00, KEY_MUTE }, + { 0x07, 0x01, KEY_MENU }, // Pinnacle logo + { 0x07, 0x39, KEY_POWER }, + { 0x07, 0x03, KEY_VOLUMEUP }, + { 0x07, 0x09, KEY_VOLUMEDOWN }, + { 0x07, 0x06, KEY_CHANNELUP }, + { 0x07, 0x0c, KEY_CHANNELDOWN }, + { 0x07, 0x0f, KEY_1 }, + { 0x07, 0x15, KEY_2 }, + { 0x07, 0x10, KEY_3 }, + { 0x07, 0x18, KEY_4 }, + { 0x07, 0x1b, KEY_5 }, + { 0x07, 0x1e, KEY_6 }, + { 0x07, 0x11, KEY_7 }, + { 0x07, 0x21, KEY_8 }, + { 0x07, 0x12, KEY_9 }, + { 0x07, 0x27, KEY_0 }, + { 0x07, 0x24, KEY_SCREEN }, // 'Square' key + { 0x07, 0x2a, KEY_TEXT }, // 'T' key + { 0x07, 0x2d, KEY_REWIND }, + { 0x07, 0x30, KEY_PLAY }, + { 0x07, 0x33, KEY_FASTFORWARD }, + { 0x07, 0x36, KEY_RECORD }, + { 0x07, 0x3c, KEY_STOP }, + { 0x07, 0x3f, KEY_CANCEL }, // '?' key + /* Key codes for the Terratec Cinergy DT XS Diversity, similar to cinergyT2.c */ + { 0xeb, 0x01, KEY_POWER }, + { 0xeb, 0x02, KEY_1 }, + { 0xeb, 0x03, KEY_2 }, + { 0xeb, 0x04, KEY_3 }, + { 0xeb, 0x05, KEY_4 }, + { 0xeb, 0x06, KEY_5 }, + { 0xeb, 0x07, KEY_6 }, + { 0xeb, 0x08, KEY_7 }, + { 0xeb, 0x09, KEY_8 }, + { 0xeb, 0x0a, KEY_9 }, + { 0xeb, 0x0b, KEY_VIDEO }, + { 0xeb, 0x0c, KEY_0 }, + { 0xeb, 0x0d, KEY_REFRESH }, + { 0xeb, 0x0f, KEY_EPG }, + { 0xeb, 0x10, KEY_UP }, + { 0xeb, 0x11, KEY_LEFT }, + { 0xeb, 0x12, KEY_OK }, + { 0xeb, 0x13, KEY_RIGHT }, + { 0xeb, 0x14, KEY_DOWN }, + { 0xeb, 0x16, KEY_INFO }, + { 0xeb, 0x17, KEY_RED }, + { 0xeb, 0x18, KEY_GREEN }, + { 0xeb, 0x19, KEY_YELLOW }, + { 0xeb, 0x1a, KEY_BLUE }, + { 0xeb, 0x1b, KEY_CHANNELUP }, + { 0xeb, 0x1c, KEY_VOLUMEUP }, + { 0xeb, 0x1d, KEY_MUTE }, + { 0xeb, 0x1e, KEY_VOLUMEDOWN }, + { 0xeb, 0x1f, KEY_CHANNELDOWN }, + { 0xeb, 0x40, KEY_PAUSE }, + { 0xeb, 0x41, KEY_HOME }, + { 0xeb, 0x42, KEY_MENU }, /* DVD Menu */ + { 0xeb, 0x43, KEY_SUBTITLE }, + { 0xeb, 0x44, KEY_TEXT }, /* Teletext */ + { 0xeb, 0x45, KEY_DELETE }, + { 0xeb, 0x46, KEY_TV }, + { 0xeb, 0x47, KEY_DVD }, + { 0xeb, 0x48, KEY_STOP }, + { 0xeb, 0x49, KEY_VIDEO }, + { 0xeb, 0x4a, KEY_AUDIO }, /* Music */ + { 0xeb, 0x4b, KEY_SCREEN }, /* Pic */ + { 0xeb, 0x4c, KEY_PLAY }, + { 0xeb, 0x4d, KEY_BACK }, + { 0xeb, 0x4e, KEY_REWIND }, + { 0xeb, 0x4f, KEY_FASTFORWARD }, + { 0xeb, 0x54, KEY_PREVIOUS }, + { 0xeb, 0x58, KEY_RECORD }, + { 0xeb, 0x5c, KEY_NEXT } + }; + /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */ static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = { BAND_UHF | BAND_VHF, // band_caps @@ -280,6 +503,9 @@ struct usb_device_id dib0700_usb_id_tabl { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P) }, { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_2) }, { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_2) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV2000E) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -372,6 +598,42 @@ struct dvb_usb_device_properties dib0700 { NULL }, }, } + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 2, + .adapter = { + { + .frontend_attach = stk7700d_frontend_attach, + .tuner_attach = stk7700d_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }, { + .frontend_attach = stk7700d_frontend_attach, + .tuner_attach = stk7700d_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + } + }, + + .num_device_descs = 3, + .devices = { + { "Pinnacle PCTV 2000e", + { &dib0700_usb_id_table[11], NULL }, + { NULL }, + }, + { "Terratec Cinergy DT XS Diversity", + { &dib0700_usb_id_table[12], NULL }, + { NULL }, + }, + { "Haupauge Nova-TD Stick", + { &dib0700_usb_id_table[13], NULL }, + { NULL }, + }, + }, + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = stk7700d_rc_keys, + .rc_key_map_size = KEY_MAP_SIZE, + .rc_query = stk7700d_rc_query } }; diff -puN drivers/media/dvb/dvb-usb/dtt200u.c~git-dvb drivers/media/dvb/dvb-usb/dtt200u.c --- a/drivers/media/dvb/dvb-usb/dtt200u.c~git-dvb +++ a/drivers/media/dvb/dvb-usb/dtt200u.c @@ -1,5 +1,5 @@ /* DVB USB library compliant Linux driver for the WideView/ Yakumo/ Hama/ - * Typhoon/ Yuan DVB-T USB2.0 receiver. + * Typhoon/ Yuan/ Miglia DVB-T USB2.0 receiver. * * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) * @@ -96,6 +96,7 @@ static struct dvb_usb_device_properties static struct dvb_usb_device_properties wt220u_fc_properties; static struct dvb_usb_device_properties wt220u_properties; static struct dvb_usb_device_properties wt220u_zl0353_properties; +static struct dvb_usb_device_properties wt220u_miglia_properties; static int dtt200u_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -103,7 +104,8 @@ static int dtt200u_usb_probe(struct usb_ if (dvb_usb_device_init(intf,&dtt200u_properties,THIS_MODULE,NULL) == 0 || dvb_usb_device_init(intf,&wt220u_properties,THIS_MODULE,NULL) == 0 || dvb_usb_device_init(intf,&wt220u_fc_properties,THIS_MODULE,NULL) == 0 || - dvb_usb_device_init(intf,&wt220u_zl0353_properties,THIS_MODULE,NULL) == 0) + dvb_usb_device_init(intf,&wt220u_zl0353_properties,THIS_MODULE,NULL) == 0 || + dvb_usb_device_init(intf,&wt220u_miglia_properties,THIS_MODULE,NULL) == 0) return 0; return -ENODEV; @@ -119,6 +121,7 @@ static struct usb_device_id dtt200u_usb_ { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_COLD) }, { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_WARM) }, { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZAP250_COLD) }, + { USB_DEVICE(USB_VID_MIGLIA, USB_PID_WT220U_ZAP250_COLD) }, { 0 }, }; MODULE_DEVICE_TABLE(usb, dtt200u_usb_table); @@ -303,6 +306,25 @@ static struct dvb_usb_device_properties } }; +static struct dvb_usb_device_properties wt220u_miglia_properties = { + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-wt220u-miglia-01.fw", + + .num_adapters = 1, + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { .name = "WideView WT-220U PenType Receiver (Miglia)", + .cold_ids = { &dtt200u_usb_table[9], NULL }, + /* This device turns into WT220U_ZL0353_WARM when fw + has been uploaded */ + .warm_ids = { NULL }, + }, + { NULL }, + } +}; + /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver dtt200u_usb_driver = { .name = "dvb_usb_dtt200u", @@ -333,6 +355,6 @@ module_init(dtt200u_usb_module_init); module_exit(dtt200u_usb_module_exit); MODULE_AUTHOR("Patrick Boettcher "); -MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D DVB-T USB2.0 devices"); +MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D/Miglia DVB-T USB2.0 devices"); MODULE_VERSION("1.0"); MODULE_LICENSE("GPL"); diff -puN drivers/media/dvb/dvb-usb/dvb-usb-ids.h~git-dvb drivers/media/dvb/dvb-usb/dvb-usb-ids.h --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h~git-dvb +++ a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -12,7 +12,7 @@ /* Vendor IDs */ #define USB_VID_ADSTECH 0x06e1 #define USB_VID_AFATECH 0x15a4 -#define USB_VID_ALCOR_MICRO 0x058f +#define USB_VID_ALCOR_MICRO 0x058f #define USB_VID_ALINK 0x05e3 #define USB_VID_ANCHOR 0x0547 #define USB_VID_ANUBIS_ELECTRONIC 0x10fd @@ -34,6 +34,7 @@ #define USB_VID_LEADTEK 0x0413 #define USB_VID_LITEON 0x04ca #define USB_VID_MEDION 0x1660 +#define USB_VID_MIGLIA 0x18f3 #define USB_VID_MSI 0x0db0 #define USB_VID_OPERA1 0x695c #define USB_VID_PINNACLE 0x2304 @@ -115,8 +116,11 @@ #define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950 #define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050 #define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060 +#define USB_PID_HAUPPAUGE_NOVA_TD_STICK 0x9580 #define USB_PID_AVERMEDIA_VOLAR 0xa807 #define USB_PID_AVERMEDIA_VOLAR_2 0xb808 +#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a +#define USB_PID_PINNACLE_PCTV2000E 0x022c #define USB_PID_NEBULA_DIGITV 0x0201 #define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820 #define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500 diff -puN drivers/media/dvb/dvb-usb/vp7045.c~git-dvb drivers/media/dvb/dvb-usb/vp7045.c --- a/drivers/media/dvb/dvb-usb/vp7045.c~git-dvb +++ a/drivers/media/dvb/dvb-usb/vp7045.c @@ -159,7 +159,7 @@ static int vp7045_rc_query(struct dvb_us return 0; } - for (i = 0; i < sizeof(vp7045_rc_keys)/sizeof(struct dvb_usb_rc_key); i++) + for (i = 0; i < ARRAY_SIZE(vp7045_rc_keys); i++) if (vp7045_rc_keys[i].data == key) { *state = REMOTE_KEY_PRESSED; *event = vp7045_rc_keys[i].event; diff -puN drivers/media/dvb/frontends/Kconfig~git-dvb drivers/media/dvb/frontends/Kconfig --- a/drivers/media/dvb/frontends/Kconfig~git-dvb +++ a/drivers/media/dvb/frontends/Kconfig @@ -283,6 +283,14 @@ config DVB_LGDT330X An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. +config DVB_S5H1409 + tristate "Samsung S5H1409 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. + comment "Tuners/PLL support" depends on DVB_CORE @@ -322,6 +330,20 @@ config DVB_TUNER_MT2060 help A driver for the silicon IF tuner MT2060 from Microtune. +config DVB_TUNER_MT2266 + tristate "Microtune MT2266 silicon tuner" + depends on I2C + default m if DVB_FE_CUSTOMISE + help + A driver for the silicon baseband tuner MT2266 from Microtune. + +config DVB_TUNER_MT2131 + tristate "Microtune MT2131 silicon tuner" + depends on I2C + default m if DVB_FE_CUSTOMISE + help + A driver for the silicon baseband tuner MT2131 from Microtune. + comment "Miscellaneous devices" depends on DVB_CORE diff -puN drivers/media/dvb/frontends/Makefile~git-dvb drivers/media/dvb/frontends/Makefile --- a/drivers/media/dvb/frontends/Makefile~git-dvb +++ a/drivers/media/dvb/frontends/Makefile @@ -40,5 +40,8 @@ obj-$(CONFIG_DVB_TDA10086) += tda10086.o obj-$(CONFIG_DVB_TDA826X) += tda826x.o obj-$(CONFIG_DVB_TDA827X) += tda827x.o obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o +obj-$(CONFIG_DVB_TUNER_MT2266) += mt2266.o obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o obj-$(CONFIG_DVB_TUA6100) += tua6100.o +obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o +obj-$(CONFIG_DVB_S5H1409) += s5h1409.o diff -puN drivers/media/dvb/frontends/dib7000p.c~git-dvb drivers/media/dvb/frontends/dib7000p.c --- a/drivers/media/dvb/frontends/dib7000p.c~git-dvb +++ a/drivers/media/dvb/frontends/dib7000p.c @@ -112,6 +112,11 @@ static int dib7000p_set_output_mode(stru break; } + if (state->cfg.hostbus_diversity) { + ret |= dib7000p_write_word(state, 204, 1); // Diversity ? + ret |= dib7000p_write_word(state, 205, 0); // Diversity ? + } + if (state->cfg.output_mpeg2_in_188_bytes) smo_mode |= (1 << 5) ; diff -puN drivers/media/dvb/frontends/dib7000p.h~git-dvb drivers/media/dvb/frontends/dib7000p.h --- a/drivers/media/dvb/frontends/dib7000p.h~git-dvb +++ a/drivers/media/dvb/frontends/dib7000p.h @@ -35,7 +35,7 @@ struct dib7000p_config { extern struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); extern struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); extern int dib7000pc_detection(struct i2c_adapter *i2c_adap); - +extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]); /* TODO extern INT dib7000p_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val); extern INT dib7000p_enable_vbg_voltage(struct dibDemod *demod); diff -puN /dev/null drivers/media/dvb/frontends/mt2131.c --- /dev/null +++ a/drivers/media/dvb/frontends/mt2131.c @@ -0,0 +1,326 @@ +/* + * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" + * + * Copyright (c) 2006 Steven Toth + * + * 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 +#include +#include +#include +#include + +#include "dvb_frontend.h" + +#include "mt2131.h" +#include "mt2131_priv.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define dprintk(level,fmt, arg...) if (debug >= level) \ + printk(KERN_INFO "%s: " fmt, "mt2131", ## arg) + +static u8 mt2131_config1[] = { + 0x01, + 0x50, 0x00, 0x50, 0x80, 0x00, 0x49, 0xfa, 0x88, + 0x08, 0x77, 0x41, 0x04, 0x00, 0x00, 0x00, 0x32, + 0x7f, 0xda, 0x4c, 0x00, 0x10, 0xaa, 0x78, 0x80, + 0xff, 0x68, 0xa0, 0xff, 0xdd, 0x00, 0x00 +}; + +static u8 mt2131_config2[] = { + 0x10, + 0x7f, 0xc8, 0x0a, 0x5f, 0x00, 0x04 +}; + +static int mt2131_readreg(struct mt2131_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->cfg->i2c_address, .flags = 0, + .buf = ®, .len = 1 }, + { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, + .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + printk(KERN_WARNING "mt2131 I2C read failed\n"); + return -EREMOTEIO; + } + return 0; +} + +static int mt2131_writereg(struct mt2131_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + struct i2c_msg msg = { .addr = priv->cfg->i2c_address, .flags = 0, + .buf = buf, .len = 2 }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "mt2131 I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +static int mt2131_writeregs(struct mt2131_priv *priv,u8 *buf, u8 len) +{ + struct i2c_msg msg = { .addr = priv->cfg->i2c_address, + .flags = 0, .buf = buf, .len = len }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "mt2131 I2C write failed (len=%i)\n", + (int)len); + return -EREMOTEIO; + } + return 0; +} + +static int mt2131_set_gpo(struct dvb_frontend *fe, u8 val) +{ + struct mt2131_priv *priv = fe->tuner_priv; + u8 v; + + mt2131_readreg(priv, 0x07, &v); + mt2131_writereg(priv, 0x07, (v & 0xfe) | (val & 0x01)); + + return 0; +} + +static int mt2131_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct mt2131_priv *priv; + int ret=0, i; + u32 freq; + u8 if_band_center; + u32 f_lo1, f_lo2; + u32 div1, num1, div2, num2; + u8 b[8]; + u8 lockval = 0; + + priv = fe->tuner_priv; + if (fe->ops.info.type == FE_OFDM) + priv->bandwidth = params->u.ofdm.bandwidth; + else + priv->bandwidth = 0; + + freq = params->frequency / 1000; // Hz -> kHz + dprintk(1, "%s() freq=%d\n", __FUNCTION__, freq); + + f_lo1 = freq + MT2131_IF1 * 1000; + f_lo1 = (f_lo1 / 250) * 250; + f_lo2 = f_lo1 - freq - MT2131_IF2; + + priv->frequency = (f_lo1 - f_lo2 - MT2131_IF2) * 1000, + + /* Frequency LO1 = 16MHz * (DIV1 + NUM1/8192 ) */ + num1 = f_lo1 * 64 / (MT2131_FREF / 128); + div1 = num1 / 8192; + num1 &= 0x1fff; + + /* Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) */ + num2 = f_lo2 * 64 / (MT2131_FREF / 128); + div2 = num2 / 8192; + num2 &= 0x1fff; + + if (freq <= 82500) if_band_center = 0x00; else + if (freq <= 137500) if_band_center = 0x01; else + if (freq <= 192500) if_band_center = 0x02; else + if (freq <= 247500) if_band_center = 0x03; else + if (freq <= 302500) if_band_center = 0x04; else + if (freq <= 357500) if_band_center = 0x05; else + if (freq <= 412500) if_band_center = 0x06; else + if (freq <= 467500) if_band_center = 0x07; else + if (freq <= 522500) if_band_center = 0x08; else + if (freq <= 577500) if_band_center = 0x09; else + if (freq <= 632500) if_band_center = 0x0A; else + if (freq <= 687500) if_band_center = 0x0B; else + if (freq <= 742500) if_band_center = 0x0C; else + if (freq <= 797500) if_band_center = 0x0D; else + if (freq <= 852500) if_band_center = 0x0E; else + if (freq <= 907500) if_band_center = 0x0F; else + if (freq <= 962500) if_band_center = 0x10; else + if (freq <= 1017500) if_band_center = 0x11; else + if (freq <= 1072500) if_band_center = 0x12; else if_band_center = 0x13; + + b[0] = 1; + b[1] = (num1 >> 5) & 0xFF; + b[2] = (num1 & 0x1F); + b[3] = div1; + b[4] = (num2 >> 5) & 0xFF; + b[5] = num2 & 0x1F; + b[6] = div2; + + dprintk(1, "IF1: %dMHz IF2: %dMHz\n", MT2131_IF1, MT2131_IF2); + dprintk(1, "PLL freq=%dkHz band=%d\n", (int)freq, (int)if_band_center); + dprintk(1, "PLL f_lo1=%dkHz f_lo2=%dkHz\n", (int)f_lo1, (int)f_lo2); + dprintk(1, "PLL div1=%d num1=%d div2=%d num2=%d\n", + (int)div1, (int)num1, (int)div2, (int)num2); + dprintk(1, "PLL [1..6]: %2x %2x %2x %2x %2x %2x\n", + (int)b[1], (int)b[2], (int)b[3], (int)b[4], (int)b[5], + (int)b[6]); + + ret = mt2131_writeregs(priv,b,7); + if (ret < 0) + return ret; + + mt2131_writereg(priv, 0x0b, if_band_center); + + /* Wait for lock */ + i = 0; + do { + mt2131_readreg(priv, 0x08, &lockval); + if ((lockval & 0x88) == 0x88) + break; + msleep(4); + i++; + } while (i < 10); + + return ret; +} + +static int mt2131_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mt2131_priv *priv = fe->tuner_priv; + dprintk(1, "%s()\n", __FUNCTION__); + *frequency = priv->frequency; + return 0; +} + +static int mt2131_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct mt2131_priv *priv = fe->tuner_priv; + dprintk(1, "%s()\n", __FUNCTION__); + *bandwidth = priv->bandwidth; + return 0; +} + +static int mt2131_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct mt2131_priv *priv = fe->tuner_priv; + u8 lock_status = 0; + u8 afc_status = 0; + + *status = 0; + + mt2131_readreg(priv, 0x08, &lock_status); + if ((lock_status & 0x88) == 0x88) + *status = TUNER_STATUS_LOCKED; + + mt2131_readreg(priv, 0x09, &afc_status); + dprintk(1, "%s() - LO Status = 0x%x, AFC Status = 0x%x\n", + __FUNCTION__, lock_status, afc_status); + + return 0; +} + +static int mt2131_init(struct dvb_frontend *fe) +{ + struct mt2131_priv *priv = fe->tuner_priv; + int ret; + dprintk(1, "%s()\n", __FUNCTION__); + + if ((ret = mt2131_writeregs(priv, mt2131_config1, + sizeof(mt2131_config1))) < 0) + return ret; + + mt2131_writereg(priv, 0x0b, 0x09); + mt2131_writereg(priv, 0x15, 0x47); + mt2131_writereg(priv, 0x07, 0xf2); + mt2131_writereg(priv, 0x0b, 0x01); + + if ((ret = mt2131_writeregs(priv, mt2131_config2, + sizeof(mt2131_config2))) < 0) + return ret; + + return ret; +} + +static int mt2131_release(struct dvb_frontend *fe) +{ + dprintk(1, "%s()\n", __FUNCTION__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static const struct dvb_tuner_ops mt2131_tuner_ops = { + .info = { + .name = "Microtune MT2131", + .frequency_min = 48000000, + .frequency_max = 860000000, + .frequency_step = 50000, + }, + + .release = mt2131_release, + .init = mt2131_init, + + .set_params = mt2131_set_params, + .get_frequency = mt2131_get_frequency, + .get_bandwidth = mt2131_get_bandwidth, + .get_status = mt2131_get_status +}; + +struct dvb_frontend * mt2131_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct mt2131_config *cfg, u16 if1) +{ + struct mt2131_priv *priv = NULL; + u8 id = 0; + + dprintk(1, "%s()\n", __FUNCTION__); + + priv = kzalloc(sizeof(struct mt2131_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->bandwidth = 6000000; /* 6MHz */ + priv->i2c = i2c; + + if (mt2131_readreg(priv, 0, &id) != 0) { + kfree(priv); + return NULL; + } + if ( (id != 0x3E) && (id != 0x3F) ) { + printk(KERN_ERR "MT2131: Device not found at addr 0x%02x\n", + cfg->i2c_address); + kfree(priv); + return NULL; + } + + printk(KERN_INFO "MT2131: successfully identified at address 0x%02x\n", + cfg->i2c_address); + memcpy(&fe->ops.tuner_ops, &mt2131_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + return fe; +} +EXPORT_SYMBOL(mt2131_attach); + +MODULE_AUTHOR("Steven Toth"); +MODULE_DESCRIPTION("Microtune MT2131 silicon tuner driver"); +MODULE_LICENSE("GPL"); + +/* + * Local variables: + * c-basic-offset: 8 + */ diff -puN /dev/null drivers/media/dvb/frontends/mt2131.h --- /dev/null +++ a/drivers/media/dvb/frontends/mt2131.h @@ -0,0 +1,54 @@ +/* + * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" + * + * Copyright (c) 2006 Steven Toth + * + * 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 __MT2131_H__ +#define __MT2131_H__ + +struct dvb_frontend; +struct i2c_adapter; + +struct mt2131_config { + u8 i2c_address; + u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */ +}; + +#if defined(CONFIG_DVB_TUNER_MT2131) || defined(CONFIG_DVB_TUNER_MT2131_MODULE) +extern struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct mt2131_config *cfg, + u16 if1); +#else +static inline struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct mt2131_config *cfg, + u16 if1) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif /* CONFIG_DVB_TUNER_MT2131 */ + +#endif /* __MT2131_H__ */ + +/* + * Local variables: + * c-basic-offset: 8 + */ diff -puN /dev/null drivers/media/dvb/frontends/mt2131_priv.h --- /dev/null +++ a/drivers/media/dvb/frontends/mt2131_priv.h @@ -0,0 +1,49 @@ +/* + * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" + * + * Copyright (c) 2006 Steven Toth + * + * 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 __MT2131_PRIV_H__ +#define __MT2131_PRIV_H__ + +/* Regs */ +#define MT2131_PWR 0x07 +#define MT2131_UPC_1 0x0b +#define MT2131_AGC_RL 0x10 +#define MT2131_MISC_2 0x15 + +/* frequency values in KHz */ +#define MT2131_IF1 1220 +#define MT2131_IF2 44000 +#define MT2131_FREF 16000 + +struct mt2131_priv { + struct mt2131_config *cfg; + struct i2c_adapter *i2c; + + u32 frequency; + u32 bandwidth; +}; + +#endif /* __MT2131_PRIV_H__ */ + +/* + * Local variables: + * c-basic-offset: 8 + */ diff -puN /dev/null drivers/media/dvb/frontends/mt2266.c --- /dev/null +++ a/drivers/media/dvb/frontends/mt2266.c @@ -0,0 +1,288 @@ +/* + * Driver for Microtune MT2266 "Direct conversion low power broadband tuner" + * + * Copyright (c) 2007 Olivier DANET + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "mt2266.h" + +#define I2C_ADDRESS 0x60 + +#define REG_PART_REV 0 +#define REG_TUNE 1 +#define REG_BAND 6 +#define REG_BANDWIDTH 8 +#define REG_LOCK 0x12 + +#define PART_REV 0x85 + +struct mt2266_priv { + struct mt2266_config *cfg; + struct i2c_adapter *i2c; + + u32 frequency; + u32 bandwidth; +}; + +/* Here, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2266: " args); printk("\n"); }} while (0) + +// Reads a single register +static int mt2266_readreg(struct mt2266_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + printk(KERN_WARNING "MT2266 I2C read failed\n"); + return -EREMOTEIO; + } + return 0; +} + +// Writes a single register +static int mt2266_writereg(struct mt2266_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + struct i2c_msg msg = { + .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 + }; + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "MT2266 I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +// Writes a set of consecutive registers +static int mt2266_writeregs(struct mt2266_priv *priv,u8 *buf, u8 len) +{ + struct i2c_msg msg = { + .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len + }; + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "MT2266 I2C write failed (len=%i)\n",(int)len); + return -EREMOTEIO; + } + return 0; +} + +// Initialisation sequences +static u8 mt2266_init1[] = { + REG_TUNE, + 0x00, 0x00, 0x28, 0x00, 0x52, 0x99, 0x3f }; + +static u8 mt2266_init2[] = { + 0x17, 0x6d, 0x71, 0x61, 0xc0, 0xbf, 0xff, 0xdc, 0x00, 0x0a, + 0xd4, 0x03, 0x64, 0x64, 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x7f, 0x5e, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x77, 0x0f, 0x2d }; + +static u8 mt2266_init_8mhz[] = { + REG_BANDWIDTH, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 }; + +static u8 mt2266_init_7mhz[] = { + REG_BANDWIDTH, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32 }; + +static u8 mt2266_init_6mhz[] = { + REG_BANDWIDTH, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7 }; + +#define FREF 30000 // Quartz oscillator 30 MHz + +static int mt2266_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct mt2266_priv *priv; + int ret=0; + u32 freq; + u32 tune; + u8 lnaband; + u8 b[10]; + int i; + + priv = fe->tuner_priv; + + mt2266_writereg(priv,0x17,0x6d); + mt2266_writereg(priv,0x1c,0xff); + + freq = params->frequency / 1000; // Hz -> kHz + priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; + priv->frequency = freq * 1000; + tune=2 * freq * (8192/16) / (FREF/16); + + if (freq <= 495000) lnaband = 0xEE; else + if (freq <= 525000) lnaband = 0xDD; else + if (freq <= 550000) lnaband = 0xCC; else + if (freq <= 580000) lnaband = 0xBB; else + if (freq <= 605000) lnaband = 0xAA; else + if (freq <= 630000) lnaband = 0x99; else + if (freq <= 655000) lnaband = 0x88; else + if (freq <= 685000) lnaband = 0x77; else + if (freq <= 710000) lnaband = 0x66; else + if (freq <= 735000) lnaband = 0x55; else + if (freq <= 765000) lnaband = 0x44; else + if (freq <= 802000) lnaband = 0x33; else + if (freq <= 840000) lnaband = 0x22; else lnaband = 0x11; + + msleep(100); + mt2266_writeregs(priv,(params->u.ofdm.bandwidth==BANDWIDTH_6_MHZ)?mt2266_init_6mhz: + (params->u.ofdm.bandwidth==BANDWIDTH_7_MHZ)?mt2266_init_7mhz: + mt2266_init_8mhz,sizeof(mt2266_init_8mhz)); + + b[0] = REG_TUNE; + b[1] = (tune >> 8) & 0x1F; + b[2] = tune & 0xFF; + b[3] = tune >> 13; + mt2266_writeregs(priv,b,4); + + dprintk("set_parms: tune=%d band=%d\n",(int)tune,(int)lnaband); + dprintk("set_parms: [1..3]: %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3]); + + b[0] = 0x05; + b[1] = 0x62; + b[2] = lnaband; + mt2266_writeregs(priv,b,3); + + //Waits for pll lock or timeout + i = 0; + do { + mt2266_readreg(priv,REG_LOCK,b); + if ((b[0] & 0x40)==0x40) + break; + msleep(10); + i++; + } while (i<10); + dprintk("Lock when i=%i\n",(int)i); + return ret; +} + +static void mt2266_calibrate(struct mt2266_priv *priv) +{ + mt2266_writereg(priv,0x11,0x03); + mt2266_writereg(priv,0x11,0x01); + + mt2266_writeregs(priv,mt2266_init1,sizeof(mt2266_init1)); + mt2266_writeregs(priv,mt2266_init2,sizeof(mt2266_init2)); + + mt2266_writereg(priv,0x33,0x5e); + mt2266_writereg(priv,0x10,0x10); + mt2266_writereg(priv,0x10,0x00); + + mt2266_writeregs(priv,mt2266_init_8mhz,sizeof(mt2266_init_8mhz)); + + msleep(25); + mt2266_writereg(priv,0x17,0x6d); + mt2266_writereg(priv,0x1c,0x00); + msleep(75); + mt2266_writereg(priv,0x17,0x6d); + mt2266_writereg(priv,0x1c,0xff); +} + +static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mt2266_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct mt2266_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static int mt2266_init(struct dvb_frontend *fe) +{ + struct mt2266_priv *priv = fe->tuner_priv; + mt2266_writereg(priv,0x17,0x6d); + mt2266_writereg(priv,0x1c,0xff); + return 0; +} + +static int mt2266_sleep(struct dvb_frontend *fe) +{ + struct mt2266_priv *priv = fe->tuner_priv; + mt2266_writereg(priv,0x17,0x6d); + mt2266_writereg(priv,0x1c,0x00); + return 0; +} + +static int mt2266_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static const struct dvb_tuner_ops mt2266_tuner_ops = { + .info = { + .name = "Microtune MT2266", + .frequency_min = 470000000, + .frequency_max = 860000000, + .frequency_step = 50000, + }, + .release = mt2266_release, + .init = mt2266_init, + .sleep = mt2266_sleep, + .set_params = mt2266_set_params, + .get_frequency = mt2266_get_frequency, + .get_bandwidth = mt2266_get_bandwidth +}; + +struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg) +{ + struct mt2266_priv *priv = NULL; + u8 id = 0; + + priv = kzalloc(sizeof(struct mt2266_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + + if (mt2266_readreg(priv,0,&id) != 0) { + kfree(priv); + return NULL; + } + if (id != PART_REV) { + kfree(priv); + return NULL; + } + printk(KERN_INFO "MT2266: successfully identified\n"); + memcpy(&fe->ops.tuner_ops, &mt2266_tuner_ops, sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + mt2266_calibrate(priv); + return fe; +} +EXPORT_SYMBOL(mt2266_attach); + +MODULE_AUTHOR("Olivier DANET"); +MODULE_DESCRIPTION("Microtune MT2266 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff -puN /dev/null drivers/media/dvb/frontends/mt2266.h --- /dev/null +++ a/drivers/media/dvb/frontends/mt2266.h @@ -0,0 +1,37 @@ +/* + * Driver for Microtune MT2266 "Direct conversion low power broadband tuner" + * + * Copyright (c) 2007 Olivier DANET + * + * 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. + */ + +#ifndef MT2266_H +#define MT2266_H + +struct dvb_frontend; +struct i2c_adapter; + +struct mt2266_config { + u8 i2c_address; +}; + +#if defined(CONFIG_DVB_TUNER_MT2266) || (defined(CONFIG_DVB_TUNER_MT2266_MODULE) && defined(MODULE)) +extern struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg); +#else +static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_TUNER_MT2266 + +#endif diff -puN /dev/null drivers/media/dvb/frontends/s5h1409.c --- /dev/null +++ a/drivers/media/dvb/frontends/s5h1409.c @@ -0,0 +1,729 @@ +/* + Samsung S5H1409 VSB/QAM demodulator driver + + Copyright (C) 2006 Steven Toth + + 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 +#include +#include +#include +#include +#include +#include "dvb_frontend.h" +#include "dvb-pll.h" +#include "s5h1409.h" + +struct s5h1409_state { + + struct i2c_adapter* i2c; + + /* configuration settings */ + const struct s5h1409_config* config; + + struct dvb_frontend frontend; + + /* previous uncorrected block counter */ + fe_modulation_t current_modulation; + + u32 current_frequency; +}; + +static int debug = 0; +#define dprintk if (debug) printk + +/* Register values to initialise the demod, this will set VSB by default */ +static struct init_tab { + u8 reg; + u16 data; +} init_tab[] = { + { 0x00, 0x0071, }, + { 0x01, 0x3213, }, + { 0x09, 0x0025, }, + { 0x1c, 0x001d, }, + { 0x1f, 0x002d, }, + { 0x20, 0x001d, }, + { 0x22, 0x0022, }, + { 0x23, 0x0020, }, + { 0x29, 0x110f, }, + { 0x2a, 0x10b4, }, + { 0x2b, 0x10ae, }, + { 0x2c, 0x0031, }, + { 0x31, 0x010d, }, + { 0x32, 0x0100, }, + { 0x44, 0x0510, }, + { 0x54, 0x0104, }, + { 0x58, 0x2222, }, + { 0x59, 0x1162, }, + { 0x5a, 0x3211, }, + { 0x5d, 0x0370, }, + { 0x5e, 0x0296, }, + { 0x61, 0x0010, }, + { 0x63, 0x4a00, }, + { 0x65, 0x0800, }, + { 0x71, 0x0003, }, + { 0x72, 0x0470, }, + { 0x81, 0x0002, }, + { 0x82, 0x0600, }, + { 0x86, 0x0002, }, + { 0x8a, 0x2c38, }, + { 0x8b, 0x2a37, }, + { 0x92, 0x302f, }, + { 0x93, 0x3332, }, + { 0x96, 0x000c, }, + { 0x99, 0x0101, }, + { 0x9c, 0x2e37, }, + { 0x9d, 0x2c37, }, + { 0x9e, 0x2c37, }, + { 0xab, 0x0100, }, + { 0xac, 0x1003, }, + { 0xad, 0x103f, }, + { 0xe2, 0x0100, }, + { 0x28, 0x1010, }, + { 0xb1, 0x000e, }, +}; + +/* VSB SNR lookup table */ +static struct vsb_snr_tab { + u16 val; + u16 data; +} vsb_snr_tab[] = { + { 1023, 770, }, + { 923, 300, }, + { 918, 295, }, + { 915, 290, }, + { 911, 285, }, + { 906, 280, }, + { 901, 275, }, + { 896, 270, }, + { 891, 265, }, + { 885, 260, }, + { 879, 255, }, + { 873, 250, }, + { 864, 245, }, + { 858, 240, }, + { 850, 235, }, + { 841, 230, }, + { 832, 225, }, + { 823, 220, }, + { 812, 215, }, + { 802, 210, }, + { 788, 205, }, + { 778, 200, }, + { 767, 195, }, + { 753, 190, }, + { 740, 185, }, + { 725, 180, }, + { 707, 175, }, + { 689, 170, }, + { 671, 165, }, + { 656, 160, }, + { 637, 155, }, + { 616, 150, }, + { 542, 145, }, + { 519, 140, }, + { 507, 135, }, + { 497, 130, }, + { 492, 125, }, + { 474, 120, }, + { 300, 111, }, + { 0, 0, }, +}; + +/* QAM64 SNR lookup table */ +static struct qam64_snr_tab { + u16 val; + u16 data; +} qam64_snr_tab[] = { + { 12, 300, }, + { 15, 290, }, + { 18, 280, }, + { 22, 270, }, + { 23, 268, }, + { 24, 266, }, + { 25, 264, }, + { 27, 262, }, + { 28, 260, }, + { 29, 258, }, + { 30, 256, }, + { 32, 254, }, + { 33, 252, }, + { 34, 250, }, + { 35, 249, }, + { 36, 248, }, + { 37, 247, }, + { 38, 246, }, + { 39, 245, }, + { 40, 244, }, + { 41, 243, }, + { 42, 241, }, + { 43, 240, }, + { 44, 239, }, + { 45, 238, }, + { 46, 237, }, + { 47, 236, }, + { 48, 235, }, + { 49, 234, }, + { 50, 233, }, + { 51, 232, }, + { 52, 231, }, + { 53, 230, }, + { 55, 229, }, + { 56, 228, }, + { 57, 227, }, + { 58, 226, }, + { 59, 225, }, + { 60, 224, }, + { 62, 223, }, + { 63, 222, }, + { 65, 221, }, + { 66, 220, }, + { 68, 219, }, + { 69, 218, }, + { 70, 217, }, + { 72, 216, }, + { 73, 215, }, + { 75, 214, }, + { 76, 213, }, + { 78, 212, }, + { 80, 211, }, + { 81, 210, }, + { 83, 209, }, + { 84, 208, }, + { 85, 207, }, + { 87, 206, }, + { 89, 205, }, + { 91, 204, }, + { 93, 203, }, + { 95, 202, }, + { 96, 201, }, + { 104, 200, }, +}; + +/* QAM256 SNR lookup table */ +static struct qam256_snr_tab { + u16 val; + u16 data; +} qam256_snr_tab[] = { + { 12, 400, }, + { 13, 390, }, + { 15, 380, }, + { 17, 360, }, + { 19, 350, }, + { 22, 348, }, + { 23, 346, }, + { 24, 344, }, + { 25, 342, }, + { 26, 340, }, + { 27, 336, }, + { 28, 334, }, + { 29, 332, }, + { 30, 330, }, + { 31, 328, }, + { 32, 326, }, + { 33, 325, }, + { 34, 322, }, + { 35, 320, }, + { 37, 318, }, + { 39, 316, }, + { 40, 314, }, + { 41, 312, }, + { 42, 310, }, + { 43, 308, }, + { 46, 306, }, + { 47, 304, }, + { 49, 302, }, + { 51, 300, }, + { 53, 298, }, + { 54, 297, }, + { 55, 296, }, + { 56, 295, }, + { 57, 294, }, + { 59, 293, }, + { 60, 292, }, + { 61, 291, }, + { 63, 290, }, + { 64, 289, }, + { 65, 288, }, + { 66, 287, }, + { 68, 286, }, + { 69, 285, }, + { 71, 284, }, + { 72, 283, }, + { 74, 282, }, + { 75, 281, }, + { 76, 280, }, + { 77, 279, }, + { 78, 278, }, + { 81, 277, }, + { 83, 276, }, + { 84, 275, }, + { 86, 274, }, + { 87, 273, }, + { 89, 272, }, + { 90, 271, }, + { 92, 270, }, + { 93, 269, }, + { 95, 268, }, + { 96, 267, }, + { 98, 266, }, + { 100, 265, }, + { 102, 264, }, + { 104, 263, }, + { 105, 262, }, + { 106, 261, }, + { 110, 260, }, +}; + +/* 8 bit registers, 16 bit values */ +static int s5h1409_writereg(struct s5h1409_state* state, u8 reg, u16 data) +{ + int ret; + u8 buf [] = { reg, data >> 8, data & 0xff }; + + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 3 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, " + "ret == %i)\n", __FUNCTION__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static u16 s5h1409_readreg(struct s5h1409_state* state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0, 0 }; + + struct i2c_msg msg [] = { + { .addr = state->config->demod_address, .flags = 0, + .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, + .buf = b1, .len = 2 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); + return (b1[0] << 8) | b1[1]; +} + +static int s5h1409_softreset(struct dvb_frontend* fe) +{ + struct s5h1409_state* state = fe->demodulator_priv; + + dprintk("%s()\n", __FUNCTION__); + + s5h1409_writereg(state, 0xf5, 0); + s5h1409_writereg(state, 0xf5, 1); + return 0; +} + +static int s5h1409_set_if_freq(struct dvb_frontend* fe, int KHz) +{ + struct s5h1409_state* state = fe->demodulator_priv; + int ret = 0; + + dprintk("%s(%d KHz)\n", __FUNCTION__, KHz); + + if( (KHz == 44000) || (KHz == 5380) ) { + s5h1409_writereg(state, 0x87, 0x01be); + s5h1409_writereg(state, 0x88, 0x0436); + s5h1409_writereg(state, 0x89, 0x054d); + } else { + printk("%s() Invalid arg = %d KHz\n", __FUNCTION__, KHz); + ret = -1; + } + + return ret; +} + +static int s5h1409_set_spectralinversion(struct dvb_frontend* fe, int inverted) +{ + struct s5h1409_state* state = fe->demodulator_priv; + + dprintk("%s()\n", __FUNCTION__); + + if(inverted == 1) + return s5h1409_writereg(state, 0x1b, 0x1101); /* Inverted */ + else + return s5h1409_writereg(state, 0x1b, 0x0110); /* Normal */ +} + +static int s5h1409_enable_modulation(struct dvb_frontend* fe, + fe_modulation_t m) +{ + struct s5h1409_state* state = fe->demodulator_priv; + + dprintk("%s(0x%08x)\n", __FUNCTION__, m); + + switch(m) { + case VSB_8: + dprintk("%s() VSB_8\n", __FUNCTION__); + s5h1409_writereg(state, 0xf4, 0); + break; + case QAM_64: + dprintk("%s() QAM_64\n", __FUNCTION__); + s5h1409_writereg(state, 0xf4, 1); + s5h1409_writereg(state, 0x85, 0x100); + break; + case QAM_256: + dprintk("%s() QAM_256\n", __FUNCTION__); + s5h1409_writereg(state, 0xf4, 1); + s5h1409_writereg(state, 0x85, 0x101); + break; + default: + dprintk("%s() Invalid modulation\n", __FUNCTION__); + return -EINVAL; + } + + state->current_modulation = m; + s5h1409_softreset(fe); + + return 0; +} + +static int s5h1409_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct s5h1409_state* state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __FUNCTION__, enable); + + if (enable) + return s5h1409_writereg(state, 0xf3, 1); + else + return s5h1409_writereg(state, 0xf3, 0); +} + +static int s5h1409_set_gpio(struct dvb_frontend* fe, int enable) +{ + struct s5h1409_state* state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __FUNCTION__, enable); + + if (enable) + return s5h1409_writereg(state, 0xe3, 0x1100); + else + return s5h1409_writereg(state, 0xe3, 0); +} + +static int s5h1409_sleep(struct dvb_frontend* fe, int enable) +{ + struct s5h1409_state* state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __FUNCTION__, enable); + + return s5h1409_writereg(state, 0xf2, enable); +} + +static int s5h1409_register_reset(struct dvb_frontend* fe) +{ + struct s5h1409_state* state = fe->demodulator_priv; + + dprintk("%s()\n", __FUNCTION__); + + return s5h1409_writereg(state, 0xfa, 0); +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +static int s5h1409_set_frontend (struct dvb_frontend* fe, + struct dvb_frontend_parameters *p) +{ + struct s5h1409_state* state = fe->demodulator_priv; + + dprintk("%s(frequency=%d)\n", __FUNCTION__, p->frequency); + + s5h1409_softreset(fe); + + state->current_frequency = p->frequency; + + s5h1409_enable_modulation(fe, p->u.vsb.modulation); + + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe, p); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +static int s5h1409_init (struct dvb_frontend* fe) +{ + int i; + + struct s5h1409_state* state = fe->demodulator_priv; + dprintk("%s()\n", __FUNCTION__); + + s5h1409_sleep(fe, 0); + s5h1409_register_reset(fe); + + for (i=0; i < ARRAY_SIZE(init_tab); i++) + s5h1409_writereg(state, init_tab[i].reg, init_tab[i].data); + + /* The datasheet says that after initialisation, VSB is default */ + state->current_modulation = VSB_8; + + if (state->config->output_mode == S5H1409_SERIAL_OUTPUT) + s5h1409_writereg(state, 0xab, 0x100); /* Serial */ + else + s5h1409_writereg(state, 0xab, 0x0); /* Parallel */ + + s5h1409_set_spectralinversion(fe, state->config->inversion); + s5h1409_set_if_freq(fe, state->config->if_freq); + s5h1409_set_gpio(fe, state->config->gpio); + s5h1409_softreset(fe); + + /* Note: Leaving the I2C gate open here. */ + s5h1409_i2c_gate_ctrl(fe, 1); + + return 0; +} + +static int s5h1409_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct s5h1409_state* state = fe->demodulator_priv; + u16 reg; + u32 tuner_status = 0; + + *status = 0; + + /* Get the demodulator status */ + reg = s5h1409_readreg(state, 0xf1); + if(reg & 0x1000) + *status |= FE_HAS_VITERBI; + if(reg & 0x8000) + *status |= FE_HAS_LOCK | FE_HAS_SYNC; + + switch(state->config->status_mode) { + case S5H1409_DEMODLOCKING: + if (*status & FE_HAS_VITERBI) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + case S5H1409_TUNERLOCKING: + /* Get the tuner status */ + if (fe->ops.tuner_ops.get_status) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.get_status(fe, &tuner_status); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + if (tuner_status) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + } + + dprintk("%s() status 0x%08x\n", __FUNCTION__, *status); + + return 0; +} + +static int s5h1409_qam256_lookup_snr(struct dvb_frontend* fe, u16* snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __FUNCTION__); + + for (i=0; i < ARRAY_SIZE(qam256_snr_tab); i++) { + if (v < qam256_snr_tab[i].val) { + *snr = qam256_snr_tab[i].data; + ret = 0; + break; + } + } + return ret; +} + +static int s5h1409_qam64_lookup_snr(struct dvb_frontend* fe, u16* snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __FUNCTION__); + + for (i=0; i < ARRAY_SIZE(qam64_snr_tab); i++) { + if (v < qam64_snr_tab[i].val) { + *snr = qam64_snr_tab[i].data; + ret = 0; + break; + } + } + return ret; +} + +static int s5h1409_vsb_lookup_snr(struct dvb_frontend* fe, u16* snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __FUNCTION__); + + for (i=0; i < ARRAY_SIZE(vsb_snr_tab); i++) { + if (v > vsb_snr_tab[i].val) { + *snr = vsb_snr_tab[i].data; + ret = 0; + break; + } + } + dprintk("%s() snr=%d\n", __FUNCTION__, *snr); + return ret; +} + +static int s5h1409_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct s5h1409_state* state = fe->demodulator_priv; + u16 reg; + dprintk("%s()\n", __FUNCTION__); + + reg = s5h1409_readreg(state, 0xf1) & 0x1ff; + + switch(state->current_modulation) { + case QAM_64: + return s5h1409_qam64_lookup_snr(fe, snr, reg); + case QAM_256: + return s5h1409_qam256_lookup_snr(fe, snr, reg); + case VSB_8: + return s5h1409_vsb_lookup_snr(fe, snr, reg); + default: + break; + } + + return -EINVAL; +} + +static int s5h1409_read_signal_strength(struct dvb_frontend* fe, + u16* signal_strength) +{ + return s5h1409_read_snr(fe, signal_strength); +} + +static int s5h1409_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct s5h1409_state* state = fe->demodulator_priv; + + *ucblocks = s5h1409_readreg(state, 0xb5); + + return 0; +} + +static int s5h1409_read_ber(struct dvb_frontend* fe, u32* ber) +{ + return s5h1409_read_ucblocks(fe, ber); +} + +static int s5h1409_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *p) +{ + struct s5h1409_state* state = fe->demodulator_priv; + + p->frequency = state->current_frequency; + p->u.vsb.modulation = state->current_modulation; + + return 0; +} + +static int s5h1409_get_tune_settings(struct dvb_frontend* fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void s5h1409_release(struct dvb_frontend* fe) +{ + struct s5h1409_state* state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops s5h1409_ops; + +struct dvb_frontend* s5h1409_attach(const struct s5h1409_config* config, + struct i2c_adapter* i2c) +{ + struct s5h1409_state* state = NULL; + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct s5h1409_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->current_modulation = 0; + + /* check if the demod exists */ + if (s5h1409_readreg(state, 0x04) != 0x0066) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &s5h1409_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + /* Note: Leaving the I2C gate open here. */ + s5h1409_writereg(state, 0xf3, 1); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static struct dvb_frontend_ops s5h1409_ops = { + + .info = { + .name = "Samsung S5H1409 QAM/8VSB Frontend", + .type = FE_ATSC, + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + + .init = s5h1409_init, + .i2c_gate_ctrl = s5h1409_i2c_gate_ctrl, + .set_frontend = s5h1409_set_frontend, + .get_frontend = s5h1409_get_frontend, + .get_tune_settings = s5h1409_get_tune_settings, + .read_status = s5h1409_read_status, + .read_ber = s5h1409_read_ber, + .read_signal_strength = s5h1409_read_signal_strength, + .read_snr = s5h1409_read_snr, + .read_ucblocks = s5h1409_read_ucblocks, + .release = s5h1409_release, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(s5h1409_attach); + +/* + * Local variables: + * c-basic-offset: 8 + */ diff -puN /dev/null drivers/media/dvb/frontends/s5h1409.h --- /dev/null +++ a/drivers/media/dvb/frontends/s5h1409.h @@ -0,0 +1,73 @@ +/* + Samsung S5H1409 VSB/QAM demodulator driver + + Copyright (C) 2006 Steven Toth + + 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 __S5H1409_H__ +#define __S5H1409_H__ + +#include + +struct s5h1409_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* serial/parallel output */ +#define S5H1409_PARALLEL_OUTPUT 0 +#define S5H1409_SERIAL_OUTPUT 1 + u8 output_mode; + + /* GPIO Setting */ +#define S5H1409_GPIO_OFF 0 +#define S5H1409_GPIO_ON 1 + u8 gpio; + + /* IF Freq in KHz */ + u16 if_freq; + + /* Spectral Inversion */ +#define S5H1409_INVERSION_OFF 0 +#define S5H1409_INVERSION_ON 1 + u8 inversion; + + /* Return lock status based on tuner lock, or demod lock */ +#define S5H1409_TUNERLOCKING 0 +#define S5H1409_DEMODLOCKING 1 + u8 status_mode; +}; + +#if defined(CONFIG_DVB_S5H1409) || defined(CONFIG_DVB_S5H1409_MODULE) +extern struct dvb_frontend* s5h1409_attach(const struct s5h1409_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* s5h1409_attach(const struct s5h1409_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif /* CONFIG_DVB_S5H1409 */ + +#endif /* __S5H1409_H__ */ + +/* + * Local variables: + * c-basic-offset: 8 + */ diff -puN drivers/media/dvb/frontends/tda10023.c~git-dvb drivers/media/dvb/frontends/tda10023.c --- a/drivers/media/dvb/frontends/tda10023.c~git-dvb +++ a/drivers/media/dvb/frontends/tda10023.c @@ -215,12 +215,6 @@ static int tda10023_set_symbolrate (stru s16 SFIL=0; u16 NDEC = 0; - if (sr > (SYSCLK/(2*4))) - sr=SYSCLK/(2*4); - - if (sr<870000) - sr=870000; - if (sr < (u32)(SYSCLK/98.40)) { NDEC=3; SFIL=1; diff -puN drivers/media/dvb/ttpci/av7110_v4l.c~git-dvb drivers/media/dvb/ttpci/av7110_v4l.c --- a/drivers/media/dvb/ttpci/av7110_v4l.c~git-dvb +++ a/drivers/media/dvb/ttpci/av7110_v4l.c @@ -129,23 +129,25 @@ static struct v4l2_input inputs[4] = { static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) { + struct av7110 *av7110 = dev->ext_priv; u8 buf[] = { 0x00, reg, data }; struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; dprintk(4, "dev: %p\n", dev); - if (1 != saa7146_i2c_transfer(dev, &msg, 1, 1)) + if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) return -1; return 0; } static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) { + struct av7110 *av7110 = dev->ext_priv; struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; dprintk(4, "dev: %p\n", dev); - if (1 != saa7146_i2c_transfer(dev, &msg, 1, 1)) + if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) return -1; return 0; } diff -puN drivers/media/dvb/ttpci/budget-av.c~git-dvb drivers/media/dvb/ttpci/budget-av.c --- a/drivers/media/dvb/ttpci/budget-av.c~git-dvb +++ a/drivers/media/dvb/ttpci/budget-av.c @@ -1232,7 +1232,7 @@ static struct saa7146_ext_vv vv_data = { .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113 .flags = 0, .stds = &standard[0], - .num_stds = sizeof(standard) / sizeof(struct saa7146_standard), + .num_stds = ARRAY_SIZE(standard), .ioctls = &ioctls[0], .ioctl = av_ioctl, }; diff -puN drivers/media/video/Kconfig~git-dvb drivers/media/video/Kconfig --- a/drivers/media/video/Kconfig~git-dvb +++ a/drivers/media/video/Kconfig @@ -148,6 +148,15 @@ config VIDEO_WM8739 To compile this driver as a module, choose M here: the module will be called wm8739. +config VIDEO_VP27SMPX + tristate "Panasonic VP27s internal MPX" + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + ---help--- + Support for the internal MPX of the Panasonic VP27s tuner. + + To compile this driver as a module, choose M here: the + module will be called vp27smpx. + comment "Video decoders" config VIDEO_BT819 @@ -197,6 +206,13 @@ config VIDEO_OV7670 OV7670 VGA camera. It currently only works with the M88ALP01 controller. +config VIDEO_TCM825X + tristate "TCM825x camera sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a driver for the Toshiba TCM825x VGA camera sensor. + It is used for example in Nokia N800. + config VIDEO_SAA7110 tristate "Philips SAA7110 video decoder" depends on VIDEO_V4L1 && I2C diff -puN drivers/media/video/Makefile~git-dvb drivers/media/video/Makefile --- a/drivers/media/video/Makefile~git-dvb +++ a/drivers/media/video/Makefile @@ -11,7 +11,8 @@ tuner-$(CONFIG_TUNER_TEA5761) += tea5761 msp3400-objs := msp3400-driver.o msp3400-kthreads.o -obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o +obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o \ + v4l2-int-device.o ifeq ($(CONFIG_VIDEO_V4L1_COMPAT),y) obj-$(CONFIG_VIDEO_DEV) += v4l1-compat.o @@ -63,7 +64,6 @@ obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ -obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o @@ -73,6 +73,7 @@ obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o obj-$(CONFIG_VIDEO_WM8775) += wm8775.o obj-$(CONFIG_VIDEO_WM8739) += wm8739.o +obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_MXB) += mxb.o @@ -97,6 +98,8 @@ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o +obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o + obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o @@ -114,6 +117,8 @@ obj-$(CONFIG_USB_KONICAWC) += usbvi obj-$(CONFIG_USB_VICAM) += usbvideo/ obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += usbvideo/ +obj-$(CONFIG_VIDEO_IVTV) += ivtv/ + obj-$(CONFIG_VIDEO_VIVI) += vivi.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff -puN drivers/media/video/arv.c~git-dvb drivers/media/video/arv.c --- a/drivers/media/video/arv.c~git-dvb +++ a/drivers/media/video/arv.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff -puN drivers/media/video/cx25840/cx25840-core.c~git-dvb drivers/media/video/cx25840/cx25840-core.c --- a/drivers/media/video/cx25840/cx25840-core.c~git-dvb +++ a/drivers/media/video/cx25840/cx25840-core.c @@ -625,6 +625,22 @@ static int cx25840_command(struct i2c_cl struct v4l2_tuner *vt = arg; struct v4l2_routing *route = arg; + /* ignore these commands */ + switch (cmd) { + case TUNER_SET_TYPE_ADDR: + return 0; + } + + if (!state->is_initialized) { + v4l_dbg(1, cx25840_debug, client, "cmd %08x triggered fw load\n", cmd); + /* initialize on first use */ + state->is_initialized = 1; + if (state->is_cx25836) + cx25836_initialize(client); + else + cx25840_initialize(client, 1); + } + switch (cmd) { #ifdef CONFIG_VIDEO_ADV_DEBUG /* ioctls to allow direct access to the @@ -906,11 +922,6 @@ static int cx25840_detect_client(struct i2c_attach_client(client); - if (state->is_cx25836) - cx25836_initialize(client); - else - cx25840_initialize(client, 1); - return 0; } diff -puN drivers/media/video/cx25840/cx25840-core.h~git-dvb drivers/media/video/cx25840/cx25840-core.h --- a/drivers/media/video/cx25840/cx25840-core.h~git-dvb +++ a/drivers/media/video/cx25840/cx25840-core.h @@ -46,6 +46,7 @@ struct cx25840_state { u32 id; u32 rev; int is_cx25836; + int is_initialized; }; /* ----------------------------------------------------------------------- */ diff -puN drivers/media/video/cx88/cx88-mpeg.c~git-dvb drivers/media/video/cx88/cx88-mpeg.c --- a/drivers/media/video/cx88/cx88-mpeg.c~git-dvb +++ a/drivers/media/video/cx88/cx88-mpeg.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include "cx88.h" diff -puN drivers/media/video/cx88/cx88-video.c~git-dvb drivers/media/video/cx88/cx88-video.c --- a/drivers/media/video/cx88/cx88-video.c~git-dvb +++ a/drivers/media/video/cx88/cx88-video.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include "cx88.h" diff -puN drivers/media/video/ivtv/Kconfig~git-dvb drivers/media/video/ivtv/Kconfig --- a/drivers/media/video/ivtv/Kconfig~git-dvb +++ a/drivers/media/video/ivtv/Kconfig @@ -14,6 +14,7 @@ config VIDEO_IVTV select VIDEO_CS53L32A select VIDEO_WM8775 select VIDEO_WM8739 + select VIDEO_VP27SMPX select VIDEO_UPD64031A select VIDEO_UPD64083 ---help--- @@ -25,3 +26,19 @@ config VIDEO_IVTV To compile this driver as a module, choose M here: the module will be called ivtv. + +config VIDEO_IVTV_FB + tristate "Conexant cx23415 framebuffer support" + depends on VIDEO_IVTV && FB && EXPERIMENTAL + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + This is a framebuffer driver for the Conexant cx23415 MPEG + encoder/decoder. + + This is used in the Hauppauge PVR-350 card. There is a driver + homepage at . + + To compile this driver as a module, choose M here: the + module will be called ivtv-fb. diff -puN drivers/media/video/ivtv/Makefile~git-dvb drivers/media/video/ivtv/Makefile --- a/drivers/media/video/ivtv/Makefile~git-dvb +++ a/drivers/media/video/ivtv/Makefile @@ -5,3 +5,4 @@ ivtv-objs := ivtv-audio.o ivtv-cards.o i ivtv-vbi.o ivtv-video.o ivtv-yuv.o obj-$(CONFIG_VIDEO_IVTV) += ivtv.o +obj-$(CONFIG_VIDEO_IVTV_FB) += ivtv-fb.o diff -puN drivers/media/video/ivtv/ivtv-cards.c~git-dvb drivers/media/video/ivtv/ivtv-cards.c --- a/drivers/media/video/ivtv/ivtv-cards.c~git-dvb +++ a/drivers/media/video/ivtv/ivtv-cards.c @@ -616,7 +616,7 @@ static const struct ivtv_card ivtv_card_ .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, .hw_audio = IVTV_HW_GPIO, .hw_audio_ctrl = IVTV_HW_WM8739, - .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TVAUDIO | + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_VP27SMPX | IVTV_HW_TUNER | IVTV_HW_WM8739 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, .video_inputs = { @@ -654,7 +654,7 @@ static const struct ivtv_card ivtv_card_ .hw_audio = IVTV_HW_GPIO, .hw_audio_ctrl = IVTV_HW_WM8739, .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER | - IVTV_HW_TVAUDIO | IVTV_HW_WM8739, + IVTV_HW_VP27SMPX | IVTV_HW_WM8739, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, @@ -845,6 +845,7 @@ static const struct ivtv_card ivtv_card_ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, }, .audio_inputs = { { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, diff -puN drivers/media/video/ivtv/ivtv-cards.h~git-dvb drivers/media/video/ivtv/ivtv-cards.h --- a/drivers/media/video/ivtv/ivtv-cards.h~git-dvb +++ a/drivers/media/video/ivtv/ivtv-cards.h @@ -33,7 +33,8 @@ #define IVTV_HW_UPD6408X (1 << 11) #define IVTV_HW_SAA717X (1 << 12) #define IVTV_HW_WM8739 (1 << 13) -#define IVTV_HW_GPIO (1 << 14) +#define IVTV_HW_VP27SMPX (1 << 14) +#define IVTV_HW_GPIO (1 << 15) #define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114) diff -puN drivers/media/video/ivtv/ivtv-driver.c~git-dvb drivers/media/video/ivtv/ivtv-driver.c --- a/drivers/media/video/ivtv/ivtv-driver.c~git-dvb +++ a/drivers/media/video/ivtv/ivtv-driver.c @@ -57,6 +57,7 @@ #include "ivtv-yuv.h" #include +#include #include /* var to keep track of the number of array elements in use */ @@ -95,8 +96,14 @@ const u32 yuv_offset[4] = { /* Parameter declarations */ static int cardtype[IVTV_MAX_CARDS]; -static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; -static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; +static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; static int cardtype_c = 1; static int tuner_c = 1; @@ -808,7 +815,6 @@ static void ivtv_request_module(struct i static void ivtv_load_and_init_modules(struct ivtv *itv) { - struct v4l2_control ctrl; u32 hw = itv->card->hw_all; int i; @@ -848,6 +854,10 @@ static void ivtv_load_and_init_modules(s if (hw & IVTV_HW_MSP34XX) ivtv_request_module(itv, "msp3400"); #endif +#ifndef CONFIG_VIDEO_VP27SMPX + if (hw & IVTV_HW_VP27SMPX) + ivtv_request_module(itv, "vp27smpx"); +#endif if (hw & IVTV_HW_TVAUDIO) ivtv_request_module(itv, "tvaudio"); #ifndef CONFIG_VIDEO_WM8775 @@ -888,13 +898,17 @@ static void ivtv_load_and_init_modules(s else if ((hw & IVTV_HW_UPD64031A) == 0) itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR); } + else if (itv->card->type == IVTV_CARD_GV_MVPRX || + itv->card->type == IVTV_CARD_GV_MVPRX2E) { + struct v4l2_crystal_freq crystal_freq; + + /* The crystal frequency of GVMVPRX is 24.576MHz */ + crystal_freq.freq = SAA7115_FREQ_24_576_MHZ; + crystal_freq.flags = SAA7115_FREQ_FL_UCGC; + itv->video_dec_func(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq); + } if (hw & IVTV_HW_CX25840) { - /* CX25840_CID_ENABLE_PVR150_WORKAROUND */ - ctrl.id = V4L2_CID_PRIVATE_BASE; - ctrl.value = itv->pvr150_workaround; - itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl); - itv->vbi.raw_decoder_line_size = 1444; itv->vbi.raw_decoder_sav_odd_field = 0x20; itv->vbi.raw_decoder_sav_even_field = 0x60; @@ -940,12 +954,9 @@ static int __devinit ivtv_probe(struct p const struct pci_device_id *pci_id) { int retval = 0; - int video_input; int yuv_buf_size; int vbi_buf_size; - int fw_retry_count = 3; struct ivtv *itv; - struct v4l2_frequency vf; spin_lock(&ivtv_cards_lock); @@ -1032,22 +1043,6 @@ static int __devinit ivtv_probe(struct p goto free_io; } - while (--fw_retry_count > 0) { - /* load firmware */ - if (ivtv_firmware_init(itv) == 0) - break; - if (fw_retry_count > 1) - IVTV_WARN("Retry loading firmware\n"); - } - if (fw_retry_count == 0) { - IVTV_ERR("Error initializing firmware\n"); - goto free_i2c; - } - - /* Try and get firmware versions */ - IVTV_DEBUG_INFO("Getting firmware version..\n"); - ivtv_firmware_versions(itv); - /* Check yuv output filter table */ if (itv->has_cx23415) ivtv_yuv_filter_check(itv); @@ -1151,39 +1146,16 @@ static int __devinit ivtv_probe(struct p ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup); } - vf.tuner = 0; - vf.type = V4L2_TUNER_ANALOG_TV; - vf.frequency = 6400; /* the tuner 'baseline' frequency */ - if (itv->std & V4L2_STD_NTSC_M) { - /* Why on earth? */ - vf.frequency = 1076; /* ch. 4 67250*16/1000 */ - } - /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) are not. */ itv->tuner_std = itv->std; - video_input = itv->active_input; - itv->active_input++; /* Force update of input */ - ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_INPUT, &video_input); - - /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code - in one place. */ - itv->std++; /* Force full standard initialization */ - itv->std_out = itv->std; - ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_FREQUENCY, &vf); - retval = ivtv_streams_setup(itv); if (retval) { IVTV_ERR("Error %d setting up streams\n", retval); goto free_i2c; } - if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) { - ivtv_init_mpeg_decoder(itv); - } - ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_STD, &itv->tuner_std); - IVTV_DEBUG_IRQ("Masking interrupts\n"); /* clear interrupt mask, effectively disabling interrupts */ ivtv_set_irq_mask(itv, 0xffffffff); @@ -1195,26 +1167,7 @@ static int __devinit ivtv_probe(struct p IVTV_ERR("Failed to register irq %d\n", retval); goto free_streams; } - - /* On a cx23416 this seems to be able to enable DMA to the chip? */ - if (!itv->has_cx23415) - write_reg_sync(0x03, IVTV_REG_DMACONTROL); - - /* Default interrupts enabled. For the PVR350 this includes the - decoder VSYNC interrupt, which is always on. It is not only used - during decoding but also by the OSD. - Some old PVR250 cards had a cx23415, so testing for that is too - general. Instead test if the card has video output capability. */ - if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) - ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC); - else - ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT); - - if (itv->has_cx23415) - ivtv_set_osd_alpha(itv); - IVTV_INFO("Initialized card #%d: %s\n", itv->num, itv->card_name); - return 0; free_irq: @@ -1237,60 +1190,140 @@ static int __devinit ivtv_probe(struct p retval = -ENODEV; IVTV_ERR("Error %d on initialization\n", retval); + spin_lock(&ivtv_cards_lock); kfree(ivtv_cards[ivtv_cards_active]); ivtv_cards[ivtv_cards_active] = NULL; + spin_unlock(&ivtv_cards_lock); return retval; } +int ivtv_init_on_first_open(struct ivtv *itv) +{ + struct v4l2_frequency vf; + int fw_retry_count = 3; + int video_input; + + if (test_bit(IVTV_F_I_FAILED, &itv->i_flags)) + return -ENXIO; + + if (test_and_set_bit(IVTV_F_I_INITED, &itv->i_flags)) + return 0; + + while (--fw_retry_count > 0) { + /* load firmware */ + if (ivtv_firmware_init(itv) == 0) + break; + if (fw_retry_count > 1) + IVTV_WARN("Retry loading firmware\n"); + } + + if (fw_retry_count == 0) { + set_bit(IVTV_F_I_FAILED, &itv->i_flags); + return -ENXIO; + } + + /* Try and get firmware versions */ + IVTV_DEBUG_INFO("Getting firmware version..\n"); + ivtv_firmware_versions(itv); + + if (itv->card->hw_all & IVTV_HW_CX25840) { + struct v4l2_control ctrl; + + /* CX25840_CID_ENABLE_PVR150_WORKAROUND */ + ctrl.id = V4L2_CID_PRIVATE_BASE; + ctrl.value = itv->pvr150_workaround; + itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl); + } + + vf.tuner = 0; + vf.type = V4L2_TUNER_ANALOG_TV; + vf.frequency = 6400; /* the tuner 'baseline' frequency */ + + /* Set initial frequency. For PAL/SECAM broadcasts no + 'default' channel exists AFAIK. */ + if (itv->std == V4L2_STD_NTSC_M_JP) { + vf.frequency = 1460; /* ch. 1 91250*16/1000 */ + } + else if (itv->std & V4L2_STD_NTSC_M) { + vf.frequency = 1076; /* ch. 4 67250*16/1000 */ + } + + video_input = itv->active_input; + itv->active_input++; /* Force update of input */ + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_INPUT, &video_input); + + /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code + in one place. */ + itv->std++; /* Force full standard initialization */ + itv->std_out = itv->std; + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_FREQUENCY, &vf); + + if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) { + ivtv_init_mpeg_decoder(itv); + } + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_STD, &itv->tuner_std); + + /* On a cx23416 this seems to be able to enable DMA to the chip? */ + if (!itv->has_cx23415) + write_reg_sync(0x03, IVTV_REG_DMACONTROL); + + /* Default interrupts enabled. For the PVR350 this includes the + decoder VSYNC interrupt, which is always on. It is not only used + during decoding but also by the OSD. + Some old PVR250 cards had a cx23415, so testing for that is too + general. Instead test if the card has video output capability. */ + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC); + ivtv_set_osd_alpha(itv); + } + else + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT); + return 0; +} + static void ivtv_remove(struct pci_dev *pci_dev) { struct ivtv *itv = pci_get_drvdata(pci_dev); IVTV_DEBUG_INFO("Removing Card #%d\n", itv->num); - /* Stop all captures */ - IVTV_DEBUG_INFO("Stopping all streams\n"); - if (atomic_read(&itv->capturing) > 0) - ivtv_stop_all_captures(itv); - - /* Stop all decoding */ - IVTV_DEBUG_INFO("Stopping decoding\n"); - if (atomic_read(&itv->decoding) > 0) { - int type; - - if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) - type = IVTV_DEC_STREAM_TYPE_YUV; - else - type = IVTV_DEC_STREAM_TYPE_MPG; - ivtv_stop_v4l2_decode_stream(&itv->streams[type], - VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) { + /* Stop all captures */ + IVTV_DEBUG_INFO("Stopping all streams\n"); + if (atomic_read(&itv->capturing) > 0) + ivtv_stop_all_captures(itv); + + /* Stop all decoding */ + IVTV_DEBUG_INFO("Stopping decoding\n"); + if (atomic_read(&itv->decoding) > 0) { + int type; + + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) + type = IVTV_DEC_STREAM_TYPE_YUV; + else + type = IVTV_DEC_STREAM_TYPE_MPG; + ivtv_stop_v4l2_decode_stream(&itv->streams[type], + VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + } + ivtv_halt_firmware(itv); } /* Interrupts */ - IVTV_DEBUG_INFO("Disabling interrupts\n"); ivtv_set_irq_mask(itv, 0xffffffff); del_timer_sync(&itv->dma_timer); /* Stop all Work Queues */ - IVTV_DEBUG_INFO("Stop Work Queues\n"); flush_workqueue(itv->irq_work_queues); destroy_workqueue(itv->irq_work_queues); - IVTV_DEBUG_INFO("Stopping Firmware\n"); - ivtv_halt_firmware(itv); - - IVTV_DEBUG_INFO("Unregistering v4l devices\n"); ivtv_streams_cleanup(itv); - IVTV_DEBUG_INFO("Freeing dma resources\n"); ivtv_udma_free(itv); exit_ivtv_i2c(itv); - IVTV_DEBUG_INFO(" Releasing irq\n"); free_irq(itv->dev->irq, (void *)itv); ivtv_iounmap(itv); - IVTV_DEBUG_INFO(" Releasing mem\n"); release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); if (itv->has_cx23415) @@ -1341,6 +1374,7 @@ static void module_cleanup(void) pci_unregister_driver(&ivtv_pci_driver); + spin_lock(&ivtv_cards_lock); for (i = 0; i < ivtv_cards_active; i++) { if (ivtv_cards[i] == NULL) continue; @@ -1349,6 +1383,7 @@ static void module_cleanup(void) } kfree(ivtv_cards[i]); } + spin_unlock(&ivtv_cards_lock); } /* Note: These symbols are exported because they are used by the ivtv-fb @@ -1356,6 +1391,7 @@ static void module_cleanup(void) EXPORT_SYMBOL(ivtv_set_irq_mask); EXPORT_SYMBOL(ivtv_cards_active); EXPORT_SYMBOL(ivtv_cards); +EXPORT_SYMBOL(ivtv_cards_lock); EXPORT_SYMBOL(ivtv_api); EXPORT_SYMBOL(ivtv_vapi); EXPORT_SYMBOL(ivtv_vapi_result); @@ -1366,6 +1402,7 @@ EXPORT_SYMBOL(ivtv_udma_setup); EXPORT_SYMBOL(ivtv_udma_unmap); EXPORT_SYMBOL(ivtv_udma_alloc); EXPORT_SYMBOL(ivtv_udma_prepare); +EXPORT_SYMBOL(ivtv_init_on_first_open); module_init(module_start); module_exit(module_cleanup); diff -puN drivers/media/video/ivtv/ivtv-driver.h~git-dvb drivers/media/video/ivtv/ivtv-driver.h --- a/drivers/media/video/ivtv/ivtv-driver.h~git-dvb +++ a/drivers/media/video/ivtv/ivtv-driver.h @@ -89,11 +89,9 @@ extern const u32 yuv_offset[4]; -/* Maximum ivtv driver instances. - Based on 6 PVR500s each with two PVR15s... - TODO: make this dynamic. I believe it is only a global in order to support - ivtv-fb. There must be a better way to do that. */ -#define IVTV_MAX_CARDS 12 +/* Maximum ivtv driver instances. Some people have a huge number of + capture cards, so set this to a high value. */ +#define IVTV_MAX_CARDS 32 /* Supported cards */ #define IVTV_CARD_PVR_250 0 /* WinTV PVR 250 */ @@ -303,28 +301,10 @@ extern const u32 yuv_offset[4]; #define IVTV_DEBUG_HI_DEC(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DEC, "dec", fmt , ## args) #define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) -#define IVTV_FB_DEBUG(x, type, fmt, args...) \ - do { \ - if ((x) & ivtv_debug) \ - printk(KERN_INFO "ivtv%d-fb " type ": " fmt, itv->num , ## args); \ - } while (0) -#define IVTV_FB_DEBUG_WARN(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args) -#define IVTV_FB_DEBUG_INFO(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args) -#define IVTV_FB_DEBUG_API(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args) -#define IVTV_FB_DEBUG_DMA(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args) -#define IVTV_FB_DEBUG_IOCTL(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) -#define IVTV_FB_DEBUG_I2C(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) -#define IVTV_FB_DEBUG_IRQ(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) -#define IVTV_FB_DEBUG_DEC(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args) -#define IVTV_FB_DEBUG_YUV(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) - /* Standard kernel messages */ #define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args) #define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args) #define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args) -#define IVTV_FB_ERR(fmt, args...) printk(KERN_ERR "ivtv%d-fb: " fmt, itv->num , ## args) -#define IVTV_FB_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d-fb: " fmt, itv->num , ## args) -#define IVTV_FB_INFO(fmt, args...) printk(KERN_INFO "ivtv%d-fb: " fmt, itv->num , ## args) /* Values for IVTV_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */ #define MPEG_FRAME_TYPE_IFRAME 1 @@ -417,7 +397,13 @@ struct ivtv_mailbox_data { #define IVTV_F_I_WORK_HANDLER_YUV 17 /* there is work to be done for YUV */ #define IVTV_F_I_WORK_HANDLER_PIO 18 /* there is work to be done for PIO */ #define IVTV_F_I_PIO 19 /* PIO in progress */ +<<<<<<< HEAD/drivers/media/video/ivtv/ivtv-driver.h +#define IVTV_F_I_DEC_PAUSED 20 /* the decoder is paused */ +======= #define IVTV_F_I_DEC_PAUSED 20 /* the decoder is paused */ +#define IVTV_F_I_INITED 21 /* set after first open */ +#define IVTV_F_I_FAILED 22 /* set if first open failed */ +>>>>>>> /drivers/media/video/ivtv/ivtv-driver.h /* Event notifications */ #define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */ @@ -858,6 +844,9 @@ int ivtv_waitq(wait_queue_head_t *waitq) struct tveeprom; /* forward reference */ void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv); +/* First-open initialization: load firmware, init cx25840, etc. */ +int ivtv_init_on_first_open(struct ivtv *itv); + /* This is a PCI post thing, where if the pci register is not read, then the write doesn't always take effect right away. By reading back the register any pending PCI writes will be performed (in order), and so diff -puN /dev/null drivers/media/video/ivtv/ivtv-fb.c --- /dev/null +++ a/drivers/media/video/ivtv/ivtv-fb.c @@ -0,0 +1,1199 @@ +/* + On Screen Display cx23415 Framebuffer driver + + This module presents the cx23415 OSD (onscreen display) framebuffer memory + as a standard Linux /dev/fb style framebuffer device. The framebuffer has + support for 8, 16 & 32 bpp packed pixel formats with alpha channel. In 16bpp + mode, there is a choice of a three color depths (12, 15 or 16 bits), but no + local alpha. The colorspace is selectable between rgb & yuv. + Depending on the TV standard configured in the ivtv module at load time, + the initial resolution is either 640x400 (NTSC) or 640x480 (PAL) at 8bpp. + Video timings are locked to ensure a vertical refresh rate of 50Hz (PAL) + or 59.94 (NTSC) + + Copyright (c) 2003 Matt T. Yourst + + Derived from drivers/video/vesafb.c + Portions (c) 1998 Gerd Knorr + + 2.6 kernel port: + Copyright (C) 2004 Matthias Badaire + + Copyright (C) 2004 Chris Kennedy + + Copyright (C) 2006 Ian Armstrong + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_MTRR +#include +#endif + +#include "ivtv-driver.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-fileops.h" +#include "ivtv-mailbox.h" +#include "ivtv-cards.h" +#include + +/* card parameters */ +static int ivtv_fb_card_id = -1; +static int ivtv_fb_debug = 0; +static int osd_laced; +static int osd_compat; +static int osd_depth; +static int osd_upper; +static int osd_left; +static int osd_yres; +static int osd_xres; + +module_param(ivtv_fb_card_id, int, 0444); +module_param_named(debug,ivtv_fb_debug, int, 0644); +module_param(osd_laced, bool, 0444); +module_param(osd_compat, bool, 0444); +module_param(osd_depth, int, 0444); +module_param(osd_upper, int, 0444); +module_param(osd_left, int, 0444); +module_param(osd_yres, int, 0444); +module_param(osd_xres, int, 0444); + +MODULE_PARM_DESC(ivtv_fb_card_id, + "Only use framebuffer of the specified ivtv card (0-31)\n" + "\t\t\tdefault -1: initialize all available framebuffers"); + +MODULE_PARM_DESC(debug, + "Debug level (bitmask). Default: errors only\n" + "\t\t\t(debug = 3 gives full debugging)"); + +MODULE_PARM_DESC(osd_compat, + "Compatibility mode - Display size is locked (use for old X drivers)\n" + "\t\t\t0=off\n" + "\t\t\t1=on\n" + "\t\t\tdefault off"); + +/* Why upper, left, xres, yres, depth, laced ? To match terminology used + by fbset. + Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */ + +MODULE_PARM_DESC(osd_laced, + "Interlaced mode\n" + "\t\t\t0=off\n" + "\t\t\t1=on\n" + "\t\t\tdefault off"); + +MODULE_PARM_DESC(osd_depth, + "Bits per pixel - 8, 16, 32\n" + "\t\t\tdefault 8"); + +MODULE_PARM_DESC(osd_upper, + "Vertical start position\n" + "\t\t\tdefault 0 (Centered)"); + +MODULE_PARM_DESC(osd_left, + "Horizontal start position\n" + "\t\t\tdefault 0 (Centered)"); + +MODULE_PARM_DESC(osd_yres, + "Display height\n" + "\t\t\tdefault 480 (PAL)\n" + "\t\t\t 400 (NTSC)"); + +MODULE_PARM_DESC(osd_xres, + "Display width\n" + "\t\t\tdefault 640"); + +MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong"); +MODULE_LICENSE("GPL"); + +/* --------------------------------------------------------------------- */ + +#define IVTV_FB_DBGFLG_WARN (1 << 0) +#define IVTV_FB_DBGFLG_INFO (1 << 1) + +#define IVTV_FB_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & ivtv_fb_debug) \ + printk(KERN_INFO "ivtv-fb%d " type ": " fmt, itv->num , ## args); \ + } while (0) +#define IVTV_FB_DEBUG_WARN(fmt, args...) IVTV_FB_DEBUG(IVTV_FB_DBGFLG_WARN, "warning", fmt , ## args) +#define IVTV_FB_DEBUG_INFO(fmt, args...) IVTV_FB_DEBUG(IVTV_FB_DBGFLG_INFO, "info", fmt , ## args) + +/* Standard kernel messages */ +#define IVTV_FB_ERR(fmt, args...) printk(KERN_ERR "ivtv-fb%d: " fmt, itv->num , ## args) +#define IVTV_FB_WARN(fmt, args...) printk(KERN_WARNING "ivtv-fb%d: " fmt, itv->num , ## args) +#define IVTV_FB_INFO(fmt, args...) printk(KERN_INFO "ivtv-fb%d: " fmt, itv->num , ## args) + +/* --------------------------------------------------------------------- */ + +#define IVTV_OSD_MAX_WIDTH 720 +#define IVTV_OSD_MAX_HEIGHT 576 + +#define IVTV_OSD_BPP_8 0x00 +#define IVTV_OSD_BPP_16_444 0x03 +#define IVTV_OSD_BPP_16_555 0x02 +#define IVTV_OSD_BPP_16_565 0x01 +#define IVTV_OSD_BPP_32 0x04 + +struct osd_info { + /* Timing info for modes */ + u32 pixclock; + u32 hlimit; + u32 vlimit; + + /* Physical base address */ + unsigned long video_pbase; + /* Relative base address (relative to start of decoder memory) */ + u32 video_rbase; + /* Mapped base address */ + volatile char __iomem *video_vbase; + /* Buffer size */ + u32 video_buffer_size; + +#ifdef CONFIG_MTRR + /* video_base rounded down as required by hardware MTRRs */ + unsigned long fb_start_aligned_physaddr; + /* video_base rounded up as required by hardware MTRRs */ + unsigned long fb_end_aligned_physaddr; +#endif + + /* Current osd mode */ + int osd_mode; + + /* Store the buffer offset */ + int set_osd_coords_x; + int set_osd_coords_y; + + /* Current dimensions (NOT VISIBLE SIZE!) */ + int display_width; + int display_height; + int display_byte_stride; + + /* Current bits per pixel */ + int bits_per_pixel; + int bytes_per_pixel; + + /* Frame buffer stuff */ + struct fb_info ivtvfb_info; + struct fb_var_screeninfo ivtvfb_defined; + struct fb_fix_screeninfo ivtvfb_fix; +}; + +struct ivtv_osd_coords { + unsigned long offset; + unsigned long max_offset; + int pixel_stride; + int lines; + int x; + int y; +}; + +/* --------------------------------------------------------------------- */ + +/* ivtv API calls for framebuffer related support */ + +static int ivtv_fb_get_framebuffer(struct ivtv *itv, u32 *fbbase, + u32 *fblength) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + int rc; + + rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0); + *fbbase = data[0]; + *fblength = data[1]; + return rc; +} + +static int ivtv_fb_get_osd_coords(struct ivtv *itv, + struct ivtv_osd_coords *osd) +{ + struct osd_info *oi = itv->osd_info; + u32 data[CX2341X_MBOX_MAX_DATA]; + + ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0); + + osd->offset = data[0] - oi->video_rbase; + osd->max_offset = oi->display_width * oi->display_height * 4; + osd->pixel_stride = data[1]; + osd->lines = data[2]; + osd->x = data[3]; + osd->y = data[4]; + return 0; +} + +static int ivtv_fb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd) +{ + struct osd_info *oi = itv->osd_info; + + oi->display_width = osd->pixel_stride; + oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel; + oi->set_osd_coords_x += osd->x; + oi->set_osd_coords_y = osd->y; + + return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5, + osd->offset + oi->video_rbase, + osd->pixel_stride, + osd->lines, osd->x, osd->y); +} + +static int ivtv_fb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window) +{ + int osd_height_limit = itv->is_50hz ? 576 : 480; + + /* Only fail if resolution too high, otherwise fudge the start coords. */ + if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH)) + return -EINVAL; + + /* Ensure we don't exceed display limits */ + if (ivtv_window->top + ivtv_window->height > osd_height_limit) { + IVTV_FB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n", + ivtv_window->top, ivtv_window->height); + ivtv_window->top = osd_height_limit - ivtv_window->height; + } + + if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) { + IVTV_FB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n", + ivtv_window->left, ivtv_window->width); + ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width; + } + + /* Set the OSD origin */ + write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04); + + /* How much to display */ + write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08); + + /* Pass this info back the yuv handler */ + itv->yuv_info.osd_vis_w = ivtv_window->width; + itv->yuv_info.osd_vis_h = ivtv_window->height; + itv->yuv_info.osd_x_offset = ivtv_window->left; + itv->yuv_info.osd_y_offset = ivtv_window->top; + + return 0; +} + +static int ivtv_fb_prep_dec_dma_to_device(struct ivtv *itv, + unsigned long ivtv_dest_addr, void __user *userbuf, + int size_in_bytes) +{ + DEFINE_WAIT(wait); + int ret = 0; + int got_sig = 0; + + mutex_lock(&itv->udma.lock); + /* Map User DMA */ + if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) { + mutex_unlock(&itv->udma.lock); + IVTV_FB_WARN("ivtvfb_prep_dec_dma_to_device, " + "Error with get_user_pages: %d bytes, %d pages returned\n", + size_in_bytes, itv->udma.page_count); + + /* get_user_pages must have failed completely */ + return -EIO; + } + + IVTV_FB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n", + size_in_bytes, itv->udma.page_count); + + ivtv_udma_prepare(itv); + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + /* if no UDMA is pending and no UDMA is in progress, then the DMA + is finished */ + while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) { + /* don't interrupt if the DMA is in progress but break off + a still pending DMA. */ + got_sig = signal_pending(current); + if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) + break; + got_sig = 0; + schedule(); + } + finish_wait(&itv->dma_waitq, &wait); + + /* Unmap Last DMA Xfer */ + ivtv_udma_unmap(itv); + mutex_unlock(&itv->udma.lock); + if (got_sig) { + IVTV_DEBUG_INFO("User stopped OSD\n"); + return -EINTR; + } + + return ret; +} + +static int ivtv_fb_prep_frame(struct ivtv *itv, int cmd, void __user *source, + unsigned long dest_offset, int count) +{ + DEFINE_WAIT(wait); + struct osd_info *oi = itv->osd_info; + + /* Nothing to do */ + if (count == 0) { + IVTV_FB_DEBUG_WARN("ivtv_fb_prep_frame: Nothing to do. count = 0\n"); + return -EINVAL; + } + + /* Check Total FB Size */ + if ((dest_offset + count) > oi->video_buffer_size) { + IVTV_FB_WARN("ivtv_fb_prep_frame: Overflowing the framebuffer %ld, only %d available\n", + dest_offset + count, oi->video_buffer_size); + return -E2BIG; + } + + /* Not fatal, but will have undesirable results */ + if ((unsigned long)source & 3) + IVTV_FB_WARN("ivtv_fb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n", + (unsigned long)source); + + if (dest_offset & 3) + IVTV_FB_WARN("ivtv_fb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset); + + if (count & 3) + IVTV_FB_WARN("ivtv_fb_prep_frame: Count not a multiple of 4 (%d)\n", count); + + /* Check Source */ + if (!access_ok(VERIFY_READ, source + dest_offset, count)) { + IVTV_FB_WARN("Invalid userspace pointer 0x%08lx\n", + (unsigned long)source); + + IVTV_FB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n", + dest_offset, (unsigned long)source, + count); + return -EINVAL; + } + + /* OSD Address to send DMA to */ + dest_offset += IVTV_DEC_MEM_START + oi->video_rbase; + + /* Fill Buffers */ + return ivtv_fb_prep_dec_dma_to_device(itv, dest_offset, source, count); +} + +static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) +{ + DEFINE_WAIT(wait); + struct ivtv *itv = (struct ivtv *)info->par; + int rc = 0; + + switch (cmd) { + case FBIOGET_VBLANK: { + struct fb_vblank vblank; + u32 trace; + + vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT | + FB_VBLANK_HAVE_VSYNC; + trace = read_reg(0x028c0) >> 16; + if (itv->is_50hz && trace > 312) trace -= 312; + else if (itv->is_60hz && trace > 262) trace -= 262; + if (trace == 1) vblank.flags |= FB_VBLANK_VSYNCING; + vblank.count = itv->lastVsyncFrame; + vblank.vcount = trace; + vblank.hcount = 0; + if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) + return -EFAULT; + return 0; + } + + case FBIO_WAITFORVSYNC: + prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE); + if (!schedule_timeout(msecs_to_jiffies(50))) rc = -ETIMEDOUT; + finish_wait(&itv->vsync_waitq, &wait); + return rc; + + case IVTVFB_IOC_DMA_FRAME: { + struct ivtvfb_dma_frame args; + + IVTV_FB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n"); + if (copy_from_user(&args, (void __user *)arg, sizeof(args))) + return -EFAULT; + + return ivtv_fb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count); + } + + default: + IVTV_FB_DEBUG_INFO("Unknown ioctl %08x\n", cmd); + return -EINVAL; + } + return 0; +} + +/* Framebuffer device handling */ + +static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var) +{ + struct osd_info *oi = itv->osd_info; + struct ivtv_osd_coords ivtv_osd; + struct v4l2_rect ivtv_window; + int osd_mode = -1; + + IVTV_FB_DEBUG_INFO("ivtvfb_set_var\n"); + + /* Select color space */ + if (var->nonstd) /* YUV */ + write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00); + else /* RGB */ + write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00); + + /* Set the color mode */ + switch (var->bits_per_pixel) { + case 8: + osd_mode = IVTV_OSD_BPP_8; + break; + case 32: + osd_mode = IVTV_OSD_BPP_32; + break; + case 16: + switch (var->green.length) { + case 4: + osd_mode = IVTV_OSD_BPP_16_444; + break; + case 5: + osd_mode = IVTV_OSD_BPP_16_555; + break; + case 6: + osd_mode = IVTV_OSD_BPP_16_565; + break; + default: + IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); + } + break; + default: + IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); + } + + /* Change osd mode if needed. + Although rare, things can go wrong. The extra mode + change seems to help... */ + if (osd_mode != -1 && osd_mode != oi->osd_mode) { + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0); + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode); + oi->osd_mode = osd_mode; + } + + oi->bits_per_pixel = var->bits_per_pixel; + oi->bytes_per_pixel = var->bits_per_pixel / 8; + + /* Set the flicker filter */ + switch (var->vmode & FB_VMODE_MASK) { + case FB_VMODE_NONINTERLACED: /* Filter on */ + ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1); + break; + case FB_VMODE_INTERLACED: /* Filter off */ + ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0); + break; + default: + IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n"); + } + + /* Read the current osd info */ + ivtv_fb_get_osd_coords(itv, &ivtv_osd); + + /* Now set the OSD to the size we want */ + ivtv_osd.pixel_stride = var->xres_virtual; + ivtv_osd.lines = var->yres_virtual; + ivtv_osd.x = 0; + ivtv_osd.y = 0; + ivtv_fb_set_osd_coords(itv, &ivtv_osd); + + /* Can't seem to find the right API combo for this. + Use another function which does what we need through direct register access. */ + ivtv_window.width = var->xres; + ivtv_window.height = var->yres; + + /* Minimum margin cannot be 0, as X won't allow such a mode */ + if (!var->upper_margin) var->upper_margin++; + if (!var->left_margin) var->left_margin++; + ivtv_window.top = var->upper_margin - 1; + ivtv_window.left = var->left_margin - 1; + + ivtv_fb_set_display_window(itv, &ivtv_window); + + /* Force update of yuv registers */ + itv->yuv_info.yuv_forced_update = 1; + + IVTV_FB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", + var->xres, var->yres, + var->xres_virtual, var->yres_virtual, + var->bits_per_pixel); + + IVTV_FB_DEBUG_INFO("Display position: %d, %d\n", + var->left_margin, var->upper_margin); + + IVTV_FB_DEBUG_INFO("Display filter: %s\n", + (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); + IVTV_FB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); + + return 0; +} + +static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix) +{ + struct osd_info *oi = itv->osd_info; + + IVTV_FB_DEBUG_INFO("ivtvfb_get_fix\n"); + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + strcpy(fix->id, "cx23415 TV out"); + fix->smem_start = oi->video_pbase; + fix->smem_len = oi->video_buffer_size; + fix->type = FB_TYPE_PACKED_PIXELS; + fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + fix->xpanstep = 1; + fix->ypanstep = 1; + fix->ywrapstep = 0; + fix->line_length = oi->display_byte_stride; + fix->accel = FB_ACCEL_NONE; + return 0; +} + +/* Check the requested display mode, returning -EINVAL if we can't + handle it. */ + +static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv) +{ + struct osd_info *oi = itv->osd_info; + int osd_height_limit = itv->is_50hz ? 576 : 480; + + IVTV_FB_DEBUG_INFO("ivtvfb_check_var\n"); + + /* Check the bits per pixel */ + if (osd_compat) { + if (var->bits_per_pixel != 32) { + IVTV_FB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel); + return -EINVAL; + } + } + + if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) { + var->transp.offset = 24; + var->transp.length = 8; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + } + else if (var->bits_per_pixel == 16) { + var->transp.offset = 0; + var->transp.length = 0; + + /* To find out the true mode, check green length */ + switch (var->green.length) { + case 4: + var->red.offset = 8; + var->red.length = 4; + var->green.offset = 4; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + break; + case 5: + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + break; + default: + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + break; + } + } + else { + IVTV_FB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel); + return -EINVAL; + } + + /* Check the resolution */ + if (osd_compat) { + if (var->xres != oi->ivtvfb_defined.xres || + var->yres != oi->ivtvfb_defined.yres || + var->xres_virtual != oi->ivtvfb_defined.xres_virtual || + var->yres_virtual != oi->ivtvfb_defined.yres_virtual) { + IVTV_FB_DEBUG_WARN("Invalid resolution: %dx%d (virtual %dx%d)\n", + var->xres, var->yres, var->xres_virtual, var->yres_virtual); + return -EINVAL; + } + } + else { + if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) { + IVTV_FB_DEBUG_WARN("Invalid resolution: %dx%d\n", + var->xres, var->yres); + return -EINVAL; + } + + /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */ + if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) || + var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size || + var->xres_virtual < var->xres || + var->yres_virtual < var->yres) { + IVTV_FB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n", + var->xres_virtual, var->yres_virtual); + return -EINVAL; + } + } + + /* Some extra checks if in 8 bit mode */ + if (var->bits_per_pixel == 8) { + /* Width must be a multiple of 4 */ + if (var->xres & 3) { + IVTV_FB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres); + return -EINVAL; + } + if (var->xres_virtual & 3) { + IVTV_FB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual); + return -EINVAL; + } + } + else if (var->bits_per_pixel == 16) { + /* Width must be a multiple of 2 */ + if (var->xres & 1) { + IVTV_FB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres); + return -EINVAL; + } + if (var->xres_virtual & 1) { + IVTV_FB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual); + return -EINVAL; + } + } + + /* Now check the offsets */ + if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) { + IVTV_FB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n", + var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual); + return -EINVAL; + } + + /* Check pixel format */ + if (var->nonstd > 1) { + IVTV_FB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd); + return -EINVAL; + } + + /* Check video mode */ + if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) && + ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) { + IVTV_FB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK); + return -EINVAL; + } + + /* Check the left & upper margins + If the margins are too large, just center the screen + (enforcing margins causes too many problems) */ + + if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) { + var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2); + } + if (var->upper_margin + var->yres > (itv->is_50hz ? 577 : 481)) { + var->upper_margin = 1 + (((itv->is_50hz ? 576 : 480) - var->yres) / 2); + } + + /* Maintain overall 'size' for a constant refresh rate */ + var->right_margin = oi->hlimit - var->left_margin - var->xres; + var->lower_margin = oi->vlimit - var->upper_margin - var->yres; + + /* Fixed sync times */ + var->hsync_len = 24; + var->vsync_len = 2; + + /* Non-interlaced / interlaced mode is used to switch the OSD filter + on or off. Adjust the clock timings to maintain a constant + vertical refresh rate. */ + var->pixclock = oi->pixclock; + if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) + var->pixclock /= 2; + + IVTV_FB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", + var->xres, var->yres, + var->xres_virtual, var->yres_virtual, + var->bits_per_pixel); + + IVTV_FB_DEBUG_INFO("Display position: %d, %d\n", + var->left_margin, var->upper_margin); + + IVTV_FB_DEBUG_INFO("Display filter: %s\n", + (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); + IVTV_FB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); + return 0; +} + +static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct ivtv *itv = (struct ivtv *) info->par; + IVTV_FB_DEBUG_INFO("ivtvfb_check_var\n"); + return _ivtvfb_check_var(var, itv); +} + +static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + u32 osd_pan_index; + struct ivtv *itv = (struct ivtv *) info->par; + + osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8; + write_reg(osd_pan_index, 0x02A0C); + + /* Pass this info back the yuv handler */ + itv->yuv_info.osd_x_pan = var->xoffset; + itv->yuv_info.osd_y_pan = var->yoffset; + /* Force update of yuv registers */ + itv->yuv_info.yuv_forced_update = 1; + return 0; +} + +static int ivtvfb_set_par(struct fb_info *info) +{ + int rc = 0; + struct ivtv *itv = (struct ivtv *) info->par; + + IVTV_FB_DEBUG_INFO("ivtvfb_set_par\n"); + + rc = ivtvfb_set_var(itv, &info->var); + ivtvfb_pan_display(&info->var, info); + ivtvfb_get_fix(itv, &info->fix); + return rc; +} + +static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + u32 color, *palette; + struct ivtv *itv = (struct ivtv *)info->par; + + if (regno >= info->cmap.len) + return -EINVAL; + + color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8); + if (info->var.bits_per_pixel <= 8) { + write_reg(regno, 0x02a30); + write_reg(color, 0x02a34); + return 0; + } + if (regno >= 16) + return -EINVAL; + + palette = info->pseudo_palette; + if (info->var.bits_per_pixel == 16) { + switch (info->var.green.length) { + case 4: + color = ((red & 0xf000) >> 4) | + ((green & 0xf000) >> 8) | + ((blue & 0xf000) >> 12); + break; + case 5: + color = ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11); + break; + case 6: + color = (red & 0xf800 ) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + break; + } + } + palette[regno] = color; + return 0; +} + +/* We don't really support blanking. All this does is enable or + disable the OSD. */ +static int ivtvfb_blank(int blank_mode, struct fb_info *info) +{ + struct ivtv *itv = (struct ivtv *)info->par; + + IVTV_FB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode); + switch (blank_mode) { + case FB_BLANK_UNBLANK: + ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1); + break; + case FB_BLANK_NORMAL: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); + break; + } + return 0; +} + +static struct fb_ops ivtvfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = ivtvfb_check_var, + .fb_set_par = ivtvfb_set_par, + .fb_setcolreg = ivtvfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = NULL, + .fb_ioctl = ivtvfb_ioctl, + .fb_pan_display = ivtvfb_pan_display, + .fb_blank = ivtvfb_blank, +}; + +/* Initialization */ + + +/* Setup our initial video mode */ +static int ivtvfb_init_vidmode(struct ivtv *itv) +{ + struct osd_info *oi = itv->osd_info; + struct v4l2_rect start_window; + int max_height; + + /* Set base references for mode calcs. */ + if (itv->is_50hz) { + oi->pixclock = 84316; + oi->hlimit = 776; + oi->vlimit = 591; + } + else { + oi->pixclock = 83926; + oi->hlimit = 776; + oi->vlimit = 495; + } + + /* Color mode */ + + if (osd_compat) osd_depth = 32; + if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) osd_depth = 8; + oi->bits_per_pixel = osd_depth; + oi->bytes_per_pixel = oi->bits_per_pixel / 8; + + /* Invalidate current osd mode to force a mode switch later */ + oi->osd_mode = -1; + + /* Horizontal size & position */ + + if (osd_xres > 720) osd_xres = 720; + + /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */ + if (osd_depth == 8) + osd_xres &= ~3; + else if (osd_depth == 16) + osd_xres &= ~1; + + if (osd_xres) + start_window.width = osd_xres; + else + start_window.width = osd_compat ? 720: 640; + + /* Check horizontal start (osd_left). */ + if (osd_left && osd_left + start_window.width > 721) { + IVTV_FB_ERR("Invalid osd_left - assuming default\n"); + osd_left = 0; + } + + /* Hardware coords start at 0, user coords start at 1. */ + osd_left--; + + start_window.left = osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2); + + oi->display_byte_stride = + start_window.width * oi->bytes_per_pixel; + + /* Vertical size & position */ + + max_height = itv->is_50hz ? 576 : 480; + + if (osd_yres > max_height) + osd_yres = max_height; + + if (osd_yres) + start_window.height = osd_yres; + else + start_window.height = osd_compat ? max_height : (itv->is_50hz ? 480 : 400); + + /* Check vertical start (osd_upper). */ + if (osd_upper + start_window.height > max_height + 1) { + IVTV_FB_ERR("Invalid osd_upper - assuming default\n"); + osd_upper = 0; + } + + /* Hardware coords start at 0, user coords start at 1. */ + osd_upper--; + + start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2); + + oi->display_width = start_window.width; + oi->display_height = start_window.height; + + /* Generate a valid fb_var_screeninfo */ + + oi->ivtvfb_defined.xres = oi->display_width; + oi->ivtvfb_defined.yres = oi->display_height; + oi->ivtvfb_defined.xres_virtual = oi->display_width; + oi->ivtvfb_defined.yres_virtual = oi->display_height; + oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel; + oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED); + oi->ivtvfb_defined.left_margin = start_window.left + 1; + oi->ivtvfb_defined.upper_margin = start_window.top + 1; + oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE; + oi->ivtvfb_defined.nonstd = 0; + + /* We've filled in the most data, let the usual mode check + routine fill in the rest. */ + _ivtvfb_check_var(&oi->ivtvfb_defined, itv); + + /* Generate valid fb_fix_screeninfo */ + + ivtvfb_get_fix(itv, &oi->ivtvfb_fix); + + /* Generate valid fb_info */ + + oi->ivtvfb_info.node = -1; + oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT; + oi->ivtvfb_info.fbops = &ivtvfb_ops; + oi->ivtvfb_info.par = itv; + oi->ivtvfb_info.var = oi->ivtvfb_defined; + oi->ivtvfb_info.fix = oi->ivtvfb_fix; + oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase; + oi->ivtvfb_info.fbops = &ivtvfb_ops; + + /* Supply some monitor specs. Bogus values will do for now */ + oi->ivtvfb_info.monspecs.hfmin = 8000; + oi->ivtvfb_info.monspecs.hfmax = 70000; + oi->ivtvfb_info.monspecs.vfmin = 10; + oi->ivtvfb_info.monspecs.vfmax = 100; + + /* Allocate color map */ + if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) { + IVTV_FB_ERR("abort, unable to alloc cmap\n"); + return -ENOMEM; + } + + /* Allocate the pseudo palette */ + oi->ivtvfb_info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); + + if (!oi->ivtvfb_info.pseudo_palette) { + IVTV_FB_ERR("abort, unable to alloc pseudo pallete\n"); + return -ENOMEM; + } + + return 0; +} + +/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */ + +static int ivtvfb_init_io(struct ivtv *itv) +{ + struct osd_info *oi = itv->osd_info; + + if (ivtv_init_on_first_open(itv)) { + IVTV_FB_ERR("Failed to initialize ivtv\n"); + return -ENXIO; + } + + ivtv_fb_get_framebuffer(itv, &oi->video_rbase, &oi->video_buffer_size); + + /* The osd buffer size depends on the number of video buffers allocated + on the PVR350 itself. For now we'll hardcode the smallest osd buffer + size to prevent any overlap. */ + oi->video_buffer_size = 1704960; + + oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase; + oi->video_vbase = itv->dec_mem + oi->video_rbase; + + if (!oi->video_vbase) { + IVTV_FB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n", + oi->video_buffer_size, oi->video_pbase); + return -EIO; + } + + IVTV_FB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n", + oi->video_pbase, oi->video_vbase, + oi->video_buffer_size / 1024); + +#ifdef CONFIG_MTRR + { + /* Find the largest power of two that maps the whole buffer */ + int size_shift = 31; + + while (!(oi->video_buffer_size & (1 << size_shift))) { + size_shift--; + } + size_shift++; + oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1); + oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size; + oi->fb_end_aligned_physaddr += (1 << size_shift) - 1; + oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1); + if (mtrr_add(oi->fb_start_aligned_physaddr, + oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr, + MTRR_TYPE_WRCOMB, 1) < 0) { + IVTV_FB_WARN("cannot use mttr\n"); + oi->fb_start_aligned_physaddr = 0; + oi->fb_end_aligned_physaddr = 0; + } + } +#endif + + /* Blank the entire osd. */ + memset_io(oi->video_vbase, 0, oi->video_buffer_size); + + return 0; +} + +/* Release any memory we've grabbed & remove mtrr entry */ +static void ivtvfb_release_buffers (struct ivtv *itv) +{ + struct osd_info *oi = itv->osd_info; + + /* Release cmap */ + if (oi->ivtvfb_info.cmap.len); + fb_dealloc_cmap(&oi->ivtvfb_info.cmap); + + /* Release pseudo palette */ + if (oi->ivtvfb_info.pseudo_palette) + kfree(oi->ivtvfb_info.pseudo_palette); + +#ifdef CONFIG_MTRR + if (oi->fb_end_aligned_physaddr) { + mtrr_del(-1, oi->fb_start_aligned_physaddr, + oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr); + } +#endif + + kfree(oi); + itv->osd_info = NULL; +} + +/* Initialize the specified card */ + +static int ivtvfb_init_card(struct ivtv *itv) +{ + int rc; + + if (itv->osd_info) { + IVTV_FB_ERR("Card %d already initialised\n", ivtv_fb_card_id); + return -EBUSY; + } + + itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC); + if (itv->osd_info == 0) { + IVTV_FB_ERR("Failed to allocate memory for osd_info\n"); + return -ENOMEM; + } + + /* Find & setup the OSD buffer */ + if ((rc = ivtvfb_init_io(itv))) + return rc; + + /* Set the startup video mode information */ + if ((rc = ivtvfb_init_vidmode(itv))) { + ivtvfb_release_buffers(itv); + return rc; + } + + /* Register the framebuffer */ + if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) { + ivtvfb_release_buffers(itv); + return -EINVAL; + } + + itv->osd_video_pbase = itv->osd_info->video_pbase; + + /* Set the card to the requested mode */ + ivtvfb_set_par(&itv->osd_info->ivtvfb_info); + + /* Set color 0 to black */ + write_reg(0, 0x02a30); + write_reg(0, 0x02a34); + + /* Enable the osd */ + ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info); + + /* Note if we're running in compatibility mode */ + if (osd_compat) + IVTV_FB_INFO("Running in compatibility mode. Display resize & mode change disabled\n"); + + /* Allocate DMA */ + ivtv_udma_alloc(itv); + return 0; + +} + +static int __init ivtvfb_init(void) +{ + struct ivtv *itv; + int i, registered = 0; + + if (ivtv_fb_card_id < -1 || ivtv_fb_card_id >= IVTV_MAX_CARDS) { + printk(KERN_ERR "ivtv-fb: ivtv_fb_card_id parameter is out of range (valid range: -1 - %d)\n", + IVTV_MAX_CARDS - 1); + return -EINVAL; + } + + /* Locate & initialise all cards supporting an OSD. */ + for (i = 0; i < ivtv_cards_active; i++) { + if (ivtv_fb_card_id != -1 && i != ivtv_fb_card_id) + continue; + itv = ivtv_cards[i]; + if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (ivtvfb_init_card(itv) == 0) { + IVTV_FB_INFO("Framebuffer registered on ivtv card id %d\n", i); + registered++; + } + } + } + if (!registered) { + printk(KERN_ERR "ivtv-fb: no cards found"); + return -ENODEV; + } + return 0; +} + +static void ivtvfb_cleanup(void) +{ + struct ivtv *itv; + int i; + + printk(KERN_INFO "ivtv-fb: Unloading framebuffer module\n"); + + for (i = 0; i < ivtv_cards_active; i++) { + itv = ivtv_cards[i]; + if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) { + IVTV_FB_DEBUG_INFO("Unregister framebuffer %d\n", i); + ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info); + unregister_framebuffer(&itv->osd_info->ivtvfb_info); + ivtvfb_release_buffers(itv); + itv->osd_video_pbase = 0; + } + } +} + +module_init(ivtvfb_init); +module_exit(ivtvfb_cleanup); diff -puN drivers/media/video/ivtv/ivtv-fileops.c~git-dvb drivers/media/video/ivtv/ivtv-fileops.c --- a/drivers/media/video/ivtv/ivtv-fileops.c~git-dvb +++ a/drivers/media/video/ivtv/ivtv-fileops.c @@ -842,7 +842,12 @@ int ivtv_v4l2_open(struct inode *inode, if (itv == NULL) { /* Couldn't find a device registered on that minor, shouldn't happen! */ - printk(KERN_WARNING "ivtv: No ivtv device found on minor %d\n", minor); + IVTV_WARN("No ivtv device found on minor %d\n", minor); + return -ENXIO; + } + + if (ivtv_init_on_first_open(itv)) { + IVTV_ERR("Failed to initialize on minor %d\n", minor); return -ENXIO; } diff -puN drivers/media/video/ivtv/ivtv-i2c.c~git-dvb drivers/media/video/ivtv/ivtv-i2c.c --- a/drivers/media/video/ivtv/ivtv-i2c.c~git-dvb +++ a/drivers/media/video/ivtv/ivtv-i2c.c @@ -109,6 +109,7 @@ static const u8 hw_driverids[] = { I2C_DRIVERID_UPD64083, I2C_DRIVERID_SAA717X, I2C_DRIVERID_WM8739, + I2C_DRIVERID_VP27SMPX, 0 /* IVTV_HW_GPIO dummy driver ID */ }; @@ -128,6 +129,7 @@ static const char * const hw_drivernames "upd64083", "saa717x", "wm8739", + "vp27smpx", "gpio", }; diff -puN drivers/media/video/ivtv/ivtv-streams.c~git-dvb drivers/media/video/ivtv/ivtv-streams.c --- a/drivers/media/video/ivtv/ivtv-streams.c~git-dvb +++ a/drivers/media/video/ivtv/ivtv-streams.c @@ -314,15 +314,6 @@ static void ivtv_vbi_setup(struct ivtv * int lines; int i; - /* If Embed then streamtype must be Program */ - /* TODO: should we really do this? */ - if (0 && !raw && itv->vbi.insert_mpeg) { - itv->params.stream_type = 0; - - /* assign stream type */ - ivtv_vapi(itv, CX2341X_ENC_SET_STREAM_TYPE, 1, itv->params.stream_type); - } - /* Reset VBI */ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0); diff -puN drivers/media/video/ivtv/ivtv-version.h~git-dvb drivers/media/video/ivtv/ivtv-version.h --- a/drivers/media/video/ivtv/ivtv-version.h~git-dvb +++ a/drivers/media/video/ivtv/ivtv-version.h @@ -19,7 +19,7 @@ #define IVTV_DRIVER_NAME "ivtv" #define IVTV_DRIVER_VERSION_MAJOR 1 -#define IVTV_DRIVER_VERSION_MINOR 0 +#define IVTV_DRIVER_VERSION_MINOR 1 #define IVTV_DRIVER_VERSION_PATCHLEVEL 0 #define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) diff -puN /dev/null drivers/media/video/tcm825x.c --- /dev/null +++ a/drivers/media/video/tcm825x.c @@ -0,0 +1,942 @@ +/* + * drivers/media/video/tcm825x.c + * + * TCM825X camera sensor driver. + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * Based on code from David Cohen + * + * This driver was based on ov9640 sensor driver from MontaVista + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include + +#include "tcm825x.h" + +/* + * The sensor has two fps modes: the lower one just gives half the fps + * at the same xclk than the high one. + */ +#define MAX_FPS 30 +#define MIN_FPS 8 +#define MAX_HALF_FPS (MAX_FPS / 2) +#define HIGH_FPS_MODE_LOWER_LIMIT 14 +#define DEFAULT_FPS MAX_HALF_FPS + +struct tcm825x_sensor { + const struct tcm825x_platform_data *platform_data; + struct v4l2_int_device *v4l2_int_device; + struct i2c_client *i2c_client; + struct v4l2_pix_format pix; + struct v4l2_fract timeperframe; +}; + +/* list of image formats supported by TCM825X sensor */ +const static struct v4l2_fmtdesc tcm825x_formats[] = { + { + .description = "YUYV (YUV 4:2:2), packed", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, { + /* Note: V4L2 defines RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 + * + * We interpret RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 + */ + .description = "RGB565, le", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, +}; + +#define TCM825X_NUM_CAPTURE_FORMATS ARRAY_SIZE(tcm825x_formats) + +/* + * TCM825X register configuration for all combinations of pixel format and + * image size + */ +const static struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ }; +const static struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ }; +const static struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ }; +const static struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ }; +const static struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ }; +const static struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ }; + +const static struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT }; +const static struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT }; + +/* Our own specific controls */ +#define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE +#define V4L2_CID_H_EDGE_EN V4L2_CID_PRIVATE_BASE + 1 +#define V4L2_CID_V_EDGE_EN V4L2_CID_PRIVATE_BASE + 2 +#define V4L2_CID_LENS V4L2_CID_PRIVATE_BASE + 3 +#define V4L2_CID_MAX_EXPOSURE_TIME V4L2_CID_PRIVATE_BASE + 4 +#define V4L2_CID_LAST_PRIV V4L2_CID_MAX_EXPOSURE_TIME + +/* Video controls */ +static struct vcontrol { + struct v4l2_queryctrl qc; + u16 reg; + u16 start_bit; +} video_control[] = { + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 63, + .step = 1, + }, + .reg = TCM825X_AG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 255, + .step = 1, + }, + .reg = TCM825X_MRG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 255, + .step = 1, + }, + .reg = TCM825X_MBG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_AWBSW, + .start_bit = 7, + }, + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure Time", + .minimum = 0, + .maximum = 0x1fff, + .step = 1, + }, + .reg = TCM825X_ESRSPD_U, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror Image", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_H_INV, + .start_bit = 6, + }, + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_V_INV, + .start_bit = 7, + }, + /* Private controls */ + { + { + .id = V4L2_CID_ALC, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Luminance Control", + .minimum = 0, + .maximum = 1, + .step = 0, + }, + .reg = TCM825X_ALCSW, + .start_bit = 7, + }, + { + { + .id = V4L2_CID_H_EDGE_EN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Horizontal Edge Enhancement", + .minimum = 0, + .maximum = 0xff, + .step = 1, + }, + .reg = TCM825X_HDTG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_V_EDGE_EN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Vertical Edge Enhancement", + .minimum = 0, + .maximum = 0xff, + .step = 1, + }, + .reg = TCM825X_VDTG, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_LENS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Lens Shading Compensation", + .minimum = 0, + .maximum = 0x3f, + .step = 1, + }, + .reg = TCM825X_LENS, + .start_bit = 0, + }, + { + { + .id = V4L2_CID_MAX_EXPOSURE_TIME, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Maximum Exposure Time", + .minimum = 0, + .maximum = 0x3, + .step = 1, + }, + .reg = TCM825X_ESRLIM, + .start_bit = 5, + }, +}; + + +const static struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] = +{ &subqcif, &qqvga, &qcif, &qvga, &cif, &vga }; + +const static struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] = +{ &yuv422, &rgb565 }; + +/* + * Read a value from a register in an TCM825X sensor device. The value is + * returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int tcm825x_read_reg(struct i2c_client *client, int reg) +{ + int err; + struct i2c_msg msg[2]; + u8 reg_buf, data_buf = 0; + + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = ®_buf; + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &data_buf; + + reg_buf = reg; + + err = i2c_transfer(client->adapter, msg, 2); + if (err < 0) + return err; + return data_buf; +} + +/* + * Write a value to a register in an TCM825X sensor device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tcm825x_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[2]; + + if (!client->adapter) + return -ENODEV; + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 2; + msg->buf = data; + data[0] = reg; + data[1] = val; + err = i2c_transfer(client->adapter, msg, 1); + if (err >= 0) + return 0; + return err; +} + +static int __tcm825x_write_reg_mask(struct i2c_client *client, + u8 reg, u8 val, u8 mask) +{ + int rc; + + /* need to do read - modify - write */ + rc = tcm825x_read_reg(client, reg); + if (rc < 0) + return rc; + + rc &= (~mask); /* Clear the masked bits */ + val &= mask; /* Enforce mask on value */ + val |= rc; + + /* write the new value to the register */ + rc = tcm825x_write_reg(client, reg, val); + if (rc) + return rc; + + return 0; +} + +#define tcm825x_write_reg_mask(client, regmask, val) \ + __tcm825x_write_reg_mask(client, TCM825X_ADDR((regmask)), val, \ + TCM825X_MASK((regmask))) + + +/* + * Initialize a list of TCM825X registers. + * The list of registers is terminated by the pair of values + * { TCM825X_REG_TERM, TCM825X_VAL_TERM }. + * Returns zero if successful, or non-zero otherwise. + */ +static int tcm825x_write_default_regs(struct i2c_client *client, + const struct tcm825x_reg *reglist) +{ + int err; + const struct tcm825x_reg *next = reglist; + + while (!((next->reg == TCM825X_REG_TERM) + && (next->val == TCM825X_VAL_TERM))) { + err = tcm825x_write_reg(client, next->reg, next->val); + if (err) { + dev_err(&client->dev, "register writing failed\n"); + return err; + } + next++; + } + + return 0; +} + +static struct vcontrol *find_vctrl(int id) +{ + int i; + + if (id < V4L2_CID_BASE) + return NULL; + + for (i = 0; i < ARRAY_SIZE(video_control); i++) + if (video_control[i].qc.id == id) + return &video_control[i]; + + return NULL; +} + +/* + * Find the best match for a requested image capture size. The best match + * is chosen as the nearest match that has the same number or fewer pixels + * as the requested size, or the smallest image size if the requested size + * has fewer pixels than the smallest image. + */ +static enum image_size tcm825x_find_size(struct v4l2_int_device *s, + unsigned int width, + unsigned int height) +{ + enum image_size isize; + unsigned long pixels = width * height; + struct tcm825x_sensor *sensor = s->priv; + + for (isize = subQCIF; isize < VGA; isize++) { + if (tcm825x_sizes[isize + 1].height + * tcm825x_sizes[isize + 1].width > pixels) { + dev_dbg(&sensor->i2c_client->dev, "size %d\n", isize); + + return isize; + } + } + + dev_dbg(&sensor->i2c_client->dev, "format default VGA\n"); + + return VGA; +} + +/* + * Configure the TCM825X for current image size, pixel format, and + * frame period. fper is the frame period (in seconds) expressed as a + * fraction. Returns zero if successful, or non-zero otherwise. The + * actual frame period is returned in fper. + */ +static int tcm825x_configure(struct v4l2_int_device *s) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &sensor->pix; + enum image_size isize = tcm825x_find_size(s, pix->width, pix->height); + struct v4l2_fract *fper = &sensor->timeperframe; + enum pixel_format pfmt; + int err; + u32 tgt_fps; + u8 val; + + /* common register initialization */ + err = tcm825x_write_default_regs( + sensor->i2c_client, sensor->platform_data->default_regs()); + if (err) + return err; + + /* configure image size */ + val = tcm825x_siz_reg[isize]->val; + dev_dbg(&sensor->i2c_client->dev, + "configuring image size %d\n", isize); + err = tcm825x_write_reg_mask(sensor->i2c_client, + tcm825x_siz_reg[isize]->reg, val); + if (err) + return err; + + /* configure pixel format */ + switch (pix->pixelformat) { + default: + case V4L2_PIX_FMT_RGB565: + pfmt = RGB565; + break; + case V4L2_PIX_FMT_UYVY: + pfmt = YUV422; + break; + } + + dev_dbg(&sensor->i2c_client->dev, + "configuring pixel format %d\n", pfmt); + val = tcm825x_fmt_reg[pfmt]->val; + + err = tcm825x_write_reg_mask(sensor->i2c_client, + tcm825x_fmt_reg[pfmt]->reg, val); + if (err) + return err; + + /* + * For frame rate < 15, the FPS reg (addr 0x02, bit 7) must be + * set. Frame rate will be halved from the normal. + */ + tgt_fps = fper->denominator / fper->numerator; + if (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) { + val = tcm825x_read_reg(sensor->i2c_client, 0x02); + val |= 0x80; + tcm825x_write_reg(sensor->i2c_client, 0x02, val); + } + + return 0; +} + +/* + * Given the image capture format in pix, the nominal frame period in + * timeperframe, calculate the required xclk frequency. + * + * TCM825X input frequency characteristics are: + * Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz + */ +#define XCLK_MIN 11900000 +#define XCLK_MAX 25000000 + +static int ioctl_g_ext_clk(struct v4l2_int_device *s, u32 *xclk) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_fract *timeperframe = &sensor->timeperframe; + u32 tgt_xclk; /* target xclk */ + u32 tgt_fps; /* target frames per secound */ + + tgt_fps = timeperframe->denominator / timeperframe->numerator; + + tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ? + (2457 * tgt_fps) / MAX_HALF_FPS : + (2457 * tgt_fps) / MAX_FPS; + tgt_xclk *= 10000; + + tgt_xclk = min(tgt_xclk, (u32)XCLK_MAX); + tgt_xclk = max(tgt_xclk, (u32)XCLK_MIN); + + *xclk = tgt_xclk; + + return 0; +} + +static int ioctl_s_ext_clk(struct v4l2_int_device *s, u32 xclk) +{ + if (xclk > XCLK_MAX || xclk < XCLK_MIN) + return -EINVAL; + + return 0; +} + +static int ioctl_queryctrl(struct v4l2_int_device *s, + struct v4l2_queryctrl *qc) +{ + struct vcontrol *control; + + control = find_vctrl(qc->id); + + if (control == NULL) + return -EINVAL; + + *qc = control->qc; + + return 0; +} + +static int ioctl_g_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + struct tcm825x_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + int val, r; + struct vcontrol *lvc; + + /* exposure time is special, spread accross 2 registers */ + if (vc->id == V4L2_CID_EXPOSURE) { + int val_lower, val_upper; + + val_upper = tcm825x_read_reg(client, + TCM825X_ADDR(TCM825X_ESRSPD_U)); + if (val_upper < 0) + return val_upper; + val_lower = tcm825x_read_reg(client, + TCM825X_ADDR(TCM825X_ESRSPD_L)); + if (val_lower < 0) + return val_lower; + + vc->value = ((val_upper & 0x1f) << 8) | (val_lower); + return 0; + } + + lvc = find_vctrl(vc->id); + if (lvc == NULL) + return -EINVAL; + + r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg)); + if (r < 0) + return r; + val = r & TCM825X_MASK(lvc->reg); + val >>= lvc->start_bit; + + if (val < 0) + return val; + + vc->value = val; + return 0; +} + +static int ioctl_s_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + struct tcm825x_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + struct vcontrol *lvc; + int val = vc->value; + + /* exposure time is special, spread accross 2 registers */ + if (vc->id == V4L2_CID_EXPOSURE) { + int val_lower, val_upper; + val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L); + val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U); + + if (tcm825x_write_reg_mask(client, + TCM825X_ESRSPD_U, val_upper)) + return -EIO; + + if (tcm825x_write_reg_mask(client, + TCM825X_ESRSPD_L, val_lower)) + return -EIO; + + return 0; + } + + lvc = find_vctrl(vc->id); + if (lvc == NULL) + return -EINVAL; + + val = val << lvc->start_bit; + if (tcm825x_write_reg_mask(client, lvc->reg, val)) + return -EIO; + + return 0; +} + +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (index >= TCM825X_NUM_CAPTURE_FORMATS) + return -EINVAL; + break; + + default: + return -EINVAL; + } + + fmt->flags = tcm825x_formats[index].flags; + strlcpy(fmt->description, tcm825x_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = tcm825x_formats[index].pixelformat; + + return 0; +} + +static int ioctl_try_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct tcm825x_sensor *sensor = s->priv; + enum image_size isize; + int ifmt; + struct v4l2_pix_format *pix = &f->fmt.pix; + + isize = tcm825x_find_size(s, pix->width, pix->height); + dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %d\n", + isize, TCM825X_NUM_CAPTURE_FORMATS); + + pix->width = tcm825x_sizes[isize].width; + pix->height = tcm825x_sizes[isize].height; + + for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++) + if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat) + break; + + if (ifmt == TCM825X_NUM_CAPTURE_FORMATS) + ifmt = 0; /* Default = YUV 4:2:2 */ + + pix->pixelformat = tcm825x_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL; + pix->sizeimage = pix->bytesperline * pix->height; + pix->priv = 0; + dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n", + pix->pixelformat); + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_UYVY: + default: + pix->colorspace = V4L2_COLORSPACE_JPEG; + break; + case V4L2_PIX_FMT_RGB565: + pix->colorspace = V4L2_COLORSPACE_SRGB; + break; + } + + return 0; +} + +static int ioctl_s_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + int rval; + + rval = ioctl_try_fmt_cap(s, f); + if (rval) + return rval; + + rval = tcm825x_configure(s); + + sensor->pix = *pix; + + return rval; +} + +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct tcm825x_sensor *sensor = s->priv; + + f->fmt.pix = sensor->pix; + + return 0; +} + +static int ioctl_g_parm(struct v4l2_int_device *s, + struct v4l2_streamparm *a) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = sensor->timeperframe; + + return 0; +} + +static int ioctl_s_parm(struct v4l2_int_device *s, + struct v4l2_streamparm *a) +{ + struct tcm825x_sensor *sensor = s->priv; + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + u32 tgt_fps; /* target frames per secound */ + int rval; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if ((timeperframe->numerator == 0) + || (timeperframe->denominator == 0)) { + timeperframe->denominator = DEFAULT_FPS; + timeperframe->numerator = 1; + } + + tgt_fps = timeperframe->denominator / timeperframe->numerator; + + if (tgt_fps > MAX_FPS) { + timeperframe->denominator = MAX_FPS; + timeperframe->numerator = 1; + } else if (tgt_fps < MIN_FPS) { + timeperframe->denominator = MIN_FPS; + timeperframe->numerator = 1; + } + + sensor->timeperframe = *timeperframe; + + rval = tcm825x_configure(s); + + return rval; +} + +static int ioctl_s_power(struct v4l2_int_device *s, int on) +{ + struct tcm825x_sensor *sensor = s->priv; + + return sensor->platform_data->power_set(on); +} + +static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf) +{ + struct tcm825x_sensor *sensor = s->priv; + + return sensor->platform_data->needs_reset(s, buf, &sensor->pix); +} + +static int ioctl_reset(struct v4l2_int_device *s) +{ + return -EBUSY; +} + +static int ioctl_init(struct v4l2_int_device *s) +{ + return tcm825x_configure(s); +} + +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct tcm825x_sensor *sensor = s->priv; + int r; + + r = tcm825x_read_reg(sensor->i2c_client, 0x01); + if (r < 0) + return r; + if (r == 0) { + dev_err(&sensor->i2c_client->dev, "device not detected\n"); + return -EIO; + } + return 0; +} + +#define NUM_IOCTLS 17 + +static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[NUM_IOCTLS] = { + { vidioc_int_dev_init_num, + (v4l2_int_ioctl_func *)&ioctl_dev_init }, + { vidioc_int_dev_exit_num, + (v4l2_int_ioctl_func *)&ioctl_dev_exit }, + { vidioc_int_s_power_num, + (v4l2_int_ioctl_func *)&ioctl_s_power }, + { vidioc_int_g_ext_clk_num, + (v4l2_int_ioctl_func *)&ioctl_g_ext_clk }, + { vidioc_int_s_ext_clk_num, + (v4l2_int_ioctl_func *)&ioctl_s_ext_clk }, + { vidioc_int_g_needs_reset_num, + (v4l2_int_ioctl_func *)&ioctl_g_needs_reset }, + { vidioc_int_reset_num, + (v4l2_int_ioctl_func *)&ioctl_reset }, + { vidioc_int_init_num, + (v4l2_int_ioctl_func *)&ioctl_init }, + { vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *)&ioctl_enum_fmt_cap }, + { vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *)&ioctl_try_fmt_cap }, + { vidioc_int_g_fmt_cap_num, + (v4l2_int_ioctl_func *)&ioctl_g_fmt_cap }, + { vidioc_int_s_fmt_cap_num, + (v4l2_int_ioctl_func *)&ioctl_s_fmt_cap }, + { vidioc_int_g_parm_num, + (v4l2_int_ioctl_func *)&ioctl_g_parm }, + { vidioc_int_s_parm_num, + (v4l2_int_ioctl_func *)&ioctl_s_parm }, + { vidioc_int_queryctrl_num, + (v4l2_int_ioctl_func *)&ioctl_queryctrl }, + { vidioc_int_g_ctrl_num, + (v4l2_int_ioctl_func *)&ioctl_g_ctrl }, + { vidioc_int_s_ctrl_num, + (v4l2_int_ioctl_func *)&ioctl_s_ctrl }, +}; + +static struct v4l2_int_slave tcm825x_slave = { + .ioctls = tcm825x_ioctl_desc, + .num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc), +}; + +static struct tcm825x_sensor tcm825x; + +static struct v4l2_int_device tcm825x_int_device = { + .module = THIS_MODULE, + .name = TCM825X_NAME, + .priv = &tcm825x, + .type = v4l2_int_type_slave, + .u = { + .slave = &tcm825x_slave, + }, +}; + +static int tcm825x_probe(struct i2c_client *client) +{ + struct tcm825x_sensor *sensor = &tcm825x; + int rval; + + if (i2c_get_clientdata(client)) + return -EBUSY; + + sensor->platform_data = client->dev.platform_data; + + if (sensor->platform_data == NULL + && !sensor->platform_data->is_okay()) + return -ENODEV; + + sensor->v4l2_int_device = &tcm825x_int_device; + + sensor->i2c_client = client; + i2c_set_clientdata(client, sensor); + + /* Make the default capture format QVGA RGB565 */ + sensor->pix.width = tcm825x_sizes[QVGA].width; + sensor->pix.height = tcm825x_sizes[QVGA].height; + sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565; + + rval = v4l2_int_device_register(sensor->v4l2_int_device); + if (rval) + i2c_set_clientdata(client, NULL); + + return rval; +} + +static int __exit tcm825x_remove(struct i2c_client *client) +{ + struct tcm825x_sensor *sensor = i2c_get_clientdata(client); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + v4l2_int_device_unregister(sensor->v4l2_int_device); + i2c_set_clientdata(client, NULL); + + return 0; +} + +static struct i2c_driver tcm825x_i2c_driver = { + .driver = { + .name = TCM825X_NAME, + }, + .probe = &tcm825x_probe, + .remove = __exit_p(&tcm825x_remove), +}; + +static struct tcm825x_sensor tcm825x = { + .timeperframe = { + .numerator = 1, + .denominator = DEFAULT_FPS, + }, +}; + +static int __init tcm825x_init(void) +{ + int rval; + int i = 0; + + /* Just an experiment --- don't use *_cb functions yet. */ + tcm825x_ioctl_desc[i++] = vidioc_int_dev_init_cb(&ioctl_dev_init); + BUG_ON(i >= NUM_IOCTLS); + + rval = i2c_add_driver(&tcm825x_i2c_driver); + if (rval) + printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n", + __FUNCTION__); + + return rval; +} + +static void __exit tcm825x_exit(void) +{ + i2c_del_driver(&tcm825x_i2c_driver); +} + +/* + * FIXME: Menelaus isn't ready (?) at module_init stage, so use + * late_initcall for now. + */ +late_initcall(tcm825x_init); +module_exit(tcm825x_exit); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("TCM825x camera sensor driver"); +MODULE_LICENSE("GPL"); diff -puN /dev/null drivers/media/video/tcm825x.h --- /dev/null +++ a/drivers/media/video/tcm825x.h @@ -0,0 +1,195 @@ +/* + * drivers/media/video/tcm825x.h + * + * Register definitions for the TCM825X CameraChip. + * + * Author: David Cohen (david.cohen@indt.org.br) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * This file was based on ov9640.h from MontaVista + */ + +#ifndef TCM825X_H +#define TCM825X_H + +#include + +#include + +#define TCM825X_NAME "tcm825x" + +#define TCM825X_MASK(x) x & 0x00ff +#define TCM825X_ADDR(x) (x & 0xff00) >> 8 + +/* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */ +#define TCM825X_I2C_ADDR 0x3d + +/* + * define register offsets for the TCM825X sensor chip + * OFFSET(8 bits) + MASK(8 bits) + * MASK bit 4 and 3 are used when the register uses more than one address + */ +#define TCM825X_FPS 0x0280 +#define TCM825X_ACF 0x0240 +#define TCM825X_DOUTBUF 0x020C +#define TCM825X_DCLKP 0x0202 +#define TCM825X_ACFDET 0x0201 +#define TCM825X_DOUTSW 0x0380 +#define TCM825X_DATAHZ 0x0340 +#define TCM825X_PICSIZ 0x033c +#define TCM825X_PICFMT 0x0302 +#define TCM825X_V_INV 0x0480 +#define TCM825X_H_INV 0x0440 +#define TCM825X_ESRLSW 0x0430 +#define TCM825X_V_LENGTH 0x040F +#define TCM825X_ALCSW 0x0580 +#define TCM825X_ESRLIM 0x0560 +#define TCM825X_ESRSPD_U 0x051F +#define TCM825X_ESRSPD_L 0x06FF +#define TCM825X_AG 0x07FF +#define TCM825X_ESRSPD2 0x06FF +#define TCM825X_ALCMODE 0x0830 +#define TCM825X_ALCH 0x080F +#define TCM825X_ALCL 0x09FF +#define TCM825X_AWBSW 0x0A80 +#define TCM825X_MRG 0x0BFF +#define TCM825X_MBG 0x0CFF +#define TCM825X_GAMSW 0x0D80 +#define TCM825X_HDTG 0x0EFF +#define TCM825X_VDTG 0x0FFF +#define TCM825X_HDTCORE 0x10F0 +#define TCM825X_VDTCORE 0x100F +#define TCM825X_CONT 0x11FF +#define TCM825X_BRIGHT 0x12FF +#define TCM825X_VHUE 0x137F +#define TCM825X_UHUE 0x147F +#define TCM825X_VGAIN 0x153F +#define TCM825X_UGAIN 0x163F +#define TCM825X_UVCORE 0x170F +#define TCM825X_SATU 0x187F +#define TCM825X_MHMODE 0x1980 +#define TCM825X_MHLPFSEL 0x1940 +#define TCM825X_YMODE 0x1930 +#define TCM825X_MIXHG 0x1907 +#define TCM825X_LENS 0x1A3F +#define TCM825X_AGLIM 0x1BE0 +#define TCM825X_LENSRPOL 0x1B10 +#define TCM825X_LENSRGAIN 0x1B0F +#define TCM825X_ES100S 0x1CFF +#define TCM825X_ES120S 0x1DFF +#define TCM825X_DMASK 0x1EC0 +#define TCM825X_CODESW 0x1E20 +#define TCM825X_CODESEL 0x1E10 +#define TCM825X_TESPIC 0x1E04 +#define TCM825X_PICSEL 0x1E03 +#define TCM825X_HNUM 0x20FF +#define TCM825X_VOUTPH 0x287F +#define TCM825X_ESROUT 0x327F +#define TCM825X_ESROUT2 0x33FF +#define TCM825X_AGOUT 0x34FF +#define TCM825X_DGOUT 0x353F +#define TCM825X_AGSLOW1 0x39C0 +#define TCM825X_FLLSMODE 0x3930 +#define TCM825X_FLLSLIM 0x390F +#define TCM825X_DETSEL 0x3AF0 +#define TCM825X_ACDETNC 0x3A0F +#define TCM825X_AGSLOW2 0x3BC0 +#define TCM825X_DG 0x3B3F +#define TCM825X_REJHLEV 0x3CFF +#define TCM825X_ALCLOCK 0x3D80 +#define TCM825X_FPSLNKSW 0x3D40 +#define TCM825X_ALCSPD 0x3D30 +#define TCM825X_REJH 0x3D03 +#define TCM825X_SHESRSW 0x3E80 +#define TCM825X_ESLIMSEL 0x3E40 +#define TCM825X_SHESRSPD 0x3E30 +#define TCM825X_ELSTEP 0x3E0C +#define TCM825X_ELSTART 0x3E03 +#define TCM825X_AGMIN 0x3FFF +#define TCM825X_PREGRG 0x423F +#define TCM825X_PREGBG 0x433F +#define TCM825X_PRERG 0x443F +#define TCM825X_PREBG 0x453F +#define TCM825X_MSKBR 0x477F +#define TCM825X_MSKGR 0x487F +#define TCM825X_MSKRB 0x497F +#define TCM825X_MSKGB 0x4A7F +#define TCM825X_MSKRG 0x4B7F +#define TCM825X_MSKBG 0x4C7F +#define TCM825X_HDTCSW 0x4D80 +#define TCM825X_VDTCSW 0x4D40 +#define TCM825X_DTCYL 0x4D3F +#define TCM825X_HDTPSW 0x4E80 +#define TCM825X_VDTPSW 0x4E40 +#define TCM825X_DTCGAIN 0x4E3F +#define TCM825X_DTLLIMSW 0x4F10 +#define TCM825X_DTLYLIM 0x4F0F +#define TCM825X_YLCUTLMSK 0x5080 +#define TCM825X_YLCUTL 0x503F +#define TCM825X_YLCUTHMSK 0x5180 +#define TCM825X_YLCUTH 0x513F +#define TCM825X_UVSKNC 0x527F +#define TCM825X_UVLJ 0x537F +#define TCM825X_WBGMIN 0x54FF +#define TCM825X_WBGMAX 0x55FF +#define TCM825X_WBSPDUP 0x5603 +#define TCM825X_ALLAREA 0x5820 +#define TCM825X_WBLOCK 0x5810 +#define TCM825X_WB2SP 0x580F +#define TCM825X_KIZUSW 0x5920 +#define TCM825X_PBRSW 0x5910 +#define TCM825X_ABCSW 0x5903 +#define TCM825X_PBDLV 0x5AFF +#define TCM825X_PBC1LV 0x5BFF + +#define TCM825X_NUM_REGS (TCM825X_ADDR(TCM825X_PBC1LV) + 1) + +#define TCM825X_BYTES_PER_PIXEL 2 + +#define TCM825X_REG_TERM 0xff /* terminating list entry for reg */ +#define TCM825X_VAL_TERM 0xff /* terminating list entry for val */ + +/* define a structure for tcm825x register initialization values */ +struct tcm825x_reg { + u8 val; + u16 reg; +}; + +enum image_size { subQCIF = 0, QQVGA, QCIF, QVGA, CIF, VGA }; +enum pixel_format { YUV422 = 0, RGB565 }; +#define NUM_IMAGE_SIZES 6 +#define NUM_PIXEL_FORMATS 2 + +struct capture_size { + unsigned long width; + unsigned long height; +}; + +struct tcm825x_platform_data { + /* Is the sensor usable? Doesn't yet mean it's there, but you + * can try! */ + int (*is_okay)(void); + /* Set power state, zero is off, non-zero is on. */ + int (*power_set)(int power); + /* Default registers written after power-on or reset. */ + const struct tcm825x_reg *(*default_regs)(void); + int (*needs_reset)(struct v4l2_int_device *s, void *buf, + struct v4l2_pix_format *fmt); +}; + +/* Array of image sizes supported by TCM825X. These must be ordered from + * smallest image size to largest. + */ +const static struct capture_size tcm825x_sizes[] = { + { 128, 96 }, /* subQCIF */ + { 160, 120 }, /* QQVGA */ + { 176, 144 }, /* QCIF */ + { 320, 240 }, /* QVGA */ + { 352, 288 }, /* CIF */ + { 640, 480 }, /* VGA */ +}; + +#endif /* ifndef TCM825X_H */ diff -puN drivers/media/video/tuner-types.c~git-dvb drivers/media/video/tuner-types.c --- a/drivers/media/video/tuner-types.c~git-dvb +++ a/drivers/media/video/tuner-types.c @@ -670,6 +670,9 @@ static struct tuner_params tuner_panason .count = ARRAY_SIZE(tuner_panasonic_vp27_ntsc_ranges), .has_tda9887 = 1, .intercarrier_mode = 1, + .default_top_low = -3, + .default_top_mid = -3, + .default_top_high = -3, }, }; diff -puN /dev/null drivers/media/video/v4l2-int-device.c --- /dev/null +++ a/drivers/media/video/v4l2-int-device.c @@ -0,0 +1,166 @@ +/* + * drivers/media/video/v4l2-int-device.c + * + * V4L2 internal ioctl interface. + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include + +#include + +static DEFINE_MUTEX(mutex); +static LIST_HEAD(int_list); + +static void v4l2_int_device_try_attach_all(void) +{ + struct list_head *head_master; + + list_for_each(head_master, &int_list) { + struct list_head *head_slave; + struct v4l2_int_device *m = + list_entry(head_master, struct v4l2_int_device, head); + + if (m->type != v4l2_int_type_master) + continue; + + list_for_each(head_slave, &int_list) { + struct v4l2_int_device *s = + list_entry(head_slave, + struct v4l2_int_device, head); + + if (s->type != v4l2_int_type_slave) + continue; + + /* Slave is connected? */ + if (s->u.slave->master) + continue; + + /* Slave wants to attach to master? */ + if (s->u.slave->attach_to[0] != 0 + && strncmp(m->name, s->u.slave->attach_to, + V4L2NAMESIZE)) + continue; + + if (!try_module_get(m->module)) + continue; + + if (m->u.master->attach(m, s)) { + module_put(m->module); + continue; + } + + s->u.slave->master = m; + } + } +} + +static int ioctl_sort_cmp(const void *a, const void *b) +{ + const struct v4l2_int_ioctl_desc *d1 = a, *d2 = b; + + if (d1->num > d2->num) + return 1; + + if (d1->num < d2->num) + return -1; + + return 0; +} + +int v4l2_int_device_register(struct v4l2_int_device *d) +{ + if (d->type == v4l2_int_type_slave) + sort(d->u.slave->ioctls, d->u.slave->num_ioctls, + sizeof(struct v4l2_int_ioctl_desc), + &ioctl_sort_cmp, NULL); + mutex_lock(&mutex); + list_add(&d->head, &int_list); + v4l2_int_device_try_attach_all(); + mutex_unlock(&mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_int_device_register); + +void v4l2_int_device_unregister(struct v4l2_int_device *d) +{ + mutex_lock(&mutex); + list_del(&d->head); + if (d->type == v4l2_int_type_slave + && d->u.slave->master != NULL) { + d->u.slave->master->u.master->detach(d); + module_put(d->u.slave->master->module); + d->u.slave->master = NULL; + } + mutex_unlock(&mutex); +} +EXPORT_SYMBOL_GPL(v4l2_int_device_unregister); + +/* Adapted from search_extable in extable.c. */ +static v4l2_int_ioctl_func *find_ioctl(struct v4l2_int_slave *slave, int cmd, + v4l2_int_ioctl_func *no_such_ioctl) +{ + const struct v4l2_int_ioctl_desc *first = slave->ioctls; + const struct v4l2_int_ioctl_desc *last = + first + slave->num_ioctls - 1; + + while (first <= last) { + const struct v4l2_int_ioctl_desc *mid; + + mid = (last - first) / 2 + first; + + if (mid->num < cmd) + first = mid + 1; + else if (mid->num > cmd) + last = mid - 1; + else + return mid->func; + } + + return no_such_ioctl; +} + +static int no_such_ioctl_0(struct v4l2_int_device *d) +{ + return -EINVAL; +} + +int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd) +{ + return ((v4l2_int_ioctl_func_0 *) + find_ioctl(d->u.slave, cmd, + (v4l2_int_ioctl_func *)&no_such_ioctl_0))(d); +} + +static int no_such_ioctl_1(struct v4l2_int_device *d, void *arg) +{ + return -EINVAL; +} + +int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg) +{ + return ((v4l2_int_ioctl_func_1 *) + find_ioctl(d->u.slave, cmd, + (v4l2_int_ioctl_func *)&no_such_ioctl_1))(d, arg); +} diff -puN /dev/null drivers/media/video/vp27smpx.c --- /dev/null +++ a/drivers/media/video/vp27smpx.c @@ -0,0 +1,211 @@ +/* + * vp27smpx - driver version 0.0.1 + * + * Copyright (C) 2007 Hans Verkuil + * + * Special thanks to Kazz for the i2c data. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("vp27smpx driver"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static unsigned short normal_i2c[] = { 0xb6 >> 1, I2C_CLIENT_END }; + + +I2C_CLIENT_INSMOD; + +/* ----------------------------------------------------------------------- */ + +struct vp27smpx_state { + int radio; + u32 audmode; +}; + +static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode) +{ + struct vp27smpx_state *state = i2c_get_clientdata(client); + u8 data[3] = { 0x00, 0x00, 0x04 }; + + switch (audmode) { + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_LANG1: + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + data[1] = 0x01; + break; + case V4L2_TUNER_MODE_LANG2: + data[1] = 0x02; + break; + } + + if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) { + v4l_err(client, "%s: I/O error setting audmode\n", client->name); + } + else { + state->audmode = audmode; + } +} + +static int vp27smpx_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + struct vp27smpx_state *state = i2c_get_clientdata(client); + struct v4l2_tuner *vt = arg; + + switch (cmd) { + case AUDC_SET_RADIO: + state->radio = 1; + break; + + case VIDIOC_S_STD: + state->radio = 0; + break; + + case VIDIOC_S_TUNER: + if (!state->radio) + vp27smpx_set_audmode(client, vt->audmode); + break; + + case VIDIOC_G_TUNER: + if (state->radio) + break; + vt->audmode = state->audmode; + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + break; + + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_VP27SMPX, 0); + + case VIDIOC_LOG_STATUS: + v4l_info(client, "Audio Mode: %u%s\n", state->audmode, + state->radio ? " (Radio)" : ""); + break; + + default: + return -EINVAL; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static struct i2c_driver i2c_driver; + +static int vp27smpx_attach(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct vp27smpx_state *state; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver; + snprintf(client->name, sizeof(client->name) - 1, "vp27smpx"); + + v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name); + + state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL); + if (state == NULL) { + kfree(client); + return -ENOMEM; + } + state->audmode = V4L2_TUNER_MODE_STEREO; + i2c_set_clientdata(client, state); + + /* initialize vp27smpx */ + vp27smpx_set_audmode(client, state->audmode); + i2c_attach_client(client); + + return 0; +} + +static int vp27smpx_probe(struct i2c_adapter *adapter) +{ + if (adapter->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adapter, &addr_data, vp27smpx_attach); + return 0; +} + +static int vp27smpx_detach(struct i2c_client *client) +{ + struct vp27smpx_state *state = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + kfree(state); + kfree(client); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +static struct i2c_driver i2c_driver = { + .driver = { + .name = "vp27smpx", + }, + .id = I2C_DRIVERID_VP27SMPX, + .attach_adapter = vp27smpx_probe, + .detach_client = vp27smpx_detach, + .command = vp27smpx_command, +}; + + +static int __init vp27smpx_init_module(void) +{ + return i2c_add_driver(&i2c_driver); +} + +static void __exit vp27smpx_cleanup_module(void) +{ + i2c_del_driver(&i2c_driver); +} + +module_init(vp27smpx_init_module); +module_exit(vp27smpx_cleanup_module); diff -puN drivers/media/video/zoran_card.c~git-dvb drivers/media/video/zoran_card.c --- a/drivers/media/video/zoran_card.c~git-dvb +++ a/drivers/media/video/zoran_card.c @@ -64,15 +64,15 @@ extern const struct zoran_format zoran_formats[]; static int card[BUZ_MAX] = { -1, -1, -1, -1 }; -module_param_array(card, int, NULL, 0); +module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "The type of card"); static int encoder[BUZ_MAX] = { -1, -1, -1, -1 }; -module_param_array(encoder, int, NULL, 0); +module_param_array(encoder, int, NULL, 0444); MODULE_PARM_DESC(encoder, "i2c TV encoder"); static int decoder[BUZ_MAX] = { -1, -1, -1, -1 }; -module_param_array(decoder, int, NULL, 0); +module_param_array(decoder, int, NULL, 0444); MODULE_PARM_DESC(decoder, "i2c TV decoder"); /* @@ -84,29 +84,31 @@ MODULE_PARM_DESC(decoder, "i2c TV decode */ static unsigned long vidmem = 0; /* Video memory base address */ -module_param(vidmem, ulong, 0); +module_param(vidmem, ulong, 0444); +MODULE_PARM_DESC(vidmem, "Default video memory base address"); /* Default input and video norm at startup of the driver. */ -static int default_input = 0; /* 0=Composite, 1=S-Video */ -module_param(default_input, int, 0); +static unsigned int default_input = 0; /* 0=Composite, 1=S-Video */ +module_param(default_input, uint, 0444); MODULE_PARM_DESC(default_input, "Default input (0=Composite, 1=S-Video, 2=Internal)"); static int default_mux = 1; /* 6 Eyes input selection */ -module_param(default_mux, int, 0); +module_param(default_mux, int, 0644); MODULE_PARM_DESC(default_mux, "Default 6 Eyes mux setting (Input selection)"); static int default_norm = 0; /* 0=PAL, 1=NTSC 2=SECAM */ -module_param(default_norm, int, 0); +module_param(default_norm, int, 0444); MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)"); -static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ -module_param(video_nr, int, 0); -MODULE_PARM_DESC(video_nr, "video device number"); +/* /dev/videoN, -1 for autodetect */ +static int video_nr[BUZ_MAX] = {-1, -1, -1, -1}; +module_param_array(video_nr, int, NULL, 0444); +MODULE_PARM_DESC(video_nr, "video device number (-1=Auto)"); /* Number and size of grab buffers for Video 4 Linux @@ -127,28 +129,27 @@ MODULE_PARM_DESC(video_nr, "video device int v4l_nbufs = 2; int v4l_bufsize = 128; /* Everybody should be able to work with this setting */ -module_param(v4l_nbufs, int, 0); +module_param(v4l_nbufs, int, 0644); MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use"); -module_param(v4l_bufsize, int, 0); +module_param(v4l_bufsize, int, 0644); 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_param(jpg_nbufs, int, 0); +module_param(jpg_nbufs, int, 0644); MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use"); -module_param(jpg_bufsize, int, 0); +module_param(jpg_bufsize, int, 0644); 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_param(pass_through, int, 0); +module_param(pass_through, int, 0644); MODULE_PARM_DESC(pass_through, "Pass TV signal through to TV-out when idling"); -static int debug = 1; -int *zr_debug = &debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-4)"); +int zr36067_debug = 1; +module_param_named(debug, zr36067_debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-5)"); MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver"); MODULE_AUTHOR("Serguei Miridonov"); @@ -161,12 +162,6 @@ static struct pci_device_id zr36067_pci_ }; MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl); -#define dprintk(num, format, args...) \ - do { \ - if (*zr_debug >= num) \ - printk(format, ##args); \ - } while (0) - int zoran_num; /* number of Buzs in use */ struct zoran zoran[BUZ_MAX]; @@ -1075,7 +1070,7 @@ test_interrupts (struct zoran *zr) if (timeout) { dprintk(1, ": time spent: %d\n", 1 * HZ - timeout); } - if (*zr_debug > 1) + if (zr36067_debug > 1) print_interrupts(zr); btwrite(icr, ZR36057_ICR); } @@ -1121,7 +1116,14 @@ zr36057_init (struct zoran *zr) zr->timing = zr->card.tvn[zr->norm]; } - zr->input = default_input = (default_input ? 1 : 0); + if (default_input > zr->card.inputs-1) { + dprintk(1, + KERN_WARNING + "%s: default_input value %d out of range (0-%d)\n", + ZR_DEVNAME(zr), default_input, zr->card.inputs-1); + default_input = 0; + } + zr->input = default_input; /* Should the following be reset at every open ? */ zr->hue = 32768; @@ -1153,12 +1155,12 @@ zr36057_init (struct zoran *zr) */ memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template)); strcpy(zr->video_dev->name, ZR_DEVNAME(zr)); - err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr); + err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr[zr->id]); if (err < 0) goto exit_unregister; zoran_init_hardware(zr); - if (*zr_debug > 2) + if (zr36067_debug > 2) detect_guest_activity(zr); test_interrupts(zr); if (!pass_through) { @@ -1620,7 +1622,7 @@ init_dc10_cards (void) } /* random nonsense */ - dprintk(5, KERN_DEBUG "Jotti is een held!\n"); + dprintk(6, KERN_DEBUG "Jotti is een held!\n"); /* some mainboards might not do PCI-PCI data transfer well */ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) { diff -puN drivers/media/video/zoran_card.h~git-dvb drivers/media/video/zoran_card.h --- a/drivers/media/video/zoran_card.h~git-dvb +++ a/drivers/media/video/zoran_card.h @@ -30,6 +30,14 @@ #ifndef __ZORAN_CARD_H__ #define __ZORAN_CARD_H__ +extern int zr36067_debug; + +#define dprintk(num, format, args...) \ + do { \ + if (zr36067_debug >= num) \ + printk(format, ##args); \ + } while (0) + /* Anybody who uses more than four? */ #define BUZ_MAX 4 extern int zoran_num; diff -puN drivers/media/video/zoran_device.c~git-dvb drivers/media/video/zoran_device.c --- a/drivers/media/video/zoran_device.c~git-dvb +++ a/drivers/media/video/zoran_device.c @@ -52,6 +52,7 @@ #include "videocodec.h" #include "zoran.h" #include "zoran_device.h" +#include "zoran_card.h" #define IRQ_MASK ( ZR36057_ISR_GIRQ0 | \ ZR36057_ISR_GIRQ1 | \ @@ -59,14 +60,6 @@ extern const struct zoran_format zoran_formats[]; -extern int *zr_debug; - -#define dprintk(num, format, args...) \ - do { \ - if (*zr_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 @@ -76,7 +69,7 @@ static int lml33dpath = 0; /* 1 will use * load on Bt819 input, there will be * some image imperfections */ -module_param(lml33dpath, bool, 0); +module_param(lml33dpath, bool, 0644); MODULE_PARM_DESC(lml33dpath, "Use digital path capture mode (on LML33 cards)"); @@ -174,7 +167,7 @@ post_office_read (struct zoran *zr, static void dump_guests (struct zoran *zr) { - if (*zr_debug > 2) { + if (zr36067_debug > 2) { int i, guest[8]; for (i = 1; i < 8; i++) { // Don't read jpeg codec here @@ -1271,7 +1264,7 @@ error_handler (struct zoran *zr, zr->num_errors++; /* Report error */ - if (*zr_debug > 1 && zr->num_errors <= 8) { + if (zr36067_debug > 1 && zr->num_errors <= 8) { long frame; frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; @@ -1531,7 +1524,7 @@ zoran_irq (int irq, if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { - if (*zr_debug > 1 && + if (zr36067_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", @@ -1568,7 +1561,7 @@ zoran_irq (int irq, zr->JPEG_missed; } - if (*zr_debug > 2 && zr->frame_num < 6) { + if (zr36067_debug > 2 && zr->frame_num < 6) { int i; printk("%s: seq=%ld stat_com:", ZR_DEVNAME(zr), zr->jpg_seq_num); diff -puN drivers/media/video/zoran_driver.c~git-dvb drivers/media/video/zoran_driver.c --- a/drivers/media/video/zoran_driver.c~git-dvb +++ a/drivers/media/video/zoran_driver.c @@ -127,6 +127,7 @@ const struct zoran_format zoran_formats[ .depth = 15, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, +<<<<<<< HEAD/drivers/media/video/zoran_driver.c .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif, }, { .name = "16-bit RGB LE", @@ -137,10 +138,28 @@ const struct zoran_format zoran_formats[ ZORAN_FORMAT_OVERLAY, .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif| ZR36057_VFESPFR_LittleEndian, +======= + .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif, +>>>>>>> /drivers/media/video/zoran_driver.c + }, { +<<<<<<< HEAD/drivers/media/video/zoran_driver.c + .name = "16-bit RGB BE", + ZFMT(-1, + V4L2_PIX_FMT_RGB565, V4L2_COLORSPACE_SRGB), +======= + .name = "16-bit RGB LE", + ZFMT(VIDEO_PALETTE_RGB565, + V4L2_PIX_FMT_RGB565, V4L2_COLORSPACE_SRGB), + .depth = 16, + .flags = ZORAN_FORMAT_CAPTURE | + ZORAN_FORMAT_OVERLAY, + .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif| + ZR36057_VFESPFR_LittleEndian, }, { .name = "16-bit RGB BE", ZFMT(-1, V4L2_PIX_FMT_RGB565, V4L2_COLORSPACE_SRGB), +>>>>>>> /drivers/media/video/zoran_driver.c .depth = 16, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, @@ -152,6 +171,9 @@ const struct zoran_format zoran_formats[ .depth = 24, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, +<<<<<<< HEAD/drivers/media/video/zoran_driver.c + .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_Pack24, +======= .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_Pack24, }, { .name = "32-bit RGB LE", @@ -161,10 +183,25 @@ const struct zoran_format zoran_formats[ .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_LittleEndian, +>>>>>>> /drivers/media/video/zoran_driver.c + }, { +<<<<<<< HEAD/drivers/media/video/zoran_driver.c + .name = "32-bit RGB LE", + ZFMT(VIDEO_PALETTE_RGB32, + V4L2_PIX_FMT_BGR32, V4L2_COLORSPACE_SRGB), + .depth = 32, + .flags = ZORAN_FORMAT_CAPTURE | + ZORAN_FORMAT_OVERLAY, + .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_LittleEndian, }, { .name = "32-bit RGB BE", ZFMT(-1, V4L2_PIX_FMT_RGB32, V4L2_COLORSPACE_SRGB), +======= + .name = "32-bit RGB BE", + ZFMT(-1, + V4L2_PIX_FMT_RGB32, V4L2_COLORSPACE_SRGB), +>>>>>>> /drivers/media/video/zoran_driver.c .depth = 32, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, @@ -200,14 +237,6 @@ const struct zoran_format zoran_formats[ // RJ: Test only - want to test BUZ_USE_HIMEM even when CONFIG_BIGPHYS_AREA is defined -extern int *zr_debug; - -#define dprintk(num, format, args...) \ - do { \ - if (*zr_debug >= num) \ - printk(format, ##args); \ - } while (0) - extern int v4l_nbufs; extern int v4l_bufsize; extern int jpg_nbufs; @@ -215,8 +244,8 @@ extern int jpg_bufsize; extern int pass_through; static int lock_norm = 0; /* 1=Don't change TV standard (norm) */ -module_param(lock_norm, int, 0); -MODULE_PARM_DESC(lock_norm, "Users can't change norm"); +module_param(lock_norm, int, 0644); +MODULE_PARM_DESC(lock_norm, "Prevent norm changes (1 = ignore, >1 = fail)"); #ifdef CONFIG_VIDEO_V4L2 /* small helper function for calculating buffersizes for v4l2 @@ -1106,12 +1135,10 @@ jpg_sync (struct file *file, frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; /* buffer should now be in BUZ_STATE_DONE */ - if (*zr_debug > 0) - if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE) - dprintk(2, - KERN_ERR - "%s: jpg_sync() - internal state error\n", - ZR_DEVNAME(zr)); + if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE) + dprintk(2, + KERN_ERR "%s: jpg_sync() - internal state error\n", + ZR_DEVNAME(zr)); *bs = zr->jpg_buffers.buffer[frame].bs; bs->frame = frame; @@ -1389,7 +1416,7 @@ zoran_close (struct inode *inode, /* disable interrupts */ btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); - if (*zr_debug > 1) + if (zr36067_debug > 1) print_interrupts(zr); /* Overlay off */ diff -puN drivers/media/video/zoran_procfs.c~git-dvb drivers/media/video/zoran_procfs.c --- a/drivers/media/video/zoran_procfs.c~git-dvb +++ a/drivers/media/video/zoran_procfs.c @@ -48,14 +48,7 @@ #include "videocodec.h" #include "zoran.h" #include "zoran_procfs.h" - -extern int *zr_debug; - -#define dprintk(num, format, args...) \ - do { \ - if (*zr_debug >= num) \ - printk(format, ##args); \ - } while (0) +#include "zoran_card.h" #ifdef CONFIG_PROC_FS struct procfs_params_zr36067 { diff -puN include/linux/i2c-id.h~git-dvb include/linux/i2c-id.h diff -puN /dev/null include/media/ivtv-fb.h --- /dev/null +++ a/include/media/ivtv-fb.h @@ -0,0 +1,34 @@ +/* + On Screen Display cx23415 Framebuffer driver + + Copyright (C) 2006 Ian Armstrong + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LINUX_IVTV_FB_H +#define _LINUX_IVTV_FB_H + +/* Framebuffer external API */ + +struct ivtvfb_dma_frame { + void __user *source; + unsigned long dest_offset; + int count; +}; + +#define IVTVFB_IOC_DMA_FRAME _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtvfb_dma_frame) + +#endif diff -puN include/media/saa7146.h~git-dvb include/media/saa7146.h --- a/include/media/saa7146.h~git-dvb +++ a/include/media/saa7146.h @@ -146,7 +146,6 @@ struct saa7146_dev /* from saa7146_i2c.c */ int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate); -int saa7146_i2c_transfer(struct saa7146_dev *saa, const struct i2c_msg *msgs, int num, int retries); /* from saa7146_core.c */ extern struct list_head saa7146_devices; diff -puN include/media/v4l2-chip-ident.h~git-dvb include/media/v4l2-chip-ident.h --- a/include/media/v4l2-chip-ident.h~git-dvb +++ a/include/media/v4l2-chip-ident.h @@ -65,6 +65,9 @@ enum { V4L2_IDENT_CX23415 = 415, V4L2_IDENT_CX23416 = 416, + /* module vp27smpx: just ident 2700 */ + V4L2_IDENT_VP27SMPX = 2700, + /* module wm8739: just ident 8739 */ V4L2_IDENT_WM8739 = 8739, diff -puN include/media/v4l2-dev.h~git-dvb include/media/v4l2-dev.h --- a/include/media/v4l2-dev.h~git-dvb +++ a/include/media/v4l2-dev.h @@ -23,8 +23,6 @@ #include #endif -#include - #define VIDEO_MAJOR 81 /* Minor device allocation */ #define MINOR_VFL_TYPE_GRABBER_MIN 0 diff -puN /dev/null include/media/v4l2-int-device.h --- /dev/null +++ a/include/media/v4l2-int-device.h @@ -0,0 +1,210 @@ +/* + * include/media/v4l2-int-device.h + * + * V4L2 internal ioctl interface. + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef V4L2_INT_DEVICE_H +#define V4L2_INT_DEVICE_H + +#include +#include + +#define V4L2NAMESIZE 32 + +enum v4l2_int_type { + v4l2_int_type_master = 1, + v4l2_int_type_slave +}; + +enum v4l2_int_ioctl_num { + /* + * + * "Proper" V4L ioctls, as in struct video_device. + * + */ + vidioc_int_enum_fmt_cap_num = 1, + vidioc_int_g_fmt_cap_num, + vidioc_int_s_fmt_cap_num, + vidioc_int_try_fmt_cap_num, + vidioc_int_queryctrl_num, + vidioc_int_g_ctrl_num, + vidioc_int_s_ctrl_num, + vidioc_int_g_parm_num, + vidioc_int_s_parm_num, + + /* + * + * Strictly internal ioctls. + * + */ + /* Initialise the device when slave attaches to the master. */ + vidioc_int_dev_init_num = 1000, + /* Delinitialise the device at slave detach. */ + vidioc_int_dev_exit_num, + /* Set device power state: 0 is off, non-zero is on. */ + vidioc_int_s_power_num, + /* Get parallel interface clock speed for current settings. */ + vidioc_int_g_ext_clk_num, + /* + * Tell what the parallel interface clock speed actually is. + */ + vidioc_int_s_ext_clk_num, + /* Does the slave need to be reset after VIDIOC_DQBUF? */ + vidioc_int_g_needs_reset_num, + + /* + * + * VIDIOC_INT_* ioctls. + * + */ + /* VIDIOC_INT_RESET */ + vidioc_int_reset_num, + /* VIDIOC_INT_INIT */ + vidioc_int_init_num, + /* VIDIOC_INT_G_CHIP_IDENT */ + vidioc_int_g_chip_ident_num, + + /* + * + * Start of private ioctls. + * + */ + vidioc_int_priv_start_num = 2000, +}; + +struct v4l2_int_device; + +struct v4l2_int_master { + int (*attach)(struct v4l2_int_device *master, + struct v4l2_int_device *slave); + void (*detach)(struct v4l2_int_device *master); +}; + +typedef int (v4l2_int_ioctl_func)(struct v4l2_int_device *); +typedef int (v4l2_int_ioctl_func_0)(struct v4l2_int_device *); +typedef int (v4l2_int_ioctl_func_1)(struct v4l2_int_device *, void *); + +struct v4l2_int_ioctl_desc { + int num; + v4l2_int_ioctl_func *func; +}; + +struct v4l2_int_slave { + /* Don't touch master. */ + struct v4l2_int_device *master; + + char attach_to[V4L2NAMESIZE]; + + int num_ioctls; + struct v4l2_int_ioctl_desc *ioctls; +}; + +struct v4l2_int_device { + /* Don't touch head. */ + struct list_head head; + + struct module *module; + + char name[V4L2NAMESIZE]; + + enum v4l2_int_type type; + union { + struct v4l2_int_master *master; + struct v4l2_int_slave *slave; + } u; + + void *priv; +}; + +int v4l2_int_device_register(struct v4l2_int_device *d); +void v4l2_int_device_unregister(struct v4l2_int_device *d); + +int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd); +int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg); + +/* + * + * IOCTL wrapper functions for better type checking. + * + */ + +#define V4L2_INT_WRAPPER_0(name) \ + static inline int vidioc_int_##name(struct v4l2_int_device *d) \ + { \ + return v4l2_int_ioctl_0(d, vidioc_int_##name##_num); \ + } \ + \ + static inline struct v4l2_int_ioctl_desc \ + vidioc_int_##name##_cb(int (*func) \ + (struct v4l2_int_device *)) \ + { \ + struct v4l2_int_ioctl_desc desc; \ + \ + desc.num = vidioc_int_##name##_num; \ + desc.func = (v4l2_int_ioctl_func *)func; \ + \ + return desc; \ + } + +#define V4L2_INT_WRAPPER_1(name, arg_type, asterisk) \ + static inline int vidioc_int_##name(struct v4l2_int_device *d, \ + arg_type asterisk arg) \ + { \ + return v4l2_int_ioctl_1(d, vidioc_int_##name##_num, \ + (void *)(unsigned long)arg); \ + } \ + \ + static inline struct v4l2_int_ioctl_desc \ + vidioc_int_##name##_cb(int (*func) \ + (struct v4l2_int_device *, \ + arg_type asterisk)) \ + { \ + struct v4l2_int_ioctl_desc desc; \ + \ + desc.num = vidioc_int_##name##_num; \ + desc.func = (v4l2_int_ioctl_func *)func; \ + \ + return desc; \ + } + +V4L2_INT_WRAPPER_1(enum_fmt_cap, struct v4l2_fmtdesc, *); +V4L2_INT_WRAPPER_1(g_fmt_cap, struct v4l2_format, *); +V4L2_INT_WRAPPER_1(s_fmt_cap, struct v4l2_format, *); +V4L2_INT_WRAPPER_1(try_fmt_cap, struct v4l2_format, *); +V4L2_INT_WRAPPER_1(queryctrl, struct v4l2_queryctrl, *); +V4L2_INT_WRAPPER_1(g_ctrl, struct v4l2_control, *); +V4L2_INT_WRAPPER_1(s_ctrl, struct v4l2_control, *); +V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *); +V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *); + +V4L2_INT_WRAPPER_0(dev_init); +V4L2_INT_WRAPPER_0(dev_exit); +V4L2_INT_WRAPPER_1(s_power, int, ); +V4L2_INT_WRAPPER_1(s_ext_clk, u32, ); +V4L2_INT_WRAPPER_1(g_ext_clk, u32, *); +V4L2_INT_WRAPPER_1(g_needs_reset, void, *); + +V4L2_INT_WRAPPER_0(reset); +V4L2_INT_WRAPPER_0(init); +V4L2_INT_WRAPPER_1(g_chip_ident, int, *); + +#endif _