diff --git a/Documentation/DocBook/media/v4l/biblio.xml b/Documentation/DocBook/media/v4l/biblio.xml
index cea6fd3ed428..7dc65c592a87 100644
--- a/Documentation/DocBook/media/v4l/biblio.xml
+++ b/Documentation/DocBook/media/v4l/biblio.xml
@@ -128,6 +128,26 @@ url="http://www.ijg.org">http://www.ijg.org)
Version 1.02
+
+ ITU-T.81
+
+ International Telecommunication Union
+(http://www.itu.int)
+
+ ITU-T Recommendation T.81
+"Information Technology — Digital Compression and Coding of Continous-Tone
+Still Images — Requirements and Guidelines"
+
+
+
+ W3C JPEG JFIF
+
+ The World Wide Web Consortium (http://www.w3.org)
+
+ JPEG JFIF
+
+
SMPTE 12M
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml
index a2485b3ff3d2..bce97c50391b 100644
--- a/Documentation/DocBook/media/v4l/compat.xml
+++ b/Documentation/DocBook/media/v4l/compat.xml
@@ -2393,6 +2393,20 @@ details.
to the User controls class.
+
+ Added the device_caps field to struct v4l2_capabilities and added the new
+ V4L2_CAP_DEVICE_CAPS capability.
+
+
+
+
+
+ V4L2 in Linux 3.4
+
+
+ Added JPEG compression control
+ class.
+
diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml
index a1be37897ad7..b84f25e9cc87 100644
--- a/Documentation/DocBook/media/v4l/controls.xml
+++ b/Documentation/DocBook/media/v4l/controls.xml
@@ -1284,6 +1284,49 @@ values are:
capturing. This is not done by muting audio hardware, which can still
produce a slight hiss, but in the encoder itself, guaranteeing a fixed
and reproducible audio bitstream. 0 = unmuted, 1 = muted.
+
+
+
+ V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK
+ enum v4l2_mpeg_audio_dec_playback
+ Determines how monolingual audio should be played back.
+Possible values are:
+
+
+
+
+
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO
+ Automatically determines the best playback mode.
+
+
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO
+ Stereo playback.
+
+
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT
+ Left channel playback.
+
+
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_RIGHT
+ Right channel playback.
+
+
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_MONO
+ Mono playback.
+
+
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO
+ Stereo playback with swapped left and right channels.
+
+
+
+
+
+
+ V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK
+ enum v4l2_mpeg_audio_dec_playback
+ Determines how multilingual audio should be played back.
@@ -1447,6 +1490,22 @@ of the video. The supplied 32-bit integer is interpreted as follows (bit
+
+
+ V4L2_CID_MPEG_VIDEO_DEC_PTS
+ integer64
+ This read-only control returns the
+33-bit video Presentation Time Stamp as defined in ITU T-REC-H.222.0 and ISO/IEC 13818-1 of
+the currently displayed frame. This is the same PTS as is used in &VIDIOC-DECODER-CMD;.
+
+
+
+ V4L2_CID_MPEG_VIDEO_DEC_FRAME
+ integer64
+ This read-only control returns the
+frame counter of the frame that is currently displayed (decoded). This value is reset to 0 whenever
+the decoder is started.
+
@@ -3377,6 +3436,167 @@ interface and may change in the future.
+
+
+ JPEG Control Reference
+ The JPEG class includes controls for common features of JPEG
+ encoders and decoders. Currently it includes features for codecs
+ implementing progressive baseline DCT compression process with
+ Huffman entrophy coding.
+
+ JPEG Control IDs
+
+
+
+
+
+
+
+
+
+
+ ID
+ Type
+ Description
+
+
+
+
+
+ V4L2_CID_JPEG_CLASS
+ class
+ The JPEG class descriptor. Calling
+ &VIDIOC-QUERYCTRL; for this control will return a description of this
+ control class.
+
+
+
+
+ V4L2_CID_JPEG_CHROMA_SUBSAMPLING
+ menu
+
+
+ The chroma subsampling factors describe how
+ each component of an input image is sampled, in respect to maximum
+ sample rate in each spatial dimension. See ,
+ clause A.1.1. for more details. The
+ V4L2_CID_JPEG_CHROMA_SUBSAMPLING control determines how
+ Cb and Cr components are downsampled after coverting an input image
+ from RGB to Y'CbCr color space.
+
+
+
+
+
+
+ V4L2_JPEG_CHROMA_SUBSAMPLING_444
+ No chroma subsampling, each pixel has
+ Y, Cr and Cb values.
+
+
+ V4L2_JPEG_CHROMA_SUBSAMPLING_422
+ Horizontally subsample Cr, Cb components
+ by a factor of 2.
+
+
+ V4L2_JPEG_CHROMA_SUBSAMPLING_420
+ Subsample Cr, Cb components horizontally
+ and vertically by 2.
+
+
+ V4L2_JPEG_CHROMA_SUBSAMPLING_411
+ Horizontally subsample Cr, Cb components
+ by a factor of 4.
+
+
+ V4L2_JPEG_CHROMA_SUBSAMPLING_410
+ Subsample Cr, Cb components horizontally
+ by 4 and vertically by 2.
+
+
+ V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY
+ Use only luminance component.
+
+
+
+
+
+ V4L2_CID_JPEG_RESTART_INTERVAL
+ integer
+
+
+ The restart interval determines an interval of inserting RSTm
+ markers (m = 0..7). The purpose of these markers is to additionally
+ reinitialize the encoder process, in order to process blocks of
+ an image independently.
+ For the lossy compression processes the restart interval unit is
+ MCU (Minimum Coded Unit) and its value is contained in DRI
+ (Define Restart Interval) marker. If
+ V4L2_CID_JPEG_RESTART_INTERVAL control is set to 0,
+ DRI and RSTm markers will not be inserted.
+
+
+
+ V4L2_CID_JPEG_COMPRESION_QUALITY
+ integer
+
+
+
+ V4L2_CID_JPEG_COMPRESION_QUALITY control
+ determines trade-off between image quality and size.
+ It provides simpler method for applications to control image quality,
+ without a need for direct reconfiguration of luminance and chrominance
+ quantization tables.
+
+ In cases where a driver uses quantization tables configured directly
+ by an application, using interfaces defined elsewhere,
+ V4L2_CID_JPEG_COMPRESION_QUALITY control should be set
+ by driver to 0.
+
+ The value range of this control is driver-specific. Only
+ positive, non-zero values are meaningful. The recommended range
+ is 1 - 100, where larger values correspond to better image quality.
+
+
+
+
+ V4L2_CID_JPEG_ACTIVE_MARKER
+ bitmask
+
+
+ Specify which JPEG markers are included
+ in compressed stream. This control is valid only for encoders.
+
+
+
+
+
+
+ V4L2_JPEG_ACTIVE_MARKER_APP0
+ Application data segment APP0.
+
+ V4L2_JPEG_ACTIVE_MARKER_APP1
+ Application data segment APP1.
+
+ V4L2_JPEG_ACTIVE_MARKER_COM
+ Comment segment.
+
+ V4L2_JPEG_ACTIVE_MARKER_DQT
+ Quantization tables segment.
+
+ V4L2_JPEG_ACTIVE_MARKER_DHT
+ Huffman tables segment.
+
+
+
+
+
+
+
+
+ For more details about JPEG specification, refer
+ to , ,
+ .
diff --git a/Documentation/DocBook/media/v4l/selection-api.xml b/Documentation/DocBook/media/v4l/selection-api.xml
index 2f0bdb4d5551..b299e4779354 100644
--- a/Documentation/DocBook/media/v4l/selection-api.xml
+++ b/Documentation/DocBook/media/v4l/selection-api.xml
@@ -52,6 +52,10 @@ cropping and composing rectangles have the same size.
+
+For complete list of the available selection targets see table
+
@@ -186,7 +190,7 @@ V4L2_SEL_TGT_COMPOSE_ACTIVE target.
- Scaling control.
+ Scaling controlAn application can detect if scaling is performed by comparing the width
and the height of rectangles obtained using V4L2_SEL_TGT_CROP_ACTIVE
@@ -200,7 +204,7 @@ the scaling ratios using these values.
- Comparison with old cropping API.
+ Comparison with old cropping APIThe selection API was introduced to cope with deficiencies of previous
API , that was designed to control simple capture
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml
index e97c512861bb..8ae38876172e 100644
--- a/Documentation/DocBook/media/v4l/v4l2.xml
+++ b/Documentation/DocBook/media/v4l/v4l2.xml
@@ -127,6 +127,22 @@ structs, ioctls) must be noted in more detail in the history chapter
(compat.xml), along with the possible impact on existing drivers and
applications. -->
+
+ 3.4
+ 2012-01-25
+ sn
+ Added JPEG compression
+ control class.
+
+
+
+
+ 3.3
+ 2012-01-11
+ hv
+ Added device_caps field to struct v4l2_capabilities.
+
+
3.22011-08-26
@@ -417,7 +433,7 @@ and discussions on the V4L mailing list.
Video for Linux Two API Specification
- Revision 3.2
+ Revision 3.3
&sub-common;
@@ -473,6 +489,7 @@ and discussions on the V4L mailing list.
&sub-cropcap;
&sub-dbg-g-chip-ident;
&sub-dbg-g-register;
+ &sub-decoder-cmd;
&sub-dqevent;
&sub-encoder-cmd;
&sub-enumaudio;
diff --git a/Documentation/DocBook/media/v4l/vidioc-decoder-cmd.xml b/Documentation/DocBook/media/v4l/vidioc-decoder-cmd.xml
new file mode 100644
index 000000000000..74b87f6e480a
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/vidioc-decoder-cmd.xml
@@ -0,0 +1,256 @@
+
+
+ ioctl VIDIOC_DECODER_CMD, VIDIOC_TRY_DECODER_CMD
+ &manvol;
+
+
+
+ VIDIOC_DECODER_CMD
+ VIDIOC_TRY_DECODER_CMD
+ Execute an decoder command
+
+
+
+
+
+ int ioctl
+ int fd
+ int request
+ struct v4l2_decoder_cmd *argp
+
+
+
+
+
+ Arguments
+
+
+
+ fd
+
+ &fd;
+
+
+
+ request
+
+ VIDIOC_DECODER_CMD, VIDIOC_TRY_DECODER_CMD
+
+
+
+ argp
+
+
+
+
+
+
+
+
+ Description
+
+
+ Experimental
+
+ This is an experimental
+interface and may change in the future.
+
+
+ These ioctls control an audio/video (usually MPEG-) decoder.
+VIDIOC_DECODER_CMD sends a command to the
+decoder, VIDIOC_TRY_DECODER_CMD can be used to
+try a command without actually executing it. To send a command applications
+must initialize all fields of a &v4l2-decoder-cmd; and call
+VIDIOC_DECODER_CMD or VIDIOC_TRY_DECODER_CMD
+with a pointer to this structure.
+
+ The cmd field must contain the
+command code. Some commands use the flags field for
+additional information.
+
+
+ A write() or &VIDIOC-STREAMON; call sends an implicit
+START command to the decoder if it has not been started yet.
+
+
+ A close() or &VIDIOC-STREAMOFF; call of a streaming
+file descriptor sends an implicit immediate STOP command to the decoder, and all
+buffered data is discarded.
+
+ These ioctls are optional, not all drivers may support
+them. They were introduced in Linux 3.3.
+
+
+ struct v4l2_decoder_cmd
+
+ &cs-str;
+
+
+ __u32
+ cmd
+
+
+ The decoder command, see .
+
+
+ __u32
+ flags
+
+
+ Flags to go with the command. If no flags are defined for
+this command, drivers and applications must set this field to zero.
+
+
+ union
+ (anonymous)
+
+
+
+
+
+
+ struct
+ start
+
+ Structure containing additional data for the
+V4L2_DEC_CMD_START command.
+
+
+
+
+ __s32
+ speed
+ Playback speed and direction. The playback speed is defined as
+speed/1000 of the normal speed. So 1000 is normal playback.
+Negative numbers denote reverse playback, so -1000 does reverse playback at normal
+speed. Speeds -1, 0 and 1 have special meanings: speed 0 is shorthand for 1000
+(normal playback). A speed of 1 steps just one frame forward, a speed of -1 steps
+just one frame back.
+
+
+
+
+
+ __u32
+ format
+ Format restrictions. This field is set by the driver, not the
+application. Possible values are V4L2_DEC_START_FMT_NONE if
+there are no format restrictions or V4L2_DEC_START_FMT_GOP
+if the decoder operates on full GOPs (Group Of Pictures).
+This is usually the case for reverse playback: the decoder needs full GOPs, which
+it can then play in reverse order. So to implement reverse playback the application
+must feed the decoder the last GOP in the video file, then the GOP before that, etc. etc.
+
+
+
+
+ struct
+ stop
+
+ Structure containing additional data for the
+V4L2_DEC_CMD_STOP command.
+
+
+
+
+ __u64
+ pts
+ Stop playback at this pts or immediately
+if the playback is already past that timestamp. Leave to 0 if you want to stop after the
+last frame was decoded.
+
+
+
+
+ struct
+ raw
+
+
+
+
+
+
+ __u32
+ data[16]
+ Reserved for future extensions. Drivers and
+applications must set the array to zero.
+
+
+
+
+
+
+ Decoder Commands
+
+ &cs-def;
+
+
+ V4L2_DEC_CMD_START
+ 0
+ Start the decoder. When the decoder is already
+running or paused, this command will just change the playback speed.
+That means that calling V4L2_DEC_CMD_START when
+the decoder was paused will not resume the decoder.
+You have to explicitly call V4L2_DEC_CMD_RESUME for that.
+This command has one flag:
+V4L2_DEC_CMD_START_MUTE_AUDIO. If set, then audio will
+be muted when playing back at a non-standard speed.
+
+
+
+ V4L2_DEC_CMD_STOP
+ 1
+ Stop the decoder. When the decoder is already stopped,
+this command does nothing. This command has two flags:
+if V4L2_DEC_CMD_STOP_TO_BLACK is set, then the decoder will
+set the picture to black after it stopped decoding. Otherwise the last image will
+repeat. If V4L2_DEC_CMD_STOP_IMMEDIATELY is set, then the decoder
+stops immediately (ignoring the pts value), otherwise it
+will keep decoding until timestamp >= pts or until the last of the pending data from
+its internal buffers was decoded.
+
+
+
+ V4L2_DEC_CMD_PAUSE
+ 2
+ Pause the decoder. When the decoder has not been
+started yet, the driver will return an &EPERM;. When the decoder is
+already paused, this command does nothing. This command has one flag:
+if V4L2_DEC_CMD_PAUSE_TO_BLACK is set, then set the
+decoder output to black when paused.
+
+
+
+ V4L2_DEC_CMD_RESUME
+ 3
+ Resume decoding after a PAUSE command. When the
+decoder has not been started yet, the driver will return an &EPERM;.
+When the decoder is already running, this command does nothing. No
+flags are defined for this command.
+
+
+
+
+
+
+
+
+ &return-value;
+
+
+
+ EINVAL
+
+ The cmd field is invalid.
+
+
+
+ EPERM
+
+ The application sent a PAUSE or RESUME command when
+the decoder was not running.
+
+
+
+
+
diff --git a/Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml b/Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml
index af7f3f2a36dd..f431b3ba79bd 100644
--- a/Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml
@@ -74,15 +74,16 @@ only used by the STOP command and contains one bit: If the
encoding will continue until the end of the current Group
Of Pictures, otherwise it will stop immediately.
- A read() call sends a START command to
-the encoder if it has not been started yet. After a STOP command,
+ A read() or &VIDIOC-STREAMON; call sends an implicit
+START command to the encoder if it has not been started yet. After a STOP command,
read() calls will read the remaining data
buffered by the driver. When the buffer is empty,
read() will return zero and the next
read() call will restart the encoder.
- A close() call sends an immediate STOP
-to the encoder, and all buffered data is discarded.
+ A close() or &VIDIOC-STREAMOFF; call of a streaming
+file descriptor sends an implicit immediate STOP to the encoder, and all buffered
+data is discarded.These ioctls are optional, not all drivers may support
them. They were introduced in Linux 2.6.21.
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-jpegcomp.xml b/Documentation/DocBook/media/v4l/vidioc-g-jpegcomp.xml
index 01ea24b84385..48748499c097 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-jpegcomp.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-jpegcomp.xml
@@ -57,6 +57,11 @@
Description
+ These ioctls are deprecated.
+ New drivers and applications should use
+ JPEG class controls for image quality and JPEG markers control.
+
+
[to do]Ronald Bultje elaborates:
@@ -86,7 +91,10 @@ to add them.intquality
-
+ Deprecated. If
+ V4L2_CID_JPEG_IMAGE_QUALITY control is exposed by
+ a driver applications should use it instead and ignore this field.
+ int
@@ -116,7 +124,11 @@ to add them.
__u32jpeg_markers
- See .
+ See . Deprecated.
+ If
+ V4L2_CID_JPEG_ACTIVE_MARKER control
+ is exposed by a driver applications should use it instead
+ and ignore this field.
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml
index a9d36e0c090e..bb04eff75f45 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml
@@ -58,43 +58,43 @@
The ioctls are used to query and configure selection rectangles.
- To query the cropping (composing) rectangle set
-&v4l2-selection;::type to the respective buffer type. Do not
-use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE
+ To query the cropping (composing) rectangle set &v4l2-selection;
+ type field to the respective buffer type.
+Do not use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE
instead of V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
. Use V4L2_BUF_TYPE_VIDEO_OUTPUT instead of
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE . The next step is
-setting &v4l2-selection;::target to value
- V4L2_SEL_TGT_CROP_ACTIVE (
+setting the value of &v4l2-selection; target field
+to V4L2_SEL_TGT_CROP_ACTIVE (
V4L2_SEL_TGT_COMPOSE_ACTIVE ). Please refer to table or for additional
-targets. Fields &v4l2-selection;::flags and
- &v4l2-selection;::reserved are ignored and they
-must be filled with zeros. The driver fills the rest of the structure or
+targets. The flags and reserved
+ fields of &v4l2-selection; are ignored and they must be filled
+with zeros. The driver fills the rest of the structure or
returns &EINVAL; if incorrect buffer type or target was used. If cropping
(composing) is not supported then the active rectangle is not mutable and it is
-always equal to the bounds rectangle. Finally, structure
-&v4l2-selection;::r is filled with the current cropping
+always equal to the bounds rectangle. Finally, the &v4l2-rect;
+r rectangle is filled with the current cropping
(composing) coordinates. The coordinates are expressed in driver-dependent
units. The only exception are rectangles for images in raw formats, whose
coordinates are always expressed in pixels.
- To change the cropping (composing) rectangle set
-&v4l2-selection;::type to the respective buffer type. Do not
+ To change the cropping (composing) rectangle set the &v4l2-selection;
+type field to the respective buffer type. Do not
use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE
instead of V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
. Use V4L2_BUF_TYPE_VIDEO_OUTPUT instead of
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE . The next step is
-setting &v4l2-selection;::target to value
- V4L2_SEL_TGT_CROP_ACTIVE (
+setting the value of &v4l2-selection; target to
+V4L2_SEL_TGT_CROP_ACTIVE (
V4L2_SEL_TGT_COMPOSE_ACTIVE ). Please refer to table or for additional
-targets. Set desired active area into the field
-&v4l2-selection;::r . Field
-&v4l2-selection;::reserved is ignored and must be filled with
-zeros. The driver may adjust the rectangle coordinates. An application may
-introduce constraints to control rounding behaviour. Set the field
- &v4l2-selection;::flags to one of values:
+targets. The &v4l2-rect; r rectangle need to be
+set to the desired active area. Field &v4l2-selection; reserved
+ is ignored and must be filled with zeros. The driver may adjust
+coordinates of the requested rectangle. An application may
+introduce constraints to control rounding behaviour. The &v4l2-selection;
+flags field must be set to one of the following:
@@ -129,7 +129,7 @@ and vertical offset and sizes are chosen according to following priority:
- Satisfy constraints from &v4l2-selection;::flags.
+ Satisfy constraints from &v4l2-selection; flags.Adjust width, height, left, and top to hardware limits and alignments.
@@ -145,7 +145,7 @@ and vertical offset and sizes are chosen according to following priority:
-On success the field &v4l2-selection;::r contains
+On success the &v4l2-rect; r field contains
the adjusted rectangle. When the parameters are unsuitable the application may
modify the cropping (composing) or image parameters and repeat the cycle until
satisfactory parameters have been negotiated. If constraints flags have to be
@@ -162,38 +162,38 @@ exist no rectangle that satisfies the constraints.V4L2_SEL_TGT_CROP_ACTIVE
- 0
- area that is currently cropped by hardware
+ 0x0000
+ The area that is currently cropped by hardware.V4L2_SEL_TGT_CROP_DEFAULT
- 1
- suggested cropping rectangle that covers the "whole picture"
+ 0x0001
+ Suggested cropping rectangle that covers the "whole picture".V4L2_SEL_TGT_CROP_BOUNDS
- 2
- limits for the cropping rectangle
+ 0x0002
+ Limits for the cropping rectangle.V4L2_SEL_TGT_COMPOSE_ACTIVE
- 256
- area to which data are composed by hardware
+ 0x0100
+ The area to which data is composed by hardware.V4L2_SEL_TGT_COMPOSE_DEFAULT
- 257
- suggested composing rectangle that covers the "whole picture"
+ 0x0101
+ Suggested composing rectangle that covers the "whole picture".V4L2_SEL_TGT_COMPOSE_BOUNDS
- 258
- limits for the composing rectangle
+ 0x0102
+ Limits for the composing rectangle.V4L2_SEL_TGT_COMPOSE_PADDED
- 259
- the active area and all padding pixels that are inserted or modified by the hardware
+ 0x0103
+ The active area and all padding pixels that are inserted or modified by hardware.
@@ -209,12 +209,14 @@ exist no rectangle that satisfies the constraints.V4L2_SEL_FLAG_GE0x00000001
- indicate that adjusted rectangle must contain a rectangle from &v4l2-selection;::r
+ Indicates that the adjusted rectangle must contain the original
+ &v4l2-selection; r rectangle.V4L2_SEL_FLAG_LE0x00000002
- indicate that adjusted rectangle must be inside a rectangle from &v4l2-selection;::r
+ Indicates that the adjusted rectangle must be inside the original
+ &v4l2-rect; r rectangle.
@@ -245,27 +247,29 @@ exist no rectangle that satisfies the constraints.__u32type
- Type of the buffer (from &v4l2-buf-type;)
+ Type of the buffer (from &v4l2-buf-type;).__u32target
- used to select between cropping and composing rectangles
+ Used to select between cropping
+ and composing rectangles.__u32flags
- control over coordinates adjustments, refer to selection flags
+ Flags controlling the selection rectangle adjustments, refer to
+ selection flags.&v4l2-rect;r
- selection rectangle
+ The selection rectangle.__u32reserved[9]
- Reserved fields for future use
+ Reserved fields for future use.
@@ -278,24 +282,24 @@ exist no rectangle that satisfies the constraints.
EINVAL
- The buffer &v4l2-selection;::type
-or &v4l2-selection;::target is not supported, or
-the &v4l2-selection;::flags are invalid.
+ Given buffer type type or
+the selection target target is not supported,
+or the flags argument is not valid.ERANGE
- it is not possible to adjust a rectangle
-&v4l2-selection;::r that satisfies all contraints from
- &v4l2-selection;::flags .
+ It is not possible to adjust &v4l2-rect;
+r rectangle to satisfy all contraints given in the
+flags argument.EBUSY
- it is not possible to apply change of selection rectangle at the moment.
-Usually because streaming is in progress.
+ It is not possible to apply change of the selection rectangle
+at the moment. Usually because streaming is in progress.
diff --git a/Documentation/DocBook/media/v4l/vidioc-querycap.xml b/Documentation/DocBook/media/v4l/vidioc-querycap.xml
index e3664d6f2de4..4643505cd4ca 100644
--- a/Documentation/DocBook/media/v4l/vidioc-querycap.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-querycap.xml
@@ -124,12 +124,35 @@ printf ("Version: %u.%u.%u\n",
__u32capabilities
- Device capabilities, see .
+ Available capabilities of the physical device as a whole, see . The same physical device can export
+ multiple devices in /dev (e.g. /dev/videoX, /dev/vbiY and /dev/radioZ).
+ The capabilities field should contain a union
+ of all capabilities available around the several V4L2 devices exported
+ to userspace.
+ For all those devices the capabilities field
+ returns the same set of capabilities. This allows applications to open
+ just one of the devices (typically the video device) and discover whether
+ video, vbi and/or radio are also supported.
+ __u32
- reserved[4]
+ device_caps
+ Device capabilities of the opened device, see . Should contain the available capabilities
+ of that specific device node. So, for example, device_caps
+ of a radio device will only contain radio related capabilities and
+ no video or vbi capabilities. This field is only set if the capabilities
+ field contains the V4L2_CAP_DEVICE_CAPS capability.
+ Only the capabilities field can have the
+ V4L2_CAP_DEVICE_CAPS capability, device_caps
+ will never set V4L2_CAP_DEVICE_CAPS.
+
+
+
+ __u32
+ reserved[3]Reserved for future extensions. Drivers must set
this array to zero.
@@ -276,6 +299,13 @@ linkend="async">asynchronous I/O methods.
The device supports the streaming I/O method.
+
+ V4L2_CAP_DEVICE_CAPS
+ 0x80000000
+ The driver fills the device_caps
+ field. This capability can only appear in the capabilities
+ field and never in the device_caps field.
+
diff --git a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
index e013da845b11..18b1a8266f7c 100644
--- a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
@@ -96,8 +96,8 @@ field and the &v4l2-tuner; index field.
__u32reserved[7]
- Reserved for future extensions. Drivers and
- applications must set the array to zero.
+ Reserved for future extensions. Applications
+ must set the array to zero.
@@ -112,7 +112,7 @@ field and the &v4l2-tuner; index field.
EINVALThe tuner index is out of
-bounds or the value in the type field is
+bounds, the wrap_around value is not supported or the value in the type field is
wrong.
diff --git a/Documentation/dvb/cards.txt b/Documentation/dvb/cards.txt
index cc09187a5db7..97709e9a3076 100644
--- a/Documentation/dvb/cards.txt
+++ b/Documentation/dvb/cards.txt
@@ -119,4 +119,5 @@ o Cards based on the Phillips saa7134 PCI bridge:
- Compro Videomate DVB-T300
- Compro Videomate DVB-T200
- AVerMedia AVerTVHD MCE A180
+ - KWorld PC150-U ATSC Hybrid
diff --git a/Documentation/dvb/lmedm04.txt b/Documentation/dvb/lmedm04.txt
index 10b5f0411386..f4b720a14675 100644
--- a/Documentation/dvb/lmedm04.txt
+++ b/Documentation/dvb/lmedm04.txt
@@ -66,5 +66,16 @@ dd if=US290D.sys ibs=1 skip=36856 count=3976 of=dvb-usb-lme2510-s0194.fw
For LME2510C
dd if=US290D.sys ibs=1 skip=33152 count=3697 of=dvb-usb-lme2510c-s0194.fw
+---------------------------------------------------------------------
+
+The m88rs2000 tuner driver can be found in windows/system32/drivers
+
+US2B0D.sys (dated 29 Jun 2010)
+
+dd if=US2B0D.sys ibs=1 skip=34432 count=3871 of=dvb-usb-lme2510c-rs2000.fw
+
+We need to modify id of rs2000 firmware or it will warm boot id 3344:1120.
+
+echo -ne \\xF0\\x22 | dd conv=notrunc bs=1 count=2 seek=266 of=dvb-usb-lme2510c-rs2000.fw
Copy the firmware file(s) to /lib/firmware
diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885
index 23584d0c6a75..f316d1816fcd 100644
--- a/Documentation/video4linux/CARDLIST.cx23885
+++ b/Documentation/video4linux/CARDLIST.cx23885
@@ -32,3 +32,4 @@
31 -> Leadtek Winfast PxDVR3200 H XC4000 [107d:6f39]
32 -> MPX-885
33 -> Mygica X8507 [14f1:8502]
+ 34 -> TerraTec Cinergy T PCIe Dual [153b:117e]
diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88
index eee18e6962b6..fa4b3f947468 100644
--- a/Documentation/video4linux/CARDLIST.cx88
+++ b/Documentation/video4linux/CARDLIST.cx88
@@ -59,7 +59,7 @@
58 -> Pinnacle PCTV HD 800i [11bd:0051]
59 -> DViCO FusionHDTV 5 PCI nano [18ac:d530]
60 -> Pinnacle Hybrid PCTV [12ab:1788]
- 61 -> Leadtek TV2000 XP Global [107d:6f18,107d:6618]
+ 61 -> Leadtek TV2000 XP Global [107d:6f18,107d:6618,107d:6619]
62 -> PowerColor RA330 [14f1:ea3d]
63 -> Geniatech X8000-MT DVBT [14f1:8852]
64 -> DViCO FusionHDTV DVB-T PRO [18ac:db30]
@@ -87,3 +87,5 @@
86 -> TeVii S464 DVB-S/S2 [d464:9022]
87 -> Leadtek WinFast DTV2000 H PLUS [107d:6f42]
88 -> Leadtek WinFast DTV1800 H (XC4000) [107d:6f38]
+ 89 -> Leadtek TV2000 XP Global (SC4100) [107d:6f36]
+ 90 -> Leadtek TV2000 XP Global (XC4100) [107d:6f43]
diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx
index e7be3ac49ead..d99262dda533 100644
--- a/Documentation/video4linux/CARDLIST.em28xx
+++ b/Documentation/video4linux/CARDLIST.em28xx
@@ -7,7 +7,7 @@
6 -> Terratec Cinergy 200 USB (em2800)
7 -> Leadtek Winfast USB II (em2800) [0413:6023]
8 -> Kworld USB2800 (em2800)
- 9 -> Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker (em2820/em2840) [1b80:e302,1b80:e304,2304:0207,2304:021a]
+ 9 -> Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker (em2820/em2840) [1b80:e302,1b80:e304,2304:0207,2304:021a,093b:a003]
10 -> Hauppauge WinTV HVR 900 (em2880) [2040:6500]
11 -> Terratec Hybrid XS (em2880)
12 -> Kworld PVR TV 2800 RF (em2820/em2840)
@@ -61,7 +61,7 @@
61 -> Pixelview PlayTV Box 4 USB 2.0 (em2820/em2840)
62 -> Gadmei TVR200 (em2820/em2840)
63 -> Kaiomy TVnPC U2 (em2860) [eb1a:e303]
- 64 -> Easy Cap Capture DC-60 (em2860)
+ 64 -> Easy Cap Capture DC-60 (em2860) [1b80:e309]
65 -> IO-DATA GV-MVP/SZ (em2820/em2840) [04bb:0515]
66 -> Empire dual TV (em2880)
67 -> Terratec Grabby (em2860) [0ccd:0096,0ccd:10AF]
@@ -76,7 +76,11 @@
76 -> KWorld PlusTV 340U or UB435-Q (ATSC) (em2870) [1b80:a340]
77 -> EM2874 Leadership ISDBT (em2874)
78 -> PCTV nanoStick T2 290e (em28174)
- 79 -> Terratec Cinergy H5 (em2884) [0ccd:10a2,0ccd:10ad]
+ 79 -> Terratec Cinergy H5 (em2884) [0ccd:008e,0ccd:00ac,0ccd:10a2,0ccd:10ad]
80 -> PCTV DVB-S2 Stick (460e) (em28174)
81 -> Hauppauge WinTV HVR 930C (em2884) [2040:1605]
82 -> Terratec Cinergy HTC Stick (em2884) [0ccd:00b2]
+ 83 -> Honestech Vidbox NW03 (em2860) [eb1a:5006]
+ 84 -> MaxMedia UB425-TC (em2874) [1b80:e425]
+ 85 -> PCTV QuatroStick (510e) (em2884) [2304:0242]
+ 86 -> PCTV QuatroStick nano (520e) (em2884) [2013:0251]
diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
index e7ef38a19859..34f3b330e5f4 100644
--- a/Documentation/video4linux/CARDLIST.saa7134
+++ b/Documentation/video4linux/CARDLIST.saa7134
@@ -187,3 +187,4 @@
186 -> Beholder BeholdTV 501 [5ace:5010]
187 -> Beholder BeholdTV 503 FM [5ace:5030]
188 -> Sensoray 811/911 [6000:0811,6000:0911]
+189 -> Kworld PC150-U [17de:a134]
diff --git a/Documentation/video4linux/CARDLIST.tuner b/Documentation/video4linux/CARDLIST.tuner
index 6323b7a20719..c83f6e418879 100644
--- a/Documentation/video4linux/CARDLIST.tuner
+++ b/Documentation/video4linux/CARDLIST.tuner
@@ -78,10 +78,11 @@ tuner=77 - TCL tuner MF02GIP-5N-E
tuner=78 - Philips FMD1216MEX MK3 Hybrid Tuner
tuner=79 - Philips PAL/SECAM multi (FM1216 MK5)
tuner=80 - Philips FQ1216LME MK3 PAL/SECAM w/active loopthrough
-tuner=81 - Xceive 4000 tuner
tuner=81 - Partsnic (Daewoo) PTI-5NF05
tuner=82 - Philips CU1216L
tuner=83 - NXP TDA18271
tuner=84 - Sony BTF-Pxn01Z
tuner=85 - Philips FQ1236 MK5
tuner=86 - Tena TNF5337 MFD
+tuner=87 - Xceive 4000 tuner
+tuner=88 - Xceive 5000C tuner
diff --git a/Documentation/video4linux/fimc.txt b/Documentation/video4linux/fimc.txt
new file mode 100644
index 000000000000..eb049708f3e4
--- /dev/null
+++ b/Documentation/video4linux/fimc.txt
@@ -0,0 +1,178 @@
+Samsung S5P/EXYNOS4 FIMC driver
+
+Copyright (C) 2012 Samsung Electronics Co., Ltd.
+---------------------------------------------------------------------------
+
+The FIMC (Fully Interactive Mobile Camera) device available in Samsung
+SoC Application Processors is an integrated camera host interface, color
+space converter, image resizer and rotator. It's also capable of capturing
+data from LCD controller (FIMD) through the SoC internal writeback data
+path. There are multiple FIMC instances in the SoCs (up to 4), having
+slightly different capabilities, like pixel alignment constraints, rotator
+availability, LCD writeback support, etc. The driver is located at
+drivers/media/video/s5p-fimc directory.
+
+1. Supported SoCs
+=================
+
+S5PC100 (mem-to-mem only), S5PV210, EXYNOS4210
+
+2. Supported features
+=====================
+
+ - camera parallel interface capture (ITU-R.BT601/565);
+ - camera serial interface capture (MIPI-CSI2);
+ - memory-to-memory processing (color space conversion, scaling, mirror
+ and rotation);
+ - dynamic pipeline re-configuration at runtime (re-attachment of any FIMC
+ instance to any parallel video input or any MIPI-CSI front-end);
+ - runtime PM and system wide suspend/resume
+
+Not currently supported:
+ - LCD writeback input
+ - per frame clock gating (mem-to-mem)
+
+3. Files partitioning
+=====================
+
+- media device driver
+ drivers/media/video/s5p-fimc/fimc-mdevice.[ch]
+
+ - camera capture video device driver
+ drivers/media/video/s5p-fimc/fimc-capture.c
+
+ - MIPI-CSI2 receiver subdev
+ drivers/media/video/s5p-fimc/mipi-csis.[ch]
+
+ - video post-processor (mem-to-mem)
+ drivers/media/video/s5p-fimc/fimc-core.c
+
+ - common files
+ drivers/media/video/s5p-fimc/fimc-core.h
+ drivers/media/video/s5p-fimc/fimc-reg.h
+ drivers/media/video/s5p-fimc/regs-fimc.h
+
+4. User space interfaces
+========================
+
+4.1. Media device interface
+
+The driver supports Media Controller API as defined at
+http://http://linuxtv.org/downloads/v4l-dvb-apis/media_common.html
+The media device driver name is "SAMSUNG S5P FIMC".
+
+The purpose of this interface is to allow changing assignment of FIMC instances
+to the SoC peripheral camera input at runtime and optionally to control internal
+connections of the MIPI-CSIS device(s) to the FIMC entities.
+
+The media device interface allows to configure the SoC for capturing image
+data from the sensor through more than one FIMC instance (e.g. for simultaneous
+viewfinder and still capture setup).
+Reconfiguration is done by enabling/disabling media links created by the driver
+during initialization. The internal device topology can be easily discovered
+through media entity and links enumeration.
+
+4.2. Memory-to-memory video node
+
+V4L2 memory-to-memory interface at /dev/video? device node. This is standalone
+video device, it has no media pads. However please note the mem-to-mem and
+capture video node operation on same FIMC instance is not allowed. The driver
+detects such cases but the applications should prevent them to avoid an
+undefined behaviour.
+
+4.3. Capture video node
+
+The driver supports V4L2 Video Capture Interface as defined at:
+http://linuxtv.org/downloads/v4l-dvb-apis/devices.html
+
+At the capture and mem-to-mem video nodes only the multi-planar API is
+supported. For more details see:
+http://linuxtv.org/downloads/v4l-dvb-apis/planar-apis.html
+
+4.4. Camera capture subdevs
+
+Each FIMC instance exports a sub-device node (/dev/v4l-subdev?), a sub-device
+node is also created per each available and enabled at the platform level
+MIPI-CSI receiver device (currently up to two).
+
+4.5. sysfs
+
+In order to enable more precise camera pipeline control through the sub-device
+API the driver creates a sysfs entry associated with "s5p-fimc-md" platform
+device. The entry path is: /sys/platform/devices/s5p-fimc-md/subdev_conf_mode.
+
+In typical use case there could be a following capture pipeline configuration:
+sensor subdev -> mipi-csi subdev -> fimc subdev -> video node
+
+When we configure these devices through sub-device API at user space, the
+configuration flow must be from left to right, and the video node is
+configured as last one.
+When we don't use sub-device user space API the whole configuration of all
+devices belonging to the pipeline is done at the video node driver.
+The sysfs entry allows to instruct the capture node driver not to configure
+the sub-devices (format, crop), to avoid resetting the subdevs' configuration
+when the last configuration steps at the video node is performed.
+
+For full sub-device control support (subdevs configured at user space before
+starting streaming):
+# echo "sub-dev" > /sys/platform/devices/s5p-fimc-md/subdev_conf_mode
+
+For V4L2 video node control only (subdevs configured internally by the host
+driver):
+# echo "vid-dev" > /sys/platform/devices/s5p-fimc-md/subdev_conf_mode
+This is a default option.
+
+5. Device mapping to video and subdev device nodes
+==================================================
+
+There are associated two video device nodes with each device instance in
+hardware - video capture and mem-to-mem and additionally a subdev node for
+more precise FIMC capture subsystem control. In addition a separate v4l2
+sub-device node is created per each MIPI-CSIS device.
+
+How to find out which /dev/video? or /dev/v4l-subdev? is assigned to which
+device?
+
+You can either grep through the kernel log to find relevant information, i.e.
+# dmesg | grep -i fimc
+(note that udev, if present, might still have rearranged the video nodes),
+
+or retrieve the information from /dev/media? with help of the media-ctl tool:
+# media-ctl -p
+
+6. Platform support
+===================
+
+The machine code (plat-s5p and arch/arm/mach-*) must select following options
+
+CONFIG_S5P_DEV_FIMC0 mandatory
+CONFIG_S5P_DEV_FIMC1 \
+CONFIG_S5P_DEV_FIMC2 | optional
+CONFIG_S5P_DEV_FIMC3 |
+CONFIG_S5P_SETUP_FIMC /
+CONFIG_S5P_SETUP_MIPIPHY \
+CONFIG_S5P_DEV_CSIS0 | optional for MIPI-CSI interface
+CONFIG_S5P_DEV_CSIS1 /
+
+Except that, relevant s5p_device_fimc? should be registered in the machine code
+in addition to a "s5p-fimc-md" platform device to which the media device driver
+is bound. The "s5p-fimc-md" device instance is required even if only mem-to-mem
+operation is used.
+
+The description of sensor(s) attached to FIMC/MIPI-CSIS camera inputs should be
+passed as the "s5p-fimc-md" device platform_data. The platform data structure
+is defined in file include/media/s5p_fimc.h.
+
+7. Build
+========
+
+This driver depends on following config options:
+PLAT_S5P,
+PM_RUNTIME,
+I2C,
+REGULATOR,
+VIDEO_V4L2_SUBDEV_API,
+
+If the driver is built as a loadable kernel module (CONFIG_VIDEO_SAMSUNG_S5P_FIMC=m)
+two modules are created (in addition to the core v4l2 modules): s5p-fimc.ko and
+optional s5p-csis.ko (MIPI-CSI receiver subdev).
diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt
index f2060f0dc02c..e6c2842407a4 100644
--- a/Documentation/video4linux/gspca.txt
+++ b/Documentation/video4linux/gspca.txt
@@ -217,6 +217,7 @@ ov534_9 06f8:3003 Hercules Dualpix HD Weblog
sonixj 06f8:3004 Hercules Classic Silver
sonixj 06f8:3008 Hercules Deluxe Optical Glass
pac7302 06f8:3009 Hercules Classic Link
+pac7302 06f8:301b Hercules Link
nw80x 0728:d001 AVerMedia Camguard
spca508 0733:0110 ViewQuest VQ110
spca501 0733:0401 Intel Create and Share
diff --git a/arch/arm/mach-imx/clock-imx27.c b/arch/arm/mach-imx/clock-imx27.c
index 88fe00a146e3..dc2d7a511d9b 100644
--- a/arch/arm/mach-imx/clock-imx27.c
+++ b/arch/arm/mach-imx/clock-imx27.c
@@ -661,7 +661,7 @@ static struct clk_lookup lookups[] = {
_REGISTER_CLOCK(NULL, "dma", dma_clk)
_REGISTER_CLOCK(NULL, "rtic", rtic_clk)
_REGISTER_CLOCK(NULL, "brom", brom_clk)
- _REGISTER_CLOCK(NULL, "emma", emma_clk)
+ _REGISTER_CLOCK("m2m-emmaprp.0", NULL, emma_clk)
_REGISTER_CLOCK(NULL, "slcdc", slcdc_clk)
_REGISTER_CLOCK("imx27-fec.0", NULL, fec_clk)
_REGISTER_CLOCK(NULL, "emi", emi_clk)
diff --git a/arch/arm/mach-imx/devices-imx27.h b/arch/arm/mach-imx/devices-imx27.h
index 2f727d7c380c..28537a5d9048 100644
--- a/arch/arm/mach-imx/devices-imx27.h
+++ b/arch/arm/mach-imx/devices-imx27.h
@@ -50,6 +50,8 @@ extern const struct imx_imx_uart_1irq_data imx27_imx_uart_data[];
extern const struct imx_mx2_camera_data imx27_mx2_camera_data;
#define imx27_add_mx2_camera(pdata) \
imx_add_mx2_camera(&imx27_mx2_camera_data, pdata)
+#define imx27_add_mx2_emmaprp(pdata) \
+ imx_add_mx2_emmaprp(&imx27_mx2_camera_data)
extern const struct imx_mxc_ehci_data imx27_mxc_ehci_otg_data;
#define imx27_add_mxc_ehci_otg(pdata) \
diff --git a/arch/arm/plat-mxc/devices/platform-mx2-camera.c b/arch/arm/plat-mxc/devices/platform-mx2-camera.c
index b3f4828dc447..11eace953a09 100644
--- a/arch/arm/plat-mxc/devices/platform-mx2-camera.c
+++ b/arch/arm/plat-mxc/devices/platform-mx2-camera.c
@@ -62,3 +62,21 @@ struct platform_device *__init imx_add_mx2_camera(
res, data->iobaseemmaprp ? 4 : 2,
pdata, sizeof(*pdata), DMA_BIT_MASK(32));
}
+
+struct platform_device *__init imx_add_mx2_emmaprp(
+ const struct imx_mx2_camera_data *data)
+{
+ struct resource res[] = {
+ {
+ .start = data->iobaseemmaprp,
+ .end = data->iobaseemmaprp + data->iosizeemmaprp - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = data->irqemmaprp,
+ .end = data->irqemmaprp,
+ .flags = IORESOURCE_IRQ,
+ },
+ };
+ return imx_add_platform_device_dmamask("m2m-emmaprp", 0,
+ res, 2, NULL, 0, DMA_BIT_MASK(32));
+}
diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h
index def9ba53e23a..1b2258daa05b 100644
--- a/arch/arm/plat-mxc/include/mach/devices-common.h
+++ b/arch/arm/plat-mxc/include/mach/devices-common.h
@@ -223,6 +223,8 @@ struct imx_mx2_camera_data {
struct platform_device *__init imx_add_mx2_camera(
const struct imx_mx2_camera_data *data,
const struct mx2_camera_platform_data *pdata);
+struct platform_device *__init imx_add_mx2_emmaprp(
+ const struct imx_mx2_camera_data *data);
#include
struct imx_mxc_ehci_data {
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 70ca07f10d5c..4da66b4b977c 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2026,6 +2026,16 @@ static bool hid_ignore(struct hid_device *hdev)
if (hdev->product >= USB_DEVICE_ID_LOGITECH_HARMONY_FIRST &&
hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST)
return true;
+ /*
+ * The Keene FM transmitter USB device has the same USB ID as
+ * the Logitech AudioHub Speaker, but it should ignore the hid.
+ * Check if the name is that of the Keene device.
+ * For reference: the name of the AudioHub is
+ * "HOLTEK AudioHub Speaker".
+ */
+ if (hdev->product == USB_DEVICE_ID_LOGITECH_AUDIOHUB &&
+ !strcmp(hdev->name, "HOLTEK B-LINK USB Audio "))
+ return true;
break;
case USB_VENDOR_ID_SOUNDGRAPH:
if (hdev->product >= USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST &&
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 2a5cef2f53a7..e39aecb1f9f2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -471,6 +471,7 @@
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
#define USB_VENDOR_ID_LOGITECH 0x046d
+#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
index 8295854ab94b..f80407eb8998 100644
--- a/drivers/media/common/tuners/Makefile
+++ b/drivers/media/common/tuners/Makefile
@@ -29,5 +29,5 @@ obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/max2165.c b/drivers/media/common/tuners/max2165.c
index cb2c98fbad1b..ba84936aafd6 100644
--- a/drivers/media/common/tuners/max2165.c
+++ b/drivers/media/common/tuners/max2165.c
@@ -168,7 +168,7 @@ int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction)
int i;
if (0 == divisor)
- return -1;
+ return -EINVAL;
q = dividend / divisor;
remainder = dividend - q * divisor;
@@ -194,10 +194,13 @@ static int max2165_set_rf(struct max2165_priv *priv, u32 freq)
u8 tf_ntch;
u32 t;
u32 quotient, fraction;
+ int ret;
/* Set PLL divider according to RF frequency */
- fixpt_div32(freq / 1000, priv->config->osc_clk * 1000,
- "ient, &fraction);
+ ret = fixpt_div32(freq / 1000, priv->config->osc_clk * 1000,
+ "ient, &fraction);
+ if (ret != 0)
+ return ret;
/* 20-bit fraction */
fraction >>= 12;
diff --git a/drivers/media/common/tuners/mt2063.c b/drivers/media/common/tuners/mt2063.c
index c89af3cd5eba..0ed9091ff48e 100644
--- a/drivers/media/common/tuners/mt2063.c
+++ b/drivers/media/common/tuners/mt2063.c
@@ -350,7 +350,7 @@ static int MT2063_Sleep(struct dvb_frontend *fe)
/*
* ToDo: Add code here to implement a OS blocking
*/
- msleep(10);
+ msleep(100);
return 0;
}
@@ -2226,7 +2226,7 @@ static struct dvb_tuner_ops mt2063_ops = {
.info = {
.name = "MT2063 Silicon Tuner",
.frequency_min = 45000000,
- .frequency_max = 850000000,
+ .frequency_max = 865000000,
.frequency_step = 0,
},
diff --git a/drivers/media/common/tuners/mt2063.h b/drivers/media/common/tuners/mt2063.h
index 62d0e8ec4e99..3f5cfd93713f 100644
--- a/drivers/media/common/tuners/mt2063.h
+++ b/drivers/media/common/tuners/mt2063.h
@@ -23,10 +23,6 @@ static inline struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
return NULL;
}
-int mt2063_setTune(struct dvb_frontend *fe, u32 f_in,
- u32 bw_in,
- enum MTTune_atv_standard tv_type);
-
/* FIXME: Should use the standard DVB attachment interfaces */
unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe);
unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe);
diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c
index e13683bab6b3..2da4440c16ee 100644
--- a/drivers/media/common/tuners/tuner-types.c
+++ b/drivers/media/common/tuners/tuner-types.c
@@ -1868,6 +1868,10 @@ struct tunertype tuners[] = {
.params = tuner_tena_tnf_5337_params,
.count = ARRAY_SIZE(tuner_tena_tnf_5337_params),
},
+ [TUNER_XC5000C] = { /* Xceive 5000C */
+ .name = "Xceive 5000C tuner",
+ /* see xc5000.c for details */
+ },
};
EXPORT_SYMBOL(tuners);
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index 296df05b8cda..7f98984e4fad 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -49,9 +49,6 @@ static LIST_HEAD(hybrid_tuner_instance_list);
#define dprintk(level, fmt, arg...) if (debug >= level) \
printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
-#define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
-#define XC5000_DEFAULT_FIRMWARE_SIZE 12401
-
struct xc5000_priv {
struct tuner_i2c_props i2c_props;
struct list_head hybrid_tuner_instance_list;
@@ -62,6 +59,8 @@ struct xc5000_priv {
u8 video_standard;
u8 rf_mode;
u8 radio_input;
+
+ int chip_id;
};
/* Misc Defines */
@@ -204,6 +203,33 @@ static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
{"FM Radio-INPUT1_MONO", 0x0278, 0x9002}
};
+
+struct xc5000_fw_cfg {
+ char *name;
+ u16 size;
+};
+
+static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
+ .name = "dvb-fe-xc5000-1.6.114.fw",
+ .size = 12401,
+};
+
+static const struct xc5000_fw_cfg xc5000c_41_024_5_31875 = {
+ .name = "dvb-fe-xc5000c-41.024.5-31875.fw",
+ .size = 16503,
+};
+
+static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
+{
+ switch (chip_id) {
+ default:
+ case XC5000A:
+ return &xc5000a_1_6_114;
+ case XC5000C:
+ return &xc5000c_41_024_5_31875;
+ }
+}
+
static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe);
static int xc5000_is_firmware_loaded(struct dvb_frontend *fe);
static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val);
@@ -552,12 +578,14 @@ static int xc5000_fwupload(struct dvb_frontend *fe)
struct xc5000_priv *priv = fe->tuner_priv;
const struct firmware *fw;
int ret;
+ const struct xc5000_fw_cfg *desired_fw =
+ xc5000_assign_firmware(priv->chip_id);
/* request the firmware, this will block and timeout */
printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
- XC5000_DEFAULT_FIRMWARE);
+ desired_fw->name);
- ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE,
+ ret = request_firmware(&fw, desired_fw->name,
priv->i2c_props.adap->dev.parent);
if (ret) {
printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
@@ -569,7 +597,7 @@ static int xc5000_fwupload(struct dvb_frontend *fe)
ret = XC_RESULT_SUCCESS;
}
- if (fw->size != XC5000_DEFAULT_FIRMWARE_SIZE) {
+ if (fw->size != desired_fw->size) {
printk(KERN_ERR "xc5000: firmware incorrect size\n");
ret = XC_RESULT_RESET_FAILURE;
} else {
@@ -1139,6 +1167,13 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
if (priv->radio_input == 0)
priv->radio_input = cfg->radio_input;
+ /* don't override chip id if it's already been set
+ unless explicitly specified */
+ if ((priv->chip_id == 0) || (cfg->chip_id))
+ /* use default chip id if none specified, set to 0 so
+ it can be overridden if this is a hybrid driver */
+ priv->chip_id = (cfg->chip_id) ? cfg->chip_id : 0;
+
/* Check if firmware has been loaded. It is possible that another
instance of the driver has loaded the firmware.
*/
diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h
index e2957451b532..3396f8e02b40 100644
--- a/drivers/media/common/tuners/xc5000.h
+++ b/drivers/media/common/tuners/xc5000.h
@@ -27,10 +27,15 @@
struct dvb_frontend;
struct i2c_adapter;
+#define XC5000A 1
+#define XC5000C 2
+
struct xc5000_config {
u8 i2c_address;
u32 if_khz;
u8 radio_input;
+
+ int chip_id;
};
/* xc5000 callback command */
diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c
index ce4f85849e7b..d88c4aa7d24d 100644
--- a/drivers/media/dvb/ddbridge/ddbridge-core.c
+++ b/drivers/media/dvb/ddbridge/ddbridge-core.c
@@ -578,6 +578,7 @@ static int demod_attach_drxk(struct ddb_input *input)
struct drxk_config config;
memset(&config, 0, sizeof(config));
+ config.microcode_name = "drxk_a3.mc";
config.adr = 0x29 + (input->nr & 1);
fe = input->fe = dvb_attach(drxk_attach, &config, i2c);
diff --git a/drivers/media/dvb/ddbridge/ddbridge.h b/drivers/media/dvb/ddbridge/ddbridge.h
index 6d14893218f4..8b1b41d2a52d 100644
--- a/drivers/media/dvb/ddbridge/ddbridge.h
+++ b/drivers/media/dvb/ddbridge/ddbridge.h
@@ -32,8 +32,6 @@
#include
#include
#include
-#include
-#include
#include
#include "dmxdev.h"
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index fbbe545a74cb..4555baa383b2 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -655,6 +655,8 @@ restart:
dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__);
re_tune = true;
fepriv->state = FESTATE_TUNED;
+ } else {
+ re_tune = false;
}
if (fe->ops.tune)
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 9f203c6767a6..63bf45679f98 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -361,6 +361,14 @@ config DVB_USB_EC168
help
Say Y here to support the E3C EC168 DVB-T USB2.0 receiver.
+config DVB_USB_AZ6007
+ tristate "AzureWave 6007 and clones DVB-T/C USB2.0 support"
+ depends on DVB_USB
+ select DVB_DRXK if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2063 if !DVB_FE_CUSTOMISE
+ help
+ Say Y here to support theAfatech AF9005 based DVB-T/DVB-C receivers.
+
config DVB_USB_AZ6027
tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support"
depends on DVB_USB
@@ -378,6 +386,7 @@ config DVB_USB_LME2510
select DVB_IX2505V if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_PLL if !DVB_FE_CUSTOMISE
+ select DVB_M88RS2000 if !DVB_FE_CUSTOMISE
help
Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 .
@@ -403,3 +412,13 @@ config DVB_USB_MXL111SF
select VIDEO_TVEEPROM
help
Say Y here to support the MxL111SF USB2.0 DTV receiver.
+
+config DVB_USB_RTL28XXU
+ tristate "Realtek RTL28xxU DVB USB support"
+ depends on DVB_USB && EXPERIMENTAL
+ select DVB_RTL2830
+ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the Realtek RTL28xxU DVB USB receiver.
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 26c8b9e57050..b76acb5387e6 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -54,7 +54,6 @@ obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o
dvb-usb-opera-objs = opera1.o
obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o
-
dvb-usb-af9005-objs = af9005.o af9005-fe.o
obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o
@@ -88,6 +87,9 @@ obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o
dvb-usb-ec168-objs = ec168.o
obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o
+dvb-usb-az6007-objs = az6007.o
+obj-$(CONFIG_DVB_USB_AZ6007) += dvb-usb-az6007.o
+
dvb-usb-az6027-objs = az6027.o
obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o
@@ -105,8 +107,12 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
-# due to tuner-xc3028
-ccflags-y += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/ttpci
+dvb-usb-rtl28xxu-objs = rtl28xxu.o
+obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o
+
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends/
+# due to tuner-xc3028
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb/ttpci
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index 282a43d648df..7e70ea50ef26 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -1164,6 +1164,41 @@ static int af9015_af9013_sleep(struct dvb_frontend *fe)
return ret;
}
+/* override tuner callbacks for resource locking */
+static int af9015_tuner_init(struct dvb_frontend *fe)
+{
+ int ret;
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct af9015_state *priv = adap->dev->priv;
+
+ if (mutex_lock_interruptible(&adap->dev->usb_mutex))
+ return -EAGAIN;
+
+ ret = priv->tuner_init[adap->id](fe);
+
+ mutex_unlock(&adap->dev->usb_mutex);
+
+ return ret;
+}
+
+/* override tuner callbacks for resource locking */
+static int af9015_tuner_sleep(struct dvb_frontend *fe)
+{
+ int ret;
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct af9015_state *priv = adap->dev->priv;
+
+ if (mutex_lock_interruptible(&adap->dev->usb_mutex))
+ return -EAGAIN;
+
+ ret = priv->tuner_sleep[adap->id](fe);
+
+ mutex_unlock(&adap->dev->usb_mutex);
+
+ return ret;
+}
+
+
static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
{
int ret;
@@ -1283,6 +1318,7 @@ static struct mxl5007t_config af9015_mxl5007t_config = {
static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
{
int ret;
+ struct af9015_state *state = adap->dev->priv;
deb_info("%s:\n", __func__);
switch (af9015_af9013_config[adap->id].tuner) {
@@ -1340,6 +1376,19 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
err("Unknown tuner id:%d",
af9015_af9013_config[adap->id].tuner);
}
+
+ if (adap->fe_adap[0].fe->ops.tuner_ops.init) {
+ state->tuner_init[adap->id] =
+ adap->fe_adap[0].fe->ops.tuner_ops.init;
+ adap->fe_adap[0].fe->ops.tuner_ops.init = af9015_tuner_init;
+ }
+
+ if (adap->fe_adap[0].fe->ops.tuner_ops.sleep) {
+ state->tuner_sleep[adap->id] =
+ adap->fe_adap[0].fe->ops.tuner_ops.sleep;
+ adap->fe_adap[0].fe->ops.tuner_ops.sleep = af9015_tuner_sleep;
+ }
+
return ret;
}
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
index f619063fa72f..2f68419e899b 100644
--- a/drivers/media/dvb/dvb-usb/af9015.h
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -108,6 +108,8 @@ struct af9015_state {
int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status);
int (*init[2]) (struct dvb_frontend *fe);
int (*sleep[2]) (struct dvb_frontend *fe);
+ int (*tuner_init[2]) (struct dvb_frontend *fe);
+ int (*tuner_sleep[2]) (struct dvb_frontend *fe);
};
struct af9015_config {
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
index cf0c318d6989..03c28655af1b 100644
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -58,7 +58,7 @@ static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen,
u8 *rbuf, u8 rlen)
{
struct anysee_state *state = d->priv;
- int act_len, ret;
+ int act_len, ret, i;
u8 buf[64];
memcpy(&buf[0], sbuf, slen);
@@ -73,26 +73,52 @@ static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen,
/* We need receive one message more after dvb_usb_generic_rw due
to weird transaction flow, which is 1 x send + 2 x receive. */
ret = dvb_usb_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf), 0);
- if (!ret) {
+ if (ret)
+ goto error_unlock;
+
+ /* TODO FIXME: dvb_usb_generic_rw() fails rarely with error code -32
+ * (EPIPE, Broken pipe). Function supports currently msleep() as a
+ * parameter but I would not like to use it, since according to
+ * Documentation/timers/timers-howto.txt it should not be used such
+ * short, under < 20ms, sleeps. Repeating failed message would be
+ * better choice as not to add unwanted delays...
+ * Fixing that correctly is one of those or both;
+ * 1) use repeat if possible
+ * 2) add suitable delay
+ */
+
+ /* get answer, retry few times if error returned */
+ for (i = 0; i < 3; i++) {
/* receive 2nd answer */
ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev,
d->props.generic_bulk_ctrl_endpoint), buf, sizeof(buf),
&act_len, 2000);
- if (ret)
- err("%s: recv bulk message failed: %d", __func__, ret);
- else {
+
+ if (ret) {
+ deb_info("%s: recv bulk message failed: %d",
+ __func__, ret);
+ } else {
deb_xfer("<<< ");
debug_dump(buf, rlen, deb_xfer);
if (buf[63] != 0x4f)
deb_info("%s: cmd failed\n", __func__);
+
+ break;
}
}
+ if (ret) {
+ /* all retries failed, it is fatal */
+ err("%s: recv bulk message failed: %d", __func__, ret);
+ goto error_unlock;
+ }
+
/* read request, copy returned data to return buf */
- if (!ret && rbuf && rlen)
+ if (rbuf && rlen)
memcpy(rbuf, buf, rlen);
+error_unlock:
mutex_unlock(&anysee_usb_mutex);
return ret;
diff --git a/drivers/media/dvb/dvb-usb/az6007.c b/drivers/media/dvb/dvb-usb/az6007.c
new file mode 100644
index 000000000000..4008b9c50fbd
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/az6007.c
@@ -0,0 +1,957 @@
+/*
+ * Driver for AzureWave 6007 DVB-C/T USB2.0 and clones
+ *
+ * Copyright (c) Henry Wang
+ *
+ * This driver was made publicly available by Terratec, at:
+ * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
+ * The original driver's license is GPL, as declared with MODULE_LICENSE()
+ *
+ * Copyright (c) 2010-2011 Mauro Carvalho Chehab
+ * Driver modified by in order to work with upstream drxk driver, and
+ * tons of bugs got fixed.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "drxk.h"
+#include "mt2063.h"
+#include "dvb_ca_en50221.h"
+
+#define DVB_USB_LOG_PREFIX "az6007"
+#include "dvb-usb.h"
+
+/* debug */
+int dvb_usb_az6007_debug;
+module_param_named(debug, dvb_usb_az6007_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))."
+ DVB_USB_DEBUG_STATUS);
+
+#define deb_info(args...) dprintk(dvb_usb_az6007_debug, 0x01, args)
+#define deb_xfer(args...) dprintk(dvb_usb_az6007_debug, 0x02, args)
+#define deb_rc(args...) dprintk(dvb_usb_az6007_debug, 0x04, args)
+#define deb_fe(args...) dprintk(dvb_usb_az6007_debug, 0x08, args)
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* Known requests (Cypress FX2 firmware + az6007 "private" ones*/
+
+#define FX2_OED 0xb5
+#define AZ6007_READ_DATA 0xb7
+#define AZ6007_I2C_RD 0xb9
+#define AZ6007_POWER 0xbc
+#define AZ6007_I2C_WR 0xbd
+#define FX2_SCON1 0xc0
+#define AZ6007_TS_THROUGH 0xc7
+#define AZ6007_READ_IR 0xb4
+
+struct az6007_device_state {
+ struct mutex mutex;
+ struct mutex ca_mutex;
+ struct dvb_ca_en50221 ca;
+ unsigned warm:1;
+ int (*gate_ctrl) (struct dvb_frontend *, int);
+ unsigned char data[4096];
+};
+
+static struct drxk_config terratec_h7_drxk = {
+ .adr = 0x29,
+ .parallel_ts = true,
+ .dynamic_clk = true,
+ .single_master = true,
+ .enable_merr_cfg = true,
+ .no_i2c_bridge = false,
+ .chunk_size = 64,
+ .mpeg_out_clk_strength = 0x02,
+ .microcode_name = "dvb-usb-terratec-h7-drxk.fw",
+};
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct dvb_usb_adapter *adap = fe->sec_priv;
+ struct az6007_device_state *st;
+ int status = 0;
+
+ deb_info("%s: %s\n", __func__, enable ? "enable" : "disable");
+
+ if (!adap)
+ return -EINVAL;
+
+ st = adap->dev->priv;
+
+ if (!st)
+ return -EINVAL;
+
+ if (enable)
+ status = st->gate_ctrl(fe, 1);
+ else
+ status = st->gate_ctrl(fe, 0);
+
+ return status;
+}
+
+static struct mt2063_config az6007_mt2063_config = {
+ .tuner_address = 0x60,
+ .refclock = 36125000,
+};
+
+static int __az6007_read(struct usb_device *udev, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ int ret;
+
+ ret = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ req,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ value, index, b, blen, 5000);
+ if (ret < 0) {
+ warn("usb read operation failed. (%d)", ret);
+ return -EIO;
+ }
+
+ deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
+ index);
+ debug_dump(b, blen, deb_xfer);
+
+ return ret;
+}
+
+static int az6007_read(struct dvb_usb_device *d, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ struct az6007_device_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&st->mutex) < 0)
+ return -EAGAIN;
+
+ ret = __az6007_read(d->udev, req, value, index, b, blen);
+
+ mutex_unlock(&st->mutex);
+
+ return ret;
+}
+
+static int __az6007_write(struct usb_device *udev, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ int ret;
+
+ deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
+ index);
+ debug_dump(b, blen, deb_xfer);
+
+ if (blen > 64) {
+ err("az6007: tried to write %d bytes, but I2C max size is 64 bytes\n",
+ blen);
+ return -EOPNOTSUPP;
+ }
+
+ ret = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ req,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ value, index, b, blen, 5000);
+ if (ret != blen) {
+ err("usb write operation failed. (%d)", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int az6007_write(struct dvb_usb_device *d, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ struct az6007_device_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&st->mutex) < 0)
+ return -EAGAIN;
+
+ ret = __az6007_write(d->udev, req, value, index, b, blen);
+
+ mutex_unlock(&st->mutex);
+
+ return ret;
+}
+
+static int az6007_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dvb_usb_device *d = adap->dev;
+
+ deb_info("%s: %s", __func__, onoff ? "enable" : "disable");
+
+ return az6007_write(d, 0xbc, onoff, 0, NULL, 0);
+}
+
+/* remote control stuff (does not work with my box) */
+static int az6007_rc_query(struct dvb_usb_device *d)
+{
+ struct az6007_device_state *st = d->priv;
+ unsigned code = 0;
+
+ az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10);
+
+ if (st->data[1] == 0x44)
+ return 0;
+
+ if ((st->data[1] ^ st->data[2]) == 0xff)
+ code = st->data[1];
+ else
+ code = st->data[1] << 8 | st->data[2];
+
+ if ((st->data[3] ^ st->data[4]) == 0xff)
+ code = code << 8 | st->data[3];
+ else
+ code = code << 16 | st->data[3] << 8 | st->data[4];
+
+ rc_keydown(d->rc_dev, code, st->data[5]);
+
+ return 0;
+}
+
+static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot,
+ int address)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC1;
+ value = address;
+ index = 0;
+ blen = 1;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EINVAL;
+ } else {
+ ret = b[0];
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ kfree(b);
+ return ret;
+}
+
+static int az6007_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot,
+ int address,
+ u8 value)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value1;
+ u16 index;
+ int blen;
+
+ deb_info("%s %d", __func__, slot);
+ if (slot != 0)
+ return -EINVAL;
+
+ mutex_lock(&state->ca_mutex);
+ req = 0xC2;
+ value1 = address;
+ index = value;
+ blen = 0;
+
+ ret = az6007_write(d, req, value1, index, NULL, blen);
+ if (ret != 0)
+ warn("usb out operation failed. (%d)", ret);
+
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6007_ci_read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC3;
+ value = address;
+ index = 0;
+ blen = 2;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EINVAL;
+ } else {
+ if (b[0] == 0)
+ warn("Read CI IO error");
+
+ ret = b[1];
+ deb_info("read cam data = %x from 0x%x", b[1], value);
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ kfree(b);
+ return ret;
+}
+
+static int az6007_ci_write_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address,
+ u8 value)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value1;
+ u16 index;
+ int blen;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ mutex_lock(&state->ca_mutex);
+ req = 0xC4;
+ value1 = address;
+ index = value;
+ blen = 0;
+
+ ret = az6007_write(d, req, value1, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ req = 0xC8;
+ value = 0;
+ index = 0;
+ blen = 1;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EIO;
+ } else{
+ ret = b[0];
+ }
+ kfree(b);
+ return ret;
+}
+
+static int az6007_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret, i;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC6;
+ value = 1;
+ index = 0;
+ blen = 0;
+
+ ret = az6007_write(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+ msleep(500);
+ req = 0xC6;
+ value = 0;
+ index = 0;
+ blen = 0;
+
+ ret = az6007_write(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+ for (i = 0; i < 15; i++) {
+ msleep(100);
+
+ if (CI_CamReady(ca, slot)) {
+ deb_info("CAM Ready");
+ break;
+ }
+ }
+ msleep(5000);
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6007_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ return 0;
+}
+
+static int az6007_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ deb_info("%s", __func__);
+ mutex_lock(&state->ca_mutex);
+ req = 0xC7;
+ value = 1;
+ index = 0;
+ blen = 0;
+
+ ret = az6007_write(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC5;
+ value = 0;
+ index = 0;
+ blen = 1;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EIO;
+ } else
+ ret = 0;
+
+ if (!ret && b[0] == 1) {
+ ret = DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ kfree(b);
+ return ret;
+}
+
+
+static void az6007_ci_uninit(struct dvb_usb_device *d)
+{
+ struct az6007_device_state *state;
+
+ deb_info("%s", __func__);
+
+ if (NULL == d)
+ return;
+
+ state = (struct az6007_device_state *)d->priv;
+ if (NULL == state)
+ return;
+
+ if (NULL == state->ca.data)
+ return;
+
+ dvb_ca_en50221_release(&state->ca);
+
+ memset(&state->ca, 0, sizeof(state->ca));
+}
+
+
+static int az6007_ci_init(struct dvb_usb_adapter *a)
+{
+ struct dvb_usb_device *d = a->dev;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+ int ret;
+
+ deb_info("%s", __func__);
+
+ mutex_init(&state->ca_mutex);
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = az6007_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = az6007_ci_write_attribute_mem;
+ state->ca.read_cam_control = az6007_ci_read_cam_control;
+ state->ca.write_cam_control = az6007_ci_write_cam_control;
+ state->ca.slot_reset = az6007_ci_slot_reset;
+ state->ca.slot_shutdown = az6007_ci_slot_shutdown;
+ state->ca.slot_ts_enable = az6007_ci_slot_ts_enable;
+ state->ca.poll_slot_status = az6007_ci_poll_slot_status;
+ state->ca.data = d;
+
+ ret = dvb_ca_en50221_init(&a->dvb_adap,
+ &state->ca,
+ 0, /* flags */
+ 1);/* n_slots */
+ if (ret != 0) {
+ err("Cannot initialize CI: Error %d.", ret);
+ memset(&state->ca, 0, sizeof(state->ca));
+ return ret;
+ }
+
+ deb_info("CI initialized.");
+
+ return 0;
+}
+
+static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
+{
+ struct az6007_device_state *st = d->priv;
+ int ret;
+
+ ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6);
+ memcpy(mac, st->data, sizeof(mac));
+
+ if (ret > 0)
+ deb_info("%s: mac is %02x:%02x:%02x:%02x:%02x:%02x\n",
+ __func__, mac[0], mac[1], mac[2],
+ mac[3], mac[4], mac[5]);
+
+ return ret;
+}
+
+static int az6007_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct az6007_device_state *st = adap->dev->priv;
+
+ deb_info("attaching demod drxk");
+
+ adap->fe_adap[0].fe = dvb_attach(drxk_attach, &terratec_h7_drxk,
+ &adap->dev->i2c_adap);
+ if (!adap->fe_adap[0].fe)
+ return -EINVAL;
+
+ adap->fe_adap[0].fe->sec_priv = adap;
+ st->gate_ctrl = adap->fe_adap[0].fe->ops.i2c_gate_ctrl;
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+
+ az6007_ci_init(adap);
+
+ return 0;
+}
+
+static int az6007_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ deb_info("attaching tuner mt2063");
+
+ /* Attach mt2063 to DVB-C frontend */
+ if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 1);
+ if (!dvb_attach(mt2063_attach, adap->fe_adap[0].fe,
+ &az6007_mt2063_config,
+ &adap->dev->i2c_adap))
+ return -EINVAL;
+
+ if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 0);
+
+ return 0;
+}
+
+int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ struct az6007_device_state *st = d->priv;
+ int ret;
+
+ deb_info("%s()\n", __func__);
+
+ if (!st->warm) {
+ mutex_init(&st->mutex);
+
+ ret = az6007_write(d, AZ6007_POWER, 0, 2, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(60);
+ ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(100);
+ ret = az6007_write(d, AZ6007_POWER, 1, 3, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(20);
+ ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ msleep(400);
+ ret = az6007_write(d, FX2_SCON1, 0, 3, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(150);
+ ret = az6007_write(d, FX2_SCON1, 1, 3, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(430);
+ ret = az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ st->warm = true;
+
+ return 0;
+ }
+
+ if (!onoff)
+ return 0;
+
+ az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
+ az6007_write(d, AZ6007_TS_THROUGH, 0, 0, NULL, 0);
+
+ return 0;
+}
+
+/* I2C */
+static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct az6007_device_state *st = d->priv;
+ int i, j, len;
+ int ret = 0;
+ u16 index;
+ u16 value;
+ int length;
+ u8 req, addr;
+
+ if (mutex_lock_interruptible(&st->mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ addr = msgs[i].addr << 1;
+ if (((i + 1) < num)
+ && (msgs[i].len == 1)
+ && (!msgs[i].flags & I2C_M_RD)
+ && (msgs[i + 1].flags & I2C_M_RD)
+ && (msgs[i].addr == msgs[i + 1].addr)) {
+ /*
+ * A write + read xfer for the same address, where
+ * the first xfer has just 1 byte length.
+ * Need to join both into one operation
+ */
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_DEBUG
+ "az6007 I2C xfer write+read addr=0x%x len=%d/%d: ",
+ addr, msgs[i].len, msgs[i + 1].len);
+ req = AZ6007_I2C_RD;
+ index = msgs[i].buf[0];
+ value = addr | (1 << 8);
+ length = 6 + msgs[i + 1].len;
+ len = msgs[i + 1].len;
+ ret = __az6007_read(d->udev, req, value, index,
+ st->data, length);
+ if (ret >= len) {
+ for (j = 0; j < len; j++) {
+ msgs[i + 1].buf[j] = st->data[j + 5];
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_CONT
+ "0x%02x ",
+ msgs[i + 1].buf[j]);
+ }
+ } else
+ ret = -EIO;
+ i++;
+ } else if (!(msgs[i].flags & I2C_M_RD)) {
+ /* write bytes */
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_DEBUG
+ "az6007 I2C xfer write addr=0x%x len=%d: ",
+ addr, msgs[i].len);
+ req = AZ6007_I2C_WR;
+ index = msgs[i].buf[0];
+ value = addr | (1 << 8);
+ length = msgs[i].len - 1;
+ len = msgs[i].len - 1;
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_CONT "(0x%02x) ", msgs[i].buf[0]);
+ for (j = 0; j < len; j++) {
+ st->data[j] = msgs[i].buf[j + 1];
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_CONT "0x%02x ",
+ st->data[j]);
+ }
+ ret = __az6007_write(d->udev, req, value, index,
+ st->data, length);
+ } else {
+ /* read bytes */
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_DEBUG
+ "az6007 I2C xfer read addr=0x%x len=%d: ",
+ addr, msgs[i].len);
+ req = AZ6007_I2C_RD;
+ index = msgs[i].buf[0];
+ value = addr;
+ length = msgs[i].len + 6;
+ len = msgs[i].len;
+ ret = __az6007_read(d->udev, req, value, index,
+ st->data, length);
+ for (j = 0; j < len; j++) {
+ msgs[i].buf[j] = st->data[j + 5];
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_CONT
+ "0x%02x ", st->data[j + 5]);
+ }
+ }
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_CONT "\n");
+ if (ret < 0)
+ goto err;
+ }
+err:
+ mutex_unlock(&st->mutex);
+
+ if (ret < 0) {
+ info("%s ERROR: %i", __func__, ret);
+ return ret;
+ }
+ return num;
+}
+
+static u32 az6007_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm az6007_i2c_algo = {
+ .master_xfer = az6007_i2c_xfer,
+ .functionality = az6007_i2c_func,
+};
+
+int az6007_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc, int *cold)
+{
+ int ret;
+ u8 *mac;
+
+ mac = kmalloc(6, GFP_ATOMIC);
+ if (!mac)
+ return -ENOMEM;
+
+ /* Try to read the mac address */
+ ret = __az6007_read(udev, AZ6007_READ_DATA, 6, 0, mac, 6);
+ if (ret == 6)
+ *cold = 0;
+ else
+ *cold = 1;
+
+ kfree(mac);
+
+ if (*cold) {
+ __az6007_write(udev, 0x09, 1, 0, NULL, 0);
+ __az6007_write(udev, 0x00, 0, 0, NULL, 0);
+ __az6007_write(udev, 0x00, 0, 0, NULL, 0);
+ }
+
+ deb_info("Device is on %s state\n", *cold ? "warm" : "cold");
+ return 0;
+}
+
+static struct dvb_usb_device_properties az6007_properties;
+
+static void az6007_usb_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ az6007_ci_uninit(d);
+ dvb_usb_device_exit(intf);
+}
+
+static int az6007_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return dvb_usb_device_init(intf, &az6007_properties,
+ THIS_MODULE, NULL, adapter_nr);
+}
+
+static struct usb_device_id az6007_usb_table[] = {
+ {USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007)},
+ {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7)},
+ {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7_2)},
+ {0},
+};
+
+MODULE_DEVICE_TABLE(usb, az6007_usb_table);
+
+static struct dvb_usb_device_properties az6007_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+ .usb_ctrl = CYPRESS_FX2,
+ .firmware = "dvb-usb-terratec-h7-az6007.fw",
+ .no_reconnect = 1,
+ .size_of_priv = sizeof(struct az6007_device_state),
+ .identify_state = az6007_identify_state,
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {{
+ .streaming_ctrl = az6007_streaming_ctrl,
+ .tuner_attach = az6007_tuner_attach,
+ .frontend_attach = az6007_frontend_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 10,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
+ } }
+ } },
+ .power_ctrl = az6007_power_ctrl,
+ .read_mac_address = az6007_read_mac_addr,
+
+ .rc.core = {
+ .rc_interval = 400,
+ .rc_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
+ .module_name = "az6007",
+ .rc_query = az6007_rc_query,
+ .allowed_protos = RC_TYPE_NEC,
+ },
+ .i2c_algo = &az6007_i2c_algo,
+
+ .num_device_descs = 2,
+ .devices = {
+ { .name = "AzureWave DTV StarBox DVB-T/C USB2.0 (az6007)",
+ .cold_ids = { &az6007_usb_table[0], NULL },
+ .warm_ids = { NULL },
+ },
+ { .name = "TerraTec DTV StarBox DVB-T/C USB2.0 (az6007)",
+ .cold_ids = { &az6007_usb_table[1], &az6007_usb_table[2], NULL },
+ .warm_ids = { NULL },
+ },
+ { NULL },
+ }
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver az6007_usb_driver = {
+ .name = "dvb_usb_az6007",
+ .probe = az6007_usb_probe,
+ .disconnect = az6007_usb_disconnect,
+ .id_table = az6007_usb_table,
+};
+
+/* module stuff */
+static int __init az6007_usb_module_init(void)
+{
+ int result;
+ deb_info("az6007 usb module init\n");
+
+ result = usb_register(&az6007_usb_driver);
+ if (result) {
+ err("usb_register failed. (%d)", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit az6007_usb_module_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ deb_info("az6007 usb module exit\n");
+ usb_deregister(&az6007_usb_driver);
+}
+
+module_init(az6007_usb_module_init);
+module_exit(az6007_usb_module_exit);
+
+MODULE_AUTHOR("Henry Wang ");
+MODULE_AUTHOR("Mauro Carvalho Chehab ");
+MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
+MODULE_VERSION("1.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index 070e82aa53f5..02290c60f72f 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -677,11 +677,9 @@ static void dib0700_rc_urb_completion(struct urb *purb)
u8 toggle;
deb_info("%s()\n", __func__);
- if (d == NULL)
- return;
-
if (d->rc_dev == NULL) {
/* This will occur if disable_rc_polling=1 */
+ kfree(purb->transfer_buffer);
usb_free_urb(purb);
return;
}
@@ -690,6 +688,7 @@ static void dib0700_rc_urb_completion(struct urb *purb)
if (purb->status < 0) {
deb_info("discontinuing polling\n");
+ kfree(purb->transfer_buffer);
usb_free_urb(purb);
return;
}
@@ -784,8 +783,11 @@ int dib0700_rc_setup(struct dvb_usb_device *d)
dib0700_rc_urb_completion, d);
ret = usb_submit_urb(purb, GFP_ATOMIC);
- if (ret)
+ if (ret) {
err("rc submit urb failed\n");
+ kfree(purb->transfer_buffer);
+ usb_free_urb(purb);
+ }
return ret;
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index d390ddaa5a53..397d8f232731 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -51,6 +51,7 @@
#define USB_VID_PINNACLE 0x2304
#define USB_VID_PCTV 0x2013
#define USB_VID_PIXELVIEW 0x1554
+#define USB_VID_REALTEK 0x0bda
#define USB_VID_TECHNOTREND 0x0b48
#define USB_VID_TERRATEC 0x0ccd
#define USB_VID_TELESTAR 0x10b9
@@ -80,6 +81,7 @@
#define USB_PID_ANSONIC_DVBT_USB 0x6000
#define USB_PID_ANYSEE 0x861f
#define USB_PID_AZUREWAVE_AD_TU700 0x3237
+#define USB_PID_AZUREWAVE_6007 0x0ccd
#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
@@ -125,6 +127,8 @@
#define USB_PID_E3C_EC168_3 0xfffb
#define USB_PID_E3C_EC168_4 0x1001
#define USB_PID_E3C_EC168_5 0x1002
+#define USB_PID_FREECOM_DVBT 0x0160
+#define USB_PID_FREECOM_DVBT_2 0x0161
#define USB_PID_UNIWILL_STK7700P 0x6003
#define USB_PID_GENIUS_TVGO_DVB_T03 0x4012
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
@@ -226,6 +230,8 @@
#define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062
#define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078
#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab
+#define USB_PID_TERRATEC_H7 0x10b4
+#define USB_PID_TERRATEC_H7_2 0x10a3
#define USB_PID_TERRATEC_T3 0x10a0
#define USB_PID_TERRATEC_T5 0x10a1
#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e
@@ -249,6 +255,8 @@
#define USB_PID_PCTV_400E 0x020f
#define USB_PID_PCTV_450E 0x0222
#define USB_PID_PCTV_452E 0x021f
+#define USB_PID_REALTEK_RTL2831U 0x2831
+#define USB_PID_REALTEK_RTL2832U 0x2832
#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007
#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a
#define USB_PID_NEBULA_DIGITV 0x0201
diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c
index 9f01cd7a6e3f..3b7b102f20ae 100644
--- a/drivers/media/dvb/dvb-usb/it913x.c
+++ b/drivers/media/dvb/dvb-usb/it913x.c
@@ -64,6 +64,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
struct it913x_state {
u8 id;
struct ite_config it913x_config;
+ u8 pid_filter_onoff;
};
struct ite_config it913x_config;
@@ -259,15 +260,16 @@ static u32 it913x_query(struct usb_device *udev, u8 pro)
static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
+ struct it913x_state *st = adap->dev->priv;
struct usb_device *udev = adap->dev->udev;
int ret;
u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&adap->dev->i2c_mutex);
+
deb_info(1, "PID_C (%02x)", onoff);
- ret = it913x_wr_reg(udev, pro, PID_EN, onoff);
+ ret = it913x_wr_reg(udev, pro, PID_EN, st->pid_filter_onoff);
mutex_unlock(&adap->dev->i2c_mutex);
return ret;
@@ -276,12 +278,13 @@ static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
static int it913x_pid_filter(struct dvb_usb_adapter *adap,
int index, u16 pid, int onoff)
{
+ struct it913x_state *st = adap->dev->priv;
struct usb_device *udev = adap->dev->udev;
int ret;
u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&adap->dev->i2c_mutex);
+
deb_info(1, "PID_F (%02x)", onoff);
ret = it913x_wr_reg(udev, pro, PID_LSB, (u8)(pid & 0xff));
@@ -292,6 +295,13 @@ static int it913x_pid_filter(struct dvb_usb_adapter *adap,
ret |= it913x_wr_reg(udev, pro, PID_INX, (u8)(index & 0x1f));
+ if (udev->speed == USB_SPEED_HIGH && pid == 0x2000) {
+ ret |= it913x_wr_reg(udev, pro, PID_EN, !onoff);
+ st->pid_filter_onoff = !onoff;
+ } else
+ st->pid_filter_onoff =
+ adap->fe_adap[adap->active_fe].pid_filtering;
+
mutex_unlock(&adap->dev->i2c_mutex);
return 0;
}
@@ -316,8 +326,8 @@ static int it913x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int ret;
u32 reg;
u8 pro;
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
+
+ mutex_lock(&d->i2c_mutex);
debug_data_snipet(1, "Message out", msg[0].buf);
deb_info(2, "num of messages %d address %02x", num, msg[0].addr);
@@ -358,8 +368,7 @@ static int it913x_rc_query(struct dvb_usb_device *d)
int ret;
u32 key;
/* Avoid conflict with frontends*/
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&d->i2c_mutex);
ret = it913x_io(d->udev, READ_LONG, PRO_LINK, CMD_IR_GET,
0, 0, &ibuf[0], sizeof(ibuf));
@@ -388,19 +397,12 @@ static int ite_firmware_select(struct usb_device *udev,
{
int sw;
/* auto switch */
- if (le16_to_cpu(udev->descriptor.idProduct) ==
- USB_PID_ITETECH_IT9135)
- sw = IT9135_V1_FW;
- else if (le16_to_cpu(udev->descriptor.idProduct) ==
- USB_PID_ITETECH_IT9135_9005)
- sw = IT9135_V1_FW;
- else if (le16_to_cpu(udev->descriptor.idProduct) ==
- USB_PID_ITETECH_IT9135_9006) {
- sw = IT9135_V2_FW;
- if (it913x_config.tuner_id_0 == 0)
- it913x_config.tuner_id_0 = IT9135_60;
- } else
+ if (le16_to_cpu(udev->descriptor.idVendor) == USB_VID_KWORLD_2)
sw = IT9137_FW;
+ else if (it913x_config.chip_ver == 1)
+ sw = IT9135_V1_FW;
+ else
+ sw = IT9135_V2_FW;
/* force switch */
if (dvb_usb_it913x_firmware != IT9135_AUTO)
@@ -410,41 +412,103 @@ static int ite_firmware_select(struct usb_device *udev,
case IT9135_V1_FW:
it913x_config.firmware_ver = 1;
it913x_config.adc_x2 = 1;
+ it913x_config.read_slevel = false;
props->firmware = fw_it9135_v1;
break;
case IT9135_V2_FW:
it913x_config.firmware_ver = 1;
it913x_config.adc_x2 = 1;
+ it913x_config.read_slevel = false;
props->firmware = fw_it9135_v2;
+ switch (it913x_config.tuner_id_0) {
+ case IT9135_61:
+ case IT9135_62:
+ break;
+ default:
+ info("Unknown tuner ID applying default 0x60");
+ case IT9135_60:
+ it913x_config.tuner_id_0 = IT9135_60;
+ }
break;
case IT9137_FW:
default:
it913x_config.firmware_ver = 0;
it913x_config.adc_x2 = 0;
+ it913x_config.read_slevel = true;
props->firmware = fw_it9137;
}
return 0;
}
+static void it913x_select_remote(struct usb_device *udev,
+ struct dvb_usb_device_properties *props)
+{
+ switch (le16_to_cpu(udev->descriptor.idProduct)) {
+ case USB_PID_ITETECH_IT9135_9005:
+ props->rc.core.rc_codes = RC_MAP_IT913X_V2;
+ return;
+ default:
+ props->rc.core.rc_codes = RC_MAP_IT913X_V1;
+ }
+ return;
+}
+
#define TS_MPEG_PKT_SIZE 188
#define EP_LOW 21
#define TS_BUFFER_SIZE_PID (EP_LOW*TS_MPEG_PKT_SIZE)
#define EP_HIGH 348
#define TS_BUFFER_SIZE_MAX (EP_HIGH*TS_MPEG_PKT_SIZE)
-static int it913x_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc,
- int *cold)
+static int it913x_select_config(struct usb_device *udev,
+ struct dvb_usb_device_properties *props)
{
- int ret = 0, firm_no;
- u8 reg, remote;
+ int ret = 0, reg;
+ bool proprietary_ir = false;
- firm_no = it913x_return_status(udev);
+ if (it913x_config.chip_ver == 0x02
+ && it913x_config.chip_type == 0x9135)
+ reg = it913x_read_reg(udev, 0x461d);
+ else
+ reg = it913x_read_reg(udev, 0x461b);
- /* checnk for dual mode */
- it913x_config.dual_mode = it913x_read_reg(udev, 0x49c5);
+ if (reg < 0)
+ return reg;
+
+ if (reg == 0) {
+ it913x_config.dual_mode = 0;
+ it913x_config.tuner_id_0 = IT9135_38;
+ proprietary_ir = true;
+ } else {
+ /* TS mode */
+ reg = it913x_read_reg(udev, 0x49c5);
+ if (reg < 0)
+ return reg;
+ it913x_config.dual_mode = reg;
+
+ /* IR mode type */
+ reg = it913x_read_reg(udev, 0x49ac);
+ if (reg < 0)
+ return reg;
+ if (reg == 5) {
+ info("Remote propriety (raw) mode");
+ proprietary_ir = true;
+ } else if (reg == 1) {
+ info("Remote HID mode NOT SUPPORTED");
+ proprietary_ir = false;
+ props->rc.core.rc_codes = NULL;
+ } else
+ props->rc.core.rc_codes = NULL;
+
+ /* Tuner_id */
+ reg = it913x_read_reg(udev, 0x49d0);
+ if (reg < 0)
+ return reg;
+ it913x_config.tuner_id_0 = reg;
+ }
+
+ if (proprietary_ir)
+ it913x_select_remote(udev, props);
if (udev->speed != USB_SPEED_HIGH) {
props->adapter[0].fe[0].pid_filter_count = 5;
@@ -459,17 +523,6 @@ static int it913x_identify_state(struct usb_device *udev,
if(props->adapter[0].fe[0].pid_filter_count == 5)
props->adapter[0].fe[0].pid_filter_count = 31;
- /* TODO different remotes */
- remote = it913x_read_reg(udev, 0x49ac); /* Remote */
- if (remote == 0)
- props->rc.core.rc_codes = NULL;
-
- /* TODO at the moment tuner_id is always assigned to 0x38 */
- it913x_config.tuner_id_0 = it913x_read_reg(udev, 0x49d0);
-
- info("Dual mode=%x Remote=%x Tuner Type=%x", it913x_config.dual_mode
- , remote, it913x_config.tuner_id_0);
-
/* Select Stream Buffer Size and pid filter option*/
if (pid_filter) {
props->adapter[0].fe[0].stream.u.bulk.buffersize =
@@ -490,8 +543,29 @@ static int it913x_identify_state(struct usb_device *udev,
} else
props->num_adapters = 1;
+ info("Dual mode=%x Tuner Type=%x", it913x_config.dual_mode,
+ it913x_config.tuner_id_0);
+
ret = ite_firmware_select(udev, props);
+ return ret;
+}
+
+static int it913x_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ int ret = 0, firm_no;
+ u8 reg;
+
+ firm_no = it913x_return_status(udev);
+
+ /* Read and select config */
+ ret = it913x_select_config(udev, props);
+ if (ret < 0)
+ return ret;
+
if (firm_no > 0) {
*cold = 0;
return 0;
@@ -538,18 +612,22 @@ static int it913x_identify_state(struct usb_device *udev,
static int it913x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
+ struct it913x_state *st = adap->dev->priv;
int ret = 0;
u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
- return -EAGAIN;
deb_info(1, "STM (%02x)", onoff);
- if (!onoff)
+ if (!onoff) {
+ mutex_lock(&adap->dev->i2c_mutex);
+
ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1);
+ mutex_unlock(&adap->dev->i2c_mutex);
+ st->pid_filter_onoff =
+ adap->fe_adap[adap->active_fe].pid_filtering;
- mutex_unlock(&adap->dev->i2c_mutex);
+ }
return ret;
}
@@ -789,7 +867,7 @@ static struct dvb_usb_device_properties it913x_properties = {
.rc_query = it913x_rc_query,
.rc_interval = IT913X_POLL,
.allowed_protos = RC_TYPE_NEC,
- .rc_codes = RC_MAP_MSI_DIGIVOX_III,
+ .rc_codes = RC_MAP_IT913X_V1,
},
.i2c_algo = &it913x_i2c_algo,
.num_device_descs = 5,
@@ -823,5 +901,5 @@ module_usb_driver(it913x_driver);
MODULE_AUTHOR("Malcolm Priestley ");
MODULE_DESCRIPTION("it913x USB 2 Driver");
-MODULE_VERSION("1.22");
+MODULE_VERSION("1.27");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c
index 291f6b110399..5dde06d066ff 100644
--- a/drivers/media/dvb/dvb-usb/lmedm04.c
+++ b/drivers/media/dvb/dvb-usb/lmedm04.c
@@ -77,6 +77,7 @@
#include "stv0299.h"
#include "dvb-pll.h"
#include "z0194a.h"
+#include "m88rs2000.h"
@@ -104,7 +105,7 @@ MODULE_PARM_DESC(firmware, "set default firmware 0=Sharp7395 1=LG");
static int pid_filter;
module_param_named(pid, pid_filter, int, 0644);
-MODULE_PARM_DESC(pid, "set default 0=on 1=off");
+MODULE_PARM_DESC(pid, "set default 0=default 1=off 2=on");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
@@ -113,6 +114,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define TUNER_LG 0x1
#define TUNER_S7395 0x2
#define TUNER_S0194 0x3
+#define TUNER_RS2000 0x4
struct lme2510_state {
u8 id;
@@ -121,6 +123,8 @@ struct lme2510_state {
u8 signal_level;
u8 signal_sn;
u8 time_key;
+ u8 last_key;
+ u8 key_timeout;
u8 i2c_talk_onoff;
u8 i2c_gate;
u8 i2c_tuner_gate_w;
@@ -128,6 +132,7 @@ struct lme2510_state {
u8 i2c_tuner_addr;
u8 stream_on;
u8 pid_size;
+ u8 pid_off;
void *buffer;
struct urb *lme_urb;
void *usb_buffer;
@@ -178,14 +183,8 @@ static int lme2510_usb_talk(struct dvb_usb_device *d,
/* the read/write capped at 64 */
memcpy(buff, wbuf, (wlen < 64) ? wlen : 64);
- ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01));
-
ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x01);
- msleep(10);
-
- ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x01));
-
ret |= lme2510_bulk_read(d->udev, buff, (rlen < 64) ?
rlen : 64 , 0x01);
@@ -199,9 +198,14 @@ static int lme2510_usb_talk(struct dvb_usb_device *d,
static int lme2510_stream_restart(struct dvb_usb_device *d)
{
- static u8 stream_on[] = LME_ST_ON_W;
+ struct lme2510_state *st = d->priv;
+ u8 all_pids[] = LME_ALL_PIDS;
+ u8 stream_on[] = LME_ST_ON_W;
int ret;
- u8 rbuff[10];
+ u8 rbuff[1];
+ if (st->pid_off)
+ ret = lme2510_usb_talk(d, all_pids, sizeof(all_pids),
+ rbuff, sizeof(rbuff));
/*Restart Stream Command*/
ret = lme2510_usb_talk(d, stream_on, sizeof(stream_on),
rbuff, sizeof(rbuff));
@@ -308,6 +312,14 @@ static void lme2510_int_response(struct urb *lme_urb)
((ibuf[2] & 0x01) << 0x03);
}
break;
+ case TUNER_RS2000:
+ if (ibuf[2] > 0)
+ st->signal_lock = 0xff;
+ else
+ st->signal_lock = 0xf0;
+ st->signal_level = ibuf[4];
+ st->signal_sn = ibuf[5];
+ st->time_key = ibuf[7];
default:
break;
}
@@ -359,19 +371,20 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap)
static int lme2510_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
struct lme2510_state *st = adap->dev->priv;
- static u8 clear_pid_reg[] = LME_CLEAR_PID;
+ static u8 clear_pid_reg[] = LME_ALL_PIDS;
static u8 rbuf[1];
int ret;
deb_info(1, "PID Clearing Filter");
- ret = mutex_lock_interruptible(&adap->dev->i2c_mutex);
- if (ret < 0)
- return -EAGAIN;
+ mutex_lock(&adap->dev->i2c_mutex);
- if (!onoff)
+ if (!onoff) {
ret |= lme2510_usb_talk(adap->dev, clear_pid_reg,
sizeof(clear_pid_reg), rbuf, sizeof(rbuf));
+ st->pid_off = true;
+ } else
+ st->pid_off = false;
st->pid_size = 0;
@@ -389,11 +402,9 @@ static int lme2510_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
pid, index, onoff);
if (onoff) {
- ret = mutex_lock_interruptible(&adap->dev->i2c_mutex);
- if (ret < 0)
- return -EAGAIN;
- ret |= lme2510_enable_pid(adap->dev, index, pid);
- mutex_unlock(&adap->dev->i2c_mutex);
+ mutex_lock(&adap->dev->i2c_mutex);
+ ret |= lme2510_enable_pid(adap->dev, index, pid);
+ mutex_unlock(&adap->dev->i2c_mutex);
}
@@ -425,9 +436,6 @@ static int lme2510_msg(struct dvb_usb_device *d,
int ret = 0;
struct lme2510_state *st = d->priv;
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
if (st->i2c_talk_onoff == 1) {
ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
@@ -456,8 +464,6 @@ static int lme2510_msg(struct dvb_usb_device *d,
st->i2c_talk_onoff = 0;
}
}
- if ((wbuf[3] != 0x6) & (wbuf[3] != 0x5))
- msleep(5);
}
break;
case TUNER_S0194:
@@ -472,10 +478,12 @@ static int lme2510_msg(struct dvb_usb_device *d,
}
}
break;
+ case TUNER_RS2000:
default:
break;
}
} else {
+ /* TODO rewrite this section */
switch (st->tuner_config) {
case TUNER_LG:
switch (wbuf[3]) {
@@ -559,6 +567,24 @@ static int lme2510_msg(struct dvb_usb_device *d,
break;
}
break;
+ case TUNER_RS2000:
+ switch (wbuf[3]) {
+ case 0x8c:
+ rbuf[0] = 0x55;
+ rbuf[1] = 0xff;
+ if (st->last_key == st->time_key) {
+ st->key_timeout++;
+ if (st->key_timeout > 5)
+ rbuf[1] = 0;
+ } else
+ st->key_timeout = 0;
+ st->last_key = st->time_key;
+ break;
+ default:
+ lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
+ st->i2c_talk_onoff = 1;
+ break;
+ }
default:
break;
}
@@ -568,8 +594,6 @@ static int lme2510_msg(struct dvb_usb_device *d,
}
- mutex_unlock(&d->i2c_mutex);
-
return ret;
}
@@ -584,6 +608,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
u16 len;
u8 gate = st->i2c_gate;
+ mutex_lock(&d->i2c_mutex);
+
if (gate == 0)
gate = 5;
@@ -622,6 +648,7 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
if (lme2510_msg(d, obuf, len, ibuf, 64) < 0) {
deb_info(1, "i2c transfer failed.");
+ mutex_unlock(&d->i2c_mutex);
return -EAGAIN;
}
@@ -634,6 +661,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
}
}
}
+
+ mutex_unlock(&d->i2c_mutex);
return i;
}
@@ -653,7 +682,7 @@ static int lme2510_identify_state(struct usb_device *udev,
struct dvb_usb_device_description **desc,
int *cold)
{
- if (pid_filter > 0)
+ if (pid_filter != 2)
props->adapter[0].fe[0].caps &=
~DVB_USB_ADAP_NEED_PID_FILTERING;
*cold = 0;
@@ -663,7 +692,7 @@ static int lme2510_identify_state(struct usb_device *udev,
static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
struct lme2510_state *st = adap->dev->priv;
- static u8 clear_reg_3[] = LME_CLEAR_PID;
+ static u8 clear_reg_3[] = LME_ALL_PIDS;
static u8 rbuf[1];
int ret = 0, rlen = sizeof(rbuf);
@@ -675,8 +704,7 @@ static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
else {
deb_info(1, "STM Steam Off");
/* mutex is here only to avoid collision with I2C */
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&adap->dev->i2c_mutex);
ret = lme2510_usb_talk(adap->dev, clear_reg_3,
sizeof(clear_reg_3), rbuf, rlen);
@@ -781,16 +809,18 @@ static int lme_firmware_switch(struct usb_device *udev, int cold)
const char fw_c_s7395[] = "dvb-usb-lme2510c-s7395.fw";
const char fw_c_lg[] = "dvb-usb-lme2510c-lg.fw";
const char fw_c_s0194[] = "dvb-usb-lme2510c-s0194.fw";
+ const char fw_c_rs2000[] = "dvb-usb-lme2510c-rs2000.fw";
const char fw_lg[] = "dvb-usb-lme2510-lg.fw";
const char fw_s0194[] = "dvb-usb-lme2510-s0194.fw";
const char *fw_lme;
- int ret, cold_fw;
+ int ret = 0, cold_fw;
cold = (cold > 0) ? (cold & 1) : 0;
cold_fw = !cold;
- if (le16_to_cpu(udev->descriptor.idProduct) == 0x1122) {
+ switch (le16_to_cpu(udev->descriptor.idProduct)) {
+ case 0x1122:
switch (dvb_usb_lme2510_firmware) {
default:
dvb_usb_lme2510_firmware = TUNER_S0194;
@@ -813,7 +843,8 @@ static int lme_firmware_switch(struct usb_device *udev, int cold)
cold_fw = 0;
break;
}
- } else {
+ break;
+ case 0x1120:
switch (dvb_usb_lme2510_firmware) {
default:
dvb_usb_lme2510_firmware = TUNER_S7395;
@@ -842,8 +873,17 @@ static int lme_firmware_switch(struct usb_device *udev, int cold)
cold_fw = 0;
break;
}
+ break;
+ case 0x22f0:
+ fw_lme = fw_c_rs2000;
+ ret = request_firmware(&fw, fw_lme, &udev->dev);
+ dvb_usb_lme2510_firmware = TUNER_RS2000;
+ break;
+ default:
+ fw_lme = fw_c_s7395;
}
+
if (cold_fw) {
info("FRM Loading %s file", fw_lme);
ret = lme2510_download_firmware(udev, fw);
@@ -906,6 +946,29 @@ static struct stv0299_config sharp_z0194_config = {
.set_symbol_rate = sharp_z0194a_set_symbol_rate,
};
+static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe,
+ int caller)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dvb_usb_device *d = adap->dev;
+ struct lme2510_state *st = d->priv;
+
+ mutex_lock(&d->i2c_mutex);
+ if ((st->i2c_talk_onoff == 1) && (st->stream_on & 1)) {
+ st->i2c_talk_onoff = 0;
+ lme2510_stream_restart(d);
+ }
+ mutex_unlock(&d->i2c_mutex);
+
+ return 0;
+}
+
+static struct m88rs2000_config m88rs2000_config = {
+ .demod_addr = 0xd0,
+ .tuner_addr = 0xc0,
+ .set_ts_params = dm04_rs2000_set_ts_param,
+};
+
static int dm04_lme2510_set_voltage(struct dvb_frontend *fe,
fe_sec_voltage_t voltage)
{
@@ -915,8 +978,7 @@ static int dm04_lme2510_set_voltage(struct dvb_frontend *fe,
static u8 rbuf[1];
int ret = 0, len = 3, rlen = 1;
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&adap->dev->i2c_mutex);
switch (voltage) {
case SEC_VOLTAGE_18:
@@ -937,12 +999,31 @@ static int dm04_lme2510_set_voltage(struct dvb_frontend *fe,
return (ret < 0) ? -ENODEV : 0;
}
+static int dm04_rs2000_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct lme2510_state *st = adap->dev->priv;
+
+ *strength = (u16)((u32)st->signal_level * 0xffff / 0x7f);
+ return 0;
+}
+
+static int dm04_rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct lme2510_state *st = adap->dev->priv;
+
+ *snr = (u16)((u32)st->signal_sn * 0xffff / 0xff);
+ return 0;
+}
+
static int lme_name(struct dvb_usb_adapter *adap)
{
struct lme2510_state *st = adap->dev->priv;
const char *desc = adap->dev->desc->name;
char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395",
- " SHARP:BS2F7HZ0194"};
+ " SHARP:BS2F7HZ0194", " RS2000"};
char *name = adap->fe_adap[0].fe->ops.info.name;
strlcpy(name, desc, 128);
@@ -958,60 +1039,82 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
int ret = 0;
st->i2c_talk_onoff = 1;
-
- st->i2c_gate = 4;
- adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config,
- &adap->dev->i2c_adap);
-
- if (adap->fe_adap[0].fe) {
- info("TUN Found Frontend TDA10086");
- st->i2c_tuner_gate_w = 4;
- st->i2c_tuner_gate_r = 4;
- st->i2c_tuner_addr = 0xc0;
- st->tuner_config = TUNER_LG;
- if (dvb_usb_lme2510_firmware != TUNER_LG) {
- dvb_usb_lme2510_firmware = TUNER_LG;
- ret = lme_firmware_switch(adap->dev->udev, 1);
+ switch (le16_to_cpu(adap->dev->udev->descriptor.idProduct)) {
+ case 0x1122:
+ case 0x1120:
+ st->i2c_gate = 4;
+ adap->fe_adap[0].fe = dvb_attach(tda10086_attach,
+ &tda10086_config, &adap->dev->i2c_adap);
+ if (adap->fe_adap[0].fe) {
+ info("TUN Found Frontend TDA10086");
+ st->i2c_tuner_gate_w = 4;
+ st->i2c_tuner_gate_r = 4;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_LG;
+ if (dvb_usb_lme2510_firmware != TUNER_LG) {
+ dvb_usb_lme2510_firmware = TUNER_LG;
+ ret = lme_firmware_switch(adap->dev->udev, 1);
+ }
+ break;
}
- goto end;
- }
- st->i2c_gate = 4;
- adap->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194_config,
+ st->i2c_gate = 4;
+ adap->fe_adap[0].fe = dvb_attach(stv0299_attach,
+ &sharp_z0194_config, &adap->dev->i2c_adap);
+ if (adap->fe_adap[0].fe) {
+ info("FE Found Stv0299");
+ st->i2c_tuner_gate_w = 4;
+ st->i2c_tuner_gate_r = 5;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_S0194;
+ if (dvb_usb_lme2510_firmware != TUNER_S0194) {
+ dvb_usb_lme2510_firmware = TUNER_S0194;
+ ret = lme_firmware_switch(adap->dev->udev, 1);
+ }
+ break;
+ }
+
+ st->i2c_gate = 5;
+ adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config,
&adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe) {
- info("FE Found Stv0299");
- st->i2c_tuner_gate_w = 4;
- st->i2c_tuner_gate_r = 5;
- st->i2c_tuner_addr = 0xc0;
- st->tuner_config = TUNER_S0194;
- if (dvb_usb_lme2510_firmware != TUNER_S0194) {
- dvb_usb_lme2510_firmware = TUNER_S0194;
- ret = lme_firmware_switch(adap->dev->udev, 1);
+
+ if (adap->fe_adap[0].fe) {
+ info("FE Found Stv0288");
+ st->i2c_tuner_gate_w = 4;
+ st->i2c_tuner_gate_r = 5;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_S7395;
+ if (dvb_usb_lme2510_firmware != TUNER_S7395) {
+ dvb_usb_lme2510_firmware = TUNER_S7395;
+ ret = lme_firmware_switch(adap->dev->udev, 1);
+ }
+ break;
}
- goto end;
+ case 0x22f0:
+ st->i2c_gate = 5;
+ adap->fe_adap[0].fe = dvb_attach(m88rs2000_attach,
+ &m88rs2000_config, &adap->dev->i2c_adap);
+
+ if (adap->fe_adap[0].fe) {
+ info("FE Found M88RS2000");
+ st->i2c_tuner_gate_w = 5;
+ st->i2c_tuner_gate_r = 5;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_RS2000;
+ adap->fe_adap[0].fe->ops.read_signal_strength =
+ dm04_rs2000_read_signal_strength;
+ adap->fe_adap[0].fe->ops.read_snr =
+ dm04_rs2000_read_snr;
+ }
+ break;
}
- st->i2c_gate = 5;
- adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe) {
- info("FE Found Stv0288");
- st->i2c_tuner_gate_w = 4;
- st->i2c_tuner_gate_r = 5;
- st->i2c_tuner_addr = 0xc0;
- st->tuner_config = TUNER_S7395;
- if (dvb_usb_lme2510_firmware != TUNER_S7395) {
- dvb_usb_lme2510_firmware = TUNER_S7395;
- ret = lme_firmware_switch(adap->dev->udev, 1);
- }
- } else {
- info("DM04 Not Supported");
- return -ENODEV;
+ if (adap->fe_adap[0].fe == NULL) {
+ info("DM04/QQBOX Not Powered up or not Supported");
+ return -ENODEV;
}
-
-end: if (ret) {
+ if (ret) {
if (adap->fe_adap[0].fe) {
dvb_frontend_detach(adap->fe_adap[0].fe);
adap->fe_adap[0].fe = NULL;
@@ -1028,7 +1131,7 @@ end: if (ret) {
static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap)
{
struct lme2510_state *st = adap->dev->priv;
- char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA"};
+ char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA", "RS2000"};
int ret = 0;
switch (st->tuner_config) {
@@ -1047,6 +1150,9 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap)
&adap->dev->i2c_adap, DVB_PLL_OPERA1))
ret = st->tuner_config;
break;
+ case TUNER_RS2000:
+ ret = st->tuner_config;
+ break;
default:
break;
}
@@ -1075,10 +1181,9 @@ static int lme2510_powerup(struct dvb_usb_device *d, int onoff)
static u8 lnb_on[] = LNB_ON;
static u8 lnb_off[] = LNB_OFF;
static u8 rbuf[1];
- int ret, len = 3, rlen = 1;
+ int ret = 0, len = 3, rlen = 1;
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&d->i2c_mutex);
if (onoff)
ret = lme2510_usb_talk(d, lnb_on, len, rbuf, rlen);
@@ -1136,6 +1241,7 @@ static int lme2510_probe(struct usb_interface *intf,
static struct usb_device_id lme2510_table[] = {
{ USB_DEVICE(0x3344, 0x1122) }, /* LME2510 */
{ USB_DEVICE(0x3344, 0x1120) }, /* LME2510C */
+ { USB_DEVICE(0x3344, 0x22f0) }, /* LME2510C RS2000 */
{} /* Terminating entry */
};
@@ -1153,7 +1259,7 @@ static struct dvb_usb_device_properties lme2510_properties = {
DVB_USB_ADAP_NEED_PID_FILTERING|
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.streaming_ctrl = lme2510_streaming_ctrl,
- .pid_filter_count = 15,
+ .pid_filter_count = 32,
.pid_filter = lme2510_pid_filter,
.pid_filter_ctrl = lme2510_pid_filter_ctrl,
.frontend_attach = dm04_lme2510_frontend_attach,
@@ -1204,7 +1310,7 @@ static struct dvb_usb_device_properties lme2510c_properties = {
DVB_USB_ADAP_NEED_PID_FILTERING|
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.streaming_ctrl = lme2510_streaming_ctrl,
- .pid_filter_count = 15,
+ .pid_filter_count = 32,
.pid_filter = lme2510_pid_filter,
.pid_filter_ctrl = lme2510_pid_filter_ctrl,
.frontend_attach = dm04_lme2510_frontend_attach,
@@ -1234,11 +1340,14 @@ static struct dvb_usb_device_properties lme2510c_properties = {
.identify_state = lme2510_identify_state,
.i2c_algo = &lme2510_i2c_algo,
.generic_bulk_ctrl_endpoint = 0,
- .num_device_descs = 1,
+ .num_device_descs = 2,
.devices = {
{ "DM04_LME2510C_DVB-S",
{ &lme2510_table[1], NULL },
},
+ { "DM04_LME2510C_DVB-S RS2000",
+ { &lme2510_table[2], NULL },
+ },
}
};
@@ -1295,5 +1404,5 @@ module_usb_driver(lme2510_driver);
MODULE_AUTHOR("Malcolm Priestley ");
MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0");
-MODULE_VERSION("1.91");
+MODULE_VERSION("1.99");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.h b/drivers/media/dvb/dvb-usb/lmedm04.h
index ab21e2ef53fa..e9c207205c2f 100644
--- a/drivers/media/dvb/dvb-usb/lmedm04.h
+++ b/drivers/media/dvb/dvb-usb/lmedm04.h
@@ -41,6 +41,7 @@
#define LME_ST_ON_W {0x06, 0x00}
#define LME_CLEAR_PID {0x03, 0x02, 0x20, 0xa0}
#define LME_ZERO_PID {0x03, 0x06, 0x00, 0x00, 0x01, 0x00, 0x20, 0x9c}
+#define LME_ALL_PIDS {0x03, 0x06, 0x00, 0xff, 0x01, 0x1f, 0x20, 0x81}
/* LNB Voltage
* 07 XX XX
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c
index 38ef0253d3b5..81305de2fea5 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf.c
+++ b/drivers/media/dvb/dvb-usb/mxl111sf.c
@@ -351,15 +351,13 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
adap_state->ep6_clockphase,
0, 0);
mxl_fail(ret);
+#if 0
} else {
ret = mxl111sf_disable_656_port(state);
mxl_fail(ret);
+#endif
}
- mxl111sf_read_reg(state, 0x12, &tmp);
- tmp &= ~0x04;
- mxl111sf_write_reg(state, 0x12, tmp);
-
return ret;
}
diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.c b/drivers/media/dvb/dvb-usb/rtl28xxu.c
new file mode 100644
index 000000000000..8f4736a10fc8
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/rtl28xxu.c
@@ -0,0 +1,982 @@
+/*
+ * Realtek RTL28xxU DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari
+ * Copyright (C) 2011 Antti Palosaari
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "rtl28xxu.h"
+
+#include "rtl2830.h"
+
+#include "qt1010.h"
+#include "mt2060.h"
+#include "mxl5005s.h"
+
+/* debug */
+static int dvb_usb_rtl28xxu_debug;
+module_param_named(debug, dvb_usb_rtl28xxu_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req)
+{
+ int ret;
+ unsigned int pipe;
+ u8 requesttype;
+ u8 *buf;
+
+ buf = kmalloc(req->size, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (req->index & CMD_WR_FLAG) {
+ /* write */
+ memcpy(buf, req->data, req->size);
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+ pipe = usb_sndctrlpipe(d->udev, 0);
+ } else {
+ /* read */
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+ pipe = usb_rcvctrlpipe(d->udev, 0);
+ }
+
+ ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value,
+ req->index, buf, req->size, 1000);
+ if (ret > 0)
+ ret = 0;
+
+ deb_dump(0, requesttype, req->value, req->index, buf, req->size,
+ deb_xfer);
+
+ /* read request, copy returned data to return buf */
+ if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
+ memcpy(req->data, buf, req->size);
+
+ kfree(buf);
+
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2831_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+{
+ struct rtl28xxu_req req;
+
+ if (reg < 0x3000)
+ req.index = CMD_USB_WR;
+ else if (reg < 0x4000)
+ req.index = CMD_SYS_WR;
+ else
+ req.index = CMD_IR_WR;
+
+ req.value = reg;
+ req.size = len;
+ req.data = val;
+
+ return rtl28xxu_ctrl_msg(d, &req);
+}
+
+static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+{
+ struct rtl28xxu_req req;
+
+ if (reg < 0x3000)
+ req.index = CMD_USB_RD;
+ else if (reg < 0x4000)
+ req.index = CMD_SYS_RD;
+ else
+ req.index = CMD_IR_RD;
+
+ req.value = reg;
+ req.size = len;
+ req.data = val;
+
+ return rtl28xxu_ctrl_msg(d, &req);
+}
+
+static int rtl2831_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
+{
+ return rtl2831_wr_regs(d, reg, &val, 1);
+}
+
+static int rtl2831_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
+{
+ return rtl2831_rd_regs(d, reg, val, 1);
+}
+
+/* I2C */
+static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ int ret;
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct rtl28xxu_priv *priv = d->priv;
+ struct rtl28xxu_req req;
+
+ /*
+ * It is not known which are real I2C bus xfer limits, but testing
+ * with RTL2831U + MT2060 gives max RD 24 and max WR 22 bytes.
+ * TODO: find out RTL2832U lens
+ */
+
+ /*
+ * I2C adapter logic looks rather complicated due to fact it handles
+ * three different access methods. Those methods are;
+ * 1) integrated demod access
+ * 2) old I2C access
+ * 3) new I2C access
+ *
+ * Used method is selected in order 1, 2, 3. Method 3 can handle all
+ * requests but there is two reasons why not use it always;
+ * 1) It is most expensive, usually two USB messages are needed
+ * 2) At least RTL2831U does not support it
+ *
+ * Method 3 is needed in case of I2C write+read (typical register read)
+ * where write is more than one byte.
+ */
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
+ (msg[1].flags & I2C_M_RD)) {
+ if (msg[0].len > 24 || msg[1].len > 24) {
+ /* TODO: check msg[0].len max */
+ ret = -EOPNOTSUPP;
+ goto err_mutex_unlock;
+ } else if (msg[0].addr == 0x10) {
+ /* method 1 - integrated demod */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_DEMOD_RD | priv->page;
+ req.size = msg[1].len;
+ req.data = &msg[1].buf[0];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else if (msg[0].len < 2) {
+ /* method 2 - old I2C */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_I2C_RD;
+ req.size = msg[1].len;
+ req.data = &msg[1].buf[0];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else {
+ /* method 3 - new I2C */
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_WR;
+ req.size = msg[0].len;
+ req.data = msg[0].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ if (ret)
+ goto err_mutex_unlock;
+
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_RD;
+ req.size = msg[1].len;
+ req.data = msg[1].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+ if (msg[0].len > 22) {
+ /* TODO: check msg[0].len max */
+ ret = -EOPNOTSUPP;
+ goto err_mutex_unlock;
+ } else if (msg[0].addr == 0x10) {
+ /* method 1 - integrated demod */
+ if (msg[0].buf[0] == 0x00) {
+ /* save demod page for later demod access */
+ priv->page = msg[0].buf[1];
+ ret = 0;
+ } else {
+ req.value = (msg[0].buf[0] << 8) |
+ (msg[0].addr << 1);
+ req.index = CMD_DEMOD_WR | priv->page;
+ req.size = msg[0].len-1;
+ req.data = &msg[0].buf[1];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else if (msg[0].len < 23) {
+ /* method 2 - old I2C */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_I2C_WR;
+ req.size = msg[0].len-1;
+ req.data = &msg[0].buf[1];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else {
+ /* method 3 - new I2C */
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_WR;
+ req.size = msg[0].len;
+ req.data = msg[0].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+err_mutex_unlock:
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret ? ret : num;
+}
+
+static u32 rtl28xxu_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm rtl28xxu_i2c_algo = {
+ .master_xfer = rtl28xxu_i2c_xfer,
+ .functionality = rtl28xxu_i2c_func,
+};
+
+static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 1,
+ .if_dvbt = 36150000,
+ .vtop = 0x20,
+ .krf = 0x04,
+ .agc_targ_val = 0x2d,
+
+};
+
+static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 1,
+ .if_dvbt = 36125000,
+ .vtop = 0x20,
+ .krf = 0x04,
+ .agc_targ_val = 0x2d,
+};
+
+static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 0,
+ .if_dvbt = 4570000,
+ .vtop = 0x3f,
+ .krf = 0x04,
+ .agc_targ_val = 0x3e,
+};
+
+static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ u8 buf[1];
+ struct rtl2830_config *rtl2830_config;
+ /* open RTL2831U/RTL2830 I2C gate */
+ struct rtl28xxu_req req_gate = { 0x0120, 0x0011, 0x0001, "\x08" };
+ /* for MT2060 tuner probe */
+ struct rtl28xxu_req req_mt2060 = { 0x00c0, CMD_I2C_RD, 1, buf };
+ /* for QT1010 tuner probe */
+ struct rtl28xxu_req req_qt1010 = { 0x0fc4, CMD_I2C_RD, 1, buf };
+
+ deb_info("%s:\n", __func__);
+
+ /*
+ * RTL2831U GPIOs
+ * =========================================================
+ * GPIO0 | tuner#0 | 0 off | 1 on | MXL5005S (?)
+ * GPIO2 | LED | 0 off | 1 on |
+ * GPIO4 | tuner#1 | 0 on | 1 off | MT2060
+ */
+
+ /* GPIO direction */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
+ if (ret)
+ goto err;
+
+ /* enable as output GPIO0, GPIO2, GPIO4 */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
+ if (ret)
+ goto err;
+
+ /*
+ * Probe used tuner. We need to know used tuner before demod attach
+ * since there is some demod params needed to set according to tuner.
+ */
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
+ if (ret)
+ goto err;
+
+ /* check QT1010 ID(?) register; reg=0f val=2c */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_qt1010);
+ if (ret == 0 && buf[0] == 0x2c) {
+ priv->tuner = TUNER_RTL2830_QT1010;
+ rtl2830_config = &rtl28xxu_rtl2830_qt1010_config;
+ deb_info("%s: QT1010\n", __func__);
+ goto found;
+ } else {
+ deb_info("%s: QT1010 probe failed=%d - %02x\n",
+ __func__, ret, buf[0]);
+ }
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
+ if (ret)
+ goto err;
+
+ /* check MT2060 ID register; reg=00 val=63 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2060);
+ if (ret == 0 && buf[0] == 0x63) {
+ priv->tuner = TUNER_RTL2830_MT2060;
+ rtl2830_config = &rtl28xxu_rtl2830_mt2060_config;
+ deb_info("%s: MT2060\n", __func__);
+ goto found;
+ } else {
+ deb_info("%s: MT2060 probe failed=%d - %02x\n",
+ __func__, ret, buf[0]);
+ }
+
+ /* assume MXL5005S */
+ ret = 0;
+ priv->tuner = TUNER_RTL2830_MXL5005S;
+ rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config;
+ deb_info("%s: MXL5005S\n", __func__);
+ goto found;
+
+found:
+ /* attach demodulator */
+ adap->fe_adap[0].fe = dvb_attach(rtl2830_attach, rtl2830_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe_adap[0].fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ u8 buf[1];
+ /* open RTL2832U/RTL2832 I2C gate */
+ struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"};
+ /* close RTL2832U/RTL2832 I2C gate */
+ struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"};
+ /* for FC2580 tuner probe */
+ struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf};
+
+ deb_info("%s:\n", __func__);
+
+ /* GPIO direction */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
+ if (ret)
+ goto err;
+
+ /* enable as output GPIO0, GPIO2, GPIO4 */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_reg(adap->dev, SYS_DEMOD_CTL, 0xe8);
+ if (ret)
+ goto err;
+
+ /*
+ * Probe used tuner. We need to know used tuner before demod attach
+ * since there is some demod params needed to set according to tuner.
+ */
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_open);
+ if (ret)
+ goto err;
+
+ /* check FC2580 ID register; reg=01 val=56 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc2580);
+ if (ret == 0 && buf[0] == 0x56) {
+ priv->tuner = TUNER_RTL2832_FC2580;
+ deb_info("%s: FC2580\n", __func__);
+ goto found;
+ } else {
+ deb_info("%s: FC2580 probe failed=%d - %02x\n",
+ __func__, ret, buf[0]);
+ }
+
+ /* close demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
+ if (ret)
+ goto err;
+
+ /* tuner not found */
+ ret = -ENODEV;
+ goto err;
+
+found:
+ /* close demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
+ if (ret)
+ goto err;
+
+ /* attach demodulator */
+ /* TODO: */
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct qt1010_config rtl28xxu_qt1010_config = {
+ .i2c_address = 0x62, /* 0xc4 */
+};
+
+static struct mt2060_config rtl28xxu_mt2060_config = {
+ .i2c_address = 0x60, /* 0xc0 */
+ .clock_out = 0,
+};
+
+static struct mxl5005s_config rtl28xxu_mxl5005s_config = {
+ .i2c_address = 0x63, /* 0xc6 */
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_C_H,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static int rtl2831u_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ struct i2c_adapter *rtl2830_tuner_i2c;
+ struct dvb_frontend *fe;
+
+ deb_info("%s:\n", __func__);
+
+ /* use rtl2830 driver I2C adapter, for more info see rtl2830 driver */
+ rtl2830_tuner_i2c = rtl2830_get_tuner_i2c_adapter(adap->fe_adap[0].fe);
+
+ switch (priv->tuner) {
+ case TUNER_RTL2830_QT1010:
+ fe = dvb_attach(qt1010_attach, adap->fe_adap[0].fe,
+ rtl2830_tuner_i2c, &rtl28xxu_qt1010_config);
+ break;
+ case TUNER_RTL2830_MT2060:
+ fe = dvb_attach(mt2060_attach, adap->fe_adap[0].fe,
+ rtl2830_tuner_i2c, &rtl28xxu_mt2060_config,
+ 1220);
+ break;
+ case TUNER_RTL2830_MXL5005S:
+ fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
+ rtl2830_tuner_i2c, &rtl28xxu_mxl5005s_config);
+ break;
+ default:
+ fe = NULL;
+ err("unknown tuner=%d", priv->tuner);
+ }
+
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ struct dvb_frontend *fe;
+
+ deb_info("%s:\n", __func__);
+
+ switch (priv->tuner) {
+ case TUNER_RTL2832_FC2580:
+ /* TODO: */
+ fe = NULL;
+ break;
+ default:
+ fe = NULL;
+ err("unknown tuner=%d", priv->tuner);
+ }
+
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
+{
+ int ret;
+ u8 buf[2], gpio;
+
+ deb_info("%s: onoff=%d\n", __func__, onoff);
+
+ ret = rtl2831_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio);
+ if (ret)
+ goto err;
+
+ if (onoff) {
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ gpio |= 0x04; /* LED on */
+ } else {
+ buf[0] = 0x10; /* stall EPA */
+ buf[1] = 0x02; /* reset EPA */
+ gpio &= (~0x04); /* LED off */
+ }
+
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ int ret;
+ u8 gpio, sys0;
+
+ deb_info("%s: onoff=%d\n", __func__, onoff);
+
+ /* demod adc */
+ ret = rtl2831_rd_reg(d, SYS_SYS0, &sys0);
+ if (ret)
+ goto err;
+
+ /* tuner power, read GPIOs */
+ ret = rtl2831_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
+ if (ret)
+ goto err;
+
+ deb_info("%s: RD SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
+
+ if (onoff) {
+ gpio |= 0x01; /* GPIO0 = 1 */
+ gpio &= (~0x10); /* GPIO4 = 0 */
+ sys0 = sys0 & 0x0f;
+ sys0 |= 0xe0;
+ } else {
+ gpio &= (~0x01); /* GPIO0 = 0 */
+ gpio |= 0x10; /* GPIO4 = 1 */
+ sys0 = sys0 & (~0xc0);
+ }
+
+ deb_info("%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
+
+ /* demod adc */
+ ret = rtl2831_wr_reg(d, SYS_SYS0, sys0);
+ if (ret)
+ goto err;
+
+ /* tuner power, write GPIOs */
+ ret = rtl2831_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2831u_rc_query(struct dvb_usb_device *d)
+{
+ int ret, i;
+ struct rtl28xxu_priv *priv = d->priv;
+ u8 buf[5];
+ u32 rc_code;
+ struct rtl28xxu_reg_val rc_nec_tab[] = {
+ { 0x3033, 0x80 },
+ { 0x3020, 0x43 },
+ { 0x3021, 0x16 },
+ { 0x3022, 0x16 },
+ { 0x3023, 0x5a },
+ { 0x3024, 0x2d },
+ { 0x3025, 0x16 },
+ { 0x3026, 0x01 },
+ { 0x3028, 0xb0 },
+ { 0x3029, 0x04 },
+ { 0x302c, 0x88 },
+ { 0x302e, 0x13 },
+ { 0x3030, 0xdf },
+ { 0x3031, 0x05 },
+ };
+
+ /* init remote controller */
+ if (!priv->rc_active) {
+ for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
+ ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
+ rc_nec_tab[i].val);
+ if (ret)
+ goto err;
+ }
+ priv->rc_active = true;
+ }
+
+ ret = rtl2831_rd_regs(d, SYS_IRRC_RP, buf, 5);
+ if (ret)
+ goto err;
+
+ if (buf[4] & 0x01) {
+ if (buf[2] == (u8) ~buf[3]) {
+ if (buf[0] == (u8) ~buf[1]) {
+ /* NEC standard (16 bit) */
+ rc_code = buf[0] << 8 | buf[2];
+ } else {
+ /* NEC extended (24 bit) */
+ rc_code = buf[0] << 16 |
+ buf[1] << 8 | buf[2];
+ }
+ } else {
+ /* NEC full (32 bit) */
+ rc_code = buf[0] << 24 | buf[1] << 16 |
+ buf[2] << 8 | buf[3];
+ }
+
+ rc_keydown(d->rc_dev, rc_code, 0);
+
+ ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
+ if (ret)
+ goto err;
+
+ /* repeated intentionally to avoid extra keypress */
+ ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
+ if (ret)
+ goto err;
+ }
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_rc_query(struct dvb_usb_device *d)
+{
+ int ret, i;
+ struct rtl28xxu_priv *priv = d->priv;
+ u8 buf[128];
+ int len;
+ struct rtl28xxu_reg_val rc_nec_tab[] = {
+ { IR_RX_CTRL, 0x20 },
+ { IR_RX_BUF_CTRL, 0x80 },
+ { IR_RX_IF, 0xff },
+ { IR_RX_IE, 0xff },
+ { IR_MAX_DURATION0, 0xd0 },
+ { IR_MAX_DURATION1, 0x07 },
+ { IR_IDLE_LEN0, 0xc0 },
+ { IR_IDLE_LEN1, 0x00 },
+ { IR_GLITCH_LEN, 0x03 },
+ { IR_RX_CLK, 0x09 },
+ { IR_RX_CFG, 0x1c },
+ { IR_MAX_H_TOL_LEN, 0x1e },
+ { IR_MAX_L_TOL_LEN, 0x1e },
+ { IR_RX_CTRL, 0x80 },
+ };
+
+ /* init remote controller */
+ if (!priv->rc_active) {
+ for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
+ ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
+ rc_nec_tab[i].val);
+ if (ret)
+ goto err;
+ }
+ priv->rc_active = true;
+ }
+
+ ret = rtl2831_rd_reg(d, IR_RX_IF, &buf[0]);
+ if (ret)
+ goto err;
+
+ if (buf[0] != 0x83)
+ goto exit;
+
+ ret = rtl2831_rd_reg(d, IR_RX_BC, &buf[0]);
+ if (ret)
+ goto err;
+
+ len = buf[0];
+ ret = rtl2831_rd_regs(d, IR_RX_BUF, buf, len);
+
+ /* TODO: pass raw IR to Kernel IR decoder */
+
+ ret = rtl2831_wr_reg(d, IR_RX_IF, 0x03);
+ ret = rtl2831_wr_reg(d, IR_RX_BUF_CTRL, 0x80);
+ ret = rtl2831_wr_reg(d, IR_RX_CTRL, 0x80);
+
+exit:
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+enum rtl28xxu_usb_table_entry {
+ RTL2831U_0BDA_2831,
+ RTL2831U_14AA_0160,
+ RTL2831U_14AA_0161,
+};
+
+static struct usb_device_id rtl28xxu_table[] = {
+ /* RTL2831U */
+ [RTL2831U_0BDA_2831] = {
+ USB_DEVICE(USB_VID_REALTEK, USB_PID_REALTEK_RTL2831U)},
+ [RTL2831U_14AA_0160] = {
+ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT)},
+ [RTL2831U_14AA_0161] = {
+ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2)},
+
+ /* RTL2832U */
+ {} /* terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, rtl28xxu_table);
+
+static struct dvb_usb_device_properties rtl28xxu_properties[] = {
+ {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .no_reconnect = 1,
+
+ .size_of_priv = sizeof(struct rtl28xxu_priv),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {
+ {
+ .frontend_attach = rtl2831u_frontend_attach,
+ .tuner_attach = rtl2831u_tuner_attach,
+ .streaming_ctrl = rtl28xxu_streaming_ctrl,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x81,
+ .u = {
+ .bulk = {
+ .buffersize = 8*512,
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ .power_ctrl = rtl28xxu_power_ctrl,
+
+ .rc.core = {
+ .protocol = RC_TYPE_NEC,
+ .module_name = "rtl28xxu",
+ .rc_query = rtl2831u_rc_query,
+ .rc_interval = 400,
+ .allowed_protos = RC_TYPE_NEC,
+ .rc_codes = RC_MAP_EMPTY,
+ },
+
+ .i2c_algo = &rtl28xxu_i2c_algo,
+
+ .num_device_descs = 2,
+ .devices = {
+ {
+ .name = "Realtek RTL2831U reference design",
+ .warm_ids = {
+ &rtl28xxu_table[RTL2831U_0BDA_2831],
+ },
+ },
+ {
+ .name = "Freecom USB2.0 DVB-T",
+ .warm_ids = {
+ &rtl28xxu_table[RTL2831U_14AA_0160],
+ &rtl28xxu_table[RTL2831U_14AA_0161],
+ },
+ },
+ }
+ },
+ {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .no_reconnect = 1,
+
+ .size_of_priv = sizeof(struct rtl28xxu_priv),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {
+ {
+ .frontend_attach = rtl2832u_frontend_attach,
+ .tuner_attach = rtl2832u_tuner_attach,
+ .streaming_ctrl = rtl28xxu_streaming_ctrl,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x81,
+ .u = {
+ .bulk = {
+ .buffersize = 8*512,
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ .power_ctrl = rtl28xxu_power_ctrl,
+
+ .rc.core = {
+ .protocol = RC_TYPE_NEC,
+ .module_name = "rtl28xxu",
+ .rc_query = rtl2832u_rc_query,
+ .rc_interval = 400,
+ .allowed_protos = RC_TYPE_NEC,
+ .rc_codes = RC_MAP_EMPTY,
+ },
+
+ .i2c_algo = &rtl28xxu_i2c_algo,
+
+ .num_device_descs = 0, /* disabled as no support for RTL2832 */
+ .devices = {
+ {
+ .name = "Realtek RTL2832U reference design",
+ },
+ }
+ },
+
+};
+
+static int rtl28xxu_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret, i;
+ int properties_count = ARRAY_SIZE(rtl28xxu_properties);
+ struct dvb_usb_device *d;
+
+ deb_info("%s: interface=%d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return 0;
+
+ for (i = 0; i < properties_count; i++) {
+ ret = dvb_usb_device_init(intf, &rtl28xxu_properties[i],
+ THIS_MODULE, &d, adapter_nr);
+ if (ret == 0 || ret != -ENODEV)
+ break;
+ }
+
+ if (ret)
+ goto err;
+
+ /* init USB endpoints */
+ ret = rtl2831_wr_reg(d, USB_SYSCTL_0, 0x09);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct usb_driver rtl28xxu_driver = {
+ .name = "dvb_usb_rtl28xxu",
+ .probe = rtl28xxu_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = rtl28xxu_table,
+};
+
+/* module stuff */
+static int __init rtl28xxu_module_init(void)
+{
+ int ret;
+
+ deb_info("%s:\n", __func__);
+
+ ret = usb_register(&rtl28xxu_driver);
+ if (ret)
+ err("usb_register failed=%d", ret);
+
+ return ret;
+}
+
+static void __exit rtl28xxu_module_exit(void)
+{
+ deb_info("%s:\n", __func__);
+
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&rtl28xxu_driver);
+}
+
+module_init(rtl28xxu_module_init);
+module_exit(rtl28xxu_module_exit);
+
+MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver");
+MODULE_AUTHOR("Antti Palosaari ");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.h b/drivers/media/dvb/dvb-usb/rtl28xxu.h
new file mode 100644
index 000000000000..90f3bb4f4c0e
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/rtl28xxu.h
@@ -0,0 +1,264 @@
+/*
+ * Realtek RTL28xxU DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari
+ * Copyright (C) 2011 Antti Palosaari
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL28XXU_H
+#define RTL28XXU_H
+
+#define DVB_USB_LOG_PREFIX "rtl28xxu"
+#include "dvb-usb.h"
+
+#define deb_info(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x01, args)
+#define deb_rc(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x02, args)
+#define deb_xfer(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x04, args)
+#define deb_reg(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x08, args)
+#define deb_i2c(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x10, args)
+#define deb_fw(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x20, args)
+
+#define deb_dump(r, t, v, i, b, l, func) { \
+ int loop_; \
+ func("%02x %02x %02x %02x %02x %02x %02x %02x", \
+ t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \
+ if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
+ func(" >>> "); \
+ else \
+ func(" <<< "); \
+ for (loop_ = 0; loop_ < l; loop_++) \
+ func("%02x ", b[loop_]); \
+ func("\n");\
+}
+
+/*
+ * USB commands
+ * (usb_control_msg() index parameter)
+ */
+
+#define DEMOD 0x0000
+#define USB 0x0100
+#define SYS 0x0200
+#define I2C 0x0300
+#define I2C_DA 0x0600
+
+#define CMD_WR_FLAG 0x0010
+#define CMD_DEMOD_RD 0x0000
+#define CMD_DEMOD_WR 0x0010
+#define CMD_USB_RD 0x0100
+#define CMD_USB_WR 0x0110
+#define CMD_SYS_RD 0x0200
+#define CMD_IR_RD 0x0201
+#define CMD_IR_WR 0x0211
+#define CMD_SYS_WR 0x0210
+#define CMD_I2C_RD 0x0300
+#define CMD_I2C_WR 0x0310
+#define CMD_I2C_DA_RD 0x0600
+#define CMD_I2C_DA_WR 0x0610
+
+
+struct rtl28xxu_priv {
+ u8 chip_id;
+ u8 tuner;
+ u8 page; /* integrated demod active register page */
+ bool rc_active;
+};
+
+enum rtl28xxu_chip_id {
+ CHIP_ID_NONE,
+ CHIP_ID_RTL2831U,
+ CHIP_ID_RTL2832U,
+};
+
+enum rtl28xxu_tuner {
+ TUNER_NONE,
+
+ TUNER_RTL2830_QT1010,
+ TUNER_RTL2830_MT2060,
+ TUNER_RTL2830_MXL5005S,
+
+ TUNER_RTL2832_MT2266,
+ TUNER_RTL2832_FC2580,
+ TUNER_RTL2832_MT2063,
+ TUNER_RTL2832_MAX3543,
+ TUNER_RTL2832_TUA9001,
+ TUNER_RTL2832_MXL5007T,
+ TUNER_RTL2832_FC0012,
+ TUNER_RTL2832_E4000,
+ TUNER_RTL2832_TDA18272,
+ TUNER_RTL2832_FC0013,
+};
+
+struct rtl28xxu_req {
+ u16 value;
+ u16 index;
+ u16 size;
+ u8 *data;
+};
+
+struct rtl28xxu_reg_val {
+ u16 reg;
+ u8 val;
+};
+
+/*
+ * memory map
+ *
+ * 0x0000 DEMOD : demodulator
+ * 0x2000 USB : SIE, USB endpoint, debug, DMA
+ * 0x3000 SYS : system
+ * 0xfc00 RC : remote controller (not RTL2831U)
+ */
+
+/*
+ * USB registers
+ */
+/* SIE Control Registers */
+#define USB_SYSCTL 0x2000 /* USB system control */
+#define USB_SYSCTL_0 0x2000 /* USB system control */
+#define USB_SYSCTL_1 0x2001 /* USB system control */
+#define USB_SYSCTL_2 0x2002 /* USB system control */
+#define USB_SYSCTL_3 0x2003 /* USB system control */
+#define USB_IRQSTAT 0x2008 /* SIE interrupt status */
+#define USB_IRQEN 0x200C /* SIE interrupt enable */
+#define USB_CTRL 0x2010 /* USB control */
+#define USB_STAT 0x2014 /* USB status */
+#define USB_DEVADDR 0x2018 /* USB device address */
+#define USB_TEST 0x201C /* USB test mode */
+#define USB_FRAME_NUMBER 0x2020 /* frame number */
+#define USB_FIFO_ADDR 0x2028 /* address of SIE FIFO RAM */
+#define USB_FIFO_CMD 0x202A /* SIE FIFO RAM access command */
+#define USB_FIFO_DATA 0x2030 /* SIE FIFO RAM data */
+/* Endpoint Registers */
+#define EP0_SETUPA 0x20F8 /* EP 0 setup packet lower byte */
+#define EP0_SETUPB 0x20FC /* EP 0 setup packet higher byte */
+#define USB_EP0_CFG 0x2104 /* EP 0 configure */
+#define USB_EP0_CTL 0x2108 /* EP 0 control */
+#define USB_EP0_STAT 0x210C /* EP 0 status */
+#define USB_EP0_IRQSTAT 0x2110 /* EP 0 interrupt status */
+#define USB_EP0_IRQEN 0x2114 /* EP 0 interrupt enable */
+#define USB_EP0_MAXPKT 0x2118 /* EP 0 max packet size */
+#define USB_EP0_BC 0x2120 /* EP 0 FIFO byte counter */
+#define USB_EPA_CFG 0x2144 /* EP A configure */
+#define USB_EPA_CFG_0 0x2144 /* EP A configure */
+#define USB_EPA_CFG_1 0x2145 /* EP A configure */
+#define USB_EPA_CFG_2 0x2146 /* EP A configure */
+#define USB_EPA_CFG_3 0x2147 /* EP A configure */
+#define USB_EPA_CTL 0x2148 /* EP A control */
+#define USB_EPA_CTL_0 0x2148 /* EP A control */
+#define USB_EPA_CTL_1 0x2149 /* EP A control */
+#define USB_EPA_CTL_2 0x214A /* EP A control */
+#define USB_EPA_CTL_3 0x214B /* EP A control */
+#define USB_EPA_STAT 0x214C /* EP A status */
+#define USB_EPA_IRQSTAT 0x2150 /* EP A interrupt status */
+#define USB_EPA_IRQEN 0x2154 /* EP A interrupt enable */
+#define USB_EPA_MAXPKT 0x2158 /* EP A max packet size */
+#define USB_EPA_MAXPKT_0 0x2158 /* EP A max packet size */
+#define USB_EPA_MAXPKT_1 0x2159 /* EP A max packet size */
+#define USB_EPA_MAXPKT_2 0x215A /* EP A max packet size */
+#define USB_EPA_MAXPKT_3 0x215B /* EP A max packet size */
+#define USB_EPA_FIFO_CFG 0x2160 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_0 0x2160 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_1 0x2161 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_2 0x2162 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_3 0x2163 /* EP A FIFO configure */
+/* Debug Registers */
+#define USB_PHYTSTDIS 0x2F04 /* PHY test disable */
+#define USB_TOUT_VAL 0x2F08 /* USB time-out time */
+#define USB_VDRCTRL 0x2F10 /* UTMI vendor signal control */
+#define USB_VSTAIN 0x2F14 /* UTMI vendor signal status in */
+#define USB_VLOADM 0x2F18 /* UTMI load vendor signal status in */
+#define USB_VSTAOUT 0x2F1C /* UTMI vendor signal status out */
+#define USB_UTMI_TST 0x2F80 /* UTMI test */
+#define USB_UTMI_STATUS 0x2F84 /* UTMI status */
+#define USB_TSTCTL 0x2F88 /* test control */
+#define USB_TSTCTL2 0x2F8C /* test control 2 */
+#define USB_PID_FORCE 0x2F90 /* force PID */
+#define USB_PKTERR_CNT 0x2F94 /* packet error counter */
+#define USB_RXERR_CNT 0x2F98 /* RX error counter */
+#define USB_MEM_BIST 0x2F9C /* MEM BIST test */
+#define USB_SLBBIST 0x2FA0 /* self-loop-back BIST */
+#define USB_CNTTEST 0x2FA4 /* counter test */
+#define USB_PHYTST 0x2FC0 /* USB PHY test */
+#define USB_DBGIDX 0x2FF0 /* select individual block debug signal */
+#define USB_DBGMUX 0x2FF4 /* debug signal module mux */
+
+/*
+ * SYS registers
+ */
+/* demod control registers */
+#define SYS_SYS0 0x3000 /* include DEMOD_CTL, GPO, GPI, GPOE */
+#define SYS_DEMOD_CTL 0x3000 /* control register for DVB-T demodulator */
+/* GPIO registers */
+#define SYS_GPIO_OUT_VAL 0x3001 /* output value of GPIO */
+#define SYS_GPIO_IN_VAL 0x3002 /* input value of GPIO */
+#define SYS_GPIO_OUT_EN 0x3003 /* output enable of GPIO */
+#define SYS_SYS1 0x3004 /* include GPD, SYSINTE, SYSINTS, GP_CFG0 */
+#define SYS_GPIO_DIR 0x3004 /* direction control for GPIO */
+#define SYS_SYSINTE 0x3005 /* system interrupt enable */
+#define SYS_SYSINTS 0x3006 /* system interrupt status */
+#define SYS_GPIO_CFG0 0x3007 /* PAD configuration for GPIO0-GPIO3 */
+#define SYS_SYS2 0x3008 /* include GP_CFG1 and 3 reserved bytes */
+#define SYS_GPIO_CFG1 0x3008 /* PAD configuration for GPIO4 */
+#define SYS_DEMOD_CTL1 0x300B
+
+/* IrDA registers */
+#define SYS_IRRC_PSR 0x3020 /* IR protocol selection */
+#define SYS_IRRC_PER 0x3024 /* IR protocol extension */
+#define SYS_IRRC_SF 0x3028 /* IR sampling frequency */
+#define SYS_IRRC_DPIR 0x302C /* IR data package interval */
+#define SYS_IRRC_CR 0x3030 /* IR control */
+#define SYS_IRRC_RP 0x3034 /* IR read port */
+#define SYS_IRRC_SR 0x3038 /* IR status */
+/* I2C master registers */
+#define SYS_I2CCR 0x3040 /* I2C clock */
+#define SYS_I2CMCR 0x3044 /* I2C master control */
+#define SYS_I2CMSTR 0x3048 /* I2C master SCL timing */
+#define SYS_I2CMSR 0x304C /* I2C master status */
+#define SYS_I2CMFR 0x3050 /* I2C master FIFO */
+
+/*
+ * IR registers
+ */
+#define IR_RX_BUF 0xFC00
+#define IR_RX_IE 0xFD00
+#define IR_RX_IF 0xFD01
+#define IR_RX_CTRL 0xFD02
+#define IR_RX_CFG 0xFD03
+#define IR_MAX_DURATION0 0xFD04
+#define IR_MAX_DURATION1 0xFD05
+#define IR_IDLE_LEN0 0xFD06
+#define IR_IDLE_LEN1 0xFD07
+#define IR_GLITCH_LEN 0xFD08
+#define IR_RX_BUF_CTRL 0xFD09
+#define IR_RX_BUF_DATA 0xFD0A
+#define IR_RX_BC 0xFD0B
+#define IR_RX_CLK 0xFD0C
+#define IR_RX_C_COUNT_L 0xFD0D
+#define IR_RX_C_COUNT_H 0xFD0E
+#define IR_SUSPEND_CTRL 0xFD10
+#define IR_ERR_TOL_CTRL 0xFD11
+#define IR_UNIT_LEN 0xFD12
+#define IR_ERR_TOL_LEN 0xFD13
+#define IR_MAX_H_TOL_LEN 0xFD14
+#define IR_MAX_L_TOL_LEN 0xFD15
+#define IR_MASK_CTRL 0xFD16
+#define IR_MASK_DATA 0xFD17
+#define IR_RES_MASK_ADDR 0xFD18
+#define IR_RES_MASK_T_LEN 0xFD19
+
+#endif
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index ebb5ed7a7783..21246707fbfb 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -425,6 +425,13 @@ config DVB_CXD2820R
help
Say Y when you want to support this frontend.
+config DVB_RTL2830
+ tristate "Realtek RTL2830 DVB-T"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ Say Y when you want to support this frontend.
+
comment "DVB-C (cable) frontends"
depends on DVB_CORE
@@ -698,6 +705,14 @@ config DVB_IT913X_FE
A DVB-T tuner module.
Say Y when you want to support this frontend.
+config DVB_M88RS2000
+ tristate "M88RS2000 DVB-S demodulator and tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module.
+ Say Y when you want to support this frontend.
+
comment "Tools to develop new frontends"
config DVB_DUMMY_FE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 00a20636df62..86fa808bf589 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -2,8 +2,8 @@
# Makefile for the kernel DVB frontend device drivers.
#
-ccflags-y += -Idrivers/media/dvb/dvb-core/
-ccflags-y += -Idrivers/media/common/tuners/
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core/
+ccflags-y += -I$(srctree)/drivers/media/common/tuners/
stb0899-objs = stb0899_drv.o stb0899_algo.o
stv0900-objs = stv0900_core.o stv0900_sw.o
@@ -96,4 +96,6 @@ obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
obj-$(CONFIG_DVB_A8293) += a8293.o
obj-$(CONFIG_DVB_TDA10071) += tda10071.o
+obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
+obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c
index 2b248c12f404..55b6390198e3 100644
--- a/drivers/media/dvb/frontends/au8522_decoder.c
+++ b/drivers/media/dvb/frontends/au8522_decoder.c
@@ -839,15 +839,4 @@ static struct i2c_driver au8522_driver = {
.id_table = au8522_id,
};
-static __init int init_au8522(void)
-{
- return i2c_add_driver(&au8522_driver);
-}
-
-static __exit void exit_au8522(void)
-{
- i2c_del_driver(&au8522_driver);
-}
-
-module_init(init_au8522);
-module_exit(exit_au8522);
+module_i2c_driver(au8522_driver);
diff --git a/drivers/media/dvb/frontends/au8522_dig.c b/drivers/media/dvb/frontends/au8522_dig.c
index c688b95df486..25f650934c73 100644
--- a/drivers/media/dvb/frontends/au8522_dig.c
+++ b/drivers/media/dvb/frontends/au8522_dig.c
@@ -588,11 +588,6 @@ static int au8522_set_frontend(struct dvb_frontend *fe)
(state->current_modulation == c->modulation))
return 0;
- au8522_enable_modulation(fe, c->modulation);
-
- /* Allow the demod to settle */
- msleep(100);
-
if (fe->ops.tuner_ops.set_params) {
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
@@ -604,6 +599,11 @@ static int au8522_set_frontend(struct dvb_frontend *fe)
if (ret < 0)
return ret;
+ /* Allow the tuner to settle */
+ msleep(100);
+
+ au8522_enable_modulation(fe, c->modulation);
+
state->current_frequency = c->frequency;
return 0;
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c
index faba82485086..edc8eafc5c09 100644
--- a/drivers/media/dvb/frontends/cx22702.c
+++ b/drivers/media/dvb/frontends/cx22702.c
@@ -502,10 +502,26 @@ static int cx22702_read_signal_strength(struct dvb_frontend *fe,
u16 *signal_strength)
{
struct cx22702_state *state = fe->demodulator_priv;
+ u8 reg23;
- u16 rs_ber;
- rs_ber = cx22702_readreg(state, 0x23);
- *signal_strength = (rs_ber << 8) | rs_ber;
+ /*
+ * Experience suggests that the strength signal register works as
+ * follows:
+ * - In the absence of signal, value is 0xff.
+ * - In the presence of a weak signal, bit 7 is set, not sure what
+ * the lower 7 bits mean.
+ * - In the presence of a strong signal, the register holds a 7-bit
+ * value (bit 7 is cleared), with greater values standing for
+ * weaker signals.
+ */
+ reg23 = cx22702_readreg(state, 0x23);
+ if (reg23 & 0x80) {
+ *signal_strength = 0;
+ } else {
+ reg23 = ~reg23 & 0x7f;
+ /* Scale to 16 bit */
+ *signal_strength = (reg23 << 9) | (reg23 << 2) | (reg23 >> 5);
+ }
return 0;
}
diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c
index 224d81e85091..d9fe60b4be48 100644
--- a/drivers/media/dvb/frontends/dib0090.c
+++ b/drivers/media/dvb/frontends/dib0090.c
@@ -519,7 +519,7 @@ static int dib0090_fw_identify(struct dvb_frontend *fe)
return 0;
identification_error:
- return -EIO;;
+ return -EIO;
}
static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg)
diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c
index 863ef3cfab9f..80848b4c15d4 100644
--- a/drivers/media/dvb/frontends/dib9000.c
+++ b/drivers/media/dvb/frontends/dib9000.c
@@ -33,7 +33,7 @@ struct i2c_device {
/* lock */
#define DIB_LOCK struct mutex
-#define DibAcquireLock(lock) do { if (mutex_lock_interruptible(lock) < 0) dprintk("could not get the lock"); } while (0)
+#define DibAcquireLock(lock) mutex_lock_interruptible(lock)
#define DibReleaseLock(lock) mutex_unlock(lock)
#define DibInitLock(lock) mutex_init(lock)
#define DibFreeLock(lock)
@@ -446,7 +446,10 @@ static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u1
if (!state->platform.risc.fw_is_running)
return -EIO;
- DibAcquireLock(&state->platform.risc.mem_lock);
+ if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
dib9000_risc_mem_setup(state, cmd | 0x80);
dib9000_risc_mem_read_chunks(state, b, len);
DibReleaseLock(&state->platform.risc.mem_lock);
@@ -459,7 +462,10 @@ static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8
if (!state->platform.risc.fw_is_running)
return -EIO;
- DibAcquireLock(&state->platform.risc.mem_lock);
+ if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
dib9000_risc_mem_setup(state, cmd);
dib9000_risc_mem_write_chunks(state, b, m->size);
DibReleaseLock(&state->platform.risc.mem_lock);
@@ -531,7 +537,10 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data,
if (!state->platform.risc.fw_is_running)
return -EINVAL;
- DibAcquireLock(&state->platform.risc.mbx_if_lock);
+ if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
tmp = MAX_MAILBOX_TRY;
do {
size = dib9000_read_word_attr(state, 1043, attr) & 0xff;
@@ -593,7 +602,10 @@ static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id,
if (!state->platform.risc.fw_is_running)
return 0;
- DibAcquireLock(&state->platform.risc.mbx_if_lock);
+ if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) {
+ dprintk("could not get the lock");
+ return 0;
+ }
if (risc_id == 1)
mc_base = 16;
else
@@ -701,7 +713,10 @@ static int dib9000_mbx_process(struct dib9000_state *state, u16 attr)
if (!state->platform.risc.fw_is_running)
return -1;
- DibAcquireLock(&state->platform.risc.mbx_lock);
+ if (DibAcquireLock(&state->platform.risc.mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ return -1;
+ }
if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */
ret = dib9000_mbx_fetch_to_cache(state, attr);
@@ -1178,7 +1193,10 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe)
struct dibDVBTChannel *ch;
int ret = 0;
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
ret = -EIO;
goto error;
@@ -1660,7 +1678,10 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2
p[12] = 0;
}
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ return 0;
+ }
dib9000_risc_mem_write(state, FE_MM_W_COMPONENT_ACCESS, p);
@@ -1768,7 +1789,10 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
return 0;
}
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
val = dib9000_read_word(state, 294 + 1) & 0xffef;
val |= (onoff & 0x1) << 4;
@@ -1800,7 +1824,10 @@ int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
return 0;
}
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff);
ret = dib9000_write_word(state, 300 + 1 + id,
onoff ? (1 << 13) | pid : 0);
@@ -1848,7 +1875,10 @@ static int dib9000_sleep(struct dvb_frontend *fe)
u8 index_frontend;
int ret = 0;
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]);
if (ret < 0)
@@ -1874,8 +1904,12 @@ static int dib9000_get_frontend(struct dvb_frontend *fe)
fe_status_t stat;
int ret = 0;
- if (state->get_frontend_internal == 0)
- DibAcquireLock(&state->demod_lock);
+ if (state->get_frontend_internal == 0) {
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
+ }
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
@@ -1978,7 +2012,10 @@ static int dib9000_set_frontend(struct dvb_frontend *fe)
}
state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return 0;
+ }
fe->dtv_property_cache.delivery_system = SYS_DVBT;
@@ -2138,7 +2175,10 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
u8 index_frontend;
u16 lock = 0, lock_slave = 0;
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
lock_slave |= dib9000_read_lock(state->fe[index_frontend]);
@@ -2168,8 +2208,15 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber)
u16 *c;
int ret = 0;
- DibAcquireLock(&state->demod_lock);
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ ret = -EINTR;
+ goto error;
+ }
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
DibReleaseLock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
@@ -2196,7 +2243,10 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
u16 val;
int ret = 0;
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
*strength = 0;
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val);
@@ -2206,8 +2256,13 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
*strength += val;
}
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ ret = -EINTR;
+ goto error;
+ }
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
goto error;
}
@@ -2232,9 +2287,14 @@ static u32 dib9000_get_snr(struct dvb_frontend *fe)
u32 n, s, exp;
u16 val;
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
- return -EIO;
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ return 0;
+ }
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ return 0;
+ }
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
DibReleaseLock(&state->platform.risc.mem_mbx_lock);
@@ -2266,7 +2326,10 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
u8 index_frontend;
u32 snr_master;
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
snr_master = dib9000_get_snr(fe);
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
snr_master += dib9000_get_snr(state->fe[index_frontend]);
@@ -2288,9 +2351,17 @@ static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
u16 *c = (u16 *)state->i2c_read_buffer;
int ret = 0;
- DibAcquireLock(&state->demod_lock);
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ ret = -EINTR;
+ goto error;
+ }
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
goto error;
}
diff --git a/drivers/media/dvb/frontends/drxd_hard.c b/drivers/media/dvb/frontends/drxd_hard.c
index 7bf39cda83c5..f380eb43e9d5 100644
--- a/drivers/media/dvb/frontends/drxd_hard.c
+++ b/drivers/media/dvb/frontends/drxd_hard.c
@@ -101,9 +101,9 @@ struct SCfgAgc {
struct SNoiseCal {
int cpOpt;
- u16 cpNexpOfs;
- u16 tdCal2k;
- u16 tdCal8k;
+ short cpNexpOfs;
+ short tdCal2k;
+ short tdCal8k;
};
enum app_env {
diff --git a/drivers/media/dvb/frontends/drxk.h b/drivers/media/dvb/frontends/drxk.h
index 020981844a86..9d64e4fea066 100644
--- a/drivers/media/dvb/frontends/drxk.h
+++ b/drivers/media/dvb/frontends/drxk.h
@@ -7,15 +7,19 @@
/**
* struct drxk_config - Configure the initial parameters for DRX-K
*
- * adr: I2C Address of the DRX-K
- * parallel_ts: true means that the device uses parallel TS,
+ * @adr: I2C Address of the DRX-K
+ * @parallel_ts: True means that the device uses parallel TS,
* Serial otherwise.
- * single_master: Device is on the single master mode
- * no_i2c_bridge: Don't switch the I2C bridge to talk with tuner
- * antenna_gpio: GPIO bit used to control the antenna
- * antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1
+ * @dynamic_clk: True means that the clock will be dynamically
+ * adjusted. Static clock otherwise.
+ * @enable_merr_cfg: Enable SIO_PDR_PERR_CFG/SIO_PDR_MVAL_CFG.
+ * @single_master: Device is on the single master mode
+ * @no_i2c_bridge: Don't switch the I2C bridge to talk with tuner
+ * @antenna_gpio: GPIO bit used to control the antenna
+ * @antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1
* means that 1=DVBC, 0 = DVBT. Zero means the opposite.
- * microcode_name: Name of the firmware file with the microcode
+ * @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength.
+ * @microcode_name: Name of the firmware file with the microcode
*
* On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is
* UIO-3.
@@ -25,11 +29,14 @@ struct drxk_config {
bool single_master;
bool no_i2c_bridge;
bool parallel_ts;
+ bool dynamic_clk;
+ bool enable_merr_cfg;
bool antenna_dvbt;
u16 antenna_gpio;
- int chunk_size;
+ u8 mpeg_out_clk_strength;
+ int chunk_size;
const char *microcode_name;
};
diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c
index 5ab53795bd7a..36d11756492f 100644
--- a/drivers/media/dvb/frontends/drxk_hard.c
+++ b/drivers/media/dvb/frontends/drxk_hard.c
@@ -90,10 +90,6 @@ bool IsA1WithRomCode(struct drxk_state *state)
#define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03)
#endif
-#ifndef DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH
-#define DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH (0x06)
-#endif
-
#define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700
#define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500
@@ -649,9 +645,6 @@ static int init_state(struct drxk_state *state)
u32 ulQual83 = DEFAULT_MER_83;
u32 ulQual93 = DEFAULT_MER_93;
- u32 ulDVBTStaticTSClock = 1;
- u32 ulDVBCStaticTSClock = 1;
-
u32 ulMpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
u32 ulDemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
@@ -661,7 +654,6 @@ static int init_state(struct drxk_state *state)
u32 ulGPIOCfg = 0x0113;
u32 ulInvertTSClock = 0;
u32 ulTSDataStrength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH;
- u32 ulTSClockkStrength = DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH;
u32 ulDVBTBitrate = 50000000;
u32 ulDVBCBitrate = DRXK_QAM_SYMBOLRATE_MAX * 8;
@@ -814,8 +806,7 @@ static int init_state(struct drxk_state *state)
state->m_invertSTR = false; /* If TRUE; invert STR signals */
state->m_invertVAL = false; /* If TRUE; invert VAL signals */
state->m_invertCLK = (ulInvertTSClock != 0); /* If TRUE; invert CLK signals */
- state->m_DVBTStaticCLK = (ulDVBTStaticTSClock != 0);
- state->m_DVBCStaticCLK = (ulDVBCStaticTSClock != 0);
+
/* If TRUE; static MPEG clockrate will be used;
otherwise clockrate will adapt to the bitrate of the TS */
@@ -823,7 +814,6 @@ static int init_state(struct drxk_state *state)
state->m_DVBCBitrate = ulDVBCBitrate;
state->m_TSDataStrength = (ulTSDataStrength & 0x07);
- state->m_TSClockkStrength = (ulTSClockkStrength & 0x07);
/* Maximum bitrate in b/s in case static clockrate is selected */
state->m_mpegTsStaticBitrate = 19392658;
@@ -1188,6 +1178,7 @@ static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable)
int status = -1;
u16 sioPdrMclkCfg = 0;
u16 sioPdrMdxCfg = 0;
+ u16 err_cfg = 0;
dprintk(1, ": mpeg %s, %s mode\n",
mpegEnable ? "enable" : "disable",
@@ -1253,12 +1244,17 @@ static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable)
status = write16(state, SIO_PDR_MSTRT_CFG__A, sioPdrMdxCfg);
if (status < 0)
goto error;
- status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000); /* Disable */
+
+ if (state->enable_merr_cfg)
+ err_cfg = sioPdrMdxCfg;
+
+ status = write16(state, SIO_PDR_MERR_CFG__A, err_cfg);
if (status < 0)
goto error;
- status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000); /* Disable */
+ status = write16(state, SIO_PDR_MVAL_CFG__A, err_cfg);
if (status < 0)
goto error;
+
if (state->m_enableParallel == true) {
/* paralel -> enable MD1 to MD7 */
status = write16(state, SIO_PDR_MD1_CFG__A, sioPdrMdxCfg);
@@ -6069,9 +6065,7 @@ static int init_drxk(struct drxk_state *state)
if (status < 0)
goto error;
- if (!state->microcode_name)
- load_microcode(state, "drxk_a3.mc");
- else
+ if (state->microcode_name)
load_microcode(state, state->microcode_name);
/* disable token-ring bus through OFDM block for possible ucode upload */
@@ -6322,15 +6316,12 @@ static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_t
switch (p->delivery_system) {
case SYS_DVBC_ANNEX_A:
case SYS_DVBC_ANNEX_C:
+ case SYS_DVBT:
sets->min_delay_ms = 3000;
sets->max_drift = 0;
sets->step_size = 0;
return 0;
default:
- /*
- * For DVB-T, let it use the default DVB core way, that is:
- * fepriv->step_size = fe->ops.info.frequency_stepsize * 2
- */
return -EINVAL;
}
}
@@ -6390,6 +6381,21 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config,
state->antenna_gpio = config->antenna_gpio;
state->antenna_dvbt = config->antenna_dvbt;
state->m_ChunkSize = config->chunk_size;
+ state->enable_merr_cfg = config->enable_merr_cfg;
+
+ if (config->dynamic_clk) {
+ state->m_DVBTStaticCLK = 0;
+ state->m_DVBCStaticCLK = 0;
+ } else {
+ state->m_DVBTStaticCLK = 1;
+ state->m_DVBCStaticCLK = 1;
+ }
+
+
+ if (config->mpeg_out_clk_strength)
+ state->m_TSClockkStrength = config->mpeg_out_clk_strength & 0x07;
+ else
+ state->m_TSClockkStrength = 0x06;
if (config->parallel_ts)
state->m_enableParallel = true;
diff --git a/drivers/media/dvb/frontends/drxk_hard.h b/drivers/media/dvb/frontends/drxk_hard.h
index 3a58b73eb9b9..4bbf841de83a 100644
--- a/drivers/media/dvb/frontends/drxk_hard.h
+++ b/drivers/media/dvb/frontends/drxk_hard.h
@@ -332,6 +332,7 @@ struct drxk_state {
u16 UIO_mask; /* Bits used by UIO */
+ bool enable_merr_cfg;
bool single_master;
bool no_i2c_bridge;
bool antenna_dvbt;
diff --git a/drivers/media/dvb/frontends/it913x-fe-priv.h b/drivers/media/dvb/frontends/it913x-fe-priv.h
index 93b086ea7e1c..eb6fd8aebdb3 100644
--- a/drivers/media/dvb/frontends/it913x-fe-priv.h
+++ b/drivers/media/dvb/frontends/it913x-fe-priv.h
@@ -201,6 +201,11 @@ fe_modulation_t fe_con[] = {
QAM_64,
};
+enum {
+ PRIORITY_HIGH = 0, /* High-priority stream */
+ PRIORITY_LOW, /* Low-priority stream */
+};
+
/* Standard demodulator functions */
static struct it913xset set_solo_fe[] = {
{PRO_LINK, GPIOH5_EN, {0x01}, 0x01},
diff --git a/drivers/media/dvb/frontends/it913x-fe.c b/drivers/media/dvb/frontends/it913x-fe.c
index ccc36bf2deb4..84df03c29179 100644
--- a/drivers/media/dvb/frontends/it913x-fe.c
+++ b/drivers/media/dvb/frontends/it913x-fe.c
@@ -57,6 +57,7 @@ struct it913x_fe_state {
u32 frequency;
fe_modulation_t constellation;
fe_transmit_mode_t transmission_mode;
+ u8 priority;
u32 crystalFrequency;
u32 adcFrequency;
u8 tuner_type;
@@ -500,19 +501,87 @@ static int it913x_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
return 0;
}
+/* FEC values based on fe_code_rate_t non supported values 0*/
+int it913x_qpsk_pval[] = {0, -93, -91, -90, 0, -89, -88};
+int it913x_16qam_pval[] = {0, -87, -85, -84, 0, -83, -82};
+int it913x_64qam_pval[] = {0, -82, -80, -78, 0, -77, -76};
+
+static int it913x_get_signal_strength(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ u8 code_rate;
+ int ret, temp;
+ u8 lna_gain_os;
+
+ ret = it913x_read_reg_u8(state, VAR_P_INBAND);
+ if (ret < 0)
+ return ret;
+
+ /* VHF/UHF gain offset */
+ if (state->frequency < 300000000)
+ lna_gain_os = 7;
+ else
+ lna_gain_os = 14;
+
+ temp = (ret - 100) - lna_gain_os;
+
+ if (state->priority == PRIORITY_HIGH)
+ code_rate = p->code_rate_HP;
+ else
+ code_rate = p->code_rate_LP;
+
+ if (code_rate >= ARRAY_SIZE(it913x_qpsk_pval))
+ return -EINVAL;
+
+ deb_info("Reg VAR_P_INBAND:%d Calc Offset Value:%d", ret, temp);
+
+ /* Apply FEC offset values*/
+ switch (p->modulation) {
+ case QPSK:
+ temp -= it913x_qpsk_pval[code_rate];
+ break;
+ case QAM_16:
+ temp -= it913x_16qam_pval[code_rate];
+ break;
+ case QAM_64:
+ temp -= it913x_64qam_pval[code_rate];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (temp < -15)
+ ret = 0;
+ else if ((-15 <= temp) && (temp < 0))
+ ret = (2 * (temp + 15)) / 3;
+ else if ((0 <= temp) && (temp < 20))
+ ret = 4 * temp + 10;
+ else if ((20 <= temp) && (temp < 35))
+ ret = (2 * (temp - 20)) / 3 + 90;
+ else if (temp >= 35)
+ ret = 100;
+
+ deb_info("Signal Strength :%d", ret);
+
+ return ret;
+}
+
static int it913x_fe_read_signal_strength(struct dvb_frontend *fe,
u16 *strength)
{
struct it913x_fe_state *state = fe->demodulator_priv;
- int ret = it913x_read_reg_u8(state, SIGNAL_LEVEL);
- /*SIGNAL_LEVEL always returns 100%! so using FE_HAS_SIGNAL as switch*/
- if (state->it913x_status & FE_HAS_SIGNAL)
- ret = (ret * 0xff) / 0x64;
- else
- ret = 0x0;
- ret |= ret << 0x8;
- *strength = ret;
- return 0;
+ int ret = 0;
+ if (state->config->read_slevel) {
+ if (state->it913x_status & FE_HAS_SIGNAL)
+ ret = it913x_read_reg_u8(state, SIGNAL_LEVEL);
+ } else
+ ret = it913x_get_signal_strength(fe);
+
+ if (ret >= 0)
+ *strength = (u16)((u32)ret * 0xffff / 0x64);
+
+ return (ret < 0) ? -ENODEV : 0;
}
static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
@@ -606,6 +675,8 @@ static int it913x_fe_get_frontend(struct dvb_frontend *fe)
if (reg[2] < 4)
p->hierarchy = fe_hi[reg[2]];
+ state->priority = reg[5];
+
p->code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE;
p->code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE;
@@ -972,5 +1043,5 @@ static struct dvb_frontend_ops it913x_fe_ofdm_ops = {
MODULE_DESCRIPTION("it913x Frontend and it9137 tuner");
MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
-MODULE_VERSION("1.13");
+MODULE_VERSION("1.15");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/it913x-fe.h b/drivers/media/dvb/frontends/it913x-fe.h
index c4a908e354e0..07fa4594c12b 100644
--- a/drivers/media/dvb/frontends/it913x-fe.h
+++ b/drivers/media/dvb/frontends/it913x-fe.h
@@ -34,6 +34,8 @@ struct ite_config {
u8 tuner_id_1;
u8 dual_mode;
u8 adf;
+ /* option to read SIGNAL_LEVEL */
+ u8 read_slevel;
};
#if defined(CONFIG_DVB_IT913X_FE) || (defined(CONFIG_DVB_IT913X_FE_MODULE) && \
@@ -168,6 +170,8 @@ static inline struct dvb_frontend *it913x_fe_attach(
#define EST_SIGNAL_LEVEL 0x004a
#define FREE_BAND 0x004b
#define SUSPEND_FLAG 0x004c
+#define VAR_P_INBAND 0x00f7
+
/* Build in tuner types */
#define IT9137 0x38
#define IT9135_38 0x38
diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c
index c990d35a13dc..e046622df0e4 100644
--- a/drivers/media/dvb/frontends/lgdt330x.c
+++ b/drivers/media/dvb/frontends/lgdt330x.c
@@ -104,8 +104,8 @@ static int i2c_write_demod_bytes (struct lgdt330x_state* state,
* then reads the data returned for (len) bytes.
*/
-static u8 i2c_read_demod_bytes (struct lgdt330x_state* state,
- enum I2C_REG reg, u8* buf, int len)
+static int i2c_read_demod_bytes(struct lgdt330x_state *state,
+ enum I2C_REG reg, u8 *buf, int len)
{
u8 wr [] = { reg };
struct i2c_msg msg [] = {
@@ -118,6 +118,8 @@ static u8 i2c_read_demod_bytes (struct lgdt330x_state* state,
ret = i2c_transfer(state->i2c, msg, 2);
if (ret != 2) {
printk(KERN_WARNING "lgdt330x: %s: addr 0x%02x select 0x%02x error (ret == %i)\n", __func__, state->config->demod_address, reg, ret);
+ if (ret >= 0)
+ ret = -EIO;
} else {
ret = 0;
}
diff --git a/drivers/media/dvb/frontends/m88rs2000.c b/drivers/media/dvb/frontends/m88rs2000.c
new file mode 100644
index 000000000000..045ee5a6f7ae
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88rs2000.c
@@ -0,0 +1,904 @@
+/*
+ Driver for M88RS2000 demodulator and tuner
+
+ Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
+ Beta Driver
+
+ Include various calculation code from DS3000 driver.
+ Copyright (C) 2009 Konstantin Dimitrov.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#include "dvb_frontend.h"
+#include "m88rs2000.h"
+
+struct m88rs2000_state {
+ struct i2c_adapter *i2c;
+ const struct m88rs2000_config *config;
+ struct dvb_frontend frontend;
+ u8 no_lock_count;
+ u32 tuner_frequency;
+ u32 symbol_rate;
+ fe_code_rate_t fec_inner;
+ u8 tuner_level;
+ int errmode;
+};
+
+static int m88rs2000_debug;
+
+module_param_named(debug, m88rs2000_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define dprintk(level, args...) do { \
+ if (level & m88rs2000_debug) \
+ printk(KERN_DEBUG "m88rs2000-fe: " args); \
+} while (0)
+
+#define deb_info(args...) dprintk(0x01, args)
+#define info(format, arg...) \
+ printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg)
+
+static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner,
+ u8 reg, u8 data)
+{
+ int ret;
+ u8 addr = (tuner == 0) ? state->config->tuner_addr :
+ state->config->demod_addr;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = {
+ .addr = addr,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+ "ret == %i)\n", __func__, reg, data, ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int m88rs2000_demod_write(struct m88rs2000_state *state, u8 reg, u8 data)
+{
+ return m88rs2000_writereg(state, 1, reg, data);
+}
+
+static int m88rs2000_tuner_write(struct m88rs2000_state *state, u8 reg, u8 data)
+{
+ m88rs2000_demod_write(state, 0x81, 0x84);
+ udelay(10);
+ return m88rs2000_writereg(state, 0, reg, data);
+
+}
+
+static int m88rs2000_write(struct dvb_frontend *fe, const u8 buf[], int len)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ if (len != 2)
+ return -EINVAL;
+
+ return m88rs2000_writereg(state, 1, buf[0], buf[1]);
+}
+
+static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ u8 addr = (tuner == 0) ? state->config->tuner_addr :
+ state->config->demod_addr;
+ struct i2c_msg msg[] = {
+ {
+ .addr = addr,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ }, {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+ __func__, reg, ret);
+
+ return b1[0];
+}
+
+static u8 m88rs2000_demod_read(struct m88rs2000_state *state, u8 reg)
+{
+ return m88rs2000_readreg(state, 1, reg);
+}
+
+static u8 m88rs2000_tuner_read(struct m88rs2000_state *state, u8 reg)
+{
+ m88rs2000_demod_write(state, 0x81, 0x85);
+ udelay(10);
+ return m88rs2000_readreg(state, 0, reg);
+}
+
+static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+ u32 temp;
+ u8 b[3];
+
+ if ((srate < 1000000) || (srate > 45000000))
+ return -EINVAL;
+
+ temp = srate / 1000;
+ temp *= 11831;
+ temp /= 68;
+ temp -= 3;
+
+ b[0] = (u8) (temp >> 16) & 0xff;
+ b[1] = (u8) (temp >> 8) & 0xff;
+ b[2] = (u8) temp & 0xff;
+ ret = m88rs2000_demod_write(state, 0x93, b[2]);
+ ret |= m88rs2000_demod_write(state, 0x94, b[1]);
+ ret |= m88rs2000_demod_write(state, 0x95, b[0]);
+
+ deb_info("m88rs2000: m88rs2000_set_symbolrate\n");
+ return ret;
+}
+
+static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ int i;
+ u8 reg;
+ deb_info("%s\n", __func__);
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ reg = m88rs2000_demod_read(state, 0xb2);
+ reg &= 0x3f;
+ m88rs2000_demod_write(state, 0xb2, reg);
+ for (i = 0; i < m->msg_len; i++)
+ m88rs2000_demod_write(state, 0xb3 + i, m->msg[i]);
+
+ reg = m88rs2000_demod_read(state, 0xb1);
+ reg &= 0x87;
+ reg |= ((m->msg_len - 1) << 3) | 0x07;
+ reg &= 0x7f;
+ m88rs2000_demod_write(state, 0xb1, reg);
+
+ for (i = 0; i < 15; i++) {
+ if ((m88rs2000_demod_read(state, 0xb1) & 0x40) == 0x0)
+ break;
+ msleep(20);
+ }
+
+ reg = m88rs2000_demod_read(state, 0xb1);
+ if ((reg & 0x40) > 0x0) {
+ reg &= 0x7f;
+ reg |= 0x40;
+ m88rs2000_demod_write(state, 0xb1, reg);
+ }
+
+ reg = m88rs2000_demod_read(state, 0xb2);
+ reg &= 0x3f;
+ reg |= 0x80;
+ m88rs2000_demod_write(state, 0xb2, reg);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+
+
+ return 0;
+}
+
+static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t burst)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg0, reg1;
+ deb_info("%s\n", __func__);
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ msleep(50);
+ reg0 = m88rs2000_demod_read(state, 0xb1);
+ reg1 = m88rs2000_demod_read(state, 0xb2);
+ /* TODO complete this section */
+ m88rs2000_demod_write(state, 0xb2, reg1);
+ m88rs2000_demod_write(state, 0xb1, reg0);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+
+ return 0;
+}
+
+static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg0, reg1;
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ reg0 = m88rs2000_demod_read(state, 0xb1);
+ reg1 = m88rs2000_demod_read(state, 0xb2);
+
+ reg1 &= 0x3f;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ reg0 |= 0x4;
+ reg0 &= 0xbc;
+ break;
+ case SEC_TONE_OFF:
+ reg1 |= 0x80;
+ break;
+ default:
+ break;
+ }
+ m88rs2000_demod_write(state, 0xb2, reg1);
+ m88rs2000_demod_write(state, 0xb1, reg0);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+ return 0;
+}
+
+struct inittab {
+ u8 cmd;
+ u8 reg;
+ u8 val;
+};
+
+struct inittab m88rs2000_setup[] = {
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {WRITE_DELAY, 0x19, 0x00},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {DEMOD_WRITE, 0x81, 0xc1},
+ {TUNER_WRITE, 0x42, 0x73},
+ {TUNER_WRITE, 0x05, 0x07},
+ {TUNER_WRITE, 0x20, 0x27},
+ {TUNER_WRITE, 0x07, 0x02},
+ {TUNER_WRITE, 0x11, 0xff},
+ {TUNER_WRITE, 0x60, 0xf9},
+ {TUNER_WRITE, 0x08, 0x01},
+ {TUNER_WRITE, 0x00, 0x41},
+ {DEMOD_WRITE, 0x81, 0x81},
+ {DEMOD_WRITE, 0x86, 0xc6},
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0xf0, 0x22},
+ {DEMOD_WRITE, 0xf1, 0xbf},
+ {DEMOD_WRITE, 0xb0, 0x45},
+ {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab m88rs2000_shutdown[] = {
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0xb0, 0x00},
+ {DEMOD_WRITE, 0xf1, 0x89},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {TUNER_WRITE, 0x00, 0x40},
+ {DEMOD_WRITE, 0x81, 0x81},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab tuner_reset[] = {
+ {TUNER_WRITE, 0x42, 0x73},
+ {TUNER_WRITE, 0x05, 0x07},
+ {TUNER_WRITE, 0x20, 0x27},
+ {TUNER_WRITE, 0x07, 0x02},
+ {TUNER_WRITE, 0x11, 0xff},
+ {TUNER_WRITE, 0x60, 0xf9},
+ {TUNER_WRITE, 0x08, 0x01},
+ {TUNER_WRITE, 0x00, 0x41},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab fe_reset[] = {
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0xf1, 0xbf},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x20, 0x81},
+ {DEMOD_WRITE, 0x21, 0x80},
+ {DEMOD_WRITE, 0x10, 0x33},
+ {DEMOD_WRITE, 0x11, 0x44},
+ {DEMOD_WRITE, 0x12, 0x07},
+ {DEMOD_WRITE, 0x18, 0x20},
+ {DEMOD_WRITE, 0x28, 0x04},
+ {DEMOD_WRITE, 0x29, 0x8e},
+ {DEMOD_WRITE, 0x3b, 0xff},
+ {DEMOD_WRITE, 0x32, 0x10},
+ {DEMOD_WRITE, 0x33, 0x02},
+ {DEMOD_WRITE, 0x34, 0x30},
+ {DEMOD_WRITE, 0x35, 0xff},
+ {DEMOD_WRITE, 0x38, 0x50},
+ {DEMOD_WRITE, 0x39, 0x68},
+ {DEMOD_WRITE, 0x3c, 0x7f},
+ {DEMOD_WRITE, 0x3d, 0x0f},
+ {DEMOD_WRITE, 0x45, 0x20},
+ {DEMOD_WRITE, 0x46, 0x24},
+ {DEMOD_WRITE, 0x47, 0x7c},
+ {DEMOD_WRITE, 0x48, 0x16},
+ {DEMOD_WRITE, 0x49, 0x04},
+ {DEMOD_WRITE, 0x4a, 0x01},
+ {DEMOD_WRITE, 0x4b, 0x78},
+ {DEMOD_WRITE, 0X4d, 0xd2},
+ {DEMOD_WRITE, 0x4e, 0x6d},
+ {DEMOD_WRITE, 0x50, 0x30},
+ {DEMOD_WRITE, 0x51, 0x30},
+ {DEMOD_WRITE, 0x54, 0x7b},
+ {DEMOD_WRITE, 0x56, 0x09},
+ {DEMOD_WRITE, 0x58, 0x59},
+ {DEMOD_WRITE, 0x59, 0x37},
+ {DEMOD_WRITE, 0x63, 0xfa},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab fe_trigger[] = {
+ {DEMOD_WRITE, 0x97, 0x04},
+ {DEMOD_WRITE, 0x99, 0x77},
+ {DEMOD_WRITE, 0x9b, 0x64},
+ {DEMOD_WRITE, 0x9e, 0x00},
+ {DEMOD_WRITE, 0x9f, 0xf8},
+ {DEMOD_WRITE, 0xa0, 0x20},
+ {DEMOD_WRITE, 0xa1, 0xe0},
+ {DEMOD_WRITE, 0xa3, 0x38},
+ {DEMOD_WRITE, 0x98, 0xff},
+ {DEMOD_WRITE, 0xc0, 0x0f},
+ {DEMOD_WRITE, 0x89, 0x01},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {WRITE_DELAY, 0x0a, 0x00},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {0xff, 0xaa, 0xff}
+};
+
+static int m88rs2000_tab_set(struct m88rs2000_state *state,
+ struct inittab *tab)
+{
+ int ret = 0;
+ u8 i;
+ if (tab == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < 255; i++) {
+ switch (tab[i].cmd) {
+ case 0x01:
+ ret = m88rs2000_demod_write(state, tab[i].reg,
+ tab[i].val);
+ break;
+ case 0x02:
+ ret = m88rs2000_tuner_write(state, tab[i].reg,
+ tab[i].val);
+ break;
+ case 0x10:
+ if (tab[i].reg > 0)
+ mdelay(tab[i].reg);
+ break;
+ case 0xff:
+ if (tab[i].reg == 0xaa && tab[i].val == 0xff)
+ return 0;
+ case 0x00:
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (ret < 0)
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
+{
+ deb_info("%s: %s\n", __func__,
+ volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+ volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
+
+ return 0;
+}
+
+static int m88rs2000_startup(struct m88rs2000_state *state)
+{
+ int ret = 0;
+ u8 reg;
+
+ reg = m88rs2000_tuner_read(state, 0x00);
+ if ((reg & 0x40) == 0)
+ ret = -ENODEV;
+
+ return ret;
+}
+
+static int m88rs2000_init(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+
+ deb_info("m88rs2000: init chip\n");
+ /* Setup frontend from shutdown/cold */
+ ret = m88rs2000_tab_set(state, m88rs2000_setup);
+
+ return ret;
+}
+
+static int m88rs2000_sleep(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+ /* Shutdown the frondend */
+ ret = m88rs2000_tab_set(state, m88rs2000_shutdown);
+ return ret;
+}
+
+static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg = m88rs2000_demod_read(state, 0x8c);
+
+ *status = 0;
+
+ if ((reg & 0x7) == 0x7) {
+ *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI
+ | FE_HAS_LOCK;
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, CALL_IS_READ);
+ }
+ return 0;
+}
+
+/* Extact code for these unknown but lmedm04 driver uses interupt callbacks */
+
+static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ deb_info("m88rs2000_read_ber %d\n", *ber);
+ *ber = 0;
+ return 0;
+}
+
+static int m88rs2000_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ *strength = 0;
+ return 0;
+}
+
+static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ deb_info("m88rs2000_read_snr %d\n", *snr);
+ *snr = 0;
+ return 0;
+}
+
+static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ deb_info("m88rs2000_read_ber %d\n", *ucblocks);
+ *ucblocks = 0;
+ return 0;
+}
+
+static int m88rs2000_tuner_gate_ctrl(struct m88rs2000_state *state, u8 offset)
+{
+ int ret;
+ ret = m88rs2000_tuner_write(state, 0x51, 0x1f - offset);
+ ret |= m88rs2000_tuner_write(state, 0x51, 0x1f);
+ ret |= m88rs2000_tuner_write(state, 0x50, offset);
+ ret |= m88rs2000_tuner_write(state, 0x50, 0x00);
+ msleep(20);
+ return ret;
+}
+
+static int m88rs2000_set_tuner_rf(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int reg;
+ reg = m88rs2000_tuner_read(state, 0x3d);
+ reg &= 0x7f;
+ if (reg < 0x16)
+ reg = 0xa1;
+ else if (reg == 0x16)
+ reg = 0x99;
+ else
+ reg = 0xf9;
+
+ m88rs2000_tuner_write(state, 0x60, reg);
+ reg = m88rs2000_tuner_gate_ctrl(state, 0x08);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ return reg;
+}
+
+static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+ u32 frequency = c->frequency;
+ s32 offset_khz;
+ s32 tmp;
+ u32 symbol_rate = (c->symbol_rate / 1000);
+ u32 f3db, gdiv28;
+ u16 value, ndiv, lpf_coeff;
+ u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf;
+ u8 lo = 0x01, div4 = 0x0;
+
+ /* Reset Tuner */
+ ret = m88rs2000_tab_set(state, tuner_reset);
+
+ /* Calculate frequency divider */
+ if (frequency < 1060000) {
+ lo |= 0x10;
+ div4 = 0x1;
+ ndiv = (frequency * 14 * 4) / FE_CRYSTAL_KHZ;
+ } else
+ ndiv = (frequency * 14 * 2) / FE_CRYSTAL_KHZ;
+ ndiv = ndiv + ndiv % 2;
+ ndiv = ndiv - 1024;
+
+ ret = m88rs2000_tuner_write(state, 0x10, 0x80 | lo);
+
+ /* Set frequency divider */
+ ret |= m88rs2000_tuner_write(state, 0x01, (ndiv >> 8) & 0xf);
+ ret |= m88rs2000_tuner_write(state, 0x02, ndiv & 0xff);
+
+ ret |= m88rs2000_tuner_write(state, 0x03, 0x06);
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x10);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Tuner Frequency Range */
+ ret = m88rs2000_tuner_write(state, 0x10, lo);
+
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x08);
+
+ /* Tuner RF */
+ ret |= m88rs2000_set_tuner_rf(fe);
+
+ gdiv28 = (FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000;
+ ret |= m88rs2000_tuner_write(state, 0x04, gdiv28 & 0xff);
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x04);
+ if (ret < 0)
+ return -ENODEV;
+
+ value = m88rs2000_tuner_read(state, 0x26);
+
+ f3db = (symbol_rate * 135) / 200 + 2000;
+ f3db += FREQ_OFFSET_LOW_SYM_RATE;
+ if (f3db < 7000)
+ f3db = 7000;
+ if (f3db > 40000)
+ f3db = 40000;
+
+ gdiv28 = gdiv28 * 207 / (value * 2 + 151);
+ mlpf_max = gdiv28 * 135 / 100;
+ mlpf_min = gdiv28 * 78 / 100;
+ if (mlpf_max > 63)
+ mlpf_max = 63;
+
+ lpf_coeff = 2766;
+
+ nlpf = (f3db * gdiv28 * 2 / lpf_coeff /
+ (FE_CRYSTAL_KHZ / 1000) + 1) / 2;
+ if (nlpf > 23)
+ nlpf = 23;
+ if (nlpf < 1)
+ nlpf = 1;
+
+ lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000)
+ * lpf_coeff * 2 / f3db + 1) / 2;
+
+ if (lpf_mxdiv < mlpf_min) {
+ nlpf++;
+ lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000)
+ * lpf_coeff * 2 / f3db + 1) / 2;
+ }
+
+ if (lpf_mxdiv > mlpf_max)
+ lpf_mxdiv = mlpf_max;
+
+ ret = m88rs2000_tuner_write(state, 0x04, lpf_mxdiv);
+ ret |= m88rs2000_tuner_write(state, 0x06, nlpf);
+
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x04);
+
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x01);
+
+ msleep(80);
+ /* calculate offset assuming 96000kHz*/
+ offset_khz = (ndiv - ndiv % 2 + 1024) * FE_CRYSTAL_KHZ
+ / 14 / (div4 + 1) / 2;
+
+ offset_khz -= frequency;
+
+ tmp = offset_khz;
+ tmp *= 65536;
+
+ tmp = (2 * tmp + 96000) / (2 * 96000);
+ if (tmp < 0)
+ tmp += 65536;
+
+ *offset = tmp & 0xffff;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return (ret < 0) ? -EINVAL : 0;
+}
+
+static int m88rs2000_set_fec(struct m88rs2000_state *state,
+ fe_code_rate_t fec)
+{
+ int ret;
+ u16 fec_set;
+ switch (fec) {
+ /* This is not confirmed kept for reference */
+/* case FEC_1_2:
+ fec_set = 0x88;
+ break;
+ case FEC_2_3:
+ fec_set = 0x68;
+ break;
+ case FEC_3_4:
+ fec_set = 0x48;
+ break;
+ case FEC_5_6:
+ fec_set = 0x28;
+ break;
+ case FEC_7_8:
+ fec_set = 0x18;
+ break; */
+ case FEC_AUTO:
+ default:
+ fec_set = 0x08;
+ }
+ ret = m88rs2000_demod_write(state, 0x76, fec_set);
+
+ return 0;
+}
+
+
+static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state)
+{
+ u8 reg;
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ reg = m88rs2000_demod_read(state, 0x76);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+
+ switch (reg) {
+ case 0x88:
+ return FEC_1_2;
+ case 0x68:
+ return FEC_2_3;
+ case 0x48:
+ return FEC_3_4;
+ case 0x28:
+ return FEC_5_6;
+ case 0x18:
+ return FEC_7_8;
+ case 0x08:
+ default:
+ break;
+ }
+
+ return FEC_AUTO;
+}
+
+static int m88rs2000_set_frontend(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ fe_status_t status;
+ int i, ret;
+ u16 offset = 0;
+ u8 reg;
+
+ state->no_lock_count = 0;
+
+ if (c->delivery_system != SYS_DVBS) {
+ deb_info("%s: unsupported delivery "
+ "system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ /* Set Tuner */
+ ret = m88rs2000_set_tuner(fe, &offset);
+ if (ret < 0)
+ return -ENODEV;
+
+ ret = m88rs2000_demod_write(state, 0x9a, 0x30);
+ /* Unknown usually 0xc6 sometimes 0xc1 */
+ reg = m88rs2000_demod_read(state, 0x86);
+ ret |= m88rs2000_demod_write(state, 0x86, reg);
+ /* Offset lower nibble always 0 */
+ ret |= m88rs2000_demod_write(state, 0x9c, (offset >> 8));
+ ret |= m88rs2000_demod_write(state, 0x9d, offset & 0xf0);
+
+
+ /* Reset Demod */
+ ret = m88rs2000_tab_set(state, fe_reset);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Unknown */
+ reg = m88rs2000_demod_read(state, 0x70);
+ ret = m88rs2000_demod_write(state, 0x70, reg);
+
+ /* Set FEC */
+ ret |= m88rs2000_set_fec(state, c->fec_inner);
+ ret |= m88rs2000_demod_write(state, 0x85, 0x1);
+ ret |= m88rs2000_demod_write(state, 0x8a, 0xbf);
+ ret |= m88rs2000_demod_write(state, 0x8d, 0x1e);
+ ret |= m88rs2000_demod_write(state, 0x90, 0xf1);
+ ret |= m88rs2000_demod_write(state, 0x91, 0x08);
+
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Set Symbol Rate */
+ ret = m88rs2000_set_symbolrate(fe, c->symbol_rate);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Set up Demod */
+ ret = m88rs2000_tab_set(state, fe_trigger);
+ if (ret < 0)
+ return -ENODEV;
+
+ for (i = 0; i < 25; i++) {
+ u8 reg = m88rs2000_demod_read(state, 0x8c);
+ if ((reg & 0x7) == 0x7) {
+ status = FE_HAS_LOCK;
+ break;
+ }
+ state->no_lock_count++;
+ if (state->no_lock_count > 15) {
+ reg = m88rs2000_demod_read(state, 0x70);
+ reg ^= 0x4;
+ m88rs2000_demod_write(state, 0x70, reg);
+ state->no_lock_count = 0;
+ }
+ if (state->no_lock_count == 20)
+ m88rs2000_set_tuner_rf(fe);
+ msleep(20);
+ }
+
+ if (status & FE_HAS_LOCK) {
+ state->fec_inner = m88rs2000_get_fec(state);
+ /* Uknown suspect SNR level */
+ reg = m88rs2000_demod_read(state, 0x65);
+ }
+
+ state->tuner_frequency = c->frequency;
+ state->symbol_rate = c->symbol_rate;
+ return 0;
+}
+
+static int m88rs2000_get_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ c->fec_inner = state->fec_inner;
+ c->frequency = state->tuner_frequency;
+ c->symbol_rate = state->symbol_rate;
+ return 0;
+}
+
+static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ if (enable)
+ m88rs2000_demod_write(state, 0x81, 0x84);
+ else
+ m88rs2000_demod_write(state, 0x81, 0x81);
+ udelay(10);
+ return 0;
+}
+
+static void m88rs2000_release(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops m88rs2000_ops = {
+ .delsys = { SYS_DVBS },
+ .info = {
+ .name = "M88RS2000 DVB-S",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1000, /* kHz for QPSK frontends */
+ .frequency_tolerance = 5000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500, /* ppm */
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+ FE_CAN_QPSK |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = m88rs2000_release,
+ .init = m88rs2000_init,
+ .sleep = m88rs2000_sleep,
+ .write = m88rs2000_write,
+ .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl,
+ .read_status = m88rs2000_read_status,
+ .read_ber = m88rs2000_read_ber,
+ .read_signal_strength = m88rs2000_read_signal_strength,
+ .read_snr = m88rs2000_read_snr,
+ .read_ucblocks = m88rs2000_read_ucblocks,
+ .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg,
+ .diseqc_send_burst = m88rs2000_send_diseqc_burst,
+ .set_tone = m88rs2000_set_tone,
+ .set_voltage = m88rs2000_set_voltage,
+
+ .set_frontend = m88rs2000_set_frontend,
+ .get_frontend = m88rs2000_get_frontend,
+};
+
+struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct m88rs2000_state *state = NULL;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->tuner_frequency = 0;
+ state->symbol_rate = 0;
+ state->fec_inner = 0;
+
+ if (m88rs2000_startup(state) < 0)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &m88rs2000_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+
+ return NULL;
+}
+EXPORT_SYMBOL(m88rs2000_attach);
+
+MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.13");
+
diff --git a/drivers/media/dvb/frontends/m88rs2000.h b/drivers/media/dvb/frontends/m88rs2000.h
new file mode 100644
index 000000000000..59acdb696873
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88rs2000.h
@@ -0,0 +1,66 @@
+/*
+ Driver for M88RS2000 demodulator
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef M88RS2000_H
+#define M88RS2000_H
+
+#include
+#include "dvb_frontend.h"
+
+struct m88rs2000_config {
+ /* Demodulator i2c address */
+ u8 demod_addr;
+ /* Tuner address */
+ u8 tuner_addr;
+
+ u8 *inittab;
+
+ /* minimum delay before retuning */
+ int min_delay_ms;
+
+ int (*set_ts_params)(struct dvb_frontend *, int);
+};
+
+enum {
+ CALL_IS_SET_FRONTEND = 0x0,
+ CALL_IS_READ,
+};
+
+#if defined(CONFIG_DVB_M88RS2000) || (defined(CONFIG_DVB_M88RS2000_MODULE) && \
+ defined(MODULE))
+extern struct dvb_frontend *m88rs2000_attach(
+ const struct m88rs2000_config *config, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *m88rs2000_attach(
+ const struct m88rs2000_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_M88RS2000 */
+
+#define FE_CRYSTAL_KHZ 27000
+#define FREQ_OFFSET_LOW_SYM_RATE 3000
+
+enum {
+ DEMOD_WRITE = 0x1,
+ TUNER_WRITE,
+ WRITE_DELAY = 0x10,
+};
+#endif /* M88RS2000_H */
diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c
new file mode 100644
index 000000000000..45196c5b0736
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2830.c
@@ -0,0 +1,562 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+/*
+ * Driver implements own I2C-adapter for tuner I2C access. That's since chip
+ * have unusual I2C-gate control which closes gate automatically after each
+ * I2C transfer. Using own I2C adapter we can workaround that.
+ */
+
+#include "rtl2830_priv.h"
+
+int rtl2830_debug;
+module_param_named(debug, rtl2830_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+/* write multiple hardware registers */
+static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[1+len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1+len,
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple hardware registers */
+static int rtl2830_rd(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = ®,
+ }, {
+ .addr = priv->cfg.i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* write multiple registers */
+static int rtl2830_wr_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
+{
+ int ret;
+ u8 reg2 = (reg >> 0) & 0xff;
+ u8 page = (reg >> 8) & 0xff;
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2830_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+ }
+
+ return rtl2830_wr(priv, reg2, val, len);
+}
+
+/* read multiple registers */
+static int rtl2830_rd_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
+{
+ int ret;
+ u8 reg2 = (reg >> 0) & 0xff;
+ u8 page = (reg >> 8) & 0xff;
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2830_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+ }
+
+ return rtl2830_rd(priv, reg2, val, len);
+}
+
+#if 0 /* currently not used */
+/* write single register */
+static int rtl2830_wr_reg(struct rtl2830_priv *priv, u16 reg, u8 val)
+{
+ return rtl2830_wr_regs(priv, reg, &val, 1);
+}
+#endif
+
+/* read single register */
+static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val)
+{
+ return rtl2830_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return rtl2830_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register with mask */
+int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+
+static int rtl2830_init(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret, i;
+ u64 num;
+ u8 buf[3], tmp;
+ u32 if_ctl;
+ struct rtl2830_reg_val_mask tab[] = {
+ { 0x00d, 0x01, 0x03 },
+ { 0x00d, 0x10, 0x10 },
+ { 0x104, 0x00, 0x1e },
+ { 0x105, 0x80, 0x80 },
+ { 0x110, 0x02, 0x03 },
+ { 0x110, 0x08, 0x0c },
+ { 0x17b, 0x00, 0x40 },
+ { 0x17d, 0x05, 0x0f },
+ { 0x17d, 0x50, 0xf0 },
+ { 0x18c, 0x08, 0x0f },
+ { 0x18d, 0x00, 0xc0 },
+ { 0x188, 0x05, 0x0f },
+ { 0x189, 0x00, 0xfc },
+ { 0x2d5, 0x02, 0x02 },
+ { 0x2f1, 0x02, 0x06 },
+ { 0x2f1, 0x20, 0xf8 },
+ { 0x16d, 0x00, 0x01 },
+ { 0x1a6, 0x00, 0x80 },
+ { 0x106, priv->cfg.vtop, 0x3f },
+ { 0x107, priv->cfg.krf, 0x3f },
+ { 0x112, 0x28, 0xff },
+ { 0x103, priv->cfg.agc_targ_val, 0xff },
+ { 0x00a, 0x02, 0x07 },
+ { 0x140, 0x0c, 0x3c },
+ { 0x140, 0x40, 0xc0 },
+ { 0x15b, 0x05, 0x07 },
+ { 0x15b, 0x28, 0x38 },
+ { 0x15c, 0x05, 0x07 },
+ { 0x15c, 0x28, 0x38 },
+ { 0x115, priv->cfg.spec_inv, 0x01 },
+ { 0x16f, 0x01, 0x07 },
+ { 0x170, 0x18, 0x38 },
+ { 0x172, 0x0f, 0x0f },
+ { 0x173, 0x08, 0x38 },
+ { 0x175, 0x01, 0x07 },
+ { 0x176, 0x00, 0xc0 },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = rtl2830_wr_reg_mask(priv, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret)
+ goto err;
+ }
+
+ ret = rtl2830_wr_regs(priv, 0x18f, "\x28\x00", 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_regs(priv, 0x195,
+ "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8);
+ if (ret)
+ goto err;
+
+ num = priv->cfg.if_dvbt % priv->cfg.xtal;
+ num *= 0x400000;
+ num = div_u64(num, priv->cfg.xtal);
+ num = -num;
+ if_ctl = num & 0x3fffff;
+ dbg("%s: if_ctl=%08x", __func__, if_ctl);
+
+ ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */
+ if (ret)
+ goto err;
+
+ buf[0] = tmp << 6;
+ buf[0] = (if_ctl >> 16) & 0x3f;
+ buf[1] = (if_ctl >> 8) & 0xff;
+ buf[2] = (if_ctl >> 0) & 0xff;
+
+ ret = rtl2830_wr_regs(priv, 0x119, buf, 3);
+ if (ret)
+ goto err;
+
+ /* TODO: spec init */
+
+ /* soft reset */
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x04, 0x04);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x00, 0x04);
+ if (ret)
+ goto err;
+
+ priv->sleeping = false;
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_sleep(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ priv->sleeping = true;
+ return 0;
+}
+
+int rtl2830_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 500;
+ s->step_size = fe->ops.info.frequency_stepsize * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
+
+ return 0;
+}
+
+static int rtl2830_set_frontend(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ static u8 bw_params1[3][34] = {
+ {
+ 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41,
+ 0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a,
+ 0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82,
+ 0x03, 0x73, 0x03, 0xcf, /* 6 MHz */
+ }, {
+ 0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca,
+ 0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca,
+ 0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e,
+ 0x03, 0xd0, 0x04, 0x53, /* 7 MHz */
+ }, {
+ 0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0,
+ 0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a,
+ 0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f,
+ 0x04, 0x24, 0x04, 0xdb, /* 8 MHz */
+ },
+ };
+ static u8 bw_params2[3][6] = {
+ {0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30,}, /* 6 MHz */
+ {0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98,}, /* 7 MHz */
+ {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64,}, /* 8 MHz */
+ };
+
+
+ dbg("%s: frequency=%d bandwidth_hz=%d inversion=%d", __func__,
+ c->frequency, c->bandwidth_hz, c->inversion);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ i = 0;
+ break;
+ case 7000000:
+ i = 1;
+ break;
+ case 8000000:
+ i = 2;
+ break;
+ default:
+ dbg("invalid bandwidth");
+ return -EINVAL;
+ }
+
+ ret = rtl2830_wr_reg_mask(priv, 0x008, i << 1, 0x06);
+ if (ret)
+ goto err;
+
+ /* 1/2 split I2C write */
+ ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17);
+ if (ret)
+ goto err;
+
+ /* 2/2 split I2C write */
+ ret = rtl2830_wr_regs(priv, 0x12d, &bw_params1[i][17], 17);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_regs(priv, 0x19d, bw_params2[i], 6);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+ *status = 0;
+
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2830_rd_reg_mask(priv, 0x351, &tmp, 0x78); /* [6:3] */
+ if (ret)
+ goto err;
+
+ if (tmp == 11) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else if (tmp == 10) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ }
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ *snr = 0;
+ return 0;
+}
+
+static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+ return 0;
+}
+
+static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ *strength = 0;
+ return 0;
+}
+
+static struct dvb_frontend_ops rtl2830_ops;
+
+static u32 rtl2830_tuner_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static int rtl2830_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msg[], int num)
+{
+ struct rtl2830_priv *priv = i2c_get_adapdata(i2c_adap);
+ int ret;
+
+ /* open i2c-gate */
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x08, 0x08);
+ if (ret)
+ goto err;
+
+ ret = i2c_transfer(priv->i2c, msg, num);
+ if (ret < 0)
+ warn("tuner i2c failed=%d", ret);
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static struct i2c_algorithm rtl2830_tuner_i2c_algo = {
+ .master_xfer = rtl2830_tuner_i2c_xfer,
+ .functionality = rtl2830_tuner_i2c_func,
+};
+
+struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ return &priv->tuner_i2c_adapter;
+}
+EXPORT_SYMBOL(rtl2830_get_tuner_i2c_adapter);
+
+static void rtl2830_release(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+
+ i2c_del_adapter(&priv->tuner_i2c_adapter);
+ kfree(priv);
+}
+
+struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ struct rtl2830_priv *priv = NULL;
+ int ret = 0;
+ u8 tmp;
+
+ /* allocate memory for the internal state */
+ priv = kzalloc(sizeof(struct rtl2830_priv), GFP_KERNEL);
+ if (priv == NULL)
+ goto err;
+
+ /* setup the priv */
+ priv->i2c = i2c;
+ memcpy(&priv->cfg, cfg, sizeof(struct rtl2830_config));
+
+ /* check if the demod is there */
+ ret = rtl2830_rd_reg(priv, 0x000, &tmp);
+ if (ret)
+ goto err;
+
+ /* create dvb_frontend */
+ memcpy(&priv->fe.ops, &rtl2830_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ /* create tuner i2c adapter */
+ strlcpy(priv->tuner_i2c_adapter.name, "RTL2830 tuner I2C adapter",
+ sizeof(priv->tuner_i2c_adapter.name));
+ priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo;
+ priv->tuner_i2c_adapter.algo_data = NULL;
+ i2c_set_adapdata(&priv->tuner_i2c_adapter, priv);
+ if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) {
+ err("tuner I2C bus could not be initialized");
+ goto err;
+ }
+
+ priv->sleeping = true;
+
+ return &priv->fe;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(rtl2830_attach);
+
+static struct dvb_frontend_ops rtl2830_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Realtek RTL2830 (DVB-T)",
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = rtl2830_release,
+
+ .init = rtl2830_init,
+ .sleep = rtl2830_sleep,
+
+ .get_tune_settings = rtl2830_get_tune_settings,
+
+ .set_frontend = rtl2830_set_frontend,
+
+ .read_status = rtl2830_read_status,
+ .read_snr = rtl2830_read_snr,
+ .read_ber = rtl2830_read_ber,
+ .read_ucblocks = rtl2830_read_ucblocks,
+ .read_signal_strength = rtl2830_read_signal_strength,
+};
+
+MODULE_AUTHOR("Antti Palosaari ");
+MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/rtl2830.h b/drivers/media/dvb/frontends/rtl2830.h
new file mode 100644
index 000000000000..1c6ee91749c2
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2830.h
@@ -0,0 +1,97 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2830_H
+#define RTL2830_H
+
+#include
+
+struct rtl2830_config {
+ /*
+ * Demodulator I2C address.
+ */
+ u8 i2c_addr;
+
+ /*
+ * Xtal frequency.
+ * Hz
+ * 4000000, 16000000, 25000000, 28800000
+ */
+ u32 xtal;
+
+ /*
+ * TS output mode.
+ */
+ u8 ts_mode;
+
+ /*
+ * Spectrum inversion.
+ */
+ bool spec_inv;
+
+ /*
+ * IFs for all used modes.
+ * Hz
+ * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000
+ */
+ u32 if_dvbt;
+
+ /*
+ */
+ u8 vtop;
+
+ /*
+ */
+ u8 krf;
+
+ /*
+ */
+ u8 agc_targ_val;
+};
+
+#if defined(CONFIG_DVB_RTL2830) || \
+ (defined(CONFIG_DVB_RTL2830_MODULE) && defined(MODULE))
+extern struct dvb_frontend *rtl2830_attach(
+ const struct rtl2830_config *config,
+ struct i2c_adapter *i2c
+);
+
+extern struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
+ struct dvb_frontend *fe
+);
+#else
+static inline struct dvb_frontend *rtl2830_attach(
+ const struct rtl2830_config *config,
+ struct i2c_adapter *i2c
+)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
+ struct dvb_frontend *fe
+)
+{
+ return NULL;
+}
+#endif
+
+#endif /* RTL2830_H */
diff --git a/drivers/media/dvb/frontends/rtl2830_priv.h b/drivers/media/dvb/frontends/rtl2830_priv.h
new file mode 100644
index 000000000000..4a464761b5b8
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2830_priv.h
@@ -0,0 +1,57 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2830_PRIV_H
+#define RTL2830_PRIV_H
+
+#include "dvb_frontend.h"
+#include "rtl2830.h"
+
+#define LOG_PREFIX "rtl2830"
+
+#undef dbg
+#define dbg(f, arg...) \
+ if (rtl2830_debug) \
+ printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+struct rtl2830_priv {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct rtl2830_config cfg;
+ struct i2c_adapter tuner_i2c_adapter;
+
+ bool sleeping;
+
+ u8 page; /* active register page */
+};
+
+struct rtl2830_reg_val_mask {
+ u16 reg;
+ u8 val;
+ u8 mask;
+};
+
+#endif /* RTL2830_PRIV_H */
diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c
index 38565beafe23..dd08f4ac64a8 100644
--- a/drivers/media/dvb/frontends/stb0899_drv.c
+++ b/drivers/media/dvb/frontends/stb0899_drv.c
@@ -67,7 +67,7 @@ static const struct stb0899_tab stb0899_cn_tab[] = {
* Crude linear extrapolation below -84.8dBm and above -8.0dBm.
*/
static const struct stb0899_tab stb0899_dvbsrf_tab[] = {
- { -950, -128 },
+ { -750, -128 },
{ -748, -94 },
{ -745, -92 },
{ -735, -90 },
@@ -131,7 +131,7 @@ static const struct stb0899_tab stb0899_dvbs2rf_tab[] = {
{ -730, 13645 },
{ -750, 13909 },
{ -766, 14153 },
- { -999, 16383 }
+ { -950, 16383 }
};
/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/
@@ -964,6 +964,7 @@ static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
int val;
u32 reg;
+ *strength = 0;
switch (state->delsys) {
case SYS_DVBS:
case SYS_DSS:
@@ -983,11 +984,11 @@ static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
break;
case SYS_DVBS2:
if (internal->lock) {
- reg = STB0899_READ_S2REG(STB0899_DEMOD, IF_AGC_GAIN);
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_GAIN);
val = STB0899_GETFIELD(IF_AGC_GAIN, reg);
*strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val);
- *strength += 750;
+ *strength += 950;
dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm",
val & 0x3fff, *strength);
}
@@ -1009,6 +1010,7 @@ static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr)
u8 buf[2];
u32 reg;
+ *snr = 0;
reg = stb0899_read_reg(state, STB0899_VSTATUS);
switch (state->delsys) {
case SYS_DVBS:
@@ -1071,7 +1073,7 @@ static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status)
reg = stb0899_read_reg(state, STB0899_VSTATUS);
if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK");
- *status |= FE_HAS_CARRIER | FE_HAS_LOCK;
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
reg = stb0899_read_reg(state, STB0899_PLPARM);
if (STB0899_GETFIELD(VITCURPUN, reg)) {
diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c
index fb5548a82208..632b25156e4c 100644
--- a/drivers/media/dvb/frontends/stv0288.c
+++ b/drivers/media/dvb/frontends/stv0288.c
@@ -506,7 +506,7 @@ static int stv0288_set_frontend(struct dvb_frontend *fe)
tda[1] = (unsigned char)tm;
stv0288_writeregI(state, 0x2b, tda[1]);
stv0288_writeregI(state, 0x2c, tda[2]);
- udelay(30);
+ msleep(30);
}
state->tuner_frequency = c->frequency;
state->fec_inner = FEC_AUTO;
diff --git a/drivers/media/dvb/frontends/tda10071.c b/drivers/media/dvb/frontends/tda10071.c
index a99205026751..c21bc92d2811 100644
--- a/drivers/media/dvb/frontends/tda10071.c
+++ b/drivers/media/dvb/frontends/tda10071.c
@@ -1215,7 +1215,7 @@ error:
EXPORT_SYMBOL(tda10071_attach);
static struct dvb_frontend_ops tda10071_ops = {
- .delsys = { SYS_DVBT, SYS_DVBT2 },
+ .delsys = { SYS_DVBS, SYS_DVBS2 },
.info = {
.name = "NXP TDA10071",
.frequency_min = 950000,
diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c
index 8418c02bcefe..7539a5d71029 100644
--- a/drivers/media/dvb/ngene/ngene-cards.c
+++ b/drivers/media/dvb/ngene/ngene-cards.c
@@ -216,6 +216,7 @@ static int demod_attach_drxk(struct ngene_channel *chan,
struct drxk_config config;
memset(&config, 0, sizeof(config));
+ config.microcode_name = "drxk_a3.mc";
config.adr = 0x29 + (chan->number ^ 2);
chan->fe = dvb_attach(drxk_attach, &config, i2c);
diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c
index b81df5fafe26..15b35c4725f1 100644
--- a/drivers/media/dvb/pt1/pt1.c
+++ b/drivers/media/dvb/pt1/pt1.c
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
#include "dvbdev.h"
#include "dvb_demux.h"
@@ -77,6 +78,8 @@ struct pt1 {
struct pt1_adapter *adaps[PT1_NR_ADAPS];
struct pt1_table *tables;
struct task_struct *kthread;
+ int table_index;
+ int buf_index;
struct mutex lock;
int power;
@@ -90,12 +93,12 @@ struct pt1_adapter {
u8 *buf;
int upacket_count;
int packet_count;
+ int st_count;
struct dvb_adapter adap;
struct dvb_demux demux;
int users;
struct dmxdev dmxdev;
- struct dvb_net net;
struct dvb_frontend *fe;
int (*orig_set_voltage)(struct dvb_frontend *fe,
fe_sec_voltage_t voltage);
@@ -119,7 +122,7 @@ static u32 pt1_read_reg(struct pt1 *pt1, int reg)
return readl(pt1->regs + reg * 4);
}
-static int pt1_nr_tables = 64;
+static int pt1_nr_tables = 8;
module_param_named(nr_tables, pt1_nr_tables, int, 0);
static void pt1_increment_table_count(struct pt1 *pt1)
@@ -264,6 +267,7 @@ static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page)
struct pt1_adapter *adap;
int offset;
u8 *buf;
+ int sc;
if (!page->upackets[PT1_NR_UPACKETS - 1])
return 0;
@@ -280,6 +284,16 @@ static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page)
else if (!adap->upacket_count)
continue;
+ if (upacket >> 24 & 1)
+ printk_ratelimited(KERN_INFO "earth-pt1: device "
+ "buffer overflowing. table[%d] buf[%d]\n",
+ pt1->table_index, pt1->buf_index);
+ sc = upacket >> 26 & 0x7;
+ if (adap->st_count != -1 && sc != ((adap->st_count + 1) & 0x7))
+ printk_ratelimited(KERN_INFO "earth-pt1: data loss"
+ " in streamID(adapter)[%d]\n", index);
+ adap->st_count = sc;
+
buf = adap->buf;
offset = adap->packet_count * 188 + adap->upacket_count * 3;
buf[offset] = upacket >> 16;
@@ -303,30 +317,25 @@ static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page)
static int pt1_thread(void *data)
{
struct pt1 *pt1;
- int table_index;
- int buf_index;
struct pt1_buffer_page *page;
pt1 = data;
set_freezable();
- table_index = 0;
- buf_index = 0;
-
while (!kthread_should_stop()) {
try_to_freeze();
- page = pt1->tables[table_index].bufs[buf_index].page;
+ page = pt1->tables[pt1->table_index].bufs[pt1->buf_index].page;
if (!pt1_filter(pt1, page)) {
schedule_timeout_interruptible((HZ + 999) / 1000);
continue;
}
- if (++buf_index >= PT1_NR_BUFS) {
+ if (++pt1->buf_index >= PT1_NR_BUFS) {
pt1_increment_table_count(pt1);
- buf_index = 0;
- if (++table_index >= pt1_nr_tables)
- table_index = 0;
+ pt1->buf_index = 0;
+ if (++pt1->table_index >= pt1_nr_tables)
+ pt1->table_index = 0;
}
}
@@ -477,21 +486,60 @@ err:
return ret;
}
+static int pt1_start_polling(struct pt1 *pt1)
+{
+ int ret = 0;
+
+ mutex_lock(&pt1->lock);
+ if (!pt1->kthread) {
+ pt1->kthread = kthread_run(pt1_thread, pt1, "earth-pt1");
+ if (IS_ERR(pt1->kthread)) {
+ ret = PTR_ERR(pt1->kthread);
+ pt1->kthread = NULL;
+ }
+ }
+ mutex_unlock(&pt1->lock);
+ return ret;
+}
+
static int pt1_start_feed(struct dvb_demux_feed *feed)
{
struct pt1_adapter *adap;
adap = container_of(feed->demux, struct pt1_adapter, demux);
- if (!adap->users++)
+ if (!adap->users++) {
+ int ret;
+
+ ret = pt1_start_polling(adap->pt1);
+ if (ret)
+ return ret;
pt1_set_stream(adap->pt1, adap->index, 1);
+ }
return 0;
}
+static void pt1_stop_polling(struct pt1 *pt1)
+{
+ int i, count;
+
+ mutex_lock(&pt1->lock);
+ for (i = 0, count = 0; i < PT1_NR_ADAPS; i++)
+ count += pt1->adaps[i]->users;
+
+ if (count == 0 && pt1->kthread) {
+ kthread_stop(pt1->kthread);
+ pt1->kthread = NULL;
+ }
+ mutex_unlock(&pt1->lock);
+}
+
static int pt1_stop_feed(struct dvb_demux_feed *feed)
{
struct pt1_adapter *adap;
adap = container_of(feed->demux, struct pt1_adapter, demux);
- if (!--adap->users)
+ if (!--adap->users) {
pt1_set_stream(adap->pt1, adap->index, 0);
+ pt1_stop_polling(adap->pt1);
+ }
return 0;
}
@@ -575,7 +623,6 @@ static int pt1_wakeup(struct dvb_frontend *fe)
static void pt1_free_adapter(struct pt1_adapter *adap)
{
- dvb_net_release(&adap->net);
adap->demux.dmx.close(&adap->demux.dmx);
dvb_dmxdev_release(&adap->dmxdev);
dvb_dmx_release(&adap->demux);
@@ -616,6 +663,7 @@ pt1_alloc_adapter(struct pt1 *pt1)
adap->buf = buf;
adap->upacket_count = 0;
adap->packet_count = 0;
+ adap->st_count = -1;
dvb_adap = &adap->adap;
dvb_adap->priv = adap;
@@ -644,8 +692,6 @@ pt1_alloc_adapter(struct pt1 *pt1)
if (ret < 0)
goto err_dmx_release;
- dvb_net_init(dvb_adap, &adap->net, &demux->dmx);
-
return adap;
err_dmx_release:
@@ -1020,7 +1066,8 @@ static void __devexit pt1_remove(struct pci_dev *pdev)
pt1 = pci_get_drvdata(pdev);
regs = pt1->regs;
- kthread_stop(pt1->kthread);
+ if (pt1->kthread)
+ kthread_stop(pt1->kthread);
pt1_cleanup_tables(pt1);
pt1_cleanup_frontends(pt1);
pt1_disable_ram(pt1);
@@ -1043,7 +1090,6 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
void __iomem *regs;
struct pt1 *pt1;
struct i2c_adapter *i2c_adap;
- struct task_struct *kthread;
ret = pci_enable_device(pdev);
if (ret < 0)
@@ -1139,17 +1185,8 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret < 0)
goto err_pt1_cleanup_frontends;
- kthread = kthread_run(pt1_thread, pt1, "pt1");
- if (IS_ERR(kthread)) {
- ret = PTR_ERR(kthread);
- goto err_pt1_cleanup_tables;
- }
-
- pt1->kthread = kthread;
return 0;
-err_pt1_cleanup_tables:
- pt1_cleanup_tables(pt1);
err_pt1_cleanup_frontends:
pt1_cleanup_frontends(pt1);
err_pt1_disable_ram:
diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
index 7b42ace419d9..421cf73858d3 100644
--- a/drivers/media/media-devnode.c
+++ b/drivers/media/media-devnode.c
@@ -312,7 +312,7 @@ static void __exit media_devnode_exit(void)
unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
}
-module_init(media_devnode_init)
+subsys_initcall(media_devnode_init);
module_exit(media_devnode_exit)
MODULE_AUTHOR("Laurent Pinchart ");
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index e954781c90bf..8db2d7f4b52a 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -43,7 +43,7 @@ config USB_DSBR
config RADIO_MAXIRADIO
tristate "Guillemot MAXI Radio FM 2000 radio"
- depends on VIDEO_V4L2 && PCI
+ depends on VIDEO_V4L2 && PCI && SND
---help---
Choose Y here if you have this radio card. This card may also be
found as Gemtek PCI FM.
@@ -80,6 +80,16 @@ config RADIO_SI4713
To compile this driver as a module, choose M here: the
module will be called radio-si4713.
+config USB_KEENE
+ tristate "Keene FM Transmitter USB support"
+ depends on USB && VIDEO_V4L2
+ ---help---
+ Say Y here if you want to connect this type of FM transmitter
+ to your computer's USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-keene.
+
config RADIO_TEA5764
tristate "TEA5764 I2C FM radio support"
depends on I2C && VIDEO_V4L2
@@ -167,6 +177,10 @@ menuconfig V4L_RADIO_ISA_DRIVERS
if V4L_RADIO_ISA_DRIVERS
+config RADIO_ISA
+ depends on ISA
+ tristate
+
config RADIO_CADET
tristate "ADS Cadet AM/FM Tuner"
depends on ISA && VIDEO_V4L2
@@ -174,20 +188,13 @@ config RADIO_CADET
Choose Y here if you have one of these AM/FM radio cards, and then
fill in the port address below.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- .
-
- Further documentation on this driver can be found on the WWW at
- .
-
To compile this driver as a module, choose M here: the
module will be called radio-cadet.
config RADIO_RTRACK
tristate "AIMSlab RadioTrack (aka RadioReveal) support"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address below.
@@ -201,11 +208,7 @@ config RADIO_RTRACK
You must also pass the module a suitable io parameter, 0x248 has
been reported to be used by these cards.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- . More information is
- contained in the file
+ More information is contained in the file
.
To compile this driver as a module, choose M here: the
@@ -214,7 +217,7 @@ config RADIO_RTRACK
config RADIO_RTRACK_PORT
hex "RadioTrack i/o port (0x20f or 0x30f)"
depends on RADIO_RTRACK=y
- default "20f"
+ default "30f"
help
Enter either 0x30f or 0x20f here. The card default is 0x30f, if you
haven't changed the jumper setting on the card.
@@ -222,14 +225,14 @@ config RADIO_RTRACK_PORT
config RADIO_RTRACK2
tristate "AIMSlab RadioTrack II support"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have this FM radio card, and then fill in the
port address below.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- .
+ Note: this driver hasn't been tested since a long time due to lack
+ of hardware. If you have this hardware, then please contact the
+ linux-media mailinglist.
To compile this driver as a module, choose M here: the
module will be called radio-rtrack2.
@@ -245,15 +248,11 @@ config RADIO_RTRACK2_PORT
config RADIO_AZTECH
tristate "Aztech/Packard Bell Radio"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address below.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- .
-
To compile this driver as a module, choose M here: the
module will be called radio-aztech.
@@ -269,6 +268,7 @@ config RADIO_AZTECH_PORT
config RADIO_GEMTEK
tristate "GemTek Radio card (or compatible) support"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have this FM radio card, and then fill in the
I/O port address and settings below. The following cards either have
@@ -278,23 +278,21 @@ config RADIO_GEMTEK
- Typhoon Radio card (some models)
- Hama Radio card
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- .
-
To compile this driver as a module, choose M here: the
module will be called radio-gemtek.
config RADIO_GEMTEK_PORT
- hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0c24c or 0x28c)"
+ hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c)"
depends on RADIO_GEMTEK=y
default "34c"
help
- Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is
- 0x34c, if you haven't changed the jumper setting on the card. On
- Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O
+ Enter either 0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c here. The
+ card default is 0x34c, if you haven't changed the jumper setting
+ on the card.
+
+ On Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O
port is 0x20c, 0x248 or 0x28c.
+
If automatic I/O port probing is enabled this port will be used only
in case of automatic probing failure, ie. as a fallback.
@@ -318,11 +316,6 @@ config RADIO_MIROPCM20
sound card driver "Miro miroSOUND PCM1pro/PCM12/PCM20radio" as this
is required for the radio-miropcm20.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- .
-
To compile this driver as a module, choose M here: the
module will be called radio-miropcm20.
@@ -332,11 +325,6 @@ config RADIO_SF16FMI
---help---
Choose Y here if you have one of these FM radio cards.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- .
-
To compile this driver as a module, choose M here: the
module will be called radio-sf16fmi.
@@ -346,50 +334,35 @@ config RADIO_SF16FMR2
---help---
Choose Y here if you have one of these FM radio cards.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found on the WWW at
- .
-
To compile this driver as a module, choose M here: the
module will be called radio-sf16fmr2.
config RADIO_TERRATEC
tristate "TerraTec ActiveRadio ISA Standalone"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
- Choose Y here if you have this FM radio card, and then fill in the
- port address below. (TODO)
+ Choose Y here if you have this FM radio card.
- Note: This driver is in its early stages. Right now volume and
- frequency control and muting works at least for me, but
- unfortunately I have not found anybody who wants to use this card
- with Linux. So if it is this what YOU are trying to do right now,
- PLEASE DROP ME A NOTE!! Rolf Offermanns .
-
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- .
+ Note: this driver hasn't been tested since a long time due to lack
+ of hardware. If you have this hardware, then please contact the
+ linux-media mailinglist.
To compile this driver as a module, choose M here: the
module will be called radio-terratec.
-config RADIO_TERRATEC_PORT
- hex "Terratec i/o port (normally 0x590)"
- depends on RADIO_TERRATEC=y
- default "590"
- help
- Fill in the I/O port of your TerraTec FM radio card. If unsure, go
- with the default.
-
config RADIO_TRUST
tristate "Trust FM radio card"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
help
This is a driver for the Trust FM radio cards. Say Y if you have
such a card and want to use it under Linux.
+ Note: this driver hasn't been tested since a long time due to lack
+ of hardware. If you have this hardware, then please contact the
+ linux-media mailinglist.
+
To compile this driver as a module, choose M here: the
module will be called radio-trust.
@@ -404,14 +377,14 @@ config RADIO_TRUST_PORT
config RADIO_TYPHOON
tristate "Typhoon Radio (a.k.a. EcoRadio)"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address and the frequency used for muting below.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- .
+ Note: this driver hasn't been tested since a long time due to lack
+ of hardware. If you have this hardware, then please contact the
+ linux-media mailinglist.
To compile this driver as a module, choose M here: the
module will be called radio-typhoon.
@@ -438,14 +411,14 @@ config RADIO_TYPHOON_MUTEFREQ
config RADIO_ZOLTRIX
tristate "Zoltrix Radio"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address below.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- .
+ Note: this driver hasn't been tested since a long time due to lack
+ of hardware. If you have this hardware, then please contact the
+ linux-media mailinglist.
To compile this driver as a module, choose M here: the
module will be called radio-zoltrix.
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 390daf94d847..ca8c7d134b95 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -2,6 +2,7 @@
# Makefile for the kernel character device drivers.
#
+obj-$(CONFIG_RADIO_ISA) += radio-isa.o
obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o
obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o
obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o
@@ -20,6 +21,7 @@ obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
obj-$(CONFIG_RADIO_SI470X) += si470x/
obj-$(CONFIG_USB_MR800) += radio-mr800.o
+obj-$(CONFIG_USB_KEENE) += radio-keene.o
obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o
obj-$(CONFIG_RADIO_TEF6862) += tef6862.o
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 1c3f8440a55c..98e0c8c20312 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -1,16 +1,13 @@
-/* radiotrack (radioreveal) driver for Linux radio support
- * (c) 1997 M. Kirkwood
+/*
+ * AimsLab RadioTrack (aka RadioVeveal) driver
+ *
+ * Copyright 1997 M. Kirkwood
+ *
+ * Converted to the radio-isa framework by Hans Verkuil
* Converted to V4L2 API by Mauro Carvalho Chehab
* Converted to new API by Alan Cox
* Various bugfixes and enhancements by Russell Kroll
*
- * History:
- * 1999-02-24 Russell Kroll
- * Fine tuning/VIDEO_TUNER_LOW
- * Frequency range expanded to start at 87 MHz
- *
- * TODO: Allow for more than one of these foolish entities :-)
- *
* Notes on the hardware (reverse engineered from other peoples'
* reverse engineering of AIMS' code :-)
*
@@ -26,6 +23,7 @@
* wait(a_wee_while);
* out(port, stop_changing_the_volume);
*
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
*/
#include /* Modules */
@@ -34,401 +32,179 @@
#include /* msleep */
#include /* kernel radio structs */
#include /* outb, outb_p */
+#include
#include
#include
+#include
+#include "radio-isa.h"
-MODULE_AUTHOR("M.Kirkwood");
+MODULE_AUTHOR("M. Kirkwood");
MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
+MODULE_VERSION("1.0.0");
#ifndef CONFIG_RADIO_RTRACK_PORT
#define CONFIG_RADIO_RTRACK_PORT -1
#endif
-static int io = CONFIG_RADIO_RTRACK_PORT;
-static int radio_nr = -1;
+#define RTRACK_MAX 2
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
-module_param(radio_nr, int, 0);
+static int io[RTRACK_MAX] = { [0] = CONFIG_RADIO_RTRACK_PORT,
+ [1 ... (RTRACK_MAX - 1)] = -1 };
+static int radio_nr[RTRACK_MAX] = { [0 ... (RTRACK_MAX - 1)] = -1 };
-struct rtrack
-{
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int port;
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the RadioTrack card (0x20f or 0x30f)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
+
+struct rtrack {
+ struct radio_isa_card isa;
int curvol;
- unsigned long curfreq;
- int muted;
- int io;
- struct mutex lock;
};
-static struct rtrack rtrack_card;
-
-/* local things */
-
-static void rt_decvol(struct rtrack *rt)
+static struct radio_isa_card *rtrack_alloc(void)
{
- outb(0x58, rt->io); /* volume down + sigstr + on */
- msleep(100);
- outb(0xd8, rt->io); /* volume steady + sigstr + on */
+ struct rtrack *rt = kzalloc(sizeof(struct rtrack), GFP_KERNEL);
+
+ if (rt)
+ rt->curvol = 0xff;
+ return rt ? &rt->isa : NULL;
}
-static void rt_incvol(struct rtrack *rt)
-{
- outb(0x98, rt->io); /* volume up + sigstr + on */
- msleep(100);
- outb(0xd8, rt->io); /* volume steady + sigstr + on */
-}
-
-static void rt_mute(struct rtrack *rt)
-{
- rt->muted = 1;
- mutex_lock(&rt->lock);
- outb(0xd0, rt->io); /* volume steady, off */
- mutex_unlock(&rt->lock);
-}
-
-static int rt_setvol(struct rtrack *rt, int vol)
-{
- int i;
-
- mutex_lock(&rt->lock);
-
- if (vol == rt->curvol) { /* requested volume = current */
- if (rt->muted) { /* user is unmuting the card */
- rt->muted = 0;
- outb(0xd8, rt->io); /* enable card */
- }
- mutex_unlock(&rt->lock);
- return 0;
- }
-
- if (vol == 0) { /* volume = 0 means mute the card */
- outb(0x48, rt->io); /* volume down but still "on" */
- msleep(2000); /* make sure it's totally down */
- outb(0xd0, rt->io); /* volume steady, off */
- rt->curvol = 0; /* track the volume state! */
- mutex_unlock(&rt->lock);
- return 0;
- }
-
- rt->muted = 0;
- if (vol > rt->curvol)
- for (i = rt->curvol; i < vol; i++)
- rt_incvol(rt);
- else
- for (i = rt->curvol; i > vol; i--)
- rt_decvol(rt);
-
- rt->curvol = vol;
- mutex_unlock(&rt->lock);
- return 0;
-}
-
-/* the 128+64 on these outb's is to keep the volume stable while tuning
- * without them, the volume _will_ creep up with each frequency change
- * and bit 4 (+16) is to keep the signal strength meter enabled
+/* The 128+64 on these outb's is to keep the volume stable while tuning.
+ * Without them, the volume _will_ creep up with each frequency change
+ * and bit 4 (+16) is to keep the signal strength meter enabled.
*/
-static void send_0_byte(struct rtrack *rt)
+static void send_0_byte(struct radio_isa_card *isa, int on)
{
- if (rt->curvol == 0 || rt->muted) {
- outb_p(128+64+16+ 1, rt->io); /* wr-enable + data low */
- outb_p(128+64+16+2+1, rt->io); /* clock */
- }
- else {
- outb_p(128+64+16+8+ 1, rt->io); /* on + wr-enable + data low */
- outb_p(128+64+16+8+2+1, rt->io); /* clock */
- }
+ outb_p(128+64+16+on+1, isa->io); /* wr-enable + data low */
+ outb_p(128+64+16+on+2+1, isa->io); /* clock */
msleep(1);
}
-static void send_1_byte(struct rtrack *rt)
+static void send_1_byte(struct radio_isa_card *isa, int on)
{
- if (rt->curvol == 0 || rt->muted) {
- outb_p(128+64+16+4 +1, rt->io); /* wr-enable+data high */
- outb_p(128+64+16+4+2+1, rt->io); /* clock */
- }
- else {
- outb_p(128+64+16+8+4 +1, rt->io); /* on+wr-enable+data high */
- outb_p(128+64+16+8+4+2+1, rt->io); /* clock */
- }
-
+ outb_p(128+64+16+on+4+1, isa->io); /* wr-enable+data high */
+ outb_p(128+64+16+on+4+2+1, isa->io); /* clock */
msleep(1);
}
-static int rt_setfreq(struct rtrack *rt, unsigned long freq)
+static int rtrack_s_frequency(struct radio_isa_card *isa, u32 freq)
{
+ int on = v4l2_ctrl_g_ctrl(isa->mute) ? 0 : 8;
int i;
- mutex_lock(&rt->lock); /* Stop other ops interfering */
-
- rt->curfreq = freq;
-
- /* now uses VIDEO_TUNER_LOW for fine tuning */
-
freq += 171200; /* Add 10.7 MHz IF */
freq /= 800; /* Convert to 50 kHz units */
- send_0_byte(rt); /* 0: LSB of frequency */
+ send_0_byte(isa, on); /* 0: LSB of frequency */
for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
if (freq & (1 << i))
- send_1_byte(rt);
+ send_1_byte(isa, on);
else
- send_0_byte(rt);
+ send_0_byte(isa, on);
- send_0_byte(rt); /* 14: test bit - always 0 */
- send_0_byte(rt); /* 15: test bit - always 0 */
+ send_0_byte(isa, on); /* 14: test bit - always 0 */
+ send_0_byte(isa, on); /* 15: test bit - always 0 */
- send_0_byte(rt); /* 16: band data 0 - always 0 */
- send_0_byte(rt); /* 17: band data 1 - always 0 */
- send_0_byte(rt); /* 18: band data 2 - always 0 */
- send_0_byte(rt); /* 19: time base - always 0 */
+ send_0_byte(isa, on); /* 16: band data 0 - always 0 */
+ send_0_byte(isa, on); /* 17: band data 1 - always 0 */
+ send_0_byte(isa, on); /* 18: band data 2 - always 0 */
+ send_0_byte(isa, on); /* 19: time base - always 0 */
- send_0_byte(rt); /* 20: spacing (0 = 25 kHz) */
- send_1_byte(rt); /* 21: spacing (1 = 25 kHz) */
- send_0_byte(rt); /* 22: spacing (0 = 25 kHz) */
- send_1_byte(rt); /* 23: AM/FM (FM = 1, always) */
-
- if (rt->curvol == 0 || rt->muted)
- outb(0xd0, rt->io); /* volume steady + sigstr */
- else
- outb(0xd8, rt->io); /* volume steady + sigstr + on */
-
- mutex_unlock(&rt->lock);
+ send_0_byte(isa, on); /* 20: spacing (0 = 25 kHz) */
+ send_1_byte(isa, on); /* 21: spacing (1 = 25 kHz) */
+ send_0_byte(isa, on); /* 22: spacing (0 = 25 kHz) */
+ send_1_byte(isa, on); /* 23: AM/FM (FM = 1, always) */
+ outb(0xd0 + on, isa->io); /* volume steady + sigstr */
return 0;
}
-static int rt_getsigstr(struct rtrack *rt)
+static u32 rtrack_g_signal(struct radio_isa_card *isa)
{
- int sig = 1;
-
- mutex_lock(&rt->lock);
- if (inb(rt->io) & 2) /* bit set = no signal present */
- sig = 0;
- mutex_unlock(&rt->lock);
- return sig;
+ /* bit set = no signal present */
+ return 0xffff * !(inb(isa->io) & 2);
}
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
+static int rtrack_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
- strlcpy(v->driver, "radio-aimslab", sizeof(v->driver));
- strlcpy(v->card, "RadioTrack", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
+ struct rtrack *rt = container_of(isa, struct rtrack, isa);
+ int curvol = rt->curvol;
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct rtrack *rt = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 87 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xffff * rt_getsigstr(rt);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct rtrack *rt = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- rt_setfreq(rt, f->frequency);
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct rtrack *rt = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = rt->curfreq;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct rtrack *rt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = rt->muted;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = rt->curvol;
+ if (mute) {
+ outb(0xd0, isa->io); /* volume steady + sigstr + off */
return 0;
}
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct rtrack *rt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- rt_mute(rt);
- else
- rt_setvol(rt, rt->curvol);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- rt_setvol(rt, ctrl->value);
- return 0;
+ if (vol == 0) { /* volume = 0 means mute the card */
+ outb(0x48, isa->io); /* volume down but still "on" */
+ msleep(curvol * 3); /* make sure it's totally down */
+ } else if (curvol < vol) {
+ outb(0x98, isa->io); /* volume up + sigstr + on */
+ for (; curvol < vol; curvol++)
+ udelay(3000);
+ } else if (curvol > vol) {
+ outb(0x58, isa->io); /* volume down + sigstr + on */
+ for (; curvol > vol; curvol--)
+ udelay(3000);
}
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
+ outb(0xd8, isa->io); /* volume steady + sigstr + on */
+ rt->curvol = vol;
return 0;
}
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+/* Mute card - prevents noisy bootups */
+static int rtrack_initialize(struct radio_isa_card *isa)
{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
+ /* this ensures that the volume is all the way up */
+ outb(0x90, isa->io); /* volume up but still "on" */
+ msleep(3000); /* make sure it's totally up */
+ outb(0xc0, isa->io); /* steady volume, mute card */
return 0;
}
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static const struct v4l2_file_operations rtrack_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops rtrack_ops = {
+ .alloc = rtrack_alloc,
+ .init = rtrack_initialize,
+ .s_mute_volume = rtrack_s_mute_volume,
+ .s_frequency = rtrack_s_frequency,
+ .g_signal = rtrack_g_signal,
};
-static const struct v4l2_ioctl_ops rtrack_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
+static const int rtrack_ioports[] = { 0x20f, 0x30f };
+
+static struct radio_isa_driver rtrack_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-aimslab",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = rtrack_ioports,
+ .num_of_io_ports = ARRAY_SIZE(rtrack_ioports),
+ .region_size = 2,
+ .card = "AIMSlab RadioTrack/RadioReveal",
+ .ops = &rtrack_ops,
+ .has_stereo = true,
+ .max_volume = 0xff,
};
static int __init rtrack_init(void)
{
- struct rtrack *rt = &rtrack_card;
- struct v4l2_device *v4l2_dev = &rt->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "rtrack", sizeof(v4l2_dev->name));
- rt->io = io;
-
- if (rt->io == -1) {
- v4l2_err(v4l2_dev, "you must set an I/O address with io=0x20f or 0x30f\n");
- return -EINVAL;
- }
-
- if (!request_region(rt->io, 2, "rtrack")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", rt->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(rt->io, 2);
- v4l2_err(v4l2_dev, "could not register v4l2_device\n");
- return res;
- }
-
- strlcpy(rt->vdev.name, v4l2_dev->name, sizeof(rt->vdev.name));
- rt->vdev.v4l2_dev = v4l2_dev;
- rt->vdev.fops = &rtrack_fops;
- rt->vdev.ioctl_ops = &rtrack_ioctl_ops;
- rt->vdev.release = video_device_release_empty;
- video_set_drvdata(&rt->vdev, rt);
-
- /* Set up the I/O locking */
-
- mutex_init(&rt->lock);
-
- /* mute card - prevents noisy bootups */
-
- /* this ensures that the volume is all the way down */
- outb(0x48, rt->io); /* volume down but still "on" */
- msleep(2000); /* make sure it's totally down */
- outb(0xc0, rt->io); /* steady volume, mute card */
-
- if (video_register_device(&rt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(&rt->v4l2_dev);
- release_region(rt->io, 2);
- return -EINVAL;
- }
- v4l2_info(v4l2_dev, "AIMSlab RadioTrack/RadioReveal card driver.\n");
-
- return 0;
+ return isa_register_driver(&rtrack_driver.driver, RTRACK_MAX);
}
static void __exit rtrack_exit(void)
{
- struct rtrack *rt = &rtrack_card;
-
- video_unregister_device(&rt->vdev);
- v4l2_device_unregister(&rt->v4l2_dev);
- release_region(rt->io, 2);
+ isa_unregister_driver(&rtrack_driver.driver);
}
module_init(rtrack_init);
module_exit(rtrack_exit);
-
diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c
index eed7b0840734..177bcbd7a7c1 100644
--- a/drivers/media/radio/radio-aztech.c
+++ b/drivers/media/radio/radio-aztech.c
@@ -1,5 +1,7 @@
-/* radio-aztech.c - Aztech radio card driver for Linux 2.2
+/*
+ * radio-aztech.c - Aztech radio card driver
*
+ * Converted to the radio-isa framework by Hans Verkuil
* Converted to V4L2 API by Mauro Carvalho Chehab
* Adapted to support the Video for Linux API by
* Russell Kroll . Based on original tuner code by:
@@ -10,19 +12,7 @@
* Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
* William McGrath (wmcgrath@twilight.vtc.vsc.edu)
*
- * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
- * along with more information on the card itself.
- *
- * History:
- * 1999-02-24 Russell Kroll
- * Fine tuning/VIDEO_TUNER_LOW
- * Range expanded to 87-108 MHz (from 87.9-107.8)
- *
- * Notable changes from the original source:
- * - includes stripped down to the essentials
- * - for loops used as delays replaced with udelay()
- * - #defines removed, changed to static values
- * - tuning structure changed - no more character arrays, other changes
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
*/
#include /* Modules */
@@ -31,126 +21,72 @@
#include /* udelay */
#include /* kernel radio structs */
#include /* outb, outb_p */
+#include
#include
#include
+#include
+#include "radio-isa.h"
MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
MODULE_DESCRIPTION("A driver for the Aztech radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
+MODULE_VERSION("1.0.0");
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
-
#ifndef CONFIG_RADIO_AZTECH_PORT
#define CONFIG_RADIO_AZTECH_PORT -1
#endif
-static int io = CONFIG_RADIO_AZTECH_PORT;
-static int radio_nr = -1;
-static int radio_wait_time = 1000;
+#define AZTECH_MAX 2
-module_param(io, int, 0);
-module_param(radio_nr, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
+static int io[AZTECH_MAX] = { [0] = CONFIG_RADIO_AZTECH_PORT,
+ [1 ... (AZTECH_MAX - 1)] = -1 };
+static int radio_nr[AZTECH_MAX] = { [0 ... (AZTECH_MAX - 1)] = -1 };
+static const int radio_wait_time = 1000;
-struct aztech
-{
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the Aztech card (0x350 or 0x358)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
+
+struct aztech {
+ struct radio_isa_card isa;
int curvol;
- unsigned long curfreq;
- int stereo;
- struct mutex lock;
};
-static struct aztech aztech_card;
-
-static int volconvert(int level)
-{
- level >>= 14; /* Map 16bits down to 2 bit */
- level &= 3;
-
- /* convert to card-friendly values */
- switch (level) {
- case 0:
- return 0;
- case 1:
- return 1;
- case 2:
- return 4;
- case 3:
- return 5;
- }
- return 0; /* Quieten gcc */
-}
-
static void send_0_byte(struct aztech *az)
{
udelay(radio_wait_time);
- outb_p(2 + volconvert(az->curvol), az->io);
- outb_p(64 + 2 + volconvert(az->curvol), az->io);
+ outb_p(2 + az->curvol, az->isa.io);
+ outb_p(64 + 2 + az->curvol, az->isa.io);
}
static void send_1_byte(struct aztech *az)
{
- udelay (radio_wait_time);
- outb_p(128 + 2 + volconvert(az->curvol), az->io);
- outb_p(128 + 64 + 2 + volconvert(az->curvol), az->io);
+ udelay(radio_wait_time);
+ outb_p(128 + 2 + az->curvol, az->isa.io);
+ outb_p(128 + 64 + 2 + az->curvol, az->isa.io);
}
-static int az_setvol(struct aztech *az, int vol)
+static struct radio_isa_card *aztech_alloc(void)
{
- mutex_lock(&az->lock);
- outb(volconvert(vol), az->io);
- mutex_unlock(&az->lock);
- return 0;
+ struct aztech *az = kzalloc(sizeof(*az), GFP_KERNEL);
+
+ return az ? &az->isa : NULL;
}
-/* thanks to Michael Dwyer for giving me a dose of clues in
- * the signal strength department..
- *
- * This card has a stereo bit - bit 0 set = mono, not set = stereo
- * It also has a "signal" bit - bit 1 set = bad signal, not set = good
- *
- */
-
-static int az_getsigstr(struct aztech *az)
-{
- int sig = 1;
-
- mutex_lock(&az->lock);
- if (inb(az->io) & 2) /* bit set = no signal present */
- sig = 0;
- mutex_unlock(&az->lock);
- return sig;
-}
-
-static int az_getstereo(struct aztech *az)
-{
- int stereo = 1;
-
- mutex_lock(&az->lock);
- if (inb(az->io) & 1) /* bit set = mono */
- stereo = 0;
- mutex_unlock(&az->lock);
- return stereo;
-}
-
-static int az_setfreq(struct aztech *az, unsigned long frequency)
+static int aztech_s_frequency(struct radio_isa_card *isa, u32 freq)
{
+ struct aztech *az = container_of(isa, struct aztech, isa);
int i;
- mutex_lock(&az->lock);
-
- az->curfreq = frequency;
- frequency += 171200; /* Add 10.7 MHz IF */
- frequency /= 800; /* Convert to 50 kHz units */
+ freq += 171200; /* Add 10.7 MHz IF */
+ freq /= 800; /* Convert to 50 kHz units */
send_0_byte(az); /* 0: LSB of frequency */
for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
- if (frequency & (1 << i))
+ if (freq & (1 << i))
send_1_byte(az);
else
send_0_byte(az);
@@ -158,7 +94,7 @@ static int az_setfreq(struct aztech *az, unsigned long frequency)
send_0_byte(az); /* 14: test bit - always 0 */
send_0_byte(az); /* 15: test bit - always 0 */
send_0_byte(az); /* 16: band data 0 - always 0 */
- if (az->stereo) /* 17: stereo (1 to enable) */
+ if (isa->stereo) /* 17: stereo (1 to enable) */
send_1_byte(az);
else
send_0_byte(az);
@@ -173,225 +109,77 @@ static int az_setfreq(struct aztech *az, unsigned long frequency)
/* latch frequency */
udelay(radio_wait_time);
- outb_p(128 + 64 + volconvert(az->curvol), az->io);
-
- mutex_unlock(&az->lock);
+ outb_p(128 + 64 + az->curvol, az->isa.io);
return 0;
}
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
+/* thanks to Michael Dwyer for giving me a dose of clues in
+ * the signal strength department..
+ *
+ * This card has a stereo bit - bit 0 set = mono, not set = stereo
+ */
+static u32 aztech_g_rxsubchans(struct radio_isa_card *isa)
{
- strlcpy(v->driver, "radio-aztech", sizeof(v->driver));
- strlcpy(v->card, "Aztech Radio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ if (inb(isa->io) & 1)
+ return V4L2_TUNER_SUB_MONO;
+ return V4L2_TUNER_SUB_STEREO;
+}
+
+static int aztech_s_stereo(struct radio_isa_card *isa, bool stereo)
+{
+ return aztech_s_frequency(isa, isa->freq);
+}
+
+static int aztech_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
+{
+ struct aztech *az = container_of(isa, struct aztech, isa);
+
+ if (mute)
+ vol = 0;
+ az->curvol = (vol & 1) + ((vol & 2) << 1);
+ outb(az->curvol, isa->io);
return 0;
}
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct aztech *az = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
-
- v->rangelow = 87 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- if (az_getstereo(az))
- v->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xFFFF * az_getsigstr(az);
-
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct aztech *az = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- az_setfreq(az, f->frequency);
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct aztech *az = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = az->curfreq;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct aztech *az = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (az->curvol == 0)
- ctrl->value = 1;
- else
- ctrl->value = 0;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = az->curvol * 6554;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct aztech *az = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- az_setvol(az, 0);
- else
- az_setvol(az, az->curvol);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- az_setvol(az, ctrl->value);
- return 0;
- }
- return -EINVAL;
-}
-
-static const struct v4l2_file_operations aztech_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops aztech_ops = {
+ .alloc = aztech_alloc,
+ .s_mute_volume = aztech_s_mute_volume,
+ .s_frequency = aztech_s_frequency,
+ .s_stereo = aztech_s_stereo,
+ .g_rxsubchans = aztech_g_rxsubchans,
};
-static const struct v4l2_ioctl_ops aztech_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
+static const int aztech_ioports[] = { 0x350, 0x358 };
+
+static struct radio_isa_driver aztech_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-aztech",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = aztech_ioports,
+ .num_of_io_ports = ARRAY_SIZE(aztech_ioports),
+ .region_size = 2,
+ .card = "Aztech Radio",
+ .ops = &aztech_ops,
+ .has_stereo = true,
+ .max_volume = 3,
};
static int __init aztech_init(void)
{
- struct aztech *az = &aztech_card;
- struct v4l2_device *v4l2_dev = &az->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "aztech", sizeof(v4l2_dev->name));
- az->io = io;
-
- if (az->io == -1) {
- v4l2_err(v4l2_dev, "you must set an I/O address with io=0x350 or 0x358\n");
- return -EINVAL;
- }
-
- if (!request_region(az->io, 2, "aztech")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", az->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(az->io, 2);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
-
- mutex_init(&az->lock);
- strlcpy(az->vdev.name, v4l2_dev->name, sizeof(az->vdev.name));
- az->vdev.v4l2_dev = v4l2_dev;
- az->vdev.fops = &aztech_fops;
- az->vdev.ioctl_ops = &aztech_ioctl_ops;
- az->vdev.release = video_device_release_empty;
- video_set_drvdata(&az->vdev, az);
- /* mute card - prevents noisy bootups */
- outb(0, az->io);
-
- if (video_register_device(&az->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
- release_region(az->io, 2);
- return -EINVAL;
- }
-
- v4l2_info(v4l2_dev, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
- return 0;
+ return isa_register_driver(&aztech_driver.driver, AZTECH_MAX);
}
static void __exit aztech_exit(void)
{
- struct aztech *az = &aztech_card;
-
- video_unregister_device(&az->vdev);
- v4l2_device_unregister(&az->v4l2_dev);
- release_region(az->io, 2);
+ isa_unregister_driver(&aztech_driver.driver);
}
module_init(aztech_init);
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index 36ce0611c037..2e639ce6f256 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -1,4 +1,7 @@
-/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin
+/*
+ * GemTek radio card driver
+ *
+ * Copyright 1998 Jonas Munsin
*
* GemTek hasn't released any specs on the card, so the protocol had to
* be reverse engineered with dosemu.
@@ -11,9 +14,12 @@
* Converted to new API by Alan Cox
* Various bugfixes and enhancements by Russell Kroll
*
- * TODO: Allow for more than one of these foolish entities :-)
- *
+ * Converted to the radio-isa framework by Hans Verkuil
* Converted to V4L2 API by Mauro Carvalho Chehab
+ *
+ * Note: this card seems to swap the left and right audio channels!
+ *
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
*/
#include /* Modules */
@@ -23,8 +29,10 @@
#include /* kernel radio structs */
#include
#include /* outb, outb_p */
+#include
#include
#include
+#include "radio-isa.h"
/*
* Module info.
@@ -33,7 +41,7 @@
MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen ");
MODULE_DESCRIPTION("A driver for the GemTek Radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.4");
+MODULE_VERSION("1.0.0");
/*
* Module params.
@@ -46,45 +54,29 @@ MODULE_VERSION("0.0.4");
#define CONFIG_RADIO_GEMTEK_PROBE 1
#endif
-static int io = CONFIG_RADIO_GEMTEK_PORT;
-static bool probe = CONFIG_RADIO_GEMTEK_PROBE;
-static bool hardmute;
-static bool shutdown = 1;
-static bool keepmuted = 1;
-static bool initmute = 1;
-static int radio_nr = -1;
+#define GEMTEK_MAX 4
-module_param(io, int, 0444);
-MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic "
+static bool probe = CONFIG_RADIO_GEMTEK_PROBE;
+static bool hardmute;
+static int io[GEMTEK_MAX] = { [0] = CONFIG_RADIO_GEMTEK_PORT,
+ [1 ... (GEMTEK_MAX - 1)] = -1 };
+static int radio_nr[GEMTEK_MAX] = { [0 ... (GEMTEK_MAX - 1)] = -1 };
+
+module_param(probe, bool, 0444);
+MODULE_PARM_DESC(probe, "Enable automatic device probing.");
+
+module_param(hardmute, bool, 0644);
+MODULE_PARM_DESC(hardmute, "Enable 'hard muting' by shutting down PLL, may "
+ "reduce static noise.");
+
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "Force I/O ports for the GemTek Radio card if automatic "
"probing is disabled or fails. The most common I/O ports are: 0x20c "
"0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "
"work for the combined sound/radiocard).");
-module_param(probe, bool, 0444);
-MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most "
- "common I/O ports used by the card are probed.");
-
-module_param(hardmute, bool, 0644);
-MODULE_PARM_DESC(hardmute, "Enable `hard muting' by shutting down PLL, may "
- "reduce static noise.");
-
-module_param(shutdown, bool, 0644);
-MODULE_PARM_DESC(shutdown, "Enable shutting down PLL and muting line when "
- "module is unloaded.");
-
-module_param(keepmuted, bool, 0644);
-MODULE_PARM_DESC(keepmuted, "Keep card muted even when frequency is changed.");
-
-module_param(initmute, bool, 0444);
-MODULE_PARM_DESC(initmute, "Mute card when module is loaded.");
-
-module_param(radio_nr, int, 0444);
-
-/*
- * Functions for controlling the card.
- */
-#define GEMTEK_LOWFREQ (87*16000)
-#define GEMTEK_HIGHFREQ (108*16000)
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
/*
* Frequency calculation constants. Intermediate frequency 10.52 MHz (nominal
@@ -108,18 +100,11 @@ module_param(radio_nr, int, 0444);
#define LONG_DELAY 75 /* usec */
struct gemtek {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- struct mutex lock;
- unsigned long lastfreq;
- int muted;
- int verified;
- int io;
+ struct radio_isa_card isa;
+ bool muted;
u32 bu2614data;
};
-static struct gemtek gemtek_card;
-
#define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */
#define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */
#define BU2614_VOID_BITS 4 /* unused */
@@ -166,31 +151,24 @@ static struct gemtek gemtek_card;
*/
static void gemtek_bu2614_transmit(struct gemtek *gt)
{
+ struct radio_isa_card *isa = >->isa;
int i, bit, q, mute;
- mutex_lock(>->lock);
-
mute = gt->muted ? GEMTEK_MT : 0x00;
- outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);
- udelay(SHORT_DELAY);
- outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
+ outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, isa->io);
udelay(LONG_DELAY);
for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) {
bit = (q & 1) ? GEMTEK_DA : 0;
- outb_p(mute | GEMTEK_CE | bit, gt->io);
+ outb_p(mute | GEMTEK_CE | bit, isa->io);
udelay(SHORT_DELAY);
- outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, gt->io);
+ outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, isa->io);
udelay(SHORT_DELAY);
}
- outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);
+ outb_p(mute | GEMTEK_DA | GEMTEK_CK, isa->io);
udelay(SHORT_DELAY);
- outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
- udelay(LONG_DELAY);
-
- mutex_unlock(>->lock);
}
/*
@@ -198,21 +176,27 @@ static void gemtek_bu2614_transmit(struct gemtek *gt)
*/
static unsigned long gemtek_convfreq(unsigned long freq)
{
- return ((freq<muted = true;
+ return gt ? >->isa : NULL;
}
/*
* Set FM-frequency.
*/
-static void gemtek_setfreq(struct gemtek *gt, unsigned long freq)
+static int gemtek_s_frequency(struct radio_isa_card *isa, u32 freq)
{
- if (keepmuted && hardmute && gt->muted)
- return;
+ struct gemtek *gt = container_of(isa, struct gemtek, isa);
- freq = clamp_val(freq, GEMTEK_LOWFREQ, GEMTEK_HIGHFREQ);
-
- gt->lastfreq = freq;
- gt->muted = 0;
+ if (hardmute && gt->muted)
+ return 0;
gemtek_bu2614_set(gt, BU2614_PORT, 0);
gemtek_bu2614_set(gt, BU2614_FMES, 0);
@@ -220,23 +204,25 @@ static void gemtek_setfreq(struct gemtek *gt, unsigned long freq)
gemtek_bu2614_set(gt, BU2614_SWAL, 0);
gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */
gemtek_bu2614_set(gt, BU2614_TEST, 0);
-
gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ);
gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq));
-
gemtek_bu2614_transmit(gt);
+ return 0;
}
/*
* Set mute flag.
*/
-static void gemtek_mute(struct gemtek *gt)
+static int gemtek_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
+ struct gemtek *gt = container_of(isa, struct gemtek, isa);
int i;
- gt->muted = 1;
-
+ gt->muted = mute;
if (hardmute) {
+ if (!mute)
+ return gemtek_s_frequency(isa, isa->freq);
+
/* Turn off PLL, disable data output */
gemtek_bu2614_set(gt, BU2614_PORT, 0);
gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */
@@ -247,367 +233,85 @@ static void gemtek_mute(struct gemtek *gt)
gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF);
gemtek_bu2614_set(gt, BU2614_FREQ, 0);
gemtek_bu2614_transmit(gt);
- return;
+ return 0;
}
- mutex_lock(>->lock);
-
/* Read bus contents (CE, CK and DA). */
- i = inb_p(gt->io);
+ i = inb_p(isa->io);
/* Write it back with mute flag set. */
- outb_p((i >> 5) | GEMTEK_MT, gt->io);
+ outb_p((i >> 5) | (mute ? GEMTEK_MT : 0), isa->io);
udelay(SHORT_DELAY);
-
- mutex_unlock(>->lock);
+ return 0;
}
-/*
- * Unset mute flag.
- */
-static void gemtek_unmute(struct gemtek *gt)
+static u32 gemtek_g_rxsubchans(struct radio_isa_card *isa)
{
- int i;
-
- gt->muted = 0;
- if (hardmute) {
- /* Turn PLL back on. */
- gemtek_setfreq(gt, gt->lastfreq);
- return;
- }
- mutex_lock(>->lock);
-
- i = inb_p(gt->io);
- outb_p(i >> 5, gt->io);
- udelay(SHORT_DELAY);
-
- mutex_unlock(>->lock);
-}
-
-/*
- * Get signal strength (= stereo status).
- */
-static inline int gemtek_getsigstr(struct gemtek *gt)
-{
- int sig;
-
- mutex_lock(>->lock);
- sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1;
- mutex_unlock(>->lock);
- return sig;
+ if (inb_p(isa->io) & GEMTEK_NS)
+ return V4L2_TUNER_SUB_MONO;
+ return V4L2_TUNER_SUB_STEREO;
}
/*
* Check if requested card acts like GemTek Radio card.
*/
-static int gemtek_verify(struct gemtek *gt, int port)
+static bool gemtek_probe(struct radio_isa_card *isa, int io)
{
int i, q;
- if (gt->verified == port)
- return 1;
-
- mutex_lock(>->lock);
-
- q = inb_p(port); /* Read bus contents before probing. */
+ q = inb_p(io); /* Read bus contents before probing. */
/* Try to turn on CE, CK and DA respectively and check if card responds
properly. */
for (i = 0; i < 3; ++i) {
- outb_p(1 << i, port);
+ outb_p(1 << i, io);
udelay(SHORT_DELAY);
- if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) {
- mutex_unlock(>->lock);
- return 0;
- }
+ if ((inb_p(io) & ~GEMTEK_NS) != (0x17 | (1 << (i + 5))))
+ return false;
}
- outb_p(q >> 5, port); /* Write bus contents back. */
+ outb_p(q >> 5, io); /* Write bus contents back. */
udelay(SHORT_DELAY);
-
- mutex_unlock(>->lock);
- gt->verified = port;
-
- return 1;
+ return true;
}
-/*
- * Automatic probing for card.
- */
-static int gemtek_probe(struct gemtek *gt)
-{
- struct v4l2_device *v4l2_dev = >->v4l2_dev;
- int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
- int i;
-
- if (!probe) {
- v4l2_info(v4l2_dev, "Automatic device probing disabled.\n");
- return -1;
- }
-
- v4l2_info(v4l2_dev, "Automatic device probing enabled.\n");
-
- for (i = 0; i < ARRAY_SIZE(ioports); ++i) {
- v4l2_info(v4l2_dev, "Trying I/O port 0x%x...\n", ioports[i]);
-
- if (!request_region(ioports[i], 1, "gemtek-probe")) {
- v4l2_warn(v4l2_dev, "I/O port 0x%x busy!\n",
- ioports[i]);
- continue;
- }
-
- if (gemtek_verify(gt, ioports[i])) {
- v4l2_info(v4l2_dev, "Card found from I/O port "
- "0x%x!\n", ioports[i]);
-
- release_region(ioports[i], 1);
- gt->io = ioports[i];
- return gt->io;
- }
-
- release_region(ioports[i], 1);
- }
-
- v4l2_err(v4l2_dev, "Automatic probing failed!\n");
- return -1;
-}
-
-/*
- * Video 4 Linux stuff.
- */
-
-static const struct v4l2_file_operations gemtek_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops gemtek_ops = {
+ .alloc = gemtek_alloc,
+ .probe = gemtek_probe,
+ .s_mute_volume = gemtek_s_mute_volume,
+ .s_frequency = gemtek_s_frequency,
+ .g_rxsubchans = gemtek_g_rxsubchans,
};
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-gemtek", sizeof(v->driver));
- strlcpy(v->card, "GemTek", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
+static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
-static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
-{
- struct gemtek *gt = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = GEMTEK_LOWFREQ;
- v->rangehigh = GEMTEK_HIGHFREQ;
- v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
- v->signal = 0xffff * gemtek_getsigstr(gt);
- if (v->signal) {
- v->audmode = V4L2_TUNER_MODE_STEREO;
- v->rxsubchans = V4L2_TUNER_SUB_STEREO;
- } else {
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
- }
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
-{
- return (v->index != 0) ? -EINVAL : 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct gemtek *gt = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = gt->lastfreq;
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct gemtek *gt = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- gemtek_setfreq(gt, f->frequency);
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
- default:
- return -EINVAL;
- }
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct gemtek *gt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = gt->muted;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct gemtek *gt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- gemtek_mute(gt);
- else
- gemtek_unmute(gt);
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return (i != 0) ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- return (a->index != 0) ? -EINVAL : 0;
-}
-
-static const struct v4l2_ioctl_ops gemtek_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl
+static struct radio_isa_driver gemtek_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-gemtek",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = gemtek_ioports,
+ .num_of_io_ports = ARRAY_SIZE(gemtek_ioports),
+ .region_size = 1,
+ .card = "GemTek Radio",
+ .ops = &gemtek_ops,
+ .has_stereo = true,
};
-/*
- * Initialization / cleanup related stuff.
- */
-
static int __init gemtek_init(void)
{
- struct gemtek *gt = &gemtek_card;
- struct v4l2_device *v4l2_dev = >->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "gemtek", sizeof(v4l2_dev->name));
-
- v4l2_info(v4l2_dev, "GemTek Radio card driver: v0.0.3\n");
-
- mutex_init(>->lock);
-
- gt->verified = -1;
- gt->io = io;
- gemtek_probe(gt);
- if (gt->io) {
- if (!request_region(gt->io, 1, "gemtek")) {
- v4l2_err(v4l2_dev, "I/O port 0x%x already in use.\n", gt->io);
- return -EBUSY;
- }
-
- if (!gemtek_verify(gt, gt->io))
- v4l2_warn(v4l2_dev, "Card at I/O port 0x%x does not "
- "respond properly, check your "
- "configuration.\n", gt->io);
- else
- v4l2_info(v4l2_dev, "Using I/O port 0x%x.\n", gt->io);
- } else if (probe) {
- v4l2_err(v4l2_dev, "Automatic probing failed and no "
- "fixed I/O port defined.\n");
- return -ENODEV;
- } else {
- v4l2_err(v4l2_dev, "Automatic probing disabled but no fixed "
- "I/O port defined.");
- return -EINVAL;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- release_region(gt->io, 1);
- return res;
- }
-
- strlcpy(gt->vdev.name, v4l2_dev->name, sizeof(gt->vdev.name));
- gt->vdev.v4l2_dev = v4l2_dev;
- gt->vdev.fops = &gemtek_fops;
- gt->vdev.ioctl_ops = &gemtek_ioctl_ops;
- gt->vdev.release = video_device_release_empty;
- video_set_drvdata(>->vdev, gt);
-
- /* Set defaults */
- gt->lastfreq = GEMTEK_LOWFREQ;
- gt->bu2614data = 0;
-
- if (initmute)
- gemtek_mute(gt);
-
- if (video_register_device(>->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
- release_region(gt->io, 1);
- return -EBUSY;
- }
-
- return 0;
+ gemtek_driver.probe = probe;
+ return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX);
}
-/*
- * Module cleanup
- */
static void __exit gemtek_exit(void)
{
- struct gemtek *gt = &gemtek_card;
- struct v4l2_device *v4l2_dev = >->v4l2_dev;
-
- if (shutdown) {
- hardmute = 1; /* Turn off PLL */
- gemtek_mute(gt);
- } else {
- v4l2_info(v4l2_dev, "Module unloaded but card not muted!\n");
- }
-
- video_unregister_device(>->vdev);
- v4l2_device_unregister(>->v4l2_dev);
- release_region(gt->io, 1);
+ hardmute = 1; /* Turn off PLL */
+ isa_unregister_driver(&gemtek_driver.driver);
}
module_init(gemtek_init);
diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c
new file mode 100644
index 000000000000..06f906351fad
--- /dev/null
+++ b/drivers/media/radio/radio-isa.c
@@ -0,0 +1,340 @@
+/*
+ * Framework for ISA radio drivers.
+ * This takes care of all the V4L2 scaffolding, allowing the ISA drivers
+ * to concentrate on the actual hardware operation.
+ *
+ * Copyright (C) 2012 Hans Verkuil
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "radio-isa.h"
+
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_DESCRIPTION("A framework for ISA radio drivers.");
+MODULE_LICENSE("GPL");
+
+#define FREQ_LOW (87U * 16000U)
+#define FREQ_HIGH (108U * 16000U)
+
+static int radio_isa_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+
+ strlcpy(v->driver, isa->drv->driver.driver.name, sizeof(v->driver));
+ strlcpy(v->card, isa->drv->card, sizeof(v->card));
+ snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", isa->v4l2_dev.name);
+
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ v->device_caps = v->capabilities | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int radio_isa_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+ const struct radio_isa_ops *ops = isa->drv->ops;
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = FREQ_LOW;
+ v->rangehigh = FREQ_HIGH;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ if (isa->drv->has_stereo)
+ v->capability |= V4L2_TUNER_CAP_STEREO;
+
+ if (ops->g_rxsubchans)
+ v->rxsubchans = ops->g_rxsubchans(isa);
+ else
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->audmode = isa->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
+ if (ops->g_signal)
+ v->signal = ops->g_signal(isa);
+ else
+ v->signal = (v->rxsubchans & V4L2_TUNER_SUB_STEREO) ?
+ 0xffff : 0;
+ return 0;
+}
+
+static int radio_isa_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+ const struct radio_isa_ops *ops = isa->drv->ops;
+
+ if (v->index)
+ return -EINVAL;
+ if (ops->s_stereo) {
+ isa->stereo = (v->audmode == V4L2_TUNER_MODE_STEREO);
+ return ops->s_stereo(isa, isa->stereo);
+ }
+ return 0;
+}
+
+static int radio_isa_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+ int res;
+
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+ f->frequency = clamp(f->frequency, FREQ_LOW, FREQ_HIGH);
+ res = isa->drv->ops->s_frequency(isa, f->frequency);
+ if (res == 0)
+ isa->freq = f->frequency;
+ return res;
+}
+
+static int radio_isa_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+
+ if (f->tuner != 0)
+ return -EINVAL;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = isa->freq;
+ return 0;
+}
+
+static int radio_isa_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct radio_isa_card *isa =
+ container_of(ctrl->handler, struct radio_isa_card, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ return isa->drv->ops->s_mute_volume(isa, ctrl->val,
+ isa->volume ? isa->volume->val : 0);
+ }
+ return -EINVAL;
+}
+
+static int radio_isa_log_status(struct file *file, void *priv)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+
+ v4l2_info(&isa->v4l2_dev, "I/O Port = 0x%03x\n", isa->io);
+ v4l2_ctrl_handler_log_status(&isa->hdl, isa->v4l2_dev.name);
+ return 0;
+}
+
+static int radio_isa_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type == V4L2_EVENT_CTRL)
+ return v4l2_event_subscribe(fh, sub, 0);
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops radio_isa_ctrl_ops = {
+ .s_ctrl = radio_isa_s_ctrl,
+};
+
+static const struct v4l2_file_operations radio_isa_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops radio_isa_ioctl_ops = {
+ .vidioc_querycap = radio_isa_querycap,
+ .vidioc_g_tuner = radio_isa_g_tuner,
+ .vidioc_s_tuner = radio_isa_s_tuner,
+ .vidioc_g_frequency = radio_isa_g_frequency,
+ .vidioc_s_frequency = radio_isa_s_frequency,
+ .vidioc_log_status = radio_isa_log_status,
+ .vidioc_subscribe_event = radio_isa_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+int radio_isa_match(struct device *pdev, unsigned int dev)
+{
+ struct radio_isa_driver *drv = pdev->platform_data;
+
+ return drv->probe || drv->io_params[dev] >= 0;
+}
+EXPORT_SYMBOL_GPL(radio_isa_match);
+
+static bool radio_isa_valid_io(const struct radio_isa_driver *drv, int io)
+{
+ int i;
+
+ for (i = 0; i < drv->num_of_io_ports; i++)
+ if (drv->io_ports[i] == io)
+ return true;
+ return false;
+}
+
+int radio_isa_probe(struct device *pdev, unsigned int dev)
+{
+ struct radio_isa_driver *drv = pdev->platform_data;
+ const struct radio_isa_ops *ops = drv->ops;
+ struct v4l2_device *v4l2_dev;
+ struct radio_isa_card *isa;
+ int res;
+
+ isa = drv->ops->alloc();
+ if (isa == NULL)
+ return -ENOMEM;
+ dev_set_drvdata(pdev, isa);
+ isa->drv = drv;
+ isa->io = drv->io_params[dev];
+ v4l2_dev = &isa->v4l2_dev;
+ strlcpy(v4l2_dev->name, dev_name(pdev), sizeof(v4l2_dev->name));
+
+ if (drv->probe && ops->probe) {
+ int i;
+
+ for (i = 0; i < drv->num_of_io_ports; ++i) {
+ int io = drv->io_ports[i];
+
+ if (request_region(io, drv->region_size, v4l2_dev->name)) {
+ bool found = ops->probe(isa, io);
+
+ release_region(io, drv->region_size);
+ if (found) {
+ isa->io = io;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!radio_isa_valid_io(drv, isa->io)) {
+ int i;
+
+ if (isa->io < 0)
+ return -ENODEV;
+ v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x",
+ drv->io_ports[0]);
+ for (i = 1; i < drv->num_of_io_ports; i++)
+ printk(KERN_CONT "/0x%03x", drv->io_ports[i]);
+ printk(KERN_CONT ".\n");
+ kfree(isa);
+ return -EINVAL;
+ }
+
+ if (!request_region(isa->io, drv->region_size, v4l2_dev->name)) {
+ v4l2_err(v4l2_dev, "port 0x%x already in use\n", isa->io);
+ kfree(isa);
+ return -EBUSY;
+ }
+
+ res = v4l2_device_register(pdev, v4l2_dev);
+ if (res < 0) {
+ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ goto err_dev_reg;
+ }
+
+ v4l2_ctrl_handler_init(&isa->hdl, 1);
+ isa->mute = v4l2_ctrl_new_std(&isa->hdl, &radio_isa_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ if (drv->max_volume)
+ isa->volume = v4l2_ctrl_new_std(&isa->hdl, &radio_isa_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, drv->max_volume, 1,
+ drv->max_volume);
+ v4l2_dev->ctrl_handler = &isa->hdl;
+ if (isa->hdl.error) {
+ res = isa->hdl.error;
+ v4l2_err(v4l2_dev, "Could not register controls\n");
+ goto err_hdl;
+ }
+ if (drv->max_volume)
+ v4l2_ctrl_cluster(2, &isa->mute);
+ v4l2_dev->ctrl_handler = &isa->hdl;
+
+ mutex_init(&isa->lock);
+ isa->vdev.lock = &isa->lock;
+ strlcpy(isa->vdev.name, v4l2_dev->name, sizeof(isa->vdev.name));
+ isa->vdev.v4l2_dev = v4l2_dev;
+ isa->vdev.fops = &radio_isa_fops;
+ isa->vdev.ioctl_ops = &radio_isa_ioctl_ops;
+ isa->vdev.release = video_device_release_empty;
+ set_bit(V4L2_FL_USE_FH_PRIO, &isa->vdev.flags);
+ video_set_drvdata(&isa->vdev, isa);
+ isa->freq = FREQ_LOW;
+ isa->stereo = drv->has_stereo;
+
+ if (ops->init)
+ res = ops->init(isa);
+ if (!res)
+ res = v4l2_ctrl_handler_setup(&isa->hdl);
+ if (!res)
+ res = ops->s_frequency(isa, isa->freq);
+ if (!res && ops->s_stereo)
+ res = ops->s_stereo(isa, isa->stereo);
+ if (res < 0) {
+ v4l2_err(v4l2_dev, "Could not setup card\n");
+ goto err_node_reg;
+ }
+ res = video_register_device(&isa->vdev, VFL_TYPE_RADIO,
+ drv->radio_nr_params[dev]);
+ if (res < 0) {
+ v4l2_err(v4l2_dev, "Could not register device node\n");
+ goto err_node_reg;
+ }
+
+ v4l2_info(v4l2_dev, "Initialized radio card %s on port 0x%03x\n",
+ drv->card, isa->io);
+ return 0;
+
+err_node_reg:
+ v4l2_ctrl_handler_free(&isa->hdl);
+err_hdl:
+ v4l2_device_unregister(&isa->v4l2_dev);
+err_dev_reg:
+ release_region(isa->io, drv->region_size);
+ kfree(isa);
+ return res;
+}
+EXPORT_SYMBOL_GPL(radio_isa_probe);
+
+int radio_isa_remove(struct device *pdev, unsigned int dev)
+{
+ struct radio_isa_card *isa = dev_get_drvdata(pdev);
+ const struct radio_isa_ops *ops = isa->drv->ops;
+
+ ops->s_mute_volume(isa, true, isa->volume ? isa->volume->cur.val : 0);
+ video_unregister_device(&isa->vdev);
+ v4l2_ctrl_handler_free(&isa->hdl);
+ v4l2_device_unregister(&isa->v4l2_dev);
+ release_region(isa->io, isa->drv->region_size);
+ v4l2_info(&isa->v4l2_dev, "Removed radio card %s\n", isa->drv->card);
+ kfree(isa);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(radio_isa_remove);
diff --git a/drivers/media/radio/radio-isa.h b/drivers/media/radio/radio-isa.h
new file mode 100644
index 000000000000..8a0ea84d86de
--- /dev/null
+++ b/drivers/media/radio/radio-isa.h
@@ -0,0 +1,105 @@
+/*
+ * Framework for ISA radio drivers.
+ * This takes care of all the V4L2 scaffolding, allowing the ISA drivers
+ * to concentrate on the actual hardware operation.
+ *
+ * Copyright (C) 2012 Hans Verkuil
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef _RADIO_ISA_H_
+#define _RADIO_ISA_H_
+
+#include
+#include
+#include
+#include
+
+struct radio_isa_driver;
+struct radio_isa_ops;
+
+/* Core structure for radio ISA cards */
+struct radio_isa_card {
+ const struct radio_isa_driver *drv;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
+ struct video_device vdev;
+ struct mutex lock;
+ const struct radio_isa_ops *ops;
+ struct { /* mute/volume cluster */
+ struct v4l2_ctrl *mute;
+ struct v4l2_ctrl *volume;
+ };
+ /* I/O port */
+ int io;
+
+ /* Card is in stereo audio mode */
+ bool stereo;
+ /* Current frequency */
+ u32 freq;
+};
+
+struct radio_isa_ops {
+ /* Allocate and initialize a radio_isa_card struct */
+ struct radio_isa_card *(*alloc)(void);
+ /* Probe whether a card is present at the given port */
+ bool (*probe)(struct radio_isa_card *isa, int io);
+ /* Special card initialization can be done here, this is called after
+ * the standard controls are registered, but before they are setup,
+ * thus allowing drivers to add their own controls here. */
+ int (*init)(struct radio_isa_card *isa);
+ /* Set mute and volume. */
+ int (*s_mute_volume)(struct radio_isa_card *isa, bool mute, int volume);
+ /* Set frequency */
+ int (*s_frequency)(struct radio_isa_card *isa, u32 freq);
+ /* Set stereo/mono audio mode */
+ int (*s_stereo)(struct radio_isa_card *isa, bool stereo);
+ /* Get rxsubchans value for VIDIOC_G_TUNER */
+ u32 (*g_rxsubchans)(struct radio_isa_card *isa);
+ /* Get the signal strength for VIDIOC_G_TUNER */
+ u32 (*g_signal)(struct radio_isa_card *isa);
+};
+
+/* Top level structure needed to instantiate the cards */
+struct radio_isa_driver {
+ struct isa_driver driver;
+ const struct radio_isa_ops *ops;
+ /* The module_param_array with the specified I/O ports */
+ int *io_params;
+ /* The module_param_array with the radio_nr values */
+ int *radio_nr_params;
+ /* Whether we should probe for possible cards */
+ bool probe;
+ /* The list of possible I/O ports */
+ const int *io_ports;
+ /* The size of that list */
+ int num_of_io_ports;
+ /* The region size to request */
+ unsigned region_size;
+ /* The name of the card */
+ const char *card;
+ /* Card can capture stereo audio */
+ bool has_stereo;
+ /* The maximum volume for the volume control. If 0, then there
+ is no volume control possible. */
+ int max_volume;
+};
+
+int radio_isa_match(struct device *pdev, unsigned int dev);
+int radio_isa_probe(struct device *pdev, unsigned int dev);
+int radio_isa_remove(struct device *pdev, unsigned int dev);
+
+#endif
diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c
new file mode 100644
index 000000000000..55bd1d2937c8
--- /dev/null
+++ b/drivers/media/radio/radio-keene.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2012 Hans Verkuil
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* kernel includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* driver and module definitions */
+MODULE_AUTHOR("Hans Verkuil ");
+MODULE_DESCRIPTION("Keene FM Transmitter driver");
+MODULE_LICENSE("GPL");
+
+/* Actually, it advertises itself as a Logitech */
+#define USB_KEENE_VENDOR 0x046d
+#define USB_KEENE_PRODUCT 0x0a0e
+
+/* Probably USB_TIMEOUT should be modified in module parameter */
+#define BUFFER_LENGTH 8
+#define USB_TIMEOUT 500
+
+/* Frequency limits in MHz */
+#define FREQ_MIN 76U
+#define FREQ_MAX 108U
+#define FREQ_MUL 16000U
+
+/* USB Device ID List */
+static struct usb_device_id usb_keene_device_table[] = {
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_KEENE_VENDOR, USB_KEENE_PRODUCT,
+ USB_CLASS_HID, 0, 0) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, usb_keene_device_table);
+
+struct keene_device {
+ struct usb_device *usbdev;
+ struct usb_interface *intf;
+ struct video_device vdev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
+ struct mutex lock;
+
+ u8 *buffer;
+ unsigned curfreq;
+ u8 tx;
+ u8 pa;
+ bool stereo;
+ bool muted;
+ bool preemph_75_us;
+};
+
+static inline struct keene_device *to_keene_dev(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct keene_device, v4l2_dev);
+}
+
+/* Set frequency (if non-0), PA, mute and turn on/off the FM transmitter. */
+static int keene_cmd_main(struct keene_device *radio, unsigned freq, bool play)
+{
+ unsigned short freq_send = freq ? (freq - 76 * 16000) / 800 : 0;
+ int ret;
+
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x50;
+ radio->buffer[2] = (freq_send >> 8) & 0xff;
+ radio->buffer[3] = freq_send & 0xff;
+ radio->buffer[4] = radio->pa;
+ /* If bit 4 is set, then tune to the frequency.
+ If bit 3 is set, then unmute; if bit 2 is set, then mute.
+ If bit 1 is set, then enter idle mode; if bit 0 is set,
+ then enter transit mode.
+ */
+ radio->buffer[5] = (radio->muted ? 4 : 8) | (play ? 1 : 2) |
+ (freq ? 0x10 : 0);
+ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x00;
+
+ ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
+ 9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
+
+ if (ret < 0) {
+ dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret);
+ return ret;
+ }
+ if (freq)
+ radio->curfreq = freq;
+ return 0;
+}
+
+/* Set TX, stereo and preemphasis mode (50 us vs 75 us). */
+static int keene_cmd_set(struct keene_device *radio)
+{
+ int ret;
+
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x51;
+ radio->buffer[2] = radio->tx;
+ /* If bit 0 is set, then transmit mono, otherwise stereo.
+ If bit 2 is set, then enable 75 us preemphasis, otherwise
+ it is 50 us. */
+ radio->buffer[3] = (!radio->stereo) | (radio->preemph_75_us ? 4 : 0);
+ radio->buffer[4] = 0x00;
+ radio->buffer[5] = 0x00;
+ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x00;
+
+ ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
+ 9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
+
+ if (ret < 0) {
+ dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+/* Handle unplugging the device.
+ * We call video_unregister_device in any case.
+ * The last function called in this procedure is
+ * usb_keene_device_release.
+ */
+static void usb_keene_disconnect(struct usb_interface *intf)
+{
+ struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf));
+
+ v4l2_device_get(&radio->v4l2_dev);
+ mutex_lock(&radio->lock);
+ usb_set_intfdata(intf, NULL);
+ video_unregister_device(&radio->vdev);
+ v4l2_device_disconnect(&radio->v4l2_dev);
+ mutex_unlock(&radio->lock);
+ v4l2_device_put(&radio->v4l2_dev);
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ struct keene_device *radio = video_drvdata(file);
+
+ strlcpy(v->driver, "radio-keene", sizeof(v->driver));
+ strlcpy(v->card, "Keene FM Transmitter", sizeof(v->card));
+ usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
+ v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_MODULATOR;
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int vidioc_g_modulator(struct file *file, void *priv,
+ struct v4l2_modulator *v)
+{
+ struct keene_device *radio = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ strlcpy(v->name, "FM", sizeof(v->name));
+ v->rangelow = FREQ_MIN * FREQ_MUL;
+ v->rangehigh = FREQ_MAX * FREQ_MUL;
+ v->txsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+ return 0;
+}
+
+static int vidioc_s_modulator(struct file *file, void *priv,
+ struct v4l2_modulator *v)
+{
+ struct keene_device *radio = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ radio->stereo = (v->txsubchans == V4L2_TUNER_SUB_STEREO);
+ return keene_cmd_set(radio);
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct keene_device *radio = video_drvdata(file);
+
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+ f->frequency = clamp(f->frequency,
+ FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL);
+ return keene_cmd_main(radio, f->frequency, true);
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct keene_device *radio = video_drvdata(file);
+
+ if (f->tuner != 0)
+ return -EINVAL;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = radio->curfreq;
+ return 0;
+}
+
+static int keene_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ static const u8 db2tx[] = {
+ /* -15, -12, -9, -6, -3, 0 dB */
+ 0x03, 0x13, 0x02, 0x12, 0x22, 0x32,
+ /* 3, 6, 9, 12, 15, 18 dB */
+ 0x21, 0x31, 0x20, 0x30, 0x40, 0x50
+ };
+ struct keene_device *radio =
+ container_of(ctrl->handler, struct keene_device, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ radio->muted = ctrl->val;
+ return keene_cmd_main(radio, 0, true);
+
+ case V4L2_CID_TUNE_POWER_LEVEL:
+ /* To go from dBuV to the register value we apply the
+ following formula: */
+ radio->pa = (ctrl->val - 71) * 100 / 62;
+ return keene_cmd_main(radio, 0, true);
+
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ radio->preemph_75_us = ctrl->val == V4L2_PREEMPHASIS_75_uS;
+ return keene_cmd_set(radio);
+
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN:
+ radio->tx = db2tx[(ctrl->val - ctrl->minimum) / ctrl->step];
+ return keene_cmd_set(radio);
+ }
+ return -EINVAL;
+}
+
+static int vidioc_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_CTRL:
+ return v4l2_event_subscribe(fh, sub, 0);
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/* File system interface */
+static const struct v4l2_file_operations usb_keene_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ctrl_ops keene_ctrl_ops = {
+ .s_ctrl = keene_s_ctrl,
+};
+
+static const struct v4l2_ioctl_ops usb_keene_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_modulator = vidioc_g_modulator,
+ .vidioc_s_modulator = vidioc_s_modulator,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = vidioc_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static void usb_keene_video_device_release(struct v4l2_device *v4l2_dev)
+{
+ struct keene_device *radio = to_keene_dev(v4l2_dev);
+
+ /* free rest memory */
+ v4l2_ctrl_handler_free(&radio->hdl);
+ kfree(radio->buffer);
+ kfree(radio);
+}
+
+/* check if the device is present and register with v4l and usb if it is */
+static int usb_keene_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct keene_device *radio;
+ struct v4l2_ctrl_handler *hdl;
+ int retval = 0;
+
+ /*
+ * The Keene FM transmitter USB device has the same USB ID as
+ * the Logitech AudioHub Speaker, but it should ignore the hid.
+ * Check if the name is that of the Keene device.
+ * If not, then someone connected the AudioHub and we shouldn't
+ * attempt to handle this driver.
+ * For reference: the product name of the AudioHub is
+ * "AudioHub Speaker".
+ */
+ if (dev->product && strcmp(dev->product, "B-LINK USB Audio "))
+ return -ENODEV;
+
+ radio = kzalloc(sizeof(struct keene_device), GFP_KERNEL);
+ if (radio)
+ radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
+
+ if (!radio || !radio->buffer) {
+ dev_err(&intf->dev, "kmalloc for keene_device failed\n");
+ kfree(radio);
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ hdl = &radio->hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_MUTE,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std_menu(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS,
+ V4L2_PREEMPHASIS_75_uS, 1, V4L2_PREEMPHASIS_50_uS);
+ v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_POWER_LEVEL,
+ 84, 118, 1, 118);
+ v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_COMPRESSION_GAIN,
+ -15, 18, 3, 0);
+ radio->pa = 118;
+ radio->tx = 0x32;
+ radio->stereo = true;
+ radio->curfreq = 95.16 * FREQ_MUL;
+ if (hdl->error) {
+ retval = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ goto err_v4l2;
+ }
+ retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
+ if (retval < 0) {
+ dev_err(&intf->dev, "couldn't register v4l2_device\n");
+ goto err_v4l2;
+ }
+
+ mutex_init(&radio->lock);
+
+ radio->v4l2_dev.ctrl_handler = hdl;
+ radio->v4l2_dev.release = usb_keene_video_device_release;
+ strlcpy(radio->vdev.name, radio->v4l2_dev.name,
+ sizeof(radio->vdev.name));
+ radio->vdev.v4l2_dev = &radio->v4l2_dev;
+ radio->vdev.fops = &usb_keene_fops;
+ radio->vdev.ioctl_ops = &usb_keene_ioctl_ops;
+ radio->vdev.lock = &radio->lock;
+ radio->vdev.release = video_device_release_empty;
+
+ radio->usbdev = interface_to_usbdev(intf);
+ radio->intf = intf;
+ usb_set_intfdata(intf, &radio->v4l2_dev);
+
+ video_set_drvdata(&radio->vdev, radio);
+ set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags);
+
+ retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
+ if (retval < 0) {
+ dev_err(&intf->dev, "could not register video device\n");
+ goto err_vdev;
+ }
+ v4l2_ctrl_handler_setup(hdl);
+ dev_info(&intf->dev, "V4L2 device registered as %s\n",
+ video_device_node_name(&radio->vdev));
+ return 0;
+
+err_vdev:
+ v4l2_device_unregister(&radio->v4l2_dev);
+err_v4l2:
+ kfree(radio->buffer);
+ kfree(radio);
+err:
+ return retval;
+}
+
+/* USB subsystem interface */
+static struct usb_driver usb_keene_driver = {
+ .name = "radio-keene",
+ .probe = usb_keene_probe,
+ .disconnect = usb_keene_disconnect,
+ .id_table = usb_keene_device_table,
+};
+
+static int __init keene_init(void)
+{
+ int retval = usb_register(&usb_keene_driver);
+
+ if (retval)
+ pr_err(KBUILD_MODNAME
+ ": usb_register failed. Error number %d\n", retval);
+
+ return retval;
+}
+
+static void __exit keene_exit(void)
+{
+ usb_deregister(&usb_keene_driver);
+}
+
+module_init(keene_init);
+module_exit(keene_exit);
+
diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
index f872a54cf3d9..740a3d5520c7 100644
--- a/drivers/media/radio/radio-maxiradio.c
+++ b/drivers/media/radio/radio-maxiradio.c
@@ -42,67 +42,37 @@
#include
#include
#include
+#include
#include
#include
-
-#define DRIVER_VERSION "0.7.8"
-
+#include
+#include
+#include
MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
-MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
+MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000.");
MODULE_LICENSE("GPL");
-MODULE_VERSION(DRIVER_VERSION);
+MODULE_VERSION("1.0.0");
static int radio_nr = -1;
-module_param(radio_nr, int, 0);
-
-static int debug;
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "activates debug info");
-
-#define dprintk(dev, num, fmt, arg...) \
- v4l2_dbg(num, debug, &dev->v4l2_dev, fmt, ## arg)
-
-#ifndef PCI_VENDOR_ID_GUILLEMOT
-#define PCI_VENDOR_ID_GUILLEMOT 0x5046
-#endif
-
-#ifndef PCI_DEVICE_ID_GUILLEMOT
-#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
-#endif
-
+module_param(radio_nr, int, 0644);
+MODULE_PARM_DESC(radio_nr, "Radio device number");
/* TEA5757 pin mappings */
static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16;
-#define FREQ_LO (87 * 16000)
-#define FREQ_HI (108 * 16000)
-
-#define FREQ_IF 171200 /* 10.7*16000 */
-#define FREQ_STEP 200 /* 12.5*16 */
-
-/* (x==fmhz*16*1000) -> bits */
-#define FREQ2BITS(x) \
- ((((unsigned int)(x) + FREQ_IF + (FREQ_STEP << 1)) / (FREQ_STEP << 2)) << 2)
-
-#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
+static atomic_t maxiradio_instance = ATOMIC_INIT(0);
+#define PCI_VENDOR_ID_GUILLEMOT 0x5046
+#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
struct maxiradio
{
+ struct snd_tea575x tea;
struct v4l2_device v4l2_dev;
- struct video_device vdev;
struct pci_dev *pdev;
u16 io; /* base of radio io */
- u16 muted; /* VIDEO_AUDIO_MUTE */
- u16 stereo; /* VIDEO_TUNER_STEREO_ON */
- u16 tuned; /* signal strength (0 or 0xffff) */
-
- unsigned long freq;
-
- struct mutex lock;
};
static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev)
@@ -110,259 +80,41 @@ static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev)
return container_of(v4l2_dev, struct maxiradio, v4l2_dev);
}
-static void outbit(unsigned long bit, u16 io)
+static void maxiradio_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
- int val = power | wren | (bit ? data : 0);
+ struct maxiradio *dev = tea->private_data;
+ u8 bits = 0;
- outb(val, io);
- udelay(4);
- outb(val | clk, io);
- udelay(4);
- outb(val, io);
- udelay(4);
+ bits |= (pins & TEA575X_DATA) ? data : 0;
+ bits |= (pins & TEA575X_CLK) ? clk : 0;
+ bits |= (pins & TEA575X_WREN) ? wren : 0;
+ bits |= power;
+
+ outb(bits, dev->io);
}
-static void turn_power(struct maxiradio *dev, int p)
+/* Note: this card cannot read out the data of the shift registers,
+ only the mono/stereo pin works. */
+static u8 maxiradio_tea575x_get_pins(struct snd_tea575x *tea)
{
- if (p != 0) {
- dprintk(dev, 1, "Radio powered on\n");
- outb(power, dev->io);
- } else {
- dprintk(dev, 1, "Radio powered off\n");
- outb(0, dev->io);
- }
+ struct maxiradio *dev = tea->private_data;
+ u8 bits = inb(dev->io);
+
+ return ((bits & data) ? TEA575X_DATA : 0) |
+ ((bits & mo_st) ? TEA575X_MOST : 0);
}
-static void set_freq(struct maxiradio *dev, u32 freq)
+static void maxiradio_tea575x_set_direction(struct snd_tea575x *tea, bool output)
{
- unsigned long int si;
- int bl;
- int io = dev->io;
- int val = FREQ2BITS(freq);
-
- /* TEA5757 shift register bits (see pdf) */
-
- outbit(0, io); /* 24 search */
- outbit(1, io); /* 23 search up/down */
-
- outbit(0, io); /* 22 stereo/mono */
-
- outbit(0, io); /* 21 band */
- outbit(0, io); /* 20 band (only 00=FM works I think) */
-
- outbit(0, io); /* 19 port ? */
- outbit(0, io); /* 18 port ? */
-
- outbit(0, io); /* 17 search level */
- outbit(0, io); /* 16 search level */
-
- si = 0x8000;
- for (bl = 1; bl <= 16; bl++) {
- outbit(val & si, io);
- si >>= 1;
- }
-
- dprintk(dev, 1, "Radio freq set to %d.%02d MHz\n",
- freq / 16000,
- freq % 16000 * 100 / 16000);
-
- turn_power(dev, 1);
}
-static int get_stereo(u16 io)
-{
- outb(power,io);
- udelay(4);
-
- return !(inb(io) & mo_st);
-}
-
-static int get_tune(u16 io)
-{
- outb(power+clk,io);
- udelay(4);
-
- return !(inb(io) & mo_st);
-}
-
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- struct maxiradio *dev = video_drvdata(file);
-
- strlcpy(v->driver, "radio-maxiradio", sizeof(v->driver));
- strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof(v->card));
- snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct maxiradio *dev = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- mutex_lock(&dev->lock);
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = FREQ_LO;
- v->rangehigh = FREQ_HI;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- if (get_stereo(dev->io))
- v->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xffff * get_tune(dev->io);
- mutex_unlock(&dev->lock);
-
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct maxiradio *dev = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) {
- dprintk(dev, 1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n",
- f->frequency / 16000,
- f->frequency % 16000 * 100 / 16000,
- FREQ_LO / 16000, FREQ_HI / 16000);
-
- return -EINVAL;
- }
-
- mutex_lock(&dev->lock);
- dev->freq = f->frequency;
- set_freq(dev, dev->freq);
- msleep(125);
- mutex_unlock(&dev->lock);
-
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct maxiradio *dev = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = dev->freq;
-
- dprintk(dev, 4, "radio freq is %d.%02d MHz",
- f->frequency / 16000,
- f->frequency % 16000 * 100 / 16000);
-
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct maxiradio *dev = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = dev->muted;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct maxiradio *dev = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- mutex_lock(&dev->lock);
- dev->muted = ctrl->value;
- if (dev->muted)
- turn_power(dev, 0);
- else
- set_freq(dev, dev->freq);
- mutex_unlock(&dev->lock);
- return 0;
- }
-
- return -EINVAL;
-}
-
-static const struct v4l2_file_operations maxiradio_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static struct snd_tea575x_ops maxiradio_tea_ops = {
+ .set_pins = maxiradio_tea575x_set_pins,
+ .get_pins = maxiradio_tea575x_get_pins,
+ .set_direction = maxiradio_tea575x_set_direction,
};
-static const struct v4l2_ioctl_ops maxiradio_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
-};
-
-static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit maxiradio_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct maxiradio *dev;
struct v4l2_device *v4l2_dev;
@@ -375,63 +127,60 @@ static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_d
}
v4l2_dev = &dev->v4l2_dev;
- mutex_init(&dev->lock);
- dev->pdev = pdev;
- dev->muted = 1;
- dev->freq = FREQ_LO;
-
- strlcpy(v4l2_dev->name, "maxiradio", sizeof(v4l2_dev->name));
+ v4l2_device_set_name(v4l2_dev, "maxiradio", &maxiradio_instance);
retval = v4l2_device_register(&pdev->dev, v4l2_dev);
if (retval < 0) {
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
goto errfr;
}
+ dev->tea.private_data = dev;
+ dev->tea.ops = &maxiradio_tea_ops;
+ /* The data pin cannot be read. This may be a hardware limitation, or
+ we just don't know how to read it. */
+ dev->tea.cannot_read_data = true;
+ dev->tea.v4l2_dev = v4l2_dev;
+ dev->tea.radio_nr = radio_nr;
+ strlcpy(dev->tea.card, "Maxi Radio FM2000", sizeof(dev->tea.card));
+ snprintf(dev->tea.bus_info, sizeof(dev->tea.bus_info),
+ "PCI:%s", pci_name(pdev));
+
+ retval = -ENODEV;
if (!request_region(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) {
- v4l2_err(v4l2_dev, "can't reserve I/O ports\n");
- goto err_out;
+ pci_resource_len(pdev, 0), v4l2_dev->name)) {
+ dev_err(&pdev->dev, "can't reserve I/O ports\n");
+ goto err_hdl;
}
if (pci_enable_device(pdev))
goto err_out_free_region;
dev->io = pci_resource_start(pdev, 0);
- strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
- dev->vdev.v4l2_dev = v4l2_dev;
- dev->vdev.fops = &maxiradio_fops;
- dev->vdev.ioctl_ops = &maxiradio_ioctl_ops;
- dev->vdev.release = video_device_release_empty;
- video_set_drvdata(&dev->vdev, dev);
-
- if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_err(v4l2_dev, "can't register device!");
+ if (snd_tea575x_init(&dev->tea)) {
+ printk(KERN_ERR "radio-maxiradio: Unable to detect TEA575x tuner\n");
goto err_out_free_region;
}
-
- v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n");
-
- v4l2_info(v4l2_dev, "found Guillemot MAXI Radio device (io = 0x%x)\n",
- dev->io);
return 0;
err_out_free_region:
release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
-err_out:
+err_hdl:
v4l2_device_unregister(v4l2_dev);
errfr:
kfree(dev);
- return -ENODEV;
+ return retval;
}
-static void __devexit maxiradio_remove_one(struct pci_dev *pdev)
+static void __devexit maxiradio_remove(struct pci_dev *pdev)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
struct maxiradio *dev = to_maxiradio(v4l2_dev);
- video_unregister_device(&dev->vdev);
- v4l2_device_unregister(&dev->v4l2_dev);
+ snd_tea575x_exit(&dev->tea);
+ /* Turn off power */
+ outb(0, dev->io);
+ v4l2_device_unregister(v4l2_dev);
release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
}
@@ -446,19 +195,19 @@ MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl);
static struct pci_driver maxiradio_driver = {
.name = "radio-maxiradio",
.id_table = maxiradio_pci_tbl,
- .probe = maxiradio_init_one,
- .remove = __devexit_p(maxiradio_remove_one),
+ .probe = maxiradio_probe,
+ .remove = __devexit_p(maxiradio_remove),
};
-static int __init maxiradio_radio_init(void)
+static int __init maxiradio_init(void)
{
return pci_register_driver(&maxiradio_driver);
}
-static void __exit maxiradio_radio_exit(void)
+static void __exit maxiradio_exit(void)
{
pci_unregister_driver(&maxiradio_driver);
}
-module_init(maxiradio_radio_init);
-module_exit(maxiradio_radio_exit);
+module_init(maxiradio_init);
+module_exit(maxiradio_exit);
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index 3628be617ee9..b275c5d0fe9a 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -1,11 +1,12 @@
-/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
+/*
+ * RadioTrack II driver
+ * Copyright 1998 Ben Pfaff
*
* Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
* Converted to new API by Alan Cox
* Various bugfixes and enhancements by Russell Kroll
*
- * TODO: Allow for more than one of these foolish entities :-)
- *
+ * Converted to the radio-isa framework by Hans Verkuil
* Converted to V4L2 API by Mauro Carvalho Chehab
*/
@@ -18,323 +19,120 @@
#include /* outb, outb_p */
#include
#include
+#include "radio-isa.h"
MODULE_AUTHOR("Ben Pfaff");
MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
+MODULE_VERSION("0.1.99");
#ifndef CONFIG_RADIO_RTRACK2_PORT
#define CONFIG_RADIO_RTRACK2_PORT -1
#endif
-static int io = CONFIG_RADIO_RTRACK2_PORT;
-static int radio_nr = -1;
+#define RTRACK2_MAX 2
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
-module_param(radio_nr, int, 0);
+static int io[RTRACK2_MAX] = { [0] = CONFIG_RADIO_RTRACK2_PORT,
+ [1 ... (RTRACK2_MAX - 1)] = -1 };
+static int radio_nr[RTRACK2_MAX] = { [0 ... (RTRACK2_MAX - 1)] = -1 };
-struct rtrack2
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the RadioTrack card (0x20f or 0x30f)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
+
+static struct radio_isa_card *rtrack2_alloc(void)
{
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
- unsigned long curfreq;
- int muted;
- struct mutex lock;
-};
-
-static struct rtrack2 rtrack2_card;
-
-
-/* local things */
-
-static void rt_mute(struct rtrack2 *dev)
-{
- if (dev->muted)
- return;
- mutex_lock(&dev->lock);
- outb(1, dev->io);
- mutex_unlock(&dev->lock);
- dev->muted = 1;
+ return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL);
}
-static void rt_unmute(struct rtrack2 *dev)
+static void zero(struct radio_isa_card *isa)
{
- if(dev->muted == 0)
- return;
- mutex_lock(&dev->lock);
- outb(0, dev->io);
- mutex_unlock(&dev->lock);
- dev->muted = 0;
+ outb_p(1, isa->io);
+ outb_p(3, isa->io);
+ outb_p(1, isa->io);
}
-static void zero(struct rtrack2 *dev)
+static void one(struct radio_isa_card *isa)
{
- outb_p(1, dev->io);
- outb_p(3, dev->io);
- outb_p(1, dev->io);
+ outb_p(5, isa->io);
+ outb_p(7, isa->io);
+ outb_p(5, isa->io);
}
-static void one(struct rtrack2 *dev)
-{
- outb_p(5, dev->io);
- outb_p(7, dev->io);
- outb_p(5, dev->io);
-}
-
-static int rt_setfreq(struct rtrack2 *dev, unsigned long freq)
+static int rtrack2_s_frequency(struct radio_isa_card *isa, u32 freq)
{
int i;
- mutex_lock(&dev->lock);
- dev->curfreq = freq;
freq = freq / 200 + 856;
- outb_p(0xc8, dev->io);
- outb_p(0xc9, dev->io);
- outb_p(0xc9, dev->io);
+ outb_p(0xc8, isa->io);
+ outb_p(0xc9, isa->io);
+ outb_p(0xc9, isa->io);
for (i = 0; i < 10; i++)
- zero(dev);
+ zero(isa);
for (i = 14; i >= 0; i--)
if (freq & (1 << i))
- one(dev);
+ one(isa);
else
- zero(dev);
+ zero(isa);
- outb_p(0xc8, dev->io);
- if (!dev->muted)
- outb_p(0, dev->io);
-
- mutex_unlock(&dev->lock);
+ outb_p(0xc8, isa->io);
+ if (!v4l2_ctrl_g_ctrl(isa->mute))
+ outb_p(0, isa->io);
return 0;
}
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
+static u32 rtrack2_g_signal(struct radio_isa_card *isa)
{
- strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver));
- strlcpy(v->card, "RadioTrack II", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ /* bit set = no signal present */
+ return (inb(isa->io) & 2) ? 0 : 0xffff;
+}
+
+static int rtrack2_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
+{
+ outb(mute, isa->io);
return 0;
}
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int rt_getsigstr(struct rtrack2 *dev)
-{
- int sig = 1;
-
- mutex_lock(&dev->lock);
- if (inb(dev->io) & 2) /* bit set = no signal present */
- sig = 0;
- mutex_unlock(&dev->lock);
- return sig;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct rtrack2 *rt = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 88 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xFFFF * rt_getsigstr(rt);
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct rtrack2 *rt = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- rt_setfreq(rt, f->frequency);
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct rtrack2 *rt = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = rt->curfreq;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535, 65535);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct rtrack2 *rt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = rt->muted;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- if (rt->muted)
- ctrl->value = 0;
- else
- ctrl->value = 65535;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct rtrack2 *rt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- rt_mute(rt);
- else
- rt_unmute(rt);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- if (ctrl->value)
- rt_unmute(rt);
- else
- rt_mute(rt);
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static const struct v4l2_file_operations rtrack2_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops rtrack2_ops = {
+ .alloc = rtrack2_alloc,
+ .s_mute_volume = rtrack2_s_mute_volume,
+ .s_frequency = rtrack2_s_frequency,
+ .g_signal = rtrack2_g_signal,
};
-static const struct v4l2_ioctl_ops rtrack2_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
+static const int rtrack2_ioports[] = { 0x20f, 0x30f };
+
+static struct radio_isa_driver rtrack2_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-rtrack2",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = rtrack2_ioports,
+ .num_of_io_ports = ARRAY_SIZE(rtrack2_ioports),
+ .region_size = 4,
+ .card = "AIMSlab RadioTrack II",
+ .ops = &rtrack2_ops,
+ .has_stereo = true,
};
static int __init rtrack2_init(void)
{
- struct rtrack2 *dev = &rtrack2_card;
- struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "rtrack2", sizeof(v4l2_dev->name));
- dev->io = io;
- if (dev->io == -1) {
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or io=0x30c\n");
- return -EINVAL;
- }
- if (!request_region(dev->io, 4, "rtrack2")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", dev->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(dev->io, 4);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
-
- strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
- dev->vdev.v4l2_dev = v4l2_dev;
- dev->vdev.fops = &rtrack2_fops;
- dev->vdev.ioctl_ops = &rtrack2_ioctl_ops;
- dev->vdev.release = video_device_release_empty;
- video_set_drvdata(&dev->vdev, dev);
-
- /* mute card - prevents noisy bootups */
- outb(1, dev->io);
- dev->muted = 1;
-
- mutex_init(&dev->lock);
- if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
- release_region(dev->io, 4);
- return -EINVAL;
- }
-
- v4l2_info(v4l2_dev, "AIMSlab Radiotrack II card driver.\n");
-
- return 0;
+ return isa_register_driver(&rtrack2_driver.driver, RTRACK2_MAX);
}
static void __exit rtrack2_exit(void)
{
- struct rtrack2 *dev = &rtrack2_card;
-
- video_unregister_device(&dev->vdev);
- v4l2_device_unregister(&dev->v4l2_dev);
- release_region(dev->io, 4);
+ isa_unregister_driver(&rtrack2_driver.driver);
}
module_init(rtrack2_init);
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index 7ab9afadf29b..7c69214334bf 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -9,16 +9,23 @@
#include
#include /* Modules */
#include /* Initdata */
+#include
#include /* request_region */
#include /* outb, outb_p */
+#include
#include
MODULE_AUTHOR("Ondrej Zary");
MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
MODULE_LICENSE("GPL");
+static int radio_nr = -1;
+module_param(radio_nr, int, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device number");
+
struct fmr2 {
int io;
+ struct v4l2_device v4l2_dev;
struct snd_tea575x tea;
struct v4l2_ctrl *volume;
struct v4l2_ctrl *balance;
@@ -26,7 +33,6 @@ struct fmr2 {
/* the port is hardwired so no need to support multiple cards */
#define FMR2_PORT 0x384
-static struct fmr2 fmr2_card;
/* TEA575x tuner pins */
#define STR_DATA (1 << 0)
@@ -180,26 +186,46 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea)
return 0;
}
-static int __init fmr2_init(void)
+static int __devinit fmr2_probe(struct device *pdev, unsigned int dev)
{
- struct fmr2 *fmr2 = &fmr2_card;
+ struct fmr2 *fmr2;
+ int err;
+ fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL);
+ if (fmr2 == NULL)
+ return -ENOMEM;
+
+ strlcpy(fmr2->v4l2_dev.name, dev_name(pdev),
+ sizeof(fmr2->v4l2_dev.name));
fmr2->io = FMR2_PORT;
- if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+ if (!request_region(fmr2->io, 2, fmr2->v4l2_dev.name)) {
printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io);
+ kfree(fmr2);
return -EBUSY;
}
+ dev_set_drvdata(pdev, fmr2);
+ err = v4l2_device_register(pdev, &fmr2->v4l2_dev);
+ if (err < 0) {
+ v4l2_err(&fmr2->v4l2_dev, "Could not register v4l2_device\n");
+ release_region(fmr2->io, 2);
+ kfree(fmr2);
+ return err;
+ }
+ fmr2->tea.v4l2_dev = &fmr2->v4l2_dev;
fmr2->tea.private_data = fmr2;
+ fmr2->tea.radio_nr = radio_nr;
fmr2->tea.ops = &fmr2_tea_ops;
fmr2->tea.ext_init = fmr2_tea_ext_init;
strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
- strcpy(fmr2->tea.bus_info, "ISA");
+ snprintf(fmr2->tea.bus_info, sizeof(fmr2->tea.bus_info), "ISA:%s",
+ fmr2->v4l2_dev.name);
if (snd_tea575x_init(&fmr2->tea)) {
printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n");
release_region(fmr2->io, 2);
+ kfree(fmr2);
return -ENODEV;
}
@@ -207,12 +233,33 @@ static int __init fmr2_init(void)
return 0;
}
-static void __exit fmr2_exit(void)
+static int __exit fmr2_remove(struct device *pdev, unsigned int dev)
{
- struct fmr2 *fmr2 = &fmr2_card;
+ struct fmr2 *fmr2 = dev_get_drvdata(pdev);
snd_tea575x_exit(&fmr2->tea);
release_region(fmr2->io, 2);
+ v4l2_device_unregister(&fmr2->v4l2_dev);
+ kfree(fmr2);
+ return 0;
+}
+
+struct isa_driver fmr2_driver = {
+ .probe = fmr2_probe,
+ .remove = fmr2_remove,
+ .driver = {
+ .name = "radio-sf16fmr2",
+ },
+};
+
+static int __init fmr2_init(void)
+{
+ return isa_register_driver(&fmr2_driver, 1);
+}
+
+static void __exit fmr2_exit(void)
+{
+ isa_unregister_driver(&fmr2_driver);
}
module_init(fmr2_init);
diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
index db20904d01f0..6b1fae32b483 100644
--- a/drivers/media/radio/radio-tea5764.c
+++ b/drivers/media/radio/radio-tea5764.c
@@ -575,21 +575,7 @@ static struct i2c_driver tea5764_i2c_driver = {
.id_table = tea5764_id,
};
-/* init the driver */
-static int __init tea5764_init(void)
-{
- int ret = i2c_add_driver(&tea5764_i2c_driver);
-
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ": "
- DRIVER_DESC "\n");
- return ret;
-}
-
-/* cleanup the driver */
-static void __exit tea5764_exit(void)
-{
- i2c_del_driver(&tea5764_i2c_driver);
-}
+module_i2c_driver(tea5764_i2c_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
@@ -600,6 +586,3 @@ module_param(use_xtal, int, 0);
MODULE_PARM_DESC(use_xtal, "Chip have a xtal connected in board");
module_param(radio_nr, int, 0);
MODULE_PARM_DESC(radio_nr, "video4linux device number to use");
-
-module_init(tea5764_init);
-module_exit(tea5764_exit);
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
index f2ed9cc3cf3b..be10a802e3a9 100644
--- a/drivers/media/radio/radio-terratec.c
+++ b/drivers/media/radio/radio-terratec.c
@@ -16,11 +16,7 @@
* Frequency control is done digitally -- ie out(port,encodefreq(95.8));
* Volume Control is done digitally
*
- * there is a I2C controlled RDS decoder (SAA6588) onboard, which i would like to support someday
- * (as soon i have understand how to get started :)
- * If you can help me out with that, please contact me!!
- *
- *
+ * Converted to the radio-isa framework by Hans Verkuil
* Converted to V4L2 API by Mauro Carvalho Chehab
*/
@@ -30,43 +26,24 @@
#include /* kernel radio structs */
#include
#include /* outb, outb_p */
+#include
#include
#include
+#include "radio-isa.h"
-MODULE_AUTHOR("R.OFFERMANNS & others");
+MODULE_AUTHOR("R. Offermans & others");
MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
+MODULE_VERSION("0.1.99");
-#ifndef CONFIG_RADIO_TERRATEC_PORT
-#define CONFIG_RADIO_TERRATEC_PORT 0x590
-#endif
-
-static int io = CONFIG_RADIO_TERRATEC_PORT;
+/* Note: there seems to be only one possible port (0x590), but without
+ hardware this is hard to verify. For now, this is the only one we will
+ support. */
+static int io = 0x590;
static int radio_nr = -1;
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
-module_param(radio_nr, int, 0);
-
-static struct v4l2_queryctrl radio_qctrl[] = {
- {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .default_value = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_AUDIO_VOLUME,
- .name = "Volume",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0xff,
- .type = V4L2_CTRL_TYPE_INTEGER,
- }
-};
+module_param(radio_nr, int, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device number");
#define WRT_DIS 0x00
#define CLK_OFF 0x00
@@ -76,63 +53,24 @@ static struct v4l2_queryctrl radio_qctrl[] = {
#define CLK_ON 0x08
#define WRT_EN 0x10
-struct terratec
+static struct radio_isa_card *terratec_alloc(void)
{
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
- int curvol;
- unsigned long curfreq;
- int muted;
- struct mutex lock;
-};
+ return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL);
+}
-static struct terratec terratec_card;
-
-/* local things */
-
-static void tt_write_vol(struct terratec *tt, int volume)
+static int terratec_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
int i;
- volume = volume + (volume * 32); /* change both channels */
- mutex_lock(&tt->lock);
+ if (mute)
+ vol = 0;
+ vol = vol + (vol * 32); /* change both channels */
for (i = 0; i < 8; i++) {
- if (volume & (0x80 >> i))
- outb(0x80, tt->io + 1);
+ if (vol & (0x80 >> i))
+ outb(0x80, isa->io + 1);
else
- outb(0x00, tt->io + 1);
+ outb(0x00, isa->io + 1);
}
- mutex_unlock(&tt->lock);
-}
-
-
-
-static void tt_mute(struct terratec *tt)
-{
- tt->muted = 1;
- tt_write_vol(tt, 0);
-}
-
-static int tt_setvol(struct terratec *tt, int vol)
-{
- if (vol == tt->curvol) { /* requested volume = current */
- if (tt->muted) { /* user is unmuting the card */
- tt->muted = 0;
- tt_write_vol(tt, vol); /* enable card */
- }
- return 0;
- }
-
- if (vol == 0) { /* volume = 0 means mute the card */
- tt_write_vol(tt, 0); /* "turn off card" by setting vol to 0 */
- tt->curvol = vol; /* track the volume state! */
- return 0;
- }
-
- tt->muted = 0;
- tt_write_vol(tt, vol);
- tt->curvol = vol;
return 0;
}
@@ -140,20 +78,15 @@ static int tt_setvol(struct terratec *tt, int vol)
/* this is the worst part in this driver */
/* many more or less strange things are going on here, but hey, it works :) */
-static int tt_setfreq(struct terratec *tt, unsigned long freq1)
+static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq)
{
- int freq;
int i;
int p;
- int temp;
+ int temp;
long rest;
unsigned char buffer[25]; /* we have to bit shift 25 registers */
- mutex_lock(&tt->lock);
-
- tt->curfreq = freq1;
-
- freq = freq1 / 160; /* convert the freq. to a nice to handle value */
+ freq = freq / 160; /* convert the freq. to a nice to handle value */
memset(buffer, 0, sizeof(buffer));
rest = freq * 10 + 10700; /* I once had understood what is going on here */
@@ -175,239 +108,61 @@ static int tt_setfreq(struct terratec *tt, unsigned long freq1)
for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */
if (buffer[i] == 1) {
- outb(WRT_EN | DATA, tt->io);
- outb(WRT_EN | DATA | CLK_ON, tt->io);
- outb(WRT_EN | DATA, tt->io);
+ outb(WRT_EN | DATA, isa->io);
+ outb(WRT_EN | DATA | CLK_ON, isa->io);
+ outb(WRT_EN | DATA, isa->io);
} else {
- outb(WRT_EN | 0x00, tt->io);
- outb(WRT_EN | 0x00 | CLK_ON, tt->io);
+ outb(WRT_EN | 0x00, isa->io);
+ outb(WRT_EN | 0x00 | CLK_ON, isa->io);
}
}
- outb(0x00, tt->io);
-
- mutex_unlock(&tt->lock);
-
+ outb(0x00, isa->io);
return 0;
}
-static int tt_getsigstr(struct terratec *tt)
+static u32 terratec_g_signal(struct radio_isa_card *isa)
{
- if (inb(tt->io) & 2) /* bit set = no signal present */
- return 0;
- return 1; /* signal present */
+ /* bit set = no signal present */
+ return (inb(isa->io) & 2) ? 0 : 0xffff;
}
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-terratec", sizeof(v->driver));
- strlcpy(v->card, "ActiveRadio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct terratec *tt = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 87 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xFFFF * tt_getsigstr(tt);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct terratec *tt = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- tt_setfreq(tt, f->frequency);
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct terratec *tt = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = tt->curfreq;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
- if (qc->id && qc->id == radio_qctrl[i].id) {
- memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct terratec *tt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (tt->muted)
- ctrl->value = 1;
- else
- ctrl->value = 0;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = tt->curvol * 6554;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct terratec *tt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- tt_mute(tt);
- else
- tt_setvol(tt,tt->curvol);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- tt_setvol(tt,ctrl->value);
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static const struct v4l2_file_operations terratec_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops terratec_ops = {
+ .alloc = terratec_alloc,
+ .s_mute_volume = terratec_s_mute_volume,
+ .s_frequency = terratec_s_frequency,
+ .g_signal = terratec_g_signal,
};
-static const struct v4l2_ioctl_ops terratec_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
+static const int terratec_ioports[] = { 0x590 };
+
+static struct radio_isa_driver terratec_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-terratec",
+ },
+ },
+ .io_params = &io,
+ .radio_nr_params = &radio_nr,
+ .io_ports = terratec_ioports,
+ .num_of_io_ports = ARRAY_SIZE(terratec_ioports),
+ .region_size = 2,
+ .card = "TerraTec ActiveRadio",
+ .ops = &terratec_ops,
+ .has_stereo = true,
+ .max_volume = 10,
};
static int __init terratec_init(void)
{
- struct terratec *tt = &terratec_card;
- struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "terratec", sizeof(v4l2_dev->name));
- tt->io = io;
- if (tt->io == -1) {
- v4l2_err(v4l2_dev, "you must set an I/O address with io=0x590 or 0x591\n");
- return -EINVAL;
- }
- if (!request_region(tt->io, 2, "terratec")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(tt->io, 2);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
-
- strlcpy(tt->vdev.name, v4l2_dev->name, sizeof(tt->vdev.name));
- tt->vdev.v4l2_dev = v4l2_dev;
- tt->vdev.fops = &terratec_fops;
- tt->vdev.ioctl_ops = &terratec_ioctl_ops;
- tt->vdev.release = video_device_release_empty;
- video_set_drvdata(&tt->vdev, tt);
-
- mutex_init(&tt->lock);
-
- /* mute card - prevents noisy bootups */
- tt_write_vol(tt, 0);
-
- if (video_register_device(&tt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(&tt->v4l2_dev);
- release_region(tt->io, 2);
- return -EINVAL;
- }
-
- v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver.\n");
- return 0;
+ return isa_register_driver(&terratec_driver.driver, 1);
}
static void __exit terratec_exit(void)
{
- struct terratec *tt = &terratec_card;
- struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
-
- video_unregister_device(&tt->vdev);
- v4l2_device_unregister(&tt->v4l2_dev);
- release_region(tt->io, 2);
- v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver unloaded.\n");
+ isa_unregister_driver(&terratec_driver.driver);
}
module_init(terratec_init);
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
index b3f45a019d82..26a8c6002121 100644
--- a/drivers/media/radio/radio-trust.c
+++ b/drivers/media/radio/radio-trust.c
@@ -21,13 +21,15 @@
#include
#include
#include
+#include
#include
#include
+#include "radio-isa.h"
MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
MODULE_DESCRIPTION("A driver for the Trust FM Radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
+MODULE_VERSION("0.1.99");
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
@@ -35,39 +37,38 @@ MODULE_VERSION("0.0.3");
#define CONFIG_RADIO_TRUST_PORT -1
#endif
-static int io = CONFIG_RADIO_TRUST_PORT;
-static int radio_nr = -1;
+#define TRUST_MAX 2
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)");
-module_param(radio_nr, int, 0);
+static int io[TRUST_MAX] = { [0] = CONFIG_RADIO_TRUST_PORT,
+ [1 ... (TRUST_MAX - 1)] = -1 };
+static int radio_nr[TRUST_MAX] = { [0 ... (TRUST_MAX - 1)] = -1 };
+
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the Trust FM Radio card (0x350 or 0x358)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
struct trust {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
+ struct radio_isa_card isa;
int ioval;
- __u16 curvol;
- __u16 curbass;
- __u16 curtreble;
- int muted;
- unsigned long curfreq;
- int curstereo;
- int curmute;
- struct mutex lock;
};
-static struct trust trust_card;
+static struct radio_isa_card *trust_alloc(void)
+{
+ struct trust *tr = kzalloc(sizeof(*tr), GFP_KERNEL);
+
+ return tr ? &tr->isa : NULL;
+}
/* i2c addresses */
#define TDA7318_ADDR 0x88
#define TSA6060T_ADDR 0xc4
-#define TR_DELAY do { inb(tr->io); inb(tr->io); inb(tr->io); } while (0)
-#define TR_SET_SCL outb(tr->ioval |= 2, tr->io)
-#define TR_CLR_SCL outb(tr->ioval &= 0xfd, tr->io)
-#define TR_SET_SDA outb(tr->ioval |= 1, tr->io)
-#define TR_CLR_SDA outb(tr->ioval &= 0xfe, tr->io)
+#define TR_DELAY do { inb(tr->isa.io); inb(tr->isa.io); inb(tr->isa.io); } while (0)
+#define TR_SET_SCL outb(tr->ioval |= 2, tr->isa.io)
+#define TR_CLR_SCL outb(tr->ioval &= 0xfd, tr->isa.io)
+#define TR_SET_SDA outb(tr->ioval |= 1, tr->isa.io)
+#define TR_CLR_SDA outb(tr->ioval &= 0xfe, tr->isa.io)
static void write_i2c(struct trust *tr, int n, ...)
{
@@ -84,10 +85,10 @@ static void write_i2c(struct trust *tr, int n, ...)
TR_CLR_SCL;
TR_DELAY;
- for(; n; n--) {
+ for (; n; n--) {
val = va_arg(args, unsigned);
- for(mask = 0x80; mask; mask >>= 1) {
- if(val & mask)
+ for (mask = 0x80; mask; mask >>= 1) {
+ if (val & mask)
TR_SET_SDA;
else
TR_CLR_SDA;
@@ -115,317 +116,128 @@ static void write_i2c(struct trust *tr, int n, ...)
va_end(args);
}
-static void tr_setvol(struct trust *tr, __u16 vol)
+static int trust_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
- mutex_lock(&tr->lock);
- tr->curvol = vol / 2048;
- write_i2c(tr, 2, TDA7318_ADDR, tr->curvol ^ 0x1f);
- mutex_unlock(&tr->lock);
+ struct trust *tr = container_of(isa, struct trust, isa);
+
+ tr->ioval = (tr->ioval & 0xf7) | (mute << 3);
+ outb(tr->ioval, isa->io);
+ write_i2c(tr, 2, TDA7318_ADDR, vol ^ 0x1f);
+ return 0;
+}
+
+static int trust_s_stereo(struct radio_isa_card *isa, bool stereo)
+{
+ struct trust *tr = container_of(isa, struct trust, isa);
+
+ tr->ioval = (tr->ioval & 0xfb) | (!stereo << 2);
+ outb(tr->ioval, isa->io);
+ return 0;
+}
+
+static u32 trust_g_signal(struct radio_isa_card *isa)
+{
+ int i, v;
+
+ for (i = 0, v = 0; i < 100; i++)
+ v |= inb(isa->io);
+ return (v & 1) ? 0 : 0xffff;
+}
+
+static int trust_s_frequency(struct radio_isa_card *isa, u32 freq)
+{
+ struct trust *tr = container_of(isa, struct trust, isa);
+
+ freq /= 160; /* Convert to 10 kHz units */
+ freq += 1070; /* Add 10.7 MHz IF */
+ write_i2c(tr, 5, TSA6060T_ADDR, (freq << 1) | 1,
+ freq >> 7, 0x60 | ((freq >> 15) & 1), 0);
+ return 0;
}
static int basstreble2chip[15] = {
0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8
};
-static void tr_setbass(struct trust *tr, __u16 bass)
+static int trust_s_ctrl(struct v4l2_ctrl *ctrl)
{
- mutex_lock(&tr->lock);
- tr->curbass = bass / 4370;
- write_i2c(tr, 2, TDA7318_ADDR, 0x60 | basstreble2chip[tr->curbass]);
- mutex_unlock(&tr->lock);
-}
-
-static void tr_settreble(struct trust *tr, __u16 treble)
-{
- mutex_lock(&tr->lock);
- tr->curtreble = treble / 4370;
- write_i2c(tr, 2, TDA7318_ADDR, 0x70 | basstreble2chip[tr->curtreble]);
- mutex_unlock(&tr->lock);
-}
-
-static void tr_setstereo(struct trust *tr, int stereo)
-{
- mutex_lock(&tr->lock);
- tr->curstereo = !!stereo;
- tr->ioval = (tr->ioval & 0xfb) | (!tr->curstereo << 2);
- outb(tr->ioval, tr->io);
- mutex_unlock(&tr->lock);
-}
-
-static void tr_setmute(struct trust *tr, int mute)
-{
- mutex_lock(&tr->lock);
- tr->curmute = !!mute;
- tr->ioval = (tr->ioval & 0xf7) | (tr->curmute << 3);
- outb(tr->ioval, tr->io);
- mutex_unlock(&tr->lock);
-}
-
-static int tr_getsigstr(struct trust *tr)
-{
- int i, v;
-
- mutex_lock(&tr->lock);
- for (i = 0, v = 0; i < 100; i++)
- v |= inb(tr->io);
- mutex_unlock(&tr->lock);
- return (v & 1) ? 0 : 0xffff;
-}
-
-static int tr_getstereo(struct trust *tr)
-{
- /* don't know how to determine it, just return the setting */
- return tr->curstereo;
-}
-
-static void tr_setfreq(struct trust *tr, unsigned long f)
-{
- mutex_lock(&tr->lock);
- tr->curfreq = f;
- f /= 160; /* Convert to 10 kHz units */
- f += 1070; /* Add 10.7 MHz IF */
- write_i2c(tr, 5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0);
- mutex_unlock(&tr->lock);
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-trust", sizeof(v->driver));
- strlcpy(v->card, "Trust FM Radio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct trust *tr = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 87.5 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- if (tr_getstereo(tr))
- v->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = tr_getsigstr(tr);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct trust *tr = video_drvdata(file);
-
- if (v->index)
- return -EINVAL;
- tr_setstereo(tr, v->audmode == V4L2_TUNER_MODE_STEREO);
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct trust *tr = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- tr_setfreq(tr, f->frequency);
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct trust *tr = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = tr->curfreq;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 2048, 65535);
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 4370, 32768);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct trust *tr = video_drvdata(file);
+ struct radio_isa_card *isa =
+ container_of(ctrl->handler, struct radio_isa_card, hdl);
+ struct trust *tr = container_of(isa, struct trust, isa);
switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = tr->curmute;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = tr->curvol * 2048;
- return 0;
case V4L2_CID_AUDIO_BASS:
- ctrl->value = tr->curbass * 4370;
+ write_i2c(tr, 2, TDA7318_ADDR, 0x60 | basstreble2chip[ctrl->val]);
return 0;
case V4L2_CID_AUDIO_TREBLE:
- ctrl->value = tr->curtreble * 4370;
+ write_i2c(tr, 2, TDA7318_ADDR, 0x70 | basstreble2chip[ctrl->val]);
return 0;
}
return -EINVAL;
}
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct trust *tr = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- tr_setmute(tr, ctrl->value);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- tr_setvol(tr, ctrl->value);
- return 0;
- case V4L2_CID_AUDIO_BASS:
- tr_setbass(tr, ctrl->value);
- return 0;
- case V4L2_CID_AUDIO_TREBLE:
- tr_settreble(tr, ctrl->value);
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static const struct v4l2_file_operations trust_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct v4l2_ctrl_ops trust_ctrl_ops = {
+ .s_ctrl = trust_s_ctrl,
};
-static const struct v4l2_ioctl_ops trust_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
-};
-
-static int __init trust_init(void)
+static int trust_initialize(struct radio_isa_card *isa)
{
- struct trust *tr = &trust_card;
- struct v4l2_device *v4l2_dev = &tr->v4l2_dev;
- int res;
+ struct trust *tr = container_of(isa, struct trust, isa);
- strlcpy(v4l2_dev->name, "trust", sizeof(v4l2_dev->name));
- tr->io = io;
tr->ioval = 0xf;
- mutex_init(&tr->lock);
-
- if (tr->io == -1) {
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x0x350 or 0x358\n");
- return -EINVAL;
- }
- if (!request_region(tr->io, 2, "Trust FM Radio")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", tr->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(tr->io, 2);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
-
- strlcpy(tr->vdev.name, v4l2_dev->name, sizeof(tr->vdev.name));
- tr->vdev.v4l2_dev = v4l2_dev;
- tr->vdev.fops = &trust_fops;
- tr->vdev.ioctl_ops = &trust_ioctl_ops;
- tr->vdev.release = video_device_release_empty;
- video_set_drvdata(&tr->vdev, tr);
-
write_i2c(tr, 2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */
write_i2c(tr, 2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */
write_i2c(tr, 2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */
write_i2c(tr, 2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */
write_i2c(tr, 2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */
- tr_setvol(tr, 0xffff);
- tr_setbass(tr, 0x8000);
- tr_settreble(tr, 0x8000);
- tr_setstereo(tr, 1);
-
- /* mute card - prevents noisy bootups */
- tr_setmute(tr, 1);
-
- if (video_register_device(&tr->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
- release_region(tr->io, 2);
- return -EINVAL;
- }
-
- v4l2_info(v4l2_dev, "Trust FM Radio card driver v1.0.\n");
-
- return 0;
+ v4l2_ctrl_new_std(&isa->hdl, &trust_ctrl_ops,
+ V4L2_CID_AUDIO_BASS, 0, 15, 1, 8);
+ v4l2_ctrl_new_std(&isa->hdl, &trust_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE, 0, 15, 1, 8);
+ return isa->hdl.error;
}
-static void __exit cleanup_trust_module(void)
-{
- struct trust *tr = &trust_card;
+static const struct radio_isa_ops trust_ops = {
+ .init = trust_initialize,
+ .alloc = trust_alloc,
+ .s_mute_volume = trust_s_mute_volume,
+ .s_frequency = trust_s_frequency,
+ .s_stereo = trust_s_stereo,
+ .g_signal = trust_g_signal,
+};
- video_unregister_device(&tr->vdev);
- v4l2_device_unregister(&tr->v4l2_dev);
- release_region(tr->io, 2);
+static const int trust_ioports[] = { 0x350, 0x358 };
+
+static struct radio_isa_driver trust_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-trust",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = trust_ioports,
+ .num_of_io_ports = ARRAY_SIZE(trust_ioports),
+ .region_size = 2,
+ .card = "Trust FM Radio",
+ .ops = &trust_ops,
+ .has_stereo = true,
+ .max_volume = 31,
+};
+
+static int __init trust_init(void)
+{
+ return isa_register_driver(&trust_driver.driver, TRUST_MAX);
+}
+
+static void __exit trust_exit(void)
+{
+ isa_unregister_driver(&trust_driver.driver);
}
module_init(trust_init);
-module_exit(cleanup_trust_module);
+module_exit(trust_exit);
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
index 398726abc0c8..eb72a4d13758 100644
--- a/drivers/media/radio/radio-typhoon.c
+++ b/drivers/media/radio/radio-typhoon.c
@@ -33,63 +33,53 @@
#include /* request_region */
#include /* kernel radio structs */
#include /* outb, outb_p */
+#include
#include
#include
+#include "radio-isa.h"
#define DRIVER_VERSION "0.1.2"
MODULE_AUTHOR("Dr. Henrik Seidel");
MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
MODULE_LICENSE("GPL");
-MODULE_VERSION(DRIVER_VERSION);
+MODULE_VERSION("0.1.99");
#ifndef CONFIG_RADIO_TYPHOON_PORT
#define CONFIG_RADIO_TYPHOON_PORT -1
#endif
#ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
-#define CONFIG_RADIO_TYPHOON_MUTEFREQ 0
+#define CONFIG_RADIO_TYPHOON_MUTEFREQ 87000
#endif
-static int io = CONFIG_RADIO_TYPHOON_PORT;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
-
-module_param(radio_nr, int, 0);
+#define TYPHOON_MAX 2
+static int io[TYPHOON_MAX] = { [0] = CONFIG_RADIO_TYPHOON_PORT,
+ [1 ... (TYPHOON_MAX - 1)] = -1 };
+static int radio_nr[TYPHOON_MAX] = { [0 ... (TYPHOON_MAX - 1)] = -1 };
static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ;
+
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the Typhoon card (0x316 or 0x336)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
module_param(mutefreq, ulong, 0);
MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
-#define BANNER "Typhoon Radio Card driver v" DRIVER_VERSION "\n"
-
struct typhoon {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
- int curvol;
+ struct radio_isa_card isa;
int muted;
- unsigned long curfreq;
- unsigned long mutefreq;
- struct mutex lock;
};
-static struct typhoon typhoon_card;
-
-static void typhoon_setvol_generic(struct typhoon *dev, int vol)
+static struct radio_isa_card *typhoon_alloc(void)
{
- mutex_lock(&dev->lock);
- vol >>= 14; /* Map 16 bit to 2 bit */
- vol &= 3;
- outb_p(vol / 2, dev->io); /* Set the volume, high bit. */
- outb_p(vol % 2, dev->io + 2); /* Set the volume, low bit. */
- mutex_unlock(&dev->lock);
+ struct typhoon *ty = kzalloc(sizeof(*ty), GFP_KERNEL);
+
+ return ty ? &ty->isa : NULL;
}
-static int typhoon_setfreq_generic(struct typhoon *dev,
- unsigned long frequency)
+static int typhoon_s_frequency(struct radio_isa_card *isa, u32 freq)
{
unsigned long outval;
unsigned long x;
@@ -105,302 +95,86 @@ static int typhoon_setfreq_generic(struct typhoon *dev,
*
*/
- mutex_lock(&dev->lock);
- x = frequency / 160;
+ x = freq / 160;
outval = (x * x + 2500) / 5000;
outval = (outval * x + 5000) / 10000;
outval -= (10 * x * x + 10433) / 20866;
outval += 4 * x - 11505;
- outb_p((outval >> 8) & 0x01, dev->io + 4);
- outb_p(outval >> 9, dev->io + 6);
- outb_p(outval & 0xff, dev->io + 8);
- mutex_unlock(&dev->lock);
-
+ outb_p((outval >> 8) & 0x01, isa->io + 4);
+ outb_p(outval >> 9, isa->io + 6);
+ outb_p(outval & 0xff, isa->io + 8);
return 0;
}
-static int typhoon_setfreq(struct typhoon *dev, unsigned long frequency)
+static int typhoon_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
- typhoon_setfreq_generic(dev, frequency);
- dev->curfreq = frequency;
- return 0;
-}
+ struct typhoon *ty = container_of(isa, struct typhoon, isa);
-static void typhoon_mute(struct typhoon *dev)
-{
- if (dev->muted == 1)
- return;
- typhoon_setvol_generic(dev, 0);
- typhoon_setfreq_generic(dev, dev->mutefreq);
- dev->muted = 1;
-}
+ if (mute)
+ vol = 0;
+ vol >>= 14; /* Map 16 bit to 2 bit */
+ vol &= 3;
+ outb_p(vol / 2, isa->io); /* Set the volume, high bit. */
+ outb_p(vol % 2, isa->io + 2); /* Set the volume, low bit. */
-static void typhoon_unmute(struct typhoon *dev)
-{
- if (dev->muted == 0)
- return;
- typhoon_setfreq_generic(dev, dev->curfreq);
- typhoon_setvol_generic(dev, dev->curvol);
- dev->muted = 0;
-}
-
-static int typhoon_setvol(struct typhoon *dev, int vol)
-{
- if (dev->muted && vol != 0) { /* user is unmuting the card */
- dev->curvol = vol;
- typhoon_unmute(dev);
- return 0;
+ if (vol == 0 && !ty->muted) {
+ ty->muted = true;
+ return typhoon_s_frequency(isa, mutefreq << 4);
}
- if (vol == dev->curvol) /* requested volume == current */
- return 0;
-
- if (vol == 0) { /* volume == 0 means mute the card */
- typhoon_mute(dev);
- dev->curvol = vol;
- return 0;
+ if (vol && ty->muted) {
+ ty->muted = false;
+ return typhoon_s_frequency(isa, isa->freq);
}
- typhoon_setvol_generic(dev, vol);
- dev->curvol = vol;
return 0;
}
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-typhoon", sizeof(v->driver));
- strlcpy(v->card, "Typhoon Radio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 87.5 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xFFFF; /* We can't get the signal strength */
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct typhoon *dev = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = dev->curfreq;
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct typhoon *dev = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- dev->curfreq = f->frequency;
- typhoon_setfreq(dev, dev->curfreq);
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 16384, 65535);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct typhoon *dev = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = dev->muted;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = dev->curvol;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl (struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct typhoon *dev = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- typhoon_mute(dev);
- else
- typhoon_unmute(dev);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- typhoon_setvol(dev, ctrl->value);
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static int vidioc_log_status(struct file *file, void *priv)
-{
- struct typhoon *dev = video_drvdata(file);
- struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
-
- v4l2_info(v4l2_dev, BANNER);
-#ifdef MODULE
- v4l2_info(v4l2_dev, "Load type: Driver loaded as a module\n\n");
-#else
- v4l2_info(v4l2_dev, "Load type: Driver compiled into kernel\n\n");
-#endif
- v4l2_info(v4l2_dev, "frequency = %lu kHz\n", dev->curfreq >> 4);
- v4l2_info(v4l2_dev, "volume = %d\n", dev->curvol);
- v4l2_info(v4l2_dev, "mute = %s\n", dev->muted ? "on" : "off");
- v4l2_info(v4l2_dev, "io = 0x%x\n", dev->io);
- v4l2_info(v4l2_dev, "mute frequency = %lu kHz\n", dev->mutefreq >> 4);
- return 0;
-}
-
-static const struct v4l2_file_operations typhoon_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops typhoon_ops = {
+ .alloc = typhoon_alloc,
+ .s_mute_volume = typhoon_s_mute_volume,
+ .s_frequency = typhoon_s_frequency,
};
-static const struct v4l2_ioctl_ops typhoon_ioctl_ops = {
- .vidioc_log_status = vidioc_log_status,
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
+static const int typhoon_ioports[] = { 0x316, 0x336 };
+
+static struct radio_isa_driver typhoon_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-typhoon",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = typhoon_ioports,
+ .num_of_io_ports = ARRAY_SIZE(typhoon_ioports),
+ .region_size = 8,
+ .card = "Typhoon Radio",
+ .ops = &typhoon_ops,
+ .has_stereo = true,
+ .max_volume = 3,
};
static int __init typhoon_init(void)
{
- struct typhoon *dev = &typhoon_card;
- struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "typhoon", sizeof(v4l2_dev->name));
- dev->io = io;
-
- if (dev->io == -1) {
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x316 or io=0x336\n");
- return -EINVAL;
+ if (mutefreq < 87000 || mutefreq > 108000) {
+ printk(KERN_ERR "%s: You must set a frequency (in kHz) used when muting the card,\n",
+ typhoon_driver.driver.driver.name);
+ printk(KERN_ERR "%s: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108000)\n",
+ typhoon_driver.driver.driver.name);
+ return -ENODEV;
}
-
- if (mutefreq < 87000 || mutefreq > 108500) {
- v4l2_err(v4l2_dev, "You must set a frequency (in kHz) used when muting the card,\n");
- v4l2_err(v4l2_dev, "e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
- return -EINVAL;
- }
- dev->curfreq = dev->mutefreq = mutefreq << 4;
-
- mutex_init(&dev->lock);
- if (!request_region(dev->io, 8, "typhoon")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n",
- dev->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(dev->io, 8);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
- v4l2_info(v4l2_dev, BANNER);
-
- strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
- dev->vdev.v4l2_dev = v4l2_dev;
- dev->vdev.fops = &typhoon_fops;
- dev->vdev.ioctl_ops = &typhoon_ioctl_ops;
- dev->vdev.release = video_device_release_empty;
- video_set_drvdata(&dev->vdev, dev);
-
- /* mute card - prevents noisy bootups */
- typhoon_mute(dev);
-
- if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(&dev->v4l2_dev);
- release_region(dev->io, 8);
- return -EINVAL;
- }
- v4l2_info(v4l2_dev, "port 0x%x.\n", dev->io);
- v4l2_info(v4l2_dev, "mute frequency is %lu kHz.\n", mutefreq);
-
- return 0;
+ return isa_register_driver(&typhoon_driver.driver, TYPHOON_MAX);
}
static void __exit typhoon_exit(void)
{
- struct typhoon *dev = &typhoon_card;
-
- video_unregister_device(&dev->vdev);
- v4l2_device_unregister(&dev->v4l2_dev);
- release_region(dev->io, 8);
+ isa_unregister_driver(&typhoon_driver.driver);
}
+
module_init(typhoon_init);
module_exit(typhoon_exit);
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
index f5613b948203..026e88eef29c 100644
--- a/drivers/media/radio/radio-zoltrix.c
+++ b/drivers/media/radio/radio-zoltrix.c
@@ -1,5 +1,6 @@
-/* zoltrix radio plus driver for Linux radio support
- * (c) 1998 C. van Schaik
+/*
+ * Zoltrix Radio Plus driver
+ * Copyright 1998 C. van Schaik
*
* BUGS
* Due to the inconsistency in reading from the signal flags
@@ -27,6 +28,14 @@
*
* 2006-07-24 - Converted to V4L2 API
* by Mauro Carvalho Chehab
+ *
+ * Converted to the radio-isa framework by Hans Verkuil
+ *
+ * Note that this is the driver for the Zoltrix Radio Plus.
+ * This driver does not work for the Zoltrix Radio Plus 108 or the
+ * Zoltrix Radio Plus for Windows.
+ *
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
*/
#include /* Modules */
@@ -36,82 +45,70 @@
#include /* kernel radio structs */
#include
#include /* outb, outb_p */
+#include
#include
#include
+#include "radio-isa.h"
-MODULE_AUTHOR("C.van Schaik");
+MODULE_AUTHOR("C. van Schaik");
MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
+MODULE_VERSION("0.1.99");
#ifndef CONFIG_RADIO_ZOLTRIX_PORT
#define CONFIG_RADIO_ZOLTRIX_PORT -1
#endif
-static int io = CONFIG_RADIO_ZOLTRIX_PORT;
-static int radio_nr = -1;
+#define ZOLTRIX_MAX 2
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
-module_param(radio_nr, int, 0);
+static int io[ZOLTRIX_MAX] = { [0] = CONFIG_RADIO_ZOLTRIX_PORT,
+ [1 ... (ZOLTRIX_MAX - 1)] = -1 };
+static int radio_nr[ZOLTRIX_MAX] = { [0 ... (ZOLTRIX_MAX - 1)] = -1 };
+
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the Zoltrix Radio Plus card (0x20c or 0x30c)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
struct zoltrix {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
+ struct radio_isa_card isa;
int curvol;
- unsigned long curfreq;
- int muted;
- unsigned int stereo;
- struct mutex lock;
+ bool muted;
};
-static struct zoltrix zoltrix_card;
-
-static int zol_setvol(struct zoltrix *zol, int vol)
+static struct radio_isa_card *zoltrix_alloc(void)
{
- zol->curvol = vol;
- if (zol->muted)
- return 0;
+ struct zoltrix *zol = kzalloc(sizeof(*zol), GFP_KERNEL);
- mutex_lock(&zol->lock);
- if (vol == 0) {
- outb(0, zol->io);
- outb(0, zol->io);
- inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
- mutex_unlock(&zol->lock);
+ return zol ? &zol->isa : NULL;
+}
+
+static int zoltrix_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
+{
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
+
+ zol->curvol = vol;
+ zol->muted = mute;
+ if (mute || vol == 0) {
+ outb(0, isa->io);
+ outb(0, isa->io);
+ inb(isa->io + 3); /* Zoltrix needs to be read to confirm */
return 0;
}
- outb(zol->curvol-1, zol->io);
+ outb(vol - 1, isa->io);
msleep(10);
- inb(zol->io + 2);
- mutex_unlock(&zol->lock);
+ inb(isa->io + 2);
return 0;
}
-static void zol_mute(struct zoltrix *zol)
+/* tunes the radio to the desired frequency */
+static int zoltrix_s_frequency(struct radio_isa_card *isa, u32 freq)
{
- zol->muted = 1;
- mutex_lock(&zol->lock);
- outb(0, zol->io);
- outb(0, zol->io);
- inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
- mutex_unlock(&zol->lock);
-}
-
-static void zol_unmute(struct zoltrix *zol)
-{
- zol->muted = 0;
- zol_setvol(zol, zol->curvol);
-}
-
-static int zol_setfreq(struct zoltrix *zol, unsigned long freq)
-{
- /* tunes the radio to the desired frequency */
- struct v4l2_device *v4l2_dev = &zol->v4l2_dev;
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
+ struct v4l2_device *v4l2_dev = &isa->v4l2_dev;
unsigned long long bitmask, f, m;
- unsigned int stereo = zol->stereo;
+ bool stereo = isa->stereo;
int i;
if (freq == 0) {
@@ -125,340 +122,125 @@ static int zol_setfreq(struct zoltrix *zol, unsigned long freq)
bitmask = 0xc480402c10080000ull;
i = 45;
- mutex_lock(&zol->lock);
+ outb(0, isa->io);
+ outb(0, isa->io);
+ inb(isa->io + 3); /* Zoltrix needs to be read to confirm */
- zol->curfreq = freq;
-
- outb(0, zol->io);
- outb(0, zol->io);
- inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
-
- outb(0x40, zol->io);
- outb(0xc0, zol->io);
+ outb(0x40, isa->io);
+ outb(0xc0, isa->io);
bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ (stereo << 31));
while (i--) {
if ((bitmask & 0x8000000000000000ull) != 0) {
- outb(0x80, zol->io);
+ outb(0x80, isa->io);
udelay(50);
- outb(0x00, zol->io);
+ outb(0x00, isa->io);
udelay(50);
- outb(0x80, zol->io);
+ outb(0x80, isa->io);
udelay(50);
} else {
- outb(0xc0, zol->io);
+ outb(0xc0, isa->io);
udelay(50);
- outb(0x40, zol->io);
+ outb(0x40, isa->io);
udelay(50);
- outb(0xc0, zol->io);
+ outb(0xc0, isa->io);
udelay(50);
}
bitmask *= 2;
}
/* termination sequence */
- outb(0x80, zol->io);
- outb(0xc0, zol->io);
- outb(0x40, zol->io);
+ outb(0x80, isa->io);
+ outb(0xc0, isa->io);
+ outb(0x40, isa->io);
udelay(1000);
- inb(zol->io + 2);
-
+ inb(isa->io + 2);
udelay(1000);
- if (zol->muted) {
- outb(0, zol->io);
- outb(0, zol->io);
- inb(zol->io + 3);
- udelay(1000);
- }
-
- mutex_unlock(&zol->lock);
-
- if (!zol->muted)
- zol_setvol(zol, zol->curvol);
- return 0;
+ return zoltrix_s_mute_volume(isa, zol->muted, zol->curvol);
}
/* Get signal strength */
-static int zol_getsigstr(struct zoltrix *zol)
+static u32 zoltrix_g_rxsubchans(struct radio_isa_card *isa)
{
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
int a, b;
- mutex_lock(&zol->lock);
- outb(0x00, zol->io); /* This stuff I found to do nothing */
- outb(zol->curvol, zol->io);
+ outb(0x00, isa->io); /* This stuff I found to do nothing */
+ outb(zol->curvol, isa->io);
msleep(20);
- a = inb(zol->io);
+ a = inb(isa->io);
msleep(10);
- b = inb(zol->io);
+ b = inb(isa->io);
- mutex_unlock(&zol->lock);
+ return (a == b && a == 0xcf) ?
+ V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
+}
+
+static u32 zoltrix_g_signal(struct radio_isa_card *isa)
+{
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
+ int a, b;
+
+ outb(0x00, isa->io); /* This stuff I found to do nothing */
+ outb(zol->curvol, isa->io);
+ msleep(20);
+
+ a = inb(isa->io);
+ msleep(10);
+ b = inb(isa->io);
if (a != b)
return 0;
/* I found this out by playing with a binary scanner on the card io */
- return a == 0xcf || a == 0xdf || a == 0xef;
+ return (a == 0xcf || a == 0xdf || a == 0xef) ? 0xffff : 0;
}
-static int zol_is_stereo(struct zoltrix *zol)
+static int zoltrix_s_stereo(struct radio_isa_card *isa, bool stereo)
{
- int x1, x2;
-
- mutex_lock(&zol->lock);
-
- outb(0x00, zol->io);
- outb(zol->curvol, zol->io);
- msleep(20);
-
- x1 = inb(zol->io);
- msleep(10);
- x2 = inb(zol->io);
-
- mutex_unlock(&zol->lock);
-
- return x1 == x2 && x1 == 0xcf;
+ return zoltrix_s_frequency(isa, isa->freq);
}
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver));
- strlcpy(v->card, "Zoltrix Radio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct zoltrix *zol = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 88 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- if (zol_is_stereo(zol))
- v->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xFFFF * zol_getsigstr(zol);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct zoltrix *zol = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- if (zol_setfreq(zol, f->frequency) != 0)
- return -EINVAL;
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct zoltrix *zol = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = zol->curfreq;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 4096, 65535);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct zoltrix *zol = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = zol->muted;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = zol->curvol * 4096;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct zoltrix *zol = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- zol_mute(zol);
- else {
- zol_unmute(zol);
- zol_setvol(zol, zol->curvol);
- }
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- zol_setvol(zol, ctrl->value / 4096);
- return 0;
- }
- zol->stereo = 1;
- if (zol_setfreq(zol, zol->curfreq) != 0)
- return -EINVAL;
-#if 0
-/* FIXME: Implement stereo/mono switch on V4L2 */
- if (v->mode & VIDEO_SOUND_STEREO) {
- zol->stereo = 1;
- zol_setfreq(zol, zol->curfreq);
- }
- if (v->mode & VIDEO_SOUND_MONO) {
- zol->stereo = 0;
- zol_setfreq(zol, zol->curfreq);
- }
-#endif
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static const struct v4l2_file_operations zoltrix_fops =
-{
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops zoltrix_ops = {
+ .alloc = zoltrix_alloc,
+ .s_mute_volume = zoltrix_s_mute_volume,
+ .s_frequency = zoltrix_s_frequency,
+ .s_stereo = zoltrix_s_stereo,
+ .g_rxsubchans = zoltrix_g_rxsubchans,
+ .g_signal = zoltrix_g_signal,
};
-static const struct v4l2_ioctl_ops zoltrix_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
+static const int zoltrix_ioports[] = { 0x20c, 0x30c };
+
+static struct radio_isa_driver zoltrix_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-zoltrix",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = zoltrix_ioports,
+ .num_of_io_ports = ARRAY_SIZE(zoltrix_ioports),
+ .region_size = 2,
+ .card = "Zoltrix Radio Plus",
+ .ops = &zoltrix_ops,
+ .has_stereo = true,
+ .max_volume = 15,
};
static int __init zoltrix_init(void)
{
- struct zoltrix *zol = &zoltrix_card;
- struct v4l2_device *v4l2_dev = &zol->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "zoltrix", sizeof(v4l2_dev->name));
- zol->io = io;
- if (zol->io == -1) {
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or 0x30c\n");
- return -EINVAL;
- }
- if (zol->io != 0x20c && zol->io != 0x30c) {
- v4l2_err(v4l2_dev, "invalid port, try 0x20c or 0x30c\n");
- return -ENXIO;
- }
-
- if (!request_region(zol->io, 2, "zoltrix")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", zol->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(zol->io, 2);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
-
- mutex_init(&zol->lock);
-
- /* mute card - prevents noisy bootups */
-
- /* this ensures that the volume is all the way down */
-
- outb(0, zol->io);
- outb(0, zol->io);
- msleep(20);
- inb(zol->io + 3);
-
- zol->curvol = 0;
- zol->stereo = 1;
-
- strlcpy(zol->vdev.name, v4l2_dev->name, sizeof(zol->vdev.name));
- zol->vdev.v4l2_dev = v4l2_dev;
- zol->vdev.fops = &zoltrix_fops;
- zol->vdev.ioctl_ops = &zoltrix_ioctl_ops;
- zol->vdev.release = video_device_release_empty;
- video_set_drvdata(&zol->vdev, zol);
-
- if (video_register_device(&zol->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
- release_region(zol->io, 2);
- return -EINVAL;
- }
- v4l2_info(v4l2_dev, "Zoltrix Radio Plus card driver.\n");
-
- return 0;
+ return isa_register_driver(&zoltrix_driver.driver, ZOLTRIX_MAX);
}
static void __exit zoltrix_exit(void)
{
- struct zoltrix *zol = &zoltrix_card;
-
- video_unregister_device(&zol->vdev);
- v4l2_device_unregister(&zol->v4l2_dev);
- release_region(zol->io, 2);
+ isa_unregister_driver(&zoltrix_driver.driver);
}
module_init(zoltrix_init);
diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c
index b1193dfc5087..9474706350f8 100644
--- a/drivers/media/radio/saa7706h.c
+++ b/drivers/media/radio/saa7706h.c
@@ -434,18 +434,7 @@ static struct i2c_driver saa7706h_driver = {
.id_table = saa7706h_id,
};
-static __init int saa7706h_init(void)
-{
- return i2c_add_driver(&saa7706h_driver);
-}
-
-static __exit void saa7706h_exit(void)
-{
- i2c_del_driver(&saa7706h_driver);
-}
-
-module_init(saa7706h_init);
-module_exit(saa7706h_exit);
+module_i2c_driver(saa7706h_driver);
MODULE_DESCRIPTION("SAA7706H Car Radio DSP driver");
MODULE_AUTHOR("Mocean Laboratories");
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index fd3541b0e91c..9b546a5523f3 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -539,33 +539,7 @@ static struct i2c_driver si470x_i2c_driver = {
.id_table = si470x_i2c_id,
};
-
-
-/**************************************************************************
- * Module Interface
- **************************************************************************/
-
-/*
- * si470x_i2c_init - module init
- */
-static int __init si470x_i2c_init(void)
-{
- printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
- return i2c_add_driver(&si470x_i2c_driver);
-}
-
-
-/*
- * si470x_i2c_exit - module exit
- */
-static void __exit si470x_i2c_exit(void)
-{
- i2c_del_driver(&si470x_i2c_driver);
-}
-
-
-module_init(si470x_i2c_init);
-module_exit(si470x_i2c_exit);
+module_i2c_driver(si470x_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c
index 27aba936fb2b..b898c8925ab7 100644
--- a/drivers/media/radio/si4713-i2c.c
+++ b/drivers/media/radio/si4713-i2c.c
@@ -2106,17 +2106,4 @@ static struct i2c_driver si4713_i2c_driver = {
.id_table = si4713_id,
};
-/* Module Interface */
-static int __init si4713_module_init(void)
-{
- return i2c_add_driver(&si4713_i2c_driver);
-}
-
-static void __exit si4713_module_exit(void)
-{
- i2c_del_driver(&si4713_i2c_driver);
-}
-
-module_init(si4713_module_init);
-module_exit(si4713_module_exit);
-
+module_i2c_driver(si4713_i2c_driver);
diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c
index 3408685b690c..6418c4c9faf1 100644
--- a/drivers/media/radio/tef6862.c
+++ b/drivers/media/radio/tef6862.c
@@ -215,20 +215,8 @@ static struct i2c_driver tef6862_driver = {
.id_table = tef6862_id,
};
-static __init int tef6862_init(void)
-{
- return i2c_add_driver(&tef6862_driver);
-}
-
-static __exit void tef6862_exit(void)
-{
- i2c_del_driver(&tef6862_driver);
-}
-
-module_init(tef6862_init);
-module_exit(tef6862_exit);
+module_i2c_driver(tef6862_driver);
MODULE_DESCRIPTION("TEF6862 Car Radio Enhanced Selectivity Tuner");
MODULE_AUTHOR("Mocean Laboratories");
MODULE_LICENSE("GPL v2");
-
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 4df4affeea5f..a3fbb21350e9 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -266,4 +266,13 @@ config RC_LOOPBACK
To compile this driver as a module, choose M here: the module will
be called rc_loopback.
+config IR_GPIO_CIR
+ tristate "GPIO IR remote control"
+ depends on RC_CORE
+ ---help---
+ Say Y if you want to use GPIO based IR Receiver.
+
+ To compile this driver as a module, choose M here: the module will
+ be called gpio-ir-recv.
+
endif #RC_CORE
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index fb3dee2dd845..29f364f88a94 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_IR_REDRAT3) += redrat3.o
obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
+obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index 7f7079b12f23..392d4be91f8f 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -117,7 +117,7 @@ static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset)
static void cir_dump_regs(struct fintek_dev *fintek)
{
fintek_config_mode_enable(fintek);
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
pr_reg("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);
pr_reg(" * CR CIR BASE ADDR: 0x%x\n",
@@ -143,7 +143,7 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
u8 chip_major, chip_minor;
u8 vendor_major, vendor_minor;
u8 portsel, ir_class;
- u16 vendor;
+ u16 vendor, chip;
int ret = 0;
fintek_config_mode_enable(fintek);
@@ -176,6 +176,7 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI);
chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO);
+ chip = chip_major << 8 | chip_minor;
vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI);
vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO);
@@ -192,6 +193,15 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
fintek->chip_major = chip_major;
fintek->chip_minor = chip_minor;
fintek->chip_vendor = vendor;
+
+ /*
+ * Newer reviews of this chipset uses port 8 instead of 5
+ */
+ if ((chip != 0x0408) || (chip != 0x0804))
+ fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV2;
+ else
+ fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV1;
+
spin_unlock_irqrestore(&fintek->fintek_lock, flags);
return ret;
@@ -200,7 +210,7 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
static void fintek_cir_ldev_init(struct fintek_dev *fintek)
{
/* Select CIR logical device and enable */
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
/* Write allocated CIR address and IRQ information to hardware */
@@ -381,7 +391,7 @@ static irqreturn_t fintek_cir_isr(int irq, void *data)
fit_dbg_verbose("%s firing", __func__);
fintek_config_mode_enable(fintek);
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_config_mode_disable(fintek);
/*
@@ -422,7 +432,7 @@ static void fintek_enable_cir(struct fintek_dev *fintek)
fintek_config_mode_enable(fintek);
/* enable the CIR logical device */
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
fintek_config_mode_disable(fintek);
@@ -439,7 +449,7 @@ static void fintek_disable_cir(struct fintek_dev *fintek)
fintek_config_mode_enable(fintek);
/* disable the CIR logical device */
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
fintek_config_mode_disable(fintek);
@@ -611,7 +621,7 @@ static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state)
fintek_config_mode_enable(fintek);
/* disable cir logical dev */
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
fintek_config_mode_disable(fintek);
@@ -634,7 +644,7 @@ static int fintek_resume(struct pnp_dev *pdev)
/* Enable CIR logical device */
fintek_config_mode_enable(fintek);
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
fintek_config_mode_disable(fintek);
diff --git a/drivers/media/rc/fintek-cir.h b/drivers/media/rc/fintek-cir.h
index 1b10b2011f5e..82516a1d39b0 100644
--- a/drivers/media/rc/fintek-cir.h
+++ b/drivers/media/rc/fintek-cir.h
@@ -88,6 +88,7 @@ struct fintek_dev {
u8 chip_major;
u8 chip_minor;
u16 chip_vendor;
+ u8 logical_dev_cir;
/* hardware features */
bool hw_learning_capable;
@@ -172,7 +173,8 @@ struct fintek_dev {
#define LOGICAL_DEV_ENABLE 0x01
/* Logical device number of the CIR function */
-#define LOGICAL_DEV_CIR 0x05
+#define LOGICAL_DEV_CIR_REV1 0x05
+#define LOGICAL_DEV_CIR_REV2 0x08
/* CIR Logical Device (LDN 0x08) config registers */
#define CIR_CR_COMMAND_INDEX 0x04
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
new file mode 100644
index 000000000000..0d875450c5ce
--- /dev/null
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -0,0 +1,205 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define GPIO_IR_DRIVER_NAME "gpio-rc-recv"
+#define GPIO_IR_DEVICE_NAME "gpio_ir_recv"
+
+struct gpio_rc_dev {
+ struct rc_dev *rcdev;
+ int gpio_nr;
+ bool active_low;
+};
+
+static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
+{
+ struct gpio_rc_dev *gpio_dev = dev_id;
+ int gval;
+ int rc = 0;
+ enum raw_event_type type = IR_SPACE;
+
+ gval = gpio_get_value_cansleep(gpio_dev->gpio_nr);
+
+ if (gval < 0)
+ goto err_get_value;
+
+ if (gpio_dev->active_low)
+ gval = !gval;
+
+ if (gval == 1)
+ type = IR_PULSE;
+
+ rc = ir_raw_event_store_edge(gpio_dev->rcdev, type);
+ if (rc < 0)
+ goto err_get_value;
+
+ ir_raw_event_handle(gpio_dev->rcdev);
+
+err_get_value:
+ return IRQ_HANDLED;
+}
+
+static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
+{
+ struct gpio_rc_dev *gpio_dev;
+ struct rc_dev *rcdev;
+ const struct gpio_ir_recv_platform_data *pdata =
+ pdev->dev.platform_data;
+ int rc;
+
+ if (!pdata)
+ return -EINVAL;
+
+ if (pdata->gpio_nr < 0)
+ return -EINVAL;
+
+ gpio_dev = kzalloc(sizeof(struct gpio_rc_dev), GFP_KERNEL);
+ if (!gpio_dev)
+ return -ENOMEM;
+
+ rcdev = rc_allocate_device();
+ if (!rcdev) {
+ rc = -ENOMEM;
+ goto err_allocate_device;
+ }
+
+ rcdev->driver_type = RC_DRIVER_IR_RAW;
+ rcdev->allowed_protos = RC_TYPE_ALL;
+ rcdev->input_name = GPIO_IR_DEVICE_NAME;
+ rcdev->input_id.bustype = BUS_HOST;
+ rcdev->driver_name = GPIO_IR_DRIVER_NAME;
+ rcdev->map_name = RC_MAP_EMPTY;
+
+ gpio_dev->rcdev = rcdev;
+ gpio_dev->gpio_nr = pdata->gpio_nr;
+ gpio_dev->active_low = pdata->active_low;
+
+ rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
+ if (rc < 0)
+ goto err_gpio_request;
+ rc = gpio_direction_input(pdata->gpio_nr);
+ if (rc < 0)
+ goto err_gpio_direction_input;
+
+ rc = rc_register_device(rcdev);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "failed to register rc device\n");
+ goto err_register_rc_device;
+ }
+
+ platform_set_drvdata(pdev, gpio_dev);
+
+ rc = request_any_context_irq(gpio_to_irq(pdata->gpio_nr),
+ gpio_ir_recv_irq,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "gpio-ir-recv-irq", gpio_dev);
+ if (rc < 0)
+ goto err_request_irq;
+
+ return 0;
+
+err_request_irq:
+ platform_set_drvdata(pdev, NULL);
+ rc_unregister_device(rcdev);
+err_register_rc_device:
+err_gpio_direction_input:
+ gpio_free(pdata->gpio_nr);
+err_gpio_request:
+ rc_free_device(rcdev);
+ rcdev = NULL;
+err_allocate_device:
+ kfree(gpio_dev);
+ return rc;
+}
+
+static int __devexit gpio_ir_recv_remove(struct platform_device *pdev)
+{
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+ free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
+ platform_set_drvdata(pdev, NULL);
+ rc_unregister_device(gpio_dev->rcdev);
+ gpio_free(gpio_dev->gpio_nr);
+ rc_free_device(gpio_dev->rcdev);
+ kfree(gpio_dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int gpio_ir_recv_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
+ else
+ disable_irq(gpio_to_irq(gpio_dev->gpio_nr));
+
+ return 0;
+}
+
+static int gpio_ir_recv_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
+ else
+ enable_irq(gpio_to_irq(gpio_dev->gpio_nr));
+
+ return 0;
+}
+
+static const struct dev_pm_ops gpio_ir_recv_pm_ops = {
+ .suspend = gpio_ir_recv_suspend,
+ .resume = gpio_ir_recv_resume,
+};
+#endif
+
+static struct platform_driver gpio_ir_recv_driver = {
+ .probe = gpio_ir_recv_probe,
+ .remove = __devexit_p(gpio_ir_recv_remove),
+ .driver = {
+ .name = GPIO_IR_DRIVER_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &gpio_ir_recv_pm_ops,
+#endif
+ },
+};
+
+static int __init gpio_ir_recv_init(void)
+{
+ return platform_driver_register(&gpio_ir_recv_driver);
+}
+module_init(gpio_ir_recv_init);
+
+static void __exit gpio_ir_recv_exit(void)
+{
+ platform_driver_unregister(&gpio_ir_recv_driver);
+}
+module_exit(gpio_ir_recv_exit);
+
+MODULE_DESCRIPTION("GPIO IR Receiver driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
index d5e2b50aff1f..dab98b37621a 100644
--- a/drivers/media/rc/ir-sony-decoder.c
+++ b/drivers/media/rc/ir-sony-decoder.c
@@ -130,7 +130,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
case 15:
device = bitrev8((data->bits >> 0) & 0xFF);
subdevice = 0;
- function = bitrev8((data->bits >> 7) & 0xFD);
+ function = bitrev8((data->bits >> 7) & 0xFE);
break;
case 20:
device = bitrev8((data->bits >> 5) & 0xF8);
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index 36e4d5e8dd6a..49ce2662f56b 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -41,8 +41,11 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-imon-mce.o \
rc-imon-pad.o \
rc-iodata-bctv7e.o \
+ rc-it913x-v1.o \
+ rc-it913x-v2.o \
rc-kaiomy.o \
rc-kworld-315u.o \
+ rc-kworld-pc150u.o \
rc-kworld-plus-tv-analog.o \
rc-leadtek-y04g0051.o \
rc-lirc.o \
diff --git a/drivers/media/rc/keymaps/rc-it913x-v1.c b/drivers/media/rc/keymaps/rc-it913x-v1.c
new file mode 100644
index 000000000000..0ac775fd109d
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-it913x-v1.c
@@ -0,0 +1,95 @@
+/* ITE Generic remotes Version 1
+ *
+ * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include
+#include
+
+
+static struct rc_map_table it913x_v1_rc[] = {
+ /* Type 1 */
+ { 0x61d601, KEY_VIDEO }, /* Source */
+ { 0x61d602, KEY_3 },
+ { 0x61d603, KEY_POWER }, /* ShutDown */
+ { 0x61d604, KEY_1 },
+ { 0x61d605, KEY_5 },
+ { 0x61d606, KEY_6 },
+ { 0x61d607, KEY_CHANNELDOWN }, /* CH- */
+ { 0x61d608, KEY_2 },
+ { 0x61d609, KEY_CHANNELUP }, /* CH+ */
+ { 0x61d60a, KEY_9 },
+ { 0x61d60b, KEY_ZOOM }, /* Zoom */
+ { 0x61d60c, KEY_7 },
+ { 0x61d60d, KEY_8 },
+ { 0x61d60e, KEY_VOLUMEUP }, /* Vol+ */
+ { 0x61d60f, KEY_4 },
+ { 0x61d610, KEY_ESC }, /* [back up arrow] */
+ { 0x61d611, KEY_0 },
+ { 0x61d612, KEY_OK }, /* [enter arrow] */
+ { 0x61d613, KEY_VOLUMEDOWN }, /* Vol- */
+ { 0x61d614, KEY_RECORD }, /* Rec */
+ { 0x61d615, KEY_STOP }, /* Stop */
+ { 0x61d616, KEY_PLAY }, /* Play */
+ { 0x61d617, KEY_MUTE }, /* Mute */
+ { 0x61d618, KEY_UP },
+ { 0x61d619, KEY_DOWN },
+ { 0x61d61a, KEY_LEFT },
+ { 0x61d61b, KEY_RIGHT },
+ { 0x61d61c, KEY_RED },
+ { 0x61d61d, KEY_GREEN },
+ { 0x61d61e, KEY_YELLOW },
+ { 0x61d61f, KEY_BLUE },
+ { 0x61d643, KEY_POWER2 }, /* [red power button] */
+ /* Type 2 - 20 buttons */
+ { 0x807f0d, KEY_0 },
+ { 0x807f04, KEY_1 },
+ { 0x807f05, KEY_2 },
+ { 0x807f06, KEY_3 },
+ { 0x807f07, KEY_4 },
+ { 0x807f08, KEY_5 },
+ { 0x807f09, KEY_6 },
+ { 0x807f0a, KEY_7 },
+ { 0x807f1b, KEY_8 },
+ { 0x807f1f, KEY_9 },
+ { 0x807f12, KEY_POWER },
+ { 0x807f01, KEY_MEDIA_REPEAT}, /* Recall */
+ { 0x807f19, KEY_PAUSE }, /* Timeshift */
+ { 0x807f1e, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
+ { 0x807f03, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
+ { 0x807f1a, KEY_CHANNELUP },
+ { 0x807f02, KEY_CHANNELDOWN },
+ { 0x807f0c, KEY_ZOOM },
+ { 0x807f00, KEY_RECORD },
+ { 0x807f0e, KEY_STOP },
+};
+
+static struct rc_map_list it913x_v1_map = {
+ .map = {
+ .scan = it913x_v1_rc,
+ .size = ARRAY_SIZE(it913x_v1_rc),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_IT913X_V1,
+ }
+};
+
+static int __init init_rc_it913x_v1_map(void)
+{
+ return rc_map_register(&it913x_v1_map);
+}
+
+static void __exit exit_rc_it913x_v1_map(void)
+{
+ rc_map_unregister(&it913x_v1_map);
+}
+
+module_init(init_rc_it913x_v1_map)
+module_exit(exit_rc_it913x_v1_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
diff --git a/drivers/media/rc/keymaps/rc-it913x-v2.c b/drivers/media/rc/keymaps/rc-it913x-v2.c
new file mode 100644
index 000000000000..28e376e18b99
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-it913x-v2.c
@@ -0,0 +1,94 @@
+/* ITE Generic remotes Version 2
+ *
+ * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include
+#include
+
+
+static struct rc_map_table it913x_v2_rc[] = {
+ /* Type 1 */
+ /* 9005 remote */
+ { 0x807f12, KEY_POWER2 }, /* Power (RED POWER BUTTON)*/
+ { 0x807f1a, KEY_VIDEO }, /* Source */
+ { 0x807f1e, KEY_MUTE }, /* Mute */
+ { 0x807f01, KEY_RECORD }, /* Record */
+ { 0x807f02, KEY_CHANNELUP }, /* Channel+ */
+ { 0x807f03, KEY_TIME }, /* TimeShift */
+ { 0x807f04, KEY_VOLUMEUP }, /* Volume- */
+ { 0x807f05, KEY_SCREEN }, /* FullScreen */
+ { 0x807f06, KEY_VOLUMEDOWN }, /* Volume- */
+ { 0x807f07, KEY_0 }, /* 0 */
+ { 0x807f08, KEY_CHANNELDOWN }, /* Channel- */
+ { 0x807f09, KEY_PREVIOUS }, /* Recall */
+ { 0x807f0a, KEY_1 }, /* 1 */
+ { 0x807f1b, KEY_2 }, /* 2 */
+ { 0x807f1f, KEY_3 }, /* 3 */
+ { 0x807f0c, KEY_4 }, /* 4 */
+ { 0x807f0d, KEY_5 }, /* 5 */
+ { 0x807f0e, KEY_6 }, /* 6 */
+ { 0x807f00, KEY_7 }, /* 7 */
+ { 0x807f0f, KEY_8 }, /* 8 */
+ { 0x807f19, KEY_9 }, /* 9 */
+
+ /* Type 2 */
+ /* keys stereo, snapshot unassigned */
+ { 0x866b00, KEY_0 },
+ { 0x866b1b, KEY_1 },
+ { 0x866b02, KEY_2 },
+ { 0x866b03, KEY_3 },
+ { 0x866b04, KEY_4 },
+ { 0x866b05, KEY_5 },
+ { 0x866b06, KEY_6 },
+ { 0x866b07, KEY_7 },
+ { 0x866b08, KEY_8 },
+ { 0x866b09, KEY_9 },
+ { 0x866b12, KEY_POWER },
+ { 0x866b13, KEY_MUTE },
+ { 0x866b0a, KEY_PREVIOUS }, /* Recall */
+ { 0x866b1e, KEY_PAUSE },
+ { 0x866b0c, KEY_VOLUMEUP },
+ { 0x866b18, KEY_VOLUMEDOWN },
+ { 0x866b0b, KEY_CHANNELUP },
+ { 0x866b18, KEY_CHANNELDOWN },
+ { 0x866b10, KEY_ZOOM },
+ { 0x866b1d, KEY_RECORD },
+ { 0x866b0e, KEY_STOP },
+ { 0x866b11, KEY_EPG},
+ { 0x866b1a, KEY_FASTFORWARD },
+ { 0x866b0f, KEY_REWIND },
+ { 0x866b1c, KEY_TV },
+ { 0x866b1b, KEY_TEXT },
+
+};
+
+static struct rc_map_list it913x_v2_map = {
+ .map = {
+ .scan = it913x_v2_rc,
+ .size = ARRAY_SIZE(it913x_v2_rc),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_IT913X_V2,
+ }
+};
+
+static int __init init_rc_it913x_v2_map(void)
+{
+ return rc_map_register(&it913x_v2_map);
+}
+
+static void __exit exit_rc_it913x_v2_map(void)
+{
+ rc_map_unregister(&it913x_v2_map);
+}
+
+module_init(init_rc_it913x_v2_map)
+module_exit(exit_rc_it913x_v2_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
diff --git a/drivers/media/rc/keymaps/rc-kworld-pc150u.c b/drivers/media/rc/keymaps/rc-kworld-pc150u.c
new file mode 100644
index 000000000000..233bb5ee087f
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-kworld-pc150u.c
@@ -0,0 +1,102 @@
+/* kworld-pc150u.c - Keytable for kworld_pc150u Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Kyle Strickland
+ * (based on kworld-plus-tv-analog.c by
+ * Mauro Carvalho Chehab )
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include
+#include
+
+/* Kworld PC150-U
+ Kyle Strickland
+ */
+
+static struct rc_map_table kworld_pc150u[] = {
+ { 0x0c, KEY_MEDIA }, /* Kworld key */
+ { 0x16, KEY_EJECTCLOSECD }, /* -> ) */
+ { 0x1d, KEY_POWER2 },
+
+ { 0x00, KEY_1 },
+ { 0x01, KEY_2 },
+ { 0x02, KEY_3 },
+ { 0x03, KEY_4 },
+ { 0x04, KEY_5 },
+ { 0x05, KEY_6 },
+ { 0x06, KEY_7 },
+ { 0x07, KEY_8 },
+ { 0x08, KEY_9 },
+ { 0x0a, KEY_0 },
+
+ { 0x09, KEY_AGAIN },
+ { 0x14, KEY_MUTE },
+
+ { 0x1e, KEY_LAST },
+ { 0x17, KEY_ZOOM },
+ { 0x1f, KEY_HOMEPAGE },
+ { 0x0e, KEY_ESC },
+
+ { 0x20, KEY_UP },
+ { 0x21, KEY_DOWN },
+ { 0x42, KEY_LEFT },
+ { 0x43, KEY_RIGHT },
+ { 0x0b, KEY_ENTER },
+
+ { 0x10, KEY_CHANNELUP },
+ { 0x11, KEY_CHANNELDOWN },
+
+ { 0x13, KEY_VOLUMEUP },
+ { 0x12, KEY_VOLUMEDOWN },
+
+ { 0x19, KEY_TIME}, /* Timeshift */
+ { 0x1a, KEY_STOP},
+ { 0x1b, KEY_RECORD},
+ { 0x4b, KEY_EMAIL},
+
+ { 0x40, KEY_REWIND},
+ { 0x44, KEY_PLAYPAUSE},
+ { 0x41, KEY_FORWARD},
+ { 0x22, KEY_TEXT},
+
+ { 0x15, KEY_AUDIO}, /* ((*)) */
+ { 0x0f, KEY_MODE}, /* display ratio */
+ { 0x1c, KEY_SYSRQ}, /* snapshot */
+ { 0x4a, KEY_SLEEP}, /* sleep timer */
+
+ { 0x48, KEY_SOUND}, /* switch theater mode */
+ { 0x49, KEY_BLUE}, /* A */
+ { 0x18, KEY_RED}, /* B */
+ { 0x23, KEY_GREEN}, /* C */
+};
+
+static struct rc_map_list kworld_pc150u_map = {
+ .map = {
+ .scan = kworld_pc150u,
+ .size = ARRAY_SIZE(kworld_pc150u),
+ .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */
+ .name = RC_MAP_KWORLD_PC150U,
+ }
+};
+
+static int __init init_rc_map_kworld_pc150u(void)
+{
+ return rc_map_register(&kworld_pc150u_map);
+}
+
+static void __exit exit_rc_map_kworld_pc150u(void)
+{
+ rc_map_unregister(&kworld_pc150u_map);
+}
+
+module_init(init_rc_map_kworld_pc150u)
+module_exit(exit_rc_map_kworld_pc150u)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kyle Strickland ");
diff --git a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
index f3b86c8db679..8d4dae2e2ece 100644
--- a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
+++ b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
@@ -18,6 +18,8 @@
*/
static struct rc_map_table nec_terratec_cinergy_xs[] = {
+
+ /* Terratec Grey IR, with most keys in orange */
{ 0x1441, KEY_HOME},
{ 0x1401, KEY_POWER2},
@@ -78,6 +80,56 @@ static struct rc_map_table nec_terratec_cinergy_xs[] = {
{ 0x144e, KEY_REWIND},
{ 0x144f, KEY_FASTFORWARD},
{ 0x145c, KEY_NEXT},
+
+ /* Terratec Black IR, with most keys in black */
+ { 0x04eb01, KEY_POWER2},
+
+ { 0x04eb02, KEY_1},
+ { 0x04eb03, KEY_2},
+ { 0x04eb04, KEY_3},
+ { 0x04eb05, KEY_4},
+ { 0x04eb06, KEY_5},
+ { 0x04eb07, KEY_6},
+ { 0x04eb08, KEY_7},
+ { 0x04eb09, KEY_8},
+ { 0x04eb0a, KEY_9},
+ { 0x04eb0c, KEY_0},
+
+ { 0x04eb0b, KEY_TEXT}, /* TXT */
+ { 0x04eb0d, KEY_REFRESH}, /* Refresh */
+
+ { 0x04eb0e, KEY_HOME},
+ { 0x04eb0f, KEY_EPG},
+
+ { 0x04eb10, KEY_UP},
+ { 0x04eb11, KEY_LEFT},
+ { 0x04eb12, KEY_OK},
+ { 0x04eb13, KEY_RIGHT},
+ { 0x04eb14, KEY_DOWN},
+
+ { 0x04eb15, KEY_BACKSPACE},
+ { 0x04eb16, KEY_INFO},
+
+ { 0x04eb17, KEY_RED},
+ { 0x04eb18, KEY_GREEN},
+ { 0x04eb19, KEY_YELLOW},
+ { 0x04eb1a, KEY_BLUE},
+
+ { 0x04eb1c, KEY_VOLUMEUP},
+ { 0x04eb1e, KEY_VOLUMEDOWN},
+
+ { 0x04eb1d, KEY_MUTE},
+
+ { 0x04eb1b, KEY_CHANNELUP},
+ { 0x04eb1f, KEY_CHANNELDOWN},
+
+ { 0x04eb40, KEY_RECORD},
+ { 0x04eb4c, KEY_PLAY},
+ { 0x04eb58, KEY_PAUSE},
+
+ { 0x04eb54, KEY_REWIND},
+ { 0x04eb48, KEY_STOP},
+ { 0x04eb5c, KEY_NEXT},
};
static struct rc_map_list nec_terratec_cinergy_xs_map = {
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 21105bf9594d..e150a2e29a4b 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -361,6 +361,8 @@ static struct usb_device_id mceusb_dev_table[] = {
{ USB_DEVICE(VENDOR_FORMOSA, 0xe03c) },
/* Formosa Industrial Computing */
{ USB_DEVICE(VENDOR_FORMOSA, 0xe03e) },
+ /* Formosa Industrial Computing */
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe042) },
/* Fintek eHome Infrared Transceiver (HP branded) */
{ USB_DEVICE(VENDOR_FINTEK, 0x5168) },
/* Fintek eHome Infrared Transceiver */
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index b72f8580e317..96f0a8bb39ea 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -35,7 +35,7 @@ struct ir_raw_event_ctrl {
struct list_head list; /* to keep track of raw clients */
struct task_struct *thread;
spinlock_t lock;
- struct kfifo kfifo; /* fifo for the pulse/space durations */
+ struct kfifo_rec_ptr_1 kfifo; /* fifo for the pulse/space durations */
ktime_t last_event; /* when last event occurred */
enum raw_event_type last_type; /* last event type */
struct rc_dev *dev; /* pointer to the parent rc_dev */
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index f6a930b70c69..6e16b09c24a9 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -1029,6 +1029,7 @@ EXPORT_SYMBOL_GPL(rc_free_device);
int rc_register_device(struct rc_dev *dev)
{
+ static bool raw_init = false; /* raw decoders loaded? */
static atomic_t devno = ATOMIC_INIT(0);
struct rc_map *rc_map;
const char *path;
@@ -1103,6 +1104,12 @@ int rc_register_device(struct rc_dev *dev)
kfree(path);
if (dev->driver_type == RC_DRIVER_IR_RAW) {
+ /* Load raw decoders, if they aren't already */
+ if (!raw_init) {
+ IR_dprintk(1, "Loading raw decoders\n");
+ ir_raw_init();
+ raw_init = true;
+ }
rc = ir_raw_event_register(dev);
if (rc < 0)
goto out_input;
@@ -1176,8 +1183,6 @@ static int __init rc_core_init(void)
return rc;
}
- /* Initialize/load the decoders/keymap code that will be used */
- ir_raw_init();
rc_map_register(&empty_map);
return 0;
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 9adada0d7447..f2479c5c0eb2 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -273,6 +273,16 @@ config VIDEO_ADV7180
To compile this driver as a module, choose M here: the
module will be called adv7180.
+config VIDEO_ADV7183
+ tristate "Analog Devices ADV7183 decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ V4l2 subdevice driver for the Analog Devices
+ ADV7183 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7183.
+
config VIDEO_BT819
tristate "BT819A VideoStream decoder"
depends on VIDEO_V4L2 && I2C
@@ -459,6 +469,9 @@ config VIDEO_AK881X
comment "Camera sensor devices"
+config VIDEO_APTINA_PLL
+ tristate
+
config VIDEO_OV7670
tristate "OmniVision OV7670 sensor support"
depends on I2C && VIDEO_V4L2
@@ -467,9 +480,28 @@ config VIDEO_OV7670
OV7670 VGA camera. It currently only works with the M88ALP01
controller.
+config VIDEO_VS6624
+ tristate "ST VS6624 sensor support"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the ST VS6624
+ camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vs6624.
+
+config VIDEO_MT9M032
+ tristate "MT9M032 camera sensor support"
+ depends on I2C && VIDEO_V4L2
+ select VIDEO_APTINA_PLL
+ ---help---
+ This driver supports MT9M032 camera sensors from Aptina, monochrome
+ models only.
+
config VIDEO_MT9P031
tristate "Aptina MT9P031 support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ select VIDEO_APTINA_PLL
---help---
This is a Video4Linux2 sensor-level driver for the Aptina
(Micron) mt9p031 5 Mpixel camera.
@@ -851,6 +883,8 @@ source "drivers/media/video/davinci/Kconfig"
source "drivers/media/video/omap/Kconfig"
+source "drivers/media/video/blackfin/Kconfig"
+
config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on VIDEO_DEV && ARCH_SHMOBILE
@@ -1087,7 +1121,7 @@ config VIDEO_MX2_HOSTSUPPORT
config VIDEO_MX2
tristate "i.MX27/i.MX25 Camera Sensor Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || ARCH_MX25)
- select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF2_DMA_CONTIG
select VIDEO_MX2_HOSTSUPPORT
---help---
This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor
@@ -1116,7 +1150,8 @@ config VIDEO_ATMEL_ISI
config VIDEO_S5P_MIPI_CSIS
tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver"
- depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P
+ depends on VIDEO_V4L2_SUBDEV_API && REGULATOR
---help---
This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver.
@@ -1176,4 +1211,14 @@ config VIDEO_SAMSUNG_S5P_MFC
help
MFC 5.1 driver for V4L2.
+config VIDEO_MX2_EMMAPRP
+ tristate "MX2 eMMa-PrP support"
+ depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ MX2X chips have a PrP that can be used to process buffers from
+ memory to memory. Operations include resizing and format
+ conversion.
+
endif # V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 354138804cda..a6282a3a6a82 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -12,16 +12,19 @@ omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
+ifeq ($(CONFIG_COMPAT),y)
+ videodev-objs += v4l2-compat-ioctl32.o
+endif
# V4L2 core modules
obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o
-ifeq ($(CONFIG_COMPAT),y)
- obj-$(CONFIG_VIDEO_DEV) += v4l2-compat-ioctl32.o
-endif
-
obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o
+# Helper modules
+
+obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
+
# All i2c modules must come first:
obj-$(CONFIG_VIDEO_TUNER) += tuner.o
@@ -40,8 +43,10 @@ obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
+obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
+obj-$(CONFIG_VIDEO_VS6624) += vs6624.o
obj-$(CONFIG_VIDEO_BT819) += bt819.o
obj-$(CONFIG_VIDEO_BT856) += bt856.o
obj-$(CONFIG_VIDEO_BT866) += bt866.o
@@ -65,6 +70,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
+obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
@@ -177,6 +183,8 @@ obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o
+obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
+
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
@@ -184,6 +192,8 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/
+obj-$(CONFIG_BLACKFIN) += blackfin/
+
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
@@ -199,6 +209,6 @@ obj-y += davinci/
obj-$(CONFIG_ARCH_OMAP) += omap/
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
-ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
index 12eedf4d515a..5b045b4a66fe 100644
--- a/drivers/media/video/adp1653.c
+++ b/drivers/media/video/adp1653.c
@@ -33,7 +33,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -482,24 +481,7 @@ static struct i2c_driver adp1653_i2c_driver = {
.id_table = adp1653_id_table,
};
-static int __init adp1653_init(void)
-{
- int rval;
-
- rval = i2c_add_driver(&adp1653_i2c_driver);
- if (rval)
- printk(KERN_ALERT "%s: failed at i2c_add_driver\n", __func__);
-
- return rval;
-}
-
-static void __exit adp1653_exit(void)
-{
- i2c_del_driver(&adp1653_i2c_driver);
-}
-
-module_init(adp1653_init);
-module_exit(adp1653_exit);
+module_i2c_driver(adp1653_i2c_driver);
MODULE_AUTHOR("Sakari Ailus ");
MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c
index 879f1d839760..6bc01fb98ff8 100644
--- a/drivers/media/video/adv7170.c
+++ b/drivers/media/video/adv7170.c
@@ -407,15 +407,4 @@ static struct i2c_driver adv7170_driver = {
.id_table = adv7170_id,
};
-static __init int init_adv7170(void)
-{
- return i2c_add_driver(&adv7170_driver);
-}
-
-static __exit void exit_adv7170(void)
-{
- i2c_del_driver(&adv7170_driver);
-}
-
-module_init(init_adv7170);
-module_exit(exit_adv7170);
+module_i2c_driver(adv7170_driver);
diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c
index 206078eca853..c7640fab5730 100644
--- a/drivers/media/video/adv7175.c
+++ b/drivers/media/video/adv7175.c
@@ -457,15 +457,4 @@ static struct i2c_driver adv7175_driver = {
.id_table = adv7175_id,
};
-static __init int init_adv7175(void)
-{
- return i2c_add_driver(&adv7175_driver);
-}
-
-static __exit void exit_adv7175(void)
-{
- i2c_del_driver(&adv7175_driver);
-}
-
-module_init(init_adv7175);
-module_exit(exit_adv7175);
+module_i2c_driver(adv7175_driver);
diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c
index d2138d06bcad..b8b6c4b0cad4 100644
--- a/drivers/media/video/adv7180.c
+++ b/drivers/media/video/adv7180.c
@@ -444,20 +444,8 @@ static struct i2c_driver adv7180_driver = {
.id_table = adv7180_id,
};
-static __init int adv7180_init(void)
-{
- return i2c_add_driver(&adv7180_driver);
-}
-
-static __exit void adv7180_exit(void)
-{
- i2c_del_driver(&adv7180_driver);
-}
-
-module_init(adv7180_init);
-module_exit(adv7180_exit);
+module_i2c_driver(adv7180_driver);
MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
MODULE_AUTHOR("Mocean Laboratories");
MODULE_LICENSE("GPL v2");
-
diff --git a/drivers/media/video/adv7183.c b/drivers/media/video/adv7183.c
new file mode 100644
index 000000000000..e1d4c89d7140
--- /dev/null
+++ b/drivers/media/video/adv7183.c
@@ -0,0 +1,699 @@
+/*
+ * adv7183.c Analog Devices ADV7183 video decoder driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "adv7183_regs.h"
+
+struct adv7183 {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+
+ v4l2_std_id std; /* Current set standard */
+ u32 input;
+ u32 output;
+ unsigned reset_pin;
+ unsigned oe_pin;
+ struct v4l2_mbus_framefmt fmt;
+};
+
+/* EXAMPLES USING 27 MHz CLOCK
+ * Mode 1 CVBS Input (Composite Video on AIN5)
+ * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8.
+ */
+static const unsigned char adv7183_init_regs[] = {
+ ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */
+ ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */
+ ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */
+ ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */
+ ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */
+ /* ADI recommended programming sequence */
+ ADV7183_ADI_CTRL, 0x80,
+ ADV7183_CTI_DNR_CTRL_4, 0x20,
+ 0x52, 0x18,
+ 0x58, 0xED,
+ 0x77, 0xC5,
+ 0x7C, 0x93,
+ 0x7D, 0x00,
+ 0xD0, 0x48,
+ 0xD5, 0xA0,
+ 0xD7, 0xEA,
+ ADV7183_SD_SATURATION_CR, 0x3E,
+ ADV7183_PAL_V_END, 0x3E,
+ ADV7183_PAL_F_TOGGLE, 0x0F,
+ ADV7183_ADI_CTRL, 0x00,
+};
+
+static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct adv7183, sd);
+}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct adv7183, hdl)->sd;
+}
+
+static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg,
+ unsigned char value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int adv7183_writeregs(struct v4l2_subdev *sd,
+ const unsigned char *regs, unsigned int num)
+{
+ unsigned char reg, data;
+ unsigned int cnt = 0;
+
+ if (num & 0x1) {
+ v4l2_err(sd, "invalid regs array\n");
+ return -1;
+ }
+
+ while (cnt < num) {
+ reg = *regs++;
+ data = *regs++;
+ cnt += 2;
+
+ adv7183_write(sd, reg, data);
+ }
+ return 0;
+}
+
+static int adv7183_log_status(struct v4l2_subdev *sd)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ v4l2_info(sd, "adv7183: Input control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_IN_CTRL));
+ v4l2_info(sd, "adv7183: Video selection = 0x%02x\n",
+ adv7183_read(sd, ADV7183_VD_SEL));
+ v4l2_info(sd, "adv7183: Output control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_OUT_CTRL));
+ v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_EXT_OUT_CTRL));
+ v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n",
+ adv7183_read(sd, ADV7183_AUTO_DET_EN));
+ v4l2_info(sd, "adv7183: Contrast = 0x%02x\n",
+ adv7183_read(sd, ADV7183_CONTRAST));
+ v4l2_info(sd, "adv7183: Brightness = 0x%02x\n",
+ adv7183_read(sd, ADV7183_BRIGHTNESS));
+ v4l2_info(sd, "adv7183: Hue = 0x%02x\n",
+ adv7183_read(sd, ADV7183_HUE));
+ v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DEF_Y));
+ v4l2_info(sd, "adv7183: Default value C = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DEF_C));
+ v4l2_info(sd, "adv7183: ADI control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ADI_CTRL));
+ v4l2_info(sd, "adv7183: Power Management = 0x%02x\n",
+ adv7183_read(sd, ADV7183_POW_MANAGE));
+ v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_STATUS_1),
+ adv7183_read(sd, ADV7183_STATUS_2),
+ adv7183_read(sd, ADV7183_STATUS_3));
+ v4l2_info(sd, "adv7183: Ident = 0x%02x\n",
+ adv7183_read(sd, ADV7183_IDENT));
+ v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL));
+ v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1));
+ v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_SHAP_FILT_CTRL),
+ adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2));
+ v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_COMB_FILT_CTRL));
+ v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ADI_CTRL_2));
+ v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_PIX_DELAY_CTRL));
+ v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_MISC_GAIN_CTRL));
+ v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_AGC_MODE_CTRL));
+ v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1),
+ adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2));
+ v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1),
+ adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2));
+ v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1),
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2),
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3));
+ v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_1),
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_2),
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_3));
+ v4l2_info(sd, "adv7183: Polarity = 0x%02x\n",
+ adv7183_read(sd, ADV7183_POLARITY));
+ v4l2_info(sd, "adv7183: ADC control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ADC_CTRL));
+ v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_SD_OFFSET_CB),
+ adv7183_read(sd, ADV7183_SD_OFFSET_CR));
+ v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_SD_SATURATION_CB),
+ adv7183_read(sd, ADV7183_SD_SATURATION_CR));
+ v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DRIVE_STR));
+ v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name);
+ return 0;
+}
+
+static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ *std = decoder->std;
+ return 0;
+}
+
+static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+ int reg;
+
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
+ if (std == V4L2_STD_PAL_60)
+ reg |= 0x60;
+ else if (std == V4L2_STD_NTSC_443)
+ reg |= 0x70;
+ else if (std == V4L2_STD_PAL_N)
+ reg |= 0x90;
+ else if (std == V4L2_STD_PAL_M)
+ reg |= 0xA0;
+ else if (std == V4L2_STD_PAL_Nc)
+ reg |= 0xC0;
+ else if (std & V4L2_STD_PAL)
+ reg |= 0x80;
+ else if (std & V4L2_STD_NTSC)
+ reg |= 0x50;
+ else if (std & V4L2_STD_SECAM)
+ reg |= 0xE0;
+ else
+ return -EINVAL;
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
+
+ decoder->std = std;
+
+ return 0;
+}
+
+static int adv7183_reset(struct v4l2_subdev *sd, u32 val)
+{
+ int reg;
+
+ reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80;
+ adv7183_write(sd, ADV7183_POW_MANAGE, reg);
+ /* wait 5ms before any further i2c writes are performed */
+ usleep_range(5000, 10000);
+ return 0;
+}
+
+static int adv7183_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+ int reg;
+
+ if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT))
+ return -EINVAL;
+
+ if (input != decoder->input) {
+ decoder->input = input;
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0;
+ switch (input) {
+ case ADV7183_COMPOSITE1:
+ reg |= 0x1;
+ break;
+ case ADV7183_COMPOSITE2:
+ reg |= 0x2;
+ break;
+ case ADV7183_COMPOSITE3:
+ reg |= 0x3;
+ break;
+ case ADV7183_COMPOSITE4:
+ reg |= 0x4;
+ break;
+ case ADV7183_COMPOSITE5:
+ reg |= 0x5;
+ break;
+ case ADV7183_COMPOSITE6:
+ reg |= 0xB;
+ break;
+ case ADV7183_COMPOSITE7:
+ reg |= 0xC;
+ break;
+ case ADV7183_COMPOSITE8:
+ reg |= 0xD;
+ break;
+ case ADV7183_COMPOSITE9:
+ reg |= 0xE;
+ break;
+ case ADV7183_COMPOSITE10:
+ reg |= 0xF;
+ break;
+ case ADV7183_SVIDEO0:
+ reg |= 0x6;
+ break;
+ case ADV7183_SVIDEO1:
+ reg |= 0x7;
+ break;
+ case ADV7183_SVIDEO2:
+ reg |= 0x8;
+ break;
+ case ADV7183_COMPONENT0:
+ reg |= 0x9;
+ break;
+ case ADV7183_COMPONENT1:
+ reg |= 0xA;
+ break;
+ default:
+ break;
+ }
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
+ }
+
+ if (output != decoder->output) {
+ decoder->output = output;
+ reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0;
+ switch (output) {
+ case ADV7183_16BIT_OUT:
+ reg |= 0x9;
+ break;
+ default:
+ reg |= 0xC;
+ break;
+ }
+ adv7183_write(sd, ADV7183_OUT_CTRL, reg);
+ }
+
+ return 0;
+}
+
+static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ int val = ctrl->val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (val < 0)
+ val = 127 - val;
+ adv7183_write(sd, ADV7183_BRIGHTNESS, val);
+ break;
+ case V4L2_CID_CONTRAST:
+ adv7183_write(sd, ADV7183_CONTRAST, val);
+ break;
+ case V4L2_CID_SATURATION:
+ adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8);
+ adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF));
+ break;
+ case V4L2_CID_HUE:
+ adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8);
+ adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+ int reg;
+
+ /* enable autodetection block */
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
+
+ /* wait autodetection switch */
+ mdelay(10);
+
+ /* get autodetection result */
+ reg = adv7183_read(sd, ADV7183_STATUS_1);
+ switch ((reg >> 0x4) & 0x7) {
+ case 0:
+ *std = V4L2_STD_NTSC;
+ break;
+ case 1:
+ *std = V4L2_STD_NTSC_443;
+ break;
+ case 2:
+ *std = V4L2_STD_PAL_M;
+ break;
+ case 3:
+ *std = V4L2_STD_PAL_60;
+ break;
+ case 4:
+ *std = V4L2_STD_PAL;
+ break;
+ case 5:
+ *std = V4L2_STD_SECAM;
+ break;
+ case 6:
+ *std = V4L2_STD_PAL_Nc;
+ break;
+ case 7:
+ *std = V4L2_STD_SECAM;
+ break;
+ default:
+ *std = V4L2_STD_UNKNOWN;
+ break;
+ }
+
+ /* after std detection, write back user set std */
+ adv7183_s_std(sd, decoder->std);
+ return 0;
+}
+
+static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ int reg;
+
+ *status = V4L2_IN_ST_NO_SIGNAL;
+ reg = adv7183_read(sd, ADV7183_STATUS_1);
+ if (reg < 0)
+ return reg;
+ if (reg & 0x1)
+ *status = 0;
+ return 0;
+}
+
+static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index > 0)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_UYVY8_2X8;
+ return 0;
+}
+
+static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ if (decoder->std & V4L2_STD_525_60) {
+ fmt->field = V4L2_FIELD_SEQ_TB;
+ fmt->width = 720;
+ fmt->height = 480;
+ } else {
+ fmt->field = V4L2_FIELD_SEQ_BT;
+ fmt->width = 720;
+ fmt->height = 576;
+ }
+ return 0;
+}
+
+static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ adv7183_try_mbus_fmt(sd, fmt);
+ decoder->fmt = *fmt;
+ return 0;
+}
+
+static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ *fmt = decoder->fmt;
+ return 0;
+}
+
+static int adv7183_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ if (enable)
+ gpio_direction_output(decoder->oe_pin, 0);
+ else
+ gpio_direction_output(decoder->oe_pin, 1);
+ udelay(1);
+ return 0;
+}
+
+static int adv7183_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int rev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* 0x11 for adv7183, 0x13 for adv7183b */
+ rev = adv7183_read(sd, ADV7183_IDENT);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, ®->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = adv7183_read(sd, reg->reg & 0xff);
+ reg->size = 1;
+ return 0;
+}
+
+static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, ®->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static const struct v4l2_ctrl_ops adv7183_ctrl_ops = {
+ .s_ctrl = adv7183_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops adv7183_core_ops = {
+ .log_status = adv7183_log_status,
+ .g_std = adv7183_g_std,
+ .s_std = adv7183_s_std,
+ .reset = adv7183_reset,
+ .g_chip_ident = adv7183_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = adv7183_g_register,
+ .s_register = adv7183_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops adv7183_video_ops = {
+ .s_routing = adv7183_s_routing,
+ .querystd = adv7183_querystd,
+ .g_input_status = adv7183_g_input_status,
+ .enum_mbus_fmt = adv7183_enum_mbus_fmt,
+ .try_mbus_fmt = adv7183_try_mbus_fmt,
+ .s_mbus_fmt = adv7183_s_mbus_fmt,
+ .g_mbus_fmt = adv7183_g_mbus_fmt,
+ .s_stream = adv7183_s_stream,
+};
+
+static const struct v4l2_subdev_ops adv7183_ops = {
+ .core = &adv7183_core_ops,
+ .video = &adv7183_video_ops,
+};
+
+static int adv7183_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adv7183 *decoder;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
+ int ret;
+ struct v4l2_mbus_framefmt fmt;
+ const unsigned *pin_array;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ pin_array = client->dev.platform_data;
+ if (pin_array == NULL)
+ return -EINVAL;
+
+ decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL);
+ if (decoder == NULL)
+ return -ENOMEM;
+
+ decoder->reset_pin = pin_array[0];
+ decoder->oe_pin = pin_array[1];
+
+ if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) {
+ v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin);
+ ret = -EBUSY;
+ goto err_free_decoder;
+ }
+
+ if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) {
+ v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin);
+ ret = -EBUSY;
+ goto err_free_reset;
+ }
+
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &adv7183_ops);
+
+ hdl = &decoder->hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080);
+ /* hook the control handler into the driver */
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ ret = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ goto err_free_oe;
+ }
+
+ /* v4l2 doesn't support an autodetect standard, pick PAL as default */
+ decoder->std = V4L2_STD_PAL;
+ decoder->input = ADV7183_COMPOSITE4;
+ decoder->output = ADV7183_8BIT_OUT;
+
+ gpio_direction_output(decoder->oe_pin, 1);
+ /* reset chip */
+ gpio_direction_output(decoder->reset_pin, 0);
+ /* reset pulse width at least 5ms */
+ mdelay(10);
+ gpio_direction_output(decoder->reset_pin, 1);
+ /* wait 5ms before any further i2c writes are performed */
+ mdelay(5);
+
+ adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs));
+ adv7183_s_std(sd, decoder->std);
+ fmt.width = 720;
+ fmt.height = 576;
+ adv7183_s_mbus_fmt(sd, &fmt);
+
+ /* initialize the hardware to the default control values */
+ ret = v4l2_ctrl_handler_setup(hdl);
+ if (ret) {
+ v4l2_ctrl_handler_free(hdl);
+ goto err_free_oe;
+ }
+
+ return 0;
+err_free_oe:
+ gpio_free(decoder->oe_pin);
+err_free_reset:
+ gpio_free(decoder->reset_pin);
+err_free_decoder:
+ kfree(decoder);
+ return ret;
+}
+
+static int adv7183_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ gpio_free(decoder->oe_pin);
+ gpio_free(decoder->reset_pin);
+ kfree(decoder);
+ return 0;
+}
+
+static const struct i2c_device_id adv7183_id[] = {
+ {"adv7183", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, adv7183_id);
+
+static struct i2c_driver adv7183_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adv7183",
+ },
+ .probe = adv7183_probe,
+ .remove = __devexit_p(adv7183_remove),
+ .id_table = adv7183_id,
+};
+
+static __init int adv7183_init(void)
+{
+ return i2c_add_driver(&adv7183_driver);
+}
+
+static __exit void adv7183_exit(void)
+{
+ i2c_del_driver(&adv7183_driver);
+}
+
+module_init(adv7183_init);
+module_exit(adv7183_exit);
+
+MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver");
+MODULE_AUTHOR("Scott Jiang ");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/adv7183_regs.h b/drivers/media/video/adv7183_regs.h
new file mode 100644
index 000000000000..4a5b7d211d2f
--- /dev/null
+++ b/drivers/media/video/adv7183_regs.h
@@ -0,0 +1,107 @@
+/*
+ * adv7183 - Analog Devices ADV7183 video decoder registers
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _ADV7183_REGS_H_
+#define _ADV7183_REGS_H_
+
+#define ADV7183_IN_CTRL 0x00 /* Input control */
+#define ADV7183_VD_SEL 0x01 /* Video selection */
+#define ADV7183_OUT_CTRL 0x03 /* Output control */
+#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */
+#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */
+#define ADV7183_CONTRAST 0x08 /* Contrast */
+#define ADV7183_BRIGHTNESS 0x0A /* Brightness */
+#define ADV7183_HUE 0x0B /* Hue */
+#define ADV7183_DEF_Y 0x0C /* Default value Y */
+#define ADV7183_DEF_C 0x0D /* Default value C */
+#define ADV7183_ADI_CTRL 0x0E /* ADI control */
+#define ADV7183_POW_MANAGE 0x0F /* Power Management */
+#define ADV7183_STATUS_1 0x10 /* Status 1 */
+#define ADV7183_IDENT 0x11 /* Ident */
+#define ADV7183_STATUS_2 0x12 /* Status 2 */
+#define ADV7183_STATUS_3 0x13 /* Status 3 */
+#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */
+#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */
+#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */
+#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */
+#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */
+#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */
+#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */
+#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */
+#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */
+#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */
+#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */
+#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */
+#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */
+#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */
+#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */
+#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */
+#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */
+#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */
+#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */
+#define ADV7183_POLARITY 0x37 /* Polarity */
+#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */
+#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */
+#define ADV7183_ADC_CTRL 0x3A /* ADC control */
+#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */
+#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */
+#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */
+#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */
+#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */
+#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */
+#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */
+#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */
+#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */
+#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */
+#define ADV7183_LOCK_CNT 0x51 /* Lock count */
+#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */
+#define ADV7183_VBI_INFO 0x90 /* VBI info */
+#define ADV7183_WSS_1 0x91 /* WSS 1 */
+#define ADV7183_WSS_2 0x92 /* WSS 2 */
+#define ADV7183_EDTV_1 0x93 /* EDTV 1 */
+#define ADV7183_EDTV_2 0x94 /* EDTV 2 */
+#define ADV7183_EDTV_3 0x95 /* EDTV 3 */
+#define ADV7183_CGMS_1 0x96 /* CGMS 1 */
+#define ADV7183_CGMS_2 0x97 /* CGMS 2 */
+#define ADV7183_CGMS_3 0x98 /* CGMS 3 */
+#define ADV7183_CCAP_1 0x99 /* CCAP 1 */
+#define ADV7183_CCAP_2 0x9A /* CCAP 2 */
+#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */
+#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */
+#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */
+#define ADV7183_CRC_EN 0xB2 /* CRC enable */
+#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */
+#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */
+#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */
+#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */
+#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */
+#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */
+#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */
+#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */
+#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */
+#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */
+#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */
+#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */
+#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */
+#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */
+#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */
+#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */
+#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */
+
+#endif
diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c
index 021fab23070d..119b60401bf3 100644
--- a/drivers/media/video/adv7343.c
+++ b/drivers/media/video/adv7343.c
@@ -475,15 +475,4 @@ static struct i2c_driver adv7343_driver = {
.id_table = adv7343_id,
};
-static __init int init_adv7343(void)
-{
- return i2c_add_driver(&adv7343_driver);
-}
-
-static __exit void exit_adv7343(void)
-{
- i2c_del_driver(&adv7343_driver);
-}
-
-module_init(init_adv7343);
-module_exit(exit_adv7343);
+module_i2c_driver(adv7343_driver);
diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c
index 53c496c00fb6..ba674656b10d 100644
--- a/drivers/media/video/ak881x.c
+++ b/drivers/media/video/ak881x.c
@@ -352,18 +352,7 @@ static struct i2c_driver ak881x_i2c_driver = {
.id_table = ak881x_id,
};
-static int __init ak881x_module_init(void)
-{
- return i2c_add_driver(&ak881x_i2c_driver);
-}
-
-static void __exit ak881x_module_exit(void)
-{
- i2c_del_driver(&ak881x_i2c_driver);
-}
-
-module_init(ak881x_module_init);
-module_exit(ak881x_module_exit);
+module_i2c_driver(ak881x_i2c_driver);
MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814");
MODULE_AUTHOR("Guennadi Liakhovetski ");
diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/video/aptina-pll.c
new file mode 100644
index 000000000000..0bd3813bb59d
--- /dev/null
+++ b/drivers/media/video/aptina-pll.c
@@ -0,0 +1,174 @@
+/*
+ * Aptina Sensor PLL Configuration
+ *
+ * Copyright (C) 2012 Laurent Pinchart
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "aptina-pll.h"
+
+int aptina_pll_calculate(struct device *dev,
+ const struct aptina_pll_limits *limits,
+ struct aptina_pll *pll)
+{
+ unsigned int mf_min;
+ unsigned int mf_max;
+ unsigned int p1_min;
+ unsigned int p1_max;
+ unsigned int p1;
+ unsigned int div;
+
+ dev_dbg(dev, "PLL: ext clock %u pix clock %u\n",
+ pll->ext_clock, pll->pix_clock);
+
+ if (pll->ext_clock < limits->ext_clock_min ||
+ pll->ext_clock > limits->ext_clock_max) {
+ dev_err(dev, "pll: invalid external clock frequency.\n");
+ return -EINVAL;
+ }
+
+ if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) {
+ dev_err(dev, "pll: invalid pixel clock frequency.\n");
+ return -EINVAL;
+ }
+
+ /* Compute the multiplier M and combined N*P1 divisor. */
+ div = gcd(pll->pix_clock, pll->ext_clock);
+ pll->m = pll->pix_clock / div;
+ div = pll->ext_clock / div;
+
+ /* We now have the smallest M and N*P1 values that will result in the
+ * desired pixel clock frequency, but they might be out of the valid
+ * range. Compute the factor by which we should multiply them given the
+ * following constraints:
+ *
+ * - minimum/maximum multiplier
+ * - minimum/maximum multiplier output clock frequency assuming the
+ * minimum/maximum N value
+ * - minimum/maximum combined N*P1 divisor
+ */
+ mf_min = DIV_ROUND_UP(limits->m_min, pll->m);
+ mf_min = max(mf_min, limits->out_clock_min /
+ (pll->ext_clock / limits->n_min * pll->m));
+ mf_min = max(mf_min, limits->n_min * limits->p1_min / div);
+ mf_max = limits->m_max / pll->m;
+ mf_max = min(mf_max, limits->out_clock_max /
+ (pll->ext_clock / limits->n_max * pll->m));
+ mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div));
+
+ dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max);
+ if (mf_min > mf_max) {
+ dev_err(dev, "pll: no valid combined N*P1 divisor.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * We're looking for the highest acceptable P1 value for which a
+ * multiplier factor MF exists that fulfills the following conditions:
+ *
+ * 1. p1 is in the [p1_min, p1_max] range given by the limits and is
+ * even
+ * 2. mf is in the [mf_min, mf_max] range computed above
+ * 3. div * mf is a multiple of p1, in order to compute
+ * n = div * mf / p1
+ * m = pll->m * mf
+ * 4. the internal clock frequency, given by ext_clock / n, is in the
+ * [int_clock_min, int_clock_max] range given by the limits
+ * 5. the output clock frequency, given by ext_clock / n * m, is in the
+ * [out_clock_min, out_clock_max] range given by the limits
+ *
+ * The first naive approach is to iterate over all p1 values acceptable
+ * according to (1) and all mf values acceptable according to (2), and
+ * stop at the first combination that fulfills (3), (4) and (5). This
+ * has a O(n^2) complexity.
+ *
+ * Instead of iterating over all mf values in the [mf_min, mf_max] range
+ * we can compute the mf increment between two acceptable values
+ * according to (3) with
+ *
+ * mf_inc = p1 / gcd(div, p1) (6)
+ *
+ * and round the minimum up to the nearest multiple of mf_inc. This will
+ * restrict the number of mf values to be checked.
+ *
+ * Furthermore, conditions (4) and (5) only restrict the range of
+ * acceptable p1 and mf values by modifying the minimum and maximum
+ * limits. (5) can be expressed as
+ *
+ * ext_clock / (div * mf / p1) * m * mf >= out_clock_min
+ * ext_clock / (div * mf / p1) * m * mf <= out_clock_max
+ *
+ * or
+ *
+ * p1 >= out_clock_min * div / (ext_clock * m) (7)
+ * p1 <= out_clock_max * div / (ext_clock * m)
+ *
+ * Similarly, (4) can be expressed as
+ *
+ * mf >= ext_clock * p1 / (int_clock_max * div) (8)
+ * mf <= ext_clock * p1 / (int_clock_min * div)
+ *
+ * We can thus iterate over the restricted p1 range defined by the
+ * combination of (1) and (7), and then compute the restricted mf range
+ * defined by the combination of (2), (6) and (8). If the resulting mf
+ * range is not empty, any value in the mf range is acceptable. We thus
+ * select the mf lwoer bound and the corresponding p1 value.
+ */
+ if (limits->p1_min == 0) {
+ dev_err(dev, "pll: P1 minimum value must be >0.\n");
+ return -EINVAL;
+ }
+
+ p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div,
+ pll->ext_clock * pll->m));
+ p1_max = min(limits->p1_max, limits->out_clock_max * div /
+ (pll->ext_clock * pll->m));
+
+ for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) {
+ unsigned int mf_inc = p1 / gcd(div, p1);
+ unsigned int mf_high;
+ unsigned int mf_low;
+
+ mf_low = max(roundup(mf_min, mf_inc),
+ DIV_ROUND_UP(pll->ext_clock * p1,
+ limits->int_clock_max * div));
+ mf_high = min(mf_max, pll->ext_clock * p1 /
+ (limits->int_clock_min * div));
+
+ if (mf_low > mf_high)
+ continue;
+
+ pll->n = div * mf_low / p1;
+ pll->m *= mf_low;
+ pll->p1 = p1;
+ dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1);
+ return 0;
+ }
+
+ dev_err(dev, "pll: no valid N and P1 divisors found.\n");
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(aptina_pll_calculate);
+
+MODULE_DESCRIPTION("Aptina PLL Helpers");
+MODULE_AUTHOR("Laurent Pinchart ");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/aptina-pll.h b/drivers/media/video/aptina-pll.h
new file mode 100644
index 000000000000..b370e341e75d
--- /dev/null
+++ b/drivers/media/video/aptina-pll.h
@@ -0,0 +1,56 @@
+/*
+ * Aptina Sensor PLL Configuration
+ *
+ * Copyright (C) 2012 Laurent Pinchart
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __APTINA_PLL_H
+#define __APTINA_PLL_H
+
+struct aptina_pll {
+ unsigned int ext_clock;
+ unsigned int pix_clock;
+
+ unsigned int n;
+ unsigned int m;
+ unsigned int p1;
+};
+
+struct aptina_pll_limits {
+ unsigned int ext_clock_min;
+ unsigned int ext_clock_max;
+ unsigned int int_clock_min;
+ unsigned int int_clock_max;
+ unsigned int out_clock_min;
+ unsigned int out_clock_max;
+ unsigned int pix_clock_max;
+
+ unsigned int n_min;
+ unsigned int n_max;
+ unsigned int m_min;
+ unsigned int m_max;
+ unsigned int p1_min;
+ unsigned int p1_max;
+};
+
+struct device;
+
+int aptina_pll_calculate(struct device *dev,
+ const struct aptina_pll_limits *limits,
+ struct aptina_pll *pll);
+
+#endif /* __APTINA_PLL_H */
diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c
index f241702a0f36..7a3371f044fc 100644
--- a/drivers/media/video/as3645a.c
+++ b/drivers/media/video/as3645a.c
@@ -881,24 +881,7 @@ static struct i2c_driver as3645a_i2c_driver = {
.id_table = as3645a_id_table,
};
-static int __init as3645a_init(void)
-{
- int rval;
-
- rval = i2c_add_driver(&as3645a_i2c_driver);
- if (rval)
- pr_err("%s: Failed to register the driver\n", AS3645A_NAME);
-
- return rval;
-}
-
-static void __exit as3645a_exit(void)
-{
- i2c_del_driver(&as3645a_i2c_driver);
-}
-
-module_init(as3645a_init);
-module_exit(as3645a_exit);
+module_i2c_driver(as3645a_i2c_driver);
MODULE_AUTHOR("Laurent Pinchart ");
MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones");
diff --git a/drivers/media/video/blackfin/Kconfig b/drivers/media/video/blackfin/Kconfig
new file mode 100644
index 000000000000..ecd5323768b7
--- /dev/null
+++ b/drivers/media/video/blackfin/Kconfig
@@ -0,0 +1,10 @@
+config VIDEO_BLACKFIN_CAPTURE
+ tristate "Blackfin Video Capture Driver"
+ depends on VIDEO_V4L2 && BLACKFIN && I2C
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ V4L2 bridge driver for Blackfin video capture device.
+ Choose PPI or EPPI as its interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin_video_capture.
diff --git a/drivers/media/video/blackfin/Makefile b/drivers/media/video/blackfin/Makefile
new file mode 100644
index 000000000000..aa3a0a216387
--- /dev/null
+++ b/drivers/media/video/blackfin/Makefile
@@ -0,0 +1,2 @@
+bfin_video_capture-objs := bfin_capture.o ppi.o
+obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o
diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c
new file mode 100644
index 000000000000..514fcf742f5a
--- /dev/null
+++ b/drivers/media/video/blackfin/bfin_capture.c
@@ -0,0 +1,1059 @@
+/*
+ * Analog Devices video capture driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include