diff options
| author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-27 21:22:13 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-27 21:22:13 -0400 |
| commit | 12e56b601f66a415f88e7d60f6b6707a19c430c9 (patch) | |
| tree | c294adb1c5c1f80bbb2de82e6e41da97b3f5800f | |
| parent | ca6f8792bd5281ebaf04bf23a43ed486e5e453a9 (diff) | |
| parent | c78059f0a948404fb515db6be4bca5ec93eecd2a (diff) | |
Merge master.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb
* master.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb: (26 commits)
V4L/DVB (4263): Fix warning when compiling on 64 bit machines
V4L/DVB (4261): Included required header for in-kernel compilation
V4L/DVB (4260): Stradis.c: make 2 functions static
V4L/DVB (4259): Pass an explicit log prefix to cx2341x_log_status
V4L/DVB (4257): Fix 64-bit compile warnings.
V4L/DVB (4255): Tda9887 default TOP value is 0x10
V4L/DVB (4254): Remove obsoleted tuner_debug option.
V4L/DVB (4253): IVTV VBI format description too long.
V4L/DVB (4252): Remove duplicate 'tda9887' in info messages.
V4L/DVB (4245): Reduce the amount of pvrusb2-sourced noise going into the system log
V4L/DVB (4244): Implement use of cx2341x module in pvrusb2 driver
V4L/DVB (4243): Exploit new V4L control features in pvrusb2
V4L/DVB (4242): Don't suspend encoder when changing its attributes (in pvrusb2)
V4L/DVB (4241): Fix faulty encoder error recovery in pvrusb2
V4L/DVB (4240): Various V4L control enhancements in pvrusb2
V4L/DVB (4239): Handle boolean controls in pvrusb2
V4L/DVB (4238): Make sure flags field is initialized when quering a control in pvrusb2
V4L/DVB (4237): Move LOG_STATUS bracketing to a different part of the pvrusb2 driver
V4L/DVB (4236): Rearrange things in pvrusb2 driver in preparation for using cx2341x module
V4L/DVB (4235): Increase the maximum number of controls that pvrusb2-sysfs.c can handle.
...
57 files changed, 13528 insertions, 51 deletions
diff --git a/Documentation/video4linux/README.pvrusb2 b/Documentation/video4linux/README.pvrusb2 new file mode 100644 index 000000000000..c73a32c34528 --- /dev/null +++ b/Documentation/video4linux/README.pvrusb2 | |||
| @@ -0,0 +1,212 @@ | |||
| 1 | |||
| 2 | $Id$ | ||
| 3 | Mike Isely <isely@pobox.com> | ||
| 4 | |||
| 5 | pvrusb2 driver | ||
| 6 | |||
| 7 | Background: | ||
| 8 | |||
| 9 | This driver is intended for the "Hauppauge WinTV PVR USB 2.0", which | ||
| 10 | is a USB 2.0 hosted TV Tuner. This driver is a work in progress. | ||
| 11 | Its history started with the reverse-engineering effort by Björn | ||
| 12 | Danielsson <pvrusb2@dax.nu> whose web page can be found here: | ||
| 13 | |||
| 14 | http://pvrusb2.dax.nu/ | ||
| 15 | |||
| 16 | From there Aurelien Alleaume <slts@free.fr> began an effort to | ||
| 17 | create a video4linux compatible driver. I began with Aurelien's | ||
| 18 | last known snapshot and evolved the driver to the state it is in | ||
| 19 | here. | ||
| 20 | |||
| 21 | More information on this driver can be found at: | ||
| 22 | |||
| 23 | http://www.isely.net/pvrusb2.html | ||
| 24 | |||
| 25 | |||
| 26 | This driver has a strong separation of layers. They are very | ||
| 27 | roughly: | ||
| 28 | |||
| 29 | 1a. Low level wire-protocol implementation with the device. | ||
| 30 | |||
| 31 | 1b. I2C adaptor implementation and corresponding I2C client drivers | ||
| 32 | implemented elsewhere in V4L. | ||
| 33 | |||
| 34 | 1c. High level hardware driver implementation which coordinates all | ||
| 35 | activities that ensure correct operation of the device. | ||
| 36 | |||
| 37 | 2. A "context" layer which manages instancing of driver, setup, | ||
| 38 | tear-down, arbitration, and interaction with high level | ||
| 39 | interfaces appropriately as devices are hotplugged in the | ||
| 40 | system. | ||
| 41 | |||
| 42 | 3. High level interfaces which glue the driver to various published | ||
| 43 | Linux APIs (V4L, sysfs, maybe DVB in the future). | ||
| 44 | |||
| 45 | The most important shearing layer is between the top 2 layers. A | ||
| 46 | lot of work went into the driver to ensure that any kind of | ||
| 47 | conceivable API can be laid on top of the core driver. (Yes, the | ||
| 48 | driver internally leverages V4L to do its work but that really has | ||
| 49 | nothing to do with the API published by the driver to the outside | ||
| 50 | world.) The architecture allows for different APIs to | ||
| 51 | simultaneously access the driver. I have a strong sense of fairness | ||
| 52 | about APIs and also feel that it is a good design principle to keep | ||
| 53 | implementation and interface isolated from each other. Thus while | ||
| 54 | right now the V4L high level interface is the most complete, the | ||
| 55 | sysfs high level interface will work equally well for similar | ||
| 56 | functions, and there's no reason I see right now why it shouldn't be | ||
| 57 | possible to produce a DVB high level interface that can sit right | ||
| 58 | alongside V4L. | ||
| 59 | |||
| 60 | NOTE: Complete documentation on the pvrusb2 driver is contained in | ||
| 61 | the html files within the doc directory; these are exactly the same | ||
| 62 | as what is on the web site at the time. Browse those files | ||
| 63 | (especially the FAQ) before asking questions. | ||
| 64 | |||
| 65 | |||
| 66 | Building | ||
| 67 | |||
| 68 | To build these modules essentially amounts to just running "Make", | ||
| 69 | but you need the kernel source tree nearby and you will likely also | ||
| 70 | want to set a few controlling environment variables first in order | ||
| 71 | to link things up with that source tree. Please see the Makefile | ||
| 72 | here for comments that explain how to do that. | ||
| 73 | |||
| 74 | |||
| 75 | Source file list / functional overview: | ||
| 76 | |||
| 77 | (Note: The term "module" used below generally refers to loosely | ||
| 78 | defined functional units within the pvrusb2 driver and bears no | ||
| 79 | relation to the Linux kernel's concept of a loadable module.) | ||
| 80 | |||
| 81 | pvrusb2-audio.[ch] - This is glue logic that resides between this | ||
| 82 | driver and the msp3400.ko I2C client driver (which is found | ||
| 83 | elsewhere in V4L). | ||
| 84 | |||
| 85 | pvrusb2-context.[ch] - This module implements the context for an | ||
| 86 | instance of the driver. Everything else eventually ties back to | ||
| 87 | or is otherwise instanced within the data structures implemented | ||
| 88 | here. Hotplugging is ultimately coordinated here. All high level | ||
| 89 | interfaces tie into the driver through this module. This module | ||
| 90 | helps arbitrate each interface's access to the actual driver core, | ||
| 91 | and is designed to allow concurrent access through multiple | ||
| 92 | instances of multiple interfaces (thus you can for example change | ||
| 93 | the tuner's frequency through sysfs while simultaneously streaming | ||
| 94 | video through V4L out to an instance of mplayer). | ||
| 95 | |||
| 96 | pvrusb2-debug.h - This header defines a printk() wrapper and a mask | ||
| 97 | of debugging bit definitions for the various kinds of debug | ||
| 98 | messages that can be enabled within the driver. | ||
| 99 | |||
| 100 | pvrusb2-debugifc.[ch] - This module implements a crude command line | ||
| 101 | oriented debug interface into the driver. Aside from being part | ||
| 102 | of the process for implementing manual firmware extraction (see | ||
| 103 | the pvrusb2 web site mentioned earlier), probably I'm the only one | ||
| 104 | who has ever used this. It is mainly a debugging aid. | ||
| 105 | |||
| 106 | pvrusb2-eeprom.[ch] - This is glue logic that resides between this | ||
| 107 | driver the tveeprom.ko module, which is itself implemented | ||
| 108 | elsewhere in V4L. | ||
| 109 | |||
| 110 | pvrusb2-encoder.[ch] - This module implements all protocol needed to | ||
| 111 | interact with the Conexant mpeg2 encoder chip within the pvrusb2 | ||
| 112 | device. It is a crude echo of corresponding logic in ivtv, | ||
| 113 | however the design goals (strict isolation) and physical layer | ||
| 114 | (proxy through USB instead of PCI) are enough different that this | ||
| 115 | implementation had to be completely different. | ||
| 116 | |||
| 117 | pvrusb2-hdw-internal.h - This header defines the core data structure | ||
| 118 | in the driver used to track ALL internal state related to control | ||
| 119 | of the hardware. Nobody outside of the core hardware-handling | ||
| 120 | modules should have any business using this header. All external | ||
| 121 | access to the driver should be through one of the high level | ||
| 122 | interfaces (e.g. V4L, sysfs, etc), and in fact even those high | ||
| 123 | level interfaces are restricted to the API defined in | ||
| 124 | pvrusb2-hdw.h and NOT this header. | ||
| 125 | |||
| 126 | pvrusb2-hdw.h - This header defines the full internal API for | ||
| 127 | controlling the hardware. High level interfaces (e.g. V4L, sysfs) | ||
| 128 | will work through here. | ||
| 129 | |||
| 130 | pvrusb2-hdw.c - This module implements all the various bits of logic | ||
| 131 | that handle overall control of a specific pvrusb2 device. | ||
| 132 | (Policy, instantiation, and arbitration of pvrusb2 devices fall | ||
| 133 | within the jurisdiction of pvrusb-context not here). | ||
| 134 | |||
| 135 | pvrusb2-i2c-chips-*.c - These modules implement the glue logic to | ||
| 136 | tie together and configure various I2C modules as they attach to | ||
| 137 | the I2C bus. There are two versions of this file. The "v4l2" | ||
| 138 | version is intended to be used in-tree alongside V4L, where we | ||
| 139 | implement just the logic that makes sense for a pure V4L | ||
| 140 | environment. The "all" version is intended for use outside of | ||
| 141 | V4L, where we might encounter other possibly "challenging" modules | ||
| 142 | from ivtv or older kernel snapshots (or even the support modules | ||
| 143 | in the standalone snapshot). | ||
| 144 | |||
| 145 | pvrusb2-i2c-cmd-v4l1.[ch] - This module implements generic V4L1 | ||
| 146 | compatible commands to the I2C modules. It is here where state | ||
| 147 | changes inside the pvrusb2 driver are translated into V4L1 | ||
| 148 | commands that are in turn send to the various I2C modules. | ||
| 149 | |||
| 150 | pvrusb2-i2c-cmd-v4l2.[ch] - This module implements generic V4L2 | ||
| 151 | compatible commands to the I2C modules. It is here where state | ||
| 152 | changes inside the pvrusb2 driver are translated into V4L2 | ||
| 153 | commands that are in turn send to the various I2C modules. | ||
| 154 | |||
| 155 | pvrusb2-i2c-core.[ch] - This module provides an implementation of a | ||
| 156 | kernel-friendly I2C adaptor driver, through which other external | ||
| 157 | I2C client drivers (e.g. msp3400, tuner, lirc) may connect and | ||
| 158 | operate corresponding chips within the the pvrusb2 device. It is | ||
| 159 | through here that other V4L modules can reach into this driver to | ||
| 160 | operate specific pieces (and those modules are in turn driven by | ||
| 161 | glue logic which is coordinated by pvrusb2-hdw, doled out by | ||
| 162 | pvrusb2-context, and then ultimately made available to users | ||
| 163 | through one of the high level interfaces). | ||
| 164 | |||
| 165 | pvrusb2-io.[ch] - This module implements a very low level ring of | ||
| 166 | transfer buffers, required in order to stream data from the | ||
| 167 | device. This module is *very* low level. It only operates the | ||
| 168 | buffers and makes no attempt to define any policy or mechanism for | ||
| 169 | how such buffers might be used. | ||
| 170 | |||
| 171 | pvrusb2-ioread.[ch] - This module layers on top of pvrusb2-io.[ch] | ||
| 172 | to provide a streaming API usable by a read() system call style of | ||
| 173 | I/O. Right now this is the only layer on top of pvrusb2-io.[ch], | ||
| 174 | however the underlying architecture here was intended to allow for | ||
| 175 | other styles of I/O to be implemented with additonal modules, like | ||
| 176 | mmap()'ed buffers or something even more exotic. | ||
| 177 | |||
| 178 | pvrusb2-main.c - This is the top level of the driver. Module level | ||
| 179 | and USB core entry points are here. This is our "main". | ||
| 180 | |||
| 181 | pvrusb2-sysfs.[ch] - This is the high level interface which ties the | ||
| 182 | pvrusb2 driver into sysfs. Through this interface you can do | ||
| 183 | everything with the driver except actually stream data. | ||
| 184 | |||
| 185 | pvrusb2-tuner.[ch] - This is glue logic that resides between this | ||
| 186 | driver and the tuner.ko I2C client driver (which is found | ||
| 187 | elsewhere in V4L). | ||
| 188 | |||
| 189 | pvrusb2-util.h - This header defines some common macros used | ||
| 190 | throughout the driver. These macros are not really specific to | ||
| 191 | the driver, but they had to go somewhere. | ||
| 192 | |||
| 193 | pvrusb2-v4l2.[ch] - This is the high level interface which ties the | ||
| 194 | pvrusb2 driver into video4linux. It is through here that V4L | ||
| 195 | applications can open and operate the driver in the usual V4L | ||
| 196 | ways. Note that **ALL** V4L functionality is published only | ||
| 197 | through here and nowhere else. | ||
| 198 | |||
| 199 | pvrusb2-video-*.[ch] - This is glue logic that resides between this | ||
| 200 | driver and the saa711x.ko I2C client driver (which is found | ||
| 201 | elsewhere in V4L). Note that saa711x.ko used to be known as | ||
| 202 | saa7115.ko in ivtv. There are two versions of this; one is | ||
| 203 | selected depending on the particular saa711[5x].ko that is found. | ||
| 204 | |||
| 205 | pvrusb2.h - This header contains compile time tunable parameters | ||
| 206 | (and at the moment the driver has very little that needs to be | ||
| 207 | tuned). | ||
| 208 | |||
| 209 | |||
| 210 | -Mike Isely | ||
| 211 | isely@pobox.com | ||
| 212 | |||
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index e4290491fa9e..6d532f170ce5 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig | |||
| @@ -445,6 +445,8 @@ endmenu # encoder / decoder chips | |||
| 445 | menu "V4L USB devices" | 445 | menu "V4L USB devices" |
| 446 | depends on USB && VIDEO_DEV | 446 | depends on USB && VIDEO_DEV |
| 447 | 447 | ||
| 448 | source "drivers/media/video/pvrusb2/Kconfig" | ||
| 449 | |||
| 448 | source "drivers/media/video/em28xx/Kconfig" | 450 | source "drivers/media/video/em28xx/Kconfig" |
| 449 | 451 | ||
| 450 | config USB_DSBR | 452 | config USB_DSBR |
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 6c401b46398a..353d61cfac1b 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile | |||
| @@ -47,6 +47,7 @@ obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/ | |||
| 47 | obj-$(CONFIG_VIDEO_CX88) += cx88/ | 47 | obj-$(CONFIG_VIDEO_CX88) += cx88/ |
| 48 | obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ | 48 | obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ |
| 49 | obj-$(CONFIG_VIDEO_EM28XX) += tvp5150.o | 49 | obj-$(CONFIG_VIDEO_EM28XX) += tvp5150.o |
| 50 | obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ | ||
| 50 | obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o | 51 | obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o |
| 51 | obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o | 52 | obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o |
| 52 | obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o | 53 | obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o |
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index 01b22eab5725..65f00fc08fa9 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c | |||
| @@ -601,7 +601,7 @@ static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params) | |||
| 601 | } | 601 | } |
| 602 | 602 | ||
| 603 | int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, | 603 | int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, |
| 604 | struct v4l2_ext_controls *ctrls, int cmd) | 604 | struct v4l2_ext_controls *ctrls, unsigned int cmd) |
| 605 | { | 605 | { |
| 606 | int err = 0; | 606 | int err = 0; |
| 607 | int i; | 607 | int i; |
| @@ -847,22 +847,22 @@ invalid: | |||
| 847 | return "<invalid>"; | 847 | return "<invalid>"; |
| 848 | } | 848 | } |
| 849 | 849 | ||
| 850 | void cx2341x_log_status(struct cx2341x_mpeg_params *p, int card_id) | 850 | void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix) |
| 851 | { | 851 | { |
| 852 | int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; | 852 | int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; |
| 853 | 853 | ||
| 854 | /* Stream */ | 854 | /* Stream */ |
| 855 | printk(KERN_INFO "cx2341x-%d: Stream: %s\n", | 855 | printk(KERN_INFO "%s: Stream: %s\n", |
| 856 | card_id, | 856 | prefix, |
| 857 | cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); | 857 | cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); |
| 858 | 858 | ||
| 859 | /* Video */ | 859 | /* Video */ |
| 860 | printk(KERN_INFO "cx2341x-%d: Video: %dx%d, %d fps\n", | 860 | printk(KERN_INFO "%s: Video: %dx%d, %d fps\n", |
| 861 | card_id, | 861 | prefix, |
| 862 | p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1), | 862 | p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1), |
| 863 | p->is_50hz ? 25 : 30); | 863 | p->is_50hz ? 25 : 30); |
| 864 | printk(KERN_INFO "cx2341x-%d: Video: %s, %s, %s, %d", | 864 | printk(KERN_INFO "%s: Video: %s, %s, %s, %d", |
| 865 | card_id, | 865 | prefix, |
| 866 | cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING), | 866 | cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING), |
| 867 | cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT), | 867 | cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT), |
| 868 | cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE), | 868 | cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE), |
| @@ -871,19 +871,19 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, int card_id) | |||
| 871 | printk(", Peak %d", p->video_bitrate_peak); | 871 | printk(", Peak %d", p->video_bitrate_peak); |
| 872 | } | 872 | } |
| 873 | printk("\n"); | 873 | printk("\n"); |
| 874 | printk(KERN_INFO "cx2341x-%d: Video: GOP Size %d, %d B-Frames, %sGOP Closure, %s3:2 Pulldown\n", | 874 | printk(KERN_INFO "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure, %s3:2 Pulldown\n", |
| 875 | card_id, | 875 | prefix, |
| 876 | p->video_gop_size, p->video_b_frames, | 876 | p->video_gop_size, p->video_b_frames, |
| 877 | p->video_gop_closure ? "" : "No ", | 877 | p->video_gop_closure ? "" : "No ", |
| 878 | p->video_pulldown ? "" : "No "); | 878 | p->video_pulldown ? "" : "No "); |
| 879 | if (p->video_temporal_decimation) { | 879 | if (p->video_temporal_decimation) { |
| 880 | printk(KERN_INFO "cx2341x-%d: Video: Temporal Decimation %d\n", | 880 | printk(KERN_INFO "%s: Video: Temporal Decimation %d\n", |
| 881 | card_id, p->video_temporal_decimation); | 881 | prefix, p->video_temporal_decimation); |
| 882 | } | 882 | } |
| 883 | 883 | ||
| 884 | /* Audio */ | 884 | /* Audio */ |
| 885 | printk(KERN_INFO "cx2341x-%d: Audio: %s, %s, %s, %s", | 885 | printk(KERN_INFO "%s: Audio: %s, %s, %s, %s", |
| 886 | card_id, | 886 | prefix, |
| 887 | cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), | 887 | cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), |
| 888 | cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), | 888 | cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), |
| 889 | cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE), | 889 | cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE), |
| @@ -897,18 +897,18 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, int card_id) | |||
| 897 | cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC)); | 897 | cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC)); |
| 898 | 898 | ||
| 899 | /* Encoding filters */ | 899 | /* Encoding filters */ |
| 900 | printk(KERN_INFO "cx2341x-%d: Spatial Filter: %s, Luma %s, Chroma %s, %d\n", | 900 | printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n", |
| 901 | card_id, | 901 | prefix, |
| 902 | cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE), | 902 | cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE), |
| 903 | cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE), | 903 | cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE), |
| 904 | cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE), | 904 | cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE), |
| 905 | p->video_spatial_filter); | 905 | p->video_spatial_filter); |
| 906 | printk(KERN_INFO "cx2341x-%d: Temporal Filter: %s, %d\n", | 906 | printk(KERN_INFO "%s: Temporal Filter: %s, %d\n", |
| 907 | card_id, | 907 | prefix, |
| 908 | cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE), | 908 | cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE), |
| 909 | p->video_temporal_filter); | 909 | p->video_temporal_filter); |
| 910 | printk(KERN_INFO "cx2341x-%d: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n", | 910 | printk(KERN_INFO "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n", |
| 911 | card_id, | 911 | prefix, |
| 912 | cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE), | 912 | cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE), |
| 913 | p->video_luma_median_filter_bottom, | 913 | p->video_luma_median_filter_bottom, |
| 914 | p->video_luma_median_filter_top, | 914 | p->video_luma_median_filter_top, |
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 78df66671ea2..4ff81582ec56 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c | |||
| @@ -853,6 +853,19 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file, | |||
| 853 | fh->mpegq.field); | 853 | fh->mpegq.field); |
| 854 | return 0; | 854 | return 0; |
| 855 | } | 855 | } |
| 856 | case VIDIOC_LOG_STATUS: | ||
| 857 | { | ||
| 858 | char name[32 + 2]; | ||
| 859 | |||
| 860 | snprintf(name, sizeof(name), "%s/2", core->name); | ||
| 861 | printk("%s/2: ============ START LOG STATUS ============\n", | ||
| 862 | core->name); | ||
| 863 | cx88_call_i2c_clients(core, VIDIOC_LOG_STATUS, 0); | ||
| 864 | cx2341x_log_status(&dev->params, name); | ||
| 865 | printk("%s/2: ============= END LOG STATUS =============\n", | ||
| 866 | core->name); | ||
| 867 | return 0; | ||
| 868 | } | ||
| 856 | 869 | ||
| 857 | default: | 870 | default: |
| 858 | return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl); | 871 | return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl); |
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig new file mode 100644 index 000000000000..7e727fe14b32 --- /dev/null +++ b/drivers/media/video/pvrusb2/Kconfig | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | config VIDEO_PVRUSB2 | ||
| 2 | tristate "Hauppauge WinTV-PVR USB2 support" | ||
| 3 | depends on VIDEO_V4L2 && USB && I2C && EXPERIMENTAL | ||
| 4 | select FW_LOADER | ||
| 5 | select VIDEO_TUNER | ||
| 6 | select VIDEO_TVEEPROM | ||
| 7 | select VIDEO_CX2341X | ||
| 8 | select VIDEO_SAA711X | ||
| 9 | select VIDEO_MSP3400 | ||
| 10 | ---help--- | ||
| 11 | This is a video4linux driver for Conexant 23416 based | ||
| 12 | usb2 personal video recorder devices. | ||
| 13 | |||
| 14 | To compile this driver as a module, choose M here: the | ||
| 15 | module will be called pvrusb2 | ||
| 16 | |||
| 17 | config VIDEO_PVRUSB2_24XXX | ||
| 18 | bool "Hauppauge WinTV-PVR USB2 support for 24xxx model series" | ||
| 19 | depends on VIDEO_PVRUSB2 && EXPERIMENTAL | ||
| 20 | select VIDEO_CX25840 | ||
| 21 | select VIDEO_WM8775 | ||
| 22 | ---help--- | ||
| 23 | This option enables inclusion of additional logic to operate | ||
| 24 | newer WinTV-PVR USB2 devices whose model number is of the | ||
| 25 | form "24xxx" (leading prefix of "24" followed by 3 digits). | ||
| 26 | To see if you may need this option, examine the white | ||
| 27 | sticker on the underside of your device. Enabling this | ||
| 28 | option will not harm support for older devices, however it | ||
| 29 | is a separate option because of the experimental nature of | ||
| 30 | this new feature. | ||
| 31 | |||
| 32 | If you are in doubt, say N. | ||
| 33 | |||
| 34 | Note: This feature is _very_ experimental. You have been | ||
| 35 | warned. | ||
| 36 | |||
| 37 | config VIDEO_PVRUSB2_SYSFS | ||
| 38 | bool "pvrusb2 sysfs support (EXPERIMENTAL)" | ||
| 39 | default y | ||
| 40 | depends on VIDEO_PVRUSB2 && SYSFS && EXPERIMENTAL | ||
| 41 | ---help--- | ||
| 42 | This option enables the operation of a sysfs based | ||
| 43 | interface for query and control of the pvrusb2 driver. | ||
| 44 | |||
| 45 | This is not generally needed for v4l applications, | ||
| 46 | although certain applications are optimized to take | ||
| 47 | advantage of this feature. | ||
| 48 | |||
| 49 | If you are in doubt, say Y. | ||
| 50 | |||
| 51 | Note: This feature is experimental and subject to change. | ||
| 52 | |||
| 53 | config VIDEO_PVRUSB2_DEBUGIFC | ||
| 54 | bool "pvrusb2 debug interface" | ||
| 55 | depends on VIDEO_PVRUSB2_SYSFS | ||
| 56 | ---help--- | ||
| 57 | This option enables the inclusion of a debug interface | ||
| 58 | in the pvrusb2 driver, hosted through sysfs. | ||
| 59 | |||
| 60 | You do not need to select this option unless you plan | ||
| 61 | on debugging the driver or performing a manual firmware | ||
| 62 | extraction. | ||
diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile new file mode 100644 index 000000000000..fed603ad0a67 --- /dev/null +++ b/drivers/media/video/pvrusb2/Makefile | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o | ||
| 2 | obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o | ||
| 3 | |||
| 4 | obj-pvrusb2-24xxx-$(CONFIG_VIDEO_PVRUSB2_24XXX) := \ | ||
| 5 | pvrusb2-cx2584x-v4l.o \ | ||
| 6 | pvrusb2-wm8775.o | ||
| 7 | |||
| 8 | pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ | ||
| 9 | pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \ | ||
| 10 | pvrusb2-encoder.o pvrusb2-video-v4l.o \ | ||
| 11 | pvrusb2-eeprom.o pvrusb2-tuner.o pvrusb2-demod.o \ | ||
| 12 | pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ | ||
| 13 | pvrusb2-ctrl.o pvrusb2-std.o \ | ||
| 14 | pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ | ||
| 15 | $(obj-pvrusb2-24xxx-y) \ | ||
| 16 | $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) | ||
| 17 | |||
| 18 | obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c new file mode 100644 index 000000000000..313d2dcf9e4b --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include "pvrusb2-audio.h" | ||
| 24 | #include "pvrusb2-hdw-internal.h" | ||
| 25 | #include "pvrusb2-debug.h" | ||
| 26 | #include <linux/videodev2.h> | ||
| 27 | #include <media/msp3400.h> | ||
| 28 | #include <media/v4l2-common.h> | ||
| 29 | |||
| 30 | struct pvr2_msp3400_handler { | ||
| 31 | struct pvr2_hdw *hdw; | ||
| 32 | struct pvr2_i2c_client *client; | ||
| 33 | struct pvr2_i2c_handler i2c_handler; | ||
| 34 | struct pvr2_audio_stat astat; | ||
| 35 | unsigned long stale_mask; | ||
| 36 | }; | ||
| 37 | |||
| 38 | |||
| 39 | /* This function selects the correct audio input source */ | ||
| 40 | static void set_stereo(struct pvr2_msp3400_handler *ctxt) | ||
| 41 | { | ||
| 42 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 43 | struct v4l2_routing route; | ||
| 44 | |||
| 45 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo"); | ||
| 46 | |||
| 47 | if (hdw->input_val == PVR2_CVAL_INPUT_TV) { | ||
| 48 | struct v4l2_tuner vt; | ||
| 49 | memset(&vt,0,sizeof(vt)); | ||
| 50 | vt.audmode = hdw->audiomode_val; | ||
| 51 | pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_TUNER,&vt); | ||
| 52 | } | ||
| 53 | |||
| 54 | route.input = MSP_INPUT_DEFAULT; | ||
| 55 | route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); | ||
| 56 | switch (hdw->input_val) { | ||
| 57 | case PVR2_CVAL_INPUT_TV: | ||
| 58 | break; | ||
| 59 | case PVR2_CVAL_INPUT_RADIO: | ||
| 60 | /* Assume that msp34xx also handle FM decoding, in which case | ||
| 61 | we're still using the tuner. */ | ||
| 62 | /* HV: actually it is more likely to be the SCART2 input if | ||
| 63 | the ivtv experience is any indication. */ | ||
| 64 | route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, | ||
| 65 | MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); | ||
| 66 | break; | ||
| 67 | case PVR2_CVAL_INPUT_SVIDEO: | ||
| 68 | case PVR2_CVAL_INPUT_COMPOSITE: | ||
| 69 | /* SCART 1 input */ | ||
| 70 | route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, | ||
| 71 | MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); | ||
| 72 | break; | ||
| 73 | } | ||
| 74 | pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); | ||
| 75 | } | ||
| 76 | |||
| 77 | |||
| 78 | static int check_stereo(struct pvr2_msp3400_handler *ctxt) | ||
| 79 | { | ||
| 80 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 81 | return (hdw->input_dirty || | ||
| 82 | hdw->audiomode_dirty); | ||
| 83 | } | ||
| 84 | |||
| 85 | |||
| 86 | struct pvr2_msp3400_ops { | ||
| 87 | void (*update)(struct pvr2_msp3400_handler *); | ||
| 88 | int (*check)(struct pvr2_msp3400_handler *); | ||
| 89 | }; | ||
| 90 | |||
| 91 | |||
| 92 | static const struct pvr2_msp3400_ops msp3400_ops[] = { | ||
| 93 | { .update = set_stereo, .check = check_stereo}, | ||
| 94 | }; | ||
| 95 | |||
| 96 | |||
| 97 | static int msp3400_check(struct pvr2_msp3400_handler *ctxt) | ||
| 98 | { | ||
| 99 | unsigned long msk; | ||
| 100 | unsigned int idx; | ||
| 101 | |||
| 102 | for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]); | ||
| 103 | idx++) { | ||
| 104 | msk = 1 << idx; | ||
| 105 | if (ctxt->stale_mask & msk) continue; | ||
| 106 | if (msp3400_ops[idx].check(ctxt)) { | ||
| 107 | ctxt->stale_mask |= msk; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | return ctxt->stale_mask != 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | |||
| 114 | static void msp3400_update(struct pvr2_msp3400_handler *ctxt) | ||
| 115 | { | ||
| 116 | unsigned long msk; | ||
| 117 | unsigned int idx; | ||
| 118 | |||
| 119 | for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]); | ||
| 120 | idx++) { | ||
| 121 | msk = 1 << idx; | ||
| 122 | if (!(ctxt->stale_mask & msk)) continue; | ||
| 123 | ctxt->stale_mask &= ~msk; | ||
| 124 | msp3400_ops[idx].update(ctxt); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | |||
| 129 | /* This reads back the current signal type */ | ||
| 130 | static int get_audio_status(struct pvr2_msp3400_handler *ctxt) | ||
| 131 | { | ||
| 132 | struct v4l2_tuner vt; | ||
| 133 | int stat; | ||
| 134 | |||
| 135 | memset(&vt,0,sizeof(vt)); | ||
| 136 | stat = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); | ||
| 137 | if (stat < 0) return stat; | ||
| 138 | |||
| 139 | ctxt->hdw->flag_stereo = (vt.audmode & V4L2_TUNER_MODE_STEREO) != 0; | ||
| 140 | ctxt->hdw->flag_bilingual = | ||
| 141 | (vt.audmode & V4L2_TUNER_MODE_LANG2) != 0; | ||
| 142 | return 0; | ||
| 143 | } | ||
| 144 | |||
| 145 | |||
| 146 | static void pvr2_msp3400_detach(struct pvr2_msp3400_handler *ctxt) | ||
| 147 | { | ||
| 148 | ctxt->client->handler = 0; | ||
| 149 | ctxt->hdw->audio_stat = 0; | ||
| 150 | kfree(ctxt); | ||
| 151 | } | ||
| 152 | |||
| 153 | |||
| 154 | static unsigned int pvr2_msp3400_describe(struct pvr2_msp3400_handler *ctxt, | ||
| 155 | char *buf,unsigned int cnt) | ||
| 156 | { | ||
| 157 | return scnprintf(buf,cnt,"handler: pvrusb2-audio v4l2"); | ||
| 158 | } | ||
| 159 | |||
| 160 | |||
| 161 | const static struct pvr2_i2c_handler_functions msp3400_funcs = { | ||
| 162 | .detach = (void (*)(void *))pvr2_msp3400_detach, | ||
| 163 | .check = (int (*)(void *))msp3400_check, | ||
| 164 | .update = (void (*)(void *))msp3400_update, | ||
| 165 | .describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_msp3400_describe, | ||
| 166 | }; | ||
| 167 | |||
| 168 | |||
| 169 | int pvr2_i2c_msp3400_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) | ||
| 170 | { | ||
| 171 | struct pvr2_msp3400_handler *ctxt; | ||
| 172 | if (hdw->audio_stat) return 0; | ||
| 173 | if (cp->handler) return 0; | ||
| 174 | |||
| 175 | ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); | ||
| 176 | if (!ctxt) return 0; | ||
| 177 | memset(ctxt,0,sizeof(*ctxt)); | ||
| 178 | |||
| 179 | ctxt->i2c_handler.func_data = ctxt; | ||
| 180 | ctxt->i2c_handler.func_table = &msp3400_funcs; | ||
| 181 | ctxt->client = cp; | ||
| 182 | ctxt->hdw = hdw; | ||
| 183 | ctxt->astat.ctxt = ctxt; | ||
| 184 | ctxt->astat.status = (int (*)(void *))get_audio_status; | ||
| 185 | ctxt->astat.detach = (void (*)(void *))pvr2_msp3400_detach; | ||
| 186 | ctxt->stale_mask = (1 << (sizeof(msp3400_ops)/ | ||
| 187 | sizeof(msp3400_ops[0]))) - 1; | ||
| 188 | cp->handler = &ctxt->i2c_handler; | ||
| 189 | hdw->audio_stat = &ctxt->astat; | ||
| 190 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x msp3400 V4L2 handler set up", | ||
| 191 | cp->client->addr); | ||
| 192 | return !0; | ||
| 193 | } | ||
| 194 | |||
| 195 | |||
| 196 | /* | ||
| 197 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 198 | *** Local Variables: *** | ||
| 199 | *** mode: c *** | ||
| 200 | *** fill-column: 70 *** | ||
| 201 | *** tab-width: 8 *** | ||
| 202 | *** c-basic-offset: 8 *** | ||
| 203 | *** End: *** | ||
| 204 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.h b/drivers/media/video/pvrusb2/pvrusb2-audio.h new file mode 100644 index 000000000000..536339b68843 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef __PVRUSB2_AUDIO_H | ||
| 24 | #define __PVRUSB2_AUDIO_H | ||
| 25 | |||
| 26 | #include "pvrusb2-i2c-core.h" | ||
| 27 | |||
| 28 | int pvr2_i2c_msp3400_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); | ||
| 29 | |||
| 30 | #endif /* __PVRUSB2_AUDIO_H */ | ||
| 31 | |||
| 32 | /* | ||
| 33 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 34 | *** Local Variables: *** | ||
| 35 | *** mode: c *** | ||
| 36 | *** fill-column: 70 *** | ||
| 37 | *** tab-width: 8 *** | ||
| 38 | *** c-basic-offset: 8 *** | ||
| 39 | *** End: *** | ||
| 40 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c new file mode 100644 index 000000000000..40dc59871a45 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-context.c | |||
| @@ -0,0 +1,230 @@ | |||
| 1 | /* | ||
| 2 | * $Id$ | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 18 | * | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include "pvrusb2-context.h" | ||
| 22 | #include "pvrusb2-io.h" | ||
| 23 | #include "pvrusb2-ioread.h" | ||
| 24 | #include "pvrusb2-hdw.h" | ||
| 25 | #include "pvrusb2-debug.h" | ||
| 26 | #include <linux/errno.h> | ||
| 27 | #include <linux/string.h> | ||
| 28 | #include <linux/slab.h> | ||
| 29 | #include <asm/semaphore.h> | ||
| 30 | |||
| 31 | |||
| 32 | static void pvr2_context_destroy(struct pvr2_context *mp) | ||
| 33 | { | ||
| 34 | if (mp->hdw) pvr2_hdw_destroy(mp->hdw); | ||
| 35 | pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp); | ||
| 36 | flush_workqueue(mp->workqueue); | ||
| 37 | destroy_workqueue(mp->workqueue); | ||
| 38 | kfree(mp); | ||
| 39 | } | ||
| 40 | |||
| 41 | |||
| 42 | static void pvr2_context_trigger_poll(struct pvr2_context *mp) | ||
| 43 | { | ||
| 44 | queue_work(mp->workqueue,&mp->workpoll); | ||
| 45 | } | ||
| 46 | |||
| 47 | |||
| 48 | static void pvr2_context_poll(struct pvr2_context *mp) | ||
| 49 | { | ||
| 50 | pvr2_context_enter(mp); do { | ||
| 51 | pvr2_hdw_poll(mp->hdw); | ||
| 52 | } while (0); pvr2_context_exit(mp); | ||
| 53 | } | ||
| 54 | |||
| 55 | |||
| 56 | static void pvr2_context_setup(struct pvr2_context *mp) | ||
| 57 | { | ||
| 58 | pvr2_context_enter(mp); do { | ||
| 59 | if (!pvr2_hdw_dev_ok(mp->hdw)) break; | ||
| 60 | pvr2_hdw_setup(mp->hdw); | ||
| 61 | pvr2_hdw_setup_poll_trigger( | ||
| 62 | mp->hdw, | ||
| 63 | (void (*)(void *))pvr2_context_trigger_poll, | ||
| 64 | mp); | ||
| 65 | if (!pvr2_hdw_dev_ok(mp->hdw)) break; | ||
| 66 | if (!pvr2_hdw_init_ok(mp->hdw)) break; | ||
| 67 | mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw); | ||
| 68 | if (mp->setup_func) { | ||
| 69 | mp->setup_func(mp); | ||
| 70 | } | ||
| 71 | } while (0); pvr2_context_exit(mp); | ||
| 72 | } | ||
| 73 | |||
| 74 | |||
| 75 | struct pvr2_context *pvr2_context_create( | ||
| 76 | struct usb_interface *intf, | ||
| 77 | const struct usb_device_id *devid, | ||
| 78 | void (*setup_func)(struct pvr2_context *)) | ||
| 79 | { | ||
| 80 | struct pvr2_context *mp = 0; | ||
| 81 | mp = kmalloc(sizeof(*mp),GFP_KERNEL); | ||
| 82 | if (!mp) goto done; | ||
| 83 | memset(mp,0,sizeof(*mp)); | ||
| 84 | pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_main id=%p",mp); | ||
| 85 | mp->setup_func = setup_func; | ||
| 86 | mutex_init(&mp->mutex); | ||
| 87 | mp->hdw = pvr2_hdw_create(intf,devid); | ||
| 88 | if (!mp->hdw) { | ||
| 89 | pvr2_context_destroy(mp); | ||
| 90 | mp = 0; | ||
| 91 | goto done; | ||
| 92 | } | ||
| 93 | |||
| 94 | mp->workqueue = create_singlethread_workqueue("pvrusb2"); | ||
| 95 | INIT_WORK(&mp->workinit,(void (*)(void*))pvr2_context_setup,mp); | ||
| 96 | INIT_WORK(&mp->workpoll,(void (*)(void*))pvr2_context_poll,mp); | ||
| 97 | queue_work(mp->workqueue,&mp->workinit); | ||
| 98 | done: | ||
| 99 | return mp; | ||
| 100 | } | ||
| 101 | |||
| 102 | |||
| 103 | void pvr2_context_enter(struct pvr2_context *mp) | ||
| 104 | { | ||
| 105 | mutex_lock(&mp->mutex); | ||
| 106 | pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_enter(id=%p)",mp); | ||
| 107 | } | ||
| 108 | |||
| 109 | |||
| 110 | void pvr2_context_exit(struct pvr2_context *mp) | ||
| 111 | { | ||
| 112 | int destroy_flag = 0; | ||
| 113 | if (!(mp->mc_first || !mp->disconnect_flag)) { | ||
| 114 | destroy_flag = !0; | ||
| 115 | } | ||
| 116 | pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_exit(id=%p) outside",mp); | ||
| 117 | mutex_unlock(&mp->mutex); | ||
| 118 | if (destroy_flag) pvr2_context_destroy(mp); | ||
| 119 | } | ||
| 120 | |||
| 121 | |||
| 122 | static void pvr2_context_run_checks(struct pvr2_context *mp) | ||
| 123 | { | ||
| 124 | struct pvr2_channel *ch1,*ch2; | ||
| 125 | for (ch1 = mp->mc_first; ch1; ch1 = ch2) { | ||
| 126 | ch2 = ch1->mc_next; | ||
| 127 | if (ch1->check_func) { | ||
| 128 | ch1->check_func(ch1); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | |||
| 134 | void pvr2_context_disconnect(struct pvr2_context *mp) | ||
| 135 | { | ||
| 136 | pvr2_context_enter(mp); do { | ||
| 137 | pvr2_hdw_disconnect(mp->hdw); | ||
| 138 | mp->disconnect_flag = !0; | ||
| 139 | pvr2_context_run_checks(mp); | ||
| 140 | } while (0); pvr2_context_exit(mp); | ||
| 141 | } | ||
| 142 | |||
| 143 | |||
| 144 | void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp) | ||
| 145 | { | ||
| 146 | cp->hdw = mp->hdw; | ||
| 147 | cp->mc_head = mp; | ||
| 148 | cp->mc_next = 0; | ||
| 149 | cp->mc_prev = mp->mc_last; | ||
| 150 | if (mp->mc_last) { | ||
| 151 | mp->mc_last->mc_next = cp; | ||
| 152 | } else { | ||
| 153 | mp->mc_first = cp; | ||
| 154 | } | ||
| 155 | mp->mc_last = cp; | ||
| 156 | } | ||
| 157 | |||
| 158 | |||
| 159 | static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp) | ||
| 160 | { | ||
| 161 | if (!cp->stream) return; | ||
| 162 | pvr2_stream_kill(cp->stream->stream); | ||
| 163 | cp->stream->user = 0; | ||
| 164 | cp->stream = 0; | ||
| 165 | } | ||
| 166 | |||
| 167 | |||
| 168 | void pvr2_channel_done(struct pvr2_channel *cp) | ||
| 169 | { | ||
| 170 | struct pvr2_context *mp = cp->mc_head; | ||
| 171 | pvr2_channel_disclaim_stream(cp); | ||
| 172 | if (cp->mc_next) { | ||
| 173 | cp->mc_next->mc_prev = cp->mc_prev; | ||
| 174 | } else { | ||
| 175 | mp->mc_last = cp->mc_prev; | ||
| 176 | } | ||
| 177 | if (cp->mc_prev) { | ||
| 178 | cp->mc_prev->mc_next = cp->mc_next; | ||
| 179 | } else { | ||
| 180 | mp->mc_first = cp->mc_next; | ||
| 181 | } | ||
| 182 | cp->hdw = 0; | ||
| 183 | } | ||
| 184 | |||
| 185 | |||
| 186 | int pvr2_channel_claim_stream(struct pvr2_channel *cp, | ||
| 187 | struct pvr2_context_stream *sp) | ||
| 188 | { | ||
| 189 | int code = 0; | ||
| 190 | pvr2_context_enter(cp->mc_head); do { | ||
| 191 | if (sp == cp->stream) break; | ||
| 192 | if (sp->user) { | ||
| 193 | code = -EBUSY; | ||
| 194 | break; | ||
| 195 | } | ||
| 196 | pvr2_channel_disclaim_stream(cp); | ||
| 197 | if (!sp) break; | ||
| 198 | sp->user = cp; | ||
| 199 | cp->stream = sp; | ||
| 200 | } while (0); pvr2_context_exit(cp->mc_head); | ||
| 201 | return code; | ||
| 202 | } | ||
| 203 | |||
| 204 | |||
| 205 | // This is the marker for the real beginning of a legitimate mpeg2 stream. | ||
| 206 | static char stream_sync_key[] = { | ||
| 207 | 0x00, 0x00, 0x01, 0xba, | ||
| 208 | }; | ||
| 209 | |||
| 210 | struct pvr2_ioread *pvr2_channel_create_mpeg_stream( | ||
| 211 | struct pvr2_context_stream *sp) | ||
| 212 | { | ||
| 213 | struct pvr2_ioread *cp; | ||
| 214 | cp = pvr2_ioread_create(); | ||
| 215 | if (!cp) return 0; | ||
| 216 | pvr2_ioread_setup(cp,sp->stream); | ||
| 217 | pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key)); | ||
| 218 | return cp; | ||
| 219 | } | ||
| 220 | |||
| 221 | |||
| 222 | /* | ||
| 223 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 224 | *** Local Variables: *** | ||
| 225 | *** mode: c *** | ||
| 226 | *** fill-column: 75 *** | ||
| 227 | *** tab-width: 8 *** | ||
| 228 | *** c-basic-offset: 8 *** | ||
| 229 | *** End: *** | ||
| 230 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h new file mode 100644 index 000000000000..6327fa1f7e4f --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-context.h | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | /* | ||
| 2 | * $Id$ | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 18 | * | ||
| 19 | */ | ||
| 20 | #ifndef __PVRUSB2_BASE_H | ||
| 21 | #define __PVRUSB2_BASE_H | ||
| 22 | |||
| 23 | #include <linux/mutex.h> | ||
| 24 | #include <linux/usb.h> | ||
| 25 | #include <linux/workqueue.h> | ||
| 26 | |||
| 27 | struct pvr2_hdw; /* hardware interface - defined elsewhere */ | ||
| 28 | struct pvr2_stream; /* stream interface - defined elsewhere */ | ||
| 29 | |||
| 30 | struct pvr2_context; /* All central state */ | ||
| 31 | struct pvr2_channel; /* One I/O pathway to a user */ | ||
| 32 | struct pvr2_context_stream; /* Wrapper for a stream */ | ||
| 33 | struct pvr2_crit_reg; /* Critical region pointer */ | ||
| 34 | struct pvr2_ioread; /* Low level stream structure */ | ||
| 35 | |||
| 36 | struct pvr2_context_stream { | ||
| 37 | struct pvr2_channel *user; | ||
| 38 | struct pvr2_stream *stream; | ||
| 39 | }; | ||
| 40 | |||
| 41 | struct pvr2_context { | ||
| 42 | struct pvr2_channel *mc_first; | ||
| 43 | struct pvr2_channel *mc_last; | ||
| 44 | struct pvr2_hdw *hdw; | ||
| 45 | struct pvr2_context_stream video_stream; | ||
| 46 | struct mutex mutex; | ||
| 47 | int disconnect_flag; | ||
| 48 | |||
| 49 | /* Called after pvr2_context initialization is complete */ | ||
| 50 | void (*setup_func)(struct pvr2_context *); | ||
| 51 | |||
| 52 | /* Work queue overhead for out-of-line processing */ | ||
| 53 | struct workqueue_struct *workqueue; | ||
| 54 | struct work_struct workinit; | ||
| 55 | struct work_struct workpoll; | ||
| 56 | }; | ||
| 57 | |||
| 58 | struct pvr2_channel { | ||
| 59 | struct pvr2_context *mc_head; | ||
| 60 | struct pvr2_channel *mc_next; | ||
| 61 | struct pvr2_channel *mc_prev; | ||
| 62 | struct pvr2_context_stream *stream; | ||
| 63 | struct pvr2_hdw *hdw; | ||
| 64 | void (*check_func)(struct pvr2_channel *); | ||
| 65 | }; | ||
| 66 | |||
| 67 | void pvr2_context_enter(struct pvr2_context *); | ||
| 68 | void pvr2_context_exit(struct pvr2_context *); | ||
| 69 | |||
| 70 | struct pvr2_context *pvr2_context_create(struct usb_interface *intf, | ||
| 71 | const struct usb_device_id *devid, | ||
| 72 | void (*setup_func)(struct pvr2_context *)); | ||
| 73 | void pvr2_context_disconnect(struct pvr2_context *); | ||
| 74 | |||
| 75 | void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *); | ||
| 76 | void pvr2_channel_done(struct pvr2_channel *); | ||
| 77 | int pvr2_channel_claim_stream(struct pvr2_channel *, | ||
| 78 | struct pvr2_context_stream *); | ||
| 79 | struct pvr2_ioread *pvr2_channel_create_mpeg_stream( | ||
| 80 | struct pvr2_context_stream *); | ||
| 81 | |||
| 82 | |||
| 83 | #endif /* __PVRUSB2_CONTEXT_H */ | ||
| 84 | /* | ||
| 85 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 86 | *** Local Variables: *** | ||
| 87 | *** mode: c *** | ||
| 88 | *** fill-column: 75 *** | ||
| 89 | *** tab-width: 8 *** | ||
| 90 | *** c-basic-offset: 8 *** | ||
| 91 | *** End: *** | ||
| 92 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c new file mode 100644 index 000000000000..d5df9fbeba2f --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c | |||
| @@ -0,0 +1,593 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "pvrusb2-ctrl.h" | ||
| 23 | #include "pvrusb2-hdw-internal.h" | ||
| 24 | #include <linux/errno.h> | ||
| 25 | #include <linux/string.h> | ||
| 26 | #include <linux/mutex.h> | ||
| 27 | |||
| 28 | |||
| 29 | /* Set the given control. */ | ||
| 30 | int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val) | ||
| 31 | { | ||
| 32 | return pvr2_ctrl_set_mask_value(cptr,~0,val); | ||
| 33 | } | ||
| 34 | |||
| 35 | |||
| 36 | /* Set/clear specific bits of the given control. */ | ||
| 37 | int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val) | ||
| 38 | { | ||
| 39 | int ret = 0; | ||
| 40 | if (!cptr) return -EINVAL; | ||
| 41 | LOCK_TAKE(cptr->hdw->big_lock); do { | ||
| 42 | if (cptr->info->set_value != 0) { | ||
| 43 | if (cptr->info->type == pvr2_ctl_bitmask) { | ||
| 44 | mask &= cptr->info->def.type_bitmask.valid_bits; | ||
| 45 | } else if (cptr->info->type == pvr2_ctl_int) { | ||
| 46 | if (val < cptr->info->def.type_int.min_value) { | ||
| 47 | break; | ||
| 48 | } | ||
| 49 | if (val > cptr->info->def.type_int.max_value) { | ||
| 50 | break; | ||
| 51 | } | ||
| 52 | } else if (cptr->info->type == pvr2_ctl_enum) { | ||
| 53 | if (val >= cptr->info->def.type_enum.count) { | ||
| 54 | break; | ||
| 55 | } | ||
| 56 | } else if (cptr->info->type != pvr2_ctl_bool) { | ||
| 57 | break; | ||
| 58 | } | ||
| 59 | ret = cptr->info->set_value(cptr,mask,val); | ||
| 60 | } else { | ||
| 61 | ret = -EPERM; | ||
| 62 | } | ||
| 63 | } while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||
| 64 | return ret; | ||
| 65 | } | ||
| 66 | |||
| 67 | |||
| 68 | /* Get the current value of the given control. */ | ||
| 69 | int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr) | ||
| 70 | { | ||
| 71 | int ret = 0; | ||
| 72 | if (!cptr) return -EINVAL; | ||
| 73 | LOCK_TAKE(cptr->hdw->big_lock); do { | ||
| 74 | ret = cptr->info->get_value(cptr,valptr); | ||
| 75 | } while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||
| 76 | return ret; | ||
| 77 | } | ||
| 78 | |||
| 79 | |||
| 80 | /* Retrieve control's type */ | ||
| 81 | enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr) | ||
| 82 | { | ||
| 83 | if (!cptr) return pvr2_ctl_int; | ||
| 84 | return cptr->info->type; | ||
| 85 | } | ||
| 86 | |||
| 87 | |||
| 88 | /* Retrieve control's maximum value (int type) */ | ||
| 89 | int pvr2_ctrl_get_max(struct pvr2_ctrl *cptr) | ||
| 90 | { | ||
| 91 | int ret = 0; | ||
| 92 | if (!cptr) return 0; | ||
| 93 | LOCK_TAKE(cptr->hdw->big_lock); do { | ||
| 94 | if (cptr->info->type == pvr2_ctl_int) { | ||
| 95 | ret = cptr->info->def.type_int.max_value; | ||
| 96 | } | ||
| 97 | } while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||
| 98 | return ret; | ||
| 99 | } | ||
| 100 | |||
| 101 | |||
| 102 | /* Retrieve control's minimum value (int type) */ | ||
| 103 | int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr) | ||
| 104 | { | ||
| 105 | int ret = 0; | ||
| 106 | if (!cptr) return 0; | ||
| 107 | LOCK_TAKE(cptr->hdw->big_lock); do { | ||
| 108 | if (cptr->info->type == pvr2_ctl_int) { | ||
| 109 | ret = cptr->info->def.type_int.min_value; | ||
| 110 | } | ||
| 111 | } while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||
| 112 | return ret; | ||
| 113 | } | ||
| 114 | |||
| 115 | |||
| 116 | /* Retrieve control's default value (any type) */ | ||
| 117 | int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr) | ||
| 118 | { | ||
| 119 | int ret = 0; | ||
| 120 | if (!cptr) return 0; | ||
| 121 | LOCK_TAKE(cptr->hdw->big_lock); do { | ||
| 122 | if (cptr->info->type == pvr2_ctl_int) { | ||
| 123 | ret = cptr->info->default_value; | ||
| 124 | } | ||
| 125 | } while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||
| 126 | return ret; | ||
| 127 | } | ||
| 128 | |||
| 129 | |||
| 130 | /* Retrieve control's enumeration count (enum only) */ | ||
| 131 | int pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr) | ||
| 132 | { | ||
| 133 | int ret = 0; | ||
| 134 | if (!cptr) return 0; | ||
| 135 | LOCK_TAKE(cptr->hdw->big_lock); do { | ||
| 136 | if (cptr->info->type == pvr2_ctl_enum) { | ||
| 137 | ret = cptr->info->def.type_enum.count; | ||
| 138 | } | ||
| 139 | } while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||
| 140 | return ret; | ||
| 141 | } | ||
| 142 | |||
| 143 | |||
| 144 | /* Retrieve control's valid mask bits (bit mask only) */ | ||
| 145 | int pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr) | ||
| 146 | { | ||
| 147 | int ret = 0; | ||
| 148 | if (!cptr) return 0; | ||
| 149 | LOCK_TAKE(cptr->hdw->big_lock); do { | ||
| 150 | if (cptr->info->type == pvr2_ctl_bitmask) { | ||
| 151 | ret = cptr->info->def.type_bitmask.valid_bits; | ||
| 152 | } | ||
| 153 | } while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||
| 154 | return ret; | ||
| 155 | } | ||
| 156 | |||
| 157 | |||
| 158 | /* Retrieve the control's name */ | ||
| 159 | const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr) | ||
| 160 | { | ||
| 161 | if (!cptr) return 0; | ||
| 162 | return cptr->info->name; | ||
| 163 | } | ||
| 164 | |||
| 165 | |||
| 166 | /* Retrieve the control's desc */ | ||
| 167 | const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr) | ||
| 168 | { | ||
| 169 | if (!cptr) return 0; | ||
| 170 | return cptr->info->desc; | ||
| 171 | } | ||
| 172 | |||
| 173 | |||
| 174 | /* Retrieve a control enumeration or bit mask value */ | ||
| 175 | int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val, | ||
| 176 | char *bptr,unsigned int bmax, | ||
| 177 | unsigned int *blen) | ||
| 178 | { | ||
| 179 | int ret = -EINVAL; | ||
| 180 | if (!cptr) return 0; | ||
| 181 | *blen = 0; | ||
| 182 | LOCK_TAKE(cptr->hdw->big_lock); do { | ||
| 183 | if (cptr->info->type == pvr2_ctl_enum) { | ||
| 184 | const char **names; | ||
| 185 | names = cptr->info->def.type_enum.value_names; | ||
| 186 | if ((val >= 0) && | ||
| 187 | (val < cptr->info->def.type_enum.count)) { | ||
| 188 | if (names[val]) { | ||
| 189 | *blen = scnprintf( | ||
| 190 | bptr,bmax,"%s", | ||
| 191 | names[val]); | ||
| 192 | } else { | ||
| 193 | *blen = 0; | ||
| 194 | } | ||
| 195 | ret = 0; | ||
| 196 | } | ||
| 197 | } else if (cptr->info->type == pvr2_ctl_bitmask) { | ||
| 198 | const char **names; | ||
| 199 | unsigned int idx; | ||
| 200 | int msk; | ||
| 201 | names = cptr->info->def.type_bitmask.bit_names; | ||
| 202 | val &= cptr->info->def.type_bitmask.valid_bits; | ||
| 203 | for (idx = 0, msk = 1; val; idx++, msk <<= 1) { | ||
| 204 | if (val & msk) { | ||
| 205 | *blen = scnprintf(bptr,bmax,"%s", | ||
| 206 | names[idx]); | ||
| 207 | ret = 0; | ||
| 208 | break; | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | } while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||
| 213 | return ret; | ||
| 214 | } | ||
| 215 | |||
| 216 | |||
| 217 | /* Return V4L ID for this control or zero if none */ | ||
| 218 | int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *cptr) | ||
| 219 | { | ||
| 220 | if (!cptr) return 0; | ||
| 221 | return cptr->info->v4l_id; | ||
| 222 | } | ||
| 223 | |||
| 224 | |||
| 225 | unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *cptr) | ||
| 226 | { | ||
| 227 | unsigned int flags = 0; | ||
| 228 | |||
| 229 | if (cptr->info->get_v4lflags) { | ||
| 230 | flags = cptr->info->get_v4lflags(cptr); | ||
| 231 | } | ||
| 232 | |||
| 233 | if (cptr->info->set_value) { | ||
| 234 | flags &= ~V4L2_CTRL_FLAG_READ_ONLY; | ||
| 235 | } else { | ||
| 236 | flags |= V4L2_CTRL_FLAG_READ_ONLY; | ||
| 237 | } | ||
| 238 | |||
| 239 | return flags; | ||
| 240 | } | ||
| 241 | |||
| 242 | |||
| 243 | /* Return true if control is writable */ | ||
| 244 | int pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr) | ||
| 245 | { | ||
| 246 | if (!cptr) return 0; | ||
| 247 | return cptr->info->set_value != 0; | ||
| 248 | } | ||
| 249 | |||
| 250 | |||
| 251 | /* Return true if control has custom symbolic representation */ | ||
| 252 | int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr) | ||
| 253 | { | ||
| 254 | if (!cptr) return 0; | ||
| 255 | if (!cptr->info->val_to_sym) return 0; | ||
| 256 | if (!cptr->info->sym_to_val) return 0; | ||
| 257 | return !0; | ||
| 258 | } | ||
| 259 | |||
| 260 | |||
| 261 | /* Convert a given mask/val to a custom symbolic value */ | ||
| 262 | int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr, | ||
| 263 | int mask,int val, | ||
| 264 | char *buf,unsigned int maxlen, | ||
| 265 | unsigned int *len) | ||
| 266 | { | ||
| 267 | if (!cptr) return -EINVAL; | ||
| 268 | if (!cptr->info->val_to_sym) return -EINVAL; | ||
| 269 | return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len); | ||
| 270 | } | ||
| 271 | |||
| 272 | |||
| 273 | /* Convert a symbolic value to a mask/value pair */ | ||
| 274 | int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr, | ||
| 275 | const char *buf,unsigned int len, | ||
| 276 | int *maskptr,int *valptr) | ||
| 277 | { | ||
| 278 | if (!cptr) return -EINVAL; | ||
| 279 | if (!cptr->info->sym_to_val) return -EINVAL; | ||
| 280 | return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr); | ||
| 281 | } | ||
| 282 | |||
| 283 | |||
| 284 | static unsigned int gen_bitmask_string(int msk,int val,int msk_only, | ||
| 285 | const char **names, | ||
| 286 | char *ptr,unsigned int len) | ||
| 287 | { | ||
| 288 | unsigned int idx; | ||
| 289 | long sm,um; | ||
| 290 | int spcFl; | ||
| 291 | unsigned int uc,cnt; | ||
| 292 | const char *idStr; | ||
| 293 | |||
| 294 | spcFl = 0; | ||
| 295 | uc = 0; | ||
| 296 | um = 0; | ||
| 297 | for (idx = 0, sm = 1; msk; idx++, sm <<= 1) { | ||
| 298 | if (sm & msk) { | ||
| 299 | msk &= ~sm; | ||
| 300 | idStr = names[idx]; | ||
| 301 | if (idStr) { | ||
| 302 | cnt = scnprintf(ptr,len,"%s%s%s", | ||
| 303 | (spcFl ? " " : ""), | ||
| 304 | (msk_only ? "" : | ||
| 305 | ((val & sm) ? "+" : "-")), | ||
| 306 | idStr); | ||
| 307 | ptr += cnt; len -= cnt; uc += cnt; | ||
| 308 | spcFl = !0; | ||
| 309 | } else { | ||
| 310 | um |= sm; | ||
| 311 | } | ||
| 312 | } | ||
| 313 | } | ||
| 314 | if (um) { | ||
| 315 | if (msk_only) { | ||
| 316 | cnt = scnprintf(ptr,len,"%s0x%lx", | ||
| 317 | (spcFl ? " " : ""), | ||
| 318 | um); | ||
| 319 | ptr += cnt; len -= cnt; uc += cnt; | ||
| 320 | spcFl = !0; | ||
| 321 | } else if (um & val) { | ||
| 322 | cnt = scnprintf(ptr,len,"%s+0x%lx", | ||
| 323 | (spcFl ? " " : ""), | ||
| 324 | um & val); | ||
| 325 | ptr += cnt; len -= cnt; uc += cnt; | ||
| 326 | spcFl = !0; | ||
| 327 | } else if (um & ~val) { | ||
| 328 | cnt = scnprintf(ptr,len,"%s+0x%lx", | ||
| 329 | (spcFl ? " " : ""), | ||
| 330 | um & ~val); | ||
| 331 | ptr += cnt; len -= cnt; uc += cnt; | ||
| 332 | spcFl = !0; | ||
| 333 | } | ||
| 334 | } | ||
| 335 | return uc; | ||
| 336 | } | ||
| 337 | |||
| 338 | |||
| 339 | static const char *boolNames[] = { | ||
| 340 | "false", | ||
| 341 | "true", | ||
| 342 | "no", | ||
| 343 | "yes", | ||
| 344 | }; | ||
| 345 | |||
| 346 | |||
| 347 | static int parse_token(const char *ptr,unsigned int len, | ||
| 348 | int *valptr, | ||
| 349 | const char **names,unsigned int namecnt) | ||
| 350 | { | ||
| 351 | char buf[33]; | ||
| 352 | unsigned int slen; | ||
| 353 | unsigned int idx; | ||
| 354 | int negfl; | ||
| 355 | char *p2; | ||
| 356 | *valptr = 0; | ||
| 357 | if (!names) namecnt = 0; | ||
| 358 | for (idx = 0; idx < namecnt; idx++) { | ||
| 359 | if (!names[idx]) continue; | ||
| 360 | slen = strlen(names[idx]); | ||
| 361 | if (slen != len) continue; | ||
| 362 | if (memcmp(names[idx],ptr,slen)) continue; | ||
| 363 | *valptr = idx; | ||
| 364 | return 0; | ||
| 365 | } | ||
| 366 | negfl = 0; | ||
| 367 | if ((*ptr == '-') || (*ptr == '+')) { | ||
| 368 | negfl = (*ptr == '-'); | ||
| 369 | ptr++; len--; | ||
| 370 | } | ||
| 371 | if (len >= sizeof(buf)) return -EINVAL; | ||
| 372 | memcpy(buf,ptr,len); | ||
| 373 | buf[len] = 0; | ||
| 374 | *valptr = simple_strtol(buf,&p2,0); | ||
| 375 | if (negfl) *valptr = -(*valptr); | ||
| 376 | if (*p2) return -EINVAL; | ||
| 377 | return 1; | ||
| 378 | } | ||
| 379 | |||
| 380 | |||
| 381 | static int parse_mtoken(const char *ptr,unsigned int len, | ||
| 382 | int *valptr, | ||
| 383 | const char **names,int valid_bits) | ||
| 384 | { | ||
| 385 | char buf[33]; | ||
| 386 | unsigned int slen; | ||
| 387 | unsigned int idx; | ||
| 388 | char *p2; | ||
| 389 | int msk; | ||
| 390 | *valptr = 0; | ||
| 391 | for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) { | ||
| 392 | if (!msk & valid_bits) continue; | ||
| 393 | valid_bits &= ~msk; | ||
| 394 | if (!names[idx]) continue; | ||
| 395 | slen = strlen(names[idx]); | ||
| 396 | if (slen != len) continue; | ||
| 397 | if (memcmp(names[idx],ptr,slen)) continue; | ||
| 398 | *valptr = msk; | ||
| 399 | return 0; | ||
| 400 | } | ||
| 401 | if (len >= sizeof(buf)) return -EINVAL; | ||
| 402 | memcpy(buf,ptr,len); | ||
| 403 | buf[len] = 0; | ||
| 404 | *valptr = simple_strtol(buf,&p2,0); | ||
| 405 | if (*p2) return -EINVAL; | ||
| 406 | return 0; | ||
| 407 | } | ||
| 408 | |||
| 409 | |||
| 410 | static int parse_tlist(const char *ptr,unsigned int len, | ||
| 411 | int *maskptr,int *valptr, | ||
| 412 | const char **names,int valid_bits) | ||
| 413 | { | ||
| 414 | unsigned int cnt; | ||
| 415 | int mask,val,kv,mode,ret; | ||
| 416 | mask = 0; | ||
| 417 | val = 0; | ||
| 418 | ret = 0; | ||
| 419 | while (len) { | ||
| 420 | cnt = 0; | ||
| 421 | while ((cnt < len) && | ||
| 422 | ((ptr[cnt] <= 32) || | ||
| 423 | (ptr[cnt] >= 127))) cnt++; | ||
| 424 | ptr += cnt; | ||
| 425 | len -= cnt; | ||
| 426 | mode = 0; | ||
| 427 | if ((*ptr == '-') || (*ptr == '+')) { | ||
| 428 | mode = (*ptr == '-') ? -1 : 1; | ||
| 429 | ptr++; | ||
| 430 | len--; | ||
| 431 | } | ||
| 432 | cnt = 0; | ||
| 433 | while (cnt < len) { | ||
| 434 | if (ptr[cnt] <= 32) break; | ||
| 435 | if (ptr[cnt] >= 127) break; | ||
| 436 | cnt++; | ||
| 437 | } | ||
| 438 | if (!cnt) break; | ||
| 439 | if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) { | ||
| 440 | ret = -EINVAL; | ||
| 441 | break; | ||
| 442 | } | ||
| 443 | ptr += cnt; | ||
| 444 | len -= cnt; | ||
| 445 | switch (mode) { | ||
| 446 | case 0: | ||
| 447 | mask = valid_bits; | ||
| 448 | val |= kv; | ||
| 449 | break; | ||
| 450 | case -1: | ||
| 451 | mask |= kv; | ||
| 452 | val &= ~kv; | ||
| 453 | break; | ||
| 454 | case 1: | ||
| 455 | mask |= kv; | ||
| 456 | val |= kv; | ||
| 457 | break; | ||
| 458 | default: | ||
| 459 | break; | ||
| 460 | } | ||
| 461 | } | ||
| 462 | *maskptr = mask; | ||
| 463 | *valptr = val; | ||
| 464 | return ret; | ||
| 465 | } | ||
| 466 | |||
| 467 | |||
| 468 | /* Convert a symbolic value to a mask/value pair */ | ||
| 469 | int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr, | ||
| 470 | const char *ptr,unsigned int len, | ||
| 471 | int *maskptr,int *valptr) | ||
| 472 | { | ||
| 473 | int ret = -EINVAL; | ||
| 474 | unsigned int cnt; | ||
| 475 | |||
| 476 | *maskptr = 0; | ||
| 477 | *valptr = 0; | ||
| 478 | |||
| 479 | cnt = 0; | ||
| 480 | while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++; | ||
| 481 | len -= cnt; ptr += cnt; | ||
| 482 | cnt = 0; | ||
| 483 | while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) || | ||
| 484 | (ptr[len-(cnt+1)] >= 127))) cnt++; | ||
| 485 | len -= cnt; | ||
| 486 | |||
| 487 | if (!len) return -EINVAL; | ||
| 488 | |||
| 489 | LOCK_TAKE(cptr->hdw->big_lock); do { | ||
| 490 | if (cptr->info->type == pvr2_ctl_int) { | ||
| 491 | ret = parse_token(ptr,len,valptr,0,0); | ||
| 492 | if ((ret >= 0) && | ||
| 493 | ((*valptr < cptr->info->def.type_int.min_value) || | ||
| 494 | (*valptr > cptr->info->def.type_int.max_value))) { | ||
| 495 | ret = -ERANGE; | ||
| 496 | } | ||
| 497 | if (maskptr) *maskptr = ~0; | ||
| 498 | } else if (cptr->info->type == pvr2_ctl_bool) { | ||
| 499 | ret = parse_token( | ||
| 500 | ptr,len,valptr,boolNames, | ||
| 501 | sizeof(boolNames)/sizeof(boolNames[0])); | ||
| 502 | if (ret == 1) { | ||
| 503 | *valptr = *valptr ? !0 : 0; | ||
| 504 | } else if (ret == 0) { | ||
| 505 | *valptr = (*valptr & 1) ? !0 : 0; | ||
| 506 | } | ||
| 507 | if (maskptr) *maskptr = 1; | ||
| 508 | } else if (cptr->info->type == pvr2_ctl_enum) { | ||
| 509 | ret = parse_token( | ||
| 510 | ptr,len,valptr, | ||
| 511 | cptr->info->def.type_enum.value_names, | ||
| 512 | cptr->info->def.type_enum.count); | ||
| 513 | if ((ret >= 0) && | ||
| 514 | ((*valptr < 0) || | ||
| 515 | (*valptr >= cptr->info->def.type_enum.count))) { | ||
| 516 | ret = -ERANGE; | ||
| 517 | } | ||
| 518 | if (maskptr) *maskptr = ~0; | ||
| 519 | } else if (cptr->info->type == pvr2_ctl_bitmask) { | ||
| 520 | ret = parse_tlist( | ||
| 521 | ptr,len,maskptr,valptr, | ||
| 522 | cptr->info->def.type_bitmask.bit_names, | ||
| 523 | cptr->info->def.type_bitmask.valid_bits); | ||
| 524 | } | ||
| 525 | } while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||
| 526 | return ret; | ||
| 527 | } | ||
| 528 | |||
| 529 | |||
| 530 | /* Convert a given mask/val to a symbolic value */ | ||
| 531 | int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr, | ||
| 532 | int mask,int val, | ||
| 533 | char *buf,unsigned int maxlen, | ||
| 534 | unsigned int *len) | ||
| 535 | { | ||
| 536 | int ret = -EINVAL; | ||
| 537 | |||
| 538 | *len = 0; | ||
| 539 | if (cptr->info->type == pvr2_ctl_int) { | ||
| 540 | *len = scnprintf(buf,maxlen,"%d",val); | ||
| 541 | ret = 0; | ||
| 542 | } else if (cptr->info->type == pvr2_ctl_bool) { | ||
| 543 | *len = scnprintf(buf,maxlen,"%s",val ? "true" : "false"); | ||
| 544 | ret = 0; | ||
| 545 | } else if (cptr->info->type == pvr2_ctl_enum) { | ||
| 546 | const char **names; | ||
| 547 | names = cptr->info->def.type_enum.value_names; | ||
| 548 | if ((val >= 0) && | ||
| 549 | (val < cptr->info->def.type_enum.count)) { | ||
| 550 | if (names[val]) { | ||
| 551 | *len = scnprintf( | ||
| 552 | buf,maxlen,"%s", | ||
| 553 | names[val]); | ||
| 554 | } else { | ||
| 555 | *len = 0; | ||
| 556 | } | ||
| 557 | ret = 0; | ||
| 558 | } | ||
| 559 | } else if (cptr->info->type == pvr2_ctl_bitmask) { | ||
| 560 | *len = gen_bitmask_string( | ||
| 561 | val & mask & cptr->info->def.type_bitmask.valid_bits, | ||
| 562 | ~0,!0, | ||
| 563 | cptr->info->def.type_bitmask.bit_names, | ||
| 564 | buf,maxlen); | ||
| 565 | } | ||
| 566 | return ret; | ||
| 567 | } | ||
| 568 | |||
| 569 | |||
| 570 | /* Convert a given mask/val to a symbolic value */ | ||
| 571 | int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr, | ||
| 572 | int mask,int val, | ||
| 573 | char *buf,unsigned int maxlen, | ||
| 574 | unsigned int *len) | ||
| 575 | { | ||
| 576 | int ret; | ||
| 577 | LOCK_TAKE(cptr->hdw->big_lock); do { | ||
| 578 | ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val, | ||
| 579 | buf,maxlen,len); | ||
| 580 | } while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||
| 581 | return ret; | ||
| 582 | } | ||
| 583 | |||
| 584 | |||
| 585 | /* | ||
| 586 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 587 | *** Local Variables: *** | ||
| 588 | *** mode: c *** | ||
| 589 | *** fill-column: 75 *** | ||
| 590 | *** tab-width: 8 *** | ||
| 591 | *** c-basic-offset: 8 *** | ||
| 592 | *** End: *** | ||
| 593 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h new file mode 100644 index 000000000000..c1680053cd64 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_CTRL_H | ||
| 22 | #define __PVRUSB2_CTRL_H | ||
| 23 | |||
| 24 | struct pvr2_ctrl; | ||
| 25 | |||
| 26 | enum pvr2_ctl_type { | ||
| 27 | pvr2_ctl_int = 0, | ||
| 28 | pvr2_ctl_enum = 1, | ||
| 29 | pvr2_ctl_bitmask = 2, | ||
| 30 | pvr2_ctl_bool = 3, | ||
| 31 | }; | ||
| 32 | |||
| 33 | |||
| 34 | /* Set the given control. */ | ||
| 35 | int pvr2_ctrl_set_value(struct pvr2_ctrl *,int val); | ||
| 36 | |||
| 37 | /* Set/clear specific bits of the given control. */ | ||
| 38 | int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *,int mask,int val); | ||
| 39 | |||
| 40 | /* Get the current value of the given control. */ | ||
| 41 | int pvr2_ctrl_get_value(struct pvr2_ctrl *,int *valptr); | ||
| 42 | |||
| 43 | /* Retrieve control's type */ | ||
| 44 | enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *); | ||
| 45 | |||
| 46 | /* Retrieve control's maximum value (int type) */ | ||
| 47 | int pvr2_ctrl_get_max(struct pvr2_ctrl *); | ||
| 48 | |||
| 49 | /* Retrieve control's minimum value (int type) */ | ||
| 50 | int pvr2_ctrl_get_min(struct pvr2_ctrl *); | ||
| 51 | |||
| 52 | /* Retrieve control's default value (any type) */ | ||
| 53 | int pvr2_ctrl_get_def(struct pvr2_ctrl *); | ||
| 54 | |||
| 55 | /* Retrieve control's enumeration count (enum only) */ | ||
| 56 | int pvr2_ctrl_get_cnt(struct pvr2_ctrl *); | ||
| 57 | |||
| 58 | /* Retrieve control's valid mask bits (bit mask only) */ | ||
| 59 | int pvr2_ctrl_get_mask(struct pvr2_ctrl *); | ||
| 60 | |||
| 61 | /* Retrieve the control's name */ | ||
| 62 | const char *pvr2_ctrl_get_name(struct pvr2_ctrl *); | ||
| 63 | |||
| 64 | /* Retrieve the control's desc */ | ||
| 65 | const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *); | ||
| 66 | |||
| 67 | /* Retrieve a control enumeration or bit mask value */ | ||
| 68 | int pvr2_ctrl_get_valname(struct pvr2_ctrl *,int,char *,unsigned int, | ||
| 69 | unsigned int *); | ||
| 70 | |||
| 71 | /* Return true if control is writable */ | ||
| 72 | int pvr2_ctrl_is_writable(struct pvr2_ctrl *); | ||
| 73 | |||
| 74 | /* Return V4L flags value for control (or zero if there is no v4l control | ||
| 75 | actually under this control) */ | ||
| 76 | unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *); | ||
| 77 | |||
| 78 | /* Return V4L ID for this control or zero if none */ | ||
| 79 | int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *); | ||
| 80 | |||
| 81 | /* Return true if control has custom symbolic representation */ | ||
| 82 | int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *); | ||
| 83 | |||
| 84 | /* Convert a given mask/val to a custom symbolic value */ | ||
| 85 | int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *, | ||
| 86 | int mask,int val, | ||
| 87 | char *buf,unsigned int maxlen, | ||
| 88 | unsigned int *len); | ||
| 89 | |||
| 90 | /* Convert a symbolic value to a mask/value pair */ | ||
| 91 | int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *, | ||
| 92 | const char *buf,unsigned int len, | ||
| 93 | int *maskptr,int *valptr); | ||
| 94 | |||
| 95 | /* Convert a given mask/val to a symbolic value */ | ||
| 96 | int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *, | ||
| 97 | int mask,int val, | ||
| 98 | char *buf,unsigned int maxlen, | ||
| 99 | unsigned int *len); | ||
| 100 | |||
| 101 | /* Convert a symbolic value to a mask/value pair */ | ||
| 102 | int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *, | ||
| 103 | const char *buf,unsigned int len, | ||
| 104 | int *maskptr,int *valptr); | ||
| 105 | |||
| 106 | /* Convert a given mask/val to a symbolic value - must already be | ||
| 107 | inside of critical region. */ | ||
| 108 | int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *, | ||
| 109 | int mask,int val, | ||
| 110 | char *buf,unsigned int maxlen, | ||
| 111 | unsigned int *len); | ||
| 112 | |||
| 113 | #endif /* __PVRUSB2_CTRL_H */ | ||
| 114 | |||
| 115 | /* | ||
| 116 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 117 | *** Local Variables: *** | ||
| 118 | *** mode: c *** | ||
| 119 | *** fill-column: 75 *** | ||
| 120 | *** tab-width: 8 *** | ||
| 121 | *** c-basic-offset: 8 *** | ||
| 122 | *** End: *** | ||
| 123 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c new file mode 100644 index 000000000000..27eadaff75a0 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c | |||
| @@ -0,0 +1,279 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | /* | ||
| 24 | |||
| 25 | This source file is specifically designed to interface with the | ||
| 26 | cx2584x, in kernels 2.6.16 or newer. | ||
| 27 | |||
| 28 | */ | ||
| 29 | |||
| 30 | #include "pvrusb2-cx2584x-v4l.h" | ||
| 31 | #include "pvrusb2-video-v4l.h" | ||
| 32 | #include "pvrusb2-i2c-cmd-v4l2.h" | ||
| 33 | |||
| 34 | |||
| 35 | #include "pvrusb2-hdw-internal.h" | ||
| 36 | #include "pvrusb2-debug.h" | ||
| 37 | #include <media/cx25840.h> | ||
| 38 | #include <linux/videodev2.h> | ||
| 39 | #include <media/v4l2-common.h> | ||
| 40 | #include <linux/errno.h> | ||
| 41 | #include <linux/slab.h> | ||
| 42 | |||
| 43 | struct pvr2_v4l_cx2584x { | ||
| 44 | struct pvr2_i2c_handler handler; | ||
| 45 | struct pvr2_decoder_ctrl ctrl; | ||
| 46 | struct pvr2_i2c_client *client; | ||
| 47 | struct pvr2_hdw *hdw; | ||
| 48 | unsigned long stale_mask; | ||
| 49 | }; | ||
| 50 | |||
| 51 | |||
| 52 | static void set_input(struct pvr2_v4l_cx2584x *ctxt) | ||
| 53 | { | ||
| 54 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 55 | struct v4l2_routing route; | ||
| 56 | enum cx25840_video_input vid_input; | ||
| 57 | enum cx25840_audio_input aud_input; | ||
| 58 | |||
| 59 | memset(&route,0,sizeof(route)); | ||
| 60 | |||
| 61 | switch(hdw->input_val) { | ||
| 62 | case PVR2_CVAL_INPUT_TV: | ||
| 63 | vid_input = CX25840_COMPOSITE7; | ||
| 64 | aud_input = CX25840_AUDIO8; | ||
| 65 | break; | ||
| 66 | case PVR2_CVAL_INPUT_COMPOSITE: | ||
| 67 | vid_input = CX25840_COMPOSITE3; | ||
| 68 | aud_input = CX25840_AUDIO_SERIAL; | ||
| 69 | break; | ||
| 70 | case PVR2_CVAL_INPUT_SVIDEO: | ||
| 71 | vid_input = CX25840_SVIDEO1; | ||
| 72 | aud_input = CX25840_AUDIO_SERIAL; | ||
| 73 | break; | ||
| 74 | case PVR2_CVAL_INPUT_RADIO: | ||
| 75 | default: | ||
| 76 | // Just set it to be composite input for now... | ||
| 77 | vid_input = CX25840_COMPOSITE3; | ||
| 78 | aud_input = CX25840_AUDIO_SERIAL; | ||
| 79 | break; | ||
| 80 | } | ||
| 81 | |||
| 82 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x", | ||
| 83 | vid_input,aud_input); | ||
| 84 | route.input = (u32)vid_input; | ||
| 85 | pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route); | ||
| 86 | route.input = (u32)aud_input; | ||
| 87 | pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); | ||
| 88 | } | ||
| 89 | |||
| 90 | |||
| 91 | static int check_input(struct pvr2_v4l_cx2584x *ctxt) | ||
| 92 | { | ||
| 93 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 94 | return hdw->input_dirty != 0; | ||
| 95 | } | ||
| 96 | |||
| 97 | |||
| 98 | static void set_audio(struct pvr2_v4l_cx2584x *ctxt) | ||
| 99 | { | ||
| 100 | u32 val; | ||
| 101 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 102 | |||
| 103 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_audio %d", | ||
| 104 | hdw->srate_val); | ||
| 105 | switch (hdw->srate_val) { | ||
| 106 | default: | ||
| 107 | case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000: | ||
| 108 | val = 48000; | ||
| 109 | break; | ||
| 110 | case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100: | ||
| 111 | val = 44100; | ||
| 112 | break; | ||
| 113 | case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000: | ||
| 114 | val = 32000; | ||
| 115 | break; | ||
| 116 | } | ||
| 117 | pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val); | ||
| 118 | } | ||
| 119 | |||
| 120 | |||
| 121 | static int check_audio(struct pvr2_v4l_cx2584x *ctxt) | ||
| 122 | { | ||
| 123 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 124 | return hdw->srate_dirty != 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | |||
| 128 | struct pvr2_v4l_cx2584x_ops { | ||
| 129 | void (*update)(struct pvr2_v4l_cx2584x *); | ||
| 130 | int (*check)(struct pvr2_v4l_cx2584x *); | ||
| 131 | }; | ||
| 132 | |||
| 133 | |||
| 134 | static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = { | ||
| 135 | { .update = set_input, .check = check_input}, | ||
| 136 | { .update = set_audio, .check = check_audio}, | ||
| 137 | }; | ||
| 138 | |||
| 139 | |||
| 140 | static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt) | ||
| 141 | { | ||
| 142 | ctxt->client->handler = 0; | ||
| 143 | ctxt->hdw->decoder_ctrl = 0; | ||
| 144 | kfree(ctxt); | ||
| 145 | } | ||
| 146 | |||
| 147 | |||
| 148 | static int decoder_check(struct pvr2_v4l_cx2584x *ctxt) | ||
| 149 | { | ||
| 150 | unsigned long msk; | ||
| 151 | unsigned int idx; | ||
| 152 | |||
| 153 | for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); | ||
| 154 | idx++) { | ||
| 155 | msk = 1 << idx; | ||
| 156 | if (ctxt->stale_mask & msk) continue; | ||
| 157 | if (decoder_ops[idx].check(ctxt)) { | ||
| 158 | ctxt->stale_mask |= msk; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | return ctxt->stale_mask != 0; | ||
| 162 | } | ||
| 163 | |||
| 164 | |||
| 165 | static void decoder_update(struct pvr2_v4l_cx2584x *ctxt) | ||
| 166 | { | ||
| 167 | unsigned long msk; | ||
| 168 | unsigned int idx; | ||
| 169 | |||
| 170 | for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); | ||
| 171 | idx++) { | ||
| 172 | msk = 1 << idx; | ||
| 173 | if (!(ctxt->stale_mask & msk)) continue; | ||
| 174 | ctxt->stale_mask &= ~msk; | ||
| 175 | decoder_ops[idx].update(ctxt); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | |||
| 180 | static void decoder_enable(struct pvr2_v4l_cx2584x *ctxt,int fl) | ||
| 181 | { | ||
| 182 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_enable(%d)",fl); | ||
| 183 | pvr2_v4l2_cmd_stream(ctxt->client,fl); | ||
| 184 | } | ||
| 185 | |||
| 186 | |||
| 187 | static int decoder_detect(struct pvr2_i2c_client *cp) | ||
| 188 | { | ||
| 189 | int ret; | ||
| 190 | /* Attempt to query the decoder - let's see if it will answer */ | ||
| 191 | struct v4l2_queryctrl qc; | ||
| 192 | |||
| 193 | memset(&qc,0,sizeof(qc)); | ||
| 194 | |||
| 195 | qc.id = V4L2_CID_BRIGHTNESS; | ||
| 196 | |||
| 197 | ret = pvr2_i2c_client_cmd(cp,VIDIOC_QUERYCTRL,&qc); | ||
| 198 | return ret == 0; /* Return true if it answered */ | ||
| 199 | } | ||
| 200 | |||
| 201 | |||
| 202 | static int decoder_is_tuned(struct pvr2_v4l_cx2584x *ctxt) | ||
| 203 | { | ||
| 204 | struct v4l2_tuner vt; | ||
| 205 | int ret; | ||
| 206 | |||
| 207 | memset(&vt,0,sizeof(vt)); | ||
| 208 | ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); | ||
| 209 | if (ret < 0) return -EINVAL; | ||
| 210 | return vt.signal ? 1 : 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | |||
| 214 | static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt, | ||
| 215 | char *buf,unsigned int cnt) | ||
| 216 | { | ||
| 217 | return scnprintf(buf,cnt,"handler: pvrusb2-cx2584x-v4l"); | ||
| 218 | } | ||
| 219 | |||
| 220 | |||
| 221 | static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt) | ||
| 222 | { | ||
| 223 | int ret; | ||
| 224 | ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,0); | ||
| 225 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_reset (ret=%d)",ret); | ||
| 226 | } | ||
| 227 | |||
| 228 | |||
| 229 | const static struct pvr2_i2c_handler_functions hfuncs = { | ||
| 230 | .detach = (void (*)(void *))decoder_detach, | ||
| 231 | .check = (int (*)(void *))decoder_check, | ||
| 232 | .update = (void (*)(void *))decoder_update, | ||
| 233 | .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe, | ||
| 234 | }; | ||
| 235 | |||
| 236 | |||
| 237 | int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw, | ||
| 238 | struct pvr2_i2c_client *cp) | ||
| 239 | { | ||
| 240 | struct pvr2_v4l_cx2584x *ctxt; | ||
| 241 | |||
| 242 | if (hdw->decoder_ctrl) return 0; | ||
| 243 | if (cp->handler) return 0; | ||
| 244 | if (!decoder_detect(cp)) return 0; | ||
| 245 | |||
| 246 | ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); | ||
| 247 | if (!ctxt) return 0; | ||
| 248 | memset(ctxt,0,sizeof(*ctxt)); | ||
| 249 | |||
| 250 | ctxt->handler.func_data = ctxt; | ||
| 251 | ctxt->handler.func_table = &hfuncs; | ||
| 252 | ctxt->ctrl.ctxt = ctxt; | ||
| 253 | ctxt->ctrl.detach = (void (*)(void *))decoder_detach; | ||
| 254 | ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; | ||
| 255 | ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned; | ||
| 256 | ctxt->ctrl.force_reset = (void (*)(void*))decoder_reset; | ||
| 257 | ctxt->client = cp; | ||
| 258 | ctxt->hdw = hdw; | ||
| 259 | ctxt->stale_mask = (1 << (sizeof(decoder_ops)/ | ||
| 260 | sizeof(decoder_ops[0]))) - 1; | ||
| 261 | hdw->decoder_ctrl = &ctxt->ctrl; | ||
| 262 | cp->handler = &ctxt->handler; | ||
| 263 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up", | ||
| 264 | cp->client->addr); | ||
| 265 | return !0; | ||
| 266 | } | ||
| 267 | |||
| 268 | |||
| 269 | |||
| 270 | |||
| 271 | /* | ||
| 272 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 273 | *** Local Variables: *** | ||
| 274 | *** mode: c *** | ||
| 275 | *** fill-column: 70 *** | ||
| 276 | *** tab-width: 8 *** | ||
| 277 | *** c-basic-offset: 8 *** | ||
| 278 | *** End: *** | ||
| 279 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h new file mode 100644 index 000000000000..54b2844e7a71 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef __PVRUSB2_CX2584X_V4L_H | ||
| 24 | #define __PVRUSB2_CX2584X_V4L_H | ||
| 25 | |||
| 26 | /* | ||
| 27 | |||
| 28 | This module connects the pvrusb2 driver to the I2C chip level | ||
| 29 | driver which handles combined device audio & video processing. | ||
| 30 | This interface is used internally by the driver; higher level code | ||
| 31 | should only interact through the interface provided by | ||
| 32 | pvrusb2-hdw.h. | ||
| 33 | |||
| 34 | */ | ||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | #include "pvrusb2-i2c-core.h" | ||
| 39 | |||
| 40 | int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); | ||
| 41 | |||
| 42 | |||
| 43 | #endif /* __PVRUSB2_CX2584X_V4L_H */ | ||
| 44 | |||
| 45 | /* | ||
| 46 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 47 | *** Local Variables: *** | ||
| 48 | *** mode: c *** | ||
| 49 | *** fill-column: 70 *** | ||
| 50 | *** tab-width: 8 *** | ||
| 51 | *** c-basic-offset: 8 *** | ||
| 52 | *** End: *** | ||
| 53 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h new file mode 100644 index 000000000000..d95a8588e4f8 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | /* | ||
| 2 | * $Id$ | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 18 | * | ||
| 19 | */ | ||
| 20 | #ifndef __PVRUSB2_DEBUG_H | ||
| 21 | #define __PVRUSB2_DEBUG_H | ||
| 22 | |||
| 23 | extern int pvrusb2_debug; | ||
| 24 | |||
| 25 | #define pvr2_trace(msk, fmt, arg...) do {if(msk & pvrusb2_debug) printk(KERN_INFO "pvrusb2: " fmt "\n", ##arg); } while (0) | ||
| 26 | |||
| 27 | /* These are listed in *rough* order of decreasing usefulness and | ||
| 28 | increasing noise level. */ | ||
| 29 | #define PVR2_TRACE_INFO (1 << 0) // Normal messages | ||
| 30 | #define PVR2_TRACE_ERROR_LEGS (1 << 1) // error messages | ||
| 31 | #define PVR2_TRACE_TOLERANCE (1 << 2) // track tolerance-affected errors | ||
| 32 | #define PVR2_TRACE_TRAP (1 << 3) // Trap & report misbehavior from app | ||
| 33 | #define PVR2_TRACE_INIT (1 << 4) // misc initialization steps | ||
| 34 | #define PVR2_TRACE_START_STOP (1 << 5) // Streaming start / stop | ||
| 35 | #define PVR2_TRACE_CTL (1 << 6) // commit of control changes | ||
| 36 | #define PVR2_TRACE_DEBUG (1 << 7) // Temporary debug code | ||
| 37 | #define PVR2_TRACE_EEPROM (1 << 8) // eeprom parsing / report | ||
| 38 | #define PVR2_TRACE_STRUCT (1 << 9) // internal struct creation | ||
| 39 | #define PVR2_TRACE_OPEN_CLOSE (1 << 10) // application open / close | ||
| 40 | #define PVR2_TRACE_CREG (1 << 11) // Main critical region entry / exit | ||
| 41 | #define PVR2_TRACE_SYSFS (1 << 12) // Sysfs driven I/O | ||
| 42 | #define PVR2_TRACE_FIRMWARE (1 << 13) // firmware upload actions | ||
| 43 | #define PVR2_TRACE_CHIPS (1 << 14) // chip broadcast operation | ||
| 44 | #define PVR2_TRACE_I2C (1 << 15) // I2C related stuff | ||
| 45 | #define PVR2_TRACE_I2C_CMD (1 << 16) // Software commands to I2C modules | ||
| 46 | #define PVR2_TRACE_I2C_CORE (1 << 17) // I2C core debugging | ||
| 47 | #define PVR2_TRACE_I2C_TRAF (1 << 18) // I2C traffic through the adapter | ||
| 48 | #define PVR2_TRACE_V4LIOCTL (1 << 19) // v4l ioctl details | ||
| 49 | #define PVR2_TRACE_ENCODER (1 << 20) // mpeg2 encoder operation | ||
| 50 | #define PVR2_TRACE_BUF_POOL (1 << 21) // Track buffer pool management | ||
| 51 | #define PVR2_TRACE_BUF_FLOW (1 << 22) // Track buffer flow in system | ||
| 52 | #define PVR2_TRACE_DATA_FLOW (1 << 23) // Track data flow | ||
| 53 | #define PVR2_TRACE_DEBUGIFC (1 << 24) // Debug interface actions | ||
| 54 | #define PVR2_TRACE_GPIO (1 << 25) // GPIO state bit changes | ||
| 55 | |||
| 56 | |||
| 57 | #endif /* __PVRUSB2_HDW_INTERNAL_H */ | ||
| 58 | |||
| 59 | /* | ||
| 60 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 61 | *** Local Variables: *** | ||
| 62 | *** mode: c *** | ||
| 63 | *** fill-column: 75 *** | ||
| 64 | *** tab-width: 8 *** | ||
| 65 | *** c-basic-offset: 8 *** | ||
| 66 | *** End: *** | ||
| 67 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c new file mode 100644 index 000000000000..586900e365ff --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c | |||
| @@ -0,0 +1,478 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/string.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include "pvrusb2-debugifc.h" | ||
| 25 | #include "pvrusb2-hdw.h" | ||
| 26 | #include "pvrusb2-debug.h" | ||
| 27 | #include "pvrusb2-i2c-core.h" | ||
| 28 | |||
| 29 | struct debugifc_mask_item { | ||
| 30 | const char *name; | ||
| 31 | unsigned long msk; | ||
| 32 | }; | ||
| 33 | |||
| 34 | static struct debugifc_mask_item mask_items[] = { | ||
| 35 | {"ENC_FIRMWARE",(1<<PVR2_SUBSYS_B_ENC_FIRMWARE)}, | ||
| 36 | {"ENC_CFG",(1<<PVR2_SUBSYS_B_ENC_CFG)}, | ||
| 37 | {"DIG_RUN",(1<<PVR2_SUBSYS_B_DIGITIZER_RUN)}, | ||
| 38 | {"USB_RUN",(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)}, | ||
| 39 | {"ENC_RUN",(1<<PVR2_SUBSYS_B_ENC_RUN)}, | ||
| 40 | }; | ||
| 41 | |||
| 42 | |||
| 43 | static unsigned int debugifc_count_whitespace(const char *buf, | ||
| 44 | unsigned int count) | ||
| 45 | { | ||
| 46 | unsigned int scnt; | ||
| 47 | char ch; | ||
| 48 | |||
| 49 | for (scnt = 0; scnt < count; scnt++) { | ||
| 50 | ch = buf[scnt]; | ||
| 51 | if (ch == ' ') continue; | ||
| 52 | if (ch == '\t') continue; | ||
| 53 | if (ch == '\n') continue; | ||
| 54 | break; | ||
| 55 | } | ||
| 56 | return scnt; | ||
| 57 | } | ||
| 58 | |||
| 59 | |||
| 60 | static unsigned int debugifc_count_nonwhitespace(const char *buf, | ||
| 61 | unsigned int count) | ||
| 62 | { | ||
| 63 | unsigned int scnt; | ||
| 64 | char ch; | ||
| 65 | |||
| 66 | for (scnt = 0; scnt < count; scnt++) { | ||
| 67 | ch = buf[scnt]; | ||
| 68 | if (ch == ' ') break; | ||
| 69 | if (ch == '\t') break; | ||
| 70 | if (ch == '\n') break; | ||
| 71 | } | ||
| 72 | return scnt; | ||
| 73 | } | ||
| 74 | |||
| 75 | |||
| 76 | static unsigned int debugifc_isolate_word(const char *buf,unsigned int count, | ||
| 77 | const char **wstrPtr, | ||
| 78 | unsigned int *wlenPtr) | ||
| 79 | { | ||
| 80 | const char *wptr; | ||
| 81 | unsigned int consume_cnt = 0; | ||
| 82 | unsigned int wlen; | ||
| 83 | unsigned int scnt; | ||
| 84 | |||
| 85 | wptr = 0; | ||
| 86 | wlen = 0; | ||
| 87 | scnt = debugifc_count_whitespace(buf,count); | ||
| 88 | consume_cnt += scnt; count -= scnt; buf += scnt; | ||
| 89 | if (!count) goto done; | ||
| 90 | |||
| 91 | scnt = debugifc_count_nonwhitespace(buf,count); | ||
| 92 | if (!scnt) goto done; | ||
| 93 | wptr = buf; | ||
| 94 | wlen = scnt; | ||
| 95 | consume_cnt += scnt; count -= scnt; buf += scnt; | ||
| 96 | |||
| 97 | done: | ||
| 98 | *wstrPtr = wptr; | ||
| 99 | *wlenPtr = wlen; | ||
| 100 | return consume_cnt; | ||
| 101 | } | ||
| 102 | |||
| 103 | |||
| 104 | static int debugifc_parse_unsigned_number(const char *buf,unsigned int count, | ||
| 105 | u32 *num_ptr) | ||
| 106 | { | ||
| 107 | u32 result = 0; | ||
| 108 | u32 val; | ||
| 109 | int ch; | ||
| 110 | int radix = 10; | ||
| 111 | if ((count >= 2) && (buf[0] == '0') && | ||
| 112 | ((buf[1] == 'x') || (buf[1] == 'X'))) { | ||
| 113 | radix = 16; | ||
| 114 | count -= 2; | ||
| 115 | buf += 2; | ||
| 116 | } else if ((count >= 1) && (buf[0] == '0')) { | ||
| 117 | radix = 8; | ||
| 118 | } | ||
| 119 | |||
| 120 | while (count--) { | ||
| 121 | ch = *buf++; | ||
| 122 | if ((ch >= '0') && (ch <= '9')) { | ||
| 123 | val = ch - '0'; | ||
| 124 | } else if ((ch >= 'a') && (ch <= 'f')) { | ||
| 125 | val = ch - 'a' + 10; | ||
| 126 | } else if ((ch >= 'A') && (ch <= 'F')) { | ||
| 127 | val = ch - 'A' + 10; | ||
| 128 | } else { | ||
| 129 | return -EINVAL; | ||
| 130 | } | ||
| 131 | if (val >= radix) return -EINVAL; | ||
| 132 | result *= radix; | ||
| 133 | result += val; | ||
| 134 | } | ||
| 135 | *num_ptr = result; | ||
| 136 | return 0; | ||
| 137 | } | ||
| 138 | |||
| 139 | |||
| 140 | static int debugifc_match_keyword(const char *buf,unsigned int count, | ||
| 141 | const char *keyword) | ||
| 142 | { | ||
| 143 | unsigned int kl; | ||
| 144 | if (!keyword) return 0; | ||
| 145 | kl = strlen(keyword); | ||
| 146 | if (kl != count) return 0; | ||
| 147 | return !memcmp(buf,keyword,kl); | ||
| 148 | } | ||
| 149 | |||
| 150 | |||
| 151 | static unsigned long debugifc_find_mask(const char *buf,unsigned int count) | ||
| 152 | { | ||
| 153 | struct debugifc_mask_item *mip; | ||
| 154 | unsigned int idx; | ||
| 155 | for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) { | ||
| 156 | mip = mask_items + idx; | ||
| 157 | if (debugifc_match_keyword(buf,count,mip->name)) { | ||
| 158 | return mip->msk; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | return 0; | ||
| 162 | } | ||
| 163 | |||
| 164 | |||
| 165 | static int debugifc_print_mask(char *buf,unsigned int sz, | ||
| 166 | unsigned long msk,unsigned long val) | ||
| 167 | { | ||
| 168 | struct debugifc_mask_item *mip; | ||
| 169 | unsigned int idx; | ||
| 170 | int bcnt = 0; | ||
| 171 | int ccnt; | ||
| 172 | for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) { | ||
| 173 | mip = mask_items + idx; | ||
| 174 | if (!(mip->msk & msk)) continue; | ||
| 175 | ccnt = scnprintf(buf,sz,"%s%c%s", | ||
| 176 | (bcnt ? " " : ""), | ||
| 177 | ((mip->msk & val) ? '+' : '-'), | ||
| 178 | mip->name); | ||
| 179 | sz -= ccnt; | ||
| 180 | buf += ccnt; | ||
| 181 | bcnt += ccnt; | ||
| 182 | } | ||
| 183 | return bcnt; | ||
| 184 | } | ||
| 185 | |||
| 186 | static unsigned int debugifc_parse_subsys_mask(const char *buf, | ||
| 187 | unsigned int count, | ||
| 188 | unsigned long *mskPtr, | ||
| 189 | unsigned long *valPtr) | ||
| 190 | { | ||
| 191 | const char *wptr; | ||
| 192 | unsigned int consume_cnt = 0; | ||
| 193 | unsigned int scnt; | ||
| 194 | unsigned int wlen; | ||
| 195 | int mode; | ||
| 196 | unsigned long m1,msk,val; | ||
| 197 | |||
| 198 | msk = 0; | ||
| 199 | val = 0; | ||
| 200 | |||
| 201 | while (count) { | ||
| 202 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
| 203 | if (!scnt) break; | ||
| 204 | consume_cnt += scnt; count -= scnt; buf += scnt; | ||
| 205 | if (!wptr) break; | ||
| 206 | |||
| 207 | mode = 0; | ||
| 208 | if (wlen) switch (wptr[0]) { | ||
| 209 | case '+': | ||
| 210 | wptr++; | ||
| 211 | wlen--; | ||
| 212 | break; | ||
| 213 | case '-': | ||
| 214 | mode = 1; | ||
| 215 | wptr++; | ||
| 216 | wlen--; | ||
| 217 | break; | ||
| 218 | } | ||
| 219 | if (!wlen) continue; | ||
| 220 | m1 = debugifc_find_mask(wptr,wlen); | ||
| 221 | if (!m1) break; | ||
| 222 | msk |= m1; | ||
| 223 | if (!mode) val |= m1; | ||
| 224 | } | ||
| 225 | *mskPtr = msk; | ||
| 226 | *valPtr = val; | ||
| 227 | return consume_cnt; | ||
| 228 | } | ||
| 229 | |||
| 230 | |||
| 231 | int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) | ||
| 232 | { | ||
| 233 | int bcnt = 0; | ||
| 234 | int ccnt; | ||
| 235 | struct pvr2_hdw_debug_info dbg; | ||
| 236 | |||
| 237 | pvr2_hdw_get_debug_info(hdw,&dbg); | ||
| 238 | |||
| 239 | ccnt = scnprintf(buf,acnt,"big lock %s; ctl lock %s", | ||
| 240 | (dbg.big_lock_held ? "held" : "free"), | ||
| 241 | (dbg.ctl_lock_held ? "held" : "free")); | ||
| 242 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 243 | if (dbg.ctl_lock_held) { | ||
| 244 | ccnt = scnprintf(buf,acnt,"; cmd_state=%d cmd_code=%d" | ||
| 245 | " cmd_wlen=%d cmd_rlen=%d" | ||
| 246 | " wpend=%d rpend=%d tmout=%d rstatus=%d" | ||
| 247 | " wstatus=%d", | ||
| 248 | dbg.cmd_debug_state,dbg.cmd_code, | ||
| 249 | dbg.cmd_debug_write_len, | ||
| 250 | dbg.cmd_debug_read_len, | ||
| 251 | dbg.cmd_debug_write_pend, | ||
| 252 | dbg.cmd_debug_read_pend, | ||
| 253 | dbg.cmd_debug_timeout, | ||
| 254 | dbg.cmd_debug_rstatus, | ||
| 255 | dbg.cmd_debug_wstatus); | ||
| 256 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 257 | } | ||
| 258 | ccnt = scnprintf(buf,acnt,"\n"); | ||
| 259 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 260 | ccnt = scnprintf( | ||
| 261 | buf,acnt,"driver flags: %s %s %s\n", | ||
| 262 | (dbg.flag_init_ok ? "initialized" : "uninitialized"), | ||
| 263 | (dbg.flag_ok ? "ok" : "fail"), | ||
| 264 | (dbg.flag_disconnected ? "disconnected" : "connected")); | ||
| 265 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 266 | ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: "); | ||
| 267 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 268 | ccnt = debugifc_print_mask(buf,acnt,dbg.subsys_flags,dbg.subsys_flags); | ||
| 269 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 270 | ccnt = scnprintf(buf,acnt,"\n"); | ||
| 271 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 272 | ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: "); | ||
| 273 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 274 | ccnt = debugifc_print_mask(buf,acnt,~dbg.subsys_flags,dbg.subsys_flags); | ||
| 275 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 276 | ccnt = scnprintf(buf,acnt,"\n"); | ||
| 277 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 278 | |||
| 279 | ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n"); | ||
| 280 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 281 | ccnt = pvr2_i2c_report(hdw,buf,acnt); | ||
| 282 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 283 | |||
| 284 | return bcnt; | ||
| 285 | } | ||
| 286 | |||
| 287 | |||
| 288 | int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, | ||
| 289 | char *buf,unsigned int acnt) | ||
| 290 | { | ||
| 291 | int bcnt = 0; | ||
| 292 | int ccnt; | ||
| 293 | unsigned long msk; | ||
| 294 | int ret; | ||
| 295 | u32 gpio_dir,gpio_in,gpio_out; | ||
| 296 | |||
| 297 | ret = pvr2_hdw_is_hsm(hdw); | ||
| 298 | ccnt = scnprintf(buf,acnt,"USB link speed: %s\n", | ||
| 299 | (ret < 0 ? "FAIL" : (ret ? "high" : "full"))); | ||
| 300 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 301 | |||
| 302 | gpio_dir = 0; gpio_in = 0; gpio_out = 0; | ||
| 303 | pvr2_hdw_gpio_get_dir(hdw,&gpio_dir); | ||
| 304 | pvr2_hdw_gpio_get_out(hdw,&gpio_out); | ||
| 305 | pvr2_hdw_gpio_get_in(hdw,&gpio_in); | ||
| 306 | ccnt = scnprintf(buf,acnt,"GPIO state: dir=0x%x in=0x%x out=0x%x\n", | ||
| 307 | gpio_dir,gpio_in,gpio_out); | ||
| 308 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 309 | |||
| 310 | ccnt = scnprintf(buf,acnt,"Streaming is %s\n", | ||
| 311 | pvr2_hdw_get_streaming(hdw) ? "on" : "off"); | ||
| 312 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 313 | |||
| 314 | msk = pvr2_hdw_subsys_get(hdw); | ||
| 315 | ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: "); | ||
| 316 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 317 | ccnt = debugifc_print_mask(buf,acnt,msk,msk); | ||
| 318 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 319 | ccnt = scnprintf(buf,acnt,"\n"); | ||
| 320 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 321 | ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: "); | ||
| 322 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 323 | ccnt = debugifc_print_mask(buf,acnt,~msk,msk); | ||
| 324 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 325 | ccnt = scnprintf(buf,acnt,"\n"); | ||
| 326 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 327 | |||
| 328 | msk = pvr2_hdw_subsys_stream_get(hdw); | ||
| 329 | ccnt = scnprintf(buf,acnt,"Subsystems stopped on stream shutdown: "); | ||
| 330 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 331 | ccnt = debugifc_print_mask(buf,acnt,msk,msk); | ||
| 332 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 333 | ccnt = scnprintf(buf,acnt,"\n"); | ||
| 334 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
| 335 | |||
| 336 | return bcnt; | ||
| 337 | } | ||
| 338 | |||
| 339 | |||
| 340 | int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, | ||
| 341 | unsigned int count) | ||
| 342 | { | ||
| 343 | const char *wptr; | ||
| 344 | unsigned int wlen; | ||
| 345 | unsigned int scnt; | ||
| 346 | |||
| 347 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
| 348 | if (!scnt) return 0; | ||
| 349 | count -= scnt; buf += scnt; | ||
| 350 | if (!wptr) return 0; | ||
| 351 | |||
| 352 | pvr2_trace(PVR2_TRACE_DEBUGIFC,"debugifc cmd: \"%.*s\"",wlen,wptr); | ||
| 353 | if (debugifc_match_keyword(wptr,wlen,"reset")) { | ||
| 354 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
| 355 | if (!scnt) return -EINVAL; | ||
| 356 | count -= scnt; buf += scnt; | ||
| 357 | if (!wptr) return -EINVAL; | ||
| 358 | if (debugifc_match_keyword(wptr,wlen,"cpu")) { | ||
| 359 | pvr2_hdw_cpureset_assert(hdw,!0); | ||
| 360 | pvr2_hdw_cpureset_assert(hdw,0); | ||
| 361 | return 0; | ||
| 362 | } else if (debugifc_match_keyword(wptr,wlen,"bus")) { | ||
| 363 | pvr2_hdw_device_reset(hdw); | ||
| 364 | } else if (debugifc_match_keyword(wptr,wlen,"soft")) { | ||
| 365 | return pvr2_hdw_cmd_powerup(hdw); | ||
| 366 | } else if (debugifc_match_keyword(wptr,wlen,"deep")) { | ||
| 367 | return pvr2_hdw_cmd_deep_reset(hdw); | ||
| 368 | } else if (debugifc_match_keyword(wptr,wlen,"firmware")) { | ||
| 369 | return pvr2_upload_firmware2(hdw); | ||
| 370 | } else if (debugifc_match_keyword(wptr,wlen,"decoder")) { | ||
| 371 | return pvr2_hdw_cmd_decoder_reset(hdw); | ||
| 372 | } | ||
| 373 | return -EINVAL; | ||
| 374 | } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) { | ||
| 375 | unsigned long msk = 0; | ||
| 376 | unsigned long val = 0; | ||
| 377 | if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) { | ||
| 378 | pvr2_trace(PVR2_TRACE_DEBUGIFC, | ||
| 379 | "debugifc parse error on subsys mask"); | ||
| 380 | return -EINVAL; | ||
| 381 | } | ||
| 382 | pvr2_hdw_subsys_bit_chg(hdw,msk,val); | ||
| 383 | return 0; | ||
| 384 | } else if (debugifc_match_keyword(wptr,wlen,"stream_flags")) { | ||
| 385 | unsigned long msk = 0; | ||
| 386 | unsigned long val = 0; | ||
| 387 | if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) { | ||
| 388 | pvr2_trace(PVR2_TRACE_DEBUGIFC, | ||
| 389 | "debugifc parse error on stream mask"); | ||
| 390 | return -EINVAL; | ||
| 391 | } | ||
| 392 | pvr2_hdw_subsys_stream_bit_chg(hdw,msk,val); | ||
| 393 | return 0; | ||
| 394 | } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) { | ||
| 395 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
| 396 | if (!scnt) return -EINVAL; | ||
| 397 | count -= scnt; buf += scnt; | ||
| 398 | if (!wptr) return -EINVAL; | ||
| 399 | if (debugifc_match_keyword(wptr,wlen,"fetch")) { | ||
| 400 | pvr2_hdw_cpufw_set_enabled(hdw,!0); | ||
| 401 | return 0; | ||
| 402 | } else if (debugifc_match_keyword(wptr,wlen,"done")) { | ||
| 403 | pvr2_hdw_cpufw_set_enabled(hdw,0); | ||
| 404 | return 0; | ||
| 405 | } else { | ||
| 406 | return -EINVAL; | ||
| 407 | } | ||
| 408 | } else if (debugifc_match_keyword(wptr,wlen,"gpio")) { | ||
| 409 | int dir_fl = 0; | ||
| 410 | int ret; | ||
| 411 | u32 msk,val; | ||
| 412 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
| 413 | if (!scnt) return -EINVAL; | ||
| 414 | count -= scnt; buf += scnt; | ||
| 415 | if (!wptr) return -EINVAL; | ||
| 416 | if (debugifc_match_keyword(wptr,wlen,"dir")) { | ||
| 417 | dir_fl = !0; | ||
| 418 | } else if (!debugifc_match_keyword(wptr,wlen,"out")) { | ||
| 419 | return -EINVAL; | ||
| 420 | } | ||
| 421 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
| 422 | if (!scnt) return -EINVAL; | ||
| 423 | count -= scnt; buf += scnt; | ||
| 424 | if (!wptr) return -EINVAL; | ||
| 425 | ret = debugifc_parse_unsigned_number(wptr,wlen,&msk); | ||
| 426 | if (ret) return ret; | ||
| 427 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
| 428 | if (wptr) { | ||
| 429 | ret = debugifc_parse_unsigned_number(wptr,wlen,&val); | ||
| 430 | if (ret) return ret; | ||
| 431 | } else { | ||
| 432 | val = msk; | ||
| 433 | msk = 0xffffffff; | ||
| 434 | } | ||
| 435 | if (dir_fl) { | ||
| 436 | ret = pvr2_hdw_gpio_chg_dir(hdw,msk,val); | ||
| 437 | } else { | ||
| 438 | ret = pvr2_hdw_gpio_chg_out(hdw,msk,val); | ||
| 439 | } | ||
| 440 | return ret; | ||
| 441 | } | ||
| 442 | pvr2_trace(PVR2_TRACE_DEBUGIFC, | ||
| 443 | "debugifc failed to recognize cmd: \"%.*s\"",wlen,wptr); | ||
| 444 | return -EINVAL; | ||
| 445 | } | ||
| 446 | |||
| 447 | |||
| 448 | int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf, | ||
| 449 | unsigned int count) | ||
| 450 | { | ||
| 451 | unsigned int bcnt = 0; | ||
| 452 | int ret; | ||
| 453 | |||
| 454 | while (count) { | ||
| 455 | for (bcnt = 0; bcnt < count; bcnt++) { | ||
| 456 | if (buf[bcnt] == '\n') break; | ||
| 457 | } | ||
| 458 | |||
| 459 | ret = pvr2_debugifc_do1cmd(hdw,buf,bcnt); | ||
| 460 | if (ret < 0) return ret; | ||
| 461 | if (bcnt < count) bcnt++; | ||
| 462 | buf += bcnt; | ||
| 463 | count -= bcnt; | ||
| 464 | } | ||
| 465 | |||
| 466 | return 0; | ||
| 467 | } | ||
| 468 | |||
| 469 | |||
| 470 | /* | ||
| 471 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 472 | *** Local Variables: *** | ||
| 473 | *** mode: c *** | ||
| 474 | *** fill-column: 75 *** | ||
| 475 | *** tab-width: 8 *** | ||
| 476 | *** c-basic-offset: 8 *** | ||
| 477 | *** End: *** | ||
| 478 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h new file mode 100644 index 000000000000..990b02d35d36 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_DEBUGIFC_H | ||
| 22 | #define __PVRUSB2_DEBUGIFC_H | ||
| 23 | |||
| 24 | struct pvr2_hdw; | ||
| 25 | |||
| 26 | /* Non-intrusively print some useful debugging info from inside the | ||
| 27 | driver. This should work even if the driver appears to be | ||
| 28 | wedged. */ | ||
| 29 | int pvr2_debugifc_print_info(struct pvr2_hdw *, | ||
| 30 | char *buf_ptr,unsigned int buf_size); | ||
| 31 | |||
| 32 | /* Print general status of driver. This will also trigger a probe of | ||
| 33 | the USB link. Unlike print_info(), this one synchronizes with the | ||
| 34 | driver so the information should be self-consistent (but it will | ||
| 35 | hang if the driver is wedged). */ | ||
| 36 | int pvr2_debugifc_print_status(struct pvr2_hdw *, | ||
| 37 | char *buf_ptr,unsigned int buf_size); | ||
| 38 | |||
| 39 | /* Parse a string command into a driver action. */ | ||
| 40 | int pvr2_debugifc_docmd(struct pvr2_hdw *, | ||
| 41 | const char *buf_ptr,unsigned int buf_size); | ||
| 42 | |||
| 43 | #endif /* __PVRUSB2_DEBUGIFC_H */ | ||
| 44 | |||
| 45 | /* | ||
| 46 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 47 | *** Local Variables: *** | ||
| 48 | *** mode: c *** | ||
| 49 | *** fill-column: 75 *** | ||
| 50 | *** tab-width: 8 *** | ||
| 51 | *** c-basic-offset: 8 *** | ||
| 52 | *** End: *** | ||
| 53 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-demod.c b/drivers/media/video/pvrusb2/pvrusb2-demod.c new file mode 100644 index 000000000000..9686569a11f6 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-demod.c | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include "pvrusb2.h" | ||
| 24 | #include "pvrusb2-util.h" | ||
| 25 | #include "pvrusb2-demod.h" | ||
| 26 | #include "pvrusb2-hdw-internal.h" | ||
| 27 | #include "pvrusb2-debug.h" | ||
| 28 | #include <linux/videodev2.h> | ||
| 29 | #include <media/tuner.h> | ||
| 30 | #include <media/v4l2-common.h> | ||
| 31 | |||
| 32 | |||
| 33 | struct pvr2_demod_handler { | ||
| 34 | struct pvr2_hdw *hdw; | ||
| 35 | struct pvr2_i2c_client *client; | ||
| 36 | struct pvr2_i2c_handler i2c_handler; | ||
| 37 | int type_update_fl; | ||
| 38 | }; | ||
| 39 | |||
| 40 | |||
| 41 | static void set_config(struct pvr2_demod_handler *ctxt) | ||
| 42 | { | ||
| 43 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 44 | int cfg = 0; | ||
| 45 | |||
| 46 | switch (hdw->tuner_type) { | ||
| 47 | case TUNER_PHILIPS_FM1216ME_MK3: | ||
| 48 | case TUNER_PHILIPS_FM1236_MK3: | ||
| 49 | cfg = TDA9887_PORT1_ACTIVE|TDA9887_PORT2_ACTIVE; | ||
| 50 | break; | ||
| 51 | default: | ||
| 52 | break; | ||
| 53 | } | ||
| 54 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c demod set_config(0x%x)",cfg); | ||
| 55 | pvr2_i2c_client_cmd(ctxt->client,TDA9887_SET_CONFIG,&cfg); | ||
| 56 | ctxt->type_update_fl = 0; | ||
| 57 | } | ||
| 58 | |||
| 59 | |||
| 60 | static int demod_check(struct pvr2_demod_handler *ctxt) | ||
| 61 | { | ||
| 62 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 63 | if (hdw->tuner_updated) ctxt->type_update_fl = !0; | ||
| 64 | return ctxt->type_update_fl != 0; | ||
| 65 | } | ||
| 66 | |||
| 67 | |||
| 68 | static void demod_update(struct pvr2_demod_handler *ctxt) | ||
| 69 | { | ||
| 70 | if (ctxt->type_update_fl) set_config(ctxt); | ||
| 71 | } | ||
| 72 | |||
| 73 | |||
| 74 | static void demod_detach(struct pvr2_demod_handler *ctxt) | ||
| 75 | { | ||
| 76 | ctxt->client->handler = 0; | ||
| 77 | kfree(ctxt); | ||
| 78 | } | ||
| 79 | |||
| 80 | |||
| 81 | static unsigned int demod_describe(struct pvr2_demod_handler *ctxt,char *buf,unsigned int cnt) | ||
| 82 | { | ||
| 83 | return scnprintf(buf,cnt,"handler: pvrusb2-demod"); | ||
| 84 | } | ||
| 85 | |||
| 86 | |||
| 87 | const static struct pvr2_i2c_handler_functions tuner_funcs = { | ||
| 88 | .detach = (void (*)(void *))demod_detach, | ||
| 89 | .check = (int (*)(void *))demod_check, | ||
| 90 | .update = (void (*)(void *))demod_update, | ||
| 91 | .describe = (unsigned int (*)(void *,char *,unsigned int))demod_describe, | ||
| 92 | }; | ||
| 93 | |||
| 94 | |||
| 95 | int pvr2_i2c_demod_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) | ||
| 96 | { | ||
| 97 | struct pvr2_demod_handler *ctxt; | ||
| 98 | if (cp->handler) return 0; | ||
| 99 | |||
| 100 | ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); | ||
| 101 | if (!ctxt) return 0; | ||
| 102 | memset(ctxt,0,sizeof(*ctxt)); | ||
| 103 | |||
| 104 | ctxt->i2c_handler.func_data = ctxt; | ||
| 105 | ctxt->i2c_handler.func_table = &tuner_funcs; | ||
| 106 | ctxt->type_update_fl = !0; | ||
| 107 | ctxt->client = cp; | ||
| 108 | ctxt->hdw = hdw; | ||
| 109 | cp->handler = &ctxt->i2c_handler; | ||
| 110 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x tda9887 V4L2 handler set up", | ||
| 111 | cp->client->addr); | ||
| 112 | return !0; | ||
| 113 | } | ||
| 114 | |||
| 115 | |||
| 116 | |||
| 117 | |||
| 118 | /* | ||
| 119 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 120 | *** Local Variables: *** | ||
| 121 | *** mode: c *** | ||
| 122 | *** fill-column: 70 *** | ||
| 123 | *** tab-width: 8 *** | ||
| 124 | *** c-basic-offset: 8 *** | ||
| 125 | *** End: *** | ||
| 126 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-demod.h b/drivers/media/video/pvrusb2/pvrusb2-demod.h new file mode 100644 index 000000000000..4c4e40ffbf03 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-demod.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_DEMOD_H | ||
| 22 | #define __PVRUSB2_DEMOD_H | ||
| 23 | |||
| 24 | #include "pvrusb2-i2c-core.h" | ||
| 25 | |||
| 26 | int pvr2_i2c_demod_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); | ||
| 27 | |||
| 28 | #endif /* __PVRUSB2_DEMOD_H */ | ||
| 29 | |||
| 30 | /* | ||
| 31 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 32 | *** Local Variables: *** | ||
| 33 | *** mode: c *** | ||
| 34 | *** fill-column: 70 *** | ||
| 35 | *** tab-width: 8 *** | ||
| 36 | *** c-basic-offset: 8 *** | ||
| 37 | *** End: *** | ||
| 38 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c new file mode 100644 index 000000000000..94d383ff9889 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c | |||
| @@ -0,0 +1,164 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include "pvrusb2-eeprom.h" | ||
| 24 | #include "pvrusb2-hdw-internal.h" | ||
| 25 | #include "pvrusb2-debug.h" | ||
| 26 | |||
| 27 | #define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__) | ||
| 28 | |||
| 29 | |||
| 30 | |||
| 31 | /* | ||
| 32 | |||
| 33 | Read and analyze data in the eeprom. Use tveeprom to figure out | ||
| 34 | the packet structure, since this is another Hauppauge device and | ||
| 35 | internally it has a family resemblence to ivtv-type devices | ||
| 36 | |||
| 37 | */ | ||
| 38 | |||
| 39 | #include <media/tveeprom.h> | ||
| 40 | |||
| 41 | /* We seem to only be interested in the last 128 bytes of the EEPROM */ | ||
| 42 | #define EEPROM_SIZE 128 | ||
| 43 | |||
| 44 | /* Grab EEPROM contents, needed for direct method. */ | ||
| 45 | static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) | ||
| 46 | { | ||
| 47 | struct i2c_msg msg[2]; | ||
| 48 | u8 *eeprom; | ||
| 49 | u8 iadd[2]; | ||
| 50 | u8 addr; | ||
| 51 | u16 eepromSize; | ||
| 52 | unsigned int offs; | ||
| 53 | int ret; | ||
| 54 | int mode16 = 0; | ||
| 55 | unsigned pcnt,tcnt; | ||
| 56 | eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL); | ||
| 57 | if (!eeprom) { | ||
| 58 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 59 | "Failed to allocate memory" | ||
| 60 | " required to read eeprom"); | ||
| 61 | return 0; | ||
| 62 | } | ||
| 63 | |||
| 64 | trace_eeprom("Value for eeprom addr from controller was 0x%x", | ||
| 65 | hdw->eeprom_addr); | ||
| 66 | addr = hdw->eeprom_addr; | ||
| 67 | /* Seems that if the high bit is set, then the *real* eeprom | ||
| 68 | address is shifted right now bit position (noticed this in | ||
| 69 | newer PVR USB2 hardware) */ | ||
| 70 | if (addr & 0x80) addr >>= 1; | ||
| 71 | |||
| 72 | /* FX2 documentation states that a 16bit-addressed eeprom is | ||
| 73 | expected if the I2C address is an odd number (yeah, this is | ||
| 74 | strange but it's what they do) */ | ||
| 75 | mode16 = (addr & 1); | ||
| 76 | eepromSize = (mode16 ? 4096 : 256); | ||
| 77 | trace_eeprom("Examining %d byte eeprom at location 0x%x" | ||
| 78 | " using %d bit addressing",eepromSize,addr, | ||
| 79 | mode16 ? 16 : 8); | ||
| 80 | |||
| 81 | msg[0].addr = addr; | ||
| 82 | msg[0].flags = 0; | ||
| 83 | msg[0].len = mode16 ? 2 : 1; | ||
| 84 | msg[0].buf = iadd; | ||
| 85 | msg[1].addr = addr; | ||
| 86 | msg[1].flags = I2C_M_RD; | ||
| 87 | |||
| 88 | /* We have to do the actual eeprom data fetch ourselves, because | ||
| 89 | (1) we're only fetching part of the eeprom, and (2) if we were | ||
| 90 | getting the whole thing our I2C driver can't grab it in one | ||
| 91 | pass - which is what tveeprom is otherwise going to attempt */ | ||
| 92 | memset(eeprom,0,EEPROM_SIZE); | ||
| 93 | for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) { | ||
| 94 | pcnt = 16; | ||
| 95 | if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt; | ||
| 96 | offs = tcnt + (eepromSize - EEPROM_SIZE); | ||
| 97 | if (mode16) { | ||
| 98 | iadd[0] = offs >> 8; | ||
| 99 | iadd[1] = offs; | ||
| 100 | } else { | ||
| 101 | iadd[0] = offs; | ||
| 102 | } | ||
| 103 | msg[1].len = pcnt; | ||
| 104 | msg[1].buf = eeprom+tcnt; | ||
| 105 | if ((ret = i2c_transfer( | ||
| 106 | &hdw->i2c_adap, | ||
| 107 | msg,sizeof(msg)/sizeof(msg[0]))) != 2) { | ||
| 108 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 109 | "eeprom fetch set offs err=%d",ret); | ||
| 110 | kfree(eeprom); | ||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | return eeprom; | ||
| 115 | } | ||
| 116 | |||
| 117 | |||
| 118 | /* Directly call eeprom analysis function within tveeprom. */ | ||
| 119 | int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) | ||
| 120 | { | ||
| 121 | u8 *eeprom; | ||
| 122 | struct tveeprom tvdata; | ||
| 123 | |||
| 124 | memset(&tvdata,0,sizeof(tvdata)); | ||
| 125 | |||
| 126 | eeprom = pvr2_eeprom_fetch(hdw); | ||
| 127 | if (!eeprom) return -EINVAL; | ||
| 128 | |||
| 129 | { | ||
| 130 | struct i2c_client fake_client; | ||
| 131 | /* Newer version expects a useless client interface */ | ||
| 132 | fake_client.addr = hdw->eeprom_addr; | ||
| 133 | fake_client.adapter = &hdw->i2c_adap; | ||
| 134 | tveeprom_hauppauge_analog(&fake_client,&tvdata,eeprom); | ||
| 135 | } | ||
| 136 | |||
| 137 | trace_eeprom("eeprom assumed v4l tveeprom module"); | ||
| 138 | trace_eeprom("eeprom direct call results:"); | ||
| 139 | trace_eeprom("has_radio=%d",tvdata.has_radio); | ||
| 140 | trace_eeprom("tuner_type=%d",tvdata.tuner_type); | ||
| 141 | trace_eeprom("tuner_formats=0x%x",tvdata.tuner_formats); | ||
| 142 | trace_eeprom("audio_processor=%d",tvdata.audio_processor); | ||
| 143 | trace_eeprom("model=%d",tvdata.model); | ||
| 144 | trace_eeprom("revision=%d",tvdata.revision); | ||
| 145 | trace_eeprom("serial_number=%d",tvdata.serial_number); | ||
| 146 | trace_eeprom("rev_str=%s",tvdata.rev_str); | ||
| 147 | hdw->tuner_type = tvdata.tuner_type; | ||
| 148 | hdw->serial_number = tvdata.serial_number; | ||
| 149 | hdw->std_mask_eeprom = tvdata.tuner_formats; | ||
| 150 | |||
| 151 | kfree(eeprom); | ||
| 152 | |||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* | ||
| 157 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 158 | *** Local Variables: *** | ||
| 159 | *** mode: c *** | ||
| 160 | *** fill-column: 70 *** | ||
| 161 | *** tab-width: 8 *** | ||
| 162 | *** c-basic-offset: 8 *** | ||
| 163 | *** End: *** | ||
| 164 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.h b/drivers/media/video/pvrusb2/pvrusb2-eeprom.h new file mode 100644 index 000000000000..84242975dea7 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef __PVRUSB2_EEPROM_H | ||
| 24 | #define __PVRUSB2_EEPROM_H | ||
| 25 | |||
| 26 | struct pvr2_hdw; | ||
| 27 | |||
| 28 | int pvr2_eeprom_analyze(struct pvr2_hdw *); | ||
| 29 | |||
| 30 | #endif /* __PVRUSB2_EEPROM_H */ | ||
| 31 | |||
| 32 | /* | ||
| 33 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 34 | *** Local Variables: *** | ||
| 35 | *** mode: c *** | ||
| 36 | *** fill-column: 70 *** | ||
| 37 | *** tab-width: 8 *** | ||
| 38 | *** c-basic-offset: 8 *** | ||
| 39 | *** End: *** | ||
| 40 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c new file mode 100644 index 000000000000..2cc31695b435 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c | |||
| @@ -0,0 +1,418 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/device.h> // for linux/firmware.h | ||
| 24 | #include <linux/firmware.h> | ||
| 25 | #include "pvrusb2-util.h" | ||
| 26 | #include "pvrusb2-encoder.h" | ||
| 27 | #include "pvrusb2-hdw-internal.h" | ||
| 28 | #include "pvrusb2-debug.h" | ||
| 29 | |||
| 30 | |||
| 31 | |||
| 32 | /* Firmware mailbox flags - definitions found from ivtv */ | ||
| 33 | #define IVTV_MBOX_FIRMWARE_DONE 0x00000004 | ||
| 34 | #define IVTV_MBOX_DRIVER_DONE 0x00000002 | ||
| 35 | #define IVTV_MBOX_DRIVER_BUSY 0x00000001 | ||
| 36 | |||
| 37 | |||
| 38 | static int pvr2_encoder_write_words(struct pvr2_hdw *hdw, | ||
| 39 | const u32 *data, unsigned int dlen) | ||
| 40 | { | ||
| 41 | unsigned int idx; | ||
| 42 | int ret; | ||
| 43 | unsigned int offs = 0; | ||
| 44 | unsigned int chunkCnt; | ||
| 45 | |||
| 46 | /* | ||
| 47 | |||
| 48 | Format: First byte must be 0x01. Remaining 32 bit words are | ||
| 49 | spread out into chunks of 7 bytes each, little-endian ordered, | ||
| 50 | offset at zero within each 2 blank bytes following and a | ||
| 51 | single byte that is 0x44 plus the offset of the word. Repeat | ||
| 52 | request for additional words, with offset adjusted | ||
| 53 | accordingly. | ||
| 54 | |||
| 55 | */ | ||
| 56 | while (dlen) { | ||
| 57 | chunkCnt = 8; | ||
| 58 | if (chunkCnt > dlen) chunkCnt = dlen; | ||
| 59 | memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer)); | ||
| 60 | hdw->cmd_buffer[0] = 0x01; | ||
| 61 | for (idx = 0; idx < chunkCnt; idx++) { | ||
| 62 | hdw->cmd_buffer[1+(idx*7)+6] = 0x44 + idx + offs; | ||
| 63 | PVR2_DECOMPOSE_LE(hdw->cmd_buffer, 1+(idx*7), | ||
| 64 | data[idx]); | ||
| 65 | } | ||
| 66 | ret = pvr2_send_request(hdw, | ||
| 67 | hdw->cmd_buffer,1+(chunkCnt*7), | ||
| 68 | 0,0); | ||
| 69 | if (ret) return ret; | ||
| 70 | data += chunkCnt; | ||
| 71 | dlen -= chunkCnt; | ||
| 72 | offs += chunkCnt; | ||
| 73 | } | ||
| 74 | |||
| 75 | return 0; | ||
| 76 | } | ||
| 77 | |||
| 78 | |||
| 79 | static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,int statusFl, | ||
| 80 | u32 *data, unsigned int dlen) | ||
| 81 | { | ||
| 82 | unsigned int idx; | ||
| 83 | int ret; | ||
| 84 | unsigned int offs = 0; | ||
| 85 | unsigned int chunkCnt; | ||
| 86 | |||
| 87 | /* | ||
| 88 | |||
| 89 | Format: First byte must be 0x02 (status check) or 0x28 (read | ||
| 90 | back block of 32 bit words). Next 6 bytes must be zero, | ||
| 91 | followed by a single byte of 0x44+offset for portion to be | ||
| 92 | read. Returned data is packed set of 32 bits words that were | ||
| 93 | read. | ||
| 94 | |||
| 95 | */ | ||
| 96 | |||
| 97 | while (dlen) { | ||
| 98 | chunkCnt = 16; | ||
| 99 | if (chunkCnt > dlen) chunkCnt = dlen; | ||
| 100 | memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer)); | ||
| 101 | hdw->cmd_buffer[0] = statusFl ? 0x02 : 0x28; | ||
| 102 | hdw->cmd_buffer[7] = 0x44 + offs; | ||
| 103 | ret = pvr2_send_request(hdw, | ||
| 104 | hdw->cmd_buffer,8, | ||
| 105 | hdw->cmd_buffer,chunkCnt * 4); | ||
| 106 | if (ret) return ret; | ||
| 107 | |||
| 108 | for (idx = 0; idx < chunkCnt; idx++) { | ||
| 109 | data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4); | ||
| 110 | } | ||
| 111 | data += chunkCnt; | ||
| 112 | dlen -= chunkCnt; | ||
| 113 | offs += chunkCnt; | ||
| 114 | } | ||
| 115 | |||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | |||
| 120 | /* This prototype is set up to be compatible with the | ||
| 121 | cx2341x_mbox_func prototype in cx2341x.h, which should be in | ||
| 122 | kernels 2.6.18 or later. We do this so that we can enable | ||
| 123 | cx2341x.ko to write to our encoder (by handing it a pointer to this | ||
| 124 | function). For earlier kernels this doesn't really matter. */ | ||
| 125 | static int pvr2_encoder_cmd(void *ctxt, | ||
| 126 | int cmd, | ||
| 127 | int arg_cnt_send, | ||
| 128 | int arg_cnt_recv, | ||
| 129 | u32 *argp) | ||
| 130 | { | ||
| 131 | unsigned int poll_count; | ||
| 132 | int ret = 0; | ||
| 133 | unsigned int idx; | ||
| 134 | /* These sizes look to be limited by the FX2 firmware implementation */ | ||
| 135 | u32 wrData[16]; | ||
| 136 | u32 rdData[16]; | ||
| 137 | struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt; | ||
| 138 | |||
| 139 | |||
| 140 | /* | ||
| 141 | |||
| 142 | The encoder seems to speak entirely using blocks 32 bit words. | ||
| 143 | In ivtv driver terms, this is a mailbox which we populate with | ||
| 144 | data and watch what the hardware does with it. The first word | ||
| 145 | is a set of flags used to control the transaction, the second | ||
| 146 | word is the command to execute, the third byte is zero (ivtv | ||
| 147 | driver suggests that this is some kind of return value), and | ||
| 148 | the fourth byte is a specified timeout (windows driver always | ||
| 149 | uses 0x00060000 except for one case when it is zero). All | ||
| 150 | successive words are the argument words for the command. | ||
| 151 | |||
| 152 | First, write out the entire set of words, with the first word | ||
| 153 | being zero. | ||
| 154 | |||
| 155 | Next, write out just the first word again, but set it to | ||
| 156 | IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which | ||
| 157 | probably means "go"). | ||
| 158 | |||
| 159 | Next, read back 16 words as status. Check the first word, | ||
| 160 | which should have IVTV_MBOX_FIRMWARE_DONE set. If however | ||
| 161 | that bit is not set, then the command isn't done so repeat the | ||
| 162 | read. | ||
| 163 | |||
| 164 | Next, read back 32 words and compare with the original | ||
| 165 | arugments. Hopefully they will match. | ||
| 166 | |||
| 167 | Finally, write out just the first word again, but set it to | ||
| 168 | 0x0 this time (which probably means "idle"). | ||
| 169 | |||
| 170 | */ | ||
| 171 | |||
| 172 | if (arg_cnt_send > (sizeof(wrData)/sizeof(wrData[0]))-4) { | ||
| 173 | pvr2_trace( | ||
| 174 | PVR2_TRACE_ERROR_LEGS, | ||
| 175 | "Failed to write cx23416 command" | ||
| 176 | " - too many input arguments" | ||
| 177 | " (was given %u limit %u)", | ||
| 178 | arg_cnt_send, | ||
| 179 | (unsigned int)(sizeof(wrData)/sizeof(wrData[0])) - 4); | ||
| 180 | return -EINVAL; | ||
| 181 | } | ||
| 182 | |||
| 183 | if (arg_cnt_recv > (sizeof(rdData)/sizeof(rdData[0]))-4) { | ||
| 184 | pvr2_trace( | ||
| 185 | PVR2_TRACE_ERROR_LEGS, | ||
| 186 | "Failed to write cx23416 command" | ||
| 187 | " - too many return arguments" | ||
| 188 | " (was given %u limit %u)", | ||
| 189 | arg_cnt_recv, | ||
| 190 | (unsigned int)(sizeof(rdData)/sizeof(rdData[0])) - 4); | ||
| 191 | return -EINVAL; | ||
| 192 | } | ||
| 193 | |||
| 194 | |||
| 195 | LOCK_TAKE(hdw->ctl_lock); do { | ||
| 196 | |||
| 197 | wrData[0] = 0; | ||
| 198 | wrData[1] = cmd; | ||
| 199 | wrData[2] = 0; | ||
| 200 | wrData[3] = 0x00060000; | ||
| 201 | for (idx = 0; idx < arg_cnt_send; idx++) { | ||
| 202 | wrData[idx+4] = argp[idx]; | ||
| 203 | } | ||
| 204 | for (; idx < (sizeof(wrData)/sizeof(wrData[0]))-4; idx++) { | ||
| 205 | wrData[idx+4] = 0; | ||
| 206 | } | ||
| 207 | |||
| 208 | ret = pvr2_encoder_write_words(hdw,wrData,idx); | ||
| 209 | if (ret) break; | ||
| 210 | wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY; | ||
| 211 | ret = pvr2_encoder_write_words(hdw,wrData,1); | ||
| 212 | if (ret) break; | ||
| 213 | poll_count = 0; | ||
| 214 | while (1) { | ||
| 215 | if (poll_count < 10000000) poll_count++; | ||
| 216 | ret = pvr2_encoder_read_words(hdw,!0,rdData,1); | ||
| 217 | if (ret) break; | ||
| 218 | if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) { | ||
| 219 | break; | ||
| 220 | } | ||
| 221 | if (poll_count == 100) { | ||
| 222 | pvr2_trace( | ||
| 223 | PVR2_TRACE_ERROR_LEGS, | ||
| 224 | "***WARNING*** device's encoder" | ||
| 225 | " appears to be stuck" | ||
| 226 | " (status=0%08x)",rdData[0]); | ||
| 227 | pvr2_trace( | ||
| 228 | PVR2_TRACE_ERROR_LEGS, | ||
| 229 | "Encoder command: 0x%02x",cmd); | ||
| 230 | for (idx = 4; idx < arg_cnt_send; idx++) { | ||
| 231 | pvr2_trace( | ||
| 232 | PVR2_TRACE_ERROR_LEGS, | ||
| 233 | "Encoder arg%d: 0x%08x", | ||
| 234 | idx-3,wrData[idx]); | ||
| 235 | } | ||
| 236 | pvr2_trace( | ||
| 237 | PVR2_TRACE_ERROR_LEGS, | ||
| 238 | "Giving up waiting." | ||
| 239 | " It is likely that" | ||
| 240 | " this is a bad idea..."); | ||
| 241 | ret = -EBUSY; | ||
| 242 | break; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | if (ret) break; | ||
| 246 | wrData[0] = 0x7; | ||
| 247 | ret = pvr2_encoder_read_words( | ||
| 248 | hdw,0,rdData, | ||
| 249 | sizeof(rdData)/sizeof(rdData[0])); | ||
| 250 | if (ret) break; | ||
| 251 | for (idx = 0; idx < arg_cnt_recv; idx++) { | ||
| 252 | argp[idx] = rdData[idx+4]; | ||
| 253 | } | ||
| 254 | |||
| 255 | wrData[0] = 0x0; | ||
| 256 | ret = pvr2_encoder_write_words(hdw,wrData,1); | ||
| 257 | if (ret) break; | ||
| 258 | |||
| 259 | } while(0); LOCK_GIVE(hdw->ctl_lock); | ||
| 260 | |||
| 261 | return ret; | ||
| 262 | } | ||
| 263 | |||
| 264 | |||
| 265 | static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, | ||
| 266 | int args, ...) | ||
| 267 | { | ||
| 268 | va_list vl; | ||
| 269 | unsigned int idx; | ||
| 270 | u32 data[12]; | ||
| 271 | |||
| 272 | if (args > sizeof(data)/sizeof(data[0])) { | ||
| 273 | pvr2_trace( | ||
| 274 | PVR2_TRACE_ERROR_LEGS, | ||
| 275 | "Failed to write cx23416 command" | ||
| 276 | " - too many arguments" | ||
| 277 | " (was given %u limit %u)", | ||
| 278 | args,(unsigned int)(sizeof(data)/sizeof(data[0]))); | ||
| 279 | return -EINVAL; | ||
| 280 | } | ||
| 281 | |||
| 282 | va_start(vl, args); | ||
| 283 | for (idx = 0; idx < args; idx++) { | ||
| 284 | data[idx] = va_arg(vl, u32); | ||
| 285 | } | ||
| 286 | va_end(vl); | ||
| 287 | |||
| 288 | return pvr2_encoder_cmd(hdw,cmd,args,0,data); | ||
| 289 | } | ||
| 290 | |||
| 291 | int pvr2_encoder_configure(struct pvr2_hdw *hdw) | ||
| 292 | { | ||
| 293 | int ret; | ||
| 294 | pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure" | ||
| 295 | " (cx2341x module)"); | ||
| 296 | hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING; | ||
| 297 | hdw->enc_ctl_state.width = hdw->res_hor_val; | ||
| 298 | hdw->enc_ctl_state.height = hdw->res_ver_val; | ||
| 299 | hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & | ||
| 300 | (V4L2_STD_NTSC|V4L2_STD_PAL_M)) ? | ||
| 301 | 0 : 1); | ||
| 302 | |||
| 303 | ret = 0; | ||
| 304 | |||
| 305 | if (!ret) ret = pvr2_encoder_vcmd( | ||
| 306 | hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, | ||
| 307 | 0xf0, 0xf0); | ||
| 308 | |||
| 309 | /* setup firmware to notify us about some events (don't know why...) */ | ||
| 310 | if (!ret) ret = pvr2_encoder_vcmd( | ||
| 311 | hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, | ||
| 312 | 0, 0, 0x10000000, 0xffffffff); | ||
| 313 | |||
| 314 | if (!ret) ret = pvr2_encoder_vcmd( | ||
| 315 | hdw,CX2341X_ENC_SET_VBI_LINE, 5, | ||
| 316 | 0xffffffff,0,0,0,0); | ||
| 317 | |||
| 318 | if (ret) { | ||
| 319 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 320 | "Failed to configure cx32416"); | ||
| 321 | return ret; | ||
| 322 | } | ||
| 323 | |||
| 324 | ret = cx2341x_update(hdw,pvr2_encoder_cmd, | ||
| 325 | (hdw->enc_cur_valid ? &hdw->enc_cur_state : 0), | ||
| 326 | &hdw->enc_ctl_state); | ||
| 327 | if (ret) { | ||
| 328 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 329 | "Error from cx2341x module code=%d",ret); | ||
| 330 | return ret; | ||
| 331 | } | ||
| 332 | |||
| 333 | ret = 0; | ||
| 334 | |||
| 335 | if (!ret) ret = pvr2_encoder_vcmd( | ||
| 336 | hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); | ||
| 337 | |||
| 338 | if (ret) { | ||
| 339 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 340 | "Failed to initialize cx32416 video input"); | ||
| 341 | return ret; | ||
| 342 | } | ||
| 343 | |||
| 344 | hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); | ||
| 345 | memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state, | ||
| 346 | sizeof(struct cx2341x_mpeg_params)); | ||
| 347 | hdw->enc_cur_valid = !0; | ||
| 348 | return 0; | ||
| 349 | } | ||
| 350 | |||
| 351 | |||
| 352 | int pvr2_encoder_start(struct pvr2_hdw *hdw) | ||
| 353 | { | ||
| 354 | int status; | ||
| 355 | |||
| 356 | /* unmask some interrupts */ | ||
| 357 | pvr2_write_register(hdw, 0x0048, 0xbfffffff); | ||
| 358 | |||
| 359 | /* change some GPIO data */ | ||
| 360 | pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000481); | ||
| 361 | pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000); | ||
| 362 | |||
| 363 | if (hdw->config == pvr2_config_vbi) { | ||
| 364 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, | ||
| 365 | 0x01,0x14); | ||
| 366 | } else if (hdw->config == pvr2_config_mpeg) { | ||
| 367 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, | ||
| 368 | 0,0x13); | ||
| 369 | } else { | ||
| 370 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, | ||
| 371 | 0,0x13); | ||
| 372 | } | ||
| 373 | if (!status) { | ||
| 374 | hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN); | ||
| 375 | } | ||
| 376 | return status; | ||
| 377 | } | ||
| 378 | |||
| 379 | int pvr2_encoder_stop(struct pvr2_hdw *hdw) | ||
| 380 | { | ||
| 381 | int status; | ||
| 382 | |||
| 383 | /* mask all interrupts */ | ||
| 384 | pvr2_write_register(hdw, 0x0048, 0xffffffff); | ||
| 385 | |||
| 386 | if (hdw->config == pvr2_config_vbi) { | ||
| 387 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, | ||
| 388 | 0x01,0x01,0x14); | ||
| 389 | } else if (hdw->config == pvr2_config_mpeg) { | ||
| 390 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, | ||
| 391 | 0x01,0,0x13); | ||
| 392 | } else { | ||
| 393 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, | ||
| 394 | 0x01,0,0x13); | ||
| 395 | } | ||
| 396 | |||
| 397 | /* change some GPIO data */ | ||
| 398 | /* Note: Bit d7 of dir appears to control the LED. So we shut it | ||
| 399 | off here. */ | ||
| 400 | pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401); | ||
| 401 | pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000); | ||
| 402 | |||
| 403 | if (!status) { | ||
| 404 | hdw->subsys_enabled_mask &= ~(1<<PVR2_SUBSYS_B_ENC_RUN); | ||
| 405 | } | ||
| 406 | return status; | ||
| 407 | } | ||
| 408 | |||
| 409 | |||
| 410 | /* | ||
| 411 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 412 | *** Local Variables: *** | ||
| 413 | *** mode: c *** | ||
| 414 | *** fill-column: 70 *** | ||
| 415 | *** tab-width: 8 *** | ||
| 416 | *** c-basic-offset: 8 *** | ||
| 417 | *** End: *** | ||
| 418 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.h b/drivers/media/video/pvrusb2/pvrusb2-encoder.h new file mode 100644 index 000000000000..01b5a0b89c03 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.h | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef __PVRUSB2_ENCODER_H | ||
| 24 | #define __PVRUSB2_ENCODER_H | ||
| 25 | |||
| 26 | struct pvr2_hdw; | ||
| 27 | |||
| 28 | int pvr2_encoder_configure(struct pvr2_hdw *); | ||
| 29 | int pvr2_encoder_start(struct pvr2_hdw *); | ||
| 30 | int pvr2_encoder_stop(struct pvr2_hdw *); | ||
| 31 | |||
| 32 | #endif /* __PVRUSB2_ENCODER_H */ | ||
| 33 | |||
| 34 | /* | ||
| 35 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 36 | *** Local Variables: *** | ||
| 37 | *** mode: c *** | ||
| 38 | *** fill-column: 70 *** | ||
| 39 | *** tab-width: 8 *** | ||
| 40 | *** c-basic-offset: 8 *** | ||
| 41 | *** End: *** | ||
| 42 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h new file mode 100644 index 000000000000..ba2afbfe32c5 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | |||
| @@ -0,0 +1,384 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_HDW_INTERNAL_H | ||
| 22 | #define __PVRUSB2_HDW_INTERNAL_H | ||
| 23 | |||
| 24 | /* | ||
| 25 | |||
| 26 | This header sets up all the internal structures and definitions needed to | ||
| 27 | track and coordinate the driver's interaction with the hardware. ONLY | ||
| 28 | source files which actually implement part of that whole circus should be | ||
| 29 | including this header. Higher levels, like the external layers to the | ||
| 30 | various public APIs (V4L, sysfs, etc) should NOT ever include this | ||
| 31 | private, internal header. This means that pvrusb2-hdw, pvrusb2-encoder, | ||
| 32 | etc will include this, but pvrusb2-v4l should not. | ||
| 33 | |||
| 34 | */ | ||
| 35 | |||
| 36 | #include <linux/config.h> | ||
| 37 | #include <linux/videodev2.h> | ||
| 38 | #include <linux/i2c.h> | ||
| 39 | #include <linux/mutex.h> | ||
| 40 | #include "pvrusb2-hdw.h" | ||
| 41 | #include "pvrusb2-io.h" | ||
| 42 | #include <media/cx2341x.h> | ||
| 43 | |||
| 44 | /* Legal values for the SRATE state variable */ | ||
| 45 | #define PVR2_CVAL_SRATE_48 0 | ||
| 46 | #define PVR2_CVAL_SRATE_44_1 1 | ||
| 47 | |||
| 48 | /* Legal values for the AUDIOBITRATE state variable */ | ||
| 49 | #define PVR2_CVAL_AUDIOBITRATE_384 0 | ||
| 50 | #define PVR2_CVAL_AUDIOBITRATE_320 1 | ||
| 51 | #define PVR2_CVAL_AUDIOBITRATE_256 2 | ||
| 52 | #define PVR2_CVAL_AUDIOBITRATE_224 3 | ||
| 53 | #define PVR2_CVAL_AUDIOBITRATE_192 4 | ||
| 54 | #define PVR2_CVAL_AUDIOBITRATE_160 5 | ||
| 55 | #define PVR2_CVAL_AUDIOBITRATE_128 6 | ||
| 56 | #define PVR2_CVAL_AUDIOBITRATE_112 7 | ||
| 57 | #define PVR2_CVAL_AUDIOBITRATE_96 8 | ||
| 58 | #define PVR2_CVAL_AUDIOBITRATE_80 9 | ||
| 59 | #define PVR2_CVAL_AUDIOBITRATE_64 10 | ||
| 60 | #define PVR2_CVAL_AUDIOBITRATE_56 11 | ||
| 61 | #define PVR2_CVAL_AUDIOBITRATE_48 12 | ||
| 62 | #define PVR2_CVAL_AUDIOBITRATE_32 13 | ||
| 63 | #define PVR2_CVAL_AUDIOBITRATE_VBR 14 | ||
| 64 | |||
| 65 | /* Legal values for the AUDIOEMPHASIS state variable */ | ||
| 66 | #define PVR2_CVAL_AUDIOEMPHASIS_NONE 0 | ||
| 67 | #define PVR2_CVAL_AUDIOEMPHASIS_50_15 1 | ||
| 68 | #define PVR2_CVAL_AUDIOEMPHASIS_CCITT 2 | ||
| 69 | |||
| 70 | /* Legal values for PVR2_CID_HSM */ | ||
| 71 | #define PVR2_CVAL_HSM_FAIL 0 | ||
| 72 | #define PVR2_CVAL_HSM_FULL 1 | ||
| 73 | #define PVR2_CVAL_HSM_HIGH 2 | ||
| 74 | |||
| 75 | #define PVR2_VID_ENDPOINT 0x84 | ||
| 76 | #define PVR2_UNK_ENDPOINT 0x86 /* maybe raw yuv ? */ | ||
| 77 | #define PVR2_VBI_ENDPOINT 0x88 | ||
| 78 | |||
| 79 | #define PVR2_CTL_BUFFSIZE 64 | ||
| 80 | |||
| 81 | #define FREQTABLE_SIZE 500 | ||
| 82 | |||
| 83 | #define LOCK_TAKE(x) do { mutex_lock(&x##_mutex); x##_held = !0; } while (0) | ||
| 84 | #define LOCK_GIVE(x) do { x##_held = 0; mutex_unlock(&x##_mutex); } while (0) | ||
| 85 | |||
| 86 | struct pvr2_decoder; | ||
| 87 | |||
| 88 | typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *); | ||
| 89 | typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *); | ||
| 90 | typedef int (*pvr2_ctlf_get_value)(struct pvr2_ctrl *,int *); | ||
| 91 | typedef int (*pvr2_ctlf_set_value)(struct pvr2_ctrl *,int msk,int val); | ||
| 92 | typedef int (*pvr2_ctlf_val_to_sym)(struct pvr2_ctrl *,int msk,int val, | ||
| 93 | char *,unsigned int,unsigned int *); | ||
| 94 | typedef int (*pvr2_ctlf_sym_to_val)(struct pvr2_ctrl *, | ||
| 95 | const char *,unsigned int, | ||
| 96 | int *mskp,int *valp); | ||
| 97 | typedef unsigned int (*pvr2_ctlf_get_v4lflags)(struct pvr2_ctrl *); | ||
| 98 | |||
| 99 | /* This structure describes a specific control. A table of these is set up | ||
| 100 | in pvrusb2-hdw.c. */ | ||
| 101 | struct pvr2_ctl_info { | ||
| 102 | /* Control's name suitable for use as an identifier */ | ||
| 103 | const char *name; | ||
| 104 | |||
| 105 | /* Short description of control */ | ||
| 106 | const char *desc; | ||
| 107 | |||
| 108 | /* Control's implementation */ | ||
| 109 | pvr2_ctlf_get_value get_value; /* Get its value */ | ||
| 110 | pvr2_ctlf_set_value set_value; /* Set its value */ | ||
| 111 | pvr2_ctlf_val_to_sym val_to_sym; /* Custom convert value->symbol */ | ||
| 112 | pvr2_ctlf_sym_to_val sym_to_val; /* Custom convert symbol->value */ | ||
| 113 | pvr2_ctlf_is_dirty is_dirty; /* Return true if dirty */ | ||
| 114 | pvr2_ctlf_clear_dirty clear_dirty; /* Clear dirty state */ | ||
| 115 | pvr2_ctlf_get_v4lflags get_v4lflags;/* Retrieve v4l flags */ | ||
| 116 | |||
| 117 | /* Control's type (int, enum, bitmask) */ | ||
| 118 | enum pvr2_ctl_type type; | ||
| 119 | |||
| 120 | /* Associated V4L control ID, if any */ | ||
| 121 | int v4l_id; | ||
| 122 | |||
| 123 | /* Associated driver internal ID, if any */ | ||
| 124 | int internal_id; | ||
| 125 | |||
| 126 | /* Don't implicitly initialize this control's value */ | ||
| 127 | int skip_init; | ||
| 128 | |||
| 129 | /* Starting value for this control */ | ||
| 130 | int default_value; | ||
| 131 | |||
| 132 | /* Type-specific control information */ | ||
| 133 | union { | ||
| 134 | struct { /* Integer control */ | ||
| 135 | long min_value; /* lower limit */ | ||
| 136 | long max_value; /* upper limit */ | ||
| 137 | } type_int; | ||
| 138 | struct { /* enumerated control */ | ||
| 139 | unsigned int count; /* enum value count */ | ||
| 140 | const char **value_names; /* symbol names */ | ||
| 141 | } type_enum; | ||
| 142 | struct { /* bitmask control */ | ||
| 143 | unsigned int valid_bits; /* bits in use */ | ||
| 144 | const char **bit_names; /* symbol name/bit */ | ||
| 145 | } type_bitmask; | ||
| 146 | } def; | ||
| 147 | }; | ||
| 148 | |||
| 149 | |||
| 150 | /* Same as pvr2_ctl_info, but includes storage for the control description */ | ||
| 151 | #define PVR2_CTLD_INFO_DESC_SIZE 32 | ||
| 152 | struct pvr2_ctld_info { | ||
| 153 | struct pvr2_ctl_info info; | ||
| 154 | char desc[PVR2_CTLD_INFO_DESC_SIZE]; | ||
| 155 | }; | ||
| 156 | |||
| 157 | struct pvr2_ctrl { | ||
| 158 | const struct pvr2_ctl_info *info; | ||
| 159 | struct pvr2_hdw *hdw; | ||
| 160 | }; | ||
| 161 | |||
| 162 | |||
| 163 | struct pvr2_audio_stat { | ||
| 164 | void *ctxt; | ||
| 165 | void (*detach)(void *); | ||
| 166 | int (*status)(void *); | ||
| 167 | }; | ||
| 168 | |||
| 169 | struct pvr2_decoder_ctrl { | ||
| 170 | void *ctxt; | ||
| 171 | void (*detach)(void *); | ||
| 172 | void (*enable)(void *,int); | ||
| 173 | int (*tuned)(void *); | ||
| 174 | void (*force_reset)(void *); | ||
| 175 | }; | ||
| 176 | |||
| 177 | #define PVR2_I2C_PEND_DETECT 0x01 /* Need to detect a client type */ | ||
| 178 | #define PVR2_I2C_PEND_CLIENT 0x02 /* Client needs a specific update */ | ||
| 179 | #define PVR2_I2C_PEND_REFRESH 0x04 /* Client has specific pending bits */ | ||
| 180 | #define PVR2_I2C_PEND_STALE 0x08 /* Broadcast pending bits */ | ||
| 181 | |||
| 182 | #define PVR2_I2C_PEND_ALL (PVR2_I2C_PEND_DETECT |\ | ||
| 183 | PVR2_I2C_PEND_CLIENT |\ | ||
| 184 | PVR2_I2C_PEND_REFRESH |\ | ||
| 185 | PVR2_I2C_PEND_STALE) | ||
| 186 | |||
| 187 | /* Disposition of firmware1 loading situation */ | ||
| 188 | #define FW1_STATE_UNKNOWN 0 | ||
| 189 | #define FW1_STATE_MISSING 1 | ||
| 190 | #define FW1_STATE_FAILED 2 | ||
| 191 | #define FW1_STATE_RELOAD 3 | ||
| 192 | #define FW1_STATE_OK 4 | ||
| 193 | |||
| 194 | /* Known major hardware variants, keyed from device ID */ | ||
| 195 | #define PVR2_HDW_TYPE_29XXX 0 | ||
| 196 | #ifdef CONFIG_VIDEO_PVRUSB2_24XXX | ||
| 197 | #define PVR2_HDW_TYPE_24XXX 1 | ||
| 198 | #endif | ||
| 199 | |||
| 200 | typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16); | ||
| 201 | #define PVR2_I2C_FUNC_CNT 128 | ||
| 202 | |||
| 203 | /* This structure contains all state data directly needed to | ||
| 204 | manipulate the hardware (as opposed to complying with a kernel | ||
| 205 | interface) */ | ||
| 206 | struct pvr2_hdw { | ||
| 207 | /* Underlying USB device handle */ | ||
| 208 | struct usb_device *usb_dev; | ||
| 209 | struct usb_interface *usb_intf; | ||
| 210 | |||
| 211 | /* Device type, one of PVR2_HDW_TYPE_xxxxx */ | ||
| 212 | unsigned int hdw_type; | ||
| 213 | |||
| 214 | /* Video spigot */ | ||
| 215 | struct pvr2_stream *vid_stream; | ||
| 216 | |||
| 217 | /* Mutex for all hardware state control */ | ||
| 218 | struct mutex big_lock_mutex; | ||
| 219 | int big_lock_held; /* For debugging */ | ||
| 220 | |||
| 221 | void (*poll_trigger_func)(void *); | ||
| 222 | void *poll_trigger_data; | ||
| 223 | |||
| 224 | char name[32]; | ||
| 225 | |||
| 226 | /* I2C stuff */ | ||
| 227 | struct i2c_adapter i2c_adap; | ||
| 228 | struct i2c_algorithm i2c_algo; | ||
| 229 | pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT]; | ||
| 230 | int i2c_cx25840_hack_state; | ||
| 231 | int i2c_linked; | ||
| 232 | unsigned int i2c_pend_types; /* Which types of update are needed */ | ||
| 233 | unsigned long i2c_pend_mask; /* Change bits we need to scan */ | ||
| 234 | unsigned long i2c_stale_mask; /* Pending broadcast change bits */ | ||
| 235 | unsigned long i2c_active_mask; /* All change bits currently in use */ | ||
| 236 | struct list_head i2c_clients; | ||
| 237 | struct mutex i2c_list_lock; | ||
| 238 | |||
| 239 | /* Frequency table */ | ||
| 240 | unsigned int freqTable[FREQTABLE_SIZE]; | ||
| 241 | unsigned int freqProgSlot; | ||
| 242 | unsigned int freqSlot; | ||
| 243 | |||
| 244 | /* Stuff for handling low level control interaction with device */ | ||
| 245 | struct mutex ctl_lock_mutex; | ||
| 246 | int ctl_lock_held; /* For debugging */ | ||
| 247 | struct urb *ctl_write_urb; | ||
| 248 | struct urb *ctl_read_urb; | ||
| 249 | unsigned char *ctl_write_buffer; | ||
| 250 | unsigned char *ctl_read_buffer; | ||
| 251 | volatile int ctl_write_pend_flag; | ||
| 252 | volatile int ctl_read_pend_flag; | ||
| 253 | volatile int ctl_timeout_flag; | ||
| 254 | struct completion ctl_done; | ||
| 255 | unsigned char cmd_buffer[PVR2_CTL_BUFFSIZE]; | ||
| 256 | int cmd_debug_state; // Low level command debugging info | ||
| 257 | unsigned char cmd_debug_code; // | ||
| 258 | unsigned int cmd_debug_write_len; // | ||
| 259 | unsigned int cmd_debug_read_len; // | ||
| 260 | |||
| 261 | int flag_ok; // device in known good state | ||
| 262 | int flag_disconnected; // flag_ok == 0 due to disconnect | ||
| 263 | int flag_init_ok; // true if structure is fully initialized | ||
| 264 | int flag_streaming_enabled; // true if streaming should be on | ||
| 265 | int fw1_state; // current situation with fw1 | ||
| 266 | |||
| 267 | int flag_decoder_is_tuned; | ||
| 268 | |||
| 269 | struct pvr2_decoder_ctrl *decoder_ctrl; | ||
| 270 | |||
| 271 | // CPU firmware info (used to help find / save firmware data) | ||
| 272 | char *fw_buffer; | ||
| 273 | unsigned int fw_size; | ||
| 274 | |||
| 275 | // Which subsystem pieces have been enabled / configured | ||
| 276 | unsigned long subsys_enabled_mask; | ||
| 277 | |||
| 278 | // Which subsystems are manipulated to enable streaming | ||
| 279 | unsigned long subsys_stream_mask; | ||
| 280 | |||
| 281 | // True if there is a request to trigger logging of state in each | ||
| 282 | // module. | ||
| 283 | int log_requested; | ||
| 284 | |||
| 285 | /* Tuner / frequency control stuff */ | ||
| 286 | unsigned int tuner_type; | ||
| 287 | int tuner_updated; | ||
| 288 | unsigned int freqVal; | ||
| 289 | int freqDirty; | ||
| 290 | |||
| 291 | /* Video standard handling */ | ||
| 292 | v4l2_std_id std_mask_eeprom; // Hardware supported selections | ||
| 293 | v4l2_std_id std_mask_avail; // Which standards we may select from | ||
| 294 | v4l2_std_id std_mask_cur; // Currently selected standard(s) | ||
| 295 | unsigned int std_enum_cnt; // # of enumerated standards | ||
| 296 | int std_enum_cur; // selected standard enumeration value | ||
| 297 | int std_dirty; // True if std_mask_cur has changed | ||
| 298 | struct pvr2_ctl_info std_info_enum; | ||
| 299 | struct pvr2_ctl_info std_info_avail; | ||
| 300 | struct pvr2_ctl_info std_info_cur; | ||
| 301 | struct v4l2_standard *std_defs; | ||
| 302 | const char **std_enum_names; | ||
| 303 | |||
| 304 | // Generated string names, one per actual V4L2 standard | ||
| 305 | const char *std_mask_ptrs[32]; | ||
| 306 | char std_mask_names[32][10]; | ||
| 307 | |||
| 308 | int unit_number; /* ID for driver instance */ | ||
| 309 | unsigned long serial_number; /* ID for hardware itself */ | ||
| 310 | |||
| 311 | /* Minor number used by v4l logic (yes, this is a hack, as there should | ||
| 312 | be no v4l junk here). Probably a better way to do this. */ | ||
| 313 | int v4l_minor_number; | ||
| 314 | |||
| 315 | /* Location of eeprom or a negative number if none */ | ||
| 316 | int eeprom_addr; | ||
| 317 | |||
| 318 | enum pvr2_config config; | ||
| 319 | |||
| 320 | /* Information about what audio signal we're hearing */ | ||
| 321 | int flag_stereo; | ||
| 322 | int flag_bilingual; | ||
| 323 | struct pvr2_audio_stat *audio_stat; | ||
| 324 | |||
| 325 | /* Control state needed for cx2341x module */ | ||
| 326 | struct cx2341x_mpeg_params enc_cur_state; | ||
| 327 | struct cx2341x_mpeg_params enc_ctl_state; | ||
| 328 | /* True if an encoder attribute has changed */ | ||
| 329 | int enc_stale; | ||
| 330 | /* True if enc_cur_state is valid */ | ||
| 331 | int enc_cur_valid; | ||
| 332 | |||
| 333 | /* Control state */ | ||
| 334 | #define VCREATE_DATA(lab) int lab##_val; int lab##_dirty | ||
| 335 | VCREATE_DATA(brightness); | ||
| 336 | VCREATE_DATA(contrast); | ||
| 337 | VCREATE_DATA(saturation); | ||
| 338 | VCREATE_DATA(hue); | ||
| 339 | VCREATE_DATA(volume); | ||
| 340 | VCREATE_DATA(balance); | ||
| 341 | VCREATE_DATA(bass); | ||
| 342 | VCREATE_DATA(treble); | ||
| 343 | VCREATE_DATA(mute); | ||
| 344 | VCREATE_DATA(input); | ||
| 345 | VCREATE_DATA(audiomode); | ||
| 346 | VCREATE_DATA(res_hor); | ||
| 347 | VCREATE_DATA(res_ver); | ||
| 348 | VCREATE_DATA(srate); | ||
| 349 | #undef VCREATE_DATA | ||
| 350 | |||
| 351 | struct pvr2_ctld_info *mpeg_ctrl_info; | ||
| 352 | |||
| 353 | struct pvr2_ctrl *controls; | ||
| 354 | unsigned int control_cnt; | ||
| 355 | }; | ||
| 356 | |||
| 357 | int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw); | ||
| 358 | |||
| 359 | unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *); | ||
| 360 | |||
| 361 | void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, | ||
| 362 | unsigned long msk,unsigned long val); | ||
| 363 | void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw, | ||
| 364 | unsigned long msk, | ||
| 365 | unsigned long val); | ||
| 366 | |||
| 367 | void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); | ||
| 368 | void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); | ||
| 369 | |||
| 370 | int pvr2_i2c_basic_op(struct pvr2_hdw *,u8 i2c_addr, | ||
| 371 | u8 *wdata,u16 wlen, | ||
| 372 | u8 *rdata,u16 rlen); | ||
| 373 | |||
| 374 | #endif /* __PVRUSB2_HDW_INTERNAL_H */ | ||
| 375 | |||
| 376 | /* | ||
| 377 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 378 | *** Local Variables: *** | ||
| 379 | *** mode: c *** | ||
| 380 | *** fill-column: 75 *** | ||
| 381 | *** tab-width: 8 *** | ||
| 382 | *** c-basic-offset: 8 *** | ||
| 383 | *** End: *** | ||
| 384 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c new file mode 100644 index 000000000000..643c471375da --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c | |||
| @@ -0,0 +1,3120 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/errno.h> | ||
| 23 | #include <linux/string.h> | ||
| 24 | #include <linux/slab.h> | ||
| 25 | #include <linux/firmware.h> | ||
| 26 | #include <linux/videodev2.h> | ||
| 27 | #include <asm/semaphore.h> | ||
| 28 | #include "pvrusb2.h" | ||
| 29 | #include "pvrusb2-std.h" | ||
| 30 | #include "pvrusb2-util.h" | ||
| 31 | #include "pvrusb2-hdw.h" | ||
| 32 | #include "pvrusb2-i2c-core.h" | ||
| 33 | #include "pvrusb2-tuner.h" | ||
| 34 | #include "pvrusb2-eeprom.h" | ||
| 35 | #include "pvrusb2-hdw-internal.h" | ||
| 36 | #include "pvrusb2-encoder.h" | ||
| 37 | #include "pvrusb2-debug.h" | ||
| 38 | |||
| 39 | struct usb_device_id pvr2_device_table[] = { | ||
| 40 | [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) }, | ||
| 41 | #ifdef CONFIG_VIDEO_PVRUSB2_24XXX | ||
| 42 | [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) }, | ||
| 43 | #endif | ||
| 44 | { } | ||
| 45 | }; | ||
| 46 | |||
| 47 | MODULE_DEVICE_TABLE(usb, pvr2_device_table); | ||
| 48 | |||
| 49 | static const char *pvr2_device_names[] = { | ||
| 50 | [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx", | ||
| 51 | #ifdef CONFIG_VIDEO_PVRUSB2_24XXX | ||
| 52 | [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx", | ||
| 53 | #endif | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct pvr2_string_table { | ||
| 57 | const char **lst; | ||
| 58 | unsigned int cnt; | ||
| 59 | }; | ||
| 60 | |||
| 61 | #ifdef CONFIG_VIDEO_PVRUSB2_24XXX | ||
| 62 | // Names of other client modules to request for 24xxx model hardware | ||
| 63 | static const char *pvr2_client_24xxx[] = { | ||
| 64 | "cx25840", | ||
| 65 | "tuner", | ||
| 66 | "tda9887", | ||
| 67 | "wm8775", | ||
| 68 | }; | ||
| 69 | #endif | ||
| 70 | |||
| 71 | // Names of other client modules to request for 29xxx model hardware | ||
| 72 | static const char *pvr2_client_29xxx[] = { | ||
| 73 | "msp3400", | ||
| 74 | "saa7115", | ||
| 75 | "tuner", | ||
| 76 | "tda9887", | ||
| 77 | }; | ||
| 78 | |||
| 79 | static struct pvr2_string_table pvr2_client_lists[] = { | ||
| 80 | [PVR2_HDW_TYPE_29XXX] = { | ||
| 81 | pvr2_client_29xxx, | ||
| 82 | sizeof(pvr2_client_29xxx)/sizeof(pvr2_client_29xxx[0]), | ||
| 83 | }, | ||
| 84 | #ifdef CONFIG_VIDEO_PVRUSB2_24XXX | ||
| 85 | [PVR2_HDW_TYPE_24XXX] = { | ||
| 86 | pvr2_client_24xxx, | ||
| 87 | sizeof(pvr2_client_24xxx)/sizeof(pvr2_client_24xxx[0]), | ||
| 88 | }, | ||
| 89 | #endif | ||
| 90 | }; | ||
| 91 | |||
| 92 | static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = 0}; | ||
| 93 | DECLARE_MUTEX(pvr2_unit_sem); | ||
| 94 | |||
| 95 | static int ctlchg = 0; | ||
| 96 | static int initusbreset = 1; | ||
| 97 | static int procreload = 0; | ||
| 98 | static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; | ||
| 99 | static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; | ||
| 100 | static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; | ||
| 101 | static int init_pause_msec = 0; | ||
| 102 | |||
| 103 | module_param(ctlchg, int, S_IRUGO|S_IWUSR); | ||
| 104 | MODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value"); | ||
| 105 | module_param(init_pause_msec, int, S_IRUGO|S_IWUSR); | ||
| 106 | MODULE_PARM_DESC(init_pause_msec, "hardware initialization settling delay"); | ||
| 107 | module_param(initusbreset, int, S_IRUGO|S_IWUSR); | ||
| 108 | MODULE_PARM_DESC(initusbreset, "Do USB reset device on probe"); | ||
| 109 | module_param(procreload, int, S_IRUGO|S_IWUSR); | ||
| 110 | MODULE_PARM_DESC(procreload, | ||
| 111 | "Attempt init failure recovery with firmware reload"); | ||
| 112 | module_param_array(tuner, int, NULL, 0444); | ||
| 113 | MODULE_PARM_DESC(tuner,"specify installed tuner type"); | ||
| 114 | module_param_array(video_std, int, NULL, 0444); | ||
| 115 | MODULE_PARM_DESC(video_std,"specify initial video standard"); | ||
| 116 | module_param_array(tolerance, int, NULL, 0444); | ||
| 117 | MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); | ||
| 118 | |||
| 119 | #define PVR2_CTL_WRITE_ENDPOINT 0x01 | ||
| 120 | #define PVR2_CTL_READ_ENDPOINT 0x81 | ||
| 121 | |||
| 122 | #define PVR2_GPIO_IN 0x9008 | ||
| 123 | #define PVR2_GPIO_OUT 0x900c | ||
| 124 | #define PVR2_GPIO_DIR 0x9020 | ||
| 125 | |||
| 126 | #define trace_firmware(...) pvr2_trace(PVR2_TRACE_FIRMWARE,__VA_ARGS__) | ||
| 127 | |||
| 128 | #define PVR2_FIRMWARE_ENDPOINT 0x02 | ||
| 129 | |||
| 130 | /* size of a firmware chunk */ | ||
| 131 | #define FIRMWARE_CHUNK_SIZE 0x2000 | ||
| 132 | |||
| 133 | /* Define the list of additional controls we'll dynamically construct based | ||
| 134 | on query of the cx2341x module. */ | ||
| 135 | struct pvr2_mpeg_ids { | ||
| 136 | const char *strid; | ||
| 137 | int id; | ||
| 138 | }; | ||
| 139 | static const struct pvr2_mpeg_ids mpeg_ids[] = { | ||
| 140 | { | ||
| 141 | .strid = "audio_layer", | ||
| 142 | .id = V4L2_CID_MPEG_AUDIO_ENCODING, | ||
| 143 | },{ | ||
| 144 | .strid = "audio_bitrate", | ||
| 145 | .id = V4L2_CID_MPEG_AUDIO_L2_BITRATE, | ||
| 146 | },{ | ||
| 147 | /* Already using audio_mode elsewhere :-( */ | ||
| 148 | .strid = "mpeg_audio_mode", | ||
| 149 | .id = V4L2_CID_MPEG_AUDIO_MODE, | ||
| 150 | },{ | ||
| 151 | .strid = "mpeg_audio_mode_extension", | ||
| 152 | .id = V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, | ||
| 153 | },{ | ||
| 154 | .strid = "audio_emphasis", | ||
| 155 | .id = V4L2_CID_MPEG_AUDIO_EMPHASIS, | ||
| 156 | },{ | ||
| 157 | .strid = "audio_crc", | ||
| 158 | .id = V4L2_CID_MPEG_AUDIO_CRC, | ||
| 159 | },{ | ||
| 160 | .strid = "video_aspect", | ||
| 161 | .id = V4L2_CID_MPEG_VIDEO_ASPECT, | ||
| 162 | },{ | ||
| 163 | .strid = "video_b_frames", | ||
| 164 | .id = V4L2_CID_MPEG_VIDEO_B_FRAMES, | ||
| 165 | },{ | ||
| 166 | .strid = "video_gop_size", | ||
| 167 | .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE, | ||
| 168 | },{ | ||
| 169 | .strid = "video_gop_closure", | ||
| 170 | .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, | ||
| 171 | },{ | ||
| 172 | .strid = "video_pulldown", | ||
| 173 | .id = V4L2_CID_MPEG_VIDEO_PULLDOWN, | ||
| 174 | },{ | ||
| 175 | .strid = "video_bitrate_mode", | ||
| 176 | .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE, | ||
| 177 | },{ | ||
| 178 | .strid = "video_bitrate", | ||
| 179 | .id = V4L2_CID_MPEG_VIDEO_BITRATE, | ||
| 180 | },{ | ||
| 181 | .strid = "video_bitrate_peak", | ||
| 182 | .id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, | ||
| 183 | },{ | ||
| 184 | .strid = "video_temporal_decimation", | ||
| 185 | .id = V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, | ||
| 186 | },{ | ||
| 187 | .strid = "stream_type", | ||
| 188 | .id = V4L2_CID_MPEG_STREAM_TYPE, | ||
| 189 | },{ | ||
| 190 | .strid = "video_spatial_filter_mode", | ||
| 191 | .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, | ||
| 192 | },{ | ||
| 193 | .strid = "video_spatial_filter", | ||
| 194 | .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, | ||
| 195 | },{ | ||
| 196 | .strid = "video_luma_spatial_filter_type", | ||
| 197 | .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, | ||
| 198 | },{ | ||
| 199 | .strid = "video_chroma_spatial_filter_type", | ||
| 200 | .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, | ||
| 201 | },{ | ||
| 202 | .strid = "video_temporal_filter_mode", | ||
| 203 | .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, | ||
| 204 | },{ | ||
| 205 | .strid = "video_temporal_filter", | ||
| 206 | .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, | ||
| 207 | },{ | ||
| 208 | .strid = "video_median_filter_type", | ||
| 209 | .id = V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, | ||
| 210 | },{ | ||
| 211 | .strid = "video_luma_median_filter_top", | ||
| 212 | .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, | ||
| 213 | },{ | ||
| 214 | .strid = "video_luma_median_filter_bottom", | ||
| 215 | .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, | ||
| 216 | },{ | ||
| 217 | .strid = "video_chroma_median_filter_top", | ||
| 218 | .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, | ||
| 219 | },{ | ||
| 220 | .strid = "video_chroma_median_filter_bottom", | ||
| 221 | .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, | ||
| 222 | } | ||
| 223 | }; | ||
| 224 | #define MPEGDEF_COUNT (sizeof(mpeg_ids)/sizeof(mpeg_ids[0])) | ||
| 225 | |||
| 226 | static const char *control_values_srate[] = { | ||
| 227 | [PVR2_CVAL_SRATE_48] = "48KHz", | ||
| 228 | [PVR2_CVAL_SRATE_44_1] = "44.1KHz", | ||
| 229 | }; | ||
| 230 | |||
| 231 | |||
| 232 | |||
| 233 | |||
| 234 | static const char *control_values_input[] = { | ||
| 235 | [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/ | ||
| 236 | [PVR2_CVAL_INPUT_RADIO] = "radio", | ||
| 237 | [PVR2_CVAL_INPUT_SVIDEO] = "s-video", | ||
| 238 | [PVR2_CVAL_INPUT_COMPOSITE] = "composite", | ||
| 239 | }; | ||
| 240 | |||
| 241 | |||
| 242 | static const char *control_values_audiomode[] = { | ||
| 243 | [V4L2_TUNER_MODE_MONO] = "Mono", | ||
| 244 | [V4L2_TUNER_MODE_STEREO] = "Stereo", | ||
| 245 | [V4L2_TUNER_MODE_LANG1] = "Lang1", | ||
| 246 | [V4L2_TUNER_MODE_LANG2] = "Lang2", | ||
| 247 | [V4L2_TUNER_MODE_LANG1_LANG2] = "Lang1+Lang2", | ||
| 248 | }; | ||
| 249 | |||
| 250 | |||
| 251 | static const char *control_values_hsm[] = { | ||
| 252 | [PVR2_CVAL_HSM_FAIL] = "Fail", | ||
| 253 | [PVR2_CVAL_HSM_HIGH] = "High", | ||
| 254 | [PVR2_CVAL_HSM_FULL] = "Full", | ||
| 255 | }; | ||
| 256 | |||
| 257 | |||
| 258 | static const char *control_values_subsystem[] = { | ||
| 259 | [PVR2_SUBSYS_B_ENC_FIRMWARE] = "enc_firmware", | ||
| 260 | [PVR2_SUBSYS_B_ENC_CFG] = "enc_config", | ||
| 261 | [PVR2_SUBSYS_B_DIGITIZER_RUN] = "digitizer_run", | ||
| 262 | [PVR2_SUBSYS_B_USBSTREAM_RUN] = "usbstream_run", | ||
| 263 | [PVR2_SUBSYS_B_ENC_RUN] = "enc_run", | ||
| 264 | }; | ||
| 265 | |||
| 266 | |||
| 267 | static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 268 | { | ||
| 269 | struct pvr2_hdw *hdw = cptr->hdw; | ||
| 270 | if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { | ||
| 271 | *vp = hdw->freqTable[hdw->freqProgSlot-1]; | ||
| 272 | } else { | ||
| 273 | *vp = 0; | ||
| 274 | } | ||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | |||
| 278 | static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v) | ||
| 279 | { | ||
| 280 | struct pvr2_hdw *hdw = cptr->hdw; | ||
| 281 | if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { | ||
| 282 | hdw->freqTable[hdw->freqProgSlot-1] = v; | ||
| 283 | } | ||
| 284 | return 0; | ||
| 285 | } | ||
| 286 | |||
| 287 | static int ctrl_channelprog_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 288 | { | ||
| 289 | *vp = cptr->hdw->freqProgSlot; | ||
| 290 | return 0; | ||
| 291 | } | ||
| 292 | |||
| 293 | static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v) | ||
| 294 | { | ||
| 295 | struct pvr2_hdw *hdw = cptr->hdw; | ||
| 296 | if ((v >= 0) && (v <= FREQTABLE_SIZE)) { | ||
| 297 | hdw->freqProgSlot = v; | ||
| 298 | } | ||
| 299 | return 0; | ||
| 300 | } | ||
| 301 | |||
| 302 | static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 303 | { | ||
| 304 | *vp = cptr->hdw->freqSlot; | ||
| 305 | return 0; | ||
| 306 | } | ||
| 307 | |||
| 308 | static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int v) | ||
| 309 | { | ||
| 310 | unsigned freq = 0; | ||
| 311 | struct pvr2_hdw *hdw = cptr->hdw; | ||
| 312 | hdw->freqSlot = v; | ||
| 313 | if ((hdw->freqSlot > 0) && (hdw->freqSlot <= FREQTABLE_SIZE)) { | ||
| 314 | freq = hdw->freqTable[hdw->freqSlot-1]; | ||
| 315 | } | ||
| 316 | if (freq && (freq != hdw->freqVal)) { | ||
| 317 | hdw->freqVal = freq; | ||
| 318 | hdw->freqDirty = !0; | ||
| 319 | } | ||
| 320 | return 0; | ||
| 321 | } | ||
| 322 | |||
| 323 | static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 324 | { | ||
| 325 | *vp = cptr->hdw->freqVal; | ||
| 326 | return 0; | ||
| 327 | } | ||
| 328 | |||
| 329 | static int ctrl_freq_is_dirty(struct pvr2_ctrl *cptr) | ||
| 330 | { | ||
| 331 | return cptr->hdw->freqDirty != 0; | ||
| 332 | } | ||
| 333 | |||
| 334 | static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr) | ||
| 335 | { | ||
| 336 | cptr->hdw->freqDirty = 0; | ||
| 337 | } | ||
| 338 | |||
| 339 | static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) | ||
| 340 | { | ||
| 341 | struct pvr2_hdw *hdw = cptr->hdw; | ||
| 342 | hdw->freqVal = v; | ||
| 343 | hdw->freqDirty = !0; | ||
| 344 | hdw->freqSlot = 0; | ||
| 345 | return 0; | ||
| 346 | } | ||
| 347 | |||
| 348 | static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr) | ||
| 349 | { | ||
| 350 | return cptr->hdw->enc_stale != 0; | ||
| 351 | } | ||
| 352 | |||
| 353 | static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr) | ||
| 354 | { | ||
| 355 | cptr->hdw->enc_stale = 0; | ||
| 356 | } | ||
| 357 | |||
| 358 | static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 359 | { | ||
| 360 | int ret; | ||
| 361 | struct v4l2_ext_controls cs; | ||
| 362 | struct v4l2_ext_control c1; | ||
| 363 | memset(&cs,0,sizeof(cs)); | ||
| 364 | memset(&c1,0,sizeof(c1)); | ||
| 365 | cs.controls = &c1; | ||
| 366 | cs.count = 1; | ||
| 367 | c1.id = cptr->info->v4l_id; | ||
| 368 | ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state,&cs, | ||
| 369 | VIDIOC_G_EXT_CTRLS); | ||
| 370 | if (ret) return ret; | ||
| 371 | *vp = c1.value; | ||
| 372 | return 0; | ||
| 373 | } | ||
| 374 | |||
| 375 | static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v) | ||
| 376 | { | ||
| 377 | int ret; | ||
| 378 | struct v4l2_ext_controls cs; | ||
| 379 | struct v4l2_ext_control c1; | ||
| 380 | memset(&cs,0,sizeof(cs)); | ||
| 381 | memset(&c1,0,sizeof(c1)); | ||
| 382 | cs.controls = &c1; | ||
| 383 | cs.count = 1; | ||
| 384 | c1.id = cptr->info->v4l_id; | ||
| 385 | c1.value = v; | ||
| 386 | ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state,&cs, | ||
| 387 | VIDIOC_S_EXT_CTRLS); | ||
| 388 | if (ret) return ret; | ||
| 389 | cptr->hdw->enc_stale = !0; | ||
| 390 | return 0; | ||
| 391 | } | ||
| 392 | |||
| 393 | static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr) | ||
| 394 | { | ||
| 395 | struct v4l2_queryctrl qctrl; | ||
| 396 | struct pvr2_ctl_info *info; | ||
| 397 | qctrl.id = cptr->info->v4l_id; | ||
| 398 | cx2341x_ctrl_query(&cptr->hdw->enc_ctl_state,&qctrl); | ||
| 399 | /* Strip out the const so we can adjust a function pointer. It's | ||
| 400 | OK to do this here because we know this is a dynamically created | ||
| 401 | control, so the underlying storage for the info pointer is (a) | ||
| 402 | private to us, and (b) not in read-only storage. Either we do | ||
| 403 | this or we significantly complicate the underlying control | ||
| 404 | implementation. */ | ||
| 405 | info = (struct pvr2_ctl_info *)(cptr->info); | ||
| 406 | if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) { | ||
| 407 | if (info->set_value) { | ||
| 408 | info->set_value = 0; | ||
| 409 | } | ||
| 410 | } else { | ||
| 411 | if (!(info->set_value)) { | ||
| 412 | info->set_value = ctrl_cx2341x_set; | ||
| 413 | } | ||
| 414 | } | ||
| 415 | return qctrl.flags; | ||
| 416 | } | ||
| 417 | |||
| 418 | static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 419 | { | ||
| 420 | *vp = cptr->hdw->flag_streaming_enabled; | ||
| 421 | return 0; | ||
| 422 | } | ||
| 423 | |||
| 424 | static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 425 | { | ||
| 426 | int result = pvr2_hdw_is_hsm(cptr->hdw); | ||
| 427 | *vp = PVR2_CVAL_HSM_FULL; | ||
| 428 | if (result < 0) *vp = PVR2_CVAL_HSM_FAIL; | ||
| 429 | if (result) *vp = PVR2_CVAL_HSM_HIGH; | ||
| 430 | return 0; | ||
| 431 | } | ||
| 432 | |||
| 433 | static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 434 | { | ||
| 435 | *vp = cptr->hdw->std_mask_avail; | ||
| 436 | return 0; | ||
| 437 | } | ||
| 438 | |||
| 439 | static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v) | ||
| 440 | { | ||
| 441 | struct pvr2_hdw *hdw = cptr->hdw; | ||
| 442 | v4l2_std_id ns; | ||
| 443 | ns = hdw->std_mask_avail; | ||
| 444 | ns = (ns & ~m) | (v & m); | ||
| 445 | if (ns == hdw->std_mask_avail) return 0; | ||
| 446 | hdw->std_mask_avail = ns; | ||
| 447 | pvr2_hdw_internal_set_std_avail(hdw); | ||
| 448 | pvr2_hdw_internal_find_stdenum(hdw); | ||
| 449 | return 0; | ||
| 450 | } | ||
| 451 | |||
| 452 | static int ctrl_std_val_to_sym(struct pvr2_ctrl *cptr,int msk,int val, | ||
| 453 | char *bufPtr,unsigned int bufSize, | ||
| 454 | unsigned int *len) | ||
| 455 | { | ||
| 456 | *len = pvr2_std_id_to_str(bufPtr,bufSize,msk & val); | ||
| 457 | return 0; | ||
| 458 | } | ||
| 459 | |||
| 460 | static int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr, | ||
| 461 | const char *bufPtr,unsigned int bufSize, | ||
| 462 | int *mskp,int *valp) | ||
| 463 | { | ||
| 464 | int ret; | ||
| 465 | v4l2_std_id id; | ||
| 466 | ret = pvr2_std_str_to_id(&id,bufPtr,bufSize); | ||
| 467 | if (ret < 0) return ret; | ||
| 468 | if (mskp) *mskp = id; | ||
| 469 | if (valp) *valp = id; | ||
| 470 | return 0; | ||
| 471 | } | ||
| 472 | |||
| 473 | static int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 474 | { | ||
| 475 | *vp = cptr->hdw->std_mask_cur; | ||
| 476 | return 0; | ||
| 477 | } | ||
| 478 | |||
| 479 | static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v) | ||
| 480 | { | ||
| 481 | struct pvr2_hdw *hdw = cptr->hdw; | ||
| 482 | v4l2_std_id ns; | ||
| 483 | ns = hdw->std_mask_cur; | ||
| 484 | ns = (ns & ~m) | (v & m); | ||
| 485 | if (ns == hdw->std_mask_cur) return 0; | ||
| 486 | hdw->std_mask_cur = ns; | ||
| 487 | hdw->std_dirty = !0; | ||
| 488 | pvr2_hdw_internal_find_stdenum(hdw); | ||
| 489 | return 0; | ||
| 490 | } | ||
| 491 | |||
| 492 | static int ctrl_stdcur_is_dirty(struct pvr2_ctrl *cptr) | ||
| 493 | { | ||
| 494 | return cptr->hdw->std_dirty != 0; | ||
| 495 | } | ||
| 496 | |||
| 497 | static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr) | ||
| 498 | { | ||
| 499 | cptr->hdw->std_dirty = 0; | ||
| 500 | } | ||
| 501 | |||
| 502 | static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 503 | { | ||
| 504 | *vp = ((pvr2_hdw_get_signal_status_internal(cptr->hdw) & | ||
| 505 | PVR2_SIGNAL_OK) ? 1 : 0); | ||
| 506 | return 0; | ||
| 507 | } | ||
| 508 | |||
| 509 | static int ctrl_subsys_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 510 | { | ||
| 511 | *vp = cptr->hdw->subsys_enabled_mask; | ||
| 512 | return 0; | ||
| 513 | } | ||
| 514 | |||
| 515 | static int ctrl_subsys_set(struct pvr2_ctrl *cptr,int m,int v) | ||
| 516 | { | ||
| 517 | pvr2_hdw_subsys_bit_chg_no_lock(cptr->hdw,m,v); | ||
| 518 | return 0; | ||
| 519 | } | ||
| 520 | |||
| 521 | static int ctrl_subsys_stream_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 522 | { | ||
| 523 | *vp = cptr->hdw->subsys_stream_mask; | ||
| 524 | return 0; | ||
| 525 | } | ||
| 526 | |||
| 527 | static int ctrl_subsys_stream_set(struct pvr2_ctrl *cptr,int m,int v) | ||
| 528 | { | ||
| 529 | pvr2_hdw_subsys_stream_bit_chg_no_lock(cptr->hdw,m,v); | ||
| 530 | return 0; | ||
| 531 | } | ||
| 532 | |||
| 533 | static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v) | ||
| 534 | { | ||
| 535 | struct pvr2_hdw *hdw = cptr->hdw; | ||
| 536 | if (v < 0) return -EINVAL; | ||
| 537 | if (v > hdw->std_enum_cnt) return -EINVAL; | ||
| 538 | hdw->std_enum_cur = v; | ||
| 539 | if (!v) return 0; | ||
| 540 | v--; | ||
| 541 | if (hdw->std_mask_cur == hdw->std_defs[v].id) return 0; | ||
| 542 | hdw->std_mask_cur = hdw->std_defs[v].id; | ||
| 543 | hdw->std_dirty = !0; | ||
| 544 | return 0; | ||
| 545 | } | ||
| 546 | |||
| 547 | |||
| 548 | static int ctrl_stdenumcur_get(struct pvr2_ctrl *cptr,int *vp) | ||
| 549 | { | ||
| 550 | *vp = cptr->hdw->std_enum_cur; | ||
| 551 | return 0; | ||
| 552 | } | ||
| 553 | |||
| 554 | |||
| 555 | static int ctrl_stdenumcur_is_dirty(struct pvr2_ctrl *cptr) | ||
| 556 | { | ||
| 557 | return cptr->hdw->std_dirty != 0; | ||
| 558 | } | ||
| 559 | |||
| 560 | |||
| 561 | static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr) | ||
| 562 | { | ||
| 563 | cptr->hdw->std_dirty = 0; | ||
| 564 | } | ||
| 565 | |||
| 566 | |||
| 567 | #define DEFINT(vmin,vmax) \ | ||
| 568 | .type = pvr2_ctl_int, \ | ||
| 569 | .def.type_int.min_value = vmin, \ | ||
| 570 | .def.type_int.max_value = vmax | ||
| 571 | |||
| 572 | #define DEFENUM(tab) \ | ||
| 573 | .type = pvr2_ctl_enum, \ | ||
| 574 | .def.type_enum.count = (sizeof(tab)/sizeof((tab)[0])), \ | ||
| 575 | .def.type_enum.value_names = tab | ||
| 576 | |||
| 577 | #define DEFBOOL \ | ||
| 578 | .type = pvr2_ctl_bool | ||
| 579 | |||
| 580 | #define DEFMASK(msk,tab) \ | ||
| 581 | .type = pvr2_ctl_bitmask, \ | ||
| 582 | .def.type_bitmask.valid_bits = msk, \ | ||
| 583 | .def.type_bitmask.bit_names = tab | ||
| 584 | |||
| 585 | #define DEFREF(vname) \ | ||
| 586 | .set_value = ctrl_set_##vname, \ | ||
| 587 | .get_value = ctrl_get_##vname, \ | ||
| 588 | .is_dirty = ctrl_isdirty_##vname, \ | ||
| 589 | .clear_dirty = ctrl_cleardirty_##vname | ||
| 590 | |||
| 591 | |||
| 592 | #define VCREATE_FUNCS(vname) \ | ||
| 593 | static int ctrl_get_##vname(struct pvr2_ctrl *cptr,int *vp) \ | ||
| 594 | {*vp = cptr->hdw->vname##_val; return 0;} \ | ||
| 595 | static int ctrl_set_##vname(struct pvr2_ctrl *cptr,int m,int v) \ | ||
| 596 | {cptr->hdw->vname##_val = v; cptr->hdw->vname##_dirty = !0; return 0;} \ | ||
| 597 | static int ctrl_isdirty_##vname(struct pvr2_ctrl *cptr) \ | ||
| 598 | {return cptr->hdw->vname##_dirty != 0;} \ | ||
| 599 | static void ctrl_cleardirty_##vname(struct pvr2_ctrl *cptr) \ | ||
| 600 | {cptr->hdw->vname##_dirty = 0;} | ||
| 601 | |||
| 602 | VCREATE_FUNCS(brightness) | ||
| 603 | VCREATE_FUNCS(contrast) | ||
| 604 | VCREATE_FUNCS(saturation) | ||
| 605 | VCREATE_FUNCS(hue) | ||
| 606 | VCREATE_FUNCS(volume) | ||
| 607 | VCREATE_FUNCS(balance) | ||
| 608 | VCREATE_FUNCS(bass) | ||
| 609 | VCREATE_FUNCS(treble) | ||
| 610 | VCREATE_FUNCS(mute) | ||
| 611 | VCREATE_FUNCS(input) | ||
| 612 | VCREATE_FUNCS(audiomode) | ||
| 613 | VCREATE_FUNCS(res_hor) | ||
| 614 | VCREATE_FUNCS(res_ver) | ||
| 615 | VCREATE_FUNCS(srate) | ||
| 616 | |||
| 617 | #define MIN_FREQ 55250000L | ||
| 618 | #define MAX_FREQ 850000000L | ||
| 619 | |||
| 620 | /* Table definition of all controls which can be manipulated */ | ||
| 621 | static const struct pvr2_ctl_info control_defs[] = { | ||
| 622 | { | ||
| 623 | .v4l_id = V4L2_CID_BRIGHTNESS, | ||
| 624 | .desc = "Brightness", | ||
| 625 | .name = "brightness", | ||
| 626 | .default_value = 128, | ||
| 627 | DEFREF(brightness), | ||
| 628 | DEFINT(0,255), | ||
| 629 | },{ | ||
| 630 | .v4l_id = V4L2_CID_CONTRAST, | ||
| 631 | .desc = "Contrast", | ||
| 632 | .name = "contrast", | ||
| 633 | .default_value = 68, | ||
| 634 | DEFREF(contrast), | ||
| 635 | DEFINT(0,127), | ||
| 636 | },{ | ||
| 637 | .v4l_id = V4L2_CID_SATURATION, | ||
| 638 | .desc = "Saturation", | ||
| 639 | .name = "saturation", | ||
| 640 | .default_value = 64, | ||
| 641 | DEFREF(saturation), | ||
| 642 | DEFINT(0,127), | ||
| 643 | },{ | ||
| 644 | .v4l_id = V4L2_CID_HUE, | ||
| 645 | .desc = "Hue", | ||
| 646 | .name = "hue", | ||
| 647 | .default_value = 0, | ||
| 648 | DEFREF(hue), | ||
| 649 | DEFINT(-128,127), | ||
| 650 | },{ | ||
| 651 | .v4l_id = V4L2_CID_AUDIO_VOLUME, | ||
| 652 | .desc = "Volume", | ||
| 653 | .name = "volume", | ||
| 654 | .default_value = 65535, | ||
| 655 | DEFREF(volume), | ||
| 656 | DEFINT(0,65535), | ||
| 657 | },{ | ||
| 658 | .v4l_id = V4L2_CID_AUDIO_BALANCE, | ||
| 659 | .desc = "Balance", | ||
| 660 | .name = "balance", | ||
| 661 | .default_value = 0, | ||
| 662 | DEFREF(balance), | ||
| 663 | DEFINT(-32768,32767), | ||
| 664 | },{ | ||
| 665 | .v4l_id = V4L2_CID_AUDIO_BASS, | ||
| 666 | .desc = "Bass", | ||
| 667 | .name = "bass", | ||
| 668 | .default_value = 0, | ||
| 669 | DEFREF(bass), | ||
| 670 | DEFINT(-32768,32767), | ||
| 671 | },{ | ||
| 672 | .v4l_id = V4L2_CID_AUDIO_TREBLE, | ||
| 673 | .desc = "Treble", | ||
| 674 | .name = "treble", | ||
| 675 | .default_value = 0, | ||
| 676 | DEFREF(treble), | ||
| 677 | DEFINT(-32768,32767), | ||
| 678 | },{ | ||
| 679 | .v4l_id = V4L2_CID_AUDIO_MUTE, | ||
| 680 | .desc = "Mute", | ||
| 681 | .name = "mute", | ||
| 682 | .default_value = 0, | ||
| 683 | DEFREF(mute), | ||
| 684 | DEFBOOL, | ||
| 685 | },{ | ||
| 686 | .desc = "Video Source", | ||
| 687 | .name = "input", | ||
| 688 | .internal_id = PVR2_CID_INPUT, | ||
| 689 | .default_value = PVR2_CVAL_INPUT_TV, | ||
| 690 | DEFREF(input), | ||
| 691 | DEFENUM(control_values_input), | ||
| 692 | },{ | ||
| 693 | .desc = "Audio Mode", | ||
| 694 | .name = "audio_mode", | ||
| 695 | .internal_id = PVR2_CID_AUDIOMODE, | ||
| 696 | .default_value = V4L2_TUNER_MODE_STEREO, | ||
| 697 | DEFREF(audiomode), | ||
| 698 | DEFENUM(control_values_audiomode), | ||
| 699 | },{ | ||
| 700 | .desc = "Horizontal capture resolution", | ||
| 701 | .name = "resolution_hor", | ||
| 702 | .internal_id = PVR2_CID_HRES, | ||
| 703 | .default_value = 720, | ||
| 704 | DEFREF(res_hor), | ||
| 705 | DEFINT(320,720), | ||
| 706 | },{ | ||
| 707 | .desc = "Vertical capture resolution", | ||
| 708 | .name = "resolution_ver", | ||
| 709 | .internal_id = PVR2_CID_VRES, | ||
| 710 | .default_value = 480, | ||
| 711 | DEFREF(res_ver), | ||
| 712 | DEFINT(200,625), | ||
| 713 | },{ | ||
| 714 | .v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, | ||
| 715 | .desc = "Sample rate", | ||
| 716 | .name = "srate", | ||
| 717 | .default_value = PVR2_CVAL_SRATE_48, | ||
| 718 | DEFREF(srate), | ||
| 719 | DEFENUM(control_values_srate), | ||
| 720 | },{ | ||
| 721 | .desc = "Tuner Frequency (Hz)", | ||
| 722 | .name = "frequency", | ||
| 723 | .internal_id = PVR2_CID_FREQUENCY, | ||
| 724 | .default_value = 175250000L, | ||
| 725 | .set_value = ctrl_freq_set, | ||
| 726 | .get_value = ctrl_freq_get, | ||
| 727 | .is_dirty = ctrl_freq_is_dirty, | ||
| 728 | .clear_dirty = ctrl_freq_clear_dirty, | ||
| 729 | DEFINT(MIN_FREQ,MAX_FREQ), | ||
| 730 | },{ | ||
| 731 | .desc = "Channel", | ||
| 732 | .name = "channel", | ||
| 733 | .set_value = ctrl_channel_set, | ||
| 734 | .get_value = ctrl_channel_get, | ||
| 735 | DEFINT(0,FREQTABLE_SIZE), | ||
| 736 | },{ | ||
| 737 | .desc = "Channel Program Frequency", | ||
| 738 | .name = "freq_table_value", | ||
| 739 | .set_value = ctrl_channelfreq_set, | ||
| 740 | .get_value = ctrl_channelfreq_get, | ||
| 741 | DEFINT(MIN_FREQ,MAX_FREQ), | ||
| 742 | },{ | ||
| 743 | .desc = "Channel Program ID", | ||
| 744 | .name = "freq_table_channel", | ||
| 745 | .set_value = ctrl_channelprog_set, | ||
| 746 | .get_value = ctrl_channelprog_get, | ||
| 747 | DEFINT(0,FREQTABLE_SIZE), | ||
| 748 | },{ | ||
| 749 | .desc = "Streaming Enabled", | ||
| 750 | .name = "streaming_enabled", | ||
| 751 | .get_value = ctrl_streamingenabled_get, | ||
| 752 | DEFBOOL, | ||
| 753 | },{ | ||
| 754 | .desc = "USB Speed", | ||
| 755 | .name = "usb_speed", | ||
| 756 | .get_value = ctrl_hsm_get, | ||
| 757 | DEFENUM(control_values_hsm), | ||
| 758 | },{ | ||
| 759 | .desc = "Signal Present", | ||
| 760 | .name = "signal_present", | ||
| 761 | .get_value = ctrl_signal_get, | ||
| 762 | DEFBOOL, | ||
| 763 | },{ | ||
| 764 | .desc = "Video Standards Available Mask", | ||
| 765 | .name = "video_standard_mask_available", | ||
| 766 | .internal_id = PVR2_CID_STDAVAIL, | ||
| 767 | .skip_init = !0, | ||
| 768 | .get_value = ctrl_stdavail_get, | ||
| 769 | .set_value = ctrl_stdavail_set, | ||
| 770 | .val_to_sym = ctrl_std_val_to_sym, | ||
| 771 | .sym_to_val = ctrl_std_sym_to_val, | ||
| 772 | .type = pvr2_ctl_bitmask, | ||
| 773 | },{ | ||
| 774 | .desc = "Video Standards In Use Mask", | ||
| 775 | .name = "video_standard_mask_active", | ||
| 776 | .internal_id = PVR2_CID_STDCUR, | ||
| 777 | .skip_init = !0, | ||
| 778 | .get_value = ctrl_stdcur_get, | ||
| 779 | .set_value = ctrl_stdcur_set, | ||
| 780 | .is_dirty = ctrl_stdcur_is_dirty, | ||
| 781 | .clear_dirty = ctrl_stdcur_clear_dirty, | ||
| 782 | .val_to_sym = ctrl_std_val_to_sym, | ||
| 783 | .sym_to_val = ctrl_std_sym_to_val, | ||
| 784 | .type = pvr2_ctl_bitmask, | ||
| 785 | },{ | ||
| 786 | .desc = "Subsystem enabled mask", | ||
| 787 | .name = "debug_subsys_mask", | ||
| 788 | .skip_init = !0, | ||
| 789 | .get_value = ctrl_subsys_get, | ||
| 790 | .set_value = ctrl_subsys_set, | ||
| 791 | DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem), | ||
| 792 | },{ | ||
| 793 | .desc = "Subsystem stream mask", | ||
| 794 | .name = "debug_subsys_stream_mask", | ||
| 795 | .skip_init = !0, | ||
| 796 | .get_value = ctrl_subsys_stream_get, | ||
| 797 | .set_value = ctrl_subsys_stream_set, | ||
| 798 | DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem), | ||
| 799 | },{ | ||
| 800 | .desc = "Video Standard Name", | ||
| 801 | .name = "video_standard", | ||
| 802 | .internal_id = PVR2_CID_STDENUM, | ||
| 803 | .skip_init = !0, | ||
| 804 | .get_value = ctrl_stdenumcur_get, | ||
| 805 | .set_value = ctrl_stdenumcur_set, | ||
| 806 | .is_dirty = ctrl_stdenumcur_is_dirty, | ||
| 807 | .clear_dirty = ctrl_stdenumcur_clear_dirty, | ||
| 808 | .type = pvr2_ctl_enum, | ||
| 809 | } | ||
| 810 | }; | ||
| 811 | |||
| 812 | #define CTRLDEF_COUNT (sizeof(control_defs)/sizeof(control_defs[0])) | ||
| 813 | |||
| 814 | |||
| 815 | const char *pvr2_config_get_name(enum pvr2_config cfg) | ||
| 816 | { | ||
| 817 | switch (cfg) { | ||
| 818 | case pvr2_config_empty: return "empty"; | ||
| 819 | case pvr2_config_mpeg: return "mpeg"; | ||
| 820 | case pvr2_config_vbi: return "vbi"; | ||
| 821 | case pvr2_config_radio: return "radio"; | ||
| 822 | } | ||
| 823 | return "<unknown>"; | ||
| 824 | } | ||
| 825 | |||
| 826 | |||
| 827 | struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *hdw) | ||
| 828 | { | ||
| 829 | return hdw->usb_dev; | ||
| 830 | } | ||
| 831 | |||
| 832 | |||
| 833 | unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw) | ||
| 834 | { | ||
| 835 | return hdw->serial_number; | ||
| 836 | } | ||
| 837 | |||
| 838 | |||
| 839 | struct pvr2_hdw *pvr2_hdw_find(int unit_number) | ||
| 840 | { | ||
| 841 | if (unit_number < 0) return 0; | ||
| 842 | if (unit_number >= PVR_NUM) return 0; | ||
| 843 | return unit_pointers[unit_number]; | ||
| 844 | } | ||
| 845 | |||
| 846 | |||
| 847 | int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw) | ||
| 848 | { | ||
| 849 | return hdw->unit_number; | ||
| 850 | } | ||
| 851 | |||
| 852 | |||
| 853 | /* Attempt to locate one of the given set of files. Messages are logged | ||
| 854 | appropriate to what has been found. The return value will be 0 or | ||
| 855 | greater on success (it will be the index of the file name found) and | ||
| 856 | fw_entry will be filled in. Otherwise a negative error is returned on | ||
| 857 | failure. If the return value is -ENOENT then no viable firmware file | ||
| 858 | could be located. */ | ||
| 859 | static int pvr2_locate_firmware(struct pvr2_hdw *hdw, | ||
| 860 | const struct firmware **fw_entry, | ||
| 861 | const char *fwtypename, | ||
| 862 | unsigned int fwcount, | ||
| 863 | const char *fwnames[]) | ||
| 864 | { | ||
| 865 | unsigned int idx; | ||
| 866 | int ret = -EINVAL; | ||
| 867 | for (idx = 0; idx < fwcount; idx++) { | ||
| 868 | ret = request_firmware(fw_entry, | ||
| 869 | fwnames[idx], | ||
| 870 | &hdw->usb_dev->dev); | ||
| 871 | if (!ret) { | ||
| 872 | trace_firmware("Located %s firmware: %s;" | ||
| 873 | " uploading...", | ||
| 874 | fwtypename, | ||
| 875 | fwnames[idx]); | ||
| 876 | return idx; | ||
| 877 | } | ||
| 878 | if (ret == -ENOENT) continue; | ||
| 879 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 880 | "request_firmware fatal error with code=%d",ret); | ||
| 881 | return ret; | ||
| 882 | } | ||
| 883 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 884 | "***WARNING***" | ||
| 885 | " Device %s firmware" | ||
| 886 | " seems to be missing.", | ||
| 887 | fwtypename); | ||
| 888 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 889 | "Did you install the pvrusb2 firmware files" | ||
| 890 | " in their proper location?"); | ||
| 891 | if (fwcount == 1) { | ||
| 892 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 893 | "request_firmware unable to locate %s file %s", | ||
| 894 | fwtypename,fwnames[0]); | ||
| 895 | } else { | ||
| 896 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 897 | "request_firmware unable to locate" | ||
| 898 | " one of the following %s files:", | ||
| 899 | fwtypename); | ||
| 900 | for (idx = 0; idx < fwcount; idx++) { | ||
| 901 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 902 | "request_firmware: Failed to find %s", | ||
| 903 | fwnames[idx]); | ||
| 904 | } | ||
| 905 | } | ||
| 906 | return ret; | ||
| 907 | } | ||
| 908 | |||
| 909 | |||
| 910 | /* | ||
| 911 | * pvr2_upload_firmware1(). | ||
| 912 | * | ||
| 913 | * Send the 8051 firmware to the device. After the upload, arrange for | ||
| 914 | * device to re-enumerate. | ||
| 915 | * | ||
| 916 | * NOTE : the pointer to the firmware data given by request_firmware() | ||
| 917 | * is not suitable for an usb transaction. | ||
| 918 | * | ||
| 919 | */ | ||
| 920 | int pvr2_upload_firmware1(struct pvr2_hdw *hdw) | ||
| 921 | { | ||
| 922 | const struct firmware *fw_entry = 0; | ||
| 923 | void *fw_ptr; | ||
| 924 | unsigned int pipe; | ||
| 925 | int ret; | ||
| 926 | u16 address; | ||
| 927 | static const char *fw_files_29xxx[] = { | ||
| 928 | "v4l-pvrusb2-29xxx-01.fw", | ||
| 929 | }; | ||
| 930 | #ifdef CONFIG_VIDEO_PVRUSB2_24XXX | ||
| 931 | static const char *fw_files_24xxx[] = { | ||
| 932 | "v4l-pvrusb2-24xxx-01.fw", | ||
| 933 | }; | ||
| 934 | #endif | ||
| 935 | static const struct pvr2_string_table fw_file_defs[] = { | ||
| 936 | [PVR2_HDW_TYPE_29XXX] = { | ||
| 937 | fw_files_29xxx, | ||
| 938 | sizeof(fw_files_29xxx)/sizeof(fw_files_29xxx[0]), | ||
| 939 | }, | ||
| 940 | #ifdef CONFIG_VIDEO_PVRUSB2_24XXX | ||
| 941 | [PVR2_HDW_TYPE_24XXX] = { | ||
| 942 | fw_files_24xxx, | ||
| 943 | sizeof(fw_files_24xxx)/sizeof(fw_files_24xxx[0]), | ||
| 944 | }, | ||
| 945 | #endif | ||
| 946 | }; | ||
| 947 | hdw->fw1_state = FW1_STATE_FAILED; // default result | ||
| 948 | |||
| 949 | trace_firmware("pvr2_upload_firmware1"); | ||
| 950 | |||
| 951 | ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller", | ||
| 952 | fw_file_defs[hdw->hdw_type].cnt, | ||
| 953 | fw_file_defs[hdw->hdw_type].lst); | ||
| 954 | if (ret < 0) { | ||
| 955 | if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING; | ||
| 956 | return ret; | ||
| 957 | } | ||
| 958 | |||
| 959 | usb_settoggle(hdw->usb_dev, 0 & 0xf, !(0 & USB_DIR_IN), 0); | ||
| 960 | usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f)); | ||
| 961 | |||
| 962 | pipe = usb_sndctrlpipe(hdw->usb_dev, 0); | ||
| 963 | |||
| 964 | if (fw_entry->size != 0x2000){ | ||
| 965 | pvr2_trace(PVR2_TRACE_ERROR_LEGS,"wrong fx2 firmware size"); | ||
| 966 | release_firmware(fw_entry); | ||
| 967 | return -ENOMEM; | ||
| 968 | } | ||
| 969 | |||
| 970 | fw_ptr = kmalloc(0x800, GFP_KERNEL); | ||
| 971 | if (fw_ptr == NULL){ | ||
| 972 | release_firmware(fw_entry); | ||
| 973 | return -ENOMEM; | ||
| 974 | } | ||
| 975 | |||
| 976 | /* We have to hold the CPU during firmware upload. */ | ||
| 977 | pvr2_hdw_cpureset_assert(hdw,1); | ||
| 978 | |||
| 979 | /* upload the firmware to address 0000-1fff in 2048 (=0x800) bytes | ||
| 980 | chunk. */ | ||
| 981 | |||
| 982 | ret = 0; | ||
| 983 | for(address = 0; address < fw_entry->size; address += 0x800) { | ||
| 984 | memcpy(fw_ptr, fw_entry->data + address, 0x800); | ||
| 985 | ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address, | ||
| 986 | 0, fw_ptr, 0x800, HZ); | ||
| 987 | } | ||
| 988 | |||
| 989 | trace_firmware("Upload done, releasing device's CPU"); | ||
| 990 | |||
| 991 | /* Now release the CPU. It will disconnect and reconnect later. */ | ||
| 992 | pvr2_hdw_cpureset_assert(hdw,0); | ||
| 993 | |||
| 994 | kfree(fw_ptr); | ||
| 995 | release_firmware(fw_entry); | ||
| 996 | |||
| 997 | trace_firmware("Upload done (%d bytes sent)",ret); | ||
| 998 | |||
| 999 | /* We should have written 8192 bytes */ | ||
| 1000 | if (ret == 8192) { | ||
| 1001 | hdw->fw1_state = FW1_STATE_RELOAD; | ||
| 1002 | return 0; | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | return -EIO; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | |||
| 1009 | /* | ||
| 1010 | * pvr2_upload_firmware2() | ||
| 1011 | * | ||
| 1012 | * This uploads encoder firmware on endpoint 2. | ||
| 1013 | * | ||
| 1014 | */ | ||
| 1015 | |||
| 1016 | int pvr2_upload_firmware2(struct pvr2_hdw *hdw) | ||
| 1017 | { | ||
| 1018 | const struct firmware *fw_entry = 0; | ||
| 1019 | void *fw_ptr; | ||
| 1020 | unsigned int pipe, fw_len, fw_done; | ||
| 1021 | int actual_length; | ||
| 1022 | int ret = 0; | ||
| 1023 | int fwidx; | ||
| 1024 | static const char *fw_files[] = { | ||
| 1025 | CX2341X_FIRM_ENC_FILENAME, | ||
| 1026 | }; | ||
| 1027 | |||
| 1028 | trace_firmware("pvr2_upload_firmware2"); | ||
| 1029 | |||
| 1030 | ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder", | ||
| 1031 | sizeof(fw_files)/sizeof(fw_files[0]), | ||
| 1032 | fw_files); | ||
| 1033 | if (ret < 0) return ret; | ||
| 1034 | fwidx = ret; | ||
| 1035 | ret = 0; | ||
| 1036 | /* Since we're about to completely reinitialize the encoder, | ||
| 1037 | invalidate our cached copy of its configuration state. Next | ||
| 1038 | time we configure the encoder, then we'll fully configure it. */ | ||
| 1039 | hdw->enc_cur_valid = 0; | ||
| 1040 | |||
| 1041 | /* First prepare firmware loading */ | ||
| 1042 | ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ | ||
| 1043 | ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ | ||
| 1044 | ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/ | ||
| 1045 | ret |= pvr2_hdw_cmd_deep_reset(hdw); | ||
| 1046 | ret |= pvr2_write_register(hdw, 0xa064, 0x00000000); /*APU command*/ | ||
| 1047 | ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000408); /*gpio dir*/ | ||
| 1048 | ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/ | ||
| 1049 | ret |= pvr2_write_register(hdw, 0x9058, 0xffffffed); /*VPU ctrl*/ | ||
| 1050 | ret |= pvr2_write_register(hdw, 0x9054, 0xfffffffd); /*reset hw blocks*/ | ||
| 1051 | ret |= pvr2_write_register(hdw, 0x07f8, 0x80000800); /*encoder SDRAM refresh*/ | ||
| 1052 | ret |= pvr2_write_register(hdw, 0x07fc, 0x0000001a); /*encoder SDRAM pre-charge*/ | ||
| 1053 | ret |= pvr2_write_register(hdw, 0x0700, 0x00000000); /*I2C clock*/ | ||
| 1054 | ret |= pvr2_write_register(hdw, 0xaa00, 0x00000000); /*unknown*/ | ||
| 1055 | ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/ | ||
| 1056 | ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/ | ||
| 1057 | ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/ | ||
| 1058 | ret |= pvr2_write_u8(hdw, 0x52, 0); | ||
| 1059 | ret |= pvr2_write_u16(hdw, 0x0600, 0); | ||
| 1060 | |||
| 1061 | if (ret) { | ||
| 1062 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1063 | "firmware2 upload prep failed, ret=%d",ret); | ||
| 1064 | release_firmware(fw_entry); | ||
| 1065 | return ret; | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | /* Now send firmware */ | ||
| 1069 | |||
| 1070 | fw_len = fw_entry->size; | ||
| 1071 | |||
| 1072 | if (fw_len % FIRMWARE_CHUNK_SIZE) { | ||
| 1073 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1074 | "size of %s firmware" | ||
| 1075 | " must be a multiple of 8192B", | ||
| 1076 | fw_files[fwidx]); | ||
| 1077 | release_firmware(fw_entry); | ||
| 1078 | return -1; | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL); | ||
| 1082 | if (fw_ptr == NULL){ | ||
| 1083 | release_firmware(fw_entry); | ||
| 1084 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1085 | "failed to allocate memory for firmware2 upload"); | ||
| 1086 | return -ENOMEM; | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT); | ||
| 1090 | |||
| 1091 | for (fw_done = 0 ; (fw_done < fw_len) && !ret ; | ||
| 1092 | fw_done += FIRMWARE_CHUNK_SIZE ) { | ||
| 1093 | int i; | ||
| 1094 | memcpy(fw_ptr, fw_entry->data + fw_done, FIRMWARE_CHUNK_SIZE); | ||
| 1095 | /* Usbsnoop log shows that we must swap bytes... */ | ||
| 1096 | for (i = 0; i < FIRMWARE_CHUNK_SIZE/4 ; i++) | ||
| 1097 | ((u32 *)fw_ptr)[i] = ___swab32(((u32 *)fw_ptr)[i]); | ||
| 1098 | |||
| 1099 | ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr, | ||
| 1100 | FIRMWARE_CHUNK_SIZE, | ||
| 1101 | &actual_length, HZ); | ||
| 1102 | ret |= (actual_length != FIRMWARE_CHUNK_SIZE); | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | trace_firmware("upload of %s : %i / %i ", | ||
| 1106 | fw_files[fwidx],fw_done,fw_len); | ||
| 1107 | |||
| 1108 | kfree(fw_ptr); | ||
| 1109 | release_firmware(fw_entry); | ||
| 1110 | |||
| 1111 | if (ret) { | ||
| 1112 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1113 | "firmware2 upload transfer failure"); | ||
| 1114 | return ret; | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | /* Finish upload */ | ||
| 1118 | |||
| 1119 | ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/ | ||
| 1120 | ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/ | ||
| 1121 | ret |= pvr2_write_u16(hdw, 0x0600, 0); | ||
| 1122 | |||
| 1123 | if (ret) { | ||
| 1124 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1125 | "firmware2 upload post-proc failure"); | ||
| 1126 | } else { | ||
| 1127 | hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_FIRMWARE); | ||
| 1128 | } | ||
| 1129 | return ret; | ||
| 1130 | } | ||
| 1131 | |||
| 1132 | |||
| 1133 | #define FIRMWARE_RECOVERY_BITS \ | ||
| 1134 | ((1<<PVR2_SUBSYS_B_ENC_CFG) | \ | ||
| 1135 | (1<<PVR2_SUBSYS_B_ENC_RUN) | \ | ||
| 1136 | (1<<PVR2_SUBSYS_B_ENC_FIRMWARE) | \ | ||
| 1137 | (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) | ||
| 1138 | |||
| 1139 | /* | ||
| 1140 | |||
| 1141 | This single function is key to pretty much everything. The pvrusb2 | ||
| 1142 | device can logically be viewed as a series of subsystems which can be | ||
| 1143 | stopped / started or unconfigured / configured. To get things streaming, | ||
| 1144 | one must configure everything and start everything, but there may be | ||
| 1145 | various reasons over time to deconfigure something or stop something. | ||
| 1146 | This function handles all of this activity. Everything EVERYWHERE that | ||
| 1147 | must affect a subsystem eventually comes here to do the work. | ||
| 1148 | |||
| 1149 | The current state of all subsystems is represented by a single bit mask, | ||
| 1150 | known as subsys_enabled_mask. The bit positions are defined by the | ||
| 1151 | PVR2_SUBSYS_xxxx macros, with one subsystem per bit position. At any | ||
| 1152 | time the set of configured or active subsystems can be queried just by | ||
| 1153 | looking at that mask. To change bits in that mask, this function here | ||
| 1154 | must be called. The "msk" argument indicates which bit positions to | ||
| 1155 | change, and the "val" argument defines the new values for the positions | ||
| 1156 | defined by "msk". | ||
| 1157 | |||
| 1158 | There is a priority ordering of starting / stopping things, and for | ||
| 1159 | multiple requested changes, this function implements that ordering. | ||
| 1160 | (Thus we will act on a request to load encoder firmware before we | ||
| 1161 | configure the encoder.) In addition to priority ordering, there is a | ||
| 1162 | recovery strategy implemented here. If a particular step fails and we | ||
| 1163 | detect that failure, this function will clear the affected subsystem bits | ||
| 1164 | and restart. Thus we have a means for recovering from a dead encoder: | ||
| 1165 | Clear all bits that correspond to subsystems that we need to restart / | ||
| 1166 | reconfigure and start over. | ||
| 1167 | |||
| 1168 | */ | ||
| 1169 | void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, | ||
| 1170 | unsigned long msk,unsigned long val) | ||
| 1171 | { | ||
| 1172 | unsigned long nmsk; | ||
| 1173 | unsigned long vmsk; | ||
| 1174 | int ret; | ||
| 1175 | unsigned int tryCount = 0; | ||
| 1176 | |||
| 1177 | if (!hdw->flag_ok) return; | ||
| 1178 | |||
| 1179 | msk &= PVR2_SUBSYS_ALL; | ||
| 1180 | nmsk = (hdw->subsys_enabled_mask & ~msk) | (val & msk); | ||
| 1181 | nmsk &= PVR2_SUBSYS_ALL; | ||
| 1182 | |||
| 1183 | for (;;) { | ||
| 1184 | tryCount++; | ||
| 1185 | if (!((nmsk ^ hdw->subsys_enabled_mask) & | ||
| 1186 | PVR2_SUBSYS_ALL)) break; | ||
| 1187 | if (tryCount > 4) { | ||
| 1188 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1189 | "Too many retries when configuring device;" | ||
| 1190 | " giving up"); | ||
| 1191 | pvr2_hdw_render_useless(hdw); | ||
| 1192 | break; | ||
| 1193 | } | ||
| 1194 | if (tryCount > 1) { | ||
| 1195 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1196 | "Retrying device reconfiguration"); | ||
| 1197 | } | ||
| 1198 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1199 | "subsys mask changing 0x%lx:0x%lx" | ||
| 1200 | " from 0x%lx to 0x%lx", | ||
| 1201 | msk,val,hdw->subsys_enabled_mask,nmsk); | ||
| 1202 | |||
| 1203 | vmsk = (nmsk ^ hdw->subsys_enabled_mask) & | ||
| 1204 | hdw->subsys_enabled_mask; | ||
| 1205 | if (vmsk) { | ||
| 1206 | if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) { | ||
| 1207 | pvr2_trace(PVR2_TRACE_CTL, | ||
| 1208 | "/*---TRACE_CTL----*/" | ||
| 1209 | " pvr2_encoder_stop"); | ||
| 1210 | ret = pvr2_encoder_stop(hdw); | ||
| 1211 | if (ret) { | ||
| 1212 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1213 | "Error recovery initiated"); | ||
| 1214 | hdw->subsys_enabled_mask &= | ||
| 1215 | ~FIRMWARE_RECOVERY_BITS; | ||
| 1216 | continue; | ||
| 1217 | } | ||
| 1218 | } | ||
| 1219 | if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) { | ||
| 1220 | pvr2_trace(PVR2_TRACE_CTL, | ||
| 1221 | "/*---TRACE_CTL----*/" | ||
| 1222 | " pvr2_hdw_cmd_usbstream(0)"); | ||
| 1223 | pvr2_hdw_cmd_usbstream(hdw,0); | ||
| 1224 | } | ||
| 1225 | if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) { | ||
| 1226 | pvr2_trace(PVR2_TRACE_CTL, | ||
| 1227 | "/*---TRACE_CTL----*/" | ||
| 1228 | " decoder disable"); | ||
| 1229 | if (hdw->decoder_ctrl) { | ||
| 1230 | hdw->decoder_ctrl->enable( | ||
| 1231 | hdw->decoder_ctrl->ctxt,0); | ||
| 1232 | } else { | ||
| 1233 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1234 | "WARNING:" | ||
| 1235 | " No decoder present"); | ||
| 1236 | } | ||
| 1237 | hdw->subsys_enabled_mask &= | ||
| 1238 | ~(1<<PVR2_SUBSYS_B_DIGITIZER_RUN); | ||
| 1239 | } | ||
| 1240 | if (vmsk & PVR2_SUBSYS_CFG_ALL) { | ||
| 1241 | hdw->subsys_enabled_mask &= | ||
| 1242 | ~(vmsk & PVR2_SUBSYS_CFG_ALL); | ||
| 1243 | } | ||
| 1244 | } | ||
| 1245 | vmsk = (nmsk ^ hdw->subsys_enabled_mask) & nmsk; | ||
| 1246 | if (vmsk) { | ||
| 1247 | if (vmsk & (1<<PVR2_SUBSYS_B_ENC_FIRMWARE)) { | ||
| 1248 | pvr2_trace(PVR2_TRACE_CTL, | ||
| 1249 | "/*---TRACE_CTL----*/" | ||
| 1250 | " pvr2_upload_firmware2"); | ||
| 1251 | ret = pvr2_upload_firmware2(hdw); | ||
| 1252 | if (ret) { | ||
| 1253 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1254 | "Failure uploading encoder" | ||
| 1255 | " firmware"); | ||
| 1256 | pvr2_hdw_render_useless(hdw); | ||
| 1257 | break; | ||
| 1258 | } | ||
| 1259 | } | ||
| 1260 | if (vmsk & (1<<PVR2_SUBSYS_B_ENC_CFG)) { | ||
| 1261 | pvr2_trace(PVR2_TRACE_CTL, | ||
| 1262 | "/*---TRACE_CTL----*/" | ||
| 1263 | " pvr2_encoder_configure"); | ||
| 1264 | ret = pvr2_encoder_configure(hdw); | ||
| 1265 | if (ret) { | ||
| 1266 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1267 | "Error recovery initiated"); | ||
| 1268 | hdw->subsys_enabled_mask &= | ||
| 1269 | ~FIRMWARE_RECOVERY_BITS; | ||
| 1270 | continue; | ||
| 1271 | } | ||
| 1272 | } | ||
| 1273 | if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) { | ||
| 1274 | pvr2_trace(PVR2_TRACE_CTL, | ||
| 1275 | "/*---TRACE_CTL----*/" | ||
| 1276 | " decoder enable"); | ||
| 1277 | if (hdw->decoder_ctrl) { | ||
| 1278 | hdw->decoder_ctrl->enable( | ||
| 1279 | hdw->decoder_ctrl->ctxt,!0); | ||
| 1280 | } else { | ||
| 1281 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1282 | "WARNING:" | ||
| 1283 | " No decoder present"); | ||
| 1284 | } | ||
| 1285 | hdw->subsys_enabled_mask |= | ||
| 1286 | (1<<PVR2_SUBSYS_B_DIGITIZER_RUN); | ||
| 1287 | } | ||
| 1288 | if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) { | ||
| 1289 | pvr2_trace(PVR2_TRACE_CTL, | ||
| 1290 | "/*---TRACE_CTL----*/" | ||
| 1291 | " pvr2_hdw_cmd_usbstream(1)"); | ||
| 1292 | pvr2_hdw_cmd_usbstream(hdw,!0); | ||
| 1293 | } | ||
| 1294 | if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) { | ||
| 1295 | pvr2_trace(PVR2_TRACE_CTL, | ||
| 1296 | "/*---TRACE_CTL----*/" | ||
| 1297 | " pvr2_encoder_start"); | ||
| 1298 | ret = pvr2_encoder_start(hdw); | ||
| 1299 | if (ret) { | ||
| 1300 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1301 | "Error recovery initiated"); | ||
| 1302 | hdw->subsys_enabled_mask &= | ||
| 1303 | ~FIRMWARE_RECOVERY_BITS; | ||
| 1304 | continue; | ||
| 1305 | } | ||
| 1306 | } | ||
| 1307 | } | ||
| 1308 | } | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | |||
| 1312 | void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw, | ||
| 1313 | unsigned long msk,unsigned long val) | ||
| 1314 | { | ||
| 1315 | LOCK_TAKE(hdw->big_lock); do { | ||
| 1316 | pvr2_hdw_subsys_bit_chg_no_lock(hdw,msk,val); | ||
| 1317 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | |||
| 1321 | void pvr2_hdw_subsys_bit_set(struct pvr2_hdw *hdw,unsigned long msk) | ||
| 1322 | { | ||
| 1323 | pvr2_hdw_subsys_bit_chg(hdw,msk,msk); | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | |||
| 1327 | void pvr2_hdw_subsys_bit_clr(struct pvr2_hdw *hdw,unsigned long msk) | ||
| 1328 | { | ||
| 1329 | pvr2_hdw_subsys_bit_chg(hdw,msk,0); | ||
| 1330 | } | ||
| 1331 | |||
| 1332 | |||
| 1333 | unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *hdw) | ||
| 1334 | { | ||
| 1335 | return hdw->subsys_enabled_mask; | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | |||
| 1339 | unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *hdw) | ||
| 1340 | { | ||
| 1341 | return hdw->subsys_stream_mask; | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | |||
| 1345 | void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw, | ||
| 1346 | unsigned long msk, | ||
| 1347 | unsigned long val) | ||
| 1348 | { | ||
| 1349 | unsigned long val2; | ||
| 1350 | msk &= PVR2_SUBSYS_ALL; | ||
| 1351 | val2 = ((hdw->subsys_stream_mask & ~msk) | (val & msk)); | ||
| 1352 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1353 | "stream mask changing 0x%lx:0x%lx from 0x%lx to 0x%lx", | ||
| 1354 | msk,val,hdw->subsys_stream_mask,val2); | ||
| 1355 | hdw->subsys_stream_mask = val2; | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | |||
| 1359 | void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw, | ||
| 1360 | unsigned long msk, | ||
| 1361 | unsigned long val) | ||
| 1362 | { | ||
| 1363 | LOCK_TAKE(hdw->big_lock); do { | ||
| 1364 | pvr2_hdw_subsys_stream_bit_chg_no_lock(hdw,msk,val); | ||
| 1365 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 1366 | } | ||
| 1367 | |||
| 1368 | |||
| 1369 | int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl) | ||
| 1370 | { | ||
| 1371 | if ((!enableFl) == !(hdw->flag_streaming_enabled)) return 0; | ||
| 1372 | if (enableFl) { | ||
| 1373 | pvr2_trace(PVR2_TRACE_START_STOP, | ||
| 1374 | "/*--TRACE_STREAM--*/ enable"); | ||
| 1375 | pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,~0); | ||
| 1376 | } else { | ||
| 1377 | pvr2_trace(PVR2_TRACE_START_STOP, | ||
| 1378 | "/*--TRACE_STREAM--*/ disable"); | ||
| 1379 | pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0); | ||
| 1380 | } | ||
| 1381 | if (!hdw->flag_ok) return -EIO; | ||
| 1382 | hdw->flag_streaming_enabled = enableFl != 0; | ||
| 1383 | return 0; | ||
| 1384 | } | ||
| 1385 | |||
| 1386 | |||
| 1387 | int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw) | ||
| 1388 | { | ||
| 1389 | return hdw->flag_streaming_enabled != 0; | ||
| 1390 | } | ||
| 1391 | |||
| 1392 | |||
| 1393 | int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag) | ||
| 1394 | { | ||
| 1395 | int ret; | ||
| 1396 | LOCK_TAKE(hdw->big_lock); do { | ||
| 1397 | ret = pvr2_hdw_set_streaming_no_lock(hdw,enable_flag); | ||
| 1398 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 1399 | return ret; | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | |||
| 1403 | int pvr2_hdw_set_stream_type_no_lock(struct pvr2_hdw *hdw, | ||
| 1404 | enum pvr2_config config) | ||
| 1405 | { | ||
| 1406 | unsigned long sm = hdw->subsys_enabled_mask; | ||
| 1407 | if (!hdw->flag_ok) return -EIO; | ||
| 1408 | pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0); | ||
| 1409 | hdw->config = config; | ||
| 1410 | pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,sm); | ||
| 1411 | return 0; | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | |||
| 1415 | int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config) | ||
| 1416 | { | ||
| 1417 | int ret; | ||
| 1418 | if (!hdw->flag_ok) return -EIO; | ||
| 1419 | LOCK_TAKE(hdw->big_lock); | ||
| 1420 | ret = pvr2_hdw_set_stream_type_no_lock(hdw,config); | ||
| 1421 | LOCK_GIVE(hdw->big_lock); | ||
| 1422 | return ret; | ||
| 1423 | } | ||
| 1424 | |||
| 1425 | |||
| 1426 | static int get_default_tuner_type(struct pvr2_hdw *hdw) | ||
| 1427 | { | ||
| 1428 | int unit_number = hdw->unit_number; | ||
| 1429 | int tp = -1; | ||
| 1430 | if ((unit_number >= 0) && (unit_number < PVR_NUM)) { | ||
| 1431 | tp = tuner[unit_number]; | ||
| 1432 | } | ||
| 1433 | if (tp < 0) return -EINVAL; | ||
| 1434 | hdw->tuner_type = tp; | ||
| 1435 | return 0; | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | |||
| 1439 | static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw) | ||
| 1440 | { | ||
| 1441 | int unit_number = hdw->unit_number; | ||
| 1442 | int tp = 0; | ||
| 1443 | if ((unit_number >= 0) && (unit_number < PVR_NUM)) { | ||
| 1444 | tp = video_std[unit_number]; | ||
| 1445 | } | ||
| 1446 | return tp; | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | |||
| 1450 | static unsigned int get_default_error_tolerance(struct pvr2_hdw *hdw) | ||
| 1451 | { | ||
| 1452 | int unit_number = hdw->unit_number; | ||
| 1453 | int tp = 0; | ||
| 1454 | if ((unit_number >= 0) && (unit_number < PVR_NUM)) { | ||
| 1455 | tp = tolerance[unit_number]; | ||
| 1456 | } | ||
| 1457 | return tp; | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | |||
| 1461 | static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw) | ||
| 1462 | { | ||
| 1463 | /* Try a harmless request to fetch the eeprom's address over | ||
| 1464 | endpoint 1. See what happens. Only the full FX2 image can | ||
| 1465 | respond to this. If this probe fails then likely the FX2 | ||
| 1466 | firmware needs be loaded. */ | ||
| 1467 | int result; | ||
| 1468 | LOCK_TAKE(hdw->ctl_lock); do { | ||
| 1469 | hdw->cmd_buffer[0] = 0xeb; | ||
| 1470 | result = pvr2_send_request_ex(hdw,HZ*1,!0, | ||
| 1471 | hdw->cmd_buffer,1, | ||
| 1472 | hdw->cmd_buffer,1); | ||
| 1473 | if (result < 0) break; | ||
| 1474 | } while(0); LOCK_GIVE(hdw->ctl_lock); | ||
| 1475 | if (result) { | ||
| 1476 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1477 | "Probe of device endpoint 1 result status %d", | ||
| 1478 | result); | ||
| 1479 | } else { | ||
| 1480 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1481 | "Probe of device endpoint 1 succeeded"); | ||
| 1482 | } | ||
| 1483 | return result == 0; | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) | ||
| 1487 | { | ||
| 1488 | char buf[40]; | ||
| 1489 | unsigned int bcnt; | ||
| 1490 | v4l2_std_id std1,std2; | ||
| 1491 | |||
| 1492 | std1 = get_default_standard(hdw); | ||
| 1493 | |||
| 1494 | bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom); | ||
| 1495 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1496 | "Supported video standard(s) reported by eeprom: %.*s", | ||
| 1497 | bcnt,buf); | ||
| 1498 | |||
| 1499 | hdw->std_mask_avail = hdw->std_mask_eeprom; | ||
| 1500 | |||
| 1501 | std2 = std1 & ~hdw->std_mask_avail; | ||
| 1502 | if (std2) { | ||
| 1503 | bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2); | ||
| 1504 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1505 | "Expanding supported video standards" | ||
| 1506 | " to include: %.*s", | ||
| 1507 | bcnt,buf); | ||
| 1508 | hdw->std_mask_avail |= std2; | ||
| 1509 | } | ||
| 1510 | |||
| 1511 | pvr2_hdw_internal_set_std_avail(hdw); | ||
| 1512 | |||
| 1513 | if (std1) { | ||
| 1514 | bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1); | ||
| 1515 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1516 | "Initial video standard forced to %.*s", | ||
| 1517 | bcnt,buf); | ||
| 1518 | hdw->std_mask_cur = std1; | ||
| 1519 | hdw->std_dirty = !0; | ||
| 1520 | pvr2_hdw_internal_find_stdenum(hdw); | ||
| 1521 | return; | ||
| 1522 | } | ||
| 1523 | |||
| 1524 | if (hdw->std_enum_cnt > 1) { | ||
| 1525 | // Autoselect the first listed standard | ||
| 1526 | hdw->std_enum_cur = 1; | ||
| 1527 | hdw->std_mask_cur = hdw->std_defs[hdw->std_enum_cur-1].id; | ||
| 1528 | hdw->std_dirty = !0; | ||
| 1529 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1530 | "Initial video standard auto-selected to %s", | ||
| 1531 | hdw->std_defs[hdw->std_enum_cur-1].name); | ||
| 1532 | return; | ||
| 1533 | } | ||
| 1534 | |||
| 1535 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1536 | "Unable to select a viable initial video standard"); | ||
| 1537 | } | ||
| 1538 | |||
| 1539 | |||
| 1540 | static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) | ||
| 1541 | { | ||
| 1542 | int ret; | ||
| 1543 | unsigned int idx; | ||
| 1544 | struct pvr2_ctrl *cptr; | ||
| 1545 | int reloadFl = 0; | ||
| 1546 | if (!reloadFl) { | ||
| 1547 | reloadFl = (hdw->usb_intf->cur_altsetting->desc.bNumEndpoints | ||
| 1548 | == 0); | ||
| 1549 | if (reloadFl) { | ||
| 1550 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1551 | "USB endpoint config looks strange" | ||
| 1552 | "; possibly firmware needs to be loaded"); | ||
| 1553 | } | ||
| 1554 | } | ||
| 1555 | if (!reloadFl) { | ||
| 1556 | reloadFl = !pvr2_hdw_check_firmware(hdw); | ||
| 1557 | if (reloadFl) { | ||
| 1558 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1559 | "Check for FX2 firmware failed" | ||
| 1560 | "; possibly firmware needs to be loaded"); | ||
| 1561 | } | ||
| 1562 | } | ||
| 1563 | if (reloadFl) { | ||
| 1564 | if (pvr2_upload_firmware1(hdw) != 0) { | ||
| 1565 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1566 | "Failure uploading firmware1"); | ||
| 1567 | } | ||
| 1568 | return; | ||
| 1569 | } | ||
| 1570 | hdw->fw1_state = FW1_STATE_OK; | ||
| 1571 | |||
| 1572 | if (initusbreset) { | ||
| 1573 | pvr2_hdw_device_reset(hdw); | ||
| 1574 | } | ||
| 1575 | if (!pvr2_hdw_dev_ok(hdw)) return; | ||
| 1576 | |||
| 1577 | for (idx = 0; idx < pvr2_client_lists[hdw->hdw_type].cnt; idx++) { | ||
| 1578 | request_module(pvr2_client_lists[hdw->hdw_type].lst[idx]); | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | pvr2_hdw_cmd_powerup(hdw); | ||
| 1582 | if (!pvr2_hdw_dev_ok(hdw)) return; | ||
| 1583 | |||
| 1584 | if (pvr2_upload_firmware2(hdw)){ | ||
| 1585 | pvr2_trace(PVR2_TRACE_ERROR_LEGS,"device unstable!!"); | ||
| 1586 | pvr2_hdw_render_useless(hdw); | ||
| 1587 | return; | ||
| 1588 | } | ||
| 1589 | |||
| 1590 | // This step MUST happen after the earlier powerup step. | ||
| 1591 | pvr2_i2c_core_init(hdw); | ||
| 1592 | if (!pvr2_hdw_dev_ok(hdw)) return; | ||
| 1593 | |||
| 1594 | for (idx = 0; idx < CTRLDEF_COUNT; idx++) { | ||
| 1595 | cptr = hdw->controls + idx; | ||
| 1596 | if (cptr->info->skip_init) continue; | ||
| 1597 | if (!cptr->info->set_value) continue; | ||
| 1598 | cptr->info->set_value(cptr,~0,cptr->info->default_value); | ||
| 1599 | } | ||
| 1600 | |||
| 1601 | // Do not use pvr2_reset_ctl_endpoints() here. It is not | ||
| 1602 | // thread-safe against the normal pvr2_send_request() mechanism. | ||
| 1603 | // (We should make it thread safe). | ||
| 1604 | |||
| 1605 | ret = pvr2_hdw_get_eeprom_addr(hdw); | ||
| 1606 | if (!pvr2_hdw_dev_ok(hdw)) return; | ||
| 1607 | if (ret < 0) { | ||
| 1608 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1609 | "Unable to determine location of eeprom, skipping"); | ||
| 1610 | } else { | ||
| 1611 | hdw->eeprom_addr = ret; | ||
| 1612 | pvr2_eeprom_analyze(hdw); | ||
| 1613 | if (!pvr2_hdw_dev_ok(hdw)) return; | ||
| 1614 | } | ||
| 1615 | |||
| 1616 | pvr2_hdw_setup_std(hdw); | ||
| 1617 | |||
| 1618 | if (!get_default_tuner_type(hdw)) { | ||
| 1619 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1620 | "pvr2_hdw_setup: Tuner type overridden to %d", | ||
| 1621 | hdw->tuner_type); | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | hdw->tuner_updated = !0; | ||
| 1625 | pvr2_i2c_core_check_stale(hdw); | ||
| 1626 | hdw->tuner_updated = 0; | ||
| 1627 | |||
| 1628 | if (!pvr2_hdw_dev_ok(hdw)) return; | ||
| 1629 | |||
| 1630 | pvr2_hdw_commit_ctl_internal(hdw); | ||
| 1631 | if (!pvr2_hdw_dev_ok(hdw)) return; | ||
| 1632 | |||
| 1633 | hdw->vid_stream = pvr2_stream_create(); | ||
| 1634 | if (!pvr2_hdw_dev_ok(hdw)) return; | ||
| 1635 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1636 | "pvr2_hdw_setup: video stream is %p",hdw->vid_stream); | ||
| 1637 | if (hdw->vid_stream) { | ||
| 1638 | idx = get_default_error_tolerance(hdw); | ||
| 1639 | if (idx) { | ||
| 1640 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1641 | "pvr2_hdw_setup: video stream %p" | ||
| 1642 | " setting tolerance %u", | ||
| 1643 | hdw->vid_stream,idx); | ||
| 1644 | } | ||
| 1645 | pvr2_stream_setup(hdw->vid_stream,hdw->usb_dev, | ||
| 1646 | PVR2_VID_ENDPOINT,idx); | ||
| 1647 | } | ||
| 1648 | |||
| 1649 | if (!pvr2_hdw_dev_ok(hdw)) return; | ||
| 1650 | |||
| 1651 | /* Make sure everything is up to date */ | ||
| 1652 | pvr2_i2c_core_sync(hdw); | ||
| 1653 | |||
| 1654 | if (!pvr2_hdw_dev_ok(hdw)) return; | ||
| 1655 | |||
| 1656 | hdw->flag_init_ok = !0; | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | |||
| 1660 | int pvr2_hdw_setup(struct pvr2_hdw *hdw) | ||
| 1661 | { | ||
| 1662 | pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw); | ||
| 1663 | LOCK_TAKE(hdw->big_lock); do { | ||
| 1664 | pvr2_hdw_setup_low(hdw); | ||
| 1665 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1666 | "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d", | ||
| 1667 | hdw,hdw->flag_ok,hdw->flag_init_ok); | ||
| 1668 | if (pvr2_hdw_dev_ok(hdw)) { | ||
| 1669 | if (pvr2_hdw_init_ok(hdw)) { | ||
| 1670 | pvr2_trace( | ||
| 1671 | PVR2_TRACE_INFO, | ||
| 1672 | "Device initialization" | ||
| 1673 | " completed successfully."); | ||
| 1674 | break; | ||
| 1675 | } | ||
| 1676 | if (hdw->fw1_state == FW1_STATE_RELOAD) { | ||
| 1677 | pvr2_trace( | ||
| 1678 | PVR2_TRACE_INFO, | ||
| 1679 | "Device microcontroller firmware" | ||
| 1680 | " (re)loaded; it should now reset" | ||
| 1681 | " and reconnect."); | ||
| 1682 | break; | ||
| 1683 | } | ||
| 1684 | pvr2_trace( | ||
| 1685 | PVR2_TRACE_ERROR_LEGS, | ||
| 1686 | "Device initialization was not successful."); | ||
| 1687 | if (hdw->fw1_state == FW1_STATE_MISSING) { | ||
| 1688 | pvr2_trace( | ||
| 1689 | PVR2_TRACE_ERROR_LEGS, | ||
| 1690 | "Giving up since device" | ||
| 1691 | " microcontroller firmware" | ||
| 1692 | " appears to be missing."); | ||
| 1693 | break; | ||
| 1694 | } | ||
| 1695 | } | ||
| 1696 | if (procreload) { | ||
| 1697 | pvr2_trace( | ||
| 1698 | PVR2_TRACE_ERROR_LEGS, | ||
| 1699 | "Attempting pvrusb2 recovery by reloading" | ||
| 1700 | " primary firmware."); | ||
| 1701 | pvr2_trace( | ||
| 1702 | PVR2_TRACE_ERROR_LEGS, | ||
| 1703 | "If this works, device should disconnect" | ||
| 1704 | " and reconnect in a sane state."); | ||
| 1705 | hdw->fw1_state = FW1_STATE_UNKNOWN; | ||
| 1706 | pvr2_upload_firmware1(hdw); | ||
| 1707 | } else { | ||
| 1708 | pvr2_trace( | ||
| 1709 | PVR2_TRACE_ERROR_LEGS, | ||
| 1710 | "***WARNING*** pvrusb2 device hardware" | ||
| 1711 | " appears to be jammed" | ||
| 1712 | " and I can't clear it."); | ||
| 1713 | pvr2_trace( | ||
| 1714 | PVR2_TRACE_ERROR_LEGS, | ||
| 1715 | "You might need to power cycle" | ||
| 1716 | " the pvrusb2 device" | ||
| 1717 | " in order to recover."); | ||
| 1718 | } | ||
| 1719 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 1720 | pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw); | ||
| 1721 | return hdw->flag_init_ok; | ||
| 1722 | } | ||
| 1723 | |||
| 1724 | |||
| 1725 | /* Create and return a structure for interacting with the underlying | ||
| 1726 | hardware */ | ||
| 1727 | struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, | ||
| 1728 | const struct usb_device_id *devid) | ||
| 1729 | { | ||
| 1730 | unsigned int idx,cnt1,cnt2; | ||
| 1731 | struct pvr2_hdw *hdw; | ||
| 1732 | unsigned int hdw_type; | ||
| 1733 | int valid_std_mask; | ||
| 1734 | struct pvr2_ctrl *cptr; | ||
| 1735 | __u8 ifnum; | ||
| 1736 | struct v4l2_queryctrl qctrl; | ||
| 1737 | struct pvr2_ctl_info *ciptr; | ||
| 1738 | |||
| 1739 | hdw_type = devid - pvr2_device_table; | ||
| 1740 | if (hdw_type >= | ||
| 1741 | sizeof(pvr2_device_names)/sizeof(pvr2_device_names[0])) { | ||
| 1742 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 1743 | "Bogus device type of %u reported",hdw_type); | ||
| 1744 | return 0; | ||
| 1745 | } | ||
| 1746 | |||
| 1747 | hdw = kmalloc(sizeof(*hdw),GFP_KERNEL); | ||
| 1748 | pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", | ||
| 1749 | hdw,pvr2_device_names[hdw_type]); | ||
| 1750 | if (!hdw) goto fail; | ||
| 1751 | memset(hdw,0,sizeof(*hdw)); | ||
| 1752 | cx2341x_fill_defaults(&hdw->enc_ctl_state); | ||
| 1753 | |||
| 1754 | hdw->control_cnt = CTRLDEF_COUNT; | ||
| 1755 | hdw->control_cnt += MPEGDEF_COUNT; | ||
| 1756 | hdw->controls = kmalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, | ||
| 1757 | GFP_KERNEL); | ||
| 1758 | if (!hdw->controls) goto fail; | ||
| 1759 | memset(hdw->controls,0,sizeof(struct pvr2_ctrl) * hdw->control_cnt); | ||
| 1760 | hdw->hdw_type = hdw_type; | ||
| 1761 | for (idx = 0; idx < hdw->control_cnt; idx++) { | ||
| 1762 | cptr = hdw->controls + idx; | ||
| 1763 | cptr->hdw = hdw; | ||
| 1764 | } | ||
| 1765 | for (idx = 0; idx < 32; idx++) { | ||
| 1766 | hdw->std_mask_ptrs[idx] = hdw->std_mask_names[idx]; | ||
| 1767 | } | ||
| 1768 | for (idx = 0; idx < CTRLDEF_COUNT; idx++) { | ||
| 1769 | cptr = hdw->controls + idx; | ||
| 1770 | cptr->info = control_defs+idx; | ||
| 1771 | } | ||
| 1772 | /* Define and configure additional controls from cx2341x module. */ | ||
| 1773 | hdw->mpeg_ctrl_info = kmalloc( | ||
| 1774 | sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT, GFP_KERNEL); | ||
| 1775 | if (!hdw->mpeg_ctrl_info) goto fail; | ||
| 1776 | memset(hdw->mpeg_ctrl_info,0, | ||
| 1777 | sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT); | ||
| 1778 | for (idx = 0; idx < MPEGDEF_COUNT; idx++) { | ||
| 1779 | cptr = hdw->controls + idx + CTRLDEF_COUNT; | ||
| 1780 | ciptr = &(hdw->mpeg_ctrl_info[idx].info); | ||
| 1781 | ciptr->desc = hdw->mpeg_ctrl_info[idx].desc; | ||
| 1782 | ciptr->name = mpeg_ids[idx].strid; | ||
| 1783 | ciptr->v4l_id = mpeg_ids[idx].id; | ||
| 1784 | ciptr->skip_init = !0; | ||
| 1785 | ciptr->get_value = ctrl_cx2341x_get; | ||
| 1786 | ciptr->get_v4lflags = ctrl_cx2341x_getv4lflags; | ||
| 1787 | ciptr->is_dirty = ctrl_cx2341x_is_dirty; | ||
| 1788 | if (!idx) ciptr->clear_dirty = ctrl_cx2341x_clear_dirty; | ||
| 1789 | qctrl.id = ciptr->v4l_id; | ||
| 1790 | cx2341x_ctrl_query(&hdw->enc_ctl_state,&qctrl); | ||
| 1791 | if (!(qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)) { | ||
| 1792 | ciptr->set_value = ctrl_cx2341x_set; | ||
| 1793 | } | ||
| 1794 | strncpy(hdw->mpeg_ctrl_info[idx].desc,qctrl.name, | ||
| 1795 | PVR2_CTLD_INFO_DESC_SIZE); | ||
| 1796 | hdw->mpeg_ctrl_info[idx].desc[PVR2_CTLD_INFO_DESC_SIZE-1] = 0; | ||
| 1797 | ciptr->default_value = qctrl.default_value; | ||
| 1798 | switch (qctrl.type) { | ||
| 1799 | default: | ||
| 1800 | case V4L2_CTRL_TYPE_INTEGER: | ||
| 1801 | ciptr->type = pvr2_ctl_int; | ||
| 1802 | ciptr->def.type_int.min_value = qctrl.minimum; | ||
| 1803 | ciptr->def.type_int.max_value = qctrl.maximum; | ||
| 1804 | break; | ||
| 1805 | case V4L2_CTRL_TYPE_BOOLEAN: | ||
| 1806 | ciptr->type = pvr2_ctl_bool; | ||
| 1807 | break; | ||
| 1808 | case V4L2_CTRL_TYPE_MENU: | ||
| 1809 | ciptr->type = pvr2_ctl_enum; | ||
| 1810 | ciptr->def.type_enum.value_names = | ||
| 1811 | cx2341x_ctrl_get_menu(ciptr->v4l_id); | ||
| 1812 | for (cnt1 = 0; | ||
| 1813 | ciptr->def.type_enum.value_names[cnt1] != NULL; | ||
| 1814 | cnt1++) { } | ||
| 1815 | ciptr->def.type_enum.count = cnt1; | ||
| 1816 | break; | ||
| 1817 | } | ||
| 1818 | cptr->info = ciptr; | ||
| 1819 | } | ||
| 1820 | |||
| 1821 | // Initialize video standard enum dynamic control | ||
| 1822 | cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDENUM); | ||
| 1823 | if (cptr) { | ||
| 1824 | memcpy(&hdw->std_info_enum,cptr->info, | ||
| 1825 | sizeof(hdw->std_info_enum)); | ||
| 1826 | cptr->info = &hdw->std_info_enum; | ||
| 1827 | |||
| 1828 | } | ||
| 1829 | // Initialize control data regarding video standard masks | ||
| 1830 | valid_std_mask = pvr2_std_get_usable(); | ||
| 1831 | for (idx = 0; idx < 32; idx++) { | ||
| 1832 | if (!(valid_std_mask & (1 << idx))) continue; | ||
| 1833 | cnt1 = pvr2_std_id_to_str( | ||
| 1834 | hdw->std_mask_names[idx], | ||
| 1835 | sizeof(hdw->std_mask_names[idx])-1, | ||
| 1836 | 1 << idx); | ||
| 1837 | hdw->std_mask_names[idx][cnt1] = 0; | ||
| 1838 | } | ||
| 1839 | cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL); | ||
| 1840 | if (cptr) { | ||
| 1841 | memcpy(&hdw->std_info_avail,cptr->info, | ||
| 1842 | sizeof(hdw->std_info_avail)); | ||
| 1843 | cptr->info = &hdw->std_info_avail; | ||
| 1844 | hdw->std_info_avail.def.type_bitmask.bit_names = | ||
| 1845 | hdw->std_mask_ptrs; | ||
| 1846 | hdw->std_info_avail.def.type_bitmask.valid_bits = | ||
| 1847 | valid_std_mask; | ||
| 1848 | } | ||
| 1849 | cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR); | ||
| 1850 | if (cptr) { | ||
| 1851 | memcpy(&hdw->std_info_cur,cptr->info, | ||
| 1852 | sizeof(hdw->std_info_cur)); | ||
| 1853 | cptr->info = &hdw->std_info_cur; | ||
| 1854 | hdw->std_info_cur.def.type_bitmask.bit_names = | ||
| 1855 | hdw->std_mask_ptrs; | ||
| 1856 | hdw->std_info_avail.def.type_bitmask.valid_bits = | ||
| 1857 | valid_std_mask; | ||
| 1858 | } | ||
| 1859 | |||
| 1860 | hdw->eeprom_addr = -1; | ||
| 1861 | hdw->unit_number = -1; | ||
| 1862 | hdw->v4l_minor_number = -1; | ||
| 1863 | hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL); | ||
| 1864 | if (!hdw->ctl_write_buffer) goto fail; | ||
| 1865 | hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL); | ||
| 1866 | if (!hdw->ctl_read_buffer) goto fail; | ||
| 1867 | hdw->ctl_write_urb = usb_alloc_urb(0,GFP_KERNEL); | ||
| 1868 | if (!hdw->ctl_write_urb) goto fail; | ||
| 1869 | hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL); | ||
| 1870 | if (!hdw->ctl_read_urb) goto fail; | ||
| 1871 | |||
| 1872 | down(&pvr2_unit_sem); do { | ||
| 1873 | for (idx = 0; idx < PVR_NUM; idx++) { | ||
| 1874 | if (unit_pointers[idx]) continue; | ||
| 1875 | hdw->unit_number = idx; | ||
| 1876 | unit_pointers[idx] = hdw; | ||
| 1877 | break; | ||
| 1878 | } | ||
| 1879 | } while (0); up(&pvr2_unit_sem); | ||
| 1880 | |||
| 1881 | cnt1 = 0; | ||
| 1882 | cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"pvrusb2"); | ||
| 1883 | cnt1 += cnt2; | ||
| 1884 | if (hdw->unit_number >= 0) { | ||
| 1885 | cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"_%c", | ||
| 1886 | ('a' + hdw->unit_number)); | ||
| 1887 | cnt1 += cnt2; | ||
| 1888 | } | ||
| 1889 | if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1; | ||
| 1890 | hdw->name[cnt1] = 0; | ||
| 1891 | |||
| 1892 | pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s", | ||
| 1893 | hdw->unit_number,hdw->name); | ||
| 1894 | |||
| 1895 | hdw->tuner_type = -1; | ||
| 1896 | hdw->flag_ok = !0; | ||
| 1897 | /* Initialize the mask of subsystems that we will shut down when we | ||
| 1898 | stop streaming. */ | ||
| 1899 | hdw->subsys_stream_mask = PVR2_SUBSYS_RUN_ALL; | ||
| 1900 | hdw->subsys_stream_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); | ||
| 1901 | |||
| 1902 | pvr2_trace(PVR2_TRACE_INIT,"subsys_stream_mask: 0x%lx", | ||
| 1903 | hdw->subsys_stream_mask); | ||
| 1904 | |||
| 1905 | hdw->usb_intf = intf; | ||
| 1906 | hdw->usb_dev = interface_to_usbdev(intf); | ||
| 1907 | |||
| 1908 | ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber; | ||
| 1909 | usb_set_interface(hdw->usb_dev,ifnum,0); | ||
| 1910 | |||
| 1911 | mutex_init(&hdw->ctl_lock_mutex); | ||
| 1912 | mutex_init(&hdw->big_lock_mutex); | ||
| 1913 | |||
| 1914 | return hdw; | ||
| 1915 | fail: | ||
| 1916 | if (hdw) { | ||
| 1917 | if (hdw->ctl_read_urb) usb_free_urb(hdw->ctl_read_urb); | ||
| 1918 | if (hdw->ctl_write_urb) usb_free_urb(hdw->ctl_write_urb); | ||
| 1919 | if (hdw->ctl_read_buffer) kfree(hdw->ctl_read_buffer); | ||
| 1920 | if (hdw->ctl_write_buffer) kfree(hdw->ctl_write_buffer); | ||
| 1921 | if (hdw->controls) kfree(hdw->controls); | ||
| 1922 | if (hdw->mpeg_ctrl_info) kfree(hdw->mpeg_ctrl_info); | ||
| 1923 | kfree(hdw); | ||
| 1924 | } | ||
| 1925 | return 0; | ||
| 1926 | } | ||
| 1927 | |||
| 1928 | |||
| 1929 | /* Remove _all_ associations between this driver and the underlying USB | ||
| 1930 | layer. */ | ||
| 1931 | void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw) | ||
| 1932 | { | ||
| 1933 | if (hdw->flag_disconnected) return; | ||
| 1934 | pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_remove_usb_stuff: hdw=%p",hdw); | ||
| 1935 | if (hdw->ctl_read_urb) { | ||
| 1936 | usb_kill_urb(hdw->ctl_read_urb); | ||
| 1937 | usb_free_urb(hdw->ctl_read_urb); | ||
| 1938 | hdw->ctl_read_urb = 0; | ||
| 1939 | } | ||
| 1940 | if (hdw->ctl_write_urb) { | ||
| 1941 | usb_kill_urb(hdw->ctl_write_urb); | ||
| 1942 | usb_free_urb(hdw->ctl_write_urb); | ||
| 1943 | hdw->ctl_write_urb = 0; | ||
| 1944 | } | ||
| 1945 | if (hdw->ctl_read_buffer) { | ||
| 1946 | kfree(hdw->ctl_read_buffer); | ||
| 1947 | hdw->ctl_read_buffer = 0; | ||
| 1948 | } | ||
| 1949 | if (hdw->ctl_write_buffer) { | ||
| 1950 | kfree(hdw->ctl_write_buffer); | ||
| 1951 | hdw->ctl_write_buffer = 0; | ||
| 1952 | } | ||
| 1953 | pvr2_hdw_render_useless_unlocked(hdw); | ||
| 1954 | hdw->flag_disconnected = !0; | ||
| 1955 | hdw->usb_dev = 0; | ||
| 1956 | hdw->usb_intf = 0; | ||
| 1957 | } | ||
| 1958 | |||
| 1959 | |||
| 1960 | /* Destroy hardware interaction structure */ | ||
| 1961 | void pvr2_hdw_destroy(struct pvr2_hdw *hdw) | ||
| 1962 | { | ||
| 1963 | pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw); | ||
| 1964 | if (hdw->fw_buffer) { | ||
| 1965 | kfree(hdw->fw_buffer); | ||
| 1966 | hdw->fw_buffer = 0; | ||
| 1967 | } | ||
| 1968 | if (hdw->vid_stream) { | ||
| 1969 | pvr2_stream_destroy(hdw->vid_stream); | ||
| 1970 | hdw->vid_stream = 0; | ||
| 1971 | } | ||
| 1972 | if (hdw->audio_stat) { | ||
| 1973 | hdw->audio_stat->detach(hdw->audio_stat->ctxt); | ||
| 1974 | } | ||
| 1975 | if (hdw->decoder_ctrl) { | ||
| 1976 | hdw->decoder_ctrl->detach(hdw->decoder_ctrl->ctxt); | ||
| 1977 | } | ||
| 1978 | pvr2_i2c_core_done(hdw); | ||
| 1979 | pvr2_hdw_remove_usb_stuff(hdw); | ||
| 1980 | down(&pvr2_unit_sem); do { | ||
| 1981 | if ((hdw->unit_number >= 0) && | ||
| 1982 | (hdw->unit_number < PVR_NUM) && | ||
| 1983 | (unit_pointers[hdw->unit_number] == hdw)) { | ||
| 1984 | unit_pointers[hdw->unit_number] = 0; | ||
| 1985 | } | ||
| 1986 | } while (0); up(&pvr2_unit_sem); | ||
| 1987 | if (hdw->controls) kfree(hdw->controls); | ||
| 1988 | if (hdw->mpeg_ctrl_info) kfree(hdw->mpeg_ctrl_info); | ||
| 1989 | if (hdw->std_defs) kfree(hdw->std_defs); | ||
| 1990 | if (hdw->std_enum_names) kfree(hdw->std_enum_names); | ||
| 1991 | kfree(hdw); | ||
| 1992 | } | ||
| 1993 | |||
| 1994 | |||
| 1995 | int pvr2_hdw_init_ok(struct pvr2_hdw *hdw) | ||
| 1996 | { | ||
| 1997 | return hdw->flag_init_ok; | ||
| 1998 | } | ||
| 1999 | |||
| 2000 | |||
| 2001 | int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw) | ||
| 2002 | { | ||
| 2003 | return (hdw && hdw->flag_ok); | ||
| 2004 | } | ||
| 2005 | |||
| 2006 | |||
| 2007 | /* Called when hardware has been unplugged */ | ||
| 2008 | void pvr2_hdw_disconnect(struct pvr2_hdw *hdw) | ||
| 2009 | { | ||
| 2010 | pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_disconnect(hdw=%p)",hdw); | ||
| 2011 | LOCK_TAKE(hdw->big_lock); | ||
| 2012 | LOCK_TAKE(hdw->ctl_lock); | ||
| 2013 | pvr2_hdw_remove_usb_stuff(hdw); | ||
| 2014 | LOCK_GIVE(hdw->ctl_lock); | ||
| 2015 | LOCK_GIVE(hdw->big_lock); | ||
| 2016 | } | ||
| 2017 | |||
| 2018 | |||
| 2019 | // Attempt to autoselect an appropriate value for std_enum_cur given | ||
| 2020 | // whatever is currently in std_mask_cur | ||
| 2021 | void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw) | ||
| 2022 | { | ||
| 2023 | unsigned int idx; | ||
| 2024 | for (idx = 1; idx < hdw->std_enum_cnt; idx++) { | ||
| 2025 | if (hdw->std_defs[idx-1].id == hdw->std_mask_cur) { | ||
| 2026 | hdw->std_enum_cur = idx; | ||
| 2027 | return; | ||
| 2028 | } | ||
| 2029 | } | ||
| 2030 | hdw->std_enum_cur = 0; | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | |||
| 2034 | // Calculate correct set of enumerated standards based on currently known | ||
| 2035 | // set of available standards bits. | ||
| 2036 | void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw) | ||
| 2037 | { | ||
| 2038 | struct v4l2_standard *newstd; | ||
| 2039 | unsigned int std_cnt; | ||
| 2040 | unsigned int idx; | ||
| 2041 | |||
| 2042 | newstd = pvr2_std_create_enum(&std_cnt,hdw->std_mask_avail); | ||
| 2043 | |||
| 2044 | if (hdw->std_defs) { | ||
| 2045 | kfree(hdw->std_defs); | ||
| 2046 | hdw->std_defs = 0; | ||
| 2047 | } | ||
| 2048 | hdw->std_enum_cnt = 0; | ||
| 2049 | if (hdw->std_enum_names) { | ||
| 2050 | kfree(hdw->std_enum_names); | ||
| 2051 | hdw->std_enum_names = 0; | ||
| 2052 | } | ||
| 2053 | |||
| 2054 | if (!std_cnt) { | ||
| 2055 | pvr2_trace( | ||
| 2056 | PVR2_TRACE_ERROR_LEGS, | ||
| 2057 | "WARNING: Failed to identify any viable standards"); | ||
| 2058 | } | ||
| 2059 | hdw->std_enum_names = kmalloc(sizeof(char *)*(std_cnt+1),GFP_KERNEL); | ||
| 2060 | hdw->std_enum_names[0] = "none"; | ||
| 2061 | for (idx = 0; idx < std_cnt; idx++) { | ||
| 2062 | hdw->std_enum_names[idx+1] = | ||
| 2063 | newstd[idx].name; | ||
| 2064 | } | ||
| 2065 | // Set up the dynamic control for this standard | ||
| 2066 | hdw->std_info_enum.def.type_enum.value_names = hdw->std_enum_names; | ||
| 2067 | hdw->std_info_enum.def.type_enum.count = std_cnt+1; | ||
| 2068 | hdw->std_defs = newstd; | ||
| 2069 | hdw->std_enum_cnt = std_cnt+1; | ||
| 2070 | hdw->std_enum_cur = 0; | ||
| 2071 | hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; | ||
| 2072 | } | ||
| 2073 | |||
| 2074 | |||
| 2075 | int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw, | ||
| 2076 | struct v4l2_standard *std, | ||
| 2077 | unsigned int idx) | ||
| 2078 | { | ||
| 2079 | int ret = -EINVAL; | ||
| 2080 | if (!idx) return ret; | ||
| 2081 | LOCK_TAKE(hdw->big_lock); do { | ||
| 2082 | if (idx >= hdw->std_enum_cnt) break; | ||
| 2083 | idx--; | ||
| 2084 | memcpy(std,hdw->std_defs+idx,sizeof(*std)); | ||
| 2085 | ret = 0; | ||
| 2086 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 2087 | return ret; | ||
| 2088 | } | ||
| 2089 | |||
| 2090 | |||
| 2091 | /* Get the number of defined controls */ | ||
| 2092 | unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw) | ||
| 2093 | { | ||
| 2094 | return hdw->control_cnt; | ||
| 2095 | } | ||
| 2096 | |||
| 2097 | |||
| 2098 | /* Retrieve a control handle given its index (0..count-1) */ | ||
| 2099 | struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw, | ||
| 2100 | unsigned int idx) | ||
| 2101 | { | ||
| 2102 | if (idx >= hdw->control_cnt) return 0; | ||
| 2103 | return hdw->controls + idx; | ||
| 2104 | } | ||
| 2105 | |||
| 2106 | |||
| 2107 | /* Retrieve a control handle given its index (0..count-1) */ | ||
| 2108 | struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw, | ||
| 2109 | unsigned int ctl_id) | ||
| 2110 | { | ||
| 2111 | struct pvr2_ctrl *cptr; | ||
| 2112 | unsigned int idx; | ||
| 2113 | int i; | ||
| 2114 | |||
| 2115 | /* This could be made a lot more efficient, but for now... */ | ||
| 2116 | for (idx = 0; idx < hdw->control_cnt; idx++) { | ||
| 2117 | cptr = hdw->controls + idx; | ||
| 2118 | i = cptr->info->internal_id; | ||
| 2119 | if (i && (i == ctl_id)) return cptr; | ||
| 2120 | } | ||
| 2121 | return 0; | ||
| 2122 | } | ||
| 2123 | |||
| 2124 | |||
| 2125 | /* Given a V4L ID, retrieve the control structure associated with it. */ | ||
| 2126 | struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id) | ||
| 2127 | { | ||
| 2128 | struct pvr2_ctrl *cptr; | ||
| 2129 | unsigned int idx; | ||
| 2130 | int i; | ||
| 2131 | |||
| 2132 | /* This could be made a lot more efficient, but for now... */ | ||
| 2133 | for (idx = 0; idx < hdw->control_cnt; idx++) { | ||
| 2134 | cptr = hdw->controls + idx; | ||
| 2135 | i = cptr->info->v4l_id; | ||
| 2136 | if (i && (i == ctl_id)) return cptr; | ||
| 2137 | } | ||
| 2138 | return 0; | ||
| 2139 | } | ||
| 2140 | |||
| 2141 | |||
| 2142 | /* Given a V4L ID for its immediate predecessor, retrieve the control | ||
| 2143 | structure associated with it. */ | ||
| 2144 | struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *hdw, | ||
| 2145 | unsigned int ctl_id) | ||
| 2146 | { | ||
| 2147 | struct pvr2_ctrl *cptr,*cp2; | ||
| 2148 | unsigned int idx; | ||
| 2149 | int i; | ||
| 2150 | |||
| 2151 | /* This could be made a lot more efficient, but for now... */ | ||
| 2152 | cp2 = 0; | ||
| 2153 | for (idx = 0; idx < hdw->control_cnt; idx++) { | ||
| 2154 | cptr = hdw->controls + idx; | ||
| 2155 | i = cptr->info->v4l_id; | ||
| 2156 | if (!i) continue; | ||
| 2157 | if (i <= ctl_id) continue; | ||
| 2158 | if (cp2 && (cp2->info->v4l_id < i)) continue; | ||
| 2159 | cp2 = cptr; | ||
| 2160 | } | ||
| 2161 | return cp2; | ||
| 2162 | return 0; | ||
| 2163 | } | ||
| 2164 | |||
| 2165 | |||
| 2166 | static const char *get_ctrl_typename(enum pvr2_ctl_type tp) | ||
| 2167 | { | ||
| 2168 | switch (tp) { | ||
| 2169 | case pvr2_ctl_int: return "integer"; | ||
| 2170 | case pvr2_ctl_enum: return "enum"; | ||
| 2171 | case pvr2_ctl_bool: return "boolean"; | ||
| 2172 | case pvr2_ctl_bitmask: return "bitmask"; | ||
| 2173 | } | ||
| 2174 | return ""; | ||
| 2175 | } | ||
| 2176 | |||
| 2177 | |||
| 2178 | /* Commit all control changes made up to this point. Subsystems can be | ||
| 2179 | indirectly affected by these changes. For a given set of things being | ||
| 2180 | committed, we'll clear the affected subsystem bits and then once we're | ||
| 2181 | done committing everything we'll make a request to restore the subsystem | ||
| 2182 | state(s) back to their previous value before this function was called. | ||
| 2183 | Thus we can automatically reconfigure affected pieces of the driver as | ||
| 2184 | controls are changed. */ | ||
| 2185 | int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) | ||
| 2186 | { | ||
| 2187 | unsigned long saved_subsys_mask = hdw->subsys_enabled_mask; | ||
| 2188 | unsigned long stale_subsys_mask = 0; | ||
| 2189 | unsigned int idx; | ||
| 2190 | struct pvr2_ctrl *cptr; | ||
| 2191 | int value; | ||
| 2192 | int commit_flag = 0; | ||
| 2193 | char buf[100]; | ||
| 2194 | unsigned int bcnt,ccnt; | ||
| 2195 | |||
| 2196 | for (idx = 0; idx < hdw->control_cnt; idx++) { | ||
| 2197 | cptr = hdw->controls + idx; | ||
| 2198 | if (cptr->info->is_dirty == 0) continue; | ||
| 2199 | if (!cptr->info->is_dirty(cptr)) continue; | ||
| 2200 | if (!commit_flag) { | ||
| 2201 | commit_flag = !0; | ||
| 2202 | } | ||
| 2203 | |||
| 2204 | bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ", | ||
| 2205 | cptr->info->name); | ||
| 2206 | value = 0; | ||
| 2207 | cptr->info->get_value(cptr,&value); | ||
| 2208 | pvr2_ctrl_value_to_sym_internal(cptr,~0,value, | ||
| 2209 | buf+bcnt, | ||
| 2210 | sizeof(buf)-bcnt,&ccnt); | ||
| 2211 | bcnt += ccnt; | ||
| 2212 | bcnt += scnprintf(buf+bcnt,sizeof(buf)-bcnt," <%s>", | ||
| 2213 | get_ctrl_typename(cptr->info->type)); | ||
| 2214 | pvr2_trace(PVR2_TRACE_CTL, | ||
| 2215 | "/*--TRACE_COMMIT--*/ %.*s", | ||
| 2216 | bcnt,buf); | ||
| 2217 | } | ||
| 2218 | |||
| 2219 | if (!commit_flag) { | ||
| 2220 | /* Nothing has changed */ | ||
| 2221 | return 0; | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | /* When video standard changes, reset the hres and vres values - | ||
| 2225 | but if the user has pending changes there, then let the changes | ||
| 2226 | take priority. */ | ||
| 2227 | if (hdw->std_dirty) { | ||
| 2228 | /* Rewrite the vertical resolution to be appropriate to the | ||
| 2229 | video standard that has been selected. */ | ||
| 2230 | int nvres; | ||
| 2231 | if (hdw->std_mask_cur & V4L2_STD_525_60) { | ||
| 2232 | nvres = 480; | ||
| 2233 | } else { | ||
| 2234 | nvres = 576; | ||
| 2235 | } | ||
| 2236 | if (nvres != hdw->res_ver_val) { | ||
| 2237 | hdw->res_ver_val = nvres; | ||
| 2238 | hdw->res_ver_dirty = !0; | ||
| 2239 | } | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | if (hdw->std_dirty || | ||
| 2243 | 0) { | ||
| 2244 | /* If any of this changes, then the encoder needs to be | ||
| 2245 | reconfigured, and we need to reset the stream. */ | ||
| 2246 | stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); | ||
| 2247 | stale_subsys_mask |= hdw->subsys_stream_mask; | ||
| 2248 | } | ||
| 2249 | |||
| 2250 | if (hdw->srate_dirty) { | ||
| 2251 | /* Write new sample rate into control structure since | ||
| 2252 | * the master copy is stale. We must track srate | ||
| 2253 | * separate from the mpeg control structure because | ||
| 2254 | * other logic also uses this value. */ | ||
| 2255 | struct v4l2_ext_controls cs; | ||
| 2256 | struct v4l2_ext_control c1; | ||
| 2257 | memset(&cs,0,sizeof(cs)); | ||
| 2258 | memset(&c1,0,sizeof(c1)); | ||
| 2259 | cs.controls = &c1; | ||
| 2260 | cs.count = 1; | ||
| 2261 | c1.id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ; | ||
| 2262 | c1.value = hdw->srate_val; | ||
| 2263 | cx2341x_ext_ctrls(&hdw->enc_ctl_state,&cs,VIDIOC_S_EXT_CTRLS); | ||
| 2264 | } | ||
| 2265 | |||
| 2266 | /* Scan i2c core at this point - before we clear all the dirty | ||
| 2267 | bits. Various parts of the i2c core will notice dirty bits as | ||
| 2268 | appropriate and arrange to broadcast or directly send updates to | ||
| 2269 | the client drivers in order to keep everything in sync */ | ||
| 2270 | pvr2_i2c_core_check_stale(hdw); | ||
| 2271 | |||
| 2272 | for (idx = 0; idx < hdw->control_cnt; idx++) { | ||
| 2273 | cptr = hdw->controls + idx; | ||
| 2274 | if (!cptr->info->clear_dirty) continue; | ||
| 2275 | cptr->info->clear_dirty(cptr); | ||
| 2276 | } | ||
| 2277 | |||
| 2278 | /* Now execute i2c core update */ | ||
| 2279 | pvr2_i2c_core_sync(hdw); | ||
| 2280 | |||
| 2281 | pvr2_hdw_subsys_bit_chg_no_lock(hdw,stale_subsys_mask,0); | ||
| 2282 | pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,saved_subsys_mask); | ||
| 2283 | |||
| 2284 | return 0; | ||
| 2285 | } | ||
| 2286 | |||
| 2287 | |||
| 2288 | int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw) | ||
| 2289 | { | ||
| 2290 | LOCK_TAKE(hdw->big_lock); do { | ||
| 2291 | pvr2_hdw_commit_ctl_internal(hdw); | ||
| 2292 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 2293 | return 0; | ||
| 2294 | } | ||
| 2295 | |||
| 2296 | |||
| 2297 | void pvr2_hdw_poll(struct pvr2_hdw *hdw) | ||
| 2298 | { | ||
| 2299 | LOCK_TAKE(hdw->big_lock); do { | ||
| 2300 | pvr2_i2c_core_sync(hdw); | ||
| 2301 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 2302 | } | ||
| 2303 | |||
| 2304 | |||
| 2305 | void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *hdw, | ||
| 2306 | void (*func)(void *), | ||
| 2307 | void *data) | ||
| 2308 | { | ||
| 2309 | LOCK_TAKE(hdw->big_lock); do { | ||
| 2310 | hdw->poll_trigger_func = func; | ||
| 2311 | hdw->poll_trigger_data = data; | ||
| 2312 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 2313 | } | ||
| 2314 | |||
| 2315 | |||
| 2316 | void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *hdw) | ||
| 2317 | { | ||
| 2318 | if (hdw->poll_trigger_func) { | ||
| 2319 | hdw->poll_trigger_func(hdw->poll_trigger_data); | ||
| 2320 | } | ||
| 2321 | } | ||
| 2322 | |||
| 2323 | |||
| 2324 | void pvr2_hdw_poll_trigger(struct pvr2_hdw *hdw) | ||
| 2325 | { | ||
| 2326 | LOCK_TAKE(hdw->big_lock); do { | ||
| 2327 | pvr2_hdw_poll_trigger_unlocked(hdw); | ||
| 2328 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 2329 | } | ||
| 2330 | |||
| 2331 | |||
| 2332 | /* Return name for this driver instance */ | ||
| 2333 | const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) | ||
| 2334 | { | ||
| 2335 | return hdw->name; | ||
| 2336 | } | ||
| 2337 | |||
| 2338 | |||
| 2339 | /* Return bit mask indicating signal status */ | ||
| 2340 | unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw) | ||
| 2341 | { | ||
| 2342 | unsigned int msk = 0; | ||
| 2343 | switch (hdw->input_val) { | ||
| 2344 | case PVR2_CVAL_INPUT_TV: | ||
| 2345 | case PVR2_CVAL_INPUT_RADIO: | ||
| 2346 | if (hdw->decoder_ctrl && | ||
| 2347 | hdw->decoder_ctrl->tuned(hdw->decoder_ctrl->ctxt)) { | ||
| 2348 | msk |= PVR2_SIGNAL_OK; | ||
| 2349 | if (hdw->audio_stat && | ||
| 2350 | hdw->audio_stat->status(hdw->audio_stat->ctxt)) { | ||
| 2351 | if (hdw->flag_stereo) { | ||
| 2352 | msk |= PVR2_SIGNAL_STEREO; | ||
| 2353 | } | ||
| 2354 | if (hdw->flag_bilingual) { | ||
| 2355 | msk |= PVR2_SIGNAL_SAP; | ||
| 2356 | } | ||
| 2357 | } | ||
| 2358 | } | ||
| 2359 | break; | ||
| 2360 | default: | ||
| 2361 | msk |= PVR2_SIGNAL_OK | PVR2_SIGNAL_STEREO; | ||
| 2362 | } | ||
| 2363 | return msk; | ||
| 2364 | } | ||
| 2365 | |||
| 2366 | |||
| 2367 | int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) | ||
| 2368 | { | ||
| 2369 | int result; | ||
| 2370 | LOCK_TAKE(hdw->ctl_lock); do { | ||
| 2371 | hdw->cmd_buffer[0] = 0x0b; | ||
| 2372 | result = pvr2_send_request(hdw, | ||
| 2373 | hdw->cmd_buffer,1, | ||
| 2374 | hdw->cmd_buffer,1); | ||
| 2375 | if (result < 0) break; | ||
| 2376 | result = (hdw->cmd_buffer[0] != 0); | ||
| 2377 | } while(0); LOCK_GIVE(hdw->ctl_lock); | ||
| 2378 | return result; | ||
| 2379 | } | ||
| 2380 | |||
| 2381 | |||
| 2382 | /* Return bit mask indicating signal status */ | ||
| 2383 | unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *hdw) | ||
| 2384 | { | ||
| 2385 | unsigned int msk = 0; | ||
| 2386 | LOCK_TAKE(hdw->big_lock); do { | ||
| 2387 | msk = pvr2_hdw_get_signal_status_internal(hdw); | ||
| 2388 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 2389 | return msk; | ||
| 2390 | } | ||
| 2391 | |||
| 2392 | |||
| 2393 | /* Get handle to video output stream */ | ||
| 2394 | struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *hp) | ||
| 2395 | { | ||
| 2396 | return hp->vid_stream; | ||
| 2397 | } | ||
| 2398 | |||
| 2399 | |||
| 2400 | void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw) | ||
| 2401 | { | ||
| 2402 | int nr = pvr2_hdw_get_unit_number(hdw); | ||
| 2403 | LOCK_TAKE(hdw->big_lock); do { | ||
| 2404 | hdw->log_requested = !0; | ||
| 2405 | printk(KERN_INFO "pvrusb2: ================= START STATUS CARD #%d =================\n", nr); | ||
| 2406 | pvr2_i2c_core_check_stale(hdw); | ||
| 2407 | hdw->log_requested = 0; | ||
| 2408 | pvr2_i2c_core_sync(hdw); | ||
| 2409 | pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:"); | ||
| 2410 | cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2"); | ||
| 2411 | printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr); | ||
| 2412 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 2413 | } | ||
| 2414 | |||
| 2415 | void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, int enable_flag) | ||
| 2416 | { | ||
| 2417 | int ret; | ||
| 2418 | u16 address; | ||
| 2419 | unsigned int pipe; | ||
| 2420 | LOCK_TAKE(hdw->big_lock); do { | ||
| 2421 | if ((hdw->fw_buffer == 0) == !enable_flag) break; | ||
| 2422 | |||
| 2423 | if (!enable_flag) { | ||
| 2424 | pvr2_trace(PVR2_TRACE_FIRMWARE, | ||
| 2425 | "Cleaning up after CPU firmware fetch"); | ||
| 2426 | kfree(hdw->fw_buffer); | ||
| 2427 | hdw->fw_buffer = 0; | ||
| 2428 | hdw->fw_size = 0; | ||
| 2429 | /* Now release the CPU. It will disconnect and | ||
| 2430 | reconnect later. */ | ||
| 2431 | pvr2_hdw_cpureset_assert(hdw,0); | ||
| 2432 | break; | ||
| 2433 | } | ||
| 2434 | |||
| 2435 | pvr2_trace(PVR2_TRACE_FIRMWARE, | ||
| 2436 | "Preparing to suck out CPU firmware"); | ||
| 2437 | hdw->fw_size = 0x2000; | ||
| 2438 | hdw->fw_buffer = kmalloc(hdw->fw_size,GFP_KERNEL); | ||
| 2439 | if (!hdw->fw_buffer) { | ||
| 2440 | hdw->fw_size = 0; | ||
| 2441 | break; | ||
| 2442 | } | ||
| 2443 | |||
| 2444 | memset(hdw->fw_buffer,0,hdw->fw_size); | ||
| 2445 | |||
| 2446 | /* We have to hold the CPU during firmware upload. */ | ||
| 2447 | pvr2_hdw_cpureset_assert(hdw,1); | ||
| 2448 | |||
| 2449 | /* download the firmware from address 0000-1fff in 2048 | ||
| 2450 | (=0x800) bytes chunk. */ | ||
| 2451 | |||
| 2452 | pvr2_trace(PVR2_TRACE_FIRMWARE,"Grabbing CPU firmware"); | ||
| 2453 | pipe = usb_rcvctrlpipe(hdw->usb_dev, 0); | ||
| 2454 | for(address = 0; address < hdw->fw_size; address += 0x800) { | ||
| 2455 | ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0xc0, | ||
| 2456 | address,0, | ||
| 2457 | hdw->fw_buffer+address,0x800,HZ); | ||
| 2458 | if (ret < 0) break; | ||
| 2459 | } | ||
| 2460 | |||
| 2461 | pvr2_trace(PVR2_TRACE_FIRMWARE,"Done grabbing CPU firmware"); | ||
| 2462 | |||
| 2463 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 2464 | } | ||
| 2465 | |||
| 2466 | |||
| 2467 | /* Return true if we're in a mode for retrieval CPU firmware */ | ||
| 2468 | int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *hdw) | ||
| 2469 | { | ||
| 2470 | return hdw->fw_buffer != 0; | ||
| 2471 | } | ||
| 2472 | |||
| 2473 | |||
| 2474 | int pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs, | ||
| 2475 | char *buf,unsigned int cnt) | ||
| 2476 | { | ||
| 2477 | int ret = -EINVAL; | ||
| 2478 | LOCK_TAKE(hdw->big_lock); do { | ||
| 2479 | if (!buf) break; | ||
| 2480 | if (!cnt) break; | ||
| 2481 | |||
| 2482 | if (!hdw->fw_buffer) { | ||
| 2483 | ret = -EIO; | ||
| 2484 | break; | ||
| 2485 | } | ||
| 2486 | |||
| 2487 | if (offs >= hdw->fw_size) { | ||
| 2488 | pvr2_trace(PVR2_TRACE_FIRMWARE, | ||
| 2489 | "Read firmware data offs=%d EOF", | ||
| 2490 | offs); | ||
| 2491 | ret = 0; | ||
| 2492 | break; | ||
| 2493 | } | ||
| 2494 | |||
| 2495 | if (offs + cnt > hdw->fw_size) cnt = hdw->fw_size - offs; | ||
| 2496 | |||
| 2497 | memcpy(buf,hdw->fw_buffer+offs,cnt); | ||
| 2498 | |||
| 2499 | pvr2_trace(PVR2_TRACE_FIRMWARE, | ||
| 2500 | "Read firmware data offs=%d cnt=%d", | ||
| 2501 | offs,cnt); | ||
| 2502 | ret = cnt; | ||
| 2503 | } while (0); LOCK_GIVE(hdw->big_lock); | ||
| 2504 | |||
| 2505 | return ret; | ||
| 2506 | } | ||
| 2507 | |||
| 2508 | |||
| 2509 | int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw) | ||
| 2510 | { | ||
| 2511 | return hdw->v4l_minor_number; | ||
| 2512 | } | ||
| 2513 | |||
| 2514 | |||
| 2515 | /* Store the v4l minor device number */ | ||
| 2516 | void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,int v) | ||
| 2517 | { | ||
| 2518 | hdw->v4l_minor_number = v; | ||
| 2519 | } | ||
| 2520 | |||
| 2521 | |||
| 2522 | void pvr2_reset_ctl_endpoints(struct pvr2_hdw *hdw) | ||
| 2523 | { | ||
| 2524 | if (!hdw->usb_dev) return; | ||
| 2525 | usb_settoggle(hdw->usb_dev, PVR2_CTL_WRITE_ENDPOINT & 0xf, | ||
| 2526 | !(PVR2_CTL_WRITE_ENDPOINT & USB_DIR_IN), 0); | ||
| 2527 | usb_settoggle(hdw->usb_dev, PVR2_CTL_READ_ENDPOINT & 0xf, | ||
| 2528 | !(PVR2_CTL_READ_ENDPOINT & USB_DIR_IN), 0); | ||
| 2529 | usb_clear_halt(hdw->usb_dev, | ||
| 2530 | usb_rcvbulkpipe(hdw->usb_dev, | ||
| 2531 | PVR2_CTL_READ_ENDPOINT & 0x7f)); | ||
| 2532 | usb_clear_halt(hdw->usb_dev, | ||
| 2533 | usb_sndbulkpipe(hdw->usb_dev, | ||
| 2534 | PVR2_CTL_WRITE_ENDPOINT & 0x7f)); | ||
| 2535 | } | ||
| 2536 | |||
| 2537 | |||
| 2538 | static void pvr2_ctl_write_complete(struct urb *urb, struct pt_regs *regs) | ||
| 2539 | { | ||
| 2540 | struct pvr2_hdw *hdw = urb->context; | ||
| 2541 | hdw->ctl_write_pend_flag = 0; | ||
| 2542 | if (hdw->ctl_read_pend_flag) return; | ||
| 2543 | complete(&hdw->ctl_done); | ||
| 2544 | } | ||
| 2545 | |||
| 2546 | |||
| 2547 | static void pvr2_ctl_read_complete(struct urb *urb, struct pt_regs *regs) | ||
| 2548 | { | ||
| 2549 | struct pvr2_hdw *hdw = urb->context; | ||
| 2550 | hdw->ctl_read_pend_flag = 0; | ||
| 2551 | if (hdw->ctl_write_pend_flag) return; | ||
| 2552 | complete(&hdw->ctl_done); | ||
| 2553 | } | ||
| 2554 | |||
| 2555 | |||
| 2556 | static void pvr2_ctl_timeout(unsigned long data) | ||
| 2557 | { | ||
| 2558 | struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; | ||
| 2559 | if (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) { | ||
| 2560 | hdw->ctl_timeout_flag = !0; | ||
| 2561 | if (hdw->ctl_write_pend_flag && hdw->ctl_write_urb) { | ||
| 2562 | usb_unlink_urb(hdw->ctl_write_urb); | ||
| 2563 | } | ||
| 2564 | if (hdw->ctl_read_pend_flag && hdw->ctl_read_urb) { | ||
| 2565 | usb_unlink_urb(hdw->ctl_read_urb); | ||
| 2566 | } | ||
| 2567 | } | ||
| 2568 | } | ||
| 2569 | |||
| 2570 | |||
| 2571 | int pvr2_send_request_ex(struct pvr2_hdw *hdw, | ||
| 2572 | unsigned int timeout,int probe_fl, | ||
| 2573 | void *write_data,unsigned int write_len, | ||
| 2574 | void *read_data,unsigned int read_len) | ||
| 2575 | { | ||
| 2576 | unsigned int idx; | ||
| 2577 | int status = 0; | ||
| 2578 | struct timer_list timer; | ||
| 2579 | if (!hdw->ctl_lock_held) { | ||
| 2580 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2581 | "Attempted to execute control transfer" | ||
| 2582 | " without lock!!"); | ||
| 2583 | return -EDEADLK; | ||
| 2584 | } | ||
| 2585 | if ((!hdw->flag_ok) && !probe_fl) { | ||
| 2586 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2587 | "Attempted to execute control transfer" | ||
| 2588 | " when device not ok"); | ||
| 2589 | return -EIO; | ||
| 2590 | } | ||
| 2591 | if (!(hdw->ctl_read_urb && hdw->ctl_write_urb)) { | ||
| 2592 | if (!probe_fl) { | ||
| 2593 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2594 | "Attempted to execute control transfer" | ||
| 2595 | " when USB is disconnected"); | ||
| 2596 | } | ||
| 2597 | return -ENOTTY; | ||
| 2598 | } | ||
| 2599 | |||
| 2600 | /* Ensure that we have sane parameters */ | ||
| 2601 | if (!write_data) write_len = 0; | ||
| 2602 | if (!read_data) read_len = 0; | ||
| 2603 | if (write_len > PVR2_CTL_BUFFSIZE) { | ||
| 2604 | pvr2_trace( | ||
| 2605 | PVR2_TRACE_ERROR_LEGS, | ||
| 2606 | "Attempted to execute %d byte" | ||
| 2607 | " control-write transfer (limit=%d)", | ||
| 2608 | write_len,PVR2_CTL_BUFFSIZE); | ||
| 2609 | return -EINVAL; | ||
| 2610 | } | ||
| 2611 | if (read_len > PVR2_CTL_BUFFSIZE) { | ||
| 2612 | pvr2_trace( | ||
| 2613 | PVR2_TRACE_ERROR_LEGS, | ||
| 2614 | "Attempted to execute %d byte" | ||
| 2615 | " control-read transfer (limit=%d)", | ||
| 2616 | write_len,PVR2_CTL_BUFFSIZE); | ||
| 2617 | return -EINVAL; | ||
| 2618 | } | ||
| 2619 | if ((!write_len) && (!read_len)) { | ||
| 2620 | pvr2_trace( | ||
| 2621 | PVR2_TRACE_ERROR_LEGS, | ||
| 2622 | "Attempted to execute null control transfer?"); | ||
| 2623 | return -EINVAL; | ||
| 2624 | } | ||
| 2625 | |||
| 2626 | |||
| 2627 | hdw->cmd_debug_state = 1; | ||
| 2628 | if (write_len) { | ||
| 2629 | hdw->cmd_debug_code = ((unsigned char *)write_data)[0]; | ||
| 2630 | } else { | ||
| 2631 | hdw->cmd_debug_code = 0; | ||
| 2632 | } | ||
| 2633 | hdw->cmd_debug_write_len = write_len; | ||
| 2634 | hdw->cmd_debug_read_len = read_len; | ||
| 2635 | |||
| 2636 | /* Initialize common stuff */ | ||
| 2637 | init_completion(&hdw->ctl_done); | ||
| 2638 | hdw->ctl_timeout_flag = 0; | ||
| 2639 | hdw->ctl_write_pend_flag = 0; | ||
| 2640 | hdw->ctl_read_pend_flag = 0; | ||
| 2641 | init_timer(&timer); | ||
| 2642 | timer.expires = jiffies + timeout; | ||
| 2643 | timer.data = (unsigned long)hdw; | ||
| 2644 | timer.function = pvr2_ctl_timeout; | ||
| 2645 | |||
| 2646 | if (write_len) { | ||
| 2647 | hdw->cmd_debug_state = 2; | ||
| 2648 | /* Transfer write data to internal buffer */ | ||
| 2649 | for (idx = 0; idx < write_len; idx++) { | ||
| 2650 | hdw->ctl_write_buffer[idx] = | ||
| 2651 | ((unsigned char *)write_data)[idx]; | ||
| 2652 | } | ||
| 2653 | /* Initiate a write request */ | ||
| 2654 | usb_fill_bulk_urb(hdw->ctl_write_urb, | ||
| 2655 | hdw->usb_dev, | ||
| 2656 | usb_sndbulkpipe(hdw->usb_dev, | ||
| 2657 | PVR2_CTL_WRITE_ENDPOINT), | ||
| 2658 | hdw->ctl_write_buffer, | ||
| 2659 | write_len, | ||
| 2660 | pvr2_ctl_write_complete, | ||
| 2661 | hdw); | ||
| 2662 | hdw->ctl_write_urb->actual_length = 0; | ||
| 2663 | hdw->ctl_write_pend_flag = !0; | ||
| 2664 | status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL); | ||
| 2665 | if (status < 0) { | ||
| 2666 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2667 | "Failed to submit write-control" | ||
| 2668 | " URB status=%d",status); | ||
| 2669 | hdw->ctl_write_pend_flag = 0; | ||
| 2670 | goto done; | ||
| 2671 | } | ||
| 2672 | } | ||
| 2673 | |||
| 2674 | if (read_len) { | ||
| 2675 | hdw->cmd_debug_state = 3; | ||
| 2676 | memset(hdw->ctl_read_buffer,0x43,read_len); | ||
| 2677 | /* Initiate a read request */ | ||
| 2678 | usb_fill_bulk_urb(hdw->ctl_read_urb, | ||
| 2679 | hdw->usb_dev, | ||
| 2680 | usb_rcvbulkpipe(hdw->usb_dev, | ||
| 2681 | PVR2_CTL_READ_ENDPOINT), | ||
| 2682 | hdw->ctl_read_buffer, | ||
| 2683 | read_len, | ||
| 2684 | pvr2_ctl_read_complete, | ||
| 2685 | hdw); | ||
| 2686 | hdw->ctl_read_urb->actual_length = 0; | ||
| 2687 | hdw->ctl_read_pend_flag = !0; | ||
| 2688 | status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL); | ||
| 2689 | if (status < 0) { | ||
| 2690 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2691 | "Failed to submit read-control" | ||
| 2692 | " URB status=%d",status); | ||
| 2693 | hdw->ctl_read_pend_flag = 0; | ||
| 2694 | goto done; | ||
| 2695 | } | ||
| 2696 | } | ||
| 2697 | |||
| 2698 | /* Start timer */ | ||
| 2699 | add_timer(&timer); | ||
| 2700 | |||
| 2701 | /* Now wait for all I/O to complete */ | ||
| 2702 | hdw->cmd_debug_state = 4; | ||
| 2703 | while (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) { | ||
| 2704 | wait_for_completion(&hdw->ctl_done); | ||
| 2705 | } | ||
| 2706 | hdw->cmd_debug_state = 5; | ||
| 2707 | |||
| 2708 | /* Stop timer */ | ||
| 2709 | del_timer_sync(&timer); | ||
| 2710 | |||
| 2711 | hdw->cmd_debug_state = 6; | ||
| 2712 | status = 0; | ||
| 2713 | |||
| 2714 | if (hdw->ctl_timeout_flag) { | ||
| 2715 | status = -ETIMEDOUT; | ||
| 2716 | if (!probe_fl) { | ||
| 2717 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2718 | "Timed out control-write"); | ||
| 2719 | } | ||
| 2720 | goto done; | ||
| 2721 | } | ||
| 2722 | |||
| 2723 | if (write_len) { | ||
| 2724 | /* Validate results of write request */ | ||
| 2725 | if ((hdw->ctl_write_urb->status != 0) && | ||
| 2726 | (hdw->ctl_write_urb->status != -ENOENT) && | ||
| 2727 | (hdw->ctl_write_urb->status != -ESHUTDOWN) && | ||
| 2728 | (hdw->ctl_write_urb->status != -ECONNRESET)) { | ||
| 2729 | /* USB subsystem is reporting some kind of failure | ||
| 2730 | on the write */ | ||
| 2731 | status = hdw->ctl_write_urb->status; | ||
| 2732 | if (!probe_fl) { | ||
| 2733 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2734 | "control-write URB failure," | ||
| 2735 | " status=%d", | ||
| 2736 | status); | ||
| 2737 | } | ||
| 2738 | goto done; | ||
| 2739 | } | ||
| 2740 | if (hdw->ctl_write_urb->actual_length < write_len) { | ||
| 2741 | /* Failed to write enough data */ | ||
| 2742 | status = -EIO; | ||
| 2743 | if (!probe_fl) { | ||
| 2744 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2745 | "control-write URB short," | ||
| 2746 | " expected=%d got=%d", | ||
| 2747 | write_len, | ||
| 2748 | hdw->ctl_write_urb->actual_length); | ||
| 2749 | } | ||
| 2750 | goto done; | ||
| 2751 | } | ||
| 2752 | } | ||
| 2753 | if (read_len) { | ||
| 2754 | /* Validate results of read request */ | ||
| 2755 | if ((hdw->ctl_read_urb->status != 0) && | ||
| 2756 | (hdw->ctl_read_urb->status != -ENOENT) && | ||
| 2757 | (hdw->ctl_read_urb->status != -ESHUTDOWN) && | ||
| 2758 | (hdw->ctl_read_urb->status != -ECONNRESET)) { | ||
| 2759 | /* USB subsystem is reporting some kind of failure | ||
| 2760 | on the read */ | ||
| 2761 | status = hdw->ctl_read_urb->status; | ||
| 2762 | if (!probe_fl) { | ||
| 2763 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2764 | "control-read URB failure," | ||
| 2765 | " status=%d", | ||
| 2766 | status); | ||
| 2767 | } | ||
| 2768 | goto done; | ||
| 2769 | } | ||
| 2770 | if (hdw->ctl_read_urb->actual_length < read_len) { | ||
| 2771 | /* Failed to read enough data */ | ||
| 2772 | status = -EIO; | ||
| 2773 | if (!probe_fl) { | ||
| 2774 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2775 | "control-read URB short," | ||
| 2776 | " expected=%d got=%d", | ||
| 2777 | read_len, | ||
| 2778 | hdw->ctl_read_urb->actual_length); | ||
| 2779 | } | ||
| 2780 | goto done; | ||
| 2781 | } | ||
| 2782 | /* Transfer retrieved data out from internal buffer */ | ||
| 2783 | for (idx = 0; idx < read_len; idx++) { | ||
| 2784 | ((unsigned char *)read_data)[idx] = | ||
| 2785 | hdw->ctl_read_buffer[idx]; | ||
| 2786 | } | ||
| 2787 | } | ||
| 2788 | |||
| 2789 | done: | ||
| 2790 | |||
| 2791 | hdw->cmd_debug_state = 0; | ||
| 2792 | if ((status < 0) && (!probe_fl)) { | ||
| 2793 | pvr2_hdw_render_useless_unlocked(hdw); | ||
| 2794 | } | ||
| 2795 | return status; | ||
| 2796 | } | ||
| 2797 | |||
| 2798 | |||
| 2799 | int pvr2_send_request(struct pvr2_hdw *hdw, | ||
| 2800 | void *write_data,unsigned int write_len, | ||
| 2801 | void *read_data,unsigned int read_len) | ||
| 2802 | { | ||
| 2803 | return pvr2_send_request_ex(hdw,HZ*4,0, | ||
| 2804 | write_data,write_len, | ||
| 2805 | read_data,read_len); | ||
| 2806 | } | ||
| 2807 | |||
| 2808 | int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data) | ||
| 2809 | { | ||
| 2810 | int ret; | ||
| 2811 | |||
| 2812 | LOCK_TAKE(hdw->ctl_lock); | ||
| 2813 | |||
| 2814 | hdw->cmd_buffer[0] = 0x04; /* write register prefix */ | ||
| 2815 | PVR2_DECOMPOSE_LE(hdw->cmd_buffer,1,data); | ||
| 2816 | hdw->cmd_buffer[5] = 0; | ||
| 2817 | hdw->cmd_buffer[6] = (reg >> 8) & 0xff; | ||
| 2818 | hdw->cmd_buffer[7] = reg & 0xff; | ||
| 2819 | |||
| 2820 | |||
| 2821 | ret = pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 0); | ||
| 2822 | |||
| 2823 | LOCK_GIVE(hdw->ctl_lock); | ||
| 2824 | |||
| 2825 | return ret; | ||
| 2826 | } | ||
| 2827 | |||
| 2828 | |||
| 2829 | int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data) | ||
| 2830 | { | ||
| 2831 | int ret = 0; | ||
| 2832 | |||
| 2833 | LOCK_TAKE(hdw->ctl_lock); | ||
| 2834 | |||
| 2835 | hdw->cmd_buffer[0] = 0x05; /* read register prefix */ | ||
| 2836 | hdw->cmd_buffer[1] = 0; | ||
| 2837 | hdw->cmd_buffer[2] = 0; | ||
| 2838 | hdw->cmd_buffer[3] = 0; | ||
| 2839 | hdw->cmd_buffer[4] = 0; | ||
| 2840 | hdw->cmd_buffer[5] = 0; | ||
| 2841 | hdw->cmd_buffer[6] = (reg >> 8) & 0xff; | ||
| 2842 | hdw->cmd_buffer[7] = reg & 0xff; | ||
| 2843 | |||
| 2844 | ret |= pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 4); | ||
| 2845 | *data = PVR2_COMPOSE_LE(hdw->cmd_buffer,0); | ||
| 2846 | |||
| 2847 | LOCK_GIVE(hdw->ctl_lock); | ||
| 2848 | |||
| 2849 | return ret; | ||
| 2850 | } | ||
| 2851 | |||
| 2852 | |||
| 2853 | int pvr2_write_u16(struct pvr2_hdw *hdw, u16 data, int res) | ||
| 2854 | { | ||
| 2855 | int ret; | ||
| 2856 | |||
| 2857 | LOCK_TAKE(hdw->ctl_lock); | ||
| 2858 | |||
| 2859 | hdw->cmd_buffer[0] = (data >> 8) & 0xff; | ||
| 2860 | hdw->cmd_buffer[1] = data & 0xff; | ||
| 2861 | |||
| 2862 | ret = pvr2_send_request(hdw, hdw->cmd_buffer, 2, hdw->cmd_buffer, res); | ||
| 2863 | |||
| 2864 | LOCK_GIVE(hdw->ctl_lock); | ||
| 2865 | |||
| 2866 | return ret; | ||
| 2867 | } | ||
| 2868 | |||
| 2869 | |||
| 2870 | int pvr2_write_u8(struct pvr2_hdw *hdw, u8 data, int res) | ||
| 2871 | { | ||
| 2872 | int ret; | ||
| 2873 | |||
| 2874 | LOCK_TAKE(hdw->ctl_lock); | ||
| 2875 | |||
| 2876 | hdw->cmd_buffer[0] = data; | ||
| 2877 | |||
| 2878 | ret = pvr2_send_request(hdw, hdw->cmd_buffer, 1, hdw->cmd_buffer, res); | ||
| 2879 | |||
| 2880 | LOCK_GIVE(hdw->ctl_lock); | ||
| 2881 | |||
| 2882 | return ret; | ||
| 2883 | } | ||
| 2884 | |||
| 2885 | |||
| 2886 | void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw) | ||
| 2887 | { | ||
| 2888 | if (!hdw->flag_ok) return; | ||
| 2889 | pvr2_trace(PVR2_TRACE_INIT,"render_useless"); | ||
| 2890 | hdw->flag_ok = 0; | ||
| 2891 | if (hdw->vid_stream) { | ||
| 2892 | pvr2_stream_setup(hdw->vid_stream,0,0,0); | ||
| 2893 | } | ||
| 2894 | hdw->flag_streaming_enabled = 0; | ||
| 2895 | hdw->subsys_enabled_mask = 0; | ||
| 2896 | } | ||
| 2897 | |||
| 2898 | |||
| 2899 | void pvr2_hdw_render_useless(struct pvr2_hdw *hdw) | ||
| 2900 | { | ||
| 2901 | LOCK_TAKE(hdw->ctl_lock); | ||
| 2902 | pvr2_hdw_render_useless_unlocked(hdw); | ||
| 2903 | LOCK_GIVE(hdw->ctl_lock); | ||
| 2904 | } | ||
| 2905 | |||
| 2906 | |||
| 2907 | void pvr2_hdw_device_reset(struct pvr2_hdw *hdw) | ||
| 2908 | { | ||
| 2909 | int ret; | ||
| 2910 | pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset..."); | ||
| 2911 | ret = usb_lock_device_for_reset(hdw->usb_dev,0); | ||
| 2912 | if (ret == 1) { | ||
| 2913 | ret = usb_reset_device(hdw->usb_dev); | ||
| 2914 | usb_unlock_device(hdw->usb_dev); | ||
| 2915 | } else { | ||
| 2916 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2917 | "Failed to lock USB device ret=%d",ret); | ||
| 2918 | } | ||
| 2919 | if (init_pause_msec) { | ||
| 2920 | pvr2_trace(PVR2_TRACE_INFO, | ||
| 2921 | "Waiting %u msec for hardware to settle", | ||
| 2922 | init_pause_msec); | ||
| 2923 | msleep(init_pause_msec); | ||
| 2924 | } | ||
| 2925 | |||
| 2926 | } | ||
| 2927 | |||
| 2928 | |||
| 2929 | void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val) | ||
| 2930 | { | ||
| 2931 | char da[1]; | ||
| 2932 | unsigned int pipe; | ||
| 2933 | int ret; | ||
| 2934 | |||
| 2935 | if (!hdw->usb_dev) return; | ||
| 2936 | |||
| 2937 | pvr2_trace(PVR2_TRACE_INIT,"cpureset_assert(%d)",val); | ||
| 2938 | |||
| 2939 | da[0] = val ? 0x01 : 0x00; | ||
| 2940 | |||
| 2941 | /* Write the CPUCS register on the 8051. The lsb of the register | ||
| 2942 | is the reset bit; a 1 asserts reset while a 0 clears it. */ | ||
| 2943 | pipe = usb_sndctrlpipe(hdw->usb_dev, 0); | ||
| 2944 | ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0x40,0xe600,0,da,1,HZ); | ||
| 2945 | if (ret < 0) { | ||
| 2946 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 2947 | "cpureset_assert(%d) error=%d",val,ret); | ||
| 2948 | pvr2_hdw_render_useless(hdw); | ||
| 2949 | } | ||
| 2950 | } | ||
| 2951 | |||
| 2952 | |||
| 2953 | int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) | ||
| 2954 | { | ||
| 2955 | int status; | ||
| 2956 | LOCK_TAKE(hdw->ctl_lock); do { | ||
| 2957 | pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset"); | ||
| 2958 | hdw->flag_ok = !0; | ||
| 2959 | hdw->cmd_buffer[0] = 0xdd; | ||
| 2960 | status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0); | ||
| 2961 | } while (0); LOCK_GIVE(hdw->ctl_lock); | ||
| 2962 | return status; | ||
| 2963 | } | ||
| 2964 | |||
| 2965 | |||
| 2966 | int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) | ||
| 2967 | { | ||
| 2968 | int status; | ||
| 2969 | LOCK_TAKE(hdw->ctl_lock); do { | ||
| 2970 | pvr2_trace(PVR2_TRACE_INIT,"Requesting powerup"); | ||
| 2971 | hdw->cmd_buffer[0] = 0xde; | ||
| 2972 | status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0); | ||
| 2973 | } while (0); LOCK_GIVE(hdw->ctl_lock); | ||
| 2974 | return status; | ||
| 2975 | } | ||
| 2976 | |||
| 2977 | |||
| 2978 | int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) | ||
| 2979 | { | ||
| 2980 | if (!hdw->decoder_ctrl) { | ||
| 2981 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 2982 | "Unable to reset decoder: nothing attached"); | ||
| 2983 | return -ENOTTY; | ||
| 2984 | } | ||
| 2985 | |||
| 2986 | if (!hdw->decoder_ctrl->force_reset) { | ||
| 2987 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 2988 | "Unable to reset decoder: not implemented"); | ||
| 2989 | return -ENOTTY; | ||
| 2990 | } | ||
| 2991 | |||
| 2992 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 2993 | "Requesting decoder reset"); | ||
| 2994 | hdw->decoder_ctrl->force_reset(hdw->decoder_ctrl->ctxt); | ||
| 2995 | return 0; | ||
| 2996 | } | ||
| 2997 | |||
| 2998 | |||
| 2999 | int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) | ||
| 3000 | { | ||
| 3001 | int status; | ||
| 3002 | LOCK_TAKE(hdw->ctl_lock); do { | ||
| 3003 | hdw->cmd_buffer[0] = (runFl ? 0x36 : 0x37); | ||
| 3004 | status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0); | ||
| 3005 | } while (0); LOCK_GIVE(hdw->ctl_lock); | ||
| 3006 | if (!status) { | ||
| 3007 | hdw->subsys_enabled_mask = | ||
| 3008 | ((hdw->subsys_enabled_mask & | ||
| 3009 | ~(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) | | ||
| 3010 | (runFl ? (1<<PVR2_SUBSYS_B_USBSTREAM_RUN) : 0)); | ||
| 3011 | } | ||
| 3012 | return status; | ||
| 3013 | } | ||
| 3014 | |||
| 3015 | |||
| 3016 | void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw, | ||
| 3017 | struct pvr2_hdw_debug_info *ptr) | ||
| 3018 | { | ||
| 3019 | ptr->big_lock_held = hdw->big_lock_held; | ||
| 3020 | ptr->ctl_lock_held = hdw->ctl_lock_held; | ||
| 3021 | ptr->flag_ok = hdw->flag_ok; | ||
| 3022 | ptr->flag_disconnected = hdw->flag_disconnected; | ||
| 3023 | ptr->flag_init_ok = hdw->flag_init_ok; | ||
| 3024 | ptr->flag_streaming_enabled = hdw->flag_streaming_enabled; | ||
| 3025 | ptr->subsys_flags = hdw->subsys_enabled_mask; | ||
| 3026 | ptr->cmd_debug_state = hdw->cmd_debug_state; | ||
| 3027 | ptr->cmd_code = hdw->cmd_debug_code; | ||
| 3028 | ptr->cmd_debug_write_len = hdw->cmd_debug_write_len; | ||
| 3029 | ptr->cmd_debug_read_len = hdw->cmd_debug_read_len; | ||
| 3030 | ptr->cmd_debug_timeout = hdw->ctl_timeout_flag; | ||
| 3031 | ptr->cmd_debug_write_pend = hdw->ctl_write_pend_flag; | ||
| 3032 | ptr->cmd_debug_read_pend = hdw->ctl_read_pend_flag; | ||
| 3033 | ptr->cmd_debug_rstatus = hdw->ctl_read_urb->status; | ||
| 3034 | ptr->cmd_debug_wstatus = hdw->ctl_read_urb->status; | ||
| 3035 | } | ||
| 3036 | |||
| 3037 | |||
| 3038 | int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp) | ||
| 3039 | { | ||
| 3040 | return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp); | ||
| 3041 | } | ||
| 3042 | |||
| 3043 | |||
| 3044 | int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *dp) | ||
| 3045 | { | ||
| 3046 | return pvr2_read_register(hdw,PVR2_GPIO_OUT,dp); | ||
| 3047 | } | ||
| 3048 | |||
| 3049 | |||
| 3050 | int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *dp) | ||
| 3051 | { | ||
| 3052 | return pvr2_read_register(hdw,PVR2_GPIO_IN,dp); | ||
| 3053 | } | ||
| 3054 | |||
| 3055 | |||
| 3056 | int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val) | ||
| 3057 | { | ||
| 3058 | u32 cval,nval; | ||
| 3059 | int ret; | ||
| 3060 | if (~msk) { | ||
| 3061 | ret = pvr2_read_register(hdw,PVR2_GPIO_DIR,&cval); | ||
| 3062 | if (ret) return ret; | ||
| 3063 | nval = (cval & ~msk) | (val & msk); | ||
| 3064 | pvr2_trace(PVR2_TRACE_GPIO, | ||
| 3065 | "GPIO direction changing 0x%x:0x%x" | ||
| 3066 | " from 0x%x to 0x%x", | ||
| 3067 | msk,val,cval,nval); | ||
| 3068 | } else { | ||
| 3069 | nval = val; | ||
| 3070 | pvr2_trace(PVR2_TRACE_GPIO, | ||
| 3071 | "GPIO direction changing to 0x%x",nval); | ||
| 3072 | } | ||
| 3073 | return pvr2_write_register(hdw,PVR2_GPIO_DIR,nval); | ||
| 3074 | } | ||
| 3075 | |||
| 3076 | |||
| 3077 | int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val) | ||
| 3078 | { | ||
| 3079 | u32 cval,nval; | ||
| 3080 | int ret; | ||
| 3081 | if (~msk) { | ||
| 3082 | ret = pvr2_read_register(hdw,PVR2_GPIO_OUT,&cval); | ||
| 3083 | if (ret) return ret; | ||
| 3084 | nval = (cval & ~msk) | (val & msk); | ||
| 3085 | pvr2_trace(PVR2_TRACE_GPIO, | ||
| 3086 | "GPIO output changing 0x%x:0x%x from 0x%x to 0x%x", | ||
| 3087 | msk,val,cval,nval); | ||
| 3088 | } else { | ||
| 3089 | nval = val; | ||
| 3090 | pvr2_trace(PVR2_TRACE_GPIO, | ||
| 3091 | "GPIO output changing to 0x%x",nval); | ||
| 3092 | } | ||
| 3093 | return pvr2_write_register(hdw,PVR2_GPIO_OUT,nval); | ||
| 3094 | } | ||
| 3095 | |||
| 3096 | |||
| 3097 | int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) | ||
| 3098 | { | ||
| 3099 | int result; | ||
| 3100 | LOCK_TAKE(hdw->ctl_lock); do { | ||
| 3101 | hdw->cmd_buffer[0] = 0xeb; | ||
| 3102 | result = pvr2_send_request(hdw, | ||
| 3103 | hdw->cmd_buffer,1, | ||
| 3104 | hdw->cmd_buffer,1); | ||
| 3105 | if (result < 0) break; | ||
| 3106 | result = hdw->cmd_buffer[0]; | ||
| 3107 | } while(0); LOCK_GIVE(hdw->ctl_lock); | ||
| 3108 | return result; | ||
| 3109 | } | ||
| 3110 | |||
| 3111 | |||
| 3112 | /* | ||
| 3113 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 3114 | *** Local Variables: *** | ||
| 3115 | *** mode: c *** | ||
| 3116 | *** fill-column: 75 *** | ||
| 3117 | *** tab-width: 8 *** | ||
| 3118 | *** c-basic-offset: 8 *** | ||
| 3119 | *** End: *** | ||
| 3120 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h new file mode 100644 index 000000000000..63f529154431 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h | |||
| @@ -0,0 +1,335 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_HDW_H | ||
| 22 | #define __PVRUSB2_HDW_H | ||
| 23 | |||
| 24 | #include <linux/usb.h> | ||
| 25 | #include <linux/videodev2.h> | ||
| 26 | #include "pvrusb2-io.h" | ||
| 27 | #include "pvrusb2-ctrl.h" | ||
| 28 | |||
| 29 | |||
| 30 | /* Private internal control ids, look these up with | ||
| 31 | pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */ | ||
| 32 | #define PVR2_CID_STDENUM 1 | ||
| 33 | #define PVR2_CID_STDCUR 2 | ||
| 34 | #define PVR2_CID_STDAVAIL 3 | ||
| 35 | #define PVR2_CID_INPUT 4 | ||
| 36 | #define PVR2_CID_AUDIOMODE 5 | ||
| 37 | #define PVR2_CID_FREQUENCY 6 | ||
| 38 | #define PVR2_CID_HRES 7 | ||
| 39 | #define PVR2_CID_VRES 8 | ||
| 40 | |||
| 41 | /* Legal values for the INPUT state variable */ | ||
| 42 | #define PVR2_CVAL_INPUT_TV 0 | ||
| 43 | #define PVR2_CVAL_INPUT_SVIDEO 1 | ||
| 44 | #define PVR2_CVAL_INPUT_COMPOSITE 2 | ||
| 45 | #define PVR2_CVAL_INPUT_RADIO 3 | ||
| 46 | |||
| 47 | /* Values that pvr2_hdw_get_signal_status() returns */ | ||
| 48 | #define PVR2_SIGNAL_OK 0x0001 | ||
| 49 | #define PVR2_SIGNAL_STEREO 0x0002 | ||
| 50 | #define PVR2_SIGNAL_SAP 0x0004 | ||
| 51 | |||
| 52 | |||
| 53 | /* Subsystem definitions - these are various pieces that can be | ||
| 54 | independently stopped / started. Usually you don't want to mess with | ||
| 55 | this directly (let the driver handle things itself), but it is useful | ||
| 56 | for debugging. */ | ||
| 57 | #define PVR2_SUBSYS_B_ENC_FIRMWARE 0 | ||
| 58 | #define PVR2_SUBSYS_B_ENC_CFG 1 | ||
| 59 | #define PVR2_SUBSYS_B_DIGITIZER_RUN 2 | ||
| 60 | #define PVR2_SUBSYS_B_USBSTREAM_RUN 3 | ||
| 61 | #define PVR2_SUBSYS_B_ENC_RUN 4 | ||
| 62 | |||
| 63 | #define PVR2_SUBSYS_CFG_ALL ( \ | ||
| 64 | (1 << PVR2_SUBSYS_B_ENC_FIRMWARE) | \ | ||
| 65 | (1 << PVR2_SUBSYS_B_ENC_CFG) ) | ||
| 66 | #define PVR2_SUBSYS_RUN_ALL ( \ | ||
| 67 | (1 << PVR2_SUBSYS_B_DIGITIZER_RUN) | \ | ||
| 68 | (1 << PVR2_SUBSYS_B_USBSTREAM_RUN) | \ | ||
| 69 | (1 << PVR2_SUBSYS_B_ENC_RUN) ) | ||
| 70 | #define PVR2_SUBSYS_ALL ( \ | ||
| 71 | PVR2_SUBSYS_CFG_ALL | \ | ||
| 72 | PVR2_SUBSYS_RUN_ALL ) | ||
| 73 | |||
| 74 | enum pvr2_config { | ||
| 75 | pvr2_config_empty, | ||
| 76 | pvr2_config_mpeg, | ||
| 77 | pvr2_config_vbi, | ||
| 78 | pvr2_config_radio, | ||
| 79 | }; | ||
| 80 | |||
| 81 | const char *pvr2_config_get_name(enum pvr2_config); | ||
| 82 | |||
| 83 | struct pvr2_hdw; | ||
| 84 | |||
| 85 | /* Create and return a structure for interacting with the underlying | ||
| 86 | hardware */ | ||
| 87 | struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, | ||
| 88 | const struct usb_device_id *devid); | ||
| 89 | |||
| 90 | /* Poll for background activity (if any) */ | ||
| 91 | void pvr2_hdw_poll(struct pvr2_hdw *); | ||
| 92 | |||
| 93 | /* Trigger a poll to take place later at a convenient time */ | ||
| 94 | void pvr2_hdw_poll_trigger(struct pvr2_hdw *); | ||
| 95 | void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *); | ||
| 96 | |||
| 97 | /* Register a callback used to trigger a future poll */ | ||
| 98 | void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *, | ||
| 99 | void (*func)(void *), | ||
| 100 | void *data); | ||
| 101 | |||
| 102 | /* Get pointer to structure given unit number */ | ||
| 103 | struct pvr2_hdw *pvr2_hdw_find(int unit_number); | ||
| 104 | |||
| 105 | /* Destroy hardware interaction structure */ | ||
| 106 | void pvr2_hdw_destroy(struct pvr2_hdw *); | ||
| 107 | |||
| 108 | /* Set up the structure and attempt to put the device into a usable state. | ||
| 109 | This can be a time-consuming operation, which is why it is not done | ||
| 110 | internally as part of the create() step. Return value is exactly the | ||
| 111 | same as pvr2_hdw_init_ok(). */ | ||
| 112 | int pvr2_hdw_setup(struct pvr2_hdw *); | ||
| 113 | |||
| 114 | /* Initialization succeeded */ | ||
| 115 | int pvr2_hdw_init_ok(struct pvr2_hdw *); | ||
| 116 | |||
| 117 | /* Return true if in the ready (normal) state */ | ||
| 118 | int pvr2_hdw_dev_ok(struct pvr2_hdw *); | ||
| 119 | |||
| 120 | /* Return small integer number [1..N] for logical instance number of this | ||
| 121 | device. This is useful for indexing array-valued module parameters. */ | ||
| 122 | int pvr2_hdw_get_unit_number(struct pvr2_hdw *); | ||
| 123 | |||
| 124 | /* Get pointer to underlying USB device */ | ||
| 125 | struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *); | ||
| 126 | |||
| 127 | /* Retrieve serial number of device */ | ||
| 128 | unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *); | ||
| 129 | |||
| 130 | /* Called when hardware has been unplugged */ | ||
| 131 | void pvr2_hdw_disconnect(struct pvr2_hdw *); | ||
| 132 | |||
| 133 | /* Get the number of defined controls */ | ||
| 134 | unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *); | ||
| 135 | |||
| 136 | /* Retrieve a control handle given its index (0..count-1) */ | ||
| 137 | struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *,unsigned int); | ||
| 138 | |||
| 139 | /* Retrieve a control handle given its internal ID (if any) */ | ||
| 140 | struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *,unsigned int); | ||
| 141 | |||
| 142 | /* Retrieve a control handle given its V4L ID (if any) */ | ||
| 143 | struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *,unsigned int ctl_id); | ||
| 144 | |||
| 145 | /* Retrieve a control handle given its immediate predecessor V4L ID (if any) */ | ||
| 146 | struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *, | ||
| 147 | unsigned int ctl_id); | ||
| 148 | |||
| 149 | /* Commit all control changes made up to this point */ | ||
| 150 | int pvr2_hdw_commit_ctl(struct pvr2_hdw *); | ||
| 151 | |||
| 152 | /* Return name for this driver instance */ | ||
| 153 | const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); | ||
| 154 | |||
| 155 | /* Return PVR2_SIGNAL_XXXX bit mask indicating signal status */ | ||
| 156 | unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *); | ||
| 157 | |||
| 158 | /* Query device and see if it thinks it is on a high-speed USB link */ | ||
| 159 | int pvr2_hdw_is_hsm(struct pvr2_hdw *); | ||
| 160 | |||
| 161 | /* Turn streaming on/off */ | ||
| 162 | int pvr2_hdw_set_streaming(struct pvr2_hdw *,int); | ||
| 163 | |||
| 164 | /* Find out if streaming is on */ | ||
| 165 | int pvr2_hdw_get_streaming(struct pvr2_hdw *); | ||
| 166 | |||
| 167 | /* Configure the type of stream to generate */ | ||
| 168 | int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config); | ||
| 169 | |||
| 170 | /* Get handle to video output stream */ | ||
| 171 | struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); | ||
| 172 | |||
| 173 | /* Emit a video standard struct */ | ||
| 174 | int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, | ||
| 175 | unsigned int idx); | ||
| 176 | |||
| 177 | /* Enable / disable various pieces of hardware. Items to change are | ||
| 178 | identified by bit positions within msk, and new state for each item is | ||
| 179 | identified by corresponding bit positions within val. */ | ||
| 180 | void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw, | ||
| 181 | unsigned long msk,unsigned long val); | ||
| 182 | |||
| 183 | /* Shortcut for pvr2_hdw_subsys_bit_chg(hdw,msk,msk) */ | ||
| 184 | void pvr2_hdw_subsys_bit_set(struct pvr2_hdw *hdw,unsigned long msk); | ||
| 185 | |||
| 186 | /* Shortcut for pvr2_hdw_subsys_bit_chg(hdw,msk,0) */ | ||
| 187 | void pvr2_hdw_subsys_bit_clr(struct pvr2_hdw *hdw,unsigned long msk); | ||
| 188 | |||
| 189 | /* Retrieve mask indicating which pieces of hardware are currently enabled | ||
| 190 | / configured. */ | ||
| 191 | unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *); | ||
| 192 | |||
| 193 | /* Adjust mask of what get shut down when streaming is stopped. This is a | ||
| 194 | debugging aid. */ | ||
| 195 | void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw, | ||
| 196 | unsigned long msk,unsigned long val); | ||
| 197 | |||
| 198 | /* Retrieve mask indicating which pieces of hardware are disabled when | ||
| 199 | streaming is turned off. */ | ||
| 200 | unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *); | ||
| 201 | |||
| 202 | |||
| 203 | /* Enable / disable retrieval of CPU firmware. This must be enabled before | ||
| 204 | pvr2_hdw_cpufw_get() will function. Note that doing this may prevent | ||
| 205 | the device from running (and leaving this mode may imply a device | ||
| 206 | reset). */ | ||
| 207 | void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *, int enable_flag); | ||
| 208 | |||
| 209 | /* Return true if we're in a mode for retrieval CPU firmware */ | ||
| 210 | int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *); | ||
| 211 | |||
| 212 | /* Retrieve a piece of the CPU's firmware at the given offset. Return | ||
| 213 | value is the number of bytes retrieved or zero if we're past the end or | ||
| 214 | an error otherwise (e.g. if firmware retrieval is not enabled). */ | ||
| 215 | int pvr2_hdw_cpufw_get(struct pvr2_hdw *,unsigned int offs, | ||
| 216 | char *buf,unsigned int cnt); | ||
| 217 | |||
| 218 | /* Retrieve previously stored v4l minor device number */ | ||
| 219 | int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *); | ||
| 220 | |||
| 221 | /* Store the v4l minor device number */ | ||
| 222 | void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *,int); | ||
| 223 | |||
| 224 | |||
| 225 | /* The following entry points are all lower level things you normally don't | ||
| 226 | want to worry about. */ | ||
| 227 | |||
| 228 | /* Attempt to recover from a USB foul-up (in practice I find that if you | ||
| 229 | have to do this, then it's already too late). */ | ||
| 230 | void pvr2_reset_ctl_endpoints(struct pvr2_hdw *hdw); | ||
| 231 | |||
| 232 | /* Issue a command and get a response from the device. LOTS of higher | ||
| 233 | level stuff is built on this. */ | ||
| 234 | int pvr2_send_request(struct pvr2_hdw *, | ||
| 235 | void *write_ptr,unsigned int write_len, | ||
| 236 | void *read_ptr,unsigned int read_len); | ||
| 237 | |||
| 238 | /* Issue a command and get a response from the device. This extended | ||
| 239 | version includes a probe flag (which if set means that device errors | ||
| 240 | should not be logged or treated as fatal) and a timeout in jiffies. | ||
| 241 | This can be used to non-lethally probe the health of endpoint 1. */ | ||
| 242 | int pvr2_send_request_ex(struct pvr2_hdw *,unsigned int timeout,int probe_fl, | ||
| 243 | void *write_ptr,unsigned int write_len, | ||
| 244 | void *read_ptr,unsigned int read_len); | ||
| 245 | |||
| 246 | /* Slightly higher level device communication functions. */ | ||
| 247 | int pvr2_write_register(struct pvr2_hdw *, u16, u32); | ||
| 248 | int pvr2_read_register(struct pvr2_hdw *, u16, u32 *); | ||
| 249 | int pvr2_write_u16(struct pvr2_hdw *, u16, int); | ||
| 250 | int pvr2_write_u8(struct pvr2_hdw *, u8, int); | ||
| 251 | |||
| 252 | /* Call if for any reason we can't talk to the hardware anymore - this will | ||
| 253 | cause the driver to stop flailing on the device. */ | ||
| 254 | void pvr2_hdw_render_useless(struct pvr2_hdw *); | ||
| 255 | void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *); | ||
| 256 | |||
| 257 | /* Set / clear 8051's reset bit */ | ||
| 258 | void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int); | ||
| 259 | |||
| 260 | /* Execute a USB-commanded device reset */ | ||
| 261 | void pvr2_hdw_device_reset(struct pvr2_hdw *); | ||
| 262 | |||
| 263 | /* Execute hard reset command (after this point it's likely that the | ||
| 264 | encoder will have to be reconfigured). This also clears the "useless" | ||
| 265 | state. */ | ||
| 266 | int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *); | ||
| 267 | |||
| 268 | /* Execute simple reset command */ | ||
| 269 | int pvr2_hdw_cmd_powerup(struct pvr2_hdw *); | ||
| 270 | |||
| 271 | /* Order decoder to reset */ | ||
| 272 | int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *); | ||
| 273 | |||
| 274 | /* Stop / start video stream transport */ | ||
| 275 | int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); | ||
| 276 | |||
| 277 | /* Find I2C address of eeprom */ | ||
| 278 | int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *); | ||
| 279 | |||
| 280 | /* Direct manipulation of GPIO bits */ | ||
| 281 | int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *); | ||
| 282 | int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *); | ||
| 283 | int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *); | ||
| 284 | int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val); | ||
| 285 | int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val); | ||
| 286 | |||
| 287 | /* This data structure is specifically for the next function... */ | ||
| 288 | struct pvr2_hdw_debug_info { | ||
| 289 | int big_lock_held; | ||
| 290 | int ctl_lock_held; | ||
| 291 | int flag_ok; | ||
| 292 | int flag_disconnected; | ||
| 293 | int flag_init_ok; | ||
| 294 | int flag_streaming_enabled; | ||
| 295 | unsigned long subsys_flags; | ||
| 296 | int cmd_debug_state; | ||
| 297 | int cmd_debug_write_len; | ||
| 298 | int cmd_debug_read_len; | ||
| 299 | int cmd_debug_write_pend; | ||
| 300 | int cmd_debug_read_pend; | ||
| 301 | int cmd_debug_timeout; | ||
| 302 | int cmd_debug_rstatus; | ||
| 303 | int cmd_debug_wstatus; | ||
| 304 | unsigned char cmd_code; | ||
| 305 | }; | ||
| 306 | |||
| 307 | /* Non-intrusively retrieve internal state info - this is useful for | ||
| 308 | diagnosing lockups. Note that this operation is completed without any | ||
| 309 | kind of locking and so it is not atomic and may yield inconsistent | ||
| 310 | results. This is *purely* a debugging aid. */ | ||
| 311 | void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw, | ||
| 312 | struct pvr2_hdw_debug_info *); | ||
| 313 | |||
| 314 | /* Cause modules to log their state once */ | ||
| 315 | void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw); | ||
| 316 | |||
| 317 | /* Cause encoder firmware to be uploaded into the device. This is normally | ||
| 318 | done autonomously, but the interface is exported here because it is also | ||
| 319 | a debugging aid. */ | ||
| 320 | int pvr2_upload_firmware2(struct pvr2_hdw *hdw); | ||
| 321 | |||
| 322 | /* List of device types that we can match */ | ||
| 323 | extern struct usb_device_id pvr2_device_table[]; | ||
| 324 | |||
| 325 | #endif /* __PVRUSB2_HDW_H */ | ||
| 326 | |||
| 327 | /* | ||
| 328 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 329 | *** Local Variables: *** | ||
| 330 | *** mode: c *** | ||
| 331 | *** fill-column: 75 *** | ||
| 332 | *** tab-width: 8 *** | ||
| 333 | *** c-basic-offset: 8 *** | ||
| 334 | *** End: *** | ||
| 335 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c new file mode 100644 index 000000000000..1dd4f6249b99 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "pvrusb2-i2c-core.h" | ||
| 23 | #include "pvrusb2-hdw-internal.h" | ||
| 24 | #include "pvrusb2-debug.h" | ||
| 25 | #include "pvrusb2-i2c-cmd-v4l2.h" | ||
| 26 | #include "pvrusb2-audio.h" | ||
| 27 | #include "pvrusb2-tuner.h" | ||
| 28 | #include "pvrusb2-demod.h" | ||
| 29 | #include "pvrusb2-video-v4l.h" | ||
| 30 | #ifdef CONFIG_VIDEO_PVRUSB2_24XXX | ||
| 31 | #include "pvrusb2-cx2584x-v4l.h" | ||
| 32 | #include "pvrusb2-wm8775.h" | ||
| 33 | #endif | ||
| 34 | |||
| 35 | #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) | ||
| 36 | |||
| 37 | #define OP_STANDARD 0 | ||
| 38 | #define OP_BCSH 1 | ||
| 39 | #define OP_VOLUME 2 | ||
| 40 | #define OP_FREQ 3 | ||
| 41 | #define OP_AUDIORATE 4 | ||
| 42 | #define OP_SIZE 5 | ||
| 43 | #define OP_LOG 6 | ||
| 44 | |||
| 45 | static const struct pvr2_i2c_op * const ops[] = { | ||
| 46 | [OP_STANDARD] = &pvr2_i2c_op_v4l2_standard, | ||
| 47 | [OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh, | ||
| 48 | [OP_VOLUME] = &pvr2_i2c_op_v4l2_volume, | ||
| 49 | [OP_FREQ] = &pvr2_i2c_op_v4l2_frequency, | ||
| 50 | [OP_SIZE] = &pvr2_i2c_op_v4l2_size, | ||
| 51 | [OP_LOG] = &pvr2_i2c_op_v4l2_log, | ||
| 52 | }; | ||
| 53 | |||
| 54 | void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) | ||
| 55 | { | ||
| 56 | int id; | ||
| 57 | id = cp->client->driver->id; | ||
| 58 | cp->ctl_mask = ((1 << OP_STANDARD) | | ||
| 59 | (1 << OP_BCSH) | | ||
| 60 | (1 << OP_VOLUME) | | ||
| 61 | (1 << OP_FREQ) | | ||
| 62 | (1 << OP_SIZE) | | ||
| 63 | (1 << OP_LOG)); | ||
| 64 | |||
| 65 | if (id == I2C_DRIVERID_MSP3400) { | ||
| 66 | if (pvr2_i2c_msp3400_setup(hdw,cp)) { | ||
| 67 | return; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | if (id == I2C_DRIVERID_TUNER) { | ||
| 71 | if (pvr2_i2c_tuner_setup(hdw,cp)) { | ||
| 72 | return; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | #ifdef CONFIG_VIDEO_PVRUSB2_24XXX | ||
| 76 | if (id == I2C_DRIVERID_CX25840) { | ||
| 77 | if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) { | ||
| 78 | return; | ||
| 79 | } | ||
| 80 | } | ||
| 81 | if (id == I2C_DRIVERID_WM8775) { | ||
| 82 | if (pvr2_i2c_wm8775_setup(hdw,cp)) { | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | #endif | ||
| 87 | if (id == I2C_DRIVERID_SAA711X) { | ||
| 88 | if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) { | ||
| 89 | return; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | if (id == I2C_DRIVERID_TDA9887) { | ||
| 93 | if (pvr2_i2c_demod_setup(hdw,cp)) { | ||
| 94 | return; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | |||
| 100 | const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx) | ||
| 101 | { | ||
| 102 | if (idx >= sizeof(ops)/sizeof(ops[0])) return 0; | ||
| 103 | return ops[idx]; | ||
| 104 | } | ||
| 105 | |||
| 106 | |||
| 107 | /* | ||
| 108 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 109 | *** Local Variables: *** | ||
| 110 | *** mode: c *** | ||
| 111 | *** fill-column: 75 *** | ||
| 112 | *** tab-width: 8 *** | ||
| 113 | *** c-basic-offset: 8 *** | ||
| 114 | *** End: *** | ||
| 115 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c new file mode 100644 index 000000000000..9f81aff2b38a --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c | |||
| @@ -0,0 +1,232 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include "pvrusb2-i2c-cmd-v4l2.h" | ||
| 24 | #include "pvrusb2-hdw-internal.h" | ||
| 25 | #include "pvrusb2-debug.h" | ||
| 26 | #include <linux/videodev2.h> | ||
| 27 | |||
| 28 | |||
| 29 | static void set_standard(struct pvr2_hdw *hdw) | ||
| 30 | { | ||
| 31 | v4l2_std_id vs; | ||
| 32 | vs = hdw->std_mask_cur; | ||
| 33 | pvr2_trace(PVR2_TRACE_CHIPS, | ||
| 34 | "i2c v4l2 set_standard(0x%llx)",(__u64)vs); | ||
| 35 | |||
| 36 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs); | ||
| 37 | } | ||
| 38 | |||
| 39 | |||
| 40 | static int check_standard(struct pvr2_hdw *hdw) | ||
| 41 | { | ||
| 42 | return hdw->std_dirty != 0; | ||
| 43 | } | ||
| 44 | |||
| 45 | |||
| 46 | const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard = { | ||
| 47 | .check = check_standard, | ||
| 48 | .update = set_standard, | ||
| 49 | .name = "v4l2_standard", | ||
| 50 | }; | ||
| 51 | |||
| 52 | |||
| 53 | static void set_bcsh(struct pvr2_hdw *hdw) | ||
| 54 | { | ||
| 55 | struct v4l2_control ctrl; | ||
| 56 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_bcsh" | ||
| 57 | " b=%d c=%d s=%d h=%d", | ||
| 58 | hdw->brightness_val,hdw->contrast_val, | ||
| 59 | hdw->saturation_val,hdw->hue_val); | ||
| 60 | memset(&ctrl,0,sizeof(ctrl)); | ||
| 61 | ctrl.id = V4L2_CID_BRIGHTNESS; | ||
| 62 | ctrl.value = hdw->brightness_val; | ||
| 63 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); | ||
| 64 | ctrl.id = V4L2_CID_CONTRAST; | ||
| 65 | ctrl.value = hdw->contrast_val; | ||
| 66 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); | ||
| 67 | ctrl.id = V4L2_CID_SATURATION; | ||
| 68 | ctrl.value = hdw->saturation_val; | ||
| 69 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); | ||
| 70 | ctrl.id = V4L2_CID_HUE; | ||
| 71 | ctrl.value = hdw->hue_val; | ||
| 72 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); | ||
| 73 | } | ||
| 74 | |||
| 75 | |||
| 76 | static int check_bcsh(struct pvr2_hdw *hdw) | ||
| 77 | { | ||
| 78 | return (hdw->brightness_dirty || | ||
| 79 | hdw->contrast_dirty || | ||
| 80 | hdw->saturation_dirty || | ||
| 81 | hdw->hue_dirty); | ||
| 82 | } | ||
| 83 | |||
| 84 | |||
| 85 | const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh = { | ||
| 86 | .check = check_bcsh, | ||
| 87 | .update = set_bcsh, | ||
| 88 | .name = "v4l2_bcsh", | ||
| 89 | }; | ||
| 90 | |||
| 91 | |||
| 92 | static void set_volume(struct pvr2_hdw *hdw) | ||
| 93 | { | ||
| 94 | struct v4l2_control ctrl; | ||
| 95 | pvr2_trace(PVR2_TRACE_CHIPS, | ||
| 96 | "i2c v4l2 set_volume" | ||
| 97 | "(vol=%d bal=%d bas=%d treb=%d mute=%d)", | ||
| 98 | hdw->volume_val, | ||
| 99 | hdw->balance_val, | ||
| 100 | hdw->bass_val, | ||
| 101 | hdw->treble_val, | ||
| 102 | hdw->mute_val); | ||
| 103 | memset(&ctrl,0,sizeof(ctrl)); | ||
| 104 | ctrl.id = V4L2_CID_AUDIO_MUTE; | ||
| 105 | ctrl.value = hdw->mute_val ? 1 : 0; | ||
| 106 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); | ||
| 107 | ctrl.id = V4L2_CID_AUDIO_VOLUME; | ||
| 108 | ctrl.value = hdw->volume_val; | ||
| 109 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); | ||
| 110 | ctrl.id = V4L2_CID_AUDIO_BALANCE; | ||
| 111 | ctrl.value = hdw->balance_val; | ||
| 112 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); | ||
| 113 | ctrl.id = V4L2_CID_AUDIO_BASS; | ||
| 114 | ctrl.value = hdw->bass_val; | ||
| 115 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); | ||
| 116 | ctrl.id = V4L2_CID_AUDIO_TREBLE; | ||
| 117 | ctrl.value = hdw->treble_val; | ||
| 118 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); | ||
| 119 | } | ||
| 120 | |||
| 121 | |||
| 122 | static int check_volume(struct pvr2_hdw *hdw) | ||
| 123 | { | ||
| 124 | return (hdw->volume_dirty || | ||
| 125 | hdw->balance_dirty || | ||
| 126 | hdw->bass_dirty || | ||
| 127 | hdw->treble_dirty || | ||
| 128 | hdw->mute_dirty); | ||
| 129 | } | ||
| 130 | |||
| 131 | |||
| 132 | const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume = { | ||
| 133 | .check = check_volume, | ||
| 134 | .update = set_volume, | ||
| 135 | .name = "v4l2_volume", | ||
| 136 | }; | ||
| 137 | |||
| 138 | |||
| 139 | static void set_frequency(struct pvr2_hdw *hdw) | ||
| 140 | { | ||
| 141 | unsigned long fv; | ||
| 142 | struct v4l2_frequency freq; | ||
| 143 | fv = hdw->freqVal; | ||
| 144 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv); | ||
| 145 | memset(&freq,0,sizeof(freq)); | ||
| 146 | freq.frequency = fv / 62500; | ||
| 147 | freq.tuner = 0; | ||
| 148 | freq.type = V4L2_TUNER_ANALOG_TV; | ||
| 149 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_FREQUENCY,&freq); | ||
| 150 | } | ||
| 151 | |||
| 152 | |||
| 153 | static int check_frequency(struct pvr2_hdw *hdw) | ||
| 154 | { | ||
| 155 | return hdw->freqDirty != 0; | ||
| 156 | } | ||
| 157 | |||
| 158 | |||
| 159 | const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency = { | ||
| 160 | .check = check_frequency, | ||
| 161 | .update = set_frequency, | ||
| 162 | .name = "v4l2_freq", | ||
| 163 | }; | ||
| 164 | |||
| 165 | |||
| 166 | static void set_size(struct pvr2_hdw *hdw) | ||
| 167 | { | ||
| 168 | struct v4l2_format fmt; | ||
| 169 | |||
| 170 | memset(&fmt,0,sizeof(fmt)); | ||
| 171 | |||
| 172 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
| 173 | fmt.fmt.pix.width = hdw->res_hor_val; | ||
| 174 | fmt.fmt.pix.height = hdw->res_ver_val; | ||
| 175 | |||
| 176 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_size(%dx%d)", | ||
| 177 | fmt.fmt.pix.width,fmt.fmt.pix.height); | ||
| 178 | |||
| 179 | pvr2_i2c_core_cmd(hdw,VIDIOC_S_FMT,&fmt); | ||
| 180 | } | ||
| 181 | |||
| 182 | |||
| 183 | static int check_size(struct pvr2_hdw *hdw) | ||
| 184 | { | ||
| 185 | return (hdw->res_hor_dirty || hdw->res_ver_dirty); | ||
| 186 | } | ||
| 187 | |||
| 188 | |||
| 189 | const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size = { | ||
| 190 | .check = check_size, | ||
| 191 | .update = set_size, | ||
| 192 | .name = "v4l2_size", | ||
| 193 | }; | ||
| 194 | |||
| 195 | |||
| 196 | static void do_log(struct pvr2_hdw *hdw) | ||
| 197 | { | ||
| 198 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 do_log()"); | ||
| 199 | pvr2_i2c_core_cmd(hdw,VIDIOC_LOG_STATUS,0); | ||
| 200 | |||
| 201 | } | ||
| 202 | |||
| 203 | |||
| 204 | static int check_log(struct pvr2_hdw *hdw) | ||
| 205 | { | ||
| 206 | return hdw->log_requested != 0; | ||
| 207 | } | ||
| 208 | |||
| 209 | |||
| 210 | const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log = { | ||
| 211 | .check = check_log, | ||
| 212 | .update = do_log, | ||
| 213 | .name = "v4l2_log", | ||
| 214 | }; | ||
| 215 | |||
| 216 | |||
| 217 | void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl) | ||
| 218 | { | ||
| 219 | pvr2_i2c_client_cmd(cp, | ||
| 220 | (fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),0); | ||
| 221 | } | ||
| 222 | |||
| 223 | |||
| 224 | /* | ||
| 225 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 226 | *** Local Variables: *** | ||
| 227 | *** mode: c *** | ||
| 228 | *** fill-column: 70 *** | ||
| 229 | *** tab-width: 8 *** | ||
| 230 | *** c-basic-offset: 8 *** | ||
| 231 | *** End: *** | ||
| 232 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h new file mode 100644 index 000000000000..ecabddba1ec5 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef __PVRUSB2_CMD_V4L2_H | ||
| 24 | #define __PVRUSB2_CMD_V4L2_H | ||
| 25 | |||
| 26 | #include "pvrusb2-i2c-core.h" | ||
| 27 | |||
| 28 | extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard; | ||
| 29 | extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh; | ||
| 30 | extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume; | ||
| 31 | extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency; | ||
| 32 | extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size; | ||
| 33 | extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log; | ||
| 34 | |||
| 35 | void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *,int); | ||
| 36 | |||
| 37 | #endif /* __PVRUSB2_CMD_V4L2_H */ | ||
| 38 | |||
| 39 | /* | ||
| 40 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 41 | *** Local Variables: *** | ||
| 42 | *** mode: c *** | ||
| 43 | *** fill-column: 70 *** | ||
| 44 | *** tab-width: 8 *** | ||
| 45 | *** c-basic-offset: 8 *** | ||
| 46 | *** End: *** | ||
| 47 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c new file mode 100644 index 000000000000..c8d0bdee3ff1 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | |||
| @@ -0,0 +1,937 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "pvrusb2-i2c-core.h" | ||
| 23 | #include "pvrusb2-hdw-internal.h" | ||
| 24 | #include "pvrusb2-debug.h" | ||
| 25 | |||
| 26 | #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) | ||
| 27 | |||
| 28 | /* | ||
| 29 | |||
| 30 | This module attempts to implement a compliant I2C adapter for the pvrusb2 | ||
| 31 | device. By doing this we can then make use of existing functionality in | ||
| 32 | V4L (e.g. tuner.c) rather than rolling our own. | ||
| 33 | |||
| 34 | */ | ||
| 35 | |||
| 36 | static unsigned int i2c_scan = 0; | ||
| 37 | module_param(i2c_scan, int, S_IRUGO|S_IWUSR); | ||
| 38 | MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); | ||
| 39 | |||
| 40 | static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ | ||
| 41 | u8 i2c_addr, /* I2C address we're talking to */ | ||
| 42 | u8 *data, /* Data to write */ | ||
| 43 | u16 length) /* Size of data to write */ | ||
| 44 | { | ||
| 45 | /* Return value - default 0 means success */ | ||
| 46 | int ret; | ||
| 47 | |||
| 48 | |||
| 49 | if (!data) length = 0; | ||
| 50 | if (length > (sizeof(hdw->cmd_buffer) - 3)) { | ||
| 51 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 52 | "Killing an I2C write to %u that is too large" | ||
| 53 | " (desired=%u limit=%u)", | ||
| 54 | i2c_addr, | ||
| 55 | length,(unsigned int)(sizeof(hdw->cmd_buffer) - 3)); | ||
| 56 | return -ENOTSUPP; | ||
| 57 | } | ||
| 58 | |||
| 59 | LOCK_TAKE(hdw->ctl_lock); | ||
| 60 | |||
| 61 | /* Clear the command buffer (likely to be paranoia) */ | ||
| 62 | memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); | ||
| 63 | |||
| 64 | /* Set up command buffer for an I2C write */ | ||
| 65 | hdw->cmd_buffer[0] = 0x08; /* write prefix */ | ||
| 66 | hdw->cmd_buffer[1] = i2c_addr; /* i2c addr of chip */ | ||
| 67 | hdw->cmd_buffer[2] = length; /* length of what follows */ | ||
| 68 | if (length) memcpy(hdw->cmd_buffer + 3, data, length); | ||
| 69 | |||
| 70 | /* Do the operation */ | ||
| 71 | ret = pvr2_send_request(hdw, | ||
| 72 | hdw->cmd_buffer, | ||
| 73 | length + 3, | ||
| 74 | hdw->cmd_buffer, | ||
| 75 | 1); | ||
| 76 | if (!ret) { | ||
| 77 | if (hdw->cmd_buffer[0] != 8) { | ||
| 78 | ret = -EIO; | ||
| 79 | if (hdw->cmd_buffer[0] != 7) { | ||
| 80 | trace_i2c("unexpected status" | ||
| 81 | " from i2_write[%d]: %d", | ||
| 82 | i2c_addr,hdw->cmd_buffer[0]); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | LOCK_GIVE(hdw->ctl_lock); | ||
| 88 | |||
| 89 | return ret; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ | ||
| 93 | u8 i2c_addr, /* I2C address we're talking to */ | ||
| 94 | u8 *data, /* Data to write */ | ||
| 95 | u16 dlen, /* Size of data to write */ | ||
| 96 | u8 *res, /* Where to put data we read */ | ||
| 97 | u16 rlen) /* Amount of data to read */ | ||
| 98 | { | ||
| 99 | /* Return value - default 0 means success */ | ||
| 100 | int ret; | ||
| 101 | |||
| 102 | |||
| 103 | if (!data) dlen = 0; | ||
| 104 | if (dlen > (sizeof(hdw->cmd_buffer) - 4)) { | ||
| 105 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 106 | "Killing an I2C read to %u that has wlen too large" | ||
| 107 | " (desired=%u limit=%u)", | ||
| 108 | i2c_addr, | ||
| 109 | dlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 4)); | ||
| 110 | return -ENOTSUPP; | ||
| 111 | } | ||
| 112 | if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) { | ||
| 113 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 114 | "Killing an I2C read to %u that has rlen too large" | ||
| 115 | " (desired=%u limit=%u)", | ||
| 116 | i2c_addr, | ||
| 117 | rlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 1)); | ||
| 118 | return -ENOTSUPP; | ||
| 119 | } | ||
| 120 | |||
| 121 | LOCK_TAKE(hdw->ctl_lock); | ||
| 122 | |||
| 123 | /* Clear the command buffer (likely to be paranoia) */ | ||
| 124 | memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); | ||
| 125 | |||
| 126 | /* Set up command buffer for an I2C write followed by a read */ | ||
| 127 | hdw->cmd_buffer[0] = 0x09; /* read prefix */ | ||
| 128 | hdw->cmd_buffer[1] = dlen; /* arg length */ | ||
| 129 | hdw->cmd_buffer[2] = rlen; /* answer length. Device will send one | ||
| 130 | more byte (status). */ | ||
| 131 | hdw->cmd_buffer[3] = i2c_addr; /* i2c addr of chip */ | ||
| 132 | if (dlen) memcpy(hdw->cmd_buffer + 4, data, dlen); | ||
| 133 | |||
| 134 | /* Do the operation */ | ||
| 135 | ret = pvr2_send_request(hdw, | ||
| 136 | hdw->cmd_buffer, | ||
| 137 | 4 + dlen, | ||
| 138 | hdw->cmd_buffer, | ||
| 139 | rlen + 1); | ||
| 140 | if (!ret) { | ||
| 141 | if (hdw->cmd_buffer[0] != 8) { | ||
| 142 | ret = -EIO; | ||
| 143 | if (hdw->cmd_buffer[0] != 7) { | ||
| 144 | trace_i2c("unexpected status" | ||
| 145 | " from i2_read[%d]: %d", | ||
| 146 | i2c_addr,hdw->cmd_buffer[0]); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | /* Copy back the result */ | ||
| 152 | if (res && rlen) { | ||
| 153 | if (ret) { | ||
| 154 | /* Error, just blank out the return buffer */ | ||
| 155 | memset(res, 0, rlen); | ||
| 156 | } else { | ||
| 157 | memcpy(res, hdw->cmd_buffer + 1, rlen); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | LOCK_GIVE(hdw->ctl_lock); | ||
| 162 | |||
| 163 | return ret; | ||
| 164 | } | ||
| 165 | |||
| 166 | /* This is the common low level entry point for doing I2C operations to the | ||
| 167 | hardware. */ | ||
| 168 | int pvr2_i2c_basic_op(struct pvr2_hdw *hdw, | ||
| 169 | u8 i2c_addr, | ||
| 170 | u8 *wdata, | ||
| 171 | u16 wlen, | ||
| 172 | u8 *rdata, | ||
| 173 | u16 rlen) | ||
| 174 | { | ||
| 175 | if (!rdata) rlen = 0; | ||
| 176 | if (!wdata) wlen = 0; | ||
| 177 | if (rlen || !wlen) { | ||
| 178 | return pvr2_i2c_read(hdw,i2c_addr,wdata,wlen,rdata,rlen); | ||
| 179 | } else { | ||
| 180 | return pvr2_i2c_write(hdw,i2c_addr,wdata,wlen); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | #ifdef CONFIG_VIDEO_PVRUSB2_24XXX | ||
| 185 | |||
| 186 | /* This is a special entry point that is entered if an I2C operation is | ||
| 187 | attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this | ||
| 188 | part doesn't work, but we know it is really there. So let's look for | ||
| 189 | the autodetect attempt and just return success if we see that. */ | ||
| 190 | static int i2c_hack_wm8775(struct pvr2_hdw *hdw, | ||
| 191 | u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) | ||
| 192 | { | ||
| 193 | if (!(rlen || wlen)) { | ||
| 194 | // This is a probe attempt. Just let it succeed. | ||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); | ||
| 198 | } | ||
| 199 | |||
| 200 | /* This is a special entry point that is entered if an I2C operation is | ||
| 201 | attempted to a cx25840 chip on model 24xxx hardware. This chip can | ||
| 202 | sometimes wedge itself. Worse still, when this happens msp3400 can | ||
| 203 | falsely detect this part and then the system gets hosed up after msp3400 | ||
| 204 | gets confused and dies. What we want to do here is try to keep msp3400 | ||
| 205 | away and also try to notice if the chip is wedged and send a warning to | ||
| 206 | the system log. */ | ||
| 207 | static int i2c_hack_cx25840(struct pvr2_hdw *hdw, | ||
| 208 | u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) | ||
| 209 | { | ||
| 210 | int ret; | ||
| 211 | unsigned int subaddr; | ||
| 212 | u8 wbuf[2]; | ||
| 213 | int state = hdw->i2c_cx25840_hack_state; | ||
| 214 | |||
| 215 | if (!(rlen || wlen)) { | ||
| 216 | // Probe attempt - always just succeed and don't bother the | ||
| 217 | // hardware (this helps to make the state machine further | ||
| 218 | // down somewhat easier). | ||
| 219 | return 0; | ||
| 220 | } | ||
| 221 | |||
| 222 | if (state == 3) { | ||
| 223 | return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); | ||
| 224 | } | ||
| 225 | |||
| 226 | /* We're looking for the exact pattern where the revision register | ||
| 227 | is being read. The cx25840 module will always look at the | ||
| 228 | revision register first. Any other pattern of access therefore | ||
| 229 | has to be a probe attempt from somebody else so we'll reject it. | ||
| 230 | Normally we could just let each client just probe the part | ||
| 231 | anyway, but when the cx25840 is wedged, msp3400 will get a false | ||
| 232 | positive and that just screws things up... */ | ||
| 233 | |||
| 234 | if (wlen == 0) { | ||
| 235 | switch (state) { | ||
| 236 | case 1: subaddr = 0x0100; break; | ||
| 237 | case 2: subaddr = 0x0101; break; | ||
| 238 | default: goto fail; | ||
| 239 | } | ||
| 240 | } else if (wlen == 2) { | ||
| 241 | subaddr = (wdata[0] << 8) | wdata[1]; | ||
| 242 | switch (subaddr) { | ||
| 243 | case 0x0100: state = 1; break; | ||
| 244 | case 0x0101: state = 2; break; | ||
| 245 | default: goto fail; | ||
| 246 | } | ||
| 247 | } else { | ||
| 248 | goto fail; | ||
| 249 | } | ||
| 250 | if (!rlen) goto success; | ||
| 251 | state = 0; | ||
| 252 | if (rlen != 1) goto fail; | ||
| 253 | |||
| 254 | /* If we get to here then we have a legitimate read for one of the | ||
| 255 | two revision bytes, so pass it through. */ | ||
| 256 | wbuf[0] = subaddr >> 8; | ||
| 257 | wbuf[1] = subaddr; | ||
| 258 | ret = pvr2_i2c_basic_op(hdw,i2c_addr,wbuf,2,rdata,rlen); | ||
| 259 | |||
| 260 | if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) { | ||
| 261 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 262 | "WARNING: Detected a wedged cx25840 chip;" | ||
| 263 | " the device will not work."); | ||
| 264 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 265 | "WARNING: Try power cycling the pvrusb2 device."); | ||
| 266 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 267 | "WARNING: Disabling further access to the device" | ||
| 268 | " to prevent other foul-ups."); | ||
| 269 | // This blocks all further communication with the part. | ||
| 270 | hdw->i2c_func[0x44] = 0; | ||
| 271 | pvr2_hdw_render_useless(hdw); | ||
| 272 | goto fail; | ||
| 273 | } | ||
| 274 | |||
| 275 | /* Success! */ | ||
| 276 | pvr2_trace(PVR2_TRACE_CHIPS,"cx25840 appears to be OK."); | ||
| 277 | state = 3; | ||
| 278 | |||
| 279 | success: | ||
| 280 | hdw->i2c_cx25840_hack_state = state; | ||
| 281 | return 0; | ||
| 282 | |||
| 283 | fail: | ||
| 284 | hdw->i2c_cx25840_hack_state = state; | ||
| 285 | return -EIO; | ||
| 286 | } | ||
| 287 | |||
| 288 | #endif /* CONFIG_VIDEO_PVRUSB2_24XXX */ | ||
| 289 | |||
| 290 | /* This is a very, very limited I2C adapter implementation. We can only | ||
| 291 | support what we actually know will work on the device... */ | ||
| 292 | static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, | ||
| 293 | struct i2c_msg msgs[], | ||
| 294 | int num) | ||
| 295 | { | ||
| 296 | int ret = -ENOTSUPP; | ||
| 297 | pvr2_i2c_func funcp = 0; | ||
| 298 | struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data); | ||
| 299 | |||
| 300 | if (!num) { | ||
| 301 | ret = -EINVAL; | ||
| 302 | goto done; | ||
| 303 | } | ||
| 304 | if ((msgs[0].flags & I2C_M_NOSTART)) { | ||
| 305 | trace_i2c("i2c refusing I2C_M_NOSTART"); | ||
| 306 | goto done; | ||
| 307 | } | ||
| 308 | if (msgs[0].addr < PVR2_I2C_FUNC_CNT) { | ||
| 309 | funcp = hdw->i2c_func[msgs[0].addr]; | ||
| 310 | } | ||
| 311 | if (!funcp) { | ||
| 312 | ret = -EIO; | ||
| 313 | goto done; | ||
| 314 | } | ||
| 315 | |||
| 316 | if (num == 1) { | ||
| 317 | if (msgs[0].flags & I2C_M_RD) { | ||
| 318 | /* Simple read */ | ||
| 319 | u16 tcnt,bcnt,offs; | ||
| 320 | if (!msgs[0].len) { | ||
| 321 | /* Length == 0 read. This is a probe. */ | ||
| 322 | if (funcp(hdw,msgs[0].addr,0,0,0,0)) { | ||
| 323 | ret = -EIO; | ||
| 324 | goto done; | ||
| 325 | } | ||
| 326 | ret = 1; | ||
| 327 | goto done; | ||
| 328 | } | ||
| 329 | /* If the read is short enough we'll do the whole | ||
| 330 | thing atomically. Otherwise we have no choice | ||
| 331 | but to break apart the reads. */ | ||
| 332 | tcnt = msgs[0].len; | ||
| 333 | offs = 0; | ||
| 334 | while (tcnt) { | ||
| 335 | bcnt = tcnt; | ||
| 336 | if (bcnt > sizeof(hdw->cmd_buffer)-1) { | ||
| 337 | bcnt = sizeof(hdw->cmd_buffer)-1; | ||
| 338 | } | ||
| 339 | if (funcp(hdw,msgs[0].addr,0,0, | ||
| 340 | msgs[0].buf+offs,bcnt)) { | ||
| 341 | ret = -EIO; | ||
| 342 | goto done; | ||
| 343 | } | ||
| 344 | offs += bcnt; | ||
| 345 | tcnt -= bcnt; | ||
| 346 | } | ||
| 347 | ret = 1; | ||
| 348 | goto done; | ||
| 349 | } else { | ||
| 350 | /* Simple write */ | ||
| 351 | ret = 1; | ||
| 352 | if (funcp(hdw,msgs[0].addr, | ||
| 353 | msgs[0].buf,msgs[0].len,0,0)) { | ||
| 354 | ret = -EIO; | ||
| 355 | } | ||
| 356 | goto done; | ||
| 357 | } | ||
| 358 | } else if (num == 2) { | ||
| 359 | if (msgs[0].addr != msgs[1].addr) { | ||
| 360 | trace_i2c("i2c refusing 2 phase transfer with" | ||
| 361 | " conflicting target addresses"); | ||
| 362 | ret = -ENOTSUPP; | ||
| 363 | goto done; | ||
| 364 | } | ||
| 365 | if ((!((msgs[0].flags & I2C_M_RD))) && | ||
| 366 | (msgs[1].flags & I2C_M_RD)) { | ||
| 367 | u16 tcnt,bcnt,wcnt,offs; | ||
| 368 | /* Write followed by atomic read. If the read | ||
| 369 | portion is short enough we'll do the whole thing | ||
| 370 | atomically. Otherwise we have no choice but to | ||
| 371 | break apart the reads. */ | ||
| 372 | tcnt = msgs[1].len; | ||
| 373 | wcnt = msgs[0].len; | ||
| 374 | offs = 0; | ||
| 375 | while (tcnt || wcnt) { | ||
| 376 | bcnt = tcnt; | ||
| 377 | if (bcnt > sizeof(hdw->cmd_buffer)-1) { | ||
| 378 | bcnt = sizeof(hdw->cmd_buffer)-1; | ||
| 379 | } | ||
| 380 | if (funcp(hdw,msgs[0].addr, | ||
| 381 | msgs[0].buf,wcnt, | ||
| 382 | msgs[1].buf+offs,bcnt)) { | ||
| 383 | ret = -EIO; | ||
| 384 | goto done; | ||
| 385 | } | ||
| 386 | offs += bcnt; | ||
| 387 | tcnt -= bcnt; | ||
| 388 | wcnt = 0; | ||
| 389 | } | ||
| 390 | ret = 2; | ||
| 391 | goto done; | ||
| 392 | } else { | ||
| 393 | trace_i2c("i2c refusing complex transfer" | ||
| 394 | " read0=%d read1=%d", | ||
| 395 | (msgs[0].flags & I2C_M_RD), | ||
| 396 | (msgs[1].flags & I2C_M_RD)); | ||
| 397 | } | ||
| 398 | } else { | ||
| 399 | trace_i2c("i2c refusing %d phase transfer",num); | ||
| 400 | } | ||
| 401 | |||
| 402 | done: | ||
| 403 | if (pvrusb2_debug & PVR2_TRACE_I2C_TRAF) { | ||
| 404 | unsigned int idx,offs,cnt; | ||
| 405 | for (idx = 0; idx < num; idx++) { | ||
| 406 | cnt = msgs[idx].len; | ||
| 407 | printk(KERN_INFO | ||
| 408 | "pvrusb2 i2c xfer %u/%u:" | ||
| 409 | " addr=0x%x len=%d %s%s", | ||
| 410 | idx+1,num, | ||
| 411 | msgs[idx].addr, | ||
| 412 | cnt, | ||
| 413 | (msgs[idx].flags & I2C_M_RD ? | ||
| 414 | "read" : "write"), | ||
| 415 | (msgs[idx].flags & I2C_M_NOSTART ? | ||
| 416 | " nostart" : "")); | ||
| 417 | if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) { | ||
| 418 | if (cnt > 8) cnt = 8; | ||
| 419 | printk(" ["); | ||
| 420 | for (offs = 0; offs < (cnt>8?8:cnt); offs++) { | ||
| 421 | if (offs) printk(" "); | ||
| 422 | printk("%02x",msgs[idx].buf[offs]); | ||
| 423 | } | ||
| 424 | if (offs < cnt) printk(" ..."); | ||
| 425 | printk("]"); | ||
| 426 | } | ||
| 427 | if (idx+1 == num) { | ||
| 428 | printk(" result=%d",ret); | ||
| 429 | } | ||
| 430 | printk("\n"); | ||
| 431 | } | ||
| 432 | if (!num) { | ||
| 433 | printk(KERN_INFO | ||
| 434 | "pvrusb2 i2c xfer null transfer result=%d\n", | ||
| 435 | ret); | ||
| 436 | } | ||
| 437 | } | ||
| 438 | return ret; | ||
| 439 | } | ||
| 440 | |||
| 441 | static int pvr2_i2c_control(struct i2c_adapter *adapter, | ||
| 442 | unsigned int cmd, unsigned long arg) | ||
| 443 | { | ||
| 444 | return 0; | ||
| 445 | } | ||
| 446 | |||
| 447 | static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) | ||
| 448 | { | ||
| 449 | return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA; | ||
| 450 | } | ||
| 451 | |||
| 452 | static int pvr2_i2c_core_singleton(struct i2c_client *cp, | ||
| 453 | unsigned int cmd,void *arg) | ||
| 454 | { | ||
| 455 | int stat; | ||
| 456 | if (!cp) return -EINVAL; | ||
| 457 | if (!(cp->driver)) return -EINVAL; | ||
| 458 | if (!(cp->driver->command)) return -EINVAL; | ||
| 459 | if (!try_module_get(cp->driver->driver.owner)) return -EAGAIN; | ||
| 460 | stat = cp->driver->command(cp,cmd,arg); | ||
| 461 | module_put(cp->driver->driver.owner); | ||
| 462 | return stat; | ||
| 463 | } | ||
| 464 | |||
| 465 | int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg) | ||
| 466 | { | ||
| 467 | int stat; | ||
| 468 | if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { | ||
| 469 | char buf[100]; | ||
| 470 | unsigned int cnt; | ||
| 471 | cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, | ||
| 472 | buf,sizeof(buf)); | ||
| 473 | pvr2_trace(PVR2_TRACE_I2C_CMD, | ||
| 474 | "i2c COMMAND (code=%u 0x%x) to %.*s", | ||
| 475 | cmd,cmd,cnt,buf); | ||
| 476 | } | ||
| 477 | stat = pvr2_i2c_core_singleton(cp->client,cmd,arg); | ||
| 478 | if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { | ||
| 479 | char buf[100]; | ||
| 480 | unsigned int cnt; | ||
| 481 | cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, | ||
| 482 | buf,sizeof(buf)); | ||
| 483 | pvr2_trace(PVR2_TRACE_I2C_CMD, | ||
| 484 | "i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat); | ||
| 485 | } | ||
| 486 | return stat; | ||
| 487 | } | ||
| 488 | |||
| 489 | int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) | ||
| 490 | { | ||
| 491 | struct list_head *item,*nc; | ||
| 492 | struct pvr2_i2c_client *cp; | ||
| 493 | int stat = -EINVAL; | ||
| 494 | |||
| 495 | if (!hdw) return stat; | ||
| 496 | |||
| 497 | mutex_lock(&hdw->i2c_list_lock); | ||
| 498 | list_for_each_safe(item,nc,&hdw->i2c_clients) { | ||
| 499 | cp = list_entry(item,struct pvr2_i2c_client,list); | ||
| 500 | if (!cp->recv_enable) continue; | ||
| 501 | mutex_unlock(&hdw->i2c_list_lock); | ||
| 502 | stat = pvr2_i2c_client_cmd(cp,cmd,arg); | ||
| 503 | mutex_lock(&hdw->i2c_list_lock); | ||
| 504 | } | ||
| 505 | mutex_unlock(&hdw->i2c_list_lock); | ||
| 506 | return stat; | ||
| 507 | } | ||
| 508 | |||
| 509 | |||
| 510 | static int handler_check(struct pvr2_i2c_client *cp) | ||
| 511 | { | ||
| 512 | struct pvr2_i2c_handler *hp = cp->handler; | ||
| 513 | if (!hp) return 0; | ||
| 514 | if (!hp->func_table->check) return 0; | ||
| 515 | return hp->func_table->check(hp->func_data) != 0; | ||
| 516 | } | ||
| 517 | |||
| 518 | #define BUFSIZE 500 | ||
| 519 | |||
| 520 | void pvr2_i2c_core_sync(struct pvr2_hdw *hdw) | ||
| 521 | { | ||
| 522 | unsigned long msk; | ||
| 523 | unsigned int idx; | ||
| 524 | struct list_head *item,*nc; | ||
| 525 | struct pvr2_i2c_client *cp; | ||
| 526 | |||
| 527 | if (!hdw->i2c_linked) return; | ||
| 528 | if (!(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL)) { | ||
| 529 | return; | ||
| 530 | } | ||
| 531 | mutex_lock(&hdw->i2c_list_lock); do { | ||
| 532 | pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync BEGIN"); | ||
| 533 | if (hdw->i2c_pend_types & PVR2_I2C_PEND_DETECT) { | ||
| 534 | /* One or more I2C clients have attached since we | ||
| 535 | last synced. So scan the list and identify the | ||
| 536 | new clients. */ | ||
| 537 | char *buf; | ||
| 538 | unsigned int cnt; | ||
| 539 | unsigned long amask = 0; | ||
| 540 | buf = kmalloc(BUFSIZE,GFP_KERNEL); | ||
| 541 | pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_DETECT"); | ||
| 542 | hdw->i2c_pend_types &= ~PVR2_I2C_PEND_DETECT; | ||
| 543 | list_for_each(item,&hdw->i2c_clients) { | ||
| 544 | cp = list_entry(item,struct pvr2_i2c_client, | ||
| 545 | list); | ||
| 546 | if (!cp->detected_flag) { | ||
| 547 | cp->ctl_mask = 0; | ||
| 548 | pvr2_i2c_probe(hdw,cp); | ||
| 549 | cp->detected_flag = !0; | ||
| 550 | msk = cp->ctl_mask; | ||
| 551 | cnt = 0; | ||
| 552 | if (buf) { | ||
| 553 | cnt = pvr2_i2c_client_describe( | ||
| 554 | cp, | ||
| 555 | PVR2_I2C_DETAIL_ALL, | ||
| 556 | buf,BUFSIZE); | ||
| 557 | } | ||
| 558 | trace_i2c("Probed: %.*s",cnt,buf); | ||
| 559 | if (handler_check(cp)) { | ||
| 560 | hdw->i2c_pend_types |= | ||
| 561 | PVR2_I2C_PEND_CLIENT; | ||
| 562 | } | ||
| 563 | cp->pend_mask = msk; | ||
| 564 | hdw->i2c_pend_mask |= msk; | ||
| 565 | hdw->i2c_pend_types |= | ||
| 566 | PVR2_I2C_PEND_REFRESH; | ||
| 567 | } | ||
| 568 | amask |= cp->ctl_mask; | ||
| 569 | } | ||
| 570 | hdw->i2c_active_mask = amask; | ||
| 571 | if (buf) kfree(buf); | ||
| 572 | } | ||
| 573 | if (hdw->i2c_pend_types & PVR2_I2C_PEND_STALE) { | ||
| 574 | /* Need to do one or more global updates. Arrange | ||
| 575 | for this to happen. */ | ||
| 576 | unsigned long m2; | ||
| 577 | pvr2_trace(PVR2_TRACE_I2C_CORE, | ||
| 578 | "i2c: PEND_STALE (0x%lx)", | ||
| 579 | hdw->i2c_stale_mask); | ||
| 580 | hdw->i2c_pend_types &= ~PVR2_I2C_PEND_STALE; | ||
| 581 | list_for_each(item,&hdw->i2c_clients) { | ||
| 582 | cp = list_entry(item,struct pvr2_i2c_client, | ||
| 583 | list); | ||
| 584 | m2 = hdw->i2c_stale_mask; | ||
| 585 | m2 &= cp->ctl_mask; | ||
| 586 | m2 &= ~cp->pend_mask; | ||
| 587 | if (m2) { | ||
| 588 | pvr2_trace(PVR2_TRACE_I2C_CORE, | ||
| 589 | "i2c: cp=%p setting 0x%lx", | ||
| 590 | cp,m2); | ||
| 591 | cp->pend_mask |= m2; | ||
| 592 | } | ||
| 593 | } | ||
| 594 | hdw->i2c_pend_mask |= hdw->i2c_stale_mask; | ||
| 595 | hdw->i2c_stale_mask = 0; | ||
| 596 | hdw->i2c_pend_types |= PVR2_I2C_PEND_REFRESH; | ||
| 597 | } | ||
| 598 | if (hdw->i2c_pend_types & PVR2_I2C_PEND_CLIENT) { | ||
| 599 | /* One or more client handlers are asking for an | ||
| 600 | update. Run through the list of known clients | ||
| 601 | and update each one. */ | ||
| 602 | pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_CLIENT"); | ||
| 603 | hdw->i2c_pend_types &= ~PVR2_I2C_PEND_CLIENT; | ||
| 604 | list_for_each_safe(item,nc,&hdw->i2c_clients) { | ||
| 605 | cp = list_entry(item,struct pvr2_i2c_client, | ||
| 606 | list); | ||
| 607 | if (!cp->handler) continue; | ||
| 608 | if (!cp->handler->func_table->update) continue; | ||
| 609 | pvr2_trace(PVR2_TRACE_I2C_CORE, | ||
| 610 | "i2c: cp=%p update",cp); | ||
| 611 | mutex_unlock(&hdw->i2c_list_lock); | ||
| 612 | cp->handler->func_table->update( | ||
| 613 | cp->handler->func_data); | ||
| 614 | mutex_lock(&hdw->i2c_list_lock); | ||
| 615 | /* If client's update function set some | ||
| 616 | additional pending bits, account for that | ||
| 617 | here. */ | ||
| 618 | if (cp->pend_mask & ~hdw->i2c_pend_mask) { | ||
| 619 | hdw->i2c_pend_mask |= cp->pend_mask; | ||
| 620 | hdw->i2c_pend_types |= | ||
| 621 | PVR2_I2C_PEND_REFRESH; | ||
| 622 | } | ||
| 623 | } | ||
| 624 | } | ||
| 625 | if (hdw->i2c_pend_types & PVR2_I2C_PEND_REFRESH) { | ||
| 626 | const struct pvr2_i2c_op *opf; | ||
| 627 | unsigned long pm; | ||
| 628 | /* Some actual updates are pending. Walk through | ||
| 629 | each update type and perform it. */ | ||
| 630 | pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_REFRESH" | ||
| 631 | " (0x%lx)",hdw->i2c_pend_mask); | ||
| 632 | hdw->i2c_pend_types &= ~PVR2_I2C_PEND_REFRESH; | ||
| 633 | pm = hdw->i2c_pend_mask; | ||
| 634 | hdw->i2c_pend_mask = 0; | ||
| 635 | for (idx = 0, msk = 1; pm; idx++, msk <<= 1) { | ||
| 636 | if (!(pm & msk)) continue; | ||
| 637 | pm &= ~msk; | ||
| 638 | list_for_each(item,&hdw->i2c_clients) { | ||
| 639 | cp = list_entry(item, | ||
| 640 | struct pvr2_i2c_client, | ||
| 641 | list); | ||
| 642 | if (cp->pend_mask & msk) { | ||
| 643 | cp->pend_mask &= ~msk; | ||
| 644 | cp->recv_enable = !0; | ||
| 645 | } else { | ||
| 646 | cp->recv_enable = 0; | ||
| 647 | } | ||
| 648 | } | ||
| 649 | opf = pvr2_i2c_get_op(idx); | ||
| 650 | if (!opf) continue; | ||
| 651 | mutex_unlock(&hdw->i2c_list_lock); | ||
| 652 | opf->update(hdw); | ||
| 653 | mutex_lock(&hdw->i2c_list_lock); | ||
| 654 | } | ||
| 655 | } | ||
| 656 | pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync END"); | ||
| 657 | } while (0); mutex_unlock(&hdw->i2c_list_lock); | ||
| 658 | } | ||
| 659 | |||
| 660 | int pvr2_i2c_core_check_stale(struct pvr2_hdw *hdw) | ||
| 661 | { | ||
| 662 | unsigned long msk,sm,pm; | ||
| 663 | unsigned int idx; | ||
| 664 | const struct pvr2_i2c_op *opf; | ||
| 665 | struct list_head *item; | ||
| 666 | struct pvr2_i2c_client *cp; | ||
| 667 | unsigned int pt = 0; | ||
| 668 | |||
| 669 | pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale BEGIN"); | ||
| 670 | |||
| 671 | pm = hdw->i2c_active_mask; | ||
| 672 | sm = 0; | ||
| 673 | for (idx = 0, msk = 1; pm; idx++, msk <<= 1) { | ||
| 674 | if (!(msk & pm)) continue; | ||
| 675 | pm &= ~msk; | ||
| 676 | opf = pvr2_i2c_get_op(idx); | ||
| 677 | if (!opf) continue; | ||
| 678 | if (opf->check(hdw)) { | ||
| 679 | sm |= msk; | ||
| 680 | } | ||
| 681 | } | ||
| 682 | if (sm) pt |= PVR2_I2C_PEND_STALE; | ||
| 683 | |||
| 684 | list_for_each(item,&hdw->i2c_clients) { | ||
| 685 | cp = list_entry(item,struct pvr2_i2c_client,list); | ||
| 686 | if (!handler_check(cp)) continue; | ||
| 687 | pt |= PVR2_I2C_PEND_CLIENT; | ||
| 688 | } | ||
| 689 | |||
| 690 | if (pt) { | ||
| 691 | mutex_lock(&hdw->i2c_list_lock); do { | ||
| 692 | hdw->i2c_pend_types |= pt; | ||
| 693 | hdw->i2c_stale_mask |= sm; | ||
| 694 | hdw->i2c_pend_mask |= hdw->i2c_stale_mask; | ||
| 695 | } while (0); mutex_unlock(&hdw->i2c_list_lock); | ||
| 696 | } | ||
| 697 | |||
| 698 | pvr2_trace(PVR2_TRACE_I2C_CORE, | ||
| 699 | "i2c: types=0x%x stale=0x%lx pend=0x%lx", | ||
| 700 | hdw->i2c_pend_types, | ||
| 701 | hdw->i2c_stale_mask, | ||
| 702 | hdw->i2c_pend_mask); | ||
| 703 | pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale END"); | ||
| 704 | |||
| 705 | return (hdw->i2c_pend_types & PVR2_I2C_PEND_ALL) != 0; | ||
| 706 | } | ||
| 707 | |||
| 708 | unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp, | ||
| 709 | unsigned int detail, | ||
| 710 | char *buf,unsigned int maxlen) | ||
| 711 | { | ||
| 712 | unsigned int ccnt,bcnt; | ||
| 713 | int spcfl = 0; | ||
| 714 | const struct pvr2_i2c_op *opf; | ||
| 715 | |||
| 716 | ccnt = 0; | ||
| 717 | if (detail & PVR2_I2C_DETAIL_DEBUG) { | ||
| 718 | bcnt = scnprintf(buf,maxlen, | ||
| 719 | "ctxt=%p ctl_mask=0x%lx", | ||
| 720 | cp,cp->ctl_mask); | ||
| 721 | ccnt += bcnt; buf += bcnt; maxlen -= bcnt; | ||
| 722 | spcfl = !0; | ||
| 723 | } | ||
| 724 | bcnt = scnprintf(buf,maxlen, | ||
| 725 | "%s%s @ 0x%x", | ||
| 726 | (spcfl ? " " : ""), | ||
| 727 | cp->client->name, | ||
| 728 | cp->client->addr); | ||
| 729 | ccnt += bcnt; buf += bcnt; maxlen -= bcnt; | ||
| 730 | if ((detail & PVR2_I2C_DETAIL_HANDLER) && | ||
| 731 | cp->handler && cp->handler->func_table->describe) { | ||
| 732 | bcnt = scnprintf(buf,maxlen," ("); | ||
| 733 | ccnt += bcnt; buf += bcnt; maxlen -= bcnt; | ||
| 734 | bcnt = cp->handler->func_table->describe( | ||
| 735 | cp->handler->func_data,buf,maxlen); | ||
| 736 | ccnt += bcnt; buf += bcnt; maxlen -= bcnt; | ||
| 737 | bcnt = scnprintf(buf,maxlen,")"); | ||
| 738 | ccnt += bcnt; buf += bcnt; maxlen -= bcnt; | ||
| 739 | } | ||
| 740 | if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) { | ||
| 741 | unsigned int idx; | ||
| 742 | unsigned long msk,sm; | ||
| 743 | int spcfl; | ||
| 744 | bcnt = scnprintf(buf,maxlen," ["); | ||
| 745 | ccnt += bcnt; buf += bcnt; maxlen -= bcnt; | ||
| 746 | sm = 0; | ||
| 747 | spcfl = 0; | ||
| 748 | for (idx = 0, msk = 1; msk; idx++, msk <<= 1) { | ||
| 749 | if (!(cp->ctl_mask & msk)) continue; | ||
| 750 | opf = pvr2_i2c_get_op(idx); | ||
| 751 | if (opf) { | ||
| 752 | bcnt = scnprintf(buf,maxlen,"%s%s", | ||
| 753 | spcfl ? " " : "", | ||
| 754 | opf->name); | ||
| 755 | ccnt += bcnt; buf += bcnt; maxlen -= bcnt; | ||
| 756 | spcfl = !0; | ||
| 757 | } else { | ||
| 758 | sm |= msk; | ||
| 759 | } | ||
| 760 | } | ||
| 761 | if (sm) { | ||
| 762 | bcnt = scnprintf(buf,maxlen,"%s%lx", | ||
| 763 | idx != 0 ? " " : "",sm); | ||
| 764 | ccnt += bcnt; buf += bcnt; maxlen -= bcnt; | ||
| 765 | } | ||
| 766 | bcnt = scnprintf(buf,maxlen,"]"); | ||
| 767 | ccnt += bcnt; buf += bcnt; maxlen -= bcnt; | ||
| 768 | } | ||
| 769 | return ccnt; | ||
| 770 | } | ||
| 771 | |||
| 772 | unsigned int pvr2_i2c_report(struct pvr2_hdw *hdw, | ||
| 773 | char *buf,unsigned int maxlen) | ||
| 774 | { | ||
| 775 | unsigned int ccnt,bcnt; | ||
| 776 | struct list_head *item; | ||
| 777 | struct pvr2_i2c_client *cp; | ||
| 778 | ccnt = 0; | ||
| 779 | mutex_lock(&hdw->i2c_list_lock); do { | ||
| 780 | list_for_each(item,&hdw->i2c_clients) { | ||
| 781 | cp = list_entry(item,struct pvr2_i2c_client,list); | ||
| 782 | bcnt = pvr2_i2c_client_describe( | ||
| 783 | cp, | ||
| 784 | (PVR2_I2C_DETAIL_HANDLER| | ||
| 785 | PVR2_I2C_DETAIL_CTLMASK), | ||
| 786 | buf,maxlen); | ||
| 787 | ccnt += bcnt; buf += bcnt; maxlen -= bcnt; | ||
| 788 | bcnt = scnprintf(buf,maxlen,"\n"); | ||
| 789 | ccnt += bcnt; buf += bcnt; maxlen -= bcnt; | ||
| 790 | } | ||
| 791 | } while (0); mutex_unlock(&hdw->i2c_list_lock); | ||
| 792 | return ccnt; | ||
| 793 | } | ||
| 794 | |||
| 795 | static int pvr2_i2c_attach_inform(struct i2c_client *client) | ||
| 796 | { | ||
| 797 | struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); | ||
| 798 | struct pvr2_i2c_client *cp; | ||
| 799 | int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL); | ||
| 800 | cp = kmalloc(sizeof(*cp),GFP_KERNEL); | ||
| 801 | trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]", | ||
| 802 | client->name, | ||
| 803 | client->addr,cp); | ||
| 804 | if (!cp) return -ENOMEM; | ||
| 805 | memset(cp,0,sizeof(*cp)); | ||
| 806 | INIT_LIST_HEAD(&cp->list); | ||
| 807 | cp->client = client; | ||
| 808 | mutex_lock(&hdw->i2c_list_lock); do { | ||
| 809 | list_add_tail(&cp->list,&hdw->i2c_clients); | ||
| 810 | hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT; | ||
| 811 | } while (0); mutex_unlock(&hdw->i2c_list_lock); | ||
| 812 | if (fl) pvr2_hdw_poll_trigger_unlocked(hdw); | ||
| 813 | return 0; | ||
| 814 | } | ||
| 815 | |||
| 816 | static int pvr2_i2c_detach_inform(struct i2c_client *client) | ||
| 817 | { | ||
| 818 | struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); | ||
| 819 | struct pvr2_i2c_client *cp; | ||
| 820 | struct list_head *item,*nc; | ||
| 821 | unsigned long amask = 0; | ||
| 822 | int foundfl = 0; | ||
| 823 | mutex_lock(&hdw->i2c_list_lock); do { | ||
| 824 | list_for_each_safe(item,nc,&hdw->i2c_clients) { | ||
| 825 | cp = list_entry(item,struct pvr2_i2c_client,list); | ||
| 826 | if (cp->client == client) { | ||
| 827 | trace_i2c("pvr2_i2c_detach" | ||
| 828 | " [client=%s @ 0x%x ctxt=%p]", | ||
| 829 | client->name, | ||
| 830 | client->addr,cp); | ||
| 831 | if (cp->handler && | ||
| 832 | cp->handler->func_table->detach) { | ||
| 833 | cp->handler->func_table->detach( | ||
| 834 | cp->handler->func_data); | ||
| 835 | } | ||
| 836 | list_del(&cp->list); | ||
| 837 | kfree(cp); | ||
| 838 | foundfl = !0; | ||
| 839 | continue; | ||
| 840 | } | ||
| 841 | amask |= cp->ctl_mask; | ||
| 842 | } | ||
| 843 | hdw->i2c_active_mask = amask; | ||
| 844 | } while (0); mutex_unlock(&hdw->i2c_list_lock); | ||
| 845 | if (!foundfl) { | ||
| 846 | trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x ctxt=<unknown>]", | ||
| 847 | client->name, | ||
| 848 | client->addr); | ||
| 849 | } | ||
| 850 | return 0; | ||
| 851 | } | ||
| 852 | |||
| 853 | static struct i2c_algorithm pvr2_i2c_algo_template = { | ||
| 854 | .master_xfer = pvr2_i2c_xfer, | ||
| 855 | .algo_control = pvr2_i2c_control, | ||
| 856 | .functionality = pvr2_i2c_functionality, | ||
| 857 | }; | ||
| 858 | |||
| 859 | static struct i2c_adapter pvr2_i2c_adap_template = { | ||
| 860 | .owner = THIS_MODULE, | ||
| 861 | .class = I2C_CLASS_TV_ANALOG, | ||
| 862 | .id = I2C_HW_B_BT848, | ||
| 863 | .client_register = pvr2_i2c_attach_inform, | ||
| 864 | .client_unregister = pvr2_i2c_detach_inform, | ||
| 865 | }; | ||
| 866 | |||
| 867 | static void do_i2c_scan(struct pvr2_hdw *hdw) | ||
| 868 | { | ||
| 869 | struct i2c_msg msg[1]; | ||
| 870 | int i,rc; | ||
| 871 | msg[0].addr = 0; | ||
| 872 | msg[0].flags = I2C_M_RD; | ||
| 873 | msg[0].len = 0; | ||
| 874 | msg[0].buf = 0; | ||
| 875 | printk("%s: i2c scan beginning\n",hdw->name); | ||
| 876 | for (i = 0; i < 128; i++) { | ||
| 877 | msg[0].addr = i; | ||
| 878 | rc = i2c_transfer(&hdw->i2c_adap,msg, | ||
| 879 | sizeof(msg)/sizeof(msg[0])); | ||
| 880 | if (rc != 1) continue; | ||
| 881 | printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i); | ||
| 882 | } | ||
| 883 | printk("%s: i2c scan done.\n",hdw->name); | ||
| 884 | } | ||
| 885 | |||
| 886 | void pvr2_i2c_core_init(struct pvr2_hdw *hdw) | ||
| 887 | { | ||
| 888 | unsigned int idx; | ||
| 889 | |||
| 890 | // The default action for all possible I2C addresses is just to do | ||
| 891 | // the transfer normally. | ||
| 892 | for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) { | ||
| 893 | hdw->i2c_func[idx] = pvr2_i2c_basic_op; | ||
| 894 | } | ||
| 895 | |||
| 896 | #ifdef CONFIG_VIDEO_PVRUSB2_24XXX | ||
| 897 | // If however we're dealing with new hardware, insert some hacks in | ||
| 898 | // the I2C transfer stack to let things work better. | ||
| 899 | if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { | ||
| 900 | hdw->i2c_func[0x1b] = i2c_hack_wm8775; | ||
| 901 | hdw->i2c_func[0x44] = i2c_hack_cx25840; | ||
| 902 | } | ||
| 903 | #endif | ||
| 904 | |||
| 905 | // Configure the adapter and set up everything else related to it. | ||
| 906 | memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap)); | ||
| 907 | memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo)); | ||
| 908 | strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name)); | ||
| 909 | hdw->i2c_adap.algo = &hdw->i2c_algo; | ||
| 910 | hdw->i2c_adap.algo_data = hdw; | ||
| 911 | hdw->i2c_pend_mask = 0; | ||
| 912 | hdw->i2c_stale_mask = 0; | ||
| 913 | hdw->i2c_active_mask = 0; | ||
| 914 | INIT_LIST_HEAD(&hdw->i2c_clients); | ||
| 915 | mutex_init(&hdw->i2c_list_lock); | ||
| 916 | hdw->i2c_linked = !0; | ||
| 917 | i2c_add_adapter(&hdw->i2c_adap); | ||
| 918 | if (i2c_scan) do_i2c_scan(hdw); | ||
| 919 | } | ||
| 920 | |||
| 921 | void pvr2_i2c_core_done(struct pvr2_hdw *hdw) | ||
| 922 | { | ||
| 923 | if (hdw->i2c_linked) { | ||
| 924 | i2c_del_adapter(&hdw->i2c_adap); | ||
| 925 | hdw->i2c_linked = 0; | ||
| 926 | } | ||
| 927 | } | ||
| 928 | |||
| 929 | /* | ||
| 930 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 931 | *** Local Variables: *** | ||
| 932 | *** mode: c *** | ||
| 933 | *** fill-column: 75 *** | ||
| 934 | *** tab-width: 8 *** | ||
| 935 | *** c-basic-offset: 8 *** | ||
| 936 | *** End: *** | ||
| 937 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h new file mode 100644 index 000000000000..e8af5b0ed3ce --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_I2C_CORE_H | ||
| 22 | #define __PVRUSB2_I2C_CORE_H | ||
| 23 | |||
| 24 | #include <linux/list.h> | ||
| 25 | #include <linux/i2c.h> | ||
| 26 | |||
| 27 | struct pvr2_hdw; | ||
| 28 | struct pvr2_i2c_client; | ||
| 29 | struct pvr2_i2c_handler; | ||
| 30 | struct pvr2_i2c_handler_functions; | ||
| 31 | struct pvr2_i2c_op; | ||
| 32 | struct pvr2_i2c_op_functions; | ||
| 33 | |||
| 34 | struct pvr2_i2c_client { | ||
| 35 | struct i2c_client *client; | ||
| 36 | struct pvr2_i2c_handler *handler; | ||
| 37 | struct list_head list; | ||
| 38 | int detected_flag; | ||
| 39 | int recv_enable; | ||
| 40 | unsigned long pend_mask; | ||
| 41 | unsigned long ctl_mask; | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct pvr2_i2c_handler { | ||
| 45 | void *func_data; | ||
| 46 | const struct pvr2_i2c_handler_functions *func_table; | ||
| 47 | }; | ||
| 48 | |||
| 49 | struct pvr2_i2c_handler_functions { | ||
| 50 | void (*detach)(void *); | ||
| 51 | int (*check)(void *); | ||
| 52 | void (*update)(void *); | ||
| 53 | unsigned int (*describe)(void *,char *,unsigned int); | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct pvr2_i2c_op { | ||
| 57 | int (*check)(struct pvr2_hdw *); | ||
| 58 | void (*update)(struct pvr2_hdw *); | ||
| 59 | const char *name; | ||
| 60 | }; | ||
| 61 | |||
| 62 | void pvr2_i2c_core_init(struct pvr2_hdw *); | ||
| 63 | void pvr2_i2c_core_done(struct pvr2_hdw *); | ||
| 64 | |||
| 65 | int pvr2_i2c_client_cmd(struct pvr2_i2c_client *,unsigned int cmd,void *arg); | ||
| 66 | int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg); | ||
| 67 | |||
| 68 | int pvr2_i2c_core_check_stale(struct pvr2_hdw *); | ||
| 69 | void pvr2_i2c_core_sync(struct pvr2_hdw *); | ||
| 70 | unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen); | ||
| 71 | #define PVR2_I2C_DETAIL_DEBUG 0x0001 | ||
| 72 | #define PVR2_I2C_DETAIL_HANDLER 0x0002 | ||
| 73 | #define PVR2_I2C_DETAIL_CTLMASK 0x0004 | ||
| 74 | #define PVR2_I2C_DETAIL_ALL (\ | ||
| 75 | PVR2_I2C_DETAIL_DEBUG |\ | ||
| 76 | PVR2_I2C_DETAIL_HANDLER |\ | ||
| 77 | PVR2_I2C_DETAIL_CTLMASK) | ||
| 78 | unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *, | ||
| 79 | unsigned int detail_mask, | ||
| 80 | char *buf,unsigned int maxlen); | ||
| 81 | |||
| 82 | void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *); | ||
| 83 | const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx); | ||
| 84 | |||
| 85 | #endif /* __PVRUSB2_I2C_CORE_H */ | ||
| 86 | |||
| 87 | |||
| 88 | /* | ||
| 89 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 90 | *** Local Variables: *** | ||
| 91 | *** mode: c *** | ||
| 92 | *** fill-column: 75 *** | ||
| 93 | *** tab-width: 8 *** | ||
| 94 | *** c-basic-offset: 8 *** | ||
| 95 | *** End: *** | ||
| 96 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.c b/drivers/media/video/pvrusb2/pvrusb2-io.c new file mode 100644 index 000000000000..a984c91f571c --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-io.c | |||
| @@ -0,0 +1,695 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "pvrusb2-io.h" | ||
| 23 | #include "pvrusb2-debug.h" | ||
| 24 | #include <linux/errno.h> | ||
| 25 | #include <linux/string.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/mutex.h> | ||
| 28 | |||
| 29 | #define BUFFER_SIG 0x47653271 | ||
| 30 | |||
| 31 | // #define SANITY_CHECK_BUFFERS | ||
| 32 | |||
| 33 | |||
| 34 | #ifdef SANITY_CHECK_BUFFERS | ||
| 35 | #define BUFFER_CHECK(bp) do { \ | ||
| 36 | if ((bp)->signature != BUFFER_SIG) { \ | ||
| 37 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, \ | ||
| 38 | "Buffer %p is bad at %s:%d", \ | ||
| 39 | (bp),__FILE__,__LINE__); \ | ||
| 40 | pvr2_buffer_describe(bp,"BadSig"); \ | ||
| 41 | BUG(); \ | ||
| 42 | } \ | ||
| 43 | } while (0) | ||
| 44 | #else | ||
| 45 | #define BUFFER_CHECK(bp) do {} while(0) | ||
| 46 | #endif | ||
| 47 | |||
| 48 | struct pvr2_stream { | ||
| 49 | /* Buffers queued for reading */ | ||
| 50 | struct list_head queued_list; | ||
| 51 | unsigned int q_count; | ||
| 52 | unsigned int q_bcount; | ||
| 53 | /* Buffers with retrieved data */ | ||
| 54 | struct list_head ready_list; | ||
| 55 | unsigned int r_count; | ||
| 56 | unsigned int r_bcount; | ||
| 57 | /* Buffers available for use */ | ||
| 58 | struct list_head idle_list; | ||
| 59 | unsigned int i_count; | ||
| 60 | unsigned int i_bcount; | ||
| 61 | /* Pointers to all buffers */ | ||
| 62 | struct pvr2_buffer **buffers; | ||
| 63 | /* Array size of buffers */ | ||
| 64 | unsigned int buffer_slot_count; | ||
| 65 | /* Total buffers actually in circulation */ | ||
| 66 | unsigned int buffer_total_count; | ||
| 67 | /* Designed number of buffers to be in circulation */ | ||
| 68 | unsigned int buffer_target_count; | ||
| 69 | /* Executed when ready list become non-empty */ | ||
| 70 | pvr2_stream_callback callback_func; | ||
| 71 | void *callback_data; | ||
| 72 | /* Context for transfer endpoint */ | ||
| 73 | struct usb_device *dev; | ||
| 74 | int endpoint; | ||
| 75 | /* Overhead for mutex enforcement */ | ||
| 76 | spinlock_t list_lock; | ||
| 77 | struct mutex mutex; | ||
| 78 | /* Tracking state for tolerating errors */ | ||
| 79 | unsigned int fail_count; | ||
| 80 | unsigned int fail_tolerance; | ||
| 81 | }; | ||
| 82 | |||
| 83 | struct pvr2_buffer { | ||
| 84 | int id; | ||
| 85 | int signature; | ||
| 86 | enum pvr2_buffer_state state; | ||
| 87 | void *ptr; /* Pointer to storage area */ | ||
| 88 | unsigned int max_count; /* Size of storage area */ | ||
| 89 | unsigned int used_count; /* Amount of valid data in storage area */ | ||
| 90 | int status; /* Transfer result status */ | ||
| 91 | struct pvr2_stream *stream; | ||
| 92 | struct list_head list_overhead; | ||
| 93 | struct urb *purb; | ||
| 94 | }; | ||
| 95 | |||
| 96 | const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st) | ||
| 97 | { | ||
| 98 | switch (st) { | ||
| 99 | case pvr2_buffer_state_none: return "none"; | ||
| 100 | case pvr2_buffer_state_idle: return "idle"; | ||
| 101 | case pvr2_buffer_state_queued: return "queued"; | ||
| 102 | case pvr2_buffer_state_ready: return "ready"; | ||
| 103 | } | ||
| 104 | return "unknown"; | ||
| 105 | } | ||
| 106 | |||
| 107 | void pvr2_buffer_describe(struct pvr2_buffer *bp,const char *msg) | ||
| 108 | { | ||
| 109 | pvr2_trace(PVR2_TRACE_INFO, | ||
| 110 | "buffer%s%s %p state=%s id=%d status=%d" | ||
| 111 | " stream=%p purb=%p sig=0x%x", | ||
| 112 | (msg ? " " : ""), | ||
| 113 | (msg ? msg : ""), | ||
| 114 | bp, | ||
| 115 | (bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"), | ||
| 116 | (bp ? bp->id : 0), | ||
| 117 | (bp ? bp->status : 0), | ||
| 118 | (bp ? bp->stream : 0), | ||
| 119 | (bp ? bp->purb : 0), | ||
| 120 | (bp ? bp->signature : 0)); | ||
| 121 | } | ||
| 122 | |||
| 123 | static void pvr2_buffer_remove(struct pvr2_buffer *bp) | ||
| 124 | { | ||
| 125 | unsigned int *cnt; | ||
| 126 | unsigned int *bcnt; | ||
| 127 | unsigned int ccnt; | ||
| 128 | struct pvr2_stream *sp = bp->stream; | ||
| 129 | switch (bp->state) { | ||
| 130 | case pvr2_buffer_state_idle: | ||
| 131 | cnt = &sp->i_count; | ||
| 132 | bcnt = &sp->i_bcount; | ||
| 133 | ccnt = bp->max_count; | ||
| 134 | break; | ||
| 135 | case pvr2_buffer_state_queued: | ||
| 136 | cnt = &sp->q_count; | ||
| 137 | bcnt = &sp->q_bcount; | ||
| 138 | ccnt = bp->max_count; | ||
| 139 | break; | ||
| 140 | case pvr2_buffer_state_ready: | ||
| 141 | cnt = &sp->r_count; | ||
| 142 | bcnt = &sp->r_bcount; | ||
| 143 | ccnt = bp->used_count; | ||
| 144 | break; | ||
| 145 | default: | ||
| 146 | return; | ||
| 147 | } | ||
| 148 | list_del_init(&bp->list_overhead); | ||
| 149 | (*cnt)--; | ||
| 150 | (*bcnt) -= ccnt; | ||
| 151 | pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||
| 152 | "/*---TRACE_FLOW---*/" | ||
| 153 | " bufferPool %8s dec cap=%07d cnt=%02d", | ||
| 154 | pvr2_buffer_state_decode(bp->state),*bcnt,*cnt); | ||
| 155 | bp->state = pvr2_buffer_state_none; | ||
| 156 | } | ||
| 157 | |||
| 158 | static void pvr2_buffer_set_none(struct pvr2_buffer *bp) | ||
| 159 | { | ||
| 160 | unsigned long irq_flags; | ||
| 161 | struct pvr2_stream *sp; | ||
| 162 | BUFFER_CHECK(bp); | ||
| 163 | sp = bp->stream; | ||
| 164 | pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||
| 165 | "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", | ||
| 166 | bp, | ||
| 167 | pvr2_buffer_state_decode(bp->state), | ||
| 168 | pvr2_buffer_state_decode(pvr2_buffer_state_none)); | ||
| 169 | spin_lock_irqsave(&sp->list_lock,irq_flags); | ||
| 170 | pvr2_buffer_remove(bp); | ||
| 171 | spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||
| 172 | } | ||
| 173 | |||
| 174 | static int pvr2_buffer_set_ready(struct pvr2_buffer *bp) | ||
| 175 | { | ||
| 176 | int fl; | ||
| 177 | unsigned long irq_flags; | ||
| 178 | struct pvr2_stream *sp; | ||
| 179 | BUFFER_CHECK(bp); | ||
| 180 | sp = bp->stream; | ||
| 181 | pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||
| 182 | "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", | ||
| 183 | bp, | ||
| 184 | pvr2_buffer_state_decode(bp->state), | ||
| 185 | pvr2_buffer_state_decode(pvr2_buffer_state_ready)); | ||
| 186 | spin_lock_irqsave(&sp->list_lock,irq_flags); | ||
| 187 | fl = (sp->r_count == 0); | ||
| 188 | pvr2_buffer_remove(bp); | ||
| 189 | list_add_tail(&bp->list_overhead,&sp->ready_list); | ||
| 190 | bp->state = pvr2_buffer_state_ready; | ||
| 191 | (sp->r_count)++; | ||
| 192 | sp->r_bcount += bp->used_count; | ||
| 193 | pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||
| 194 | "/*---TRACE_FLOW---*/" | ||
| 195 | " bufferPool %8s inc cap=%07d cnt=%02d", | ||
| 196 | pvr2_buffer_state_decode(bp->state), | ||
| 197 | sp->r_bcount,sp->r_count); | ||
| 198 | spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||
| 199 | return fl; | ||
| 200 | } | ||
| 201 | |||
| 202 | static void pvr2_buffer_set_idle(struct pvr2_buffer *bp) | ||
| 203 | { | ||
| 204 | unsigned long irq_flags; | ||
| 205 | struct pvr2_stream *sp; | ||
| 206 | BUFFER_CHECK(bp); | ||
| 207 | sp = bp->stream; | ||
| 208 | pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||
| 209 | "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", | ||
| 210 | bp, | ||
| 211 | pvr2_buffer_state_decode(bp->state), | ||
| 212 | pvr2_buffer_state_decode(pvr2_buffer_state_idle)); | ||
| 213 | spin_lock_irqsave(&sp->list_lock,irq_flags); | ||
| 214 | pvr2_buffer_remove(bp); | ||
| 215 | list_add_tail(&bp->list_overhead,&sp->idle_list); | ||
| 216 | bp->state = pvr2_buffer_state_idle; | ||
| 217 | (sp->i_count)++; | ||
| 218 | sp->i_bcount += bp->max_count; | ||
| 219 | pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||
| 220 | "/*---TRACE_FLOW---*/" | ||
| 221 | " bufferPool %8s inc cap=%07d cnt=%02d", | ||
| 222 | pvr2_buffer_state_decode(bp->state), | ||
| 223 | sp->i_bcount,sp->i_count); | ||
| 224 | spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||
| 225 | } | ||
| 226 | |||
| 227 | static void pvr2_buffer_set_queued(struct pvr2_buffer *bp) | ||
| 228 | { | ||
| 229 | unsigned long irq_flags; | ||
| 230 | struct pvr2_stream *sp; | ||
| 231 | BUFFER_CHECK(bp); | ||
| 232 | sp = bp->stream; | ||
| 233 | pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||
| 234 | "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", | ||
| 235 | bp, | ||
| 236 | pvr2_buffer_state_decode(bp->state), | ||
| 237 | pvr2_buffer_state_decode(pvr2_buffer_state_queued)); | ||
| 238 | spin_lock_irqsave(&sp->list_lock,irq_flags); | ||
| 239 | pvr2_buffer_remove(bp); | ||
| 240 | list_add_tail(&bp->list_overhead,&sp->queued_list); | ||
| 241 | bp->state = pvr2_buffer_state_queued; | ||
| 242 | (sp->q_count)++; | ||
| 243 | sp->q_bcount += bp->max_count; | ||
| 244 | pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||
| 245 | "/*---TRACE_FLOW---*/" | ||
| 246 | " bufferPool %8s inc cap=%07d cnt=%02d", | ||
| 247 | pvr2_buffer_state_decode(bp->state), | ||
| 248 | sp->q_bcount,sp->q_count); | ||
| 249 | spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||
| 250 | } | ||
| 251 | |||
| 252 | static void pvr2_buffer_wipe(struct pvr2_buffer *bp) | ||
| 253 | { | ||
| 254 | if (bp->state == pvr2_buffer_state_queued) { | ||
| 255 | usb_kill_urb(bp->purb); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | static int pvr2_buffer_init(struct pvr2_buffer *bp, | ||
| 260 | struct pvr2_stream *sp, | ||
| 261 | unsigned int id) | ||
| 262 | { | ||
| 263 | memset(bp,0,sizeof(*bp)); | ||
| 264 | bp->signature = BUFFER_SIG; | ||
| 265 | bp->id = id; | ||
| 266 | pvr2_trace(PVR2_TRACE_BUF_POOL, | ||
| 267 | "/*---TRACE_FLOW---*/ bufferInit %p stream=%p",bp,sp); | ||
| 268 | bp->stream = sp; | ||
| 269 | bp->state = pvr2_buffer_state_none; | ||
| 270 | INIT_LIST_HEAD(&bp->list_overhead); | ||
| 271 | bp->purb = usb_alloc_urb(0,GFP_KERNEL); | ||
| 272 | if (! bp->purb) return -ENOMEM; | ||
| 273 | #ifdef SANITY_CHECK_BUFFERS | ||
| 274 | pvr2_buffer_describe(bp,"create"); | ||
| 275 | #endif | ||
| 276 | return 0; | ||
| 277 | } | ||
| 278 | |||
| 279 | static void pvr2_buffer_done(struct pvr2_buffer *bp) | ||
| 280 | { | ||
| 281 | #ifdef SANITY_CHECK_BUFFERS | ||
| 282 | pvr2_buffer_describe(bp,"delete"); | ||
| 283 | #endif | ||
| 284 | pvr2_buffer_wipe(bp); | ||
| 285 | pvr2_buffer_set_none(bp); | ||
| 286 | bp->signature = 0; | ||
| 287 | bp->stream = 0; | ||
| 288 | if (bp->purb) usb_free_urb(bp->purb); | ||
| 289 | pvr2_trace(PVR2_TRACE_BUF_POOL,"/*---TRACE_FLOW---*/" | ||
| 290 | " bufferDone %p",bp); | ||
| 291 | } | ||
| 292 | |||
| 293 | static int pvr2_stream_buffer_count(struct pvr2_stream *sp,unsigned int cnt) | ||
| 294 | { | ||
| 295 | int ret; | ||
| 296 | unsigned int scnt; | ||
| 297 | |||
| 298 | /* Allocate buffers pointer array in multiples of 32 entries */ | ||
| 299 | if (cnt == sp->buffer_total_count) return 0; | ||
| 300 | |||
| 301 | pvr2_trace(PVR2_TRACE_BUF_POOL, | ||
| 302 | "/*---TRACE_FLOW---*/ poolResize " | ||
| 303 | " stream=%p cur=%d adj=%+d", | ||
| 304 | sp, | ||
| 305 | sp->buffer_total_count, | ||
| 306 | cnt-sp->buffer_total_count); | ||
| 307 | |||
| 308 | scnt = cnt & ~0x1f; | ||
| 309 | if (cnt > scnt) scnt += 0x20; | ||
| 310 | |||
| 311 | if (cnt > sp->buffer_total_count) { | ||
| 312 | if (scnt > sp->buffer_slot_count) { | ||
| 313 | struct pvr2_buffer **nb; | ||
| 314 | nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL); | ||
| 315 | if (!nb) return -ENOMEM; | ||
| 316 | if (sp->buffer_slot_count) { | ||
| 317 | memcpy(nb,sp->buffers, | ||
| 318 | sp->buffer_slot_count * sizeof(*nb)); | ||
| 319 | kfree(sp->buffers); | ||
| 320 | } | ||
| 321 | sp->buffers = nb; | ||
| 322 | sp->buffer_slot_count = scnt; | ||
| 323 | } | ||
| 324 | while (sp->buffer_total_count < cnt) { | ||
| 325 | struct pvr2_buffer *bp; | ||
| 326 | bp = kmalloc(sizeof(*bp),GFP_KERNEL); | ||
| 327 | if (!bp) return -ENOMEM; | ||
| 328 | ret = pvr2_buffer_init(bp,sp,sp->buffer_total_count); | ||
| 329 | if (ret) { | ||
| 330 | kfree(bp); | ||
| 331 | return -ENOMEM; | ||
| 332 | } | ||
| 333 | sp->buffers[sp->buffer_total_count] = bp; | ||
| 334 | (sp->buffer_total_count)++; | ||
| 335 | pvr2_buffer_set_idle(bp); | ||
| 336 | } | ||
| 337 | } else { | ||
| 338 | while (sp->buffer_total_count > cnt) { | ||
| 339 | struct pvr2_buffer *bp; | ||
| 340 | bp = sp->buffers[sp->buffer_total_count - 1]; | ||
| 341 | /* Paranoia */ | ||
| 342 | sp->buffers[sp->buffer_total_count - 1] = 0; | ||
| 343 | (sp->buffer_total_count)--; | ||
| 344 | pvr2_buffer_done(bp); | ||
| 345 | kfree(bp); | ||
| 346 | } | ||
| 347 | if (scnt < sp->buffer_slot_count) { | ||
| 348 | struct pvr2_buffer **nb = 0; | ||
| 349 | if (scnt) { | ||
| 350 | nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL); | ||
| 351 | if (!nb) return -ENOMEM; | ||
| 352 | memcpy(nb,sp->buffers,scnt * sizeof(*nb)); | ||
| 353 | } | ||
| 354 | kfree(sp->buffers); | ||
| 355 | sp->buffers = nb; | ||
| 356 | sp->buffer_slot_count = scnt; | ||
| 357 | } | ||
| 358 | } | ||
| 359 | return 0; | ||
| 360 | } | ||
| 361 | |||
| 362 | static int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp) | ||
| 363 | { | ||
| 364 | struct pvr2_buffer *bp; | ||
| 365 | unsigned int cnt; | ||
| 366 | |||
| 367 | if (sp->buffer_total_count == sp->buffer_target_count) return 0; | ||
| 368 | |||
| 369 | pvr2_trace(PVR2_TRACE_BUF_POOL, | ||
| 370 | "/*---TRACE_FLOW---*/" | ||
| 371 | " poolCheck stream=%p cur=%d tgt=%d", | ||
| 372 | sp,sp->buffer_total_count,sp->buffer_target_count); | ||
| 373 | |||
| 374 | if (sp->buffer_total_count < sp->buffer_target_count) { | ||
| 375 | return pvr2_stream_buffer_count(sp,sp->buffer_target_count); | ||
| 376 | } | ||
| 377 | |||
| 378 | cnt = 0; | ||
| 379 | while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) { | ||
| 380 | bp = sp->buffers[sp->buffer_total_count - (cnt + 1)]; | ||
| 381 | if (bp->state != pvr2_buffer_state_idle) break; | ||
| 382 | cnt++; | ||
| 383 | } | ||
| 384 | if (cnt) { | ||
| 385 | pvr2_stream_buffer_count(sp,sp->buffer_total_count - cnt); | ||
| 386 | } | ||
| 387 | |||
| 388 | return 0; | ||
| 389 | } | ||
| 390 | |||
| 391 | static void pvr2_stream_internal_flush(struct pvr2_stream *sp) | ||
| 392 | { | ||
| 393 | struct list_head *lp; | ||
| 394 | struct pvr2_buffer *bp1; | ||
| 395 | while ((lp = sp->queued_list.next) != &sp->queued_list) { | ||
| 396 | bp1 = list_entry(lp,struct pvr2_buffer,list_overhead); | ||
| 397 | pvr2_buffer_wipe(bp1); | ||
| 398 | /* At this point, we should be guaranteed that no | ||
| 399 | completion callback may happen on this buffer. But it's | ||
| 400 | possible that it might have completed after we noticed | ||
| 401 | it but before we wiped it. So double check its status | ||
| 402 | here first. */ | ||
| 403 | if (bp1->state != pvr2_buffer_state_queued) continue; | ||
| 404 | pvr2_buffer_set_idle(bp1); | ||
| 405 | } | ||
| 406 | if (sp->buffer_total_count != sp->buffer_target_count) { | ||
| 407 | pvr2_stream_achieve_buffer_count(sp); | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | static void pvr2_stream_init(struct pvr2_stream *sp) | ||
| 412 | { | ||
| 413 | spin_lock_init(&sp->list_lock); | ||
| 414 | mutex_init(&sp->mutex); | ||
| 415 | INIT_LIST_HEAD(&sp->queued_list); | ||
| 416 | INIT_LIST_HEAD(&sp->ready_list); | ||
| 417 | INIT_LIST_HEAD(&sp->idle_list); | ||
| 418 | } | ||
| 419 | |||
| 420 | static void pvr2_stream_done(struct pvr2_stream *sp) | ||
| 421 | { | ||
| 422 | mutex_lock(&sp->mutex); do { | ||
| 423 | pvr2_stream_internal_flush(sp); | ||
| 424 | pvr2_stream_buffer_count(sp,0); | ||
| 425 | } while (0); mutex_unlock(&sp->mutex); | ||
| 426 | } | ||
| 427 | |||
| 428 | static void buffer_complete(struct urb *urb, struct pt_regs *regs) | ||
| 429 | { | ||
| 430 | struct pvr2_buffer *bp = urb->context; | ||
| 431 | struct pvr2_stream *sp; | ||
| 432 | unsigned long irq_flags; | ||
| 433 | BUFFER_CHECK(bp); | ||
| 434 | sp = bp->stream; | ||
| 435 | bp->used_count = 0; | ||
| 436 | bp->status = 0; | ||
| 437 | pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||
| 438 | "/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d", | ||
| 439 | bp,urb->status,urb->actual_length); | ||
| 440 | spin_lock_irqsave(&sp->list_lock,irq_flags); | ||
| 441 | if ((!(urb->status)) || | ||
| 442 | (urb->status == -ENOENT) || | ||
| 443 | (urb->status == -ECONNRESET) || | ||
| 444 | (urb->status == -ESHUTDOWN)) { | ||
| 445 | bp->used_count = urb->actual_length; | ||
| 446 | if (sp->fail_count) { | ||
| 447 | pvr2_trace(PVR2_TRACE_TOLERANCE, | ||
| 448 | "stream %p transfer ok" | ||
| 449 | " - fail count reset",sp); | ||
| 450 | sp->fail_count = 0; | ||
| 451 | } | ||
| 452 | } else if (sp->fail_count < sp->fail_tolerance) { | ||
| 453 | // We can tolerate this error, because we're below the | ||
| 454 | // threshold... | ||
| 455 | (sp->fail_count)++; | ||
| 456 | pvr2_trace(PVR2_TRACE_TOLERANCE, | ||
| 457 | "stream %p ignoring error %d" | ||
| 458 | " - fail count increased to %u", | ||
| 459 | sp,urb->status,sp->fail_count); | ||
| 460 | } else { | ||
| 461 | bp->status = urb->status; | ||
| 462 | } | ||
| 463 | spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||
| 464 | pvr2_buffer_set_ready(bp); | ||
| 465 | if (sp && sp->callback_func) { | ||
| 466 | sp->callback_func(sp->callback_data); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | struct pvr2_stream *pvr2_stream_create(void) | ||
| 471 | { | ||
| 472 | struct pvr2_stream *sp; | ||
| 473 | sp = kmalloc(sizeof(*sp),GFP_KERNEL); | ||
| 474 | if (!sp) return sp; | ||
| 475 | memset(sp,0,sizeof(*sp)); | ||
| 476 | pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_create: sp=%p",sp); | ||
| 477 | pvr2_stream_init(sp); | ||
| 478 | return sp; | ||
| 479 | } | ||
| 480 | |||
| 481 | void pvr2_stream_destroy(struct pvr2_stream *sp) | ||
| 482 | { | ||
| 483 | if (!sp) return; | ||
| 484 | pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_destroy: sp=%p",sp); | ||
| 485 | pvr2_stream_done(sp); | ||
| 486 | kfree(sp); | ||
| 487 | } | ||
| 488 | |||
| 489 | void pvr2_stream_setup(struct pvr2_stream *sp, | ||
| 490 | struct usb_device *dev, | ||
| 491 | int endpoint, | ||
| 492 | unsigned int tolerance) | ||
| 493 | { | ||
| 494 | mutex_lock(&sp->mutex); do { | ||
| 495 | pvr2_stream_internal_flush(sp); | ||
| 496 | sp->dev = dev; | ||
| 497 | sp->endpoint = endpoint; | ||
| 498 | sp->fail_tolerance = tolerance; | ||
| 499 | } while(0); mutex_unlock(&sp->mutex); | ||
| 500 | } | ||
| 501 | |||
| 502 | void pvr2_stream_set_callback(struct pvr2_stream *sp, | ||
| 503 | pvr2_stream_callback func, | ||
| 504 | void *data) | ||
| 505 | { | ||
| 506 | unsigned long irq_flags; | ||
| 507 | mutex_lock(&sp->mutex); do { | ||
| 508 | spin_lock_irqsave(&sp->list_lock,irq_flags); | ||
| 509 | sp->callback_data = data; | ||
| 510 | sp->callback_func = func; | ||
| 511 | spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||
| 512 | } while(0); mutex_unlock(&sp->mutex); | ||
| 513 | } | ||
| 514 | |||
| 515 | /* Query / set the nominal buffer count */ | ||
| 516 | int pvr2_stream_get_buffer_count(struct pvr2_stream *sp) | ||
| 517 | { | ||
| 518 | return sp->buffer_target_count; | ||
| 519 | } | ||
| 520 | |||
| 521 | int pvr2_stream_set_buffer_count(struct pvr2_stream *sp,unsigned int cnt) | ||
| 522 | { | ||
| 523 | int ret; | ||
| 524 | if (sp->buffer_target_count == cnt) return 0; | ||
| 525 | mutex_lock(&sp->mutex); do { | ||
| 526 | sp->buffer_target_count = cnt; | ||
| 527 | ret = pvr2_stream_achieve_buffer_count(sp); | ||
| 528 | } while(0); mutex_unlock(&sp->mutex); | ||
| 529 | return ret; | ||
| 530 | } | ||
| 531 | |||
| 532 | struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp) | ||
| 533 | { | ||
| 534 | struct list_head *lp = sp->idle_list.next; | ||
| 535 | if (lp == &sp->idle_list) return 0; | ||
| 536 | return list_entry(lp,struct pvr2_buffer,list_overhead); | ||
| 537 | } | ||
| 538 | |||
| 539 | struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp) | ||
| 540 | { | ||
| 541 | struct list_head *lp = sp->ready_list.next; | ||
| 542 | if (lp == &sp->ready_list) return 0; | ||
| 543 | return list_entry(lp,struct pvr2_buffer,list_overhead); | ||
| 544 | } | ||
| 545 | |||
| 546 | struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id) | ||
| 547 | { | ||
| 548 | if (id < 0) return 0; | ||
| 549 | if (id >= sp->buffer_total_count) return 0; | ||
| 550 | return sp->buffers[id]; | ||
| 551 | } | ||
| 552 | |||
| 553 | int pvr2_stream_get_ready_count(struct pvr2_stream *sp) | ||
| 554 | { | ||
| 555 | return sp->r_count; | ||
| 556 | } | ||
| 557 | |||
| 558 | int pvr2_stream_get_idle_count(struct pvr2_stream *sp) | ||
| 559 | { | ||
| 560 | return sp->i_count; | ||
| 561 | } | ||
| 562 | |||
| 563 | void pvr2_stream_flush(struct pvr2_stream *sp) | ||
| 564 | { | ||
| 565 | mutex_lock(&sp->mutex); do { | ||
| 566 | pvr2_stream_internal_flush(sp); | ||
| 567 | } while(0); mutex_unlock(&sp->mutex); | ||
| 568 | } | ||
| 569 | |||
| 570 | void pvr2_stream_kill(struct pvr2_stream *sp) | ||
| 571 | { | ||
| 572 | struct pvr2_buffer *bp; | ||
| 573 | mutex_lock(&sp->mutex); do { | ||
| 574 | pvr2_stream_internal_flush(sp); | ||
| 575 | while ((bp = pvr2_stream_get_ready_buffer(sp)) != 0) { | ||
| 576 | pvr2_buffer_set_idle(bp); | ||
| 577 | } | ||
| 578 | if (sp->buffer_total_count != sp->buffer_target_count) { | ||
| 579 | pvr2_stream_achieve_buffer_count(sp); | ||
| 580 | } | ||
| 581 | } while(0); mutex_unlock(&sp->mutex); | ||
| 582 | } | ||
| 583 | |||
| 584 | int pvr2_buffer_queue(struct pvr2_buffer *bp) | ||
| 585 | { | ||
| 586 | #undef SEED_BUFFER | ||
| 587 | #ifdef SEED_BUFFER | ||
| 588 | unsigned int idx; | ||
| 589 | unsigned int val; | ||
| 590 | #endif | ||
| 591 | int ret = 0; | ||
| 592 | struct pvr2_stream *sp; | ||
| 593 | if (!bp) return -EINVAL; | ||
| 594 | sp = bp->stream; | ||
| 595 | mutex_lock(&sp->mutex); do { | ||
| 596 | pvr2_buffer_wipe(bp); | ||
| 597 | if (!sp->dev) { | ||
| 598 | ret = -EIO; | ||
| 599 | break; | ||
| 600 | } | ||
| 601 | pvr2_buffer_set_queued(bp); | ||
| 602 | #ifdef SEED_BUFFER | ||
| 603 | for (idx = 0; idx < (bp->max_count) / 4; idx++) { | ||
| 604 | val = bp->id << 24; | ||
| 605 | val |= idx; | ||
| 606 | ((unsigned int *)(bp->ptr))[idx] = val; | ||
| 607 | } | ||
| 608 | #endif | ||
| 609 | bp->status = -EINPROGRESS; | ||
| 610 | usb_fill_bulk_urb(bp->purb, // struct urb *urb | ||
| 611 | sp->dev, // struct usb_device *dev | ||
| 612 | // endpoint (below) | ||
| 613 | usb_rcvbulkpipe(sp->dev,sp->endpoint), | ||
| 614 | bp->ptr, // void *transfer_buffer | ||
| 615 | bp->max_count, // int buffer_length | ||
| 616 | buffer_complete, | ||
| 617 | bp); | ||
| 618 | usb_submit_urb(bp->purb,GFP_KERNEL); | ||
| 619 | } while(0); mutex_unlock(&sp->mutex); | ||
| 620 | return ret; | ||
| 621 | } | ||
| 622 | |||
| 623 | int pvr2_buffer_idle(struct pvr2_buffer *bp) | ||
| 624 | { | ||
| 625 | struct pvr2_stream *sp; | ||
| 626 | if (!bp) return -EINVAL; | ||
| 627 | sp = bp->stream; | ||
| 628 | mutex_lock(&sp->mutex); do { | ||
| 629 | pvr2_buffer_wipe(bp); | ||
| 630 | pvr2_buffer_set_idle(bp); | ||
| 631 | if (sp->buffer_total_count != sp->buffer_target_count) { | ||
| 632 | pvr2_stream_achieve_buffer_count(sp); | ||
| 633 | } | ||
| 634 | } while(0); mutex_unlock(&sp->mutex); | ||
| 635 | return 0; | ||
| 636 | } | ||
| 637 | |||
| 638 | int pvr2_buffer_set_buffer(struct pvr2_buffer *bp,void *ptr,unsigned int cnt) | ||
| 639 | { | ||
| 640 | int ret = 0; | ||
| 641 | unsigned long irq_flags; | ||
| 642 | struct pvr2_stream *sp; | ||
| 643 | if (!bp) return -EINVAL; | ||
| 644 | sp = bp->stream; | ||
| 645 | mutex_lock(&sp->mutex); do { | ||
| 646 | spin_lock_irqsave(&sp->list_lock,irq_flags); | ||
| 647 | if (bp->state != pvr2_buffer_state_idle) { | ||
| 648 | ret = -EPERM; | ||
| 649 | } else { | ||
| 650 | bp->ptr = ptr; | ||
| 651 | bp->stream->i_bcount -= bp->max_count; | ||
| 652 | bp->max_count = cnt; | ||
| 653 | bp->stream->i_bcount += bp->max_count; | ||
| 654 | pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||
| 655 | "/*---TRACE_FLOW---*/ bufferPool " | ||
| 656 | " %8s cap cap=%07d cnt=%02d", | ||
| 657 | pvr2_buffer_state_decode( | ||
| 658 | pvr2_buffer_state_idle), | ||
| 659 | bp->stream->i_bcount,bp->stream->i_count); | ||
| 660 | } | ||
| 661 | spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||
| 662 | } while(0); mutex_unlock(&sp->mutex); | ||
| 663 | return ret; | ||
| 664 | } | ||
| 665 | |||
| 666 | unsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp) | ||
| 667 | { | ||
| 668 | return bp->used_count; | ||
| 669 | } | ||
| 670 | |||
| 671 | int pvr2_buffer_get_status(struct pvr2_buffer *bp) | ||
| 672 | { | ||
| 673 | return bp->status; | ||
| 674 | } | ||
| 675 | |||
| 676 | enum pvr2_buffer_state pvr2_buffer_get_state(struct pvr2_buffer *bp) | ||
| 677 | { | ||
| 678 | return bp->state; | ||
| 679 | } | ||
| 680 | |||
| 681 | int pvr2_buffer_get_id(struct pvr2_buffer *bp) | ||
| 682 | { | ||
| 683 | return bp->id; | ||
| 684 | } | ||
| 685 | |||
| 686 | |||
| 687 | /* | ||
| 688 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 689 | *** Local Variables: *** | ||
| 690 | *** mode: c *** | ||
| 691 | *** fill-column: 75 *** | ||
| 692 | *** tab-width: 8 *** | ||
| 693 | *** c-basic-offset: 8 *** | ||
| 694 | *** End: *** | ||
| 695 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.h b/drivers/media/video/pvrusb2/pvrusb2-io.h new file mode 100644 index 000000000000..65e11385b2b3 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-io.h | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_IO_H | ||
| 22 | #define __PVRUSB2_IO_H | ||
| 23 | |||
| 24 | #include <linux/usb.h> | ||
| 25 | #include <linux/list.h> | ||
| 26 | |||
| 27 | typedef void (*pvr2_stream_callback)(void *); | ||
| 28 | |||
| 29 | enum pvr2_buffer_state { | ||
| 30 | pvr2_buffer_state_none = 0, // Not on any list | ||
| 31 | pvr2_buffer_state_idle = 1, // Buffer is ready to be used again | ||
| 32 | pvr2_buffer_state_queued = 2, // Buffer has been queued for filling | ||
| 33 | pvr2_buffer_state_ready = 3, // Buffer has data available | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct pvr2_stream; | ||
| 37 | struct pvr2_buffer; | ||
| 38 | |||
| 39 | const char *pvr2_buffer_state_decode(enum pvr2_buffer_state); | ||
| 40 | |||
| 41 | /* Initialize / tear down stream structure */ | ||
| 42 | struct pvr2_stream *pvr2_stream_create(void); | ||
| 43 | void pvr2_stream_destroy(struct pvr2_stream *); | ||
| 44 | void pvr2_stream_setup(struct pvr2_stream *, | ||
| 45 | struct usb_device *dev,int endpoint, | ||
| 46 | unsigned int tolerance); | ||
| 47 | void pvr2_stream_set_callback(struct pvr2_stream *, | ||
| 48 | pvr2_stream_callback func, | ||
| 49 | void *data); | ||
| 50 | |||
| 51 | /* Query / set the nominal buffer count */ | ||
| 52 | int pvr2_stream_get_buffer_count(struct pvr2_stream *); | ||
| 53 | int pvr2_stream_set_buffer_count(struct pvr2_stream *,unsigned int); | ||
| 54 | |||
| 55 | /* Get a pointer to a buffer that is either idle, ready, or is specified | ||
| 56 | named. */ | ||
| 57 | struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *); | ||
| 58 | struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *); | ||
| 59 | struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id); | ||
| 60 | |||
| 61 | /* Find out how many buffers are idle or ready */ | ||
| 62 | int pvr2_stream_get_idle_count(struct pvr2_stream *); | ||
| 63 | int pvr2_stream_get_ready_count(struct pvr2_stream *); | ||
| 64 | |||
| 65 | /* Kill all pending operations */ | ||
| 66 | void pvr2_stream_flush(struct pvr2_stream *); | ||
| 67 | |||
| 68 | /* Kill all pending buffers and throw away any ready buffers as well */ | ||
| 69 | void pvr2_stream_kill(struct pvr2_stream *); | ||
| 70 | |||
| 71 | /* Set up the actual storage for a buffer */ | ||
| 72 | int pvr2_buffer_set_buffer(struct pvr2_buffer *,void *ptr,unsigned int cnt); | ||
| 73 | |||
| 74 | /* Find out size of data in the given ready buffer */ | ||
| 75 | unsigned int pvr2_buffer_get_count(struct pvr2_buffer *); | ||
| 76 | |||
| 77 | /* Retrieve completion code for given ready buffer */ | ||
| 78 | int pvr2_buffer_get_status(struct pvr2_buffer *); | ||
| 79 | |||
| 80 | /* Retrieve state of given buffer */ | ||
| 81 | enum pvr2_buffer_state pvr2_buffer_get_state(struct pvr2_buffer *); | ||
| 82 | |||
| 83 | /* Retrieve ID of given buffer */ | ||
| 84 | int pvr2_buffer_get_id(struct pvr2_buffer *); | ||
| 85 | |||
| 86 | /* Start reading into given buffer (kill it if needed) */ | ||
| 87 | int pvr2_buffer_queue(struct pvr2_buffer *); | ||
| 88 | |||
| 89 | /* Move buffer back to idle pool (kill it if needed) */ | ||
| 90 | int pvr2_buffer_idle(struct pvr2_buffer *); | ||
| 91 | |||
| 92 | #endif /* __PVRUSB2_IO_H */ | ||
| 93 | |||
| 94 | /* | ||
| 95 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 96 | *** Local Variables: *** | ||
| 97 | *** mode: c *** | ||
| 98 | *** fill-column: 75 *** | ||
| 99 | *** tab-width: 8 *** | ||
| 100 | *** c-basic-offset: 8 *** | ||
| 101 | *** End: *** | ||
| 102 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c new file mode 100644 index 000000000000..49da062e3271 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c | |||
| @@ -0,0 +1,513 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "pvrusb2-ioread.h" | ||
| 23 | #include "pvrusb2-debug.h" | ||
| 24 | #include <linux/errno.h> | ||
| 25 | #include <linux/string.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/mutex.h> | ||
| 28 | #include <asm/uaccess.h> | ||
| 29 | |||
| 30 | #define BUFFER_COUNT 32 | ||
| 31 | #define BUFFER_SIZE PAGE_ALIGN(0x4000) | ||
| 32 | |||
| 33 | struct pvr2_ioread { | ||
| 34 | struct pvr2_stream *stream; | ||
| 35 | char *buffer_storage[BUFFER_COUNT]; | ||
| 36 | char *sync_key_ptr; | ||
| 37 | unsigned int sync_key_len; | ||
| 38 | unsigned int sync_buf_offs; | ||
| 39 | unsigned int sync_state; | ||
| 40 | unsigned int sync_trashed_count; | ||
| 41 | int enabled; // Streaming is on | ||
| 42 | int spigot_open; // OK to pass data to client | ||
| 43 | int stream_running; // Passing data to client now | ||
| 44 | |||
| 45 | /* State relevant to current buffer being read */ | ||
| 46 | struct pvr2_buffer *c_buf; | ||
| 47 | char *c_data_ptr; | ||
| 48 | unsigned int c_data_len; | ||
| 49 | unsigned int c_data_offs; | ||
| 50 | struct mutex mutex; | ||
| 51 | }; | ||
| 52 | |||
| 53 | static int pvr2_ioread_init(struct pvr2_ioread *cp) | ||
| 54 | { | ||
| 55 | unsigned int idx; | ||
| 56 | |||
| 57 | cp->stream = 0; | ||
| 58 | mutex_init(&cp->mutex); | ||
| 59 | |||
| 60 | for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||
| 61 | cp->buffer_storage[idx] = kmalloc(BUFFER_SIZE,GFP_KERNEL); | ||
| 62 | if (!(cp->buffer_storage[idx])) break; | ||
| 63 | } | ||
| 64 | |||
| 65 | if (idx < BUFFER_COUNT) { | ||
| 66 | // An allocation appears to have failed | ||
| 67 | for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||
| 68 | if (!(cp->buffer_storage[idx])) continue; | ||
| 69 | kfree(cp->buffer_storage[idx]); | ||
| 70 | } | ||
| 71 | return -ENOMEM; | ||
| 72 | } | ||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | |||
| 76 | static void pvr2_ioread_done(struct pvr2_ioread *cp) | ||
| 77 | { | ||
| 78 | unsigned int idx; | ||
| 79 | |||
| 80 | pvr2_ioread_setup(cp,0); | ||
| 81 | for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||
| 82 | if (!(cp->buffer_storage[idx])) continue; | ||
| 83 | kfree(cp->buffer_storage[idx]); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | struct pvr2_ioread *pvr2_ioread_create(void) | ||
| 88 | { | ||
| 89 | struct pvr2_ioread *cp; | ||
| 90 | cp = kmalloc(sizeof(*cp),GFP_KERNEL); | ||
| 91 | if (!cp) return 0; | ||
| 92 | pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp); | ||
| 93 | memset(cp,0,sizeof(*cp)); | ||
| 94 | if (pvr2_ioread_init(cp) < 0) { | ||
| 95 | kfree(cp); | ||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | return cp; | ||
| 99 | } | ||
| 100 | |||
| 101 | void pvr2_ioread_destroy(struct pvr2_ioread *cp) | ||
| 102 | { | ||
| 103 | if (!cp) return; | ||
| 104 | pvr2_ioread_done(cp); | ||
| 105 | pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_destroy id=%p",cp); | ||
| 106 | if (cp->sync_key_ptr) { | ||
| 107 | kfree(cp->sync_key_ptr); | ||
| 108 | cp->sync_key_ptr = 0; | ||
| 109 | } | ||
| 110 | kfree(cp); | ||
| 111 | } | ||
| 112 | |||
| 113 | void pvr2_ioread_set_sync_key(struct pvr2_ioread *cp, | ||
| 114 | const char *sync_key_ptr, | ||
| 115 | unsigned int sync_key_len) | ||
| 116 | { | ||
| 117 | if (!cp) return; | ||
| 118 | |||
| 119 | if (!sync_key_ptr) sync_key_len = 0; | ||
| 120 | if ((sync_key_len == cp->sync_key_len) && | ||
| 121 | ((!sync_key_len) || | ||
| 122 | (!memcmp(sync_key_ptr,cp->sync_key_ptr,sync_key_len)))) return; | ||
| 123 | |||
| 124 | if (sync_key_len != cp->sync_key_len) { | ||
| 125 | if (cp->sync_key_ptr) { | ||
| 126 | kfree(cp->sync_key_ptr); | ||
| 127 | cp->sync_key_ptr = 0; | ||
| 128 | } | ||
| 129 | cp->sync_key_len = 0; | ||
| 130 | if (sync_key_len) { | ||
| 131 | cp->sync_key_ptr = kmalloc(sync_key_len,GFP_KERNEL); | ||
| 132 | if (cp->sync_key_ptr) { | ||
| 133 | cp->sync_key_len = sync_key_len; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | if (!cp->sync_key_len) return; | ||
| 138 | memcpy(cp->sync_key_ptr,sync_key_ptr,cp->sync_key_len); | ||
| 139 | } | ||
| 140 | |||
| 141 | static void pvr2_ioread_stop(struct pvr2_ioread *cp) | ||
| 142 | { | ||
| 143 | if (!(cp->enabled)) return; | ||
| 144 | pvr2_trace(PVR2_TRACE_START_STOP, | ||
| 145 | "/*---TRACE_READ---*/ pvr2_ioread_stop id=%p",cp); | ||
| 146 | pvr2_stream_kill(cp->stream); | ||
| 147 | cp->c_buf = 0; | ||
| 148 | cp->c_data_ptr = 0; | ||
| 149 | cp->c_data_len = 0; | ||
| 150 | cp->c_data_offs = 0; | ||
| 151 | cp->enabled = 0; | ||
| 152 | cp->stream_running = 0; | ||
| 153 | cp->spigot_open = 0; | ||
| 154 | if (cp->sync_state) { | ||
| 155 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
| 156 | "/*---TRACE_READ---*/ sync_state <== 0"); | ||
| 157 | cp->sync_state = 0; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | static int pvr2_ioread_start(struct pvr2_ioread *cp) | ||
| 162 | { | ||
| 163 | int stat; | ||
| 164 | struct pvr2_buffer *bp; | ||
| 165 | if (cp->enabled) return 0; | ||
| 166 | if (!(cp->stream)) return 0; | ||
| 167 | pvr2_trace(PVR2_TRACE_START_STOP, | ||
| 168 | "/*---TRACE_READ---*/ pvr2_ioread_start id=%p",cp); | ||
| 169 | while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != 0) { | ||
| 170 | stat = pvr2_buffer_queue(bp); | ||
| 171 | if (stat < 0) { | ||
| 172 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
| 173 | "/*---TRACE_READ---*/" | ||
| 174 | " pvr2_ioread_start id=%p" | ||
| 175 | " error=%d", | ||
| 176 | cp,stat); | ||
| 177 | pvr2_ioread_stop(cp); | ||
| 178 | return stat; | ||
| 179 | } | ||
| 180 | } | ||
| 181 | cp->enabled = !0; | ||
| 182 | cp->c_buf = 0; | ||
| 183 | cp->c_data_ptr = 0; | ||
| 184 | cp->c_data_len = 0; | ||
| 185 | cp->c_data_offs = 0; | ||
| 186 | cp->stream_running = 0; | ||
| 187 | if (cp->sync_key_len) { | ||
| 188 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
| 189 | "/*---TRACE_READ---*/ sync_state <== 1"); | ||
| 190 | cp->sync_state = 1; | ||
| 191 | cp->sync_trashed_count = 0; | ||
| 192 | cp->sync_buf_offs = 0; | ||
| 193 | } | ||
| 194 | cp->spigot_open = 0; | ||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | |||
| 198 | struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *cp) | ||
| 199 | { | ||
| 200 | return cp->stream; | ||
| 201 | } | ||
| 202 | |||
| 203 | int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp) | ||
| 204 | { | ||
| 205 | int ret; | ||
| 206 | unsigned int idx; | ||
| 207 | struct pvr2_buffer *bp; | ||
| 208 | |||
| 209 | mutex_lock(&cp->mutex); do { | ||
| 210 | if (cp->stream) { | ||
| 211 | pvr2_trace(PVR2_TRACE_START_STOP, | ||
| 212 | "/*---TRACE_READ---*/" | ||
| 213 | " pvr2_ioread_setup (tear-down) id=%p",cp); | ||
| 214 | pvr2_ioread_stop(cp); | ||
| 215 | pvr2_stream_kill(cp->stream); | ||
| 216 | pvr2_stream_set_buffer_count(cp->stream,0); | ||
| 217 | cp->stream = 0; | ||
| 218 | } | ||
| 219 | if (sp) { | ||
| 220 | pvr2_trace(PVR2_TRACE_START_STOP, | ||
| 221 | "/*---TRACE_READ---*/" | ||
| 222 | " pvr2_ioread_setup (setup) id=%p",cp); | ||
| 223 | pvr2_stream_kill(sp); | ||
| 224 | ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT); | ||
| 225 | if (ret < 0) return ret; | ||
| 226 | for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||
| 227 | bp = pvr2_stream_get_buffer(sp,idx); | ||
| 228 | pvr2_buffer_set_buffer(bp, | ||
| 229 | cp->buffer_storage[idx], | ||
| 230 | BUFFER_SIZE); | ||
| 231 | } | ||
| 232 | cp->stream = sp; | ||
| 233 | } | ||
| 234 | } while (0); mutex_unlock(&cp->mutex); | ||
| 235 | |||
| 236 | return 0; | ||
| 237 | } | ||
| 238 | |||
| 239 | int pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl) | ||
| 240 | { | ||
| 241 | int ret = 0; | ||
| 242 | if ((!fl) == (!(cp->enabled))) return ret; | ||
| 243 | |||
| 244 | mutex_lock(&cp->mutex); do { | ||
| 245 | if (fl) { | ||
| 246 | ret = pvr2_ioread_start(cp); | ||
| 247 | } else { | ||
| 248 | pvr2_ioread_stop(cp); | ||
| 249 | } | ||
| 250 | } while (0); mutex_unlock(&cp->mutex); | ||
| 251 | return ret; | ||
| 252 | } | ||
| 253 | |||
| 254 | int pvr2_ioread_get_enabled(struct pvr2_ioread *cp) | ||
| 255 | { | ||
| 256 | return cp->enabled != 0; | ||
| 257 | } | ||
| 258 | |||
| 259 | int pvr2_ioread_get_buffer(struct pvr2_ioread *cp) | ||
| 260 | { | ||
| 261 | int stat; | ||
| 262 | |||
| 263 | while (cp->c_data_len <= cp->c_data_offs) { | ||
| 264 | if (cp->c_buf) { | ||
| 265 | // Flush out current buffer first. | ||
| 266 | stat = pvr2_buffer_queue(cp->c_buf); | ||
| 267 | if (stat < 0) { | ||
| 268 | // Streaming error... | ||
| 269 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
| 270 | "/*---TRACE_READ---*/" | ||
| 271 | " pvr2_ioread_read id=%p" | ||
| 272 | " queue_error=%d", | ||
| 273 | cp,stat); | ||
| 274 | pvr2_ioread_stop(cp); | ||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | cp->c_buf = 0; | ||
| 278 | cp->c_data_ptr = 0; | ||
| 279 | cp->c_data_len = 0; | ||
| 280 | cp->c_data_offs = 0; | ||
| 281 | } | ||
| 282 | // Now get a freshly filled buffer. | ||
| 283 | cp->c_buf = pvr2_stream_get_ready_buffer(cp->stream); | ||
| 284 | if (!cp->c_buf) break; // Nothing ready; done. | ||
| 285 | cp->c_data_len = pvr2_buffer_get_count(cp->c_buf); | ||
| 286 | if (!cp->c_data_len) { | ||
| 287 | // Nothing transferred. Was there an error? | ||
| 288 | stat = pvr2_buffer_get_status(cp->c_buf); | ||
| 289 | if (stat < 0) { | ||
| 290 | // Streaming error... | ||
| 291 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
| 292 | "/*---TRACE_READ---*/" | ||
| 293 | " pvr2_ioread_read id=%p" | ||
| 294 | " buffer_error=%d", | ||
| 295 | cp,stat); | ||
| 296 | pvr2_ioread_stop(cp); | ||
| 297 | // Give up. | ||
| 298 | return 0; | ||
| 299 | } | ||
| 300 | // Start over... | ||
| 301 | continue; | ||
| 302 | } | ||
| 303 | cp->c_data_offs = 0; | ||
| 304 | cp->c_data_ptr = cp->buffer_storage[ | ||
| 305 | pvr2_buffer_get_id(cp->c_buf)]; | ||
| 306 | } | ||
| 307 | return !0; | ||
| 308 | } | ||
| 309 | |||
| 310 | void pvr2_ioread_filter(struct pvr2_ioread *cp) | ||
| 311 | { | ||
| 312 | unsigned int idx; | ||
| 313 | if (!cp->enabled) return; | ||
| 314 | if (cp->sync_state != 1) return; | ||
| 315 | |||
| 316 | // Search the stream for our synchronization key. This is made | ||
| 317 | // complicated by the fact that in order to be honest with | ||
| 318 | // ourselves here we must search across buffer boundaries... | ||
| 319 | mutex_lock(&cp->mutex); while (1) { | ||
| 320 | // Ensure we have a buffer | ||
| 321 | if (!pvr2_ioread_get_buffer(cp)) break; | ||
| 322 | if (!cp->c_data_len) break; | ||
| 323 | |||
| 324 | // Now walk the buffer contents until we match the key or | ||
| 325 | // run out of buffer data. | ||
| 326 | for (idx = cp->c_data_offs; idx < cp->c_data_len; idx++) { | ||
| 327 | if (cp->sync_buf_offs >= cp->sync_key_len) break; | ||
| 328 | if (cp->c_data_ptr[idx] == | ||
| 329 | cp->sync_key_ptr[cp->sync_buf_offs]) { | ||
| 330 | // Found the next key byte | ||
| 331 | (cp->sync_buf_offs)++; | ||
| 332 | } else { | ||
| 333 | // Whoops, mismatched. Start key over... | ||
| 334 | cp->sync_buf_offs = 0; | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | // Consume what we've walked through | ||
| 339 | cp->c_data_offs += idx; | ||
| 340 | cp->sync_trashed_count += idx; | ||
| 341 | |||
| 342 | // If we've found the key, then update state and get out. | ||
| 343 | if (cp->sync_buf_offs >= cp->sync_key_len) { | ||
| 344 | cp->sync_trashed_count -= cp->sync_key_len; | ||
| 345 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
| 346 | "/*---TRACE_READ---*/" | ||
| 347 | " sync_state <== 2 (skipped %u bytes)", | ||
| 348 | cp->sync_trashed_count); | ||
| 349 | cp->sync_state = 2; | ||
| 350 | cp->sync_buf_offs = 0; | ||
| 351 | break; | ||
| 352 | } | ||
| 353 | |||
| 354 | if (cp->c_data_offs < cp->c_data_len) { | ||
| 355 | // Sanity check - should NEVER get here | ||
| 356 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 357 | "ERROR: pvr2_ioread filter sync problem" | ||
| 358 | " len=%u offs=%u", | ||
| 359 | cp->c_data_len,cp->c_data_offs); | ||
| 360 | // Get out so we don't get stuck in an infinite | ||
| 361 | // loop. | ||
| 362 | break; | ||
| 363 | } | ||
| 364 | |||
| 365 | continue; // (for clarity) | ||
| 366 | } mutex_unlock(&cp->mutex); | ||
| 367 | } | ||
| 368 | |||
| 369 | int pvr2_ioread_avail(struct pvr2_ioread *cp) | ||
| 370 | { | ||
| 371 | int ret; | ||
| 372 | if (!(cp->enabled)) { | ||
| 373 | // Stream is not enabled; so this is an I/O error | ||
| 374 | return -EIO; | ||
| 375 | } | ||
| 376 | |||
| 377 | if (cp->sync_state == 1) { | ||
| 378 | pvr2_ioread_filter(cp); | ||
| 379 | if (cp->sync_state == 1) return -EAGAIN; | ||
| 380 | } | ||
| 381 | |||
| 382 | ret = 0; | ||
| 383 | if (cp->stream_running) { | ||
| 384 | if (!pvr2_stream_get_ready_count(cp->stream)) { | ||
| 385 | // No data available at all right now. | ||
| 386 | ret = -EAGAIN; | ||
| 387 | } | ||
| 388 | } else { | ||
| 389 | if (pvr2_stream_get_ready_count(cp->stream) < BUFFER_COUNT/2) { | ||
| 390 | // Haven't buffered up enough yet; try again later | ||
| 391 | ret = -EAGAIN; | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | if ((!(cp->spigot_open)) != (!(ret == 0))) { | ||
| 396 | cp->spigot_open = (ret == 0); | ||
| 397 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
| 398 | "/*---TRACE_READ---*/ data is %s", | ||
| 399 | cp->spigot_open ? "available" : "pending"); | ||
| 400 | } | ||
| 401 | |||
| 402 | return ret; | ||
| 403 | } | ||
| 404 | |||
| 405 | int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt) | ||
| 406 | { | ||
| 407 | unsigned int copied_cnt; | ||
| 408 | unsigned int bcnt; | ||
| 409 | const char *src; | ||
| 410 | int stat; | ||
| 411 | int ret = 0; | ||
| 412 | unsigned int req_cnt = cnt; | ||
| 413 | |||
| 414 | if (!cnt) { | ||
| 415 | pvr2_trace(PVR2_TRACE_TRAP, | ||
| 416 | "/*---TRACE_READ---*/ pvr2_ioread_read id=%p" | ||
| 417 | " ZERO Request? Returning zero.",cp); | ||
| 418 | return 0; | ||
| 419 | } | ||
| 420 | |||
| 421 | stat = pvr2_ioread_avail(cp); | ||
| 422 | if (stat < 0) return stat; | ||
| 423 | |||
| 424 | cp->stream_running = !0; | ||
| 425 | |||
| 426 | mutex_lock(&cp->mutex); do { | ||
| 427 | |||
| 428 | // Suck data out of the buffers and copy to the user | ||
| 429 | copied_cnt = 0; | ||
| 430 | if (!buf) cnt = 0; | ||
| 431 | while (1) { | ||
| 432 | if (!pvr2_ioread_get_buffer(cp)) { | ||
| 433 | ret = -EIO; | ||
| 434 | break; | ||
| 435 | } | ||
| 436 | |||
| 437 | if (!cnt) break; | ||
| 438 | |||
| 439 | if (cp->sync_state == 2) { | ||
| 440 | // We're repeating the sync key data into | ||
| 441 | // the stream. | ||
| 442 | src = cp->sync_key_ptr + cp->sync_buf_offs; | ||
| 443 | bcnt = cp->sync_key_len - cp->sync_buf_offs; | ||
| 444 | } else { | ||
| 445 | // Normal buffer copy | ||
| 446 | src = cp->c_data_ptr + cp->c_data_offs; | ||
| 447 | bcnt = cp->c_data_len - cp->c_data_offs; | ||
| 448 | } | ||
| 449 | |||
| 450 | if (!bcnt) break; | ||
| 451 | |||
| 452 | // Don't run past user's buffer | ||
| 453 | if (bcnt > cnt) bcnt = cnt; | ||
| 454 | |||
| 455 | if (copy_to_user(buf,src,bcnt)) { | ||
| 456 | // User supplied a bad pointer? | ||
| 457 | // Give up - this *will* cause data | ||
| 458 | // to be lost. | ||
| 459 | ret = -EFAULT; | ||
| 460 | break; | ||
| 461 | } | ||
| 462 | cnt -= bcnt; | ||
| 463 | buf += bcnt; | ||
| 464 | copied_cnt += bcnt; | ||
| 465 | |||
| 466 | if (cp->sync_state == 2) { | ||
| 467 | // Update offset inside sync key that we're | ||
| 468 | // repeating back out. | ||
| 469 | cp->sync_buf_offs += bcnt; | ||
| 470 | if (cp->sync_buf_offs >= cp->sync_key_len) { | ||
| 471 | // Consumed entire key; switch mode | ||
| 472 | // to normal. | ||
| 473 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
| 474 | "/*---TRACE_READ---*/" | ||
| 475 | " sync_state <== 0"); | ||
| 476 | cp->sync_state = 0; | ||
| 477 | } | ||
| 478 | } else { | ||
| 479 | // Update buffer offset. | ||
| 480 | cp->c_data_offs += bcnt; | ||
| 481 | } | ||
| 482 | } | ||
| 483 | |||
| 484 | } while (0); mutex_unlock(&cp->mutex); | ||
| 485 | |||
| 486 | if (!ret) { | ||
| 487 | if (copied_cnt) { | ||
| 488 | // If anything was copied, return that count | ||
| 489 | ret = copied_cnt; | ||
| 490 | } else { | ||
| 491 | // Nothing copied; suggest to caller that another | ||
| 492 | // attempt should be tried again later | ||
| 493 | ret = -EAGAIN; | ||
| 494 | } | ||
| 495 | } | ||
| 496 | |||
| 497 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
| 498 | "/*---TRACE_READ---*/ pvr2_ioread_read" | ||
| 499 | " id=%p request=%d result=%d", | ||
| 500 | cp,req_cnt,ret); | ||
| 501 | return ret; | ||
| 502 | } | ||
| 503 | |||
| 504 | |||
| 505 | /* | ||
| 506 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 507 | *** Local Variables: *** | ||
| 508 | *** mode: c *** | ||
| 509 | *** fill-column: 75 *** | ||
| 510 | *** tab-width: 8 *** | ||
| 511 | *** c-basic-offset: 8 *** | ||
| 512 | *** End: *** | ||
| 513 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.h b/drivers/media/video/pvrusb2/pvrusb2-ioread.h new file mode 100644 index 000000000000..6b002597f5de --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_IOREAD_H | ||
| 22 | #define __PVRUSB2_IOREAD_H | ||
| 23 | |||
| 24 | #include "pvrusb2-io.h" | ||
| 25 | |||
| 26 | struct pvr2_ioread; | ||
| 27 | |||
| 28 | struct pvr2_ioread *pvr2_ioread_create(void); | ||
| 29 | void pvr2_ioread_destroy(struct pvr2_ioread *); | ||
| 30 | int pvr2_ioread_setup(struct pvr2_ioread *,struct pvr2_stream *); | ||
| 31 | struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *); | ||
| 32 | void pvr2_ioread_set_sync_key(struct pvr2_ioread *, | ||
| 33 | const char *sync_key_ptr, | ||
| 34 | unsigned int sync_key_len); | ||
| 35 | int pvr2_ioread_set_enabled(struct pvr2_ioread *,int fl); | ||
| 36 | int pvr2_ioread_get_enabled(struct pvr2_ioread *); | ||
| 37 | int pvr2_ioread_read(struct pvr2_ioread *,void __user *buf,unsigned int cnt); | ||
| 38 | int pvr2_ioread_avail(struct pvr2_ioread *); | ||
| 39 | |||
| 40 | #endif /* __PVRUSB2_IOREAD_H */ | ||
| 41 | |||
| 42 | /* | ||
| 43 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 44 | *** Local Variables: *** | ||
| 45 | *** mode: c *** | ||
| 46 | *** fill-column: 75 *** | ||
| 47 | *** tab-width: 8 *** | ||
| 48 | *** c-basic-offset: 8 *** | ||
| 49 | *** End: *** | ||
| 50 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c new file mode 100644 index 000000000000..b95248274ed0 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-main.c | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/config.h> | ||
| 24 | #include <linux/kernel.h> | ||
| 25 | #include <linux/errno.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/moduleparam.h> | ||
| 29 | #include <linux/smp_lock.h> | ||
| 30 | #include <linux/usb.h> | ||
| 31 | #include <linux/videodev2.h> | ||
| 32 | |||
| 33 | #include "pvrusb2-hdw.h" | ||
| 34 | #include "pvrusb2-context.h" | ||
| 35 | #include "pvrusb2-debug.h" | ||
| 36 | #include "pvrusb2-v4l2.h" | ||
| 37 | #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS | ||
| 38 | #include "pvrusb2-sysfs.h" | ||
| 39 | #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ | ||
| 40 | |||
| 41 | #define DRIVER_AUTHOR "Mike Isely <isely@pobox.com>" | ||
| 42 | #define DRIVER_DESC "Hauppauge WinTV-PVR-USB2 MPEG2 Encoder/Tuner" | ||
| 43 | #define DRIVER_VERSION "V4L in-tree version" | ||
| 44 | |||
| 45 | #define DEFAULT_DEBUG_MASK (PVR2_TRACE_ERROR_LEGS| \ | ||
| 46 | PVR2_TRACE_INFO| \ | ||
| 47 | PVR2_TRACE_TOLERANCE| \ | ||
| 48 | PVR2_TRACE_TRAP| \ | ||
| 49 | 0) | ||
| 50 | |||
| 51 | int pvrusb2_debug = DEFAULT_DEBUG_MASK; | ||
| 52 | |||
| 53 | module_param_named(debug,pvrusb2_debug,int,S_IRUGO|S_IWUSR); | ||
| 54 | MODULE_PARM_DESC(debug, "Debug trace mask"); | ||
| 55 | |||
| 56 | #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS | ||
| 57 | static struct pvr2_sysfs_class *class_ptr = 0; | ||
| 58 | #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ | ||
| 59 | |||
| 60 | static void pvr_setup_attach(struct pvr2_context *pvr) | ||
| 61 | { | ||
| 62 | /* Create association with v4l layer */ | ||
| 63 | pvr2_v4l2_create(pvr); | ||
| 64 | #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS | ||
| 65 | pvr2_sysfs_create(pvr,class_ptr); | ||
| 66 | #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ | ||
| 67 | } | ||
| 68 | |||
| 69 | static int pvr_probe(struct usb_interface *intf, | ||
| 70 | const struct usb_device_id *devid) | ||
| 71 | { | ||
| 72 | struct pvr2_context *pvr; | ||
| 73 | |||
| 74 | /* Create underlying hardware interface */ | ||
| 75 | pvr = pvr2_context_create(intf,devid,pvr_setup_attach); | ||
| 76 | if (!pvr) { | ||
| 77 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 78 | "Failed to create hdw handler"); | ||
| 79 | return -ENOMEM; | ||
| 80 | } | ||
| 81 | |||
| 82 | pvr2_trace(PVR2_TRACE_INIT,"pvr_probe(pvr=%p)",pvr); | ||
| 83 | |||
| 84 | usb_set_intfdata(intf, pvr); | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | /* | ||
| 90 | * pvr_disconnect() | ||
| 91 | * | ||
| 92 | */ | ||
| 93 | static void pvr_disconnect(struct usb_interface *intf) | ||
| 94 | { | ||
| 95 | struct pvr2_context *pvr = usb_get_intfdata(intf); | ||
| 96 | |||
| 97 | pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) BEGIN",pvr); | ||
| 98 | |||
| 99 | usb_set_intfdata (intf, NULL); | ||
| 100 | pvr2_context_disconnect(pvr); | ||
| 101 | |||
| 102 | pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) DONE",pvr); | ||
| 103 | |||
| 104 | } | ||
| 105 | |||
| 106 | static struct usb_driver pvr_driver = { | ||
| 107 | name: "pvrusb2", | ||
| 108 | id_table: pvr2_device_table, | ||
| 109 | probe: pvr_probe, | ||
| 110 | disconnect: pvr_disconnect | ||
| 111 | }; | ||
| 112 | |||
| 113 | /* | ||
| 114 | * pvr_init() / pvr_exit() | ||
| 115 | * | ||
| 116 | * This code is run to initialize/exit the driver. | ||
| 117 | * | ||
| 118 | */ | ||
| 119 | static int __init pvr_init(void) | ||
| 120 | { | ||
| 121 | int ret; | ||
| 122 | |||
| 123 | pvr2_trace(PVR2_TRACE_INIT,"pvr_init"); | ||
| 124 | |||
| 125 | #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS | ||
| 126 | class_ptr = pvr2_sysfs_class_create(); | ||
| 127 | #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ | ||
| 128 | |||
| 129 | ret = usb_register(&pvr_driver); | ||
| 130 | |||
| 131 | if (ret == 0) | ||
| 132 | info(DRIVER_DESC " : " DRIVER_VERSION); | ||
| 133 | if (pvrusb2_debug) info("Debug mask is %d (0x%x)", | ||
| 134 | pvrusb2_debug,pvrusb2_debug); | ||
| 135 | |||
| 136 | return ret; | ||
| 137 | } | ||
| 138 | |||
| 139 | static void __exit pvr_exit(void) | ||
| 140 | { | ||
| 141 | |||
| 142 | pvr2_trace(PVR2_TRACE_INIT,"pvr_exit"); | ||
| 143 | |||
| 144 | #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS | ||
| 145 | pvr2_sysfs_class_destroy(class_ptr); | ||
| 146 | #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ | ||
| 147 | |||
| 148 | usb_deregister(&pvr_driver); | ||
| 149 | } | ||
| 150 | |||
| 151 | module_init(pvr_init); | ||
| 152 | module_exit(pvr_exit); | ||
| 153 | |||
| 154 | /* Mike Isely <mcisely@pobox.com> 11-Mar-2006: See pvrusb2-hdw.c for | ||
| 155 | MODULE_DEVICE_TABLE(). We have to declare that attribute there | ||
| 156 | because that's where the device table actually is now and it seems | ||
| 157 | that certain gcc configurations get angry if MODULE_DEVICE_TABLE() | ||
| 158 | is used on what ends up being an external symbol. */ | ||
| 159 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
| 160 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
| 161 | MODULE_LICENSE("GPL"); | ||
| 162 | |||
| 163 | |||
| 164 | /* | ||
| 165 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 166 | *** Local Variables: *** | ||
| 167 | *** mode: c *** | ||
| 168 | *** fill-column: 70 *** | ||
| 169 | *** tab-width: 8 *** | ||
| 170 | *** c-basic-offset: 8 *** | ||
| 171 | *** End: *** | ||
| 172 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c new file mode 100644 index 000000000000..134063693643 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-std.c | |||
| @@ -0,0 +1,408 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "pvrusb2-std.h" | ||
| 23 | #include "pvrusb2-debug.h" | ||
| 24 | #include <asm/string.h> | ||
| 25 | #include <linux/slab.h> | ||
| 26 | |||
| 27 | struct std_name { | ||
| 28 | const char *name; | ||
| 29 | v4l2_std_id id; | ||
| 30 | }; | ||
| 31 | |||
| 32 | |||
| 33 | #define CSTD_PAL \ | ||
| 34 | (V4L2_STD_PAL_B| \ | ||
| 35 | V4L2_STD_PAL_B1| \ | ||
| 36 | V4L2_STD_PAL_G| \ | ||
| 37 | V4L2_STD_PAL_H| \ | ||
| 38 | V4L2_STD_PAL_I| \ | ||
| 39 | V4L2_STD_PAL_D| \ | ||
| 40 | V4L2_STD_PAL_D1| \ | ||
| 41 | V4L2_STD_PAL_K| \ | ||
| 42 | V4L2_STD_PAL_M| \ | ||
| 43 | V4L2_STD_PAL_N| \ | ||
| 44 | V4L2_STD_PAL_Nc| \ | ||
| 45 | V4L2_STD_PAL_60) | ||
| 46 | |||
| 47 | #define CSTD_NTSC \ | ||
| 48 | (V4L2_STD_NTSC_M| \ | ||
| 49 | V4L2_STD_NTSC_M_JP| \ | ||
| 50 | V4L2_STD_NTSC_M_KR| \ | ||
| 51 | V4L2_STD_NTSC_443) | ||
| 52 | |||
| 53 | #define CSTD_SECAM \ | ||
| 54 | (V4L2_STD_SECAM_B| \ | ||
| 55 | V4L2_STD_SECAM_D| \ | ||
| 56 | V4L2_STD_SECAM_G| \ | ||
| 57 | V4L2_STD_SECAM_H| \ | ||
| 58 | V4L2_STD_SECAM_K| \ | ||
| 59 | V4L2_STD_SECAM_K1| \ | ||
| 60 | V4L2_STD_SECAM_L| \ | ||
| 61 | V4L2_STD_SECAM_LC) | ||
| 62 | |||
| 63 | #define TSTD_B (V4L2_STD_PAL_B|V4L2_STD_SECAM_B) | ||
| 64 | #define TSTD_B1 (V4L2_STD_PAL_B1) | ||
| 65 | #define TSTD_D (V4L2_STD_PAL_D|V4L2_STD_SECAM_D) | ||
| 66 | #define TSTD_D1 (V4L2_STD_PAL_D1) | ||
| 67 | #define TSTD_G (V4L2_STD_PAL_G|V4L2_STD_SECAM_G) | ||
| 68 | #define TSTD_H (V4L2_STD_PAL_H|V4L2_STD_SECAM_H) | ||
| 69 | #define TSTD_I (V4L2_STD_PAL_I) | ||
| 70 | #define TSTD_K (V4L2_STD_PAL_K|V4L2_STD_SECAM_K) | ||
| 71 | #define TSTD_K1 (V4L2_STD_SECAM_K1) | ||
| 72 | #define TSTD_L (V4L2_STD_SECAM_L) | ||
| 73 | #define TSTD_M (V4L2_STD_PAL_M|V4L2_STD_NTSC_M) | ||
| 74 | #define TSTD_N (V4L2_STD_PAL_N) | ||
| 75 | #define TSTD_Nc (V4L2_STD_PAL_Nc) | ||
| 76 | #define TSTD_60 (V4L2_STD_PAL_60) | ||
| 77 | |||
| 78 | #define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_SECAM) | ||
| 79 | |||
| 80 | /* Mapping of standard bits to color system */ | ||
| 81 | const static struct std_name std_groups[] = { | ||
| 82 | {"PAL",CSTD_PAL}, | ||
| 83 | {"NTSC",CSTD_NTSC}, | ||
| 84 | {"SECAM",CSTD_SECAM}, | ||
| 85 | }; | ||
| 86 | |||
| 87 | /* Mapping of standard bits to modulation system */ | ||
| 88 | const static struct std_name std_items[] = { | ||
| 89 | {"B",TSTD_B}, | ||
| 90 | {"B1",TSTD_B1}, | ||
| 91 | {"D",TSTD_D}, | ||
| 92 | {"D1",TSTD_D1}, | ||
| 93 | {"G",TSTD_G}, | ||
| 94 | {"H",TSTD_H}, | ||
| 95 | {"I",TSTD_I}, | ||
| 96 | {"K",TSTD_K}, | ||
| 97 | {"K1",TSTD_K1}, | ||
| 98 | {"L",TSTD_L}, | ||
| 99 | {"LC",V4L2_STD_SECAM_LC}, | ||
| 100 | {"M",TSTD_M}, | ||
| 101 | {"Mj",V4L2_STD_NTSC_M_JP}, | ||
| 102 | {"443",V4L2_STD_NTSC_443}, | ||
| 103 | {"Mk",V4L2_STD_NTSC_M_KR}, | ||
| 104 | {"N",TSTD_N}, | ||
| 105 | {"Nc",TSTD_Nc}, | ||
| 106 | {"60",TSTD_60}, | ||
| 107 | }; | ||
| 108 | |||
| 109 | |||
| 110 | // Search an array of std_name structures and return a pointer to the | ||
| 111 | // element with the matching name. | ||
| 112 | static const struct std_name *find_std_name(const struct std_name *arrPtr, | ||
| 113 | unsigned int arrSize, | ||
| 114 | const char *bufPtr, | ||
| 115 | unsigned int bufSize) | ||
| 116 | { | ||
| 117 | unsigned int idx; | ||
| 118 | const struct std_name *p; | ||
| 119 | for (idx = 0; idx < arrSize; idx++) { | ||
| 120 | p = arrPtr + idx; | ||
| 121 | if (strlen(p->name) != bufSize) continue; | ||
| 122 | if (!memcmp(bufPtr,p->name,bufSize)) return p; | ||
| 123 | } | ||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | |||
| 128 | int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, | ||
| 129 | unsigned int bufSize) | ||
| 130 | { | ||
| 131 | v4l2_std_id id = 0; | ||
| 132 | v4l2_std_id cmsk = 0; | ||
| 133 | v4l2_std_id t; | ||
| 134 | int mMode = 0; | ||
| 135 | unsigned int cnt; | ||
| 136 | char ch; | ||
| 137 | const struct std_name *sp; | ||
| 138 | |||
| 139 | while (bufSize) { | ||
| 140 | if (!mMode) { | ||
| 141 | cnt = 0; | ||
| 142 | while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++; | ||
| 143 | if (cnt >= bufSize) return 0; // No more characters | ||
| 144 | sp = find_std_name( | ||
| 145 | std_groups, | ||
| 146 | sizeof(std_groups)/sizeof(std_groups[0]), | ||
| 147 | bufPtr,cnt); | ||
| 148 | if (!sp) return 0; // Illegal color system name | ||
| 149 | cnt++; | ||
| 150 | bufPtr += cnt; | ||
| 151 | bufSize -= cnt; | ||
| 152 | mMode = !0; | ||
| 153 | cmsk = sp->id; | ||
| 154 | continue; | ||
| 155 | } | ||
| 156 | cnt = 0; | ||
| 157 | while (cnt < bufSize) { | ||
| 158 | ch = bufPtr[cnt]; | ||
| 159 | if (ch == ';') { | ||
| 160 | mMode = 0; | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | if (ch == '/') break; | ||
| 164 | cnt++; | ||
| 165 | } | ||
| 166 | sp = find_std_name(std_items, | ||
| 167 | sizeof(std_items)/sizeof(std_items[0]), | ||
| 168 | bufPtr,cnt); | ||
| 169 | if (!sp) return 0; // Illegal modulation system ID | ||
| 170 | t = sp->id & cmsk; | ||
| 171 | if (!t) return 0; // Specific color + modulation system illegal | ||
| 172 | id |= t; | ||
| 173 | if (cnt < bufSize) cnt++; | ||
| 174 | bufPtr += cnt; | ||
| 175 | bufSize -= cnt; | ||
| 176 | } | ||
| 177 | |||
| 178 | if (idPtr) *idPtr = id; | ||
| 179 | return !0; | ||
| 180 | } | ||
| 181 | |||
| 182 | |||
| 183 | unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, | ||
| 184 | v4l2_std_id id) | ||
| 185 | { | ||
| 186 | unsigned int idx1,idx2; | ||
| 187 | const struct std_name *ip,*gp; | ||
| 188 | int gfl,cfl; | ||
| 189 | unsigned int c1,c2; | ||
| 190 | cfl = 0; | ||
| 191 | c1 = 0; | ||
| 192 | for (idx1 = 0; | ||
| 193 | idx1 < sizeof(std_groups)/sizeof(std_groups[0]); | ||
| 194 | idx1++) { | ||
| 195 | gp = std_groups + idx1; | ||
| 196 | gfl = 0; | ||
| 197 | for (idx2 = 0; | ||
| 198 | idx2 < sizeof(std_items)/sizeof(std_items[0]); | ||
| 199 | idx2++) { | ||
| 200 | ip = std_items + idx2; | ||
| 201 | if (!(gp->id & ip->id & id)) continue; | ||
| 202 | if (!gfl) { | ||
| 203 | if (cfl) { | ||
| 204 | c2 = scnprintf(bufPtr,bufSize,";"); | ||
| 205 | c1 += c2; | ||
| 206 | bufSize -= c2; | ||
| 207 | bufPtr += c2; | ||
| 208 | } | ||
| 209 | cfl = !0; | ||
| 210 | c2 = scnprintf(bufPtr,bufSize, | ||
| 211 | "%s-",gp->name); | ||
| 212 | gfl = !0; | ||
| 213 | } else { | ||
| 214 | c2 = scnprintf(bufPtr,bufSize,"/"); | ||
| 215 | } | ||
| 216 | c1 += c2; | ||
| 217 | bufSize -= c2; | ||
| 218 | bufPtr += c2; | ||
| 219 | c2 = scnprintf(bufPtr,bufSize, | ||
| 220 | ip->name); | ||
| 221 | c1 += c2; | ||
| 222 | bufSize -= c2; | ||
| 223 | bufPtr += c2; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | return c1; | ||
| 227 | } | ||
| 228 | |||
| 229 | |||
| 230 | // Template data for possible enumerated video standards. Here we group | ||
| 231 | // standards which share common frame rates and resolution. | ||
| 232 | static struct v4l2_standard generic_standards[] = { | ||
| 233 | { | ||
| 234 | .id = (TSTD_B|TSTD_B1| | ||
| 235 | TSTD_D|TSTD_D1| | ||
| 236 | TSTD_G| | ||
| 237 | TSTD_H| | ||
| 238 | TSTD_I| | ||
| 239 | TSTD_K|TSTD_K1| | ||
| 240 | TSTD_L| | ||
| 241 | V4L2_STD_SECAM_LC | | ||
| 242 | TSTD_N|TSTD_Nc), | ||
| 243 | .frameperiod = | ||
| 244 | { | ||
| 245 | .numerator = 1, | ||
| 246 | .denominator= 25 | ||
| 247 | }, | ||
| 248 | .framelines = 625, | ||
| 249 | .reserved = {0,0,0,0} | ||
| 250 | }, { | ||
| 251 | .id = (TSTD_M| | ||
| 252 | V4L2_STD_NTSC_M_JP| | ||
| 253 | V4L2_STD_NTSC_M_KR), | ||
| 254 | .frameperiod = | ||
| 255 | { | ||
| 256 | .numerator = 1001, | ||
| 257 | .denominator= 30000 | ||
| 258 | }, | ||
| 259 | .framelines = 525, | ||
| 260 | .reserved = {0,0,0,0} | ||
| 261 | }, { // This is a total wild guess | ||
| 262 | .id = (TSTD_60), | ||
| 263 | .frameperiod = | ||
| 264 | { | ||
| 265 | .numerator = 1001, | ||
| 266 | .denominator= 30000 | ||
| 267 | }, | ||
| 268 | .framelines = 525, | ||
| 269 | .reserved = {0,0,0,0} | ||
| 270 | }, { // This is total wild guess | ||
| 271 | .id = V4L2_STD_NTSC_443, | ||
| 272 | .frameperiod = | ||
| 273 | { | ||
| 274 | .numerator = 1001, | ||
| 275 | .denominator= 30000 | ||
| 276 | }, | ||
| 277 | .framelines = 525, | ||
| 278 | .reserved = {0,0,0,0} | ||
| 279 | } | ||
| 280 | }; | ||
| 281 | |||
| 282 | #define generic_standards_cnt (sizeof(generic_standards)/sizeof(generic_standards[0])) | ||
| 283 | |||
| 284 | static struct v4l2_standard *match_std(v4l2_std_id id) | ||
| 285 | { | ||
| 286 | unsigned int idx; | ||
| 287 | for (idx = 0; idx < generic_standards_cnt; idx++) { | ||
| 288 | if (generic_standards[idx].id & id) { | ||
| 289 | return generic_standards + idx; | ||
| 290 | } | ||
| 291 | } | ||
| 292 | return 0; | ||
| 293 | } | ||
| 294 | |||
| 295 | static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id) | ||
| 296 | { | ||
| 297 | struct v4l2_standard *template; | ||
| 298 | int idx; | ||
| 299 | unsigned int bcnt; | ||
| 300 | template = match_std(id); | ||
| 301 | if (!template) return 0; | ||
| 302 | idx = std->index; | ||
| 303 | memcpy(std,template,sizeof(*template)); | ||
| 304 | std->index = idx; | ||
| 305 | std->id = id; | ||
| 306 | bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id); | ||
| 307 | std->name[bcnt] = 0; | ||
| 308 | pvr2_trace(PVR2_TRACE_INIT,"Set up standard idx=%u name=%s", | ||
| 309 | std->index,std->name); | ||
| 310 | return !0; | ||
| 311 | } | ||
| 312 | |||
| 313 | /* These are special cases of combined standards that we should enumerate | ||
| 314 | separately if the component pieces are present. */ | ||
| 315 | static v4l2_std_id std_mixes[] = { | ||
| 316 | V4L2_STD_PAL_B | V4L2_STD_PAL_G, | ||
| 317 | V4L2_STD_PAL_D | V4L2_STD_PAL_K, | ||
| 318 | V4L2_STD_SECAM_B | V4L2_STD_SECAM_G, | ||
| 319 | V4L2_STD_SECAM_D | V4L2_STD_SECAM_K, | ||
| 320 | }; | ||
| 321 | |||
| 322 | struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, | ||
| 323 | v4l2_std_id id) | ||
| 324 | { | ||
| 325 | unsigned int std_cnt = 0; | ||
| 326 | unsigned int idx,bcnt,idx2; | ||
| 327 | v4l2_std_id idmsk,cmsk,fmsk; | ||
| 328 | struct v4l2_standard *stddefs; | ||
| 329 | |||
| 330 | if (pvrusb2_debug & PVR2_TRACE_INIT) { | ||
| 331 | char buf[50]; | ||
| 332 | bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id); | ||
| 333 | pvr2_trace( | ||
| 334 | PVR2_TRACE_INIT,"Mapping standards mask=0x%x (%.*s)", | ||
| 335 | (int)id,bcnt,buf); | ||
| 336 | } | ||
| 337 | |||
| 338 | *countptr = 0; | ||
| 339 | std_cnt = 0; | ||
| 340 | fmsk = 0; | ||
| 341 | for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) { | ||
| 342 | if (!(idmsk & cmsk)) continue; | ||
| 343 | cmsk &= ~idmsk; | ||
| 344 | if (match_std(idmsk)) { | ||
| 345 | std_cnt++; | ||
| 346 | continue; | ||
| 347 | } | ||
| 348 | fmsk |= idmsk; | ||
| 349 | } | ||
| 350 | |||
| 351 | for (idx2 = 0; idx2 < sizeof(std_mixes)/sizeof(std_mixes[0]); idx2++) { | ||
| 352 | if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; | ||
| 353 | } | ||
| 354 | |||
| 355 | if (fmsk) { | ||
| 356 | char buf[50]; | ||
| 357 | bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); | ||
| 358 | pvr2_trace( | ||
| 359 | PVR2_TRACE_ERROR_LEGS, | ||
| 360 | "WARNING:" | ||
| 361 | " Failed to classify the following standard(s): %.*s", | ||
| 362 | bcnt,buf); | ||
| 363 | } | ||
| 364 | |||
| 365 | pvr2_trace(PVR2_TRACE_INIT,"Setting up %u unique standard(s)", | ||
| 366 | std_cnt); | ||
| 367 | if (!std_cnt) return 0; // paranoia | ||
| 368 | |||
| 369 | stddefs = kmalloc(sizeof(struct v4l2_standard) * std_cnt, | ||
| 370 | GFP_KERNEL); | ||
| 371 | memset(stddefs,0,sizeof(struct v4l2_standard) * std_cnt); | ||
| 372 | for (idx = 0; idx < std_cnt; idx++) stddefs[idx].index = idx; | ||
| 373 | |||
| 374 | idx = 0; | ||
| 375 | |||
| 376 | /* Enumerate potential special cases */ | ||
| 377 | for (idx2 = 0; ((idx2 < sizeof(std_mixes)/sizeof(std_mixes[0])) && | ||
| 378 | (idx < std_cnt)); idx2++) { | ||
| 379 | if (!(id & std_mixes[idx2])) continue; | ||
| 380 | if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++; | ||
| 381 | } | ||
| 382 | /* Now enumerate individual pieces */ | ||
| 383 | for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) { | ||
| 384 | if (!(idmsk & cmsk)) continue; | ||
| 385 | cmsk &= ~idmsk; | ||
| 386 | if (!pvr2_std_fill(stddefs+idx,idmsk)) continue; | ||
| 387 | idx++; | ||
| 388 | } | ||
| 389 | |||
| 390 | *countptr = std_cnt; | ||
| 391 | return stddefs; | ||
| 392 | } | ||
| 393 | |||
| 394 | v4l2_std_id pvr2_std_get_usable(void) | ||
| 395 | { | ||
| 396 | return CSTD_ALL; | ||
| 397 | } | ||
| 398 | |||
| 399 | |||
| 400 | /* | ||
| 401 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 402 | *** Local Variables: *** | ||
| 403 | *** mode: c *** | ||
| 404 | *** fill-column: 75 *** | ||
| 405 | *** tab-width: 8 *** | ||
| 406 | *** c-basic-offset: 8 *** | ||
| 407 | *** End: *** | ||
| 408 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.h b/drivers/media/video/pvrusb2/pvrusb2-std.h new file mode 100644 index 000000000000..07c399375341 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-std.h | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_STD_H | ||
| 22 | #define __PVRUSB2_STD_H | ||
| 23 | |||
| 24 | #include <linux/videodev2.h> | ||
| 25 | |||
| 26 | // Convert string describing one or more video standards into a mask of V4L | ||
| 27 | // standard bits. Return true if conversion succeeds otherwise return | ||
| 28 | // false. String is expected to be of the form: C1-x/y;C2-a/b where C1 and | ||
| 29 | // C2 are color system names (e.g. "PAL", "NTSC") and x, y, a, and b are | ||
| 30 | // modulation schemes (e.g. "M", "B", "G", etc). | ||
| 31 | int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, | ||
| 32 | unsigned int bufSize); | ||
| 33 | |||
| 34 | // Convert any arbitrary set of video standard bits into an unambiguous | ||
| 35 | // readable string. Return value is the number of bytes consumed in the | ||
| 36 | // buffer. The formatted string is of a form that can be parsed by our | ||
| 37 | // sibling std_std_to_id() function. | ||
| 38 | unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, | ||
| 39 | v4l2_std_id id); | ||
| 40 | |||
| 41 | // Create an array of suitable v4l2_standard structures given a bit mask of | ||
| 42 | // video standards to support. The array is allocated from the heap, and | ||
| 43 | // the number of elements is returned in the first argument. | ||
| 44 | struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, | ||
| 45 | v4l2_std_id id); | ||
| 46 | |||
| 47 | // Return mask of which video standard bits are valid | ||
| 48 | v4l2_std_id pvr2_std_get_usable(void); | ||
| 49 | |||
| 50 | #endif /* __PVRUSB2_STD_H */ | ||
| 51 | |||
| 52 | /* | ||
| 53 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 54 | *** Local Variables: *** | ||
| 55 | *** mode: c *** | ||
| 56 | *** fill-column: 75 *** | ||
| 57 | *** tab-width: 8 *** | ||
| 58 | *** c-basic-offset: 8 *** | ||
| 59 | *** End: *** | ||
| 60 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c new file mode 100644 index 000000000000..c6e6523d74b4 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c | |||
| @@ -0,0 +1,865 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/config.h> | ||
| 23 | #include <linux/string.h> | ||
| 24 | #include <linux/slab.h> | ||
| 25 | #include <asm/semaphore.h> | ||
| 26 | #include "pvrusb2-sysfs.h" | ||
| 27 | #include "pvrusb2-hdw.h" | ||
| 28 | #include "pvrusb2-debug.h" | ||
| 29 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||
| 30 | #include "pvrusb2-debugifc.h" | ||
| 31 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||
| 32 | |||
| 33 | #define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__) | ||
| 34 | |||
| 35 | struct pvr2_sysfs { | ||
| 36 | struct pvr2_channel channel; | ||
| 37 | struct class_device *class_dev; | ||
| 38 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||
| 39 | struct pvr2_sysfs_debugifc *debugifc; | ||
| 40 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||
| 41 | struct pvr2_sysfs_ctl_item *item_first; | ||
| 42 | struct pvr2_sysfs_ctl_item *item_last; | ||
| 43 | struct sysfs_ops kops; | ||
| 44 | struct kobj_type ktype; | ||
| 45 | struct class_device_attribute attr_v4l_minor_number; | ||
| 46 | struct class_device_attribute attr_unit_number; | ||
| 47 | }; | ||
| 48 | |||
| 49 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||
| 50 | struct pvr2_sysfs_debugifc { | ||
| 51 | struct class_device_attribute attr_debugcmd; | ||
| 52 | struct class_device_attribute attr_debuginfo; | ||
| 53 | }; | ||
| 54 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||
| 55 | |||
| 56 | struct pvr2_sysfs_ctl_item { | ||
| 57 | struct class_device_attribute attr_name; | ||
| 58 | struct class_device_attribute attr_type; | ||
| 59 | struct class_device_attribute attr_min; | ||
| 60 | struct class_device_attribute attr_max; | ||
| 61 | struct class_device_attribute attr_enum; | ||
| 62 | struct class_device_attribute attr_bits; | ||
| 63 | struct class_device_attribute attr_val; | ||
| 64 | struct class_device_attribute attr_custom; | ||
| 65 | struct pvr2_ctrl *cptr; | ||
| 66 | struct pvr2_sysfs *chptr; | ||
| 67 | struct pvr2_sysfs_ctl_item *item_next; | ||
| 68 | struct attribute *attr_gen[7]; | ||
| 69 | struct attribute_group grp; | ||
| 70 | char name[80]; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct pvr2_sysfs_class { | ||
| 74 | struct class class; | ||
| 75 | }; | ||
| 76 | |||
| 77 | static ssize_t show_name(int id,struct class_device *class_dev,char *buf) | ||
| 78 | { | ||
| 79 | struct pvr2_ctrl *cptr; | ||
| 80 | struct pvr2_sysfs *sfp; | ||
| 81 | const char *name; | ||
| 82 | |||
| 83 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 84 | if (!sfp) return -EINVAL; | ||
| 85 | cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); | ||
| 86 | if (!cptr) return -EINVAL; | ||
| 87 | |||
| 88 | name = pvr2_ctrl_get_desc(cptr); | ||
| 89 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",sfp,id,name); | ||
| 90 | |||
| 91 | if (!name) return -EINVAL; | ||
| 92 | |||
| 93 | return scnprintf(buf,PAGE_SIZE,"%s\n",name); | ||
| 94 | } | ||
| 95 | |||
| 96 | static ssize_t show_type(int id,struct class_device *class_dev,char *buf) | ||
| 97 | { | ||
| 98 | struct pvr2_ctrl *cptr; | ||
| 99 | struct pvr2_sysfs *sfp; | ||
| 100 | const char *name; | ||
| 101 | enum pvr2_ctl_type tp; | ||
| 102 | |||
| 103 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 104 | if (!sfp) return -EINVAL; | ||
| 105 | cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); | ||
| 106 | if (!cptr) return -EINVAL; | ||
| 107 | |||
| 108 | tp = pvr2_ctrl_get_type(cptr); | ||
| 109 | switch (tp) { | ||
| 110 | case pvr2_ctl_int: name = "integer"; break; | ||
| 111 | case pvr2_ctl_enum: name = "enum"; break; | ||
| 112 | case pvr2_ctl_bitmask: name = "bitmask"; break; | ||
| 113 | case pvr2_ctl_bool: name = "boolean"; break; | ||
| 114 | default: name = "?"; break; | ||
| 115 | } | ||
| 116 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s",sfp,id,name); | ||
| 117 | |||
| 118 | if (!name) return -EINVAL; | ||
| 119 | |||
| 120 | return scnprintf(buf,PAGE_SIZE,"%s\n",name); | ||
| 121 | } | ||
| 122 | |||
| 123 | static ssize_t show_min(int id,struct class_device *class_dev,char *buf) | ||
| 124 | { | ||
| 125 | struct pvr2_ctrl *cptr; | ||
| 126 | struct pvr2_sysfs *sfp; | ||
| 127 | long val; | ||
| 128 | |||
| 129 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 130 | if (!sfp) return -EINVAL; | ||
| 131 | cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); | ||
| 132 | if (!cptr) return -EINVAL; | ||
| 133 | val = pvr2_ctrl_get_min(cptr); | ||
| 134 | |||
| 135 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",sfp,id,val); | ||
| 136 | |||
| 137 | return scnprintf(buf,PAGE_SIZE,"%ld\n",val); | ||
| 138 | } | ||
| 139 | |||
| 140 | static ssize_t show_max(int id,struct class_device *class_dev,char *buf) | ||
| 141 | { | ||
| 142 | struct pvr2_ctrl *cptr; | ||
| 143 | struct pvr2_sysfs *sfp; | ||
| 144 | long val; | ||
| 145 | |||
| 146 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 147 | if (!sfp) return -EINVAL; | ||
| 148 | cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); | ||
| 149 | if (!cptr) return -EINVAL; | ||
| 150 | val = pvr2_ctrl_get_max(cptr); | ||
| 151 | |||
| 152 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",sfp,id,val); | ||
| 153 | |||
| 154 | return scnprintf(buf,PAGE_SIZE,"%ld\n",val); | ||
| 155 | } | ||
| 156 | |||
| 157 | static ssize_t show_val_norm(int id,struct class_device *class_dev,char *buf) | ||
| 158 | { | ||
| 159 | struct pvr2_ctrl *cptr; | ||
| 160 | struct pvr2_sysfs *sfp; | ||
| 161 | int val,ret; | ||
| 162 | unsigned int cnt = 0; | ||
| 163 | |||
| 164 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 165 | if (!sfp) return -EINVAL; | ||
| 166 | cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); | ||
| 167 | if (!cptr) return -EINVAL; | ||
| 168 | |||
| 169 | ret = pvr2_ctrl_get_value(cptr,&val); | ||
| 170 | if (ret < 0) return ret; | ||
| 171 | |||
| 172 | ret = pvr2_ctrl_value_to_sym(cptr,~0,val, | ||
| 173 | buf,PAGE_SIZE-1,&cnt); | ||
| 174 | |||
| 175 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)", | ||
| 176 | sfp,id,cnt,buf,val); | ||
| 177 | buf[cnt] = '\n'; | ||
| 178 | return cnt+1; | ||
| 179 | } | ||
| 180 | |||
| 181 | static ssize_t show_val_custom(int id,struct class_device *class_dev,char *buf) | ||
| 182 | { | ||
| 183 | struct pvr2_ctrl *cptr; | ||
| 184 | struct pvr2_sysfs *sfp; | ||
| 185 | int val,ret; | ||
| 186 | unsigned int cnt = 0; | ||
| 187 | |||
| 188 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 189 | if (!sfp) return -EINVAL; | ||
| 190 | cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); | ||
| 191 | if (!cptr) return -EINVAL; | ||
| 192 | |||
| 193 | ret = pvr2_ctrl_get_value(cptr,&val); | ||
| 194 | if (ret < 0) return ret; | ||
| 195 | |||
| 196 | ret = pvr2_ctrl_custom_value_to_sym(cptr,~0,val, | ||
| 197 | buf,PAGE_SIZE-1,&cnt); | ||
| 198 | |||
| 199 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)", | ||
| 200 | sfp,id,cnt,buf,val); | ||
| 201 | buf[cnt] = '\n'; | ||
| 202 | return cnt+1; | ||
| 203 | } | ||
| 204 | |||
| 205 | static ssize_t show_enum(int id,struct class_device *class_dev,char *buf) | ||
| 206 | { | ||
| 207 | struct pvr2_ctrl *cptr; | ||
| 208 | struct pvr2_sysfs *sfp; | ||
| 209 | long val; | ||
| 210 | unsigned int bcnt,ccnt,ecnt; | ||
| 211 | |||
| 212 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 213 | if (!sfp) return -EINVAL; | ||
| 214 | cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); | ||
| 215 | if (!cptr) return -EINVAL; | ||
| 216 | ecnt = pvr2_ctrl_get_cnt(cptr); | ||
| 217 | bcnt = 0; | ||
| 218 | for (val = 0; val < ecnt; val++) { | ||
| 219 | pvr2_ctrl_get_valname(cptr,val,buf+bcnt,PAGE_SIZE-bcnt,&ccnt); | ||
| 220 | if (!ccnt) continue; | ||
| 221 | bcnt += ccnt; | ||
| 222 | if (bcnt >= PAGE_SIZE) break; | ||
| 223 | buf[bcnt] = '\n'; | ||
| 224 | bcnt++; | ||
| 225 | } | ||
| 226 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",sfp,id); | ||
| 227 | return bcnt; | ||
| 228 | } | ||
| 229 | |||
| 230 | static ssize_t show_bits(int id,struct class_device *class_dev,char *buf) | ||
| 231 | { | ||
| 232 | struct pvr2_ctrl *cptr; | ||
| 233 | struct pvr2_sysfs *sfp; | ||
| 234 | int valid_bits,msk; | ||
| 235 | unsigned int bcnt,ccnt; | ||
| 236 | |||
| 237 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 238 | if (!sfp) return -EINVAL; | ||
| 239 | cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); | ||
| 240 | if (!cptr) return -EINVAL; | ||
| 241 | valid_bits = pvr2_ctrl_get_mask(cptr); | ||
| 242 | bcnt = 0; | ||
| 243 | for (msk = 1; valid_bits; msk <<= 1) { | ||
| 244 | if (!(msk & valid_bits)) continue; | ||
| 245 | valid_bits &= ~msk; | ||
| 246 | pvr2_ctrl_get_valname(cptr,msk,buf+bcnt,PAGE_SIZE-bcnt,&ccnt); | ||
| 247 | bcnt += ccnt; | ||
| 248 | if (bcnt >= PAGE_SIZE) break; | ||
| 249 | buf[bcnt] = '\n'; | ||
| 250 | bcnt++; | ||
| 251 | } | ||
| 252 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",sfp,id); | ||
| 253 | return bcnt; | ||
| 254 | } | ||
| 255 | |||
| 256 | static int store_val_any(int id,int customfl,struct pvr2_sysfs *sfp, | ||
| 257 | const char *buf,unsigned int count) | ||
| 258 | { | ||
| 259 | struct pvr2_ctrl *cptr; | ||
| 260 | int ret; | ||
| 261 | int mask,val; | ||
| 262 | |||
| 263 | cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); | ||
| 264 | if (customfl) { | ||
| 265 | ret = pvr2_ctrl_custom_sym_to_value(cptr,buf,count,&mask,&val); | ||
| 266 | } else { | ||
| 267 | ret = pvr2_ctrl_sym_to_value(cptr,buf,count,&mask,&val); | ||
| 268 | } | ||
| 269 | if (ret < 0) return ret; | ||
| 270 | ret = pvr2_ctrl_set_mask_value(cptr,mask,val); | ||
| 271 | pvr2_hdw_commit_ctl(sfp->channel.hdw); | ||
| 272 | return ret; | ||
| 273 | } | ||
| 274 | |||
| 275 | static ssize_t store_val_norm(int id,struct class_device *class_dev, | ||
| 276 | const char *buf,size_t count) | ||
| 277 | { | ||
| 278 | struct pvr2_sysfs *sfp; | ||
| 279 | int ret; | ||
| 280 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 281 | ret = store_val_any(id,0,sfp,buf,count); | ||
| 282 | if (!ret) ret = count; | ||
| 283 | return ret; | ||
| 284 | } | ||
| 285 | |||
| 286 | static ssize_t store_val_custom(int id,struct class_device *class_dev, | ||
| 287 | const char *buf,size_t count) | ||
| 288 | { | ||
| 289 | struct pvr2_sysfs *sfp; | ||
| 290 | int ret; | ||
| 291 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 292 | ret = store_val_any(id,1,sfp,buf,count); | ||
| 293 | if (!ret) ret = count; | ||
| 294 | return ret; | ||
| 295 | } | ||
| 296 | |||
| 297 | /* | ||
| 298 | Mike Isely <isely@pobox.com> 30-April-2005 | ||
| 299 | |||
| 300 | This next batch of horrible preprocessor hackery is needed because the | ||
| 301 | kernel's class_device_attribute mechanism fails to pass the actual | ||
| 302 | attribute through to the show / store functions, which means we have no | ||
| 303 | way to package up any attribute-specific parameters, like for example the | ||
| 304 | control id. So we work around this brain-damage by encoding the control | ||
| 305 | id into the show / store functions themselves and pick the function based | ||
| 306 | on the control id we're setting up. These macros try to ease the pain. | ||
| 307 | Yuck. | ||
| 308 | */ | ||
| 309 | |||
| 310 | #define CREATE_SHOW_INSTANCE(sf_name,ctl_id) \ | ||
| 311 | static ssize_t sf_name##_##ctl_id(struct class_device *class_dev,char *buf) \ | ||
| 312 | { return sf_name(ctl_id,class_dev,buf); } | ||
| 313 | |||
| 314 | #define CREATE_STORE_INSTANCE(sf_name,ctl_id) \ | ||
| 315 | static ssize_t sf_name##_##ctl_id(struct class_device *class_dev,const char *buf,size_t count) \ | ||
| 316 | { return sf_name(ctl_id,class_dev,buf,count); } | ||
| 317 | |||
| 318 | #define CREATE_BATCH(ctl_id) \ | ||
| 319 | CREATE_SHOW_INSTANCE(show_name,ctl_id) \ | ||
| 320 | CREATE_SHOW_INSTANCE(show_type,ctl_id) \ | ||
| 321 | CREATE_SHOW_INSTANCE(show_min,ctl_id) \ | ||
| 322 | CREATE_SHOW_INSTANCE(show_max,ctl_id) \ | ||
| 323 | CREATE_SHOW_INSTANCE(show_val_norm,ctl_id) \ | ||
| 324 | CREATE_SHOW_INSTANCE(show_val_custom,ctl_id) \ | ||
| 325 | CREATE_SHOW_INSTANCE(show_enum,ctl_id) \ | ||
| 326 | CREATE_SHOW_INSTANCE(show_bits,ctl_id) \ | ||
| 327 | CREATE_STORE_INSTANCE(store_val_norm,ctl_id) \ | ||
| 328 | CREATE_STORE_INSTANCE(store_val_custom,ctl_id) \ | ||
| 329 | |||
| 330 | CREATE_BATCH(0) | ||
| 331 | CREATE_BATCH(1) | ||
| 332 | CREATE_BATCH(2) | ||
| 333 | CREATE_BATCH(3) | ||
| 334 | CREATE_BATCH(4) | ||
| 335 | CREATE_BATCH(5) | ||
| 336 | CREATE_BATCH(6) | ||
| 337 | CREATE_BATCH(7) | ||
| 338 | CREATE_BATCH(8) | ||
| 339 | CREATE_BATCH(9) | ||
| 340 | CREATE_BATCH(10) | ||
| 341 | CREATE_BATCH(11) | ||
| 342 | CREATE_BATCH(12) | ||
| 343 | CREATE_BATCH(13) | ||
| 344 | CREATE_BATCH(14) | ||
| 345 | CREATE_BATCH(15) | ||
| 346 | CREATE_BATCH(16) | ||
| 347 | CREATE_BATCH(17) | ||
| 348 | CREATE_BATCH(18) | ||
| 349 | CREATE_BATCH(19) | ||
| 350 | CREATE_BATCH(20) | ||
| 351 | CREATE_BATCH(21) | ||
| 352 | CREATE_BATCH(22) | ||
| 353 | CREATE_BATCH(23) | ||
| 354 | CREATE_BATCH(24) | ||
| 355 | CREATE_BATCH(25) | ||
| 356 | CREATE_BATCH(26) | ||
| 357 | CREATE_BATCH(27) | ||
| 358 | CREATE_BATCH(28) | ||
| 359 | CREATE_BATCH(29) | ||
| 360 | CREATE_BATCH(30) | ||
| 361 | CREATE_BATCH(31) | ||
| 362 | CREATE_BATCH(32) | ||
| 363 | CREATE_BATCH(33) | ||
| 364 | CREATE_BATCH(34) | ||
| 365 | CREATE_BATCH(35) | ||
| 366 | CREATE_BATCH(36) | ||
| 367 | CREATE_BATCH(37) | ||
| 368 | CREATE_BATCH(38) | ||
| 369 | CREATE_BATCH(39) | ||
| 370 | CREATE_BATCH(40) | ||
| 371 | CREATE_BATCH(41) | ||
| 372 | CREATE_BATCH(42) | ||
| 373 | CREATE_BATCH(43) | ||
| 374 | CREATE_BATCH(44) | ||
| 375 | CREATE_BATCH(45) | ||
| 376 | CREATE_BATCH(46) | ||
| 377 | CREATE_BATCH(47) | ||
| 378 | CREATE_BATCH(48) | ||
| 379 | CREATE_BATCH(49) | ||
| 380 | CREATE_BATCH(50) | ||
| 381 | CREATE_BATCH(51) | ||
| 382 | CREATE_BATCH(52) | ||
| 383 | CREATE_BATCH(53) | ||
| 384 | CREATE_BATCH(54) | ||
| 385 | CREATE_BATCH(55) | ||
| 386 | CREATE_BATCH(56) | ||
| 387 | CREATE_BATCH(57) | ||
| 388 | CREATE_BATCH(58) | ||
| 389 | CREATE_BATCH(59) | ||
| 390 | |||
| 391 | struct pvr2_sysfs_func_set { | ||
| 392 | ssize_t (*show_name)(struct class_device *,char *); | ||
| 393 | ssize_t (*show_type)(struct class_device *,char *); | ||
| 394 | ssize_t (*show_min)(struct class_device *,char *); | ||
| 395 | ssize_t (*show_max)(struct class_device *,char *); | ||
| 396 | ssize_t (*show_enum)(struct class_device *,char *); | ||
| 397 | ssize_t (*show_bits)(struct class_device *,char *); | ||
| 398 | ssize_t (*show_val_norm)(struct class_device *,char *); | ||
| 399 | ssize_t (*store_val_norm)(struct class_device *, | ||
| 400 | const char *,size_t); | ||
| 401 | ssize_t (*show_val_custom)(struct class_device *,char *); | ||
| 402 | ssize_t (*store_val_custom)(struct class_device *, | ||
| 403 | const char *,size_t); | ||
| 404 | }; | ||
| 405 | |||
| 406 | #define INIT_BATCH(ctl_id) \ | ||
| 407 | [ctl_id] = { \ | ||
| 408 | .show_name = show_name_##ctl_id, \ | ||
| 409 | .show_type = show_type_##ctl_id, \ | ||
| 410 | .show_min = show_min_##ctl_id, \ | ||
| 411 | .show_max = show_max_##ctl_id, \ | ||
| 412 | .show_enum = show_enum_##ctl_id, \ | ||
| 413 | .show_bits = show_bits_##ctl_id, \ | ||
| 414 | .show_val_norm = show_val_norm_##ctl_id, \ | ||
| 415 | .store_val_norm = store_val_norm_##ctl_id, \ | ||
| 416 | .show_val_custom = show_val_custom_##ctl_id, \ | ||
| 417 | .store_val_custom = store_val_custom_##ctl_id, \ | ||
| 418 | } \ | ||
| 419 | |||
| 420 | static struct pvr2_sysfs_func_set funcs[] = { | ||
| 421 | INIT_BATCH(0), | ||
| 422 | INIT_BATCH(1), | ||
| 423 | INIT_BATCH(2), | ||
| 424 | INIT_BATCH(3), | ||
| 425 | INIT_BATCH(4), | ||
| 426 | INIT_BATCH(5), | ||
| 427 | INIT_BATCH(6), | ||
| 428 | INIT_BATCH(7), | ||
| 429 | INIT_BATCH(8), | ||
| 430 | INIT_BATCH(9), | ||
| 431 | INIT_BATCH(10), | ||
| 432 | INIT_BATCH(11), | ||
| 433 | INIT_BATCH(12), | ||
| 434 | INIT_BATCH(13), | ||
| 435 | INIT_BATCH(14), | ||
| 436 | INIT_BATCH(15), | ||
| 437 | INIT_BATCH(16), | ||
| 438 | INIT_BATCH(17), | ||
| 439 | INIT_BATCH(18), | ||
| 440 | INIT_BATCH(19), | ||
| 441 | INIT_BATCH(20), | ||
| 442 | INIT_BATCH(21), | ||
| 443 | INIT_BATCH(22), | ||
| 444 | INIT_BATCH(23), | ||
| 445 | INIT_BATCH(24), | ||
| 446 | INIT_BATCH(25), | ||
| 447 | INIT_BATCH(26), | ||
| 448 | INIT_BATCH(27), | ||
| 449 | INIT_BATCH(28), | ||
| 450 | INIT_BATCH(29), | ||
| 451 | INIT_BATCH(30), | ||
| 452 | INIT_BATCH(31), | ||
| 453 | INIT_BATCH(32), | ||
| 454 | INIT_BATCH(33), | ||
| 455 | INIT_BATCH(34), | ||
| 456 | INIT_BATCH(35), | ||
| 457 | INIT_BATCH(36), | ||
| 458 | INIT_BATCH(37), | ||
| 459 | INIT_BATCH(38), | ||
| 460 | INIT_BATCH(39), | ||
| 461 | INIT_BATCH(40), | ||
| 462 | INIT_BATCH(41), | ||
| 463 | INIT_BATCH(42), | ||
| 464 | INIT_BATCH(43), | ||
| 465 | INIT_BATCH(44), | ||
| 466 | INIT_BATCH(45), | ||
| 467 | INIT_BATCH(46), | ||
| 468 | INIT_BATCH(47), | ||
| 469 | INIT_BATCH(48), | ||
| 470 | INIT_BATCH(49), | ||
| 471 | INIT_BATCH(50), | ||
| 472 | INIT_BATCH(51), | ||
| 473 | INIT_BATCH(52), | ||
| 474 | INIT_BATCH(53), | ||
| 475 | INIT_BATCH(54), | ||
| 476 | INIT_BATCH(55), | ||
| 477 | INIT_BATCH(56), | ||
| 478 | INIT_BATCH(57), | ||
| 479 | INIT_BATCH(58), | ||
| 480 | INIT_BATCH(59), | ||
| 481 | }; | ||
| 482 | |||
| 483 | |||
| 484 | static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) | ||
| 485 | { | ||
| 486 | struct pvr2_sysfs_ctl_item *cip; | ||
| 487 | struct pvr2_sysfs_func_set *fp; | ||
| 488 | struct pvr2_ctrl *cptr; | ||
| 489 | unsigned int cnt,acnt; | ||
| 490 | |||
| 491 | if ((ctl_id < 0) || (ctl_id >= (sizeof(funcs)/sizeof(funcs[0])))) { | ||
| 492 | return; | ||
| 493 | } | ||
| 494 | |||
| 495 | fp = funcs + ctl_id; | ||
| 496 | cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id); | ||
| 497 | if (!cptr) return; | ||
| 498 | |||
| 499 | cip = kmalloc(sizeof(*cip),GFP_KERNEL); | ||
| 500 | if (!cip) return; | ||
| 501 | memset(cip,0,sizeof(*cip)); | ||
| 502 | pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip); | ||
| 503 | |||
| 504 | cip->cptr = cptr; | ||
| 505 | |||
| 506 | cip->chptr = sfp; | ||
| 507 | cip->item_next = 0; | ||
| 508 | if (sfp->item_last) { | ||
| 509 | sfp->item_last->item_next = cip; | ||
| 510 | } else { | ||
| 511 | sfp->item_first = cip; | ||
| 512 | } | ||
| 513 | sfp->item_last = cip; | ||
| 514 | |||
| 515 | cip->attr_name.attr.owner = THIS_MODULE; | ||
| 516 | cip->attr_name.attr.name = "name"; | ||
| 517 | cip->attr_name.attr.mode = S_IRUGO; | ||
| 518 | cip->attr_name.show = fp->show_name; | ||
| 519 | |||
| 520 | cip->attr_type.attr.owner = THIS_MODULE; | ||
| 521 | cip->attr_type.attr.name = "type"; | ||
| 522 | cip->attr_type.attr.mode = S_IRUGO; | ||
| 523 | cip->attr_type.show = fp->show_type; | ||
| 524 | |||
| 525 | cip->attr_min.attr.owner = THIS_MODULE; | ||
| 526 | cip->attr_min.attr.name = "min_val"; | ||
| 527 | cip->attr_min.attr.mode = S_IRUGO; | ||
| 528 | cip->attr_min.show = fp->show_min; | ||
| 529 | |||
| 530 | cip->attr_max.attr.owner = THIS_MODULE; | ||
| 531 | cip->attr_max.attr.name = "max_val"; | ||
| 532 | cip->attr_max.attr.mode = S_IRUGO; | ||
| 533 | cip->attr_max.show = fp->show_max; | ||
| 534 | |||
| 535 | cip->attr_val.attr.owner = THIS_MODULE; | ||
| 536 | cip->attr_val.attr.name = "cur_val"; | ||
| 537 | cip->attr_val.attr.mode = S_IRUGO; | ||
| 538 | |||
| 539 | cip->attr_custom.attr.owner = THIS_MODULE; | ||
| 540 | cip->attr_custom.attr.name = "custom_val"; | ||
| 541 | cip->attr_custom.attr.mode = S_IRUGO; | ||
| 542 | |||
| 543 | cip->attr_enum.attr.owner = THIS_MODULE; | ||
| 544 | cip->attr_enum.attr.name = "enum_val"; | ||
| 545 | cip->attr_enum.attr.mode = S_IRUGO; | ||
| 546 | cip->attr_enum.show = fp->show_enum; | ||
| 547 | |||
| 548 | cip->attr_bits.attr.owner = THIS_MODULE; | ||
| 549 | cip->attr_bits.attr.name = "bit_val"; | ||
| 550 | cip->attr_bits.attr.mode = S_IRUGO; | ||
| 551 | cip->attr_bits.show = fp->show_bits; | ||
| 552 | |||
| 553 | if (pvr2_ctrl_is_writable(cptr)) { | ||
| 554 | cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP; | ||
| 555 | cip->attr_custom.attr.mode |= S_IWUSR|S_IWGRP; | ||
| 556 | } | ||
| 557 | |||
| 558 | acnt = 0; | ||
| 559 | cip->attr_gen[acnt++] = &cip->attr_name.attr; | ||
| 560 | cip->attr_gen[acnt++] = &cip->attr_type.attr; | ||
| 561 | cip->attr_gen[acnt++] = &cip->attr_val.attr; | ||
| 562 | cip->attr_val.show = fp->show_val_norm; | ||
| 563 | cip->attr_val.store = fp->store_val_norm; | ||
| 564 | if (pvr2_ctrl_has_custom_symbols(cptr)) { | ||
| 565 | cip->attr_gen[acnt++] = &cip->attr_custom.attr; | ||
| 566 | cip->attr_custom.show = fp->show_val_custom; | ||
| 567 | cip->attr_custom.store = fp->store_val_custom; | ||
| 568 | } | ||
| 569 | switch (pvr2_ctrl_get_type(cptr)) { | ||
| 570 | case pvr2_ctl_enum: | ||
| 571 | // Control is an enumeration | ||
| 572 | cip->attr_gen[acnt++] = &cip->attr_enum.attr; | ||
| 573 | break; | ||
| 574 | case pvr2_ctl_int: | ||
| 575 | // Control is an integer | ||
| 576 | cip->attr_gen[acnt++] = &cip->attr_min.attr; | ||
| 577 | cip->attr_gen[acnt++] = &cip->attr_max.attr; | ||
| 578 | break; | ||
| 579 | case pvr2_ctl_bitmask: | ||
| 580 | // Control is an bitmask | ||
| 581 | cip->attr_gen[acnt++] = &cip->attr_bits.attr; | ||
| 582 | break; | ||
| 583 | default: break; | ||
| 584 | } | ||
| 585 | |||
| 586 | cnt = scnprintf(cip->name,sizeof(cip->name)-1,"ctl_%s", | ||
| 587 | pvr2_ctrl_get_name(cptr)); | ||
| 588 | cip->name[cnt] = 0; | ||
| 589 | cip->grp.name = cip->name; | ||
| 590 | cip->grp.attrs = cip->attr_gen; | ||
| 591 | |||
| 592 | sysfs_create_group(&sfp->class_dev->kobj,&cip->grp); | ||
| 593 | } | ||
| 594 | |||
| 595 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||
| 596 | static ssize_t debuginfo_show(struct class_device *,char *); | ||
| 597 | static ssize_t debugcmd_show(struct class_device *,char *); | ||
| 598 | static ssize_t debugcmd_store(struct class_device *,const char *,size_t count); | ||
| 599 | |||
| 600 | static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp) | ||
| 601 | { | ||
| 602 | struct pvr2_sysfs_debugifc *dip; | ||
| 603 | dip = kmalloc(sizeof(*dip),GFP_KERNEL); | ||
| 604 | if (!dip) return; | ||
| 605 | memset(dip,0,sizeof(*dip)); | ||
| 606 | dip->attr_debugcmd.attr.owner = THIS_MODULE; | ||
| 607 | dip->attr_debugcmd.attr.name = "debugcmd"; | ||
| 608 | dip->attr_debugcmd.attr.mode = S_IRUGO|S_IWUSR|S_IWGRP; | ||
| 609 | dip->attr_debugcmd.show = debugcmd_show; | ||
| 610 | dip->attr_debugcmd.store = debugcmd_store; | ||
| 611 | dip->attr_debuginfo.attr.owner = THIS_MODULE; | ||
| 612 | dip->attr_debuginfo.attr.name = "debuginfo"; | ||
| 613 | dip->attr_debuginfo.attr.mode = S_IRUGO; | ||
| 614 | dip->attr_debuginfo.show = debuginfo_show; | ||
| 615 | sfp->debugifc = dip; | ||
| 616 | class_device_create_file(sfp->class_dev,&dip->attr_debugcmd); | ||
| 617 | class_device_create_file(sfp->class_dev,&dip->attr_debuginfo); | ||
| 618 | } | ||
| 619 | |||
| 620 | |||
| 621 | static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp) | ||
| 622 | { | ||
| 623 | if (!sfp->debugifc) return; | ||
| 624 | class_device_remove_file(sfp->class_dev, | ||
| 625 | &sfp->debugifc->attr_debuginfo); | ||
| 626 | class_device_remove_file(sfp->class_dev,&sfp->debugifc->attr_debugcmd); | ||
| 627 | kfree(sfp->debugifc); | ||
| 628 | sfp->debugifc = 0; | ||
| 629 | } | ||
| 630 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||
| 631 | |||
| 632 | |||
| 633 | static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp) | ||
| 634 | { | ||
| 635 | unsigned int idx,cnt; | ||
| 636 | cnt = pvr2_hdw_get_ctrl_count(sfp->channel.hdw); | ||
| 637 | for (idx = 0; idx < cnt; idx++) { | ||
| 638 | pvr2_sysfs_add_control(sfp,idx); | ||
| 639 | } | ||
| 640 | } | ||
| 641 | |||
| 642 | |||
| 643 | static void pvr2_sysfs_tear_down_controls(struct pvr2_sysfs *sfp) | ||
| 644 | { | ||
| 645 | struct pvr2_sysfs_ctl_item *cip1,*cip2; | ||
| 646 | for (cip1 = sfp->item_first; cip1; cip1 = cip2) { | ||
| 647 | cip2 = cip1->item_next; | ||
| 648 | sysfs_remove_group(&sfp->class_dev->kobj,&cip1->grp); | ||
| 649 | pvr2_sysfs_trace("Destroying pvr2_sysfs_ctl_item id=%p",cip1); | ||
| 650 | kfree(cip1); | ||
| 651 | } | ||
| 652 | } | ||
| 653 | |||
| 654 | |||
| 655 | static void pvr2_sysfs_class_release(struct class *class) | ||
| 656 | { | ||
| 657 | struct pvr2_sysfs_class *clp; | ||
| 658 | clp = container_of(class,struct pvr2_sysfs_class,class); | ||
| 659 | pvr2_sysfs_trace("Destroying pvr2_sysfs_class id=%p",clp); | ||
| 660 | kfree(clp); | ||
| 661 | } | ||
| 662 | |||
| 663 | |||
| 664 | static void pvr2_sysfs_release(struct class_device *class_dev) | ||
| 665 | { | ||
| 666 | pvr2_sysfs_trace("Releasing class_dev id=%p",class_dev); | ||
| 667 | kfree(class_dev); | ||
| 668 | } | ||
| 669 | |||
| 670 | |||
| 671 | static void class_dev_destroy(struct pvr2_sysfs *sfp) | ||
| 672 | { | ||
| 673 | if (!sfp->class_dev) return; | ||
| 674 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||
| 675 | pvr2_sysfs_tear_down_debugifc(sfp); | ||
| 676 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||
| 677 | pvr2_sysfs_tear_down_controls(sfp); | ||
| 678 | class_device_remove_file(sfp->class_dev,&sfp->attr_v4l_minor_number); | ||
| 679 | class_device_remove_file(sfp->class_dev,&sfp->attr_unit_number); | ||
| 680 | pvr2_sysfs_trace("Destroying class_dev id=%p",sfp->class_dev); | ||
| 681 | sfp->class_dev->class_data = 0; | ||
| 682 | class_device_unregister(sfp->class_dev); | ||
| 683 | sfp->class_dev = 0; | ||
| 684 | } | ||
| 685 | |||
| 686 | |||
| 687 | static ssize_t v4l_minor_number_show(struct class_device *class_dev,char *buf) | ||
| 688 | { | ||
| 689 | struct pvr2_sysfs *sfp; | ||
| 690 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 691 | if (!sfp) return -EINVAL; | ||
| 692 | return scnprintf(buf,PAGE_SIZE,"%d\n", | ||
| 693 | pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw)); | ||
| 694 | } | ||
| 695 | |||
| 696 | |||
| 697 | static ssize_t unit_number_show(struct class_device *class_dev,char *buf) | ||
| 698 | { | ||
| 699 | struct pvr2_sysfs *sfp; | ||
| 700 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 701 | if (!sfp) return -EINVAL; | ||
| 702 | return scnprintf(buf,PAGE_SIZE,"%d\n", | ||
| 703 | pvr2_hdw_get_unit_number(sfp->channel.hdw)); | ||
| 704 | } | ||
| 705 | |||
| 706 | |||
| 707 | static void class_dev_create(struct pvr2_sysfs *sfp, | ||
| 708 | struct pvr2_sysfs_class *class_ptr) | ||
| 709 | { | ||
| 710 | struct usb_device *usb_dev; | ||
| 711 | struct class_device *class_dev; | ||
| 712 | usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw); | ||
| 713 | if (!usb_dev) return; | ||
| 714 | class_dev = kmalloc(sizeof(*class_dev),GFP_KERNEL); | ||
| 715 | if (!class_dev) return; | ||
| 716 | memset(class_dev,0,sizeof(*class_dev)); | ||
| 717 | |||
| 718 | pvr2_sysfs_trace("Creating class_dev id=%p",class_dev); | ||
| 719 | |||
| 720 | class_dev->class = &class_ptr->class; | ||
| 721 | if (pvr2_hdw_get_sn(sfp->channel.hdw)) { | ||
| 722 | snprintf(class_dev->class_id,BUS_ID_SIZE,"sn-%lu", | ||
| 723 | pvr2_hdw_get_sn(sfp->channel.hdw)); | ||
| 724 | } else if (pvr2_hdw_get_unit_number(sfp->channel.hdw) >= 0) { | ||
| 725 | snprintf(class_dev->class_id,BUS_ID_SIZE,"unit-%c", | ||
| 726 | pvr2_hdw_get_unit_number(sfp->channel.hdw) + 'a'); | ||
| 727 | } else { | ||
| 728 | kfree(class_dev); | ||
| 729 | return; | ||
| 730 | } | ||
| 731 | |||
| 732 | class_dev->dev = &usb_dev->dev; | ||
| 733 | |||
| 734 | sfp->class_dev = class_dev; | ||
| 735 | class_dev->class_data = sfp; | ||
| 736 | class_device_register(class_dev); | ||
| 737 | |||
| 738 | sfp->attr_v4l_minor_number.attr.owner = THIS_MODULE; | ||
| 739 | sfp->attr_v4l_minor_number.attr.name = "v4l_minor_number"; | ||
| 740 | sfp->attr_v4l_minor_number.attr.mode = S_IRUGO; | ||
| 741 | sfp->attr_v4l_minor_number.show = v4l_minor_number_show; | ||
| 742 | sfp->attr_v4l_minor_number.store = 0; | ||
| 743 | class_device_create_file(sfp->class_dev,&sfp->attr_v4l_minor_number); | ||
| 744 | sfp->attr_unit_number.attr.owner = THIS_MODULE; | ||
| 745 | sfp->attr_unit_number.attr.name = "unit_number"; | ||
| 746 | sfp->attr_unit_number.attr.mode = S_IRUGO; | ||
| 747 | sfp->attr_unit_number.show = unit_number_show; | ||
| 748 | sfp->attr_unit_number.store = 0; | ||
| 749 | class_device_create_file(sfp->class_dev,&sfp->attr_unit_number); | ||
| 750 | |||
| 751 | pvr2_sysfs_add_controls(sfp); | ||
| 752 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||
| 753 | pvr2_sysfs_add_debugifc(sfp); | ||
| 754 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||
| 755 | } | ||
| 756 | |||
| 757 | |||
| 758 | static void pvr2_sysfs_internal_check(struct pvr2_channel *chp) | ||
| 759 | { | ||
| 760 | struct pvr2_sysfs *sfp; | ||
| 761 | sfp = container_of(chp,struct pvr2_sysfs,channel); | ||
| 762 | if (!sfp->channel.mc_head->disconnect_flag) return; | ||
| 763 | pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_sysfs id=%p",sfp); | ||
| 764 | class_dev_destroy(sfp); | ||
| 765 | pvr2_channel_done(&sfp->channel); | ||
| 766 | kfree(sfp); | ||
| 767 | } | ||
| 768 | |||
| 769 | |||
| 770 | struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *mp, | ||
| 771 | struct pvr2_sysfs_class *class_ptr) | ||
| 772 | { | ||
| 773 | struct pvr2_sysfs *sfp; | ||
| 774 | sfp = kmalloc(sizeof(*sfp),GFP_KERNEL); | ||
| 775 | if (!sfp) return sfp; | ||
| 776 | memset(sfp,0,sizeof(*sfp)); | ||
| 777 | pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_sysfs id=%p",sfp); | ||
| 778 | pvr2_channel_init(&sfp->channel,mp); | ||
| 779 | sfp->channel.check_func = pvr2_sysfs_internal_check; | ||
| 780 | |||
| 781 | class_dev_create(sfp,class_ptr); | ||
| 782 | return sfp; | ||
| 783 | } | ||
| 784 | |||
| 785 | |||
| 786 | static int pvr2_sysfs_hotplug(struct class_device *cd,char **envp, | ||
| 787 | int numenvp,char *buf,int size) | ||
| 788 | { | ||
| 789 | /* Even though we don't do anything here, we still need this function | ||
| 790 | because sysfs will still try to call it. */ | ||
| 791 | return 0; | ||
| 792 | } | ||
| 793 | |||
| 794 | struct pvr2_sysfs_class *pvr2_sysfs_class_create(void) | ||
| 795 | { | ||
| 796 | struct pvr2_sysfs_class *clp; | ||
| 797 | clp = kmalloc(sizeof(*clp),GFP_KERNEL); | ||
| 798 | if (!clp) return clp; | ||
| 799 | memset(clp,0,sizeof(*clp)); | ||
| 800 | pvr2_sysfs_trace("Creating pvr2_sysfs_class id=%p",clp); | ||
| 801 | clp->class.name = "pvrusb2"; | ||
| 802 | clp->class.class_release = pvr2_sysfs_class_release; | ||
| 803 | clp->class.release = pvr2_sysfs_release; | ||
| 804 | clp->class.uevent = pvr2_sysfs_hotplug; | ||
| 805 | if (class_register(&clp->class)) { | ||
| 806 | pvr2_sysfs_trace( | ||
| 807 | "Registration failed for pvr2_sysfs_class id=%p",clp); | ||
| 808 | kfree(clp); | ||
| 809 | clp = 0; | ||
| 810 | } | ||
| 811 | return clp; | ||
| 812 | } | ||
| 813 | |||
| 814 | |||
| 815 | void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp) | ||
| 816 | { | ||
| 817 | class_unregister(&clp->class); | ||
| 818 | } | ||
| 819 | |||
| 820 | |||
| 821 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||
| 822 | static ssize_t debuginfo_show(struct class_device *class_dev,char *buf) | ||
| 823 | { | ||
| 824 | struct pvr2_sysfs *sfp; | ||
| 825 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 826 | if (!sfp) return -EINVAL; | ||
| 827 | pvr2_hdw_trigger_module_log(sfp->channel.hdw); | ||
| 828 | return pvr2_debugifc_print_info(sfp->channel.hdw,buf,PAGE_SIZE); | ||
| 829 | } | ||
| 830 | |||
| 831 | |||
| 832 | static ssize_t debugcmd_show(struct class_device *class_dev,char *buf) | ||
| 833 | { | ||
| 834 | struct pvr2_sysfs *sfp; | ||
| 835 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 836 | if (!sfp) return -EINVAL; | ||
| 837 | return pvr2_debugifc_print_status(sfp->channel.hdw,buf,PAGE_SIZE); | ||
| 838 | } | ||
| 839 | |||
| 840 | |||
| 841 | static ssize_t debugcmd_store(struct class_device *class_dev, | ||
| 842 | const char *buf,size_t count) | ||
| 843 | { | ||
| 844 | struct pvr2_sysfs *sfp; | ||
| 845 | int ret; | ||
| 846 | |||
| 847 | sfp = (struct pvr2_sysfs *)class_dev->class_data; | ||
| 848 | if (!sfp) return -EINVAL; | ||
| 849 | |||
| 850 | ret = pvr2_debugifc_docmd(sfp->channel.hdw,buf,count); | ||
| 851 | if (ret < 0) return ret; | ||
| 852 | return count; | ||
| 853 | } | ||
| 854 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||
| 855 | |||
| 856 | |||
| 857 | /* | ||
| 858 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 859 | *** Local Variables: *** | ||
| 860 | *** mode: c *** | ||
| 861 | *** fill-column: 75 *** | ||
| 862 | *** tab-width: 8 *** | ||
| 863 | *** c-basic-offset: 8 *** | ||
| 864 | *** End: *** | ||
| 865 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.h b/drivers/media/video/pvrusb2/pvrusb2-sysfs.h new file mode 100644 index 000000000000..ff9373b47f8f --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_SYSFS_H | ||
| 22 | #define __PVRUSB2_SYSFS_H | ||
| 23 | |||
| 24 | #include <linux/list.h> | ||
| 25 | #include <linux/sysfs.h> | ||
| 26 | #include "pvrusb2-context.h" | ||
| 27 | |||
| 28 | struct pvr2_sysfs; | ||
| 29 | struct pvr2_sysfs_class; | ||
| 30 | |||
| 31 | struct pvr2_sysfs_class *pvr2_sysfs_class_create(void); | ||
| 32 | void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *); | ||
| 33 | |||
| 34 | struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *, | ||
| 35 | struct pvr2_sysfs_class *); | ||
| 36 | |||
| 37 | #endif /* __PVRUSB2_SYSFS_H */ | ||
| 38 | |||
| 39 | /* | ||
| 40 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 41 | *** Local Variables: *** | ||
| 42 | *** mode: c *** | ||
| 43 | *** fill-column: 75 *** | ||
| 44 | *** tab-width: 8 *** | ||
| 45 | *** c-basic-offset: 8 *** | ||
| 46 | *** End: *** | ||
| 47 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/drivers/media/video/pvrusb2/pvrusb2-tuner.c new file mode 100644 index 000000000000..f4aba8144ce0 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-tuner.c | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include "pvrusb2.h" | ||
| 24 | #include "pvrusb2-util.h" | ||
| 25 | #include "pvrusb2-tuner.h" | ||
| 26 | #include "pvrusb2-hdw-internal.h" | ||
| 27 | #include "pvrusb2-debug.h" | ||
| 28 | #include <linux/videodev2.h> | ||
| 29 | #include <media/tuner.h> | ||
| 30 | #include <media/v4l2-common.h> | ||
| 31 | |||
| 32 | struct pvr2_tuner_handler { | ||
| 33 | struct pvr2_hdw *hdw; | ||
| 34 | struct pvr2_i2c_client *client; | ||
| 35 | struct pvr2_i2c_handler i2c_handler; | ||
| 36 | int type_update_fl; | ||
| 37 | }; | ||
| 38 | |||
| 39 | |||
| 40 | static void set_type(struct pvr2_tuner_handler *ctxt) | ||
| 41 | { | ||
| 42 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 43 | struct tuner_setup setup; | ||
| 44 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c tuner set_type(%d)",hdw->tuner_type); | ||
| 45 | if (((int)(hdw->tuner_type)) < 0) return; | ||
| 46 | |||
| 47 | setup.addr = ADDR_UNSET; | ||
| 48 | setup.type = hdw->tuner_type; | ||
| 49 | setup.mode_mask = T_RADIO | T_ANALOG_TV; | ||
| 50 | /* We may really want mode_mask to be T_ANALOG_TV for now */ | ||
| 51 | pvr2_i2c_client_cmd(ctxt->client,TUNER_SET_TYPE_ADDR,&setup); | ||
| 52 | ctxt->type_update_fl = 0; | ||
| 53 | } | ||
| 54 | |||
| 55 | |||
| 56 | static int tuner_check(struct pvr2_tuner_handler *ctxt) | ||
| 57 | { | ||
| 58 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 59 | if (hdw->tuner_updated) ctxt->type_update_fl = !0; | ||
| 60 | return ctxt->type_update_fl != 0; | ||
| 61 | } | ||
| 62 | |||
| 63 | |||
| 64 | static void tuner_update(struct pvr2_tuner_handler *ctxt) | ||
| 65 | { | ||
| 66 | if (ctxt->type_update_fl) set_type(ctxt); | ||
| 67 | } | ||
| 68 | |||
| 69 | |||
| 70 | static void pvr2_tuner_detach(struct pvr2_tuner_handler *ctxt) | ||
| 71 | { | ||
| 72 | ctxt->client->handler = 0; | ||
| 73 | kfree(ctxt); | ||
| 74 | } | ||
| 75 | |||
| 76 | |||
| 77 | static unsigned int pvr2_tuner_describe(struct pvr2_tuner_handler *ctxt,char *buf,unsigned int cnt) | ||
| 78 | { | ||
| 79 | return scnprintf(buf,cnt,"handler: pvrusb2-tuner"); | ||
| 80 | } | ||
| 81 | |||
| 82 | |||
| 83 | const static struct pvr2_i2c_handler_functions tuner_funcs = { | ||
| 84 | .detach = (void (*)(void *))pvr2_tuner_detach, | ||
| 85 | .check = (int (*)(void *))tuner_check, | ||
| 86 | .update = (void (*)(void *))tuner_update, | ||
| 87 | .describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_tuner_describe, | ||
| 88 | }; | ||
| 89 | |||
| 90 | |||
| 91 | int pvr2_i2c_tuner_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) | ||
| 92 | { | ||
| 93 | struct pvr2_tuner_handler *ctxt; | ||
| 94 | if (cp->handler) return 0; | ||
| 95 | |||
| 96 | ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); | ||
| 97 | if (!ctxt) return 0; | ||
| 98 | memset(ctxt,0,sizeof(*ctxt)); | ||
| 99 | |||
| 100 | ctxt->i2c_handler.func_data = ctxt; | ||
| 101 | ctxt->i2c_handler.func_table = &tuner_funcs; | ||
| 102 | ctxt->type_update_fl = !0; | ||
| 103 | ctxt->client = cp; | ||
| 104 | ctxt->hdw = hdw; | ||
| 105 | cp->handler = &ctxt->i2c_handler; | ||
| 106 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x tuner handler set up", | ||
| 107 | cp->client->addr); | ||
| 108 | return !0; | ||
| 109 | } | ||
| 110 | |||
| 111 | |||
| 112 | |||
| 113 | |||
| 114 | /* | ||
| 115 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 116 | *** Local Variables: *** | ||
| 117 | *** mode: c *** | ||
| 118 | *** fill-column: 70 *** | ||
| 119 | *** tab-width: 8 *** | ||
| 120 | *** c-basic-offset: 8 *** | ||
| 121 | *** End: *** | ||
| 122 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.h b/drivers/media/video/pvrusb2/pvrusb2-tuner.h new file mode 100644 index 000000000000..556f12aa9160 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-tuner.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_TUNER_H | ||
| 22 | #define __PVRUSB2_TUNER_H | ||
| 23 | |||
| 24 | #include "pvrusb2-i2c-core.h" | ||
| 25 | |||
| 26 | int pvr2_i2c_tuner_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); | ||
| 27 | |||
| 28 | #endif /* __PVRUSB2_TUNER_H */ | ||
| 29 | |||
| 30 | /* | ||
| 31 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 32 | *** Local Variables: *** | ||
| 33 | *** mode: c *** | ||
| 34 | *** fill-column: 70 *** | ||
| 35 | *** tab-width: 8 *** | ||
| 36 | *** c-basic-offset: 8 *** | ||
| 37 | *** End: *** | ||
| 38 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-util.h b/drivers/media/video/pvrusb2/pvrusb2-util.h new file mode 100644 index 000000000000..e53aee416f56 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-util.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_UTIL_H | ||
| 22 | #define __PVRUSB2_UTIL_H | ||
| 23 | |||
| 24 | #define PVR2_DECOMPOSE_LE(t,i,d) \ | ||
| 25 | do { \ | ||
| 26 | (t)[i] = (d) & 0xff;\ | ||
| 27 | (t)[i+1] = ((d) >> 8) & 0xff;\ | ||
| 28 | (t)[i+2] = ((d) >> 16) & 0xff;\ | ||
| 29 | (t)[i+3] = ((d) >> 24) & 0xff;\ | ||
| 30 | } while(0) | ||
| 31 | |||
| 32 | #define PVR2_DECOMPOSE_BE(t,i,d) \ | ||
| 33 | do { \ | ||
| 34 | (t)[i+3] = (d) & 0xff;\ | ||
| 35 | (t)[i+2] = ((d) >> 8) & 0xff;\ | ||
| 36 | (t)[i+1] = ((d) >> 16) & 0xff;\ | ||
| 37 | (t)[i] = ((d) >> 24) & 0xff;\ | ||
| 38 | } while(0) | ||
| 39 | |||
| 40 | #define PVR2_COMPOSE_LE(t,i) \ | ||
| 41 | ((((u32)((t)[i+3])) << 24) | \ | ||
| 42 | (((u32)((t)[i+2])) << 16) | \ | ||
| 43 | (((u32)((t)[i+1])) << 8) | \ | ||
| 44 | ((u32)((t)[i]))) | ||
| 45 | |||
| 46 | #define PVR2_COMPOSE_BE(t,i) \ | ||
| 47 | ((((u32)((t)[i])) << 24) | \ | ||
| 48 | (((u32)((t)[i+1])) << 16) | \ | ||
| 49 | (((u32)((t)[i+2])) << 8) | \ | ||
| 50 | ((u32)((t)[i+3]))) | ||
| 51 | |||
| 52 | |||
| 53 | #endif /* __PVRUSB2_UTIL_H */ | ||
| 54 | |||
| 55 | /* | ||
| 56 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 57 | *** Local Variables: *** | ||
| 58 | *** mode: c *** | ||
| 59 | *** fill-column: 75 *** | ||
| 60 | *** tab-width: 8 *** | ||
| 61 | *** c-basic-offset: 8 *** | ||
| 62 | *** End: *** | ||
| 63 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c new file mode 100644 index 000000000000..961951010c27 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | |||
| @@ -0,0 +1,1126 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/version.h> | ||
| 25 | #include "pvrusb2-context.h" | ||
| 26 | #include "pvrusb2-hdw.h" | ||
| 27 | #include "pvrusb2.h" | ||
| 28 | #include "pvrusb2-debug.h" | ||
| 29 | #include "pvrusb2-v4l2.h" | ||
| 30 | #include "pvrusb2-ioread.h" | ||
| 31 | #include <linux/videodev2.h> | ||
| 32 | #include <media/v4l2-common.h> | ||
| 33 | |||
| 34 | struct pvr2_v4l2_dev; | ||
| 35 | struct pvr2_v4l2_fh; | ||
| 36 | struct pvr2_v4l2; | ||
| 37 | |||
| 38 | /* V4L no longer provide the ability to set / get a private context pointer | ||
| 39 | (i.e. video_get_drvdata / video_set_drvdata), which means we have to | ||
| 40 | concoct our own context locating mechanism. Supposedly this is intended | ||
| 41 | to simplify driver implementation. It's not clear to me how that can | ||
| 42 | possibly be true. Our solution here is to maintain a lookup table of | ||
| 43 | our context instances, indexed by the minor device number of the V4L | ||
| 44 | device. See pvr2_v4l2_open() for some implications of this approach. */ | ||
| 45 | static struct pvr2_v4l2_dev *devices[256]; | ||
| 46 | static DEFINE_MUTEX(device_lock); | ||
| 47 | |||
| 48 | struct pvr2_v4l2_dev { | ||
| 49 | struct pvr2_v4l2 *v4lp; | ||
| 50 | struct video_device *vdev; | ||
| 51 | struct pvr2_context_stream *stream; | ||
| 52 | int ctxt_idx; | ||
| 53 | enum pvr2_config config; | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct pvr2_v4l2_fh { | ||
| 57 | struct pvr2_channel channel; | ||
| 58 | struct pvr2_v4l2_dev *dev_info; | ||
| 59 | enum v4l2_priority prio; | ||
| 60 | struct pvr2_ioread *rhp; | ||
| 61 | struct file *file; | ||
| 62 | struct pvr2_v4l2 *vhead; | ||
| 63 | struct pvr2_v4l2_fh *vnext; | ||
| 64 | struct pvr2_v4l2_fh *vprev; | ||
| 65 | wait_queue_head_t wait_data; | ||
| 66 | int fw_mode_flag; | ||
| 67 | }; | ||
| 68 | |||
| 69 | struct pvr2_v4l2 { | ||
| 70 | struct pvr2_channel channel; | ||
| 71 | struct pvr2_v4l2_fh *vfirst; | ||
| 72 | struct pvr2_v4l2_fh *vlast; | ||
| 73 | |||
| 74 | struct v4l2_prio_state prio; | ||
| 75 | |||
| 76 | /* streams */ | ||
| 77 | struct pvr2_v4l2_dev video_dev; | ||
| 78 | }; | ||
| 79 | |||
| 80 | static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; | ||
| 81 | module_param_array(video_nr, int, NULL, 0444); | ||
| 82 | MODULE_PARM_DESC(video_nr, "Offset for device's minor"); | ||
| 83 | |||
| 84 | struct v4l2_capability pvr_capability ={ | ||
| 85 | .driver = "pvrusb2", | ||
| 86 | .card = "Hauppauge WinTV pvr-usb2", | ||
| 87 | .bus_info = "usb", | ||
| 88 | .version = KERNEL_VERSION(0,8,0), | ||
| 89 | .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | | ||
| 90 | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | | ||
| 91 | V4L2_CAP_READWRITE), | ||
| 92 | .reserved = {0,0,0,0} | ||
| 93 | }; | ||
| 94 | |||
| 95 | static struct v4l2_tuner pvr_v4l2_tuners[]= { | ||
| 96 | { | ||
| 97 | .index = 0, | ||
| 98 | .name = "TV Tuner", | ||
| 99 | .type = V4L2_TUNER_ANALOG_TV, | ||
| 100 | .capability = (V4L2_TUNER_CAP_NORM | | ||
| 101 | V4L2_TUNER_CAP_STEREO | | ||
| 102 | V4L2_TUNER_CAP_LANG1 | | ||
| 103 | V4L2_TUNER_CAP_LANG2), | ||
| 104 | .rangelow = 0, | ||
| 105 | .rangehigh = 0, | ||
| 106 | .rxsubchans = V4L2_TUNER_SUB_STEREO, | ||
| 107 | .audmode = V4L2_TUNER_MODE_STEREO, | ||
| 108 | .signal = 0, | ||
| 109 | .afc = 0, | ||
| 110 | .reserved = {0,0,0,0} | ||
| 111 | } | ||
| 112 | }; | ||
| 113 | |||
| 114 | struct v4l2_fmtdesc pvr_fmtdesc [] = { | ||
| 115 | { | ||
| 116 | .index = 0, | ||
| 117 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
| 118 | .flags = V4L2_FMT_FLAG_COMPRESSED, | ||
| 119 | .description = "MPEG1/2", | ||
| 120 | // This should really be V4L2_PIX_FMT_MPEG, but xawtv | ||
| 121 | // breaks when I do that. | ||
| 122 | .pixelformat = 0, // V4L2_PIX_FMT_MPEG, | ||
| 123 | .reserved = { 0, 0, 0, 0 } | ||
| 124 | } | ||
| 125 | }; | ||
| 126 | |||
| 127 | #define PVR_FORMAT_PIX 0 | ||
| 128 | #define PVR_FORMAT_VBI 1 | ||
| 129 | |||
| 130 | struct v4l2_format pvr_format [] = { | ||
| 131 | [PVR_FORMAT_PIX] = { | ||
| 132 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
| 133 | .fmt = { | ||
| 134 | .pix = { | ||
| 135 | .width = 720, | ||
| 136 | .height = 576, | ||
| 137 | // This should really be V4L2_PIX_FMT_MPEG, | ||
| 138 | // but xawtv breaks when I do that. | ||
| 139 | .pixelformat = 0, // V4L2_PIX_FMT_MPEG, | ||
| 140 | .field = V4L2_FIELD_INTERLACED, | ||
| 141 | .bytesperline = 0, // doesn't make sense | ||
| 142 | // here | ||
| 143 | //FIXME : Don't know what to put here... | ||
| 144 | .sizeimage = (32*1024), | ||
| 145 | .colorspace = 0, // doesn't make sense here | ||
| 146 | .priv = 0 | ||
| 147 | } | ||
| 148 | } | ||
| 149 | }, | ||
| 150 | [PVR_FORMAT_VBI] = { | ||
| 151 | .type = V4L2_BUF_TYPE_VBI_CAPTURE, | ||
| 152 | .fmt = { | ||
| 153 | .vbi = { | ||
| 154 | .sampling_rate = 27000000, | ||
| 155 | .offset = 248, | ||
| 156 | .samples_per_line = 1443, | ||
| 157 | .sample_format = V4L2_PIX_FMT_GREY, | ||
| 158 | .start = { 0, 0 }, | ||
| 159 | .count = { 0, 0 }, | ||
| 160 | .flags = 0, | ||
| 161 | .reserved = { 0, 0 } | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | ||
| 165 | }; | ||
| 166 | |||
| 167 | /* | ||
| 168 | * pvr_ioctl() | ||
| 169 | * | ||
| 170 | * This is part of Video 4 Linux API. The procedure handles ioctl() calls. | ||
| 171 | * | ||
| 172 | */ | ||
| 173 | static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, | ||
| 174 | unsigned int cmd, void *arg) | ||
| 175 | { | ||
| 176 | struct pvr2_v4l2_fh *fh = file->private_data; | ||
| 177 | struct pvr2_v4l2 *vp = fh->vhead; | ||
| 178 | struct pvr2_v4l2_dev *dev_info = fh->dev_info; | ||
| 179 | struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; | ||
| 180 | int ret = -EINVAL; | ||
| 181 | |||
| 182 | if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { | ||
| 183 | v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),cmd); | ||
| 184 | } | ||
| 185 | |||
| 186 | if (!pvr2_hdw_dev_ok(hdw)) { | ||
| 187 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
| 188 | "ioctl failed - bad or no context"); | ||
| 189 | return -EFAULT; | ||
| 190 | } | ||
| 191 | |||
| 192 | /* check priority */ | ||
| 193 | switch (cmd) { | ||
| 194 | case VIDIOC_S_CTRL: | ||
| 195 | case VIDIOC_S_STD: | ||
| 196 | case VIDIOC_S_INPUT: | ||
| 197 | case VIDIOC_S_TUNER: | ||
| 198 | case VIDIOC_S_FREQUENCY: | ||
| 199 | ret = v4l2_prio_check(&vp->prio, &fh->prio); | ||
| 200 | if (ret) | ||
| 201 | return ret; | ||
| 202 | } | ||
| 203 | |||
| 204 | switch (cmd) { | ||
| 205 | case VIDIOC_QUERYCAP: | ||
| 206 | { | ||
| 207 | struct v4l2_capability *cap = arg; | ||
| 208 | |||
| 209 | memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); | ||
| 210 | |||
| 211 | ret = 0; | ||
| 212 | break; | ||
| 213 | } | ||
| 214 | |||
| 215 | case VIDIOC_G_PRIORITY: | ||
| 216 | { | ||
| 217 | enum v4l2_priority *p = arg; | ||
| 218 | |||
| 219 | *p = v4l2_prio_max(&vp->prio); | ||
| 220 | ret = 0; | ||
| 221 | break; | ||
| 222 | } | ||
| 223 | |||
| 224 | case VIDIOC_S_PRIORITY: | ||
| 225 | { | ||
| 226 | enum v4l2_priority *prio = arg; | ||
| 227 | |||
| 228 | ret = v4l2_prio_change(&vp->prio, &fh->prio, *prio); | ||
| 229 | break; | ||
| 230 | } | ||
| 231 | |||
| 232 | case VIDIOC_ENUMSTD: | ||
| 233 | { | ||
| 234 | struct v4l2_standard *vs = (struct v4l2_standard *)arg; | ||
| 235 | int idx = vs->index; | ||
| 236 | ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx+1); | ||
| 237 | break; | ||
| 238 | } | ||
| 239 | |||
| 240 | case VIDIOC_G_STD: | ||
| 241 | { | ||
| 242 | int val = 0; | ||
| 243 | ret = pvr2_ctrl_get_value( | ||
| 244 | pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),&val); | ||
| 245 | *(v4l2_std_id *)arg = val; | ||
| 246 | break; | ||
| 247 | } | ||
| 248 | |||
| 249 | case VIDIOC_S_STD: | ||
| 250 | { | ||
| 251 | ret = pvr2_ctrl_set_value( | ||
| 252 | pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR), | ||
| 253 | *(v4l2_std_id *)arg); | ||
| 254 | break; | ||
| 255 | } | ||
| 256 | |||
| 257 | case VIDIOC_ENUMINPUT: | ||
| 258 | { | ||
| 259 | struct pvr2_ctrl *cptr; | ||
| 260 | struct v4l2_input *vi = (struct v4l2_input *)arg; | ||
| 261 | struct v4l2_input tmp; | ||
| 262 | unsigned int cnt; | ||
| 263 | |||
| 264 | cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); | ||
| 265 | |||
| 266 | memset(&tmp,0,sizeof(tmp)); | ||
| 267 | tmp.index = vi->index; | ||
| 268 | ret = 0; | ||
| 269 | switch (vi->index) { | ||
| 270 | case PVR2_CVAL_INPUT_TV: | ||
| 271 | case PVR2_CVAL_INPUT_RADIO: | ||
| 272 | tmp.type = V4L2_INPUT_TYPE_TUNER; | ||
| 273 | break; | ||
| 274 | case PVR2_CVAL_INPUT_SVIDEO: | ||
| 275 | case PVR2_CVAL_INPUT_COMPOSITE: | ||
| 276 | tmp.type = V4L2_INPUT_TYPE_CAMERA; | ||
| 277 | break; | ||
| 278 | default: | ||
| 279 | ret = -EINVAL; | ||
| 280 | break; | ||
| 281 | } | ||
| 282 | if (ret < 0) break; | ||
| 283 | |||
| 284 | cnt = 0; | ||
| 285 | pvr2_ctrl_get_valname(cptr,vi->index, | ||
| 286 | tmp.name,sizeof(tmp.name)-1,&cnt); | ||
| 287 | tmp.name[cnt] = 0; | ||
| 288 | |||
| 289 | /* Don't bother with audioset, since this driver currently | ||
| 290 | always switches the audio whenever the video is | ||
| 291 | switched. */ | ||
| 292 | |||
| 293 | /* Handling std is a tougher problem. It doesn't make | ||
| 294 | sense in cases where a device might be multi-standard. | ||
| 295 | We could just copy out the current value for the | ||
| 296 | standard, but it can change over time. For now just | ||
| 297 | leave it zero. */ | ||
| 298 | |||
| 299 | memcpy(vi, &tmp, sizeof(tmp)); | ||
| 300 | |||
| 301 | ret = 0; | ||
| 302 | break; | ||
| 303 | } | ||
| 304 | |||
| 305 | case VIDIOC_G_INPUT: | ||
| 306 | { | ||
| 307 | struct pvr2_ctrl *cptr; | ||
| 308 | struct v4l2_input *vi = (struct v4l2_input *)arg; | ||
| 309 | int val; | ||
| 310 | cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); | ||
| 311 | val = 0; | ||
| 312 | ret = pvr2_ctrl_get_value(cptr,&val); | ||
| 313 | vi->index = val; | ||
| 314 | break; | ||
| 315 | } | ||
| 316 | |||
| 317 | case VIDIOC_S_INPUT: | ||
| 318 | { | ||
| 319 | struct v4l2_input *vi = (struct v4l2_input *)arg; | ||
| 320 | ret = pvr2_ctrl_set_value( | ||
| 321 | pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), | ||
| 322 | vi->index); | ||
| 323 | break; | ||
| 324 | } | ||
| 325 | |||
| 326 | case VIDIOC_ENUMAUDIO: | ||
| 327 | { | ||
| 328 | ret = -EINVAL; | ||
| 329 | break; | ||
| 330 | } | ||
| 331 | |||
| 332 | case VIDIOC_G_AUDIO: | ||
| 333 | { | ||
| 334 | ret = -EINVAL; | ||
| 335 | break; | ||
| 336 | } | ||
| 337 | |||
| 338 | case VIDIOC_S_AUDIO: | ||
| 339 | { | ||
| 340 | ret = -EINVAL; | ||
| 341 | break; | ||
| 342 | } | ||
| 343 | case VIDIOC_G_TUNER: | ||
| 344 | { | ||
| 345 | struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; | ||
| 346 | unsigned int status_mask; | ||
| 347 | int val; | ||
| 348 | if (vt->index !=0) break; | ||
| 349 | |||
| 350 | status_mask = pvr2_hdw_get_signal_status(hdw); | ||
| 351 | |||
| 352 | memcpy(vt, &pvr_v4l2_tuners[vt->index], | ||
| 353 | sizeof(struct v4l2_tuner)); | ||
| 354 | |||
| 355 | vt->signal = 0; | ||
| 356 | if (status_mask & PVR2_SIGNAL_OK) { | ||
| 357 | if (status_mask & PVR2_SIGNAL_STEREO) { | ||
| 358 | vt->rxsubchans = V4L2_TUNER_SUB_STEREO; | ||
| 359 | } else { | ||
| 360 | vt->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
| 361 | } | ||
| 362 | if (status_mask & PVR2_SIGNAL_SAP) { | ||
| 363 | vt->rxsubchans |= (V4L2_TUNER_SUB_LANG1 | | ||
| 364 | V4L2_TUNER_SUB_LANG2); | ||
| 365 | } | ||
| 366 | vt->signal = 65535; | ||
| 367 | } | ||
| 368 | |||
| 369 | val = 0; | ||
| 370 | ret = pvr2_ctrl_get_value( | ||
| 371 | pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), | ||
| 372 | &val); | ||
| 373 | vt->audmode = val; | ||
| 374 | break; | ||
| 375 | } | ||
| 376 | |||
| 377 | case VIDIOC_S_TUNER: | ||
| 378 | { | ||
| 379 | struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; | ||
| 380 | |||
| 381 | if (vt->index != 0) | ||
| 382 | break; | ||
| 383 | |||
| 384 | ret = pvr2_ctrl_set_value( | ||
| 385 | pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), | ||
| 386 | vt->audmode); | ||
| 387 | } | ||
| 388 | |||
| 389 | case VIDIOC_S_FREQUENCY: | ||
| 390 | { | ||
| 391 | const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; | ||
| 392 | ret = pvr2_ctrl_set_value( | ||
| 393 | pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), | ||
| 394 | vf->frequency * 62500); | ||
| 395 | break; | ||
| 396 | } | ||
| 397 | |||
| 398 | case VIDIOC_G_FREQUENCY: | ||
| 399 | { | ||
| 400 | struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; | ||
| 401 | int val = 0; | ||
| 402 | ret = pvr2_ctrl_get_value( | ||
| 403 | pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), | ||
| 404 | &val); | ||
| 405 | val /= 62500; | ||
| 406 | vf->frequency = val; | ||
| 407 | break; | ||
| 408 | } | ||
| 409 | |||
| 410 | case VIDIOC_ENUM_FMT: | ||
| 411 | { | ||
| 412 | struct v4l2_fmtdesc *fd = (struct v4l2_fmtdesc *)arg; | ||
| 413 | |||
| 414 | /* Only one format is supported : mpeg.*/ | ||
| 415 | if (fd->index != 0) | ||
| 416 | break; | ||
| 417 | |||
| 418 | memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc)); | ||
| 419 | ret = 0; | ||
| 420 | break; | ||
| 421 | } | ||
| 422 | |||
| 423 | case VIDIOC_G_FMT: | ||
| 424 | { | ||
| 425 | struct v4l2_format *vf = (struct v4l2_format *)arg; | ||
| 426 | int val; | ||
| 427 | switch(vf->type) { | ||
| 428 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
| 429 | memcpy(vf, &pvr_format[PVR_FORMAT_PIX], | ||
| 430 | sizeof(struct v4l2_format)); | ||
| 431 | val = 0; | ||
| 432 | pvr2_ctrl_get_value( | ||
| 433 | pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES), | ||
| 434 | &val); | ||
| 435 | vf->fmt.pix.width = val; | ||
| 436 | val = 0; | ||
| 437 | pvr2_ctrl_get_value( | ||
| 438 | pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES), | ||
| 439 | &val); | ||
| 440 | vf->fmt.pix.height = val; | ||
| 441 | ret = 0; | ||
| 442 | break; | ||
| 443 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
| 444 | // ????? Still need to figure out to do VBI correctly | ||
| 445 | ret = -EINVAL; | ||
| 446 | break; | ||
| 447 | default: | ||
| 448 | ret = -EINVAL; | ||
| 449 | break; | ||
| 450 | } | ||
| 451 | break; | ||
| 452 | } | ||
| 453 | |||
| 454 | case VIDIOC_TRY_FMT: | ||
| 455 | case VIDIOC_S_FMT: | ||
| 456 | { | ||
| 457 | struct v4l2_format *vf = (struct v4l2_format *)arg; | ||
| 458 | |||
| 459 | ret = 0; | ||
| 460 | switch(vf->type) { | ||
| 461 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: { | ||
| 462 | int h = vf->fmt.pix.height; | ||
| 463 | int w = vf->fmt.pix.width; | ||
| 464 | |||
| 465 | if (h < 200) { | ||
| 466 | h = 200; | ||
| 467 | } else if (h > 625) { | ||
| 468 | h = 625; | ||
| 469 | } | ||
| 470 | if (w < 320) { | ||
| 471 | w = 320; | ||
| 472 | } else if (w > 720) { | ||
| 473 | w = 720; | ||
| 474 | } | ||
| 475 | |||
| 476 | memcpy(vf, &pvr_format[PVR_FORMAT_PIX], | ||
| 477 | sizeof(struct v4l2_format)); | ||
| 478 | vf->fmt.pix.width = w; | ||
| 479 | vf->fmt.pix.height = h; | ||
| 480 | |||
| 481 | if (cmd == VIDIOC_S_FMT) { | ||
| 482 | pvr2_ctrl_set_value( | ||
| 483 | pvr2_hdw_get_ctrl_by_id(hdw, | ||
| 484 | PVR2_CID_HRES), | ||
| 485 | vf->fmt.pix.width); | ||
| 486 | pvr2_ctrl_set_value( | ||
| 487 | pvr2_hdw_get_ctrl_by_id(hdw, | ||
| 488 | PVR2_CID_VRES), | ||
| 489 | vf->fmt.pix.height); | ||
| 490 | } | ||
| 491 | } break; | ||
| 492 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
| 493 | // ????? Still need to figure out to do VBI correctly | ||
| 494 | ret = -EINVAL; | ||
| 495 | break; | ||
| 496 | default: | ||
| 497 | ret = -EINVAL; | ||
| 498 | break; | ||
| 499 | } | ||
| 500 | break; | ||
| 501 | } | ||
| 502 | |||
| 503 | case VIDIOC_STREAMON: | ||
| 504 | { | ||
| 505 | ret = pvr2_hdw_set_stream_type(hdw,dev_info->config); | ||
| 506 | if (ret < 0) return ret; | ||
| 507 | ret = pvr2_hdw_set_streaming(hdw,!0); | ||
| 508 | break; | ||
| 509 | } | ||
| 510 | |||
| 511 | case VIDIOC_STREAMOFF: | ||
| 512 | { | ||
| 513 | ret = pvr2_hdw_set_streaming(hdw,0); | ||
| 514 | break; | ||
| 515 | } | ||
| 516 | |||
| 517 | case VIDIOC_QUERYCTRL: | ||
| 518 | { | ||
| 519 | struct pvr2_ctrl *cptr; | ||
| 520 | struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg; | ||
| 521 | ret = 0; | ||
| 522 | if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { | ||
| 523 | cptr = pvr2_hdw_get_ctrl_nextv4l( | ||
| 524 | hdw,(vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL)); | ||
| 525 | if (cptr) vc->id = pvr2_ctrl_get_v4lid(cptr); | ||
| 526 | } else { | ||
| 527 | cptr = pvr2_hdw_get_ctrl_v4l(hdw,vc->id); | ||
| 528 | } | ||
| 529 | if (!cptr) { | ||
| 530 | pvr2_trace(PVR2_TRACE_V4LIOCTL, | ||
| 531 | "QUERYCTRL id=0x%x not implemented here", | ||
| 532 | vc->id); | ||
| 533 | ret = -EINVAL; | ||
| 534 | break; | ||
| 535 | } | ||
| 536 | |||
| 537 | pvr2_trace(PVR2_TRACE_V4LIOCTL, | ||
| 538 | "QUERYCTRL id=0x%x mapping name=%s (%s)", | ||
| 539 | vc->id,pvr2_ctrl_get_name(cptr), | ||
| 540 | pvr2_ctrl_get_desc(cptr)); | ||
| 541 | strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name)); | ||
| 542 | vc->flags = pvr2_ctrl_get_v4lflags(cptr); | ||
| 543 | vc->default_value = pvr2_ctrl_get_def(cptr); | ||
| 544 | switch (pvr2_ctrl_get_type(cptr)) { | ||
| 545 | case pvr2_ctl_enum: | ||
| 546 | vc->type = V4L2_CTRL_TYPE_MENU; | ||
| 547 | vc->minimum = 0; | ||
| 548 | vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; | ||
| 549 | vc->step = 1; | ||
| 550 | break; | ||
| 551 | case pvr2_ctl_bool: | ||
| 552 | vc->type = V4L2_CTRL_TYPE_BOOLEAN; | ||
| 553 | vc->minimum = 0; | ||
| 554 | vc->maximum = 1; | ||
| 555 | vc->step = 1; | ||
| 556 | break; | ||
| 557 | case pvr2_ctl_int: | ||
| 558 | vc->type = V4L2_CTRL_TYPE_INTEGER; | ||
| 559 | vc->minimum = pvr2_ctrl_get_min(cptr); | ||
| 560 | vc->maximum = pvr2_ctrl_get_max(cptr); | ||
| 561 | vc->step = 1; | ||
| 562 | break; | ||
| 563 | default: | ||
| 564 | pvr2_trace(PVR2_TRACE_V4LIOCTL, | ||
| 565 | "QUERYCTRL id=0x%x name=%s not mappable", | ||
| 566 | vc->id,pvr2_ctrl_get_name(cptr)); | ||
| 567 | ret = -EINVAL; | ||
| 568 | break; | ||
| 569 | } | ||
| 570 | break; | ||
| 571 | } | ||
| 572 | |||
| 573 | case VIDIOC_QUERYMENU: | ||
| 574 | { | ||
| 575 | struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg; | ||
| 576 | unsigned int cnt = 0; | ||
| 577 | ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw,vm->id), | ||
| 578 | vm->index, | ||
| 579 | vm->name,sizeof(vm->name)-1, | ||
| 580 | &cnt); | ||
| 581 | vm->name[cnt] = 0; | ||
| 582 | break; | ||
| 583 | } | ||
| 584 | |||
| 585 | case VIDIOC_G_CTRL: | ||
| 586 | { | ||
| 587 | struct v4l2_control *vc = (struct v4l2_control *)arg; | ||
| 588 | int val = 0; | ||
| 589 | ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), | ||
| 590 | &val); | ||
| 591 | vc->value = val; | ||
| 592 | break; | ||
| 593 | } | ||
| 594 | |||
| 595 | case VIDIOC_S_CTRL: | ||
| 596 | { | ||
| 597 | struct v4l2_control *vc = (struct v4l2_control *)arg; | ||
| 598 | ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), | ||
| 599 | vc->value); | ||
| 600 | break; | ||
| 601 | } | ||
| 602 | |||
| 603 | case VIDIOC_G_EXT_CTRLS: | ||
| 604 | { | ||
| 605 | struct v4l2_ext_controls *ctls = | ||
| 606 | (struct v4l2_ext_controls *)arg; | ||
| 607 | struct v4l2_ext_control *ctrl; | ||
| 608 | unsigned int idx; | ||
| 609 | int val; | ||
| 610 | for (idx = 0; idx < ctls->count; idx++) { | ||
| 611 | ctrl = ctls->controls + idx; | ||
| 612 | ret = pvr2_ctrl_get_value( | ||
| 613 | pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),&val); | ||
| 614 | if (ret) { | ||
| 615 | ctls->error_idx = idx; | ||
| 616 | break; | ||
| 617 | } | ||
| 618 | /* Ensure that if read as a 64 bit value, the user | ||
| 619 | will still get a hopefully sane value */ | ||
| 620 | ctrl->value64 = 0; | ||
| 621 | ctrl->value = val; | ||
| 622 | } | ||
| 623 | break; | ||
| 624 | } | ||
| 625 | |||
| 626 | case VIDIOC_S_EXT_CTRLS: | ||
| 627 | { | ||
| 628 | struct v4l2_ext_controls *ctls = | ||
| 629 | (struct v4l2_ext_controls *)arg; | ||
| 630 | struct v4l2_ext_control *ctrl; | ||
| 631 | unsigned int idx; | ||
| 632 | for (idx = 0; idx < ctls->count; idx++) { | ||
| 633 | ctrl = ctls->controls + idx; | ||
| 634 | ret = pvr2_ctrl_set_value( | ||
| 635 | pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id), | ||
| 636 | ctrl->value); | ||
| 637 | if (ret) { | ||
| 638 | ctls->error_idx = idx; | ||
| 639 | break; | ||
| 640 | } | ||
| 641 | } | ||
| 642 | break; | ||
| 643 | } | ||
| 644 | |||
| 645 | case VIDIOC_TRY_EXT_CTRLS: | ||
| 646 | { | ||
| 647 | struct v4l2_ext_controls *ctls = | ||
| 648 | (struct v4l2_ext_controls *)arg; | ||
| 649 | struct v4l2_ext_control *ctrl; | ||
| 650 | struct pvr2_ctrl *pctl; | ||
| 651 | unsigned int idx; | ||
| 652 | /* For the moment just validate that the requested control | ||
| 653 | actually exists. */ | ||
| 654 | for (idx = 0; idx < ctls->count; idx++) { | ||
| 655 | ctrl = ctls->controls + idx; | ||
| 656 | pctl = pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id); | ||
| 657 | if (!pctl) { | ||
| 658 | ret = -EINVAL; | ||
| 659 | ctls->error_idx = idx; | ||
| 660 | break; | ||
| 661 | } | ||
| 662 | } | ||
| 663 | break; | ||
| 664 | } | ||
| 665 | |||
| 666 | case VIDIOC_LOG_STATUS: | ||
| 667 | { | ||
| 668 | pvr2_hdw_trigger_module_log(hdw); | ||
| 669 | ret = 0; | ||
| 670 | break; | ||
| 671 | } | ||
| 672 | |||
| 673 | default : | ||
| 674 | ret = v4l_compat_translate_ioctl(inode,file,cmd, | ||
| 675 | arg,pvr2_v4l2_do_ioctl); | ||
| 676 | } | ||
| 677 | |||
| 678 | pvr2_hdw_commit_ctl(hdw); | ||
| 679 | |||
| 680 | if (ret < 0) { | ||
| 681 | if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { | ||
| 682 | pvr2_trace(PVR2_TRACE_V4LIOCTL, | ||
| 683 | "pvr2_v4l2_do_ioctl failure, ret=%d",ret); | ||
| 684 | } else { | ||
| 685 | if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { | ||
| 686 | pvr2_trace(PVR2_TRACE_V4LIOCTL, | ||
| 687 | "pvr2_v4l2_do_ioctl failure, ret=%d" | ||
| 688 | " command was:",ret); | ||
| 689 | v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), | ||
| 690 | cmd); | ||
| 691 | } | ||
| 692 | } | ||
| 693 | } else { | ||
| 694 | pvr2_trace(PVR2_TRACE_V4LIOCTL, | ||
| 695 | "pvr2_v4l2_do_ioctl complete, ret=%d (0x%x)", | ||
| 696 | ret,ret); | ||
| 697 | } | ||
| 698 | return ret; | ||
| 699 | } | ||
| 700 | |||
| 701 | |||
| 702 | static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) | ||
| 703 | { | ||
| 704 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 705 | "unregistering device video%d [%s]", | ||
| 706 | dip->vdev->minor,pvr2_config_get_name(dip->config)); | ||
| 707 | if (dip->ctxt_idx >= 0) { | ||
| 708 | mutex_lock(&device_lock); | ||
| 709 | devices[dip->ctxt_idx] = NULL; | ||
| 710 | dip->ctxt_idx = -1; | ||
| 711 | mutex_unlock(&device_lock); | ||
| 712 | } | ||
| 713 | video_unregister_device(dip->vdev); | ||
| 714 | } | ||
| 715 | |||
| 716 | |||
| 717 | static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) | ||
| 718 | { | ||
| 719 | pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,-1); | ||
| 720 | pvr2_v4l2_dev_destroy(&vp->video_dev); | ||
| 721 | |||
| 722 | pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp); | ||
| 723 | pvr2_channel_done(&vp->channel); | ||
| 724 | kfree(vp); | ||
| 725 | } | ||
| 726 | |||
| 727 | |||
| 728 | void pvr2_v4l2_internal_check(struct pvr2_channel *chp) | ||
| 729 | { | ||
| 730 | struct pvr2_v4l2 *vp; | ||
| 731 | vp = container_of(chp,struct pvr2_v4l2,channel); | ||
| 732 | if (!vp->channel.mc_head->disconnect_flag) return; | ||
| 733 | if (vp->vfirst) return; | ||
| 734 | pvr2_v4l2_destroy_no_lock(vp); | ||
| 735 | } | ||
| 736 | |||
| 737 | |||
| 738 | int pvr2_v4l2_ioctl(struct inode *inode, struct file *file, | ||
| 739 | unsigned int cmd, unsigned long arg) | ||
| 740 | { | ||
| 741 | |||
| 742 | /* Temporary hack : use ivtv api until a v4l2 one is available. */ | ||
| 743 | #define IVTV_IOC_G_CODEC 0xFFEE7703 | ||
| 744 | #define IVTV_IOC_S_CODEC 0xFFEE7704 | ||
| 745 | if (cmd == IVTV_IOC_G_CODEC || cmd == IVTV_IOC_S_CODEC) return 0; | ||
| 746 | return video_usercopy(inode, file, cmd, arg, pvr2_v4l2_do_ioctl); | ||
| 747 | } | ||
| 748 | |||
| 749 | |||
| 750 | int pvr2_v4l2_release(struct inode *inode, struct file *file) | ||
| 751 | { | ||
| 752 | struct pvr2_v4l2_fh *fhp = file->private_data; | ||
| 753 | struct pvr2_v4l2 *vp = fhp->vhead; | ||
| 754 | struct pvr2_context *mp = fhp->vhead->channel.mc_head; | ||
| 755 | |||
| 756 | pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release"); | ||
| 757 | |||
| 758 | if (fhp->rhp) { | ||
| 759 | struct pvr2_stream *sp; | ||
| 760 | struct pvr2_hdw *hdw; | ||
| 761 | hdw = fhp->channel.mc_head->hdw; | ||
| 762 | pvr2_hdw_set_streaming(hdw,0); | ||
| 763 | sp = pvr2_ioread_get_stream(fhp->rhp); | ||
| 764 | if (sp) pvr2_stream_set_callback(sp,0,0); | ||
| 765 | pvr2_ioread_destroy(fhp->rhp); | ||
| 766 | fhp->rhp = 0; | ||
| 767 | } | ||
| 768 | v4l2_prio_close(&vp->prio, &fhp->prio); | ||
| 769 | file->private_data = NULL; | ||
| 770 | |||
| 771 | pvr2_context_enter(mp); do { | ||
| 772 | if (fhp->vnext) { | ||
| 773 | fhp->vnext->vprev = fhp->vprev; | ||
| 774 | } else { | ||
| 775 | vp->vlast = fhp->vprev; | ||
| 776 | } | ||
| 777 | if (fhp->vprev) { | ||
| 778 | fhp->vprev->vnext = fhp->vnext; | ||
| 779 | } else { | ||
| 780 | vp->vfirst = fhp->vnext; | ||
| 781 | } | ||
| 782 | fhp->vnext = 0; | ||
| 783 | fhp->vprev = 0; | ||
| 784 | fhp->vhead = 0; | ||
| 785 | pvr2_channel_done(&fhp->channel); | ||
| 786 | pvr2_trace(PVR2_TRACE_STRUCT, | ||
| 787 | "Destroying pvr_v4l2_fh id=%p",fhp); | ||
| 788 | kfree(fhp); | ||
| 789 | if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) { | ||
| 790 | pvr2_v4l2_destroy_no_lock(vp); | ||
| 791 | } | ||
| 792 | } while (0); pvr2_context_exit(mp); | ||
| 793 | return 0; | ||
| 794 | } | ||
| 795 | |||
| 796 | |||
| 797 | int pvr2_v4l2_open(struct inode *inode, struct file *file) | ||
| 798 | { | ||
| 799 | struct pvr2_v4l2_dev *dip = 0; /* Our own context pointer */ | ||
| 800 | struct pvr2_v4l2_fh *fhp; | ||
| 801 | struct pvr2_v4l2 *vp; | ||
| 802 | struct pvr2_hdw *hdw; | ||
| 803 | |||
| 804 | mutex_lock(&device_lock); | ||
| 805 | /* MCI 7-Jun-2006 Even though we're just doing what amounts to an | ||
| 806 | atomic read of the device mapping array here, we still need the | ||
| 807 | mutex. The problem is that there is a tiny race possible when | ||
| 808 | we register the device. We can't update the device mapping | ||
| 809 | array until after the device has been registered, owing to the | ||
| 810 | fact that we can't know the minor device number until after the | ||
| 811 | registration succeeds. And if another thread tries to open the | ||
| 812 | device in the window of time after registration but before the | ||
| 813 | map is updated, then it will get back an erroneous null pointer | ||
| 814 | and the open will result in a spurious failure. The only way to | ||
| 815 | prevent that is to (a) be inside the mutex here before we access | ||
| 816 | the array, and (b) cover the entire registration process later | ||
| 817 | on with this same mutex. Thus if we get inside the mutex here, | ||
| 818 | then we can be assured that the registration process actually | ||
| 819 | completed correctly. This is an unhappy complication from the | ||
| 820 | use of global data in a driver that lives in a preemptible | ||
| 821 | environment. It sure would be nice if the video device itself | ||
| 822 | had a means for storing and retrieving a local context pointer. | ||
| 823 | Oh wait. It did. But now it's gone. Silly me. */ | ||
| 824 | { | ||
| 825 | unsigned int midx = iminor(file->f_dentry->d_inode); | ||
| 826 | if (midx < sizeof(devices)/sizeof(devices[0])) { | ||
| 827 | dip = devices[midx]; | ||
| 828 | } | ||
| 829 | } | ||
| 830 | mutex_unlock(&device_lock); | ||
| 831 | |||
| 832 | if (!dip) return -ENODEV; /* Should be impossible but I'm paranoid */ | ||
| 833 | |||
| 834 | vp = dip->v4lp; | ||
| 835 | hdw = vp->channel.hdw; | ||
| 836 | |||
| 837 | pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open"); | ||
| 838 | |||
| 839 | if (!pvr2_hdw_dev_ok(hdw)) { | ||
| 840 | pvr2_trace(PVR2_TRACE_OPEN_CLOSE, | ||
| 841 | "pvr2_v4l2_open: hardware not ready"); | ||
| 842 | return -EIO; | ||
| 843 | } | ||
| 844 | |||
| 845 | fhp = kmalloc(sizeof(*fhp),GFP_KERNEL); | ||
| 846 | if (!fhp) { | ||
| 847 | return -ENOMEM; | ||
| 848 | } | ||
| 849 | memset(fhp,0,sizeof(*fhp)); | ||
| 850 | |||
| 851 | init_waitqueue_head(&fhp->wait_data); | ||
| 852 | fhp->dev_info = dip; | ||
| 853 | |||
| 854 | pvr2_context_enter(vp->channel.mc_head); do { | ||
| 855 | pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); | ||
| 856 | pvr2_channel_init(&fhp->channel,vp->channel.mc_head); | ||
| 857 | fhp->vnext = 0; | ||
| 858 | fhp->vprev = vp->vlast; | ||
| 859 | if (vp->vlast) { | ||
| 860 | vp->vlast->vnext = fhp; | ||
| 861 | } else { | ||
| 862 | vp->vfirst = fhp; | ||
| 863 | } | ||
| 864 | vp->vlast = fhp; | ||
| 865 | fhp->vhead = vp; | ||
| 866 | } while (0); pvr2_context_exit(vp->channel.mc_head); | ||
| 867 | |||
| 868 | fhp->file = file; | ||
| 869 | file->private_data = fhp; | ||
| 870 | v4l2_prio_open(&vp->prio,&fhp->prio); | ||
| 871 | |||
| 872 | fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw); | ||
| 873 | |||
| 874 | return 0; | ||
| 875 | } | ||
| 876 | |||
| 877 | |||
| 878 | static void pvr2_v4l2_notify(struct pvr2_v4l2_fh *fhp) | ||
| 879 | { | ||
| 880 | wake_up(&fhp->wait_data); | ||
| 881 | } | ||
| 882 | |||
| 883 | static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh) | ||
| 884 | { | ||
| 885 | int ret; | ||
| 886 | struct pvr2_stream *sp; | ||
| 887 | struct pvr2_hdw *hdw; | ||
| 888 | if (fh->rhp) return 0; | ||
| 889 | |||
| 890 | /* First read() attempt. Try to claim the stream and start | ||
| 891 | it... */ | ||
| 892 | if ((ret = pvr2_channel_claim_stream(&fh->channel, | ||
| 893 | fh->dev_info->stream)) != 0) { | ||
| 894 | /* Someone else must already have it */ | ||
| 895 | return ret; | ||
| 896 | } | ||
| 897 | |||
| 898 | fh->rhp = pvr2_channel_create_mpeg_stream(fh->dev_info->stream); | ||
| 899 | if (!fh->rhp) { | ||
| 900 | pvr2_channel_claim_stream(&fh->channel,0); | ||
| 901 | return -ENOMEM; | ||
| 902 | } | ||
| 903 | |||
| 904 | hdw = fh->channel.mc_head->hdw; | ||
| 905 | sp = fh->dev_info->stream->stream; | ||
| 906 | pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh); | ||
| 907 | pvr2_hdw_set_stream_type(hdw,fh->dev_info->config); | ||
| 908 | pvr2_hdw_set_streaming(hdw,!0); | ||
| 909 | ret = pvr2_ioread_set_enabled(fh->rhp,!0); | ||
| 910 | |||
| 911 | return ret; | ||
| 912 | } | ||
| 913 | |||
| 914 | |||
| 915 | static ssize_t pvr2_v4l2_read(struct file *file, | ||
| 916 | char __user *buff, size_t count, loff_t *ppos) | ||
| 917 | { | ||
| 918 | struct pvr2_v4l2_fh *fh = file->private_data; | ||
| 919 | int ret; | ||
| 920 | |||
| 921 | if (fh->fw_mode_flag) { | ||
| 922 | struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; | ||
| 923 | char *tbuf; | ||
| 924 | int c1,c2; | ||
| 925 | int tcnt = 0; | ||
| 926 | unsigned int offs = *ppos; | ||
| 927 | |||
| 928 | tbuf = kmalloc(PAGE_SIZE,GFP_KERNEL); | ||
| 929 | if (!tbuf) return -ENOMEM; | ||
| 930 | |||
| 931 | while (count) { | ||
| 932 | c1 = count; | ||
| 933 | if (c1 > PAGE_SIZE) c1 = PAGE_SIZE; | ||
| 934 | c2 = pvr2_hdw_cpufw_get(hdw,offs,tbuf,c1); | ||
| 935 | if (c2 < 0) { | ||
| 936 | tcnt = c2; | ||
| 937 | break; | ||
| 938 | } | ||
| 939 | if (!c2) break; | ||
| 940 | if (copy_to_user(buff,tbuf,c2)) { | ||
| 941 | tcnt = -EFAULT; | ||
| 942 | break; | ||
| 943 | } | ||
| 944 | offs += c2; | ||
| 945 | tcnt += c2; | ||
| 946 | buff += c2; | ||
| 947 | count -= c2; | ||
| 948 | *ppos += c2; | ||
| 949 | } | ||
| 950 | kfree(tbuf); | ||
| 951 | return tcnt; | ||
| 952 | } | ||
| 953 | |||
| 954 | if (!fh->rhp) { | ||
| 955 | ret = pvr2_v4l2_iosetup(fh); | ||
| 956 | if (ret) { | ||
| 957 | return ret; | ||
| 958 | } | ||
| 959 | } | ||
| 960 | |||
| 961 | for (;;) { | ||
| 962 | ret = pvr2_ioread_read(fh->rhp,buff,count); | ||
| 963 | if (ret >= 0) break; | ||
| 964 | if (ret != -EAGAIN) break; | ||
| 965 | if (file->f_flags & O_NONBLOCK) break; | ||
| 966 | /* Doing blocking I/O. Wait here. */ | ||
| 967 | ret = wait_event_interruptible( | ||
| 968 | fh->wait_data, | ||
| 969 | pvr2_ioread_avail(fh->rhp) >= 0); | ||
| 970 | if (ret < 0) break; | ||
| 971 | } | ||
| 972 | |||
| 973 | return ret; | ||
| 974 | } | ||
| 975 | |||
| 976 | |||
| 977 | static unsigned int pvr2_v4l2_poll(struct file *file, poll_table *wait) | ||
| 978 | { | ||
| 979 | unsigned int mask = 0; | ||
| 980 | struct pvr2_v4l2_fh *fh = file->private_data; | ||
| 981 | int ret; | ||
| 982 | |||
| 983 | if (fh->fw_mode_flag) { | ||
| 984 | mask |= POLLIN | POLLRDNORM; | ||
| 985 | return mask; | ||
| 986 | } | ||
| 987 | |||
| 988 | if (!fh->rhp) { | ||
| 989 | ret = pvr2_v4l2_iosetup(fh); | ||
| 990 | if (ret) return POLLERR; | ||
| 991 | } | ||
| 992 | |||
| 993 | poll_wait(file,&fh->wait_data,wait); | ||
| 994 | |||
| 995 | if (pvr2_ioread_avail(fh->rhp) >= 0) { | ||
| 996 | mask |= POLLIN | POLLRDNORM; | ||
| 997 | } | ||
| 998 | |||
| 999 | return mask; | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | |||
| 1003 | static struct file_operations vdev_fops = { | ||
| 1004 | .owner = THIS_MODULE, | ||
| 1005 | .open = pvr2_v4l2_open, | ||
| 1006 | .release = pvr2_v4l2_release, | ||
| 1007 | .read = pvr2_v4l2_read, | ||
| 1008 | .ioctl = pvr2_v4l2_ioctl, | ||
| 1009 | .llseek = no_llseek, | ||
| 1010 | .poll = pvr2_v4l2_poll, | ||
| 1011 | }; | ||
| 1012 | |||
| 1013 | |||
| 1014 | #define VID_HARDWARE_PVRUSB2 38 /* FIXME : need a good value */ | ||
| 1015 | |||
| 1016 | static struct video_device vdev_template = { | ||
| 1017 | .owner = THIS_MODULE, | ||
| 1018 | .type = VID_TYPE_CAPTURE | VID_TYPE_TUNER, | ||
| 1019 | .type2 = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | ||
| 1020 | | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | ||
| 1021 | | V4L2_CAP_READWRITE), | ||
| 1022 | .hardware = VID_HARDWARE_PVRUSB2, | ||
| 1023 | .fops = &vdev_fops, | ||
| 1024 | }; | ||
| 1025 | |||
| 1026 | |||
| 1027 | static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, | ||
| 1028 | struct pvr2_v4l2 *vp, | ||
| 1029 | enum pvr2_config cfg) | ||
| 1030 | { | ||
| 1031 | int mindevnum; | ||
| 1032 | int unit_number; | ||
| 1033 | int v4l_type; | ||
| 1034 | dip->v4lp = vp; | ||
| 1035 | dip->config = cfg; | ||
| 1036 | |||
| 1037 | |||
| 1038 | switch (cfg) { | ||
| 1039 | case pvr2_config_mpeg: | ||
| 1040 | v4l_type = VFL_TYPE_GRABBER; | ||
| 1041 | dip->stream = &vp->channel.mc_head->video_stream; | ||
| 1042 | break; | ||
| 1043 | case pvr2_config_vbi: | ||
| 1044 | v4l_type = VFL_TYPE_VBI; | ||
| 1045 | break; | ||
| 1046 | case pvr2_config_radio: | ||
| 1047 | v4l_type = VFL_TYPE_RADIO; | ||
| 1048 | break; | ||
| 1049 | default: | ||
| 1050 | /* Bail out (this should be impossible) */ | ||
| 1051 | err("Failed to set up pvrusb2 v4l dev" | ||
| 1052 | " due to unrecognized config"); | ||
| 1053 | return; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | if (!dip->stream) { | ||
| 1057 | err("Failed to set up pvrusb2 v4l dev" | ||
| 1058 | " due to missing stream instance"); | ||
| 1059 | return; | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | dip->vdev = video_device_alloc(); | ||
| 1063 | if (!dip->vdev) { | ||
| 1064 | err("Alloc of pvrusb2 v4l video device failed"); | ||
| 1065 | return; | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | memcpy(dip->vdev,&vdev_template,sizeof(vdev_template)); | ||
| 1069 | dip->vdev->release = video_device_release; | ||
| 1070 | mutex_lock(&device_lock); | ||
| 1071 | |||
| 1072 | mindevnum = -1; | ||
| 1073 | unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw); | ||
| 1074 | if ((unit_number >= 0) && (unit_number < PVR_NUM)) { | ||
| 1075 | mindevnum = video_nr[unit_number]; | ||
| 1076 | } | ||
| 1077 | if ((video_register_device(dip->vdev, v4l_type, mindevnum) < 0) && | ||
| 1078 | (video_register_device(dip->vdev, v4l_type, -1) < 0)) { | ||
| 1079 | err("Failed to register pvrusb2 v4l video device"); | ||
| 1080 | } else { | ||
| 1081 | pvr2_trace(PVR2_TRACE_INIT, | ||
| 1082 | "registered device video%d [%s]", | ||
| 1083 | dip->vdev->minor,pvr2_config_get_name(dip->config)); | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | if ((dip->vdev->minor < sizeof(devices)/sizeof(devices[0])) && | ||
| 1087 | (devices[dip->vdev->minor] == NULL)) { | ||
| 1088 | dip->ctxt_idx = dip->vdev->minor; | ||
| 1089 | devices[dip->ctxt_idx] = dip; | ||
| 1090 | } | ||
| 1091 | mutex_unlock(&device_lock); | ||
| 1092 | |||
| 1093 | pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw, | ||
| 1094 | dip->vdev->minor); | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | |||
| 1098 | struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) | ||
| 1099 | { | ||
| 1100 | struct pvr2_v4l2 *vp; | ||
| 1101 | |||
| 1102 | vp = kmalloc(sizeof(*vp),GFP_KERNEL); | ||
| 1103 | if (!vp) return vp; | ||
| 1104 | memset(vp,0,sizeof(*vp)); | ||
| 1105 | vp->video_dev.ctxt_idx = -1; | ||
| 1106 | pvr2_channel_init(&vp->channel,mnp); | ||
| 1107 | pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp); | ||
| 1108 | |||
| 1109 | vp->channel.check_func = pvr2_v4l2_internal_check; | ||
| 1110 | |||
| 1111 | /* register streams */ | ||
| 1112 | pvr2_v4l2_dev_init(&vp->video_dev,vp,pvr2_config_mpeg); | ||
| 1113 | |||
| 1114 | |||
| 1115 | return vp; | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | /* | ||
| 1119 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 1120 | *** Local Variables: *** | ||
| 1121 | *** mode: c *** | ||
| 1122 | *** fill-column: 75 *** | ||
| 1123 | *** tab-width: 8 *** | ||
| 1124 | *** c-basic-offset: 8 *** | ||
| 1125 | *** End: *** | ||
| 1126 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-v4l2.h new file mode 100644 index 000000000000..9a995e2d2256 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __PVRUSB2_V4L2_H | ||
| 22 | #define __PVRUSB2_V4L2_H | ||
| 23 | |||
| 24 | #include "pvrusb2-context.h" | ||
| 25 | |||
| 26 | struct pvr2_v4l2; | ||
| 27 | |||
| 28 | struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *); | ||
| 29 | |||
| 30 | #endif /* __PVRUSB2_V4L2_H */ | ||
| 31 | |||
| 32 | /* | ||
| 33 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 34 | *** Local Variables: *** | ||
| 35 | *** mode: c *** | ||
| 36 | *** fill-column: 75 *** | ||
| 37 | *** tab-width: 8 *** | ||
| 38 | *** c-basic-offset: 8 *** | ||
| 39 | *** End: *** | ||
| 40 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c new file mode 100644 index 000000000000..e4ec7f25194c --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c | |||
| @@ -0,0 +1,253 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | /* | ||
| 24 | |||
| 25 | This source file is specifically designed to interface with the | ||
| 26 | saa711x support that is available in the v4l available starting | ||
| 27 | with linux 2.6.15. | ||
| 28 | |||
| 29 | */ | ||
| 30 | |||
| 31 | #include "pvrusb2-video-v4l.h" | ||
| 32 | #include "pvrusb2-i2c-cmd-v4l2.h" | ||
| 33 | |||
| 34 | |||
| 35 | #include "pvrusb2-hdw-internal.h" | ||
| 36 | #include "pvrusb2-debug.h" | ||
| 37 | #include <linux/videodev2.h> | ||
| 38 | #include <media/v4l2-common.h> | ||
| 39 | #include <media/saa7115.h> | ||
| 40 | #include <linux/errno.h> | ||
| 41 | #include <linux/slab.h> | ||
| 42 | |||
| 43 | struct pvr2_v4l_decoder { | ||
| 44 | struct pvr2_i2c_handler handler; | ||
| 45 | struct pvr2_decoder_ctrl ctrl; | ||
| 46 | struct pvr2_i2c_client *client; | ||
| 47 | struct pvr2_hdw *hdw; | ||
| 48 | unsigned long stale_mask; | ||
| 49 | }; | ||
| 50 | |||
| 51 | |||
| 52 | static void set_input(struct pvr2_v4l_decoder *ctxt) | ||
| 53 | { | ||
| 54 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 55 | struct v4l2_routing route; | ||
| 56 | |||
| 57 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val); | ||
| 58 | switch(hdw->input_val) { | ||
| 59 | case PVR2_CVAL_INPUT_TV: | ||
| 60 | route.input = SAA7115_COMPOSITE4; | ||
| 61 | break; | ||
| 62 | case PVR2_CVAL_INPUT_COMPOSITE: | ||
| 63 | route.input = SAA7115_COMPOSITE5; | ||
| 64 | break; | ||
| 65 | case PVR2_CVAL_INPUT_SVIDEO: | ||
| 66 | route.input = SAA7115_SVIDEO2; | ||
| 67 | break; | ||
| 68 | case PVR2_CVAL_INPUT_RADIO: | ||
| 69 | // ????? No idea yet what to do here | ||
| 70 | default: | ||
| 71 | return; | ||
| 72 | } | ||
| 73 | route.output = 0; | ||
| 74 | pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route); | ||
| 75 | } | ||
| 76 | |||
| 77 | |||
| 78 | static int check_input(struct pvr2_v4l_decoder *ctxt) | ||
| 79 | { | ||
| 80 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 81 | return hdw->input_dirty != 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | |||
| 85 | static void set_audio(struct pvr2_v4l_decoder *ctxt) | ||
| 86 | { | ||
| 87 | u32 val; | ||
| 88 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 89 | |||
| 90 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_audio %d", | ||
| 91 | hdw->srate_val); | ||
| 92 | switch (hdw->srate_val) { | ||
| 93 | default: | ||
| 94 | case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000: | ||
| 95 | val = 48000; | ||
| 96 | break; | ||
| 97 | case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100: | ||
| 98 | val = 44100; | ||
| 99 | break; | ||
| 100 | case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000: | ||
| 101 | val = 32000; | ||
| 102 | break; | ||
| 103 | } | ||
| 104 | pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val); | ||
| 105 | } | ||
| 106 | |||
| 107 | |||
| 108 | static int check_audio(struct pvr2_v4l_decoder *ctxt) | ||
| 109 | { | ||
| 110 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 111 | return hdw->srate_dirty != 0; | ||
| 112 | } | ||
| 113 | |||
| 114 | |||
| 115 | struct pvr2_v4l_decoder_ops { | ||
| 116 | void (*update)(struct pvr2_v4l_decoder *); | ||
| 117 | int (*check)(struct pvr2_v4l_decoder *); | ||
| 118 | }; | ||
| 119 | |||
| 120 | |||
| 121 | static const struct pvr2_v4l_decoder_ops decoder_ops[] = { | ||
| 122 | { .update = set_input, .check = check_input}, | ||
| 123 | { .update = set_audio, .check = check_audio}, | ||
| 124 | }; | ||
| 125 | |||
| 126 | |||
| 127 | static void decoder_detach(struct pvr2_v4l_decoder *ctxt) | ||
| 128 | { | ||
| 129 | ctxt->client->handler = 0; | ||
| 130 | ctxt->hdw->decoder_ctrl = 0; | ||
| 131 | kfree(ctxt); | ||
| 132 | } | ||
| 133 | |||
| 134 | |||
| 135 | static int decoder_check(struct pvr2_v4l_decoder *ctxt) | ||
| 136 | { | ||
| 137 | unsigned long msk; | ||
| 138 | unsigned int idx; | ||
| 139 | |||
| 140 | for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); | ||
| 141 | idx++) { | ||
| 142 | msk = 1 << idx; | ||
| 143 | if (ctxt->stale_mask & msk) continue; | ||
| 144 | if (decoder_ops[idx].check(ctxt)) { | ||
| 145 | ctxt->stale_mask |= msk; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | return ctxt->stale_mask != 0; | ||
| 149 | } | ||
| 150 | |||
| 151 | |||
| 152 | static void decoder_update(struct pvr2_v4l_decoder *ctxt) | ||
| 153 | { | ||
| 154 | unsigned long msk; | ||
| 155 | unsigned int idx; | ||
| 156 | |||
| 157 | for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); | ||
| 158 | idx++) { | ||
| 159 | msk = 1 << idx; | ||
| 160 | if (!(ctxt->stale_mask & msk)) continue; | ||
| 161 | ctxt->stale_mask &= ~msk; | ||
| 162 | decoder_ops[idx].update(ctxt); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | |||
| 167 | static int decoder_detect(struct pvr2_i2c_client *cp) | ||
| 168 | { | ||
| 169 | /* Attempt to query the decoder - let's see if it will answer */ | ||
| 170 | struct v4l2_tuner vt; | ||
| 171 | int ret; | ||
| 172 | |||
| 173 | memset(&vt,0,sizeof(vt)); | ||
| 174 | ret = pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&vt); | ||
| 175 | return ret == 0; /* Return true if it answered */ | ||
| 176 | } | ||
| 177 | |||
| 178 | |||
| 179 | static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl) | ||
| 180 | { | ||
| 181 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 decoder_enable(%d)",fl); | ||
| 182 | pvr2_v4l2_cmd_stream(ctxt->client,fl); | ||
| 183 | } | ||
| 184 | |||
| 185 | |||
| 186 | static int decoder_is_tuned(struct pvr2_v4l_decoder *ctxt) | ||
| 187 | { | ||
| 188 | struct v4l2_tuner vt; | ||
| 189 | int ret; | ||
| 190 | |||
| 191 | memset(&vt,0,sizeof(vt)); | ||
| 192 | ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); | ||
| 193 | if (ret < 0) return -EINVAL; | ||
| 194 | return vt.signal ? 1 : 0; | ||
| 195 | } | ||
| 196 | |||
| 197 | |||
| 198 | static unsigned int decoder_describe(struct pvr2_v4l_decoder *ctxt,char *buf,unsigned int cnt) | ||
| 199 | { | ||
| 200 | return scnprintf(buf,cnt,"handler: pvrusb2-video-v4l"); | ||
| 201 | } | ||
| 202 | |||
| 203 | |||
| 204 | const static struct pvr2_i2c_handler_functions hfuncs = { | ||
| 205 | .detach = (void (*)(void *))decoder_detach, | ||
| 206 | .check = (int (*)(void *))decoder_check, | ||
| 207 | .update = (void (*)(void *))decoder_update, | ||
| 208 | .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe, | ||
| 209 | }; | ||
| 210 | |||
| 211 | |||
| 212 | int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw, | ||
| 213 | struct pvr2_i2c_client *cp) | ||
| 214 | { | ||
| 215 | struct pvr2_v4l_decoder *ctxt; | ||
| 216 | |||
| 217 | if (hdw->decoder_ctrl) return 0; | ||
| 218 | if (cp->handler) return 0; | ||
| 219 | if (!decoder_detect(cp)) return 0; | ||
| 220 | |||
| 221 | ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); | ||
| 222 | if (!ctxt) return 0; | ||
| 223 | memset(ctxt,0,sizeof(*ctxt)); | ||
| 224 | |||
| 225 | ctxt->handler.func_data = ctxt; | ||
| 226 | ctxt->handler.func_table = &hfuncs; | ||
| 227 | ctxt->ctrl.ctxt = ctxt; | ||
| 228 | ctxt->ctrl.detach = (void (*)(void *))decoder_detach; | ||
| 229 | ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; | ||
| 230 | ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned; | ||
| 231 | ctxt->client = cp; | ||
| 232 | ctxt->hdw = hdw; | ||
| 233 | ctxt->stale_mask = (1 << (sizeof(decoder_ops)/ | ||
| 234 | sizeof(decoder_ops[0]))) - 1; | ||
| 235 | hdw->decoder_ctrl = &ctxt->ctrl; | ||
| 236 | cp->handler = &ctxt->handler; | ||
| 237 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up", | ||
| 238 | cp->client->addr); | ||
| 239 | return !0; | ||
| 240 | } | ||
| 241 | |||
| 242 | |||
| 243 | |||
| 244 | |||
| 245 | /* | ||
| 246 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 247 | *** Local Variables: *** | ||
| 248 | *** mode: c *** | ||
| 249 | *** fill-column: 70 *** | ||
| 250 | *** tab-width: 8 *** | ||
| 251 | *** c-basic-offset: 8 *** | ||
| 252 | *** End: *** | ||
| 253 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h new file mode 100644 index 000000000000..2b917fda02e4 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef __PVRUSB2_VIDEO_V4L_H | ||
| 24 | #define __PVRUSB2_VIDEO_V4L_H | ||
| 25 | |||
| 26 | /* | ||
| 27 | |||
| 28 | This module connects the pvrusb2 driver to the I2C chip level | ||
| 29 | driver which handles device video processing. This interface is | ||
| 30 | used internally by the driver; higher level code should only | ||
| 31 | interact through the interface provided by pvrusb2-hdw.h. | ||
| 32 | |||
| 33 | */ | ||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | #include "pvrusb2-i2c-core.h" | ||
| 38 | |||
| 39 | int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); | ||
| 40 | |||
| 41 | |||
| 42 | #endif /* __PVRUSB2_VIDEO_V4L_H */ | ||
| 43 | |||
| 44 | /* | ||
| 45 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 46 | *** Local Variables: *** | ||
| 47 | *** mode: c *** | ||
| 48 | *** fill-column: 70 *** | ||
| 49 | *** tab-width: 8 *** | ||
| 50 | *** c-basic-offset: 8 *** | ||
| 51 | *** End: *** | ||
| 52 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c new file mode 100644 index 000000000000..fcad346e3955 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c | |||
| @@ -0,0 +1,170 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | /* | ||
| 24 | |||
| 25 | This source file is specifically designed to interface with the | ||
| 26 | wm8775. | ||
| 27 | |||
| 28 | */ | ||
| 29 | |||
| 30 | #include "pvrusb2-wm8775.h" | ||
| 31 | #include "pvrusb2-i2c-cmd-v4l2.h" | ||
| 32 | |||
| 33 | |||
| 34 | #include "pvrusb2-hdw-internal.h" | ||
| 35 | #include "pvrusb2-debug.h" | ||
| 36 | #include <linux/videodev2.h> | ||
| 37 | #include <media/v4l2-common.h> | ||
| 38 | #include <linux/errno.h> | ||
| 39 | #include <linux/slab.h> | ||
| 40 | |||
| 41 | struct pvr2_v4l_wm8775 { | ||
| 42 | struct pvr2_i2c_handler handler; | ||
| 43 | struct pvr2_i2c_client *client; | ||
| 44 | struct pvr2_hdw *hdw; | ||
| 45 | unsigned long stale_mask; | ||
| 46 | }; | ||
| 47 | |||
| 48 | |||
| 49 | static void set_input(struct pvr2_v4l_wm8775 *ctxt) | ||
| 50 | { | ||
| 51 | struct v4l2_routing route; | ||
| 52 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 53 | int msk = 0; | ||
| 54 | |||
| 55 | memset(&route,0,sizeof(route)); | ||
| 56 | |||
| 57 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d msk=0x%x)", | ||
| 58 | hdw->input_val,msk); | ||
| 59 | |||
| 60 | // Always point to input #1 no matter what | ||
| 61 | route.input = 2; | ||
| 62 | pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); | ||
| 63 | } | ||
| 64 | |||
| 65 | static int check_input(struct pvr2_v4l_wm8775 *ctxt) | ||
| 66 | { | ||
| 67 | struct pvr2_hdw *hdw = ctxt->hdw; | ||
| 68 | return hdw->input_dirty != 0; | ||
| 69 | } | ||
| 70 | |||
| 71 | |||
| 72 | struct pvr2_v4l_wm8775_ops { | ||
| 73 | void (*update)(struct pvr2_v4l_wm8775 *); | ||
| 74 | int (*check)(struct pvr2_v4l_wm8775 *); | ||
| 75 | }; | ||
| 76 | |||
| 77 | |||
| 78 | static const struct pvr2_v4l_wm8775_ops wm8775_ops[] = { | ||
| 79 | { .update = set_input, .check = check_input}, | ||
| 80 | }; | ||
| 81 | |||
| 82 | |||
| 83 | static unsigned int wm8775_describe(struct pvr2_v4l_wm8775 *ctxt, | ||
| 84 | char *buf,unsigned int cnt) | ||
| 85 | { | ||
| 86 | return scnprintf(buf,cnt,"handler: pvrusb2-wm8775"); | ||
| 87 | } | ||
| 88 | |||
| 89 | |||
| 90 | static void wm8775_detach(struct pvr2_v4l_wm8775 *ctxt) | ||
| 91 | { | ||
| 92 | ctxt->client->handler = 0; | ||
| 93 | kfree(ctxt); | ||
| 94 | } | ||
| 95 | |||
| 96 | |||
| 97 | static int wm8775_check(struct pvr2_v4l_wm8775 *ctxt) | ||
| 98 | { | ||
| 99 | unsigned long msk; | ||
| 100 | unsigned int idx; | ||
| 101 | |||
| 102 | for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); | ||
| 103 | idx++) { | ||
| 104 | msk = 1 << idx; | ||
| 105 | if (ctxt->stale_mask & msk) continue; | ||
| 106 | if (wm8775_ops[idx].check(ctxt)) { | ||
| 107 | ctxt->stale_mask |= msk; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | return ctxt->stale_mask != 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | |||
| 114 | static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt) | ||
| 115 | { | ||
| 116 | unsigned long msk; | ||
| 117 | unsigned int idx; | ||
| 118 | |||
| 119 | for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); | ||
| 120 | idx++) { | ||
| 121 | msk = 1 << idx; | ||
| 122 | if (!(ctxt->stale_mask & msk)) continue; | ||
| 123 | ctxt->stale_mask &= ~msk; | ||
| 124 | wm8775_ops[idx].update(ctxt); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | |||
| 129 | const static struct pvr2_i2c_handler_functions hfuncs = { | ||
| 130 | .detach = (void (*)(void *))wm8775_detach, | ||
| 131 | .check = (int (*)(void *))wm8775_check, | ||
| 132 | .update = (void (*)(void *))wm8775_update, | ||
| 133 | .describe = (unsigned int (*)(void *,char *,unsigned int))wm8775_describe, | ||
| 134 | }; | ||
| 135 | |||
| 136 | |||
| 137 | int pvr2_i2c_wm8775_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) | ||
| 138 | { | ||
| 139 | struct pvr2_v4l_wm8775 *ctxt; | ||
| 140 | |||
| 141 | if (cp->handler) return 0; | ||
| 142 | |||
| 143 | ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); | ||
| 144 | if (!ctxt) return 0; | ||
| 145 | memset(ctxt,0,sizeof(*ctxt)); | ||
| 146 | |||
| 147 | ctxt->handler.func_data = ctxt; | ||
| 148 | ctxt->handler.func_table = &hfuncs; | ||
| 149 | ctxt->client = cp; | ||
| 150 | ctxt->hdw = hdw; | ||
| 151 | ctxt->stale_mask = (1 << (sizeof(wm8775_ops)/ | ||
| 152 | sizeof(wm8775_ops[0]))) - 1; | ||
| 153 | cp->handler = &ctxt->handler; | ||
| 154 | pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x wm8775 V4L2 handler set up", | ||
| 155 | cp->client->addr); | ||
| 156 | return !0; | ||
| 157 | } | ||
| 158 | |||
| 159 | |||
| 160 | |||
| 161 | |||
| 162 | /* | ||
| 163 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 164 | *** Local Variables: *** | ||
| 165 | *** mode: c *** | ||
| 166 | *** fill-column: 70 *** | ||
| 167 | *** tab-width: 8 *** | ||
| 168 | *** c-basic-offset: 8 *** | ||
| 169 | *** End: *** | ||
| 170 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h new file mode 100644 index 000000000000..8aaeff4e1e20 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef __PVRUSB2_WM8775_H | ||
| 24 | #define __PVRUSB2_WM8775_H | ||
| 25 | |||
| 26 | /* | ||
| 27 | |||
| 28 | This module connects the pvrusb2 driver to the I2C chip level | ||
| 29 | driver which performs analog -> digital audio conversion for | ||
| 30 | external audio inputs. This interface is used internally by the | ||
| 31 | driver; higher level code should only interact through the | ||
| 32 | interface provided by pvrusb2-hdw.h. | ||
| 33 | |||
| 34 | */ | ||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | #include "pvrusb2-i2c-core.h" | ||
| 39 | |||
| 40 | int pvr2_i2c_wm8775_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); | ||
| 41 | |||
| 42 | |||
| 43 | #endif /* __PVRUSB2_WM8775_H */ | ||
| 44 | |||
| 45 | /* | ||
| 46 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 47 | *** Local Variables: *** | ||
| 48 | *** mode: c *** | ||
| 49 | *** fill-column: 70 *** | ||
| 50 | *** tab-width: 8 *** | ||
| 51 | *** c-basic-offset: 8 *** | ||
| 52 | *** End: *** | ||
| 53 | */ | ||
diff --git a/drivers/media/video/pvrusb2/pvrusb2.h b/drivers/media/video/pvrusb2/pvrusb2.h new file mode 100644 index 000000000000..074533e9c21e --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2.h | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
| 6 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef __PVRUSB2_H | ||
| 24 | #define __PVRUSB2_H | ||
| 25 | |||
| 26 | /* Maximum number of pvrusb2 instances we can track at once. You | ||
| 27 | might want to increase this - however the driver operation will not | ||
| 28 | be impaired if it is too small. Instead additional units just | ||
| 29 | won't have an ID assigned and it might not be possible to specify | ||
| 30 | module paramters for those extra units. */ | ||
| 31 | #define PVR_NUM 20 | ||
| 32 | |||
| 33 | #endif /* __PVRUSB2_H */ | ||
| 34 | |||
| 35 | /* | ||
| 36 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
| 37 | *** Local Variables: *** | ||
| 38 | *** mode: c *** | ||
| 39 | *** fill-column: 70 *** | ||
| 40 | *** tab-width: 8 *** | ||
| 41 | *** c-basic-offset: 8 *** | ||
| 42 | *** End: *** | ||
| 43 | */ | ||
diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c index de7b9e6e932a..afc8f352b8e7 100644 --- a/drivers/media/video/saa7134/saa6752hs.c +++ b/drivers/media/video/saa7134/saa6752hs.c | |||
| @@ -432,10 +432,10 @@ static void saa6752hs_old_set_params(struct i2c_client* client, | |||
| 432 | } | 432 | } |
| 433 | 433 | ||
| 434 | static int handle_ctrl(struct saa6752hs_mpeg_params *params, | 434 | static int handle_ctrl(struct saa6752hs_mpeg_params *params, |
| 435 | struct v4l2_ext_control *ctrl, int cmd) | 435 | struct v4l2_ext_control *ctrl, unsigned int cmd) |
| 436 | { | 436 | { |
| 437 | int old = 0, new; | 437 | int old = 0, new; |
| 438 | int set = cmd == VIDIOC_S_EXT_CTRLS; | 438 | int set = (cmd == VIDIOC_S_EXT_CTRLS); |
| 439 | 439 | ||
| 440 | new = ctrl->value; | 440 | new = ctrl->value; |
| 441 | switch (ctrl->id) { | 441 | switch (ctrl->id) { |
diff --git a/drivers/media/video/stradis.c b/drivers/media/video/stradis.c index 6be9c1131e1f..c18b31d9928c 100644 --- a/drivers/media/video/stradis.c +++ b/drivers/media/video/stradis.c | |||
| @@ -2190,7 +2190,7 @@ static struct pci_driver stradis_driver = { | |||
| 2190 | .remove = __devexit_p(stradis_remove) | 2190 | .remove = __devexit_p(stradis_remove) |
| 2191 | }; | 2191 | }; |
| 2192 | 2192 | ||
| 2193 | int __init stradis_init(void) | 2193 | static int __init stradis_init(void) |
| 2194 | { | 2194 | { |
| 2195 | int retval; | 2195 | int retval; |
| 2196 | 2196 | ||
| @@ -2203,7 +2203,7 @@ int __init stradis_init(void) | |||
| 2203 | return retval; | 2203 | return retval; |
| 2204 | } | 2204 | } |
| 2205 | 2205 | ||
| 2206 | void __exit stradis_exit(void) | 2206 | static void __exit stradis_exit(void) |
| 2207 | { | 2207 | { |
| 2208 | pci_unregister_driver(&stradis_driver); | 2208 | pci_unregister_driver(&stradis_driver); |
| 2209 | printk(KERN_INFO "stradis: module cleanup complete\n"); | 2209 | printk(KERN_INFO "stradis: module cleanup complete\n"); |
diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index b6ae969563b2..2fadabf99688 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c | |||
| @@ -22,11 +22,11 @@ | |||
| 22 | */ | 22 | */ |
| 23 | 23 | ||
| 24 | #define tda9887_info(fmt, arg...) do {\ | 24 | #define tda9887_info(fmt, arg...) do {\ |
| 25 | printk(KERN_INFO "%s %d-%04x (tda9887): " fmt, t->i2c.name, \ | 25 | printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \ |
| 26 | i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) | 26 | i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) |
| 27 | #define tda9887_dbg(fmt, arg...) do {\ | 27 | #define tda9887_dbg(fmt, arg...) do {\ |
| 28 | if (tuner_debug) \ | 28 | if (tuner_debug) \ |
| 29 | printk(KERN_INFO "%s %d-%04x (tda9887): " fmt, t->i2c.name, \ | 29 | printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \ |
| 30 | i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) | 30 | i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) |
| 31 | 31 | ||
| 32 | 32 | ||
| @@ -84,8 +84,7 @@ struct tvnorm { | |||
| 84 | #define cAudioGain6 0x80 // bit c7 | 84 | #define cAudioGain6 0x80 // bit c7 |
| 85 | 85 | ||
| 86 | #define cTopMask 0x1f // bit c0:4 | 86 | #define cTopMask 0x1f // bit c0:4 |
| 87 | #define cTopPalSecamDefault 0x14 // bit c0:4 | 87 | #define cTopDefault 0x10 // bit c0:4 |
| 88 | #define cTopNtscRadioDefault 0x10 // bit c0:4 | ||
| 89 | 88 | ||
| 90 | //// third reg (e) | 89 | //// third reg (e) |
| 91 | #define cAudioIF_4_5 0x00 // bit e0:1 | 90 | #define cAudioIF_4_5 0x00 // bit e0:1 |
| @@ -123,7 +122,7 @@ static struct tvnorm tvnorms[] = { | |||
| 123 | cQSS ), | 122 | cQSS ), |
| 124 | .c = ( cDeemphasisON | | 123 | .c = ( cDeemphasisON | |
| 125 | cDeemphasis50 | | 124 | cDeemphasis50 | |
| 126 | cTopPalSecamDefault), | 125 | cTopDefault), |
| 127 | .e = ( cGating_36 | | 126 | .e = ( cGating_36 | |
| 128 | cAudioIF_5_5 | | 127 | cAudioIF_5_5 | |
| 129 | cVideoIF_38_90 ), | 128 | cVideoIF_38_90 ), |
| @@ -134,7 +133,7 @@ static struct tvnorm tvnorms[] = { | |||
| 134 | cQSS ), | 133 | cQSS ), |
| 135 | .c = ( cDeemphasisON | | 134 | .c = ( cDeemphasisON | |
| 136 | cDeemphasis50 | | 135 | cDeemphasis50 | |
| 137 | cTopPalSecamDefault), | 136 | cTopDefault), |
| 138 | .e = ( cGating_36 | | 137 | .e = ( cGating_36 | |
| 139 | cAudioIF_6_0 | | 138 | cAudioIF_6_0 | |
| 140 | cVideoIF_38_90 ), | 139 | cVideoIF_38_90 ), |
| @@ -145,7 +144,7 @@ static struct tvnorm tvnorms[] = { | |||
| 145 | cQSS ), | 144 | cQSS ), |
| 146 | .c = ( cDeemphasisON | | 145 | .c = ( cDeemphasisON | |
| 147 | cDeemphasis50 | | 146 | cDeemphasis50 | |
| 148 | cTopPalSecamDefault), | 147 | cTopDefault), |
| 149 | .e = ( cGating_36 | | 148 | .e = ( cGating_36 | |
| 150 | cAudioIF_6_5 | | 149 | cAudioIF_6_5 | |
| 151 | cVideoIF_38_90 ), | 150 | cVideoIF_38_90 ), |
| @@ -156,7 +155,7 @@ static struct tvnorm tvnorms[] = { | |||
| 156 | cQSS ), | 155 | cQSS ), |
| 157 | .c = ( cDeemphasisON | | 156 | .c = ( cDeemphasisON | |
| 158 | cDeemphasis75 | | 157 | cDeemphasis75 | |
| 159 | cTopNtscRadioDefault), | 158 | cTopDefault), |
| 160 | .e = ( cGating_36 | | 159 | .e = ( cGating_36 | |
| 161 | cAudioIF_4_5 | | 160 | cAudioIF_4_5 | |
| 162 | cVideoIF_45_75 ), | 161 | cVideoIF_45_75 ), |
| @@ -165,7 +164,7 @@ static struct tvnorm tvnorms[] = { | |||
| 165 | .name = "SECAM-BGH", | 164 | .name = "SECAM-BGH", |
| 166 | .b = ( cPositiveAmTV | | 165 | .b = ( cPositiveAmTV | |
| 167 | cQSS ), | 166 | cQSS ), |
| 168 | .c = ( cTopPalSecamDefault), | 167 | .c = ( cTopDefault), |
| 169 | .e = ( cGating_36 | | 168 | .e = ( cGating_36 | |
| 170 | cAudioIF_5_5 | | 169 | cAudioIF_5_5 | |
| 171 | cVideoIF_38_90 ), | 170 | cVideoIF_38_90 ), |
| @@ -174,7 +173,7 @@ static struct tvnorm tvnorms[] = { | |||
| 174 | .name = "SECAM-L", | 173 | .name = "SECAM-L", |
| 175 | .b = ( cPositiveAmTV | | 174 | .b = ( cPositiveAmTV | |
| 176 | cQSS ), | 175 | cQSS ), |
| 177 | .c = ( cTopPalSecamDefault), | 176 | .c = ( cTopDefault), |
| 178 | .e = ( cGating_36 | | 177 | .e = ( cGating_36 | |
| 179 | cAudioIF_6_5 | | 178 | cAudioIF_6_5 | |
| 180 | cVideoIF_38_90 ), | 179 | cVideoIF_38_90 ), |
| @@ -184,7 +183,7 @@ static struct tvnorm tvnorms[] = { | |||
| 184 | .b = ( cOutputPort2Inactive | | 183 | .b = ( cOutputPort2Inactive | |
| 185 | cPositiveAmTV | | 184 | cPositiveAmTV | |
| 186 | cQSS ), | 185 | cQSS ), |
| 187 | .c = ( cTopPalSecamDefault), | 186 | .c = ( cTopDefault), |
| 188 | .e = ( cGating_36 | | 187 | .e = ( cGating_36 | |
| 189 | cAudioIF_6_5 | | 188 | cAudioIF_6_5 | |
| 190 | cVideoIF_33_90 ), | 189 | cVideoIF_33_90 ), |
| @@ -195,7 +194,7 @@ static struct tvnorm tvnorms[] = { | |||
| 195 | cQSS ), | 194 | cQSS ), |
| 196 | .c = ( cDeemphasisON | | 195 | .c = ( cDeemphasisON | |
| 197 | cDeemphasis50 | | 196 | cDeemphasis50 | |
| 198 | cTopPalSecamDefault), | 197 | cTopDefault), |
| 199 | .e = ( cGating_36 | | 198 | .e = ( cGating_36 | |
| 200 | cAudioIF_6_5 | | 199 | cAudioIF_6_5 | |
| 201 | cVideoIF_38_90 ), | 200 | cVideoIF_38_90 ), |
| @@ -206,7 +205,7 @@ static struct tvnorm tvnorms[] = { | |||
| 206 | cQSS ), | 205 | cQSS ), |
| 207 | .c = ( cDeemphasisON | | 206 | .c = ( cDeemphasisON | |
| 208 | cDeemphasis75 | | 207 | cDeemphasis75 | |
| 209 | cTopNtscRadioDefault), | 208 | cTopDefault), |
| 210 | .e = ( cGating_36 | | 209 | .e = ( cGating_36 | |
| 211 | cAudioIF_4_5 | | 210 | cAudioIF_4_5 | |
| 212 | cVideoIF_45_75 ), | 211 | cVideoIF_45_75 ), |
| @@ -217,7 +216,7 @@ static struct tvnorm tvnorms[] = { | |||
| 217 | cQSS ), | 216 | cQSS ), |
| 218 | .c = ( cDeemphasisON | | 217 | .c = ( cDeemphasisON | |
| 219 | cDeemphasis50 | | 218 | cDeemphasis50 | |
| 220 | cTopNtscRadioDefault), | 219 | cTopDefault), |
| 221 | .e = ( cGating_36 | | 220 | .e = ( cGating_36 | |
| 222 | cAudioIF_4_5 | | 221 | cAudioIF_4_5 | |
| 223 | cVideoIF_58_75 ), | 222 | cVideoIF_58_75 ), |
| @@ -230,7 +229,7 @@ static struct tvnorm radio_stereo = { | |||
| 230 | cQSS ), | 229 | cQSS ), |
| 231 | .c = ( cDeemphasisOFF | | 230 | .c = ( cDeemphasisOFF | |
| 232 | cAudioGain6 | | 231 | cAudioGain6 | |
| 233 | cTopNtscRadioDefault), | 232 | cTopDefault), |
| 234 | .e = ( cTunerGainLow | | 233 | .e = ( cTunerGainLow | |
| 235 | cAudioIF_5_5 | | 234 | cAudioIF_5_5 | |
| 236 | cRadioIF_38_90 ), | 235 | cRadioIF_38_90 ), |
| @@ -242,7 +241,7 @@ static struct tvnorm radio_mono = { | |||
| 242 | cQSS ), | 241 | cQSS ), |
| 243 | .c = ( cDeemphasisON | | 242 | .c = ( cDeemphasisON | |
| 244 | cDeemphasis75 | | 243 | cDeemphasis75 | |
| 245 | cTopNtscRadioDefault), | 244 | cTopDefault), |
| 246 | .e = ( cTunerGainLow | | 245 | .e = ( cTunerGainLow | |
| 247 | cAudioIF_5_5 | | 246 | cAudioIF_5_5 | |
| 248 | cRadioIF_38_90 ), | 247 | cRadioIF_38_90 ), |
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index a26ded7d6fae..011413cf34a8 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c | |||
| @@ -40,7 +40,6 @@ static unsigned int no_autodetect = 0; | |||
| 40 | static unsigned int show_i2c = 0; | 40 | static unsigned int show_i2c = 0; |
| 41 | 41 | ||
| 42 | /* insmod options used at runtime => read/write */ | 42 | /* insmod options used at runtime => read/write */ |
| 43 | static unsigned int tuner_debug_old = 0; | ||
| 44 | int tuner_debug = 0; | 43 | int tuner_debug = 0; |
| 45 | 44 | ||
| 46 | static unsigned int tv_range[2] = { 44, 958 }; | 45 | static unsigned int tv_range[2] = { 44, 958 }; |
| @@ -54,8 +53,6 @@ static char ntsc[] = "-"; | |||
| 54 | module_param(addr, int, 0444); | 53 | module_param(addr, int, 0444); |
| 55 | module_param(no_autodetect, int, 0444); | 54 | module_param(no_autodetect, int, 0444); |
| 56 | module_param(show_i2c, int, 0444); | 55 | module_param(show_i2c, int, 0444); |
| 57 | /* Note: tuner_debug is deprecated and will be removed in 2.6.17 */ | ||
| 58 | module_param_named(tuner_debug,tuner_debug_old, int, 0444); | ||
| 59 | module_param_named(debug,tuner_debug, int, 0644); | 56 | module_param_named(debug,tuner_debug, int, 0644); |
| 60 | module_param_string(pal, pal, sizeof(pal), 0644); | 57 | module_param_string(pal, pal, sizeof(pal), 0644); |
| 61 | module_param_string(secam, secam, sizeof(secam), 0644); | 58 | module_param_string(secam, secam, sizeof(secam), 0644); |
| @@ -442,11 +439,6 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) | |||
| 442 | t->audmode = V4L2_TUNER_MODE_STEREO; | 439 | t->audmode = V4L2_TUNER_MODE_STEREO; |
| 443 | t->mode_mask = T_UNINITIALIZED; | 440 | t->mode_mask = T_UNINITIALIZED; |
| 444 | t->tuner_status = tuner_status; | 441 | t->tuner_status = tuner_status; |
| 445 | if (tuner_debug_old) { | ||
| 446 | tuner_debug = tuner_debug_old; | ||
| 447 | printk(KERN_ERR "tuner: tuner_debug is deprecated and will be removed in 2.6.17.\n"); | ||
| 448 | printk(KERN_ERR "tuner: use the debug option instead.\n"); | ||
| 449 | } | ||
| 450 | 442 | ||
| 451 | if (show_i2c) { | 443 | if (show_i2c) { |
| 452 | unsigned char buffer[16]; | 444 | unsigned char buffer[16]; |
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index f4b3d64ebf73..97f946db8597 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c | |||
| @@ -1103,7 +1103,7 @@ const char **v4l2_ctrl_get_menu(u32 id) | |||
| 1103 | }; | 1103 | }; |
| 1104 | static const char *mpeg_stream_vbi_fmt[] = { | 1104 | static const char *mpeg_stream_vbi_fmt[] = { |
| 1105 | "No VBI", | 1105 | "No VBI", |
| 1106 | "VBI in private packets, IVTV format", | 1106 | "Private packet, IVTV format", |
| 1107 | NULL | 1107 | NULL |
| 1108 | }; | 1108 | }; |
| 1109 | 1109 | ||
diff --git a/include/media/cx2341x.h b/include/media/cx2341x.h index 074c4008ad52..d91d88f93c8b 100644 --- a/include/media/cx2341x.h +++ b/include/media/cx2341x.h | |||
| @@ -89,9 +89,9 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, | |||
| 89 | struct v4l2_queryctrl *qctrl); | 89 | struct v4l2_queryctrl *qctrl); |
| 90 | const char **cx2341x_ctrl_get_menu(u32 id); | 90 | const char **cx2341x_ctrl_get_menu(u32 id); |
| 91 | int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, | 91 | int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, |
| 92 | struct v4l2_ext_controls *ctrls, int cmd); | 92 | struct v4l2_ext_controls *ctrls, unsigned int cmd); |
| 93 | void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p); | 93 | void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p); |
| 94 | void cx2341x_log_status(struct cx2341x_mpeg_params *p, int cardid); | 94 | void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix); |
| 95 | 95 | ||
| 96 | /* Firmware names */ | 96 | /* Firmware names */ |
| 97 | #define CX2341X_FIRM_ENC_FILENAME "v4l-cx2341x-enc.fw" | 97 | #define CX2341X_FIRM_ENC_FILENAME "v4l-cx2341x-enc.fw" |
