Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6

* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (94 commits)
  V4L/DVB: tvp7002: fix write to H-PLL Feedback Divider LSB register
  V4L/DVB: dvb: siano: free spinlock before schedule()
  V4L/DVB: media: video: pvrusb2: remove custom hex_to_bin()
  V4L/DVB: drivers: usbvideo: remove custom implementation of hex_to_bin()
  V4L/DVB: Report supported QAM modes on bt8xx
  V4L/DVB: media: ir-keytable: null dereference in debug code
  V4L/DVB: ivtv: convert to the new control framework
  V4L/DVB: ivtv: convert gpio subdev to new control framework
  V4L/DVB: wm8739: convert to the new control framework
  V4L/DVB: cs53l32a: convert to new control framework
  V4L/DVB: wm8775: convert to the new control framework
  V4L/DVB: cx2341x: convert to the control framework
  V4L/DVB: cx25840: convert to the new control framework
  V4L/DVB: cx25840/ivtv: replace ugly priv control with s_config
  V4L/DVB: saa717x: convert to the new control framework
  V4L/DVB: msp3400: convert to the new control framework
  V4L/DVB: saa7115: convert to the new control framework
  V4L/DVB: v4l2: hook up the new control framework into the core framework
  V4L/DVB: Documentation: add v4l2-controls.txt documenting the new controls API
  V4L/DVB: v4l2-ctrls: Whitespace cleanups
  ...
This commit is contained in:
Linus Torvalds 2010-08-10 15:09:54 -07:00
commit 7ae0dea900
109 changed files with 13447 additions and 5044 deletions

View File

@ -229,6 +229,22 @@ on working with the default settings initially.</para>
and LIRC_SETUP_END. Drivers can also choose to ignore these ioctls.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>LIRC_SET_WIDEBAND_RECEIVER</term>
<listitem>
<para>Some receivers are equipped with special wide band receiver which is intended
to be used to learn output of existing remote.
Calling that ioctl with (1) will enable it, and with (0) disable it.
This might be useful of receivers that have otherwise narrow band receiver
that prevents them to be used with some remotes.
Wide band receiver might also be more precise
On the other hand its disadvantage it usually reduced range of reception.
Note: wide band receiver might be implictly enabled if you enable
carrier reports. In that case it will be disabled as soon as you disable
carrier reports. Trying to disable wide band receiver while carrier
reports are active will do nothing.</para>
</listitem>
</varlistentry>
</variablelist>
</section>

View File

@ -240,6 +240,45 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para>
<entry>r<subscript>1</subscript></entry>
<entry>r<subscript>0</subscript></entry>
</row>
<row id="V4L2-PIX-FMT-BGR666">
<entry><constant>V4L2_PIX_FMT_BGR666</constant></entry>
<entry>'BGRH'</entry>
<entry></entry>
<entry>b<subscript>5</subscript></entry>
<entry>b<subscript>4</subscript></entry>
<entry>b<subscript>3</subscript></entry>
<entry>b<subscript>2</subscript></entry>
<entry>b<subscript>1</subscript></entry>
<entry>b<subscript>0</subscript></entry>
<entry>g<subscript>5</subscript></entry>
<entry>g<subscript>4</subscript></entry>
<entry></entry>
<entry>g<subscript>3</subscript></entry>
<entry>g<subscript>2</subscript></entry>
<entry>g<subscript>1</subscript></entry>
<entry>g<subscript>0</subscript></entry>
<entry>r<subscript>5</subscript></entry>
<entry>r<subscript>4</subscript></entry>
<entry>r<subscript>3</subscript></entry>
<entry>r<subscript>2</subscript></entry>
<entry></entry>
<entry>r<subscript>1</subscript></entry>
<entry>r<subscript>0</subscript></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
</row>
<row id="V4L2-PIX-FMT-BGR24">
<entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
<entry>'BGR3'</entry>
@ -700,6 +739,45 @@ defined in error. Drivers may interpret them as in <xref
<entry>b<subscript>1</subscript></entry>
<entry>b<subscript>0</subscript></entry>
</row>
<row id="V4L2-PIX-FMT-BGR666">
<entry><constant>V4L2_PIX_FMT_BGR666</constant></entry>
<entry>'BGRH'</entry>
<entry></entry>
<entry>b<subscript>5</subscript></entry>
<entry>b<subscript>4</subscript></entry>
<entry>b<subscript>3</subscript></entry>
<entry>b<subscript>2</subscript></entry>
<entry>b<subscript>1</subscript></entry>
<entry>b<subscript>0</subscript></entry>
<entry>g<subscript>5</subscript></entry>
<entry>g<subscript>4</subscript></entry>
<entry></entry>
<entry>g<subscript>3</subscript></entry>
<entry>g<subscript>2</subscript></entry>
<entry>g<subscript>1</subscript></entry>
<entry>g<subscript>0</subscript></entry>
<entry>r<subscript>5</subscript></entry>
<entry>r<subscript>4</subscript></entry>
<entry>r<subscript>3</subscript></entry>
<entry>r<subscript>2</subscript></entry>
<entry></entry>
<entry>r<subscript>1</subscript></entry>
<entry>r<subscript>0</subscript></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
<entry></entry>
</row>
<row><!-- id="V4L2-PIX-FMT-BGR24" -->
<entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
<entry>'BGR3'</entry>

View File

@ -0,0 +1,648 @@
Introduction
============
The V4L2 control API seems simple enough, but quickly becomes very hard to
implement correctly in drivers. But much of the code needed to handle controls
is actually not driver specific and can be moved to the V4L core framework.
After all, the only part that a driver developer is interested in is:
1) How do I add a control?
2) How do I set the control's value? (i.e. s_ctrl)
And occasionally:
3) How do I get the control's value? (i.e. g_volatile_ctrl)
4) How do I validate the user's proposed control value? (i.e. try_ctrl)
All the rest is something that can be done centrally.
The control framework was created in order to implement all the rules of the
V4L2 specification with respect to controls in a central place. And to make
life as easy as possible for the driver developer.
Note that the control framework relies on the presence of a struct v4l2_device
for V4L2 drivers and struct v4l2_subdev for sub-device drivers.
Objects in the framework
========================
There are two main objects:
The v4l2_ctrl object describes the control properties and keeps track of the
control's value (both the current value and the proposed new value).
v4l2_ctrl_handler is the object that keeps track of controls. It maintains a
list of v4l2_ctrl objects that it owns and another list of references to
controls, possibly to controls owned by other handlers.
Basic usage for V4L2 and sub-device drivers
===========================================
1) Prepare the driver:
1.1) Add the handler to your driver's top-level struct:
struct foo_dev {
...
struct v4l2_ctrl_handler ctrl_handler;
...
};
struct foo_dev *foo;
1.2) Initialize the handler:
v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
The second argument is a hint telling the function how many controls this
handler is expected to handle. It will allocate a hashtable based on this
information. It is a hint only.
1.3) Hook the control handler into the driver:
1.3.1) For V4L2 drivers do this:
struct foo_dev {
...
struct v4l2_device v4l2_dev;
...
struct v4l2_ctrl_handler ctrl_handler;
...
};
foo->v4l2_dev.ctrl_handler = &foo->ctrl_handler;
Where foo->v4l2_dev is of type struct v4l2_device.
Finally, remove all control functions from your v4l2_ioctl_ops:
vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl,
vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls.
Those are now no longer needed.
1.3.2) For sub-device drivers do this:
struct foo_dev {
...
struct v4l2_subdev sd;
...
struct v4l2_ctrl_handler ctrl_handler;
...
};
foo->sd.ctrl_handler = &foo->ctrl_handler;
Where foo->sd is of type struct v4l2_subdev.
And set all core control ops in your struct v4l2_subdev_core_ops to these
helpers:
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
Note: this is a temporary solution only. Once all V4L2 drivers that depend
on subdev drivers are converted to the control framework these helpers will
no longer be needed.
1.4) Clean up the handler at the end:
v4l2_ctrl_handler_free(&foo->ctrl_handler);
2) Add controls:
You add non-menu controls by calling v4l2_ctrl_new_std:
struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ops,
u32 id, s32 min, s32 max, u32 step, s32 def);
Menu controls are added by calling v4l2_ctrl_new_std_menu:
struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ops,
u32 id, s32 max, s32 skip_mask, s32 def);
These functions are typically called right after the v4l2_ctrl_handler_init:
v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 128);
v4l2_ctrl_new_std_menu(&foo->ctrl_handler, &foo_ctrl_ops,
V4L2_CID_POWER_LINE_FREQUENCY,
V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
...
if (foo->ctrl_handler.error) {
int err = foo->ctrl_handler.error;
v4l2_ctrl_handler_free(&foo->ctrl_handler);
return err;
}
The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new
control, but if you do not need to access the pointer outside the control ops,
then there is no need to store it.
The v4l2_ctrl_new_std function will fill in most fields based on the control
ID except for the min, max, step and default values. These are passed in the
last four arguments. These values are driver specific while control attributes
like type, name, flags are all global. The control's current value will be set
to the default value.
The v4l2_ctrl_new_std_menu function is very similar but it is used for menu
controls. There is no min argument since that is always 0 for menu controls,
and instead of a step there is a skip_mask argument: if bit X is 1, then menu
item X is skipped.
Note that if something fails, the function will return NULL or an error and
set ctrl_handler->error to the error code. If ctrl_handler->error was already
set, then it will just return and do nothing. This is also true for
v4l2_ctrl_handler_init if it cannot allocate the internal data structure.
This makes it easy to init the handler and just add all controls and only check
the error code at the end. Saves a lot of repetitive error checking.
It is recommended to add controls in ascending control ID order: it will be
a bit faster that way.
3) Optionally force initial control setup:
v4l2_ctrl_handler_setup(&foo->ctrl_handler);
This will call s_ctrl for all controls unconditionally. Effectively this
initializes the hardware to the default control values. It is recommended
that you do this as this ensures that both the internal data structures and
the hardware are in sync.
4) Finally: implement the v4l2_ctrl_ops
static const struct v4l2_ctrl_ops foo_ctrl_ops = {
.s_ctrl = foo_s_ctrl,
};
Usually all you need is s_ctrl:
static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
write_reg(0x123, ctrl->val);
break;
case V4L2_CID_CONTRAST:
write_reg(0x456, ctrl->val);
break;
}
return 0;
}
The control ops are called with the v4l2_ctrl pointer as argument.
The new control value has already been validated, so all you need to do is
to actually update the hardware registers.
You're done! And this is sufficient for most of the drivers we have. No need
to do any validation of control values, or implement QUERYCTRL/QUERYMENU. And
G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported.
==============================================================================
The remainder of this document deals with more advanced topics and scenarios.
In practice the basic usage as described above is sufficient for most drivers.
===============================================================================
Inheriting Controls
===================
When a sub-device is registered with a V4L2 driver by calling
v4l2_device_register_subdev() and the ctrl_handler fields of both v4l2_subdev
and v4l2_device are set, then the controls of the subdev will become
automatically available in the V4L2 driver as well. If the subdev driver
contains controls that already exist in the V4L2 driver, then those will be
skipped (so a V4L2 driver can always override a subdev control).
What happens here is that v4l2_device_register_subdev() calls
v4l2_ctrl_add_handler() adding the controls of the subdev to the controls
of v4l2_device.
Accessing Control Values
========================
The v4l2_ctrl struct contains these two unions:
/* The current control value. */
union {
s32 val;
s64 val64;
char *string;
} cur;
/* The new control value. */
union {
s32 val;
s64 val64;
char *string;
};
Within the control ops you can freely use these. The val and val64 speak for
themselves. The string pointers point to character buffers of length
ctrl->maximum + 1, and are always 0-terminated.
In most cases 'cur' contains the current cached control value. When you create
a new control this value is made identical to the default value. After calling
v4l2_ctrl_handler_setup() this value is passed to the hardware. It is generally
a good idea to call this function.
Whenever a new value is set that new value is automatically cached. This means
that most drivers do not need to implement the g_volatile_ctrl() op. The
exception is for controls that return a volatile register such as a signal
strength read-out that changes continuously. In that case you will need to
implement g_volatile_ctrl like this:
static int foo_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->cur.val = read_reg(0x123);
break;
}
}
The 'new value' union is not used in g_volatile_ctrl. In general controls
that need to implement g_volatile_ctrl are read-only controls.
To mark a control as volatile you have to set the is_volatile flag:
ctrl = v4l2_ctrl_new_std(&sd->ctrl_handler, ...);
if (ctrl)
ctrl->is_volatile = 1;
For try/s_ctrl the new values (i.e. as passed by the user) are filled in and
you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union
contains the current value, which you can use (but not change!) as well.
If s_ctrl returns 0 (OK), then the control framework will copy the new final
values to the 'cur' union.
While in g_volatile/s/try_ctrl you can access the value of all controls owned
by the same handler since the handler's lock is held. If you need to access
the value of controls owned by other handlers, then you have to be very careful
not to introduce deadlocks.
Outside of the control ops you have to go through to helper functions to get
or set a single control value safely in your driver:
s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
These functions go through the control framework just as VIDIOC_G/S_CTRL ioctls
do. Don't use these inside the control ops g_volatile/s/try_ctrl, though, that
will result in a deadlock since these helpers lock the handler as well.
You can also take the handler lock yourself:
mutex_lock(&state->ctrl_handler.lock);
printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string);
printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val);
mutex_unlock(&state->ctrl_handler.lock);
Menu Controls
=============
The v4l2_ctrl struct contains this union:
union {
u32 step;
u32 menu_skip_mask;
};
For menu controls menu_skip_mask is used. What it does is that it allows you
to easily exclude certain menu items. This is used in the VIDIOC_QUERYMENU
implementation where you can return -EINVAL if a certain menu item is not
present. Note that VIDIOC_QUERYCTRL always returns a step value of 1 for
menu controls.
A good example is the MPEG Audio Layer II Bitrate menu control where the
menu is a list of standardized possible bitrates. But in practice hardware
implementations will only support a subset of those. By setting the skip
mask you can tell the framework which menu items should be skipped. Setting
it to 0 means that all menu items are supported.
You set this mask either through the v4l2_ctrl_config struct for a custom
control, or by calling v4l2_ctrl_new_std_menu().
Custom Controls
===============
Driver specific controls can be created using v4l2_ctrl_new_custom():
static const struct v4l2_ctrl_config ctrl_filter = {
.ops = &ctrl_custom_ops,
.id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
.name = "Spatial Filter",
.type = V4L2_CTRL_TYPE_INTEGER,
.flags = V4L2_CTRL_FLAG_SLIDER,
.max = 15,
.step = 1,
};
ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_filter, NULL);
The last argument is the priv pointer which can be set to driver-specific
private data.
The v4l2_ctrl_config struct also has fields to set the is_private and is_volatile
flags.
If the name field is not set, then the framework will assume this is a standard
control and will fill in the name, type and flags fields accordingly.
Active and Grabbed Controls
===========================
If you get more complex relationships between controls, then you may have to
activate and deactivate controls. For example, if the Chroma AGC control is
on, then the Chroma Gain control is inactive. That is, you may set it, but
the value will not be used by the hardware as long as the automatic gain
control is on. Typically user interfaces can disable such input fields.
You can set the 'active' status using v4l2_ctrl_activate(). By default all
controls are active. Note that the framework does not check for this flag.
It is meant purely for GUIs. The function is typically called from within
s_ctrl.
The other flag is the 'grabbed' flag. A grabbed control means that you cannot
change it because it is in use by some resource. Typical examples are MPEG
bitrate controls that cannot be changed while capturing is in progress.
If a control is set to 'grabbed' using v4l2_ctrl_grab(), then the framework
will return -EBUSY if an attempt is made to set this control. The
v4l2_ctrl_grab() function is typically called from the driver when it
starts or stops streaming.
Control Clusters
================
By default all controls are independent from the others. But in more
complex scenarios you can get dependencies from one control to another.
In that case you need to 'cluster' them:
struct foo {
struct v4l2_ctrl_handler ctrl_handler;
#define AUDIO_CL_VOLUME (0)
#define AUDIO_CL_MUTE (1)
struct v4l2_ctrl *audio_cluster[2];
...
};
state->audio_cluster[AUDIO_CL_VOLUME] =
v4l2_ctrl_new_std(&state->ctrl_handler, ...);
state->audio_cluster[AUDIO_CL_MUTE] =
v4l2_ctrl_new_std(&state->ctrl_handler, ...);
v4l2_ctrl_cluster(ARRAY_SIZE(state->audio_cluster), state->audio_cluster);
From now on whenever one or more of the controls belonging to the same
cluster is set (or 'gotten', or 'tried'), only the control ops of the first
control ('volume' in this example) is called. You effectively create a new
composite control. Similar to how a 'struct' works in C.
So when s_ctrl is called with V4L2_CID_AUDIO_VOLUME as argument, you should set
all two controls belonging to the audio_cluster:
static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME: {
struct v4l2_ctrl *mute = ctrl->cluster[AUDIO_CL_MUTE];
write_reg(0x123, mute->val ? 0 : ctrl->val);
break;
}
case V4L2_CID_CONTRAST:
write_reg(0x456, ctrl->val);
break;
}
return 0;
}
In the example above the following are equivalent for the VOLUME case:
ctrl == ctrl->cluster[AUDIO_CL_VOLUME] == state->audio_cluster[AUDIO_CL_VOLUME]
ctrl->cluster[AUDIO_CL_MUTE] == state->audio_cluster[AUDIO_CL_MUTE]
Note that controls in a cluster may be NULL. For example, if for some
reason mute was never added (because the hardware doesn't support that
particular feature), then mute will be NULL. So in that case we have a
cluster of 2 controls, of which only 1 is actually instantiated. The
only restriction is that the first control of the cluster must always be
present, since that is the 'master' control of the cluster. The master
control is the one that identifies the cluster and that provides the
pointer to the v4l2_ctrl_ops struct that is used for that cluster.
Obviously, all controls in the cluster array must be initialized to either
a valid control or to NULL.
VIDIOC_LOG_STATUS Support
=========================
This ioctl allow you to dump the current status of a driver to the kernel log.
The v4l2_ctrl_handler_log_status(ctrl_handler, prefix) can be used to dump the
value of the controls owned by the given handler to the log. You can supply a
prefix as well. If the prefix didn't end with a space, then ': ' will be added
for you.
Different Handlers for Different Video Nodes
============================================
Usually the V4L2 driver has just one control handler that is global for
all video nodes. But you can also specify different control handlers for
different video nodes. You can do that by manually setting the ctrl_handler
field of struct video_device.
That is no problem if there are no subdevs involved but if there are, then
you need to block the automatic merging of subdev controls to the global
control handler. You do that by simply setting the ctrl_handler field in
struct v4l2_device to NULL. Now v4l2_device_register_subdev() will no longer
merge subdev controls.
After each subdev was added, you will then have to call v4l2_ctrl_add_handler
manually to add the subdev's control handler (sd->ctrl_handler) to the desired
control handler. This control handler may be specific to the video_device or
for a subset of video_device's. For example: the radio device nodes only have
audio controls, while the video and vbi device nodes share the same control
handler for the audio and video controls.
If you want to have one handler (e.g. for a radio device node) have a subset
of another handler (e.g. for a video device node), then you should first add
the controls to the first handler, add the other controls to the second
handler and finally add the first handler to the second. For example:
v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...);
v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler);
Or you can add specific controls to a handler:
volume = v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_AUDIO_VOLUME, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_BRIGHTNESS, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_CONTRAST, ...);
v4l2_ctrl_add_ctrl(&radio_ctrl_handler, volume);
What you should not do is make two identical controls for two handlers.
For example:
v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_AUDIO_MUTE, ...);
This would be bad since muting the radio would not change the video mute
control. The rule is to have one control for each hardware 'knob' that you
can twiddle.
Finding Controls
================
Normally you have created the controls yourself and you can store the struct
v4l2_ctrl pointer into your own struct.
But sometimes you need to find a control from another handler that you do
not own. For example, if you have to find a volume control from a subdev.
You can do that by calling v4l2_ctrl_find:
struct v4l2_ctrl *volume;
volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME);
Since v4l2_ctrl_find will lock the handler you have to be careful where you
use it. For example, this is not a good idea:
struct v4l2_ctrl_handler ctrl_handler;
v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
...and in video_ops.s_ctrl:
case V4L2_CID_BRIGHTNESS:
contrast = v4l2_find_ctrl(&ctrl_handler, V4L2_CID_CONTRAST);
...
When s_ctrl is called by the framework the ctrl_handler.lock is already taken, so
attempting to find another control from the same handler will deadlock.
It is recommended not to use this function from inside the control ops.
Inheriting Controls
===================
When one control handler is added to another using v4l2_ctrl_add_handler, then
by default all controls from one are merged to the other. But a subdev might
have low-level controls that make sense for some advanced embedded system, but
not when it is used in consumer-level hardware. In that case you want to keep
those low-level controls local to the subdev. You can do this by simply
setting the 'is_private' flag of the control to 1:
static const struct v4l2_ctrl_config ctrl_private = {
.ops = &ctrl_custom_ops,
.id = V4L2_CID_...,
.name = "Some Private Control",
.type = V4L2_CTRL_TYPE_INTEGER,
.max = 15,
.step = 1,
.is_private = 1,
};
ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_private, NULL);
These controls will now be skipped when v4l2_ctrl_add_handler is called.
V4L2_CTRL_TYPE_CTRL_CLASS Controls
==================================
Controls of this type can be used by GUIs to get the name of the control class.
A fully featured GUI can make a dialog with multiple tabs with each tab
containing the controls belonging to a particular control class. The name of
each tab can be found by querying a special control with ID <control class | 1>.
Drivers do not have to care about this. The framework will automatically add
a control of this type whenever the first control belonging to a new control
class is added.
Differences from the Spec
=========================
There are a few places where the framework acts slightly differently from the
V4L2 Specification. Those differences are described in this section. We will
have to see whether we need to adjust the spec or not.
1) It is no longer required to have all controls contained in a
v4l2_ext_control array be from the same control class. The framework will be
able to handle any type of control in the array. You need to set ctrl_class
to 0 in order to enable this. If ctrl_class is non-zero, then it will still
check that all controls belong to that control class.
If you set ctrl_class to 0 and count to 0, then it will only return an error
if there are no controls at all.
2) Clarified the way error_idx works. For get and set it will be equal to
count if nothing was done yet. If it is less than count then only the controls
up to error_idx-1 were successfully applied.
3) When attempting to read a button control the framework will return -EACCES
instead of -EINVAL as stated in the spec. It seems to make more sense since
button controls are write-only controls.
4) Attempting to write to a read-only control will return -EACCES instead of
-EINVAL as the spec says.
5) The spec does not mention what should happen when you try to set/get a
control class controls. ivtv currently returns -EINVAL (indicating that the
control ID does not exist) while the framework will return -EACCES, which
makes more sense.
Proposals for Extensions
========================
Some ideas for future extensions to the spec:
1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimal instead of
decimal. Useful for e.g. video_mute_yuv.
2) It is possible to mark in the controls array which controls have been
successfully written and which failed by for example adding a bit to the
control ID. Not sure if it is worth the effort, though.
3) Trying to set volatile inactive controls should result in -EACCESS.
4) Add a new flag to mark volatile controls. Any application that wants
to store the state of the controls can then skip volatile inactive controls.
Currently it is not possible to detect such controls.

View File

@ -2203,6 +2203,12 @@ F: drivers/misc/cb710/
F: drivers/mmc/host/cb710-mmc.*
F: include/linux/cb710.h
ENE KB2426 (ENE0100/ENE020XX) INFRARED RECEIVER
M: Maxim Levitsky <maximlevitsky@gmail.com>
S: Maintained
F: drivers/media/IR/ene_ir.c
F: drivers/media/IR/ene_ir.h
EPSON 1355 FRAMEBUFFER DRIVER
M: Christopher Hoover <ch@murgatroid.com>
M: Christopher Hoover <ch@hpl.hp.com>

View File

@ -1,8 +1,10 @@
config IR_CORE
tristate
menuconfig IR_CORE
tristate "Infrared remote controller adapters"
depends on INPUT
default INPUT
if IR_CORE
config VIDEO_IR
tristate
depends on IR_CORE
@ -16,7 +18,7 @@ config LIRC
Enable this option to build the Linux Infrared Remote
Control (LIRC) core device interface driver. The LIRC
interface passes raw IR to and from userspace, where the
LIRC daemon handles protocol decoding for IR reception ann
LIRC daemon handles protocol decoding for IR reception and
encoding for IR transmitting (aka "blasting").
source "drivers/media/IR/keymaps/Kconfig"
@ -103,3 +105,31 @@ config IR_MCEUSB
To compile this driver as a module, choose M here: the
module will be called mceusb.
config IR_ENE
tristate "ENE eHome Receiver/Transciever (pnp id: ENE0100/ENE02xxx)"
depends on PNP
depends on IR_CORE
---help---
Say Y here to enable support for integrated infrared receiver
/transciever made by ENE.
You can see if you have it by looking at lspnp output.
Output should include ENE0100 ENE0200 or something similiar.
To compile this driver as a module, choose M here: the
module will be called ene_ir.
config IR_STREAMZAP
tristate "Streamzap PC Remote IR Receiver"
depends on USB_ARCH_HAS_HCD
depends on IR_CORE
select USB
---help---
Say Y here if you want to use a Streamzap PC Remote
Infrared Receiver.
To compile this driver as a module, choose M here: the
module will be called streamzap.
endif #IR_CORE

View File

@ -16,3 +16,5 @@ obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
# stand-alone IR receivers/transmitters
obj-$(CONFIG_IR_IMON) += imon.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
obj-$(CONFIG_IR_ENE) += ene_ir.o
obj-$(CONFIG_IR_STREAMZAP) += streamzap.o

1023
drivers/media/IR/ene_ir.c Normal file

File diff suppressed because it is too large Load Diff

235
drivers/media/IR/ene_ir.h Normal file
View File

@ -0,0 +1,235 @@
/*
* driver for ENE KB3926 B/C/D CIR (also known as ENE0XXX)
*
* Copyright (C) 2010 Maxim Levitsky <maximlevitsky@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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <linux/spinlock.h>
/* hardware address */
#define ENE_STATUS 0 /* hardware status - unused */
#define ENE_ADDR_HI 1 /* hi byte of register address */
#define ENE_ADDR_LO 2 /* low byte of register address */
#define ENE_IO 3 /* read/write window */
#define ENE_MAX_IO 4
/* 8 bytes of samples, divided in 2 halfs*/
#define ENE_SAMPLE_BUFFER 0xF8F0 /* regular sample buffer */
#define ENE_SAMPLE_SPC_MASK 0x80 /* sample is space */
#define ENE_SAMPLE_VALUE_MASK 0x7F
#define ENE_SAMPLE_OVERFLOW 0x7F
#define ENE_SAMPLES_SIZE 4
/* fan input sample buffer */
#define ENE_SAMPLE_BUFFER_FAN 0xF8FB /* this buffer holds high byte of */
/* each sample of normal buffer */
#define ENE_FAN_SMPL_PULS_MSK 0x8000 /* this bit of combined sample */
/* if set, says that sample is pulse */
#define ENE_FAN_VALUE_MASK 0x0FFF /* mask for valid bits of the value */
/* first firmware register */
#define ENE_FW1 0xF8F8
#define ENE_FW1_ENABLE 0x01 /* enable fw processing */
#define ENE_FW1_TXIRQ 0x02 /* TX interrupt pending */
#define ENE_FW1_WAKE 0x40 /* enable wake from S3 */
#define ENE_FW1_IRQ 0x80 /* enable interrupt */
/* second firmware register */
#define ENE_FW2 0xF8F9
#define ENE_FW2_BUF_HIGH 0x01 /* which half of the buffer to read */
#define ENE_FW2_IRQ_CLR 0x04 /* clear this on IRQ */
#define ENE_FW2_GP40_AS_LEARN 0x08 /* normal input is used as */
/* learning input */
#define ENE_FW2_FAN_AS_NRML_IN 0x40 /* fan is used as normal input */
#define ENE_FW2_LEARNING 0x80 /* hardware supports learning and TX */
/* transmitter ports */
#define ENE_TX_PORT2 0xFC01 /* this enables one or both */
#define ENE_TX_PORT2_EN 0x20 /* TX ports */
#define ENE_TX_PORT1 0xFC08
#define ENE_TX_PORT1_EN 0x02
/* IRQ registers block (for revision B) */
#define ENEB_IRQ 0xFD09 /* IRQ number */
#define ENEB_IRQ_UNK1 0xFD17 /* unknown setting = 1 */
#define ENEB_IRQ_STATUS 0xFD80 /* irq status */
#define ENEB_IRQ_STATUS_IR 0x20 /* IR irq */
/* fan as input settings - only if learning capable */
#define ENE_FAN_AS_IN1 0xFE30 /* fan init reg 1 */
#define ENE_FAN_AS_IN1_EN 0xCD
#define ENE_FAN_AS_IN2 0xFE31 /* fan init reg 2 */
#define ENE_FAN_AS_IN2_EN 0x03
#define ENE_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */
/* IRQ registers block (for revision C,D) */
#define ENEC_IRQ 0xFE9B /* new irq settings register */
#define ENEC_IRQ_MASK 0x0F /* irq number mask */
#define ENEC_IRQ_UNK_EN 0x10 /* always enabled */
#define ENEC_IRQ_STATUS 0x20 /* irq status and ACK */
/* CIR block settings */
#define ENE_CIR_CONF1 0xFEC0
#define ENE_CIR_CONF1_TX_CLEAR 0x01 /* clear that on revC */
/* while transmitting */
#define ENE_CIR_CONF1_RX_ON 0x07 /* normal receiver enabled */
#define ENE_CIR_CONF1_LEARN1 0x08 /* enabled on learning mode */
#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */
#define ENE_CIR_CONF1_TX_CARR 0x80 /* send TX carrier or not */
#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */
#define ENE_CIR_CONF2_LEARN2 0x10 /* set on enable learning */
#define ENE_CIR_CONF2_GPIO40DIS 0x20 /* disable input via gpio40 */
#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */
#define ENE_CIR_SAMPLE_OVERFLOW 0x80 /* interrupt on overflows if set */
/* Two byte tx buffer */
#define ENE_TX_INPUT1 0xFEC9
#define ENE_TX_INPUT2 0xFECA
#define ENE_TX_PULSE_MASK 0x80 /* Transmitted sample is pulse */
#define ENE_TX_SMLP_MASK 0x7F
#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period - fixed */
/* Unknown TX setting - TX sample period ??? */
#define ENE_TX_UNK1 0xFECB /* set to 0x63 */
/* Current received carrier period */
#define ENE_RX_CARRIER 0xFECC /* RX period (500 ns) */
#define ENE_RX_CARRIER_VALID 0x80 /* Register content valid */
/* TX period (1/carrier) */
#define ENE_TX_PERIOD 0xFECE /* TX period (500 ns) */
#define ENE_TX_PERIOD_UNKBIT 0x80 /* This bit set on transmit*/
#define ENE_TX_PERIOD_PULSE 0xFECF /* TX pulse period (500 ns)*/
/* Hardware versions */
#define ENE_HW_VERSION 0xFF00 /* hardware revision */
#define ENE_PLLFRH 0xFF16
#define ENE_PLLFRL 0xFF17
#define ENE_HW_UNK 0xFF1D
#define ENE_HW_UNK_CLR 0x04
#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */
#define ENE_HW_VER_MINOR 0xFF1F
#define ENE_HW_VER_OLD 0xFD00
/* Normal/Learning carrier ranges - only valid if we have learning input*/
/* TODO: test */
#define ENE_NORMAL_RX_LOW 34
#define ENE_NORMAL_RX_HI 38
/* Tx carrier range */
/* Hardware might be able to do more, but this range is enough for
all purposes */
#define ENE_TX_PERIOD_MAX 32 /* corresponds to 29.4 kHz */
#define ENE_TX_PERIOD_MIN 16 /* corrsponds to 62.5 kHz */
/* Minimal and maximal gaps */
/* Normal case:
Minimal gap is 0x7F * sample period
Maximum gap depends on hardware.
For KB3926B, it is unlimited, for newer models its around
250000, after which HW stops sending samples, and that is
not possible to change */
/* Fan case:
Both minimal and maximal gaps are same, and equal to 0xFFF * 0x61
And there is nothing to change this setting
*/
#define ENE_MAXGAP 250000
#define ENE_MINGAP (127 * sample_period)
/******************************************************************************/
#define ENE_DRIVER_NAME "ene_ir"
#define ENE_IRQ_RX 1
#define ENE_IRQ_TX 2
#define ENE_HW_B 1 /* 3926B */
#define ENE_HW_C 2 /* 3926C */
#define ENE_HW_D 3 /* 3926D */
#define ene_printk(level, text, ...) \
printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
#define ene_dbg(text, ...) \
if (debug) \
printk(KERN_DEBUG \
ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
#define ene_dbg_verbose(text, ...) \
if (debug > 1) \
printk(KERN_DEBUG \
ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
struct ene_device {
struct pnp_dev *pnp_dev;
struct input_dev *idev;
struct ir_dev_props *props;
int in_use;
/* hw IO settings */
unsigned long hw_io;
int irq;
spinlock_t hw_lock;
/* HW features */
int hw_revision; /* hardware revision */
bool hw_learning_and_tx_capable; /* learning capable */
bool hw_gpio40_learning; /* gpio40 is learning */
bool hw_fan_as_normal_input; /* fan input is used as */
/* regular input */
/* HW state*/
int rx_pointer; /* hw pointer to rx buffer */
bool rx_fan_input_inuse; /* is fan input in use for rx*/
int tx_reg; /* current reg used for TX */
u8 saved_conf1; /* saved FEC0 reg */
/* TX sample handling */
unsigned int tx_sample; /* current sample for TX */
bool tx_sample_pulse; /* current sample is pulse */
/* TX buffer */
int *tx_buffer; /* input samples buffer*/
int tx_pos; /* position in that bufer */
int tx_len; /* current len of tx buffer */
int tx_done; /* done transmitting */
/* one more sample pending*/
struct completion tx_complete; /* TX completion */
struct timer_list tx_sim_timer;
/* TX settings */
int tx_period;
int tx_duty_cycle;
int transmitter_mask;
/* RX settings */
bool learning_enabled; /* learning input enabled */
bool carrier_detect_enabled; /* carrier detect enabled */
int rx_period_adjust;
};

View File

@ -87,7 +87,6 @@ static ssize_t lcd_write(struct file *file, const char *buf,
struct imon_context {
struct device *dev;
struct ir_dev_props *props;
struct ir_input_dev *ir;
/* Newer devices have two interfaces */
struct usb_device *usbdev_intf0;
struct usb_device *usbdev_intf1;
@ -1656,7 +1655,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
{
struct input_dev *idev;
struct ir_dev_props *props;
struct ir_input_dev *ir;
int ret, i;
idev = input_allocate_device();
@ -1671,12 +1669,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
goto props_alloc_failed;
}
ir = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL);
if (!ir) {
dev_err(ictx->dev, "remote ir input dev allocation failed\n");
goto ir_dev_alloc_failed;
}
snprintf(ictx->name_idev, sizeof(ictx->name_idev),
"iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
idev->name = ictx->name_idev;
@ -1706,14 +1698,9 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
props->change_protocol = imon_ir_change_protocol;
ictx->props = props;
ictx->ir = ir;
memcpy(&ir->dev, ictx->dev, sizeof(struct device));
usb_to_input_id(ictx->usbdev_intf0, &idev->id);
idev->dev.parent = ictx->dev;
input_set_drvdata(idev, ir);
ret = ir_input_register(idev, RC_MAP_IMON_PAD, props, MOD_NAME);
if (ret < 0) {
dev_err(ictx->dev, "remote input dev register failed\n");
@ -1723,8 +1710,6 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
return idev;
idev_register_failed:
kfree(ir);
ir_dev_alloc_failed:
kfree(props);
props_alloc_failed:
input_free_device(idev);
@ -1944,7 +1929,6 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
urb_submit_failed:
ir_input_unregister(ictx->idev);
input_free_device(ictx->idev);
idev_setup_failed:
find_endpoint_failed:
mutex_unlock(&ictx->lock);
@ -2014,10 +1998,8 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
return ictx;
urb_submit_failed:
if (ictx->touch) {
if (ictx->touch)
input_unregister_device(ictx->touch);
input_free_device(ictx->touch);
}
touch_setup_failed:
find_endpoint_failed:
mutex_unlock(&ictx->lock);

View File

@ -32,7 +32,7 @@ struct ir_raw_handler {
struct ir_raw_event_ctrl {
struct list_head list; /* to keep track of raw clients */
struct work_struct rx_work; /* for the rx decoding workqueue */
struct task_struct *thread;
struct kfifo kfifo; /* fifo for the pulse/space durations */
ktime_t last_event; /* when last event occurred */
enum raw_event_type last_type; /* last event type */
@ -41,10 +41,13 @@ struct ir_raw_event_ctrl {
/* raw decoder state follows */
struct ir_raw_event prev_ev;
struct ir_raw_event this_ev;
struct nec_dec {
int state;
unsigned count;
u32 bits;
bool is_nec_x;
bool necx_repeat;
} nec;
struct rc5_dec {
int state;
@ -76,7 +79,7 @@ struct ir_raw_event_ctrl {
struct lirc_codec {
struct ir_input_dev *ir_dev;
struct lirc_driver *drv;
int lircdata;
int carrier_low;
} lirc;
};
@ -104,10 +107,9 @@ static inline void decrease_duration(struct ir_raw_event *ev, unsigned duration)
ev->duration -= duration;
}
#define TO_US(duration) (((duration) + 500) / 1000)
#define TO_US(duration) DIV_ROUND_CLOSEST((duration), 1000)
#define TO_STR(is_pulse) ((is_pulse) ? "pulse" : "space")
#define IS_RESET(ev) (ev.duration == 0)
/*
* Routines from ir-sysfs.c - Meant to be called only internally inside
* ir-core
@ -126,7 +128,8 @@ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler);
void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler);
void ir_raw_init(void);
int ir_rcmap_init(void);
void ir_rcmap_cleanup(void);
/*
* Decoder initialization code
*

View File

@ -32,6 +32,7 @@ enum jvc_state {
STATE_BIT_SPACE,
STATE_TRAILER_PULSE,
STATE_TRAILER_SPACE,
STATE_CHECK_REPEAT,
};
/**
@ -60,6 +61,7 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
IR_dprintk(2, "JVC decode started at state %d (%uus %s)\n",
data->state, TO_US(ev.duration), TO_STR(ev.pulse));
again:
switch (data->state) {
case STATE_INACTIVE:
@ -149,8 +151,18 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
}
data->count = 0;
data->state = STATE_BIT_PULSE;
data->state = STATE_CHECK_REPEAT;
return 0;
case STATE_CHECK_REPEAT:
if (!ev.pulse)
break;
if (eq_margin(ev.duration, JVC_HEADER_PULSE, JVC_UNIT / 2))
data->state = STATE_INACTIVE;
else
data->state = STATE_BIT_PULSE;
goto again;
}
out:

View File

@ -339,6 +339,8 @@ void ir_repeat(struct input_dev *dev)
spin_lock_irqsave(&ir->keylock, flags);
input_event(dev, EV_MSC, MSC_SCAN, ir->last_scancode);
if (!ir->keypressed)
goto out;
@ -370,6 +372,8 @@ void ir_keydown(struct input_dev *dev, int scancode, u8 toggle)
spin_lock_irqsave(&ir->keylock, flags);
input_event(dev, EV_MSC, MSC_SCAN, scancode);
/* Repeat event? */
if (ir->keypressed &&
ir->last_scancode == scancode &&
@ -383,9 +387,11 @@ void ir_keydown(struct input_dev *dev, int scancode, u8 toggle)
ir->last_toggle = toggle;
ir->last_keycode = keycode;
if (keycode == KEY_RESERVED)
goto out;
/* Register a keypress */
ir->keypressed = true;
IR_dprintk(1, "%s: key down event, key 0x%04x, scancode 0x%04x\n",
@ -428,7 +434,7 @@ static void ir_close(struct input_dev *input_dev)
*/
int __ir_input_register(struct input_dev *input_dev,
const struct ir_scancode_table *rc_tab,
const struct ir_dev_props *props,
struct ir_dev_props *props,
const char *driver_name)
{
struct ir_input_dev *ir_dev;
@ -480,6 +486,8 @@ int __ir_input_register(struct input_dev *input_dev,
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_REP, input_dev->evbit);
set_bit(EV_MSC, input_dev->evbit);
set_bit(MSC_SCAN, input_dev->mscbit);
if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) {
rc = -ENOMEM;
@ -499,7 +507,8 @@ int __ir_input_register(struct input_dev *input_dev,
IR_dprintk(1, "Registered input device on %s for %s remote%s.\n",
driver_name, rc_tab->name,
ir_dev->props->driver_type == RC_DRIVER_IR_RAW ? " in raw mode" : "");
(ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_IR_RAW) ?
" in raw mode" : "");
return 0;

View File

@ -32,6 +32,7 @@
static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
{
struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
int sample;
if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC))
return 0;
@ -39,18 +40,20 @@ static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
if (!ir_dev->raw->lirc.drv || !ir_dev->raw->lirc.drv->rbuf)
return -EINVAL;
if (IS_RESET(ev))
return 0;
IR_dprintk(2, "LIRC data transfer started (%uus %s)\n",
TO_US(ev.duration), TO_STR(ev.pulse));
ir_dev->raw->lirc.lircdata += ev.duration / 1000;
sample = ev.duration / 1000;
if (ev.pulse)
ir_dev->raw->lirc.lircdata |= PULSE_BIT;
sample |= PULSE_BIT;
lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf,
(unsigned char *) &ir_dev->raw->lirc.lircdata);
(unsigned char *) &sample);
wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll);
ir_dev->raw->lirc.lircdata = 0;
return 0;
}
@ -92,13 +95,14 @@ out:
return ret;
}
static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
unsigned long __user arg)
{
struct lirc_codec *lirc;
struct ir_input_dev *ir_dev;
int ret = 0;
void *drv_data;
unsigned long val;
unsigned long val = 0;
lirc = lirc_get_pdata(filep);
if (!lirc)
@ -110,47 +114,106 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long ar
drv_data = ir_dev->props->priv;
switch (cmd) {
case LIRC_SET_TRANSMITTER_MASK:
if (_IOC_DIR(cmd) & _IOC_WRITE) {
ret = get_user(val, (unsigned long *)arg);
if (ret)
return ret;
}
if (ir_dev->props && ir_dev->props->s_tx_mask)
switch (cmd) {
/* legacy support */
case LIRC_GET_SEND_MODE:
val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
break;
case LIRC_SET_SEND_MODE:
if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
return -EINVAL;
break;
/* TX settings */
case LIRC_SET_TRANSMITTER_MASK:
if (ir_dev->props->s_tx_mask)
ret = ir_dev->props->s_tx_mask(drv_data, (u32)val);
else
return -EINVAL;
break;
case LIRC_SET_SEND_CARRIER:
ret = get_user(val, (unsigned long *)arg);
if (ret)
return ret;
if (ir_dev->props && ir_dev->props->s_tx_carrier)
if (ir_dev->props->s_tx_carrier)
ir_dev->props->s_tx_carrier(drv_data, (u32)val);
else
return -EINVAL;
break;
case LIRC_GET_SEND_MODE:
val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
ret = put_user(val, (unsigned long *)arg);
case LIRC_SET_SEND_DUTY_CYCLE:
if (!ir_dev->props->s_tx_duty_cycle)
return -ENOSYS;
if (val <= 0 || val >= 100)
return -EINVAL;
ir_dev->props->s_tx_duty_cycle(ir_dev->props->priv, val);
break;
case LIRC_SET_SEND_MODE:
ret = get_user(val, (unsigned long *)arg);
if (ret)
return ret;
/* RX settings */
case LIRC_SET_REC_CARRIER:
if (ir_dev->props->s_rx_carrier_range)
ret = ir_dev->props->s_rx_carrier_range(
ir_dev->props->priv,
ir_dev->raw->lirc.carrier_low, val);
else
return -ENOSYS;
if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
if (!ret)
ir_dev->raw->lirc.carrier_low = 0;
break;
case LIRC_SET_REC_CARRIER_RANGE:
if (val >= 0)
ir_dev->raw->lirc.carrier_low = val;
break;
case LIRC_GET_REC_RESOLUTION:
val = ir_dev->props->rx_resolution;
break;
case LIRC_SET_WIDEBAND_RECEIVER:
if (ir_dev->props->s_learning_mode)
return ir_dev->props->s_learning_mode(
ir_dev->props->priv, !!val);
else
return -ENOSYS;
/* Generic timeout support */
case LIRC_GET_MIN_TIMEOUT:
if (!ir_dev->props->max_timeout)
return -ENOSYS;
val = ir_dev->props->min_timeout / 1000;
break;
case LIRC_GET_MAX_TIMEOUT:
if (!ir_dev->props->max_timeout)
return -ENOSYS;
val = ir_dev->props->max_timeout / 1000;
break;
case LIRC_SET_REC_TIMEOUT:
if (val < ir_dev->props->min_timeout ||
val > ir_dev->props->max_timeout)
return -EINVAL;
ir_dev->props->timeout = val * 1000;
break;
default:
return lirc_dev_fop_ioctl(filep, cmd, arg);
}
if (_IOC_DIR(cmd) & _IOC_READ)
ret = put_user(val, (unsigned long *)arg);
return ret;
}
@ -196,13 +259,28 @@ static int ir_lirc_register(struct input_dev *input_dev)
features = LIRC_CAN_REC_MODE2;
if (ir_dev->props->tx_ir) {
features |= LIRC_CAN_SEND_PULSE;
if (ir_dev->props->s_tx_mask)
features |= LIRC_CAN_SET_TRANSMITTER_MASK;
if (ir_dev->props->s_tx_carrier)
features |= LIRC_CAN_SET_SEND_CARRIER;
if (ir_dev->props->s_tx_duty_cycle)
features |= LIRC_CAN_SET_REC_DUTY_CYCLE;
}
if (ir_dev->props->s_rx_carrier_range)
features |= LIRC_CAN_SET_REC_CARRIER |
LIRC_CAN_SET_REC_CARRIER_RANGE;
if (ir_dev->props->s_learning_mode)
features |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
if (ir_dev->props->max_timeout)
features |= LIRC_CAN_SET_REC_TIMEOUT;
snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)",
ir_dev->driver_name);
drv->minor = -1;
@ -224,8 +302,6 @@ static int ir_lirc_register(struct input_dev *input_dev)
ir_dev->raw->lirc.drv = drv;
ir_dev->raw->lirc.ir_dev = ir_dev;
ir_dev->raw->lirc.lircdata = PULSE_MASK;
return 0;
lirc_register_failed:

View File

@ -20,12 +20,13 @@
#define NEC_HEADER_PULSE (16 * NEC_UNIT)
#define NECX_HEADER_PULSE (8 * NEC_UNIT) /* Less common NEC variant */
#define NEC_HEADER_SPACE (8 * NEC_UNIT)
#define NEC_REPEAT_SPACE (8 * NEC_UNIT)
#define NEC_REPEAT_SPACE (4 * NEC_UNIT)
#define NEC_BIT_PULSE (1 * NEC_UNIT)
#define NEC_BIT_0_SPACE (1 * NEC_UNIT)
#define NEC_BIT_1_SPACE (3 * NEC_UNIT)
#define NEC_TRAILER_PULSE (1 * NEC_UNIT)
#define NEC_TRAILER_SPACE (10 * NEC_UNIT) /* even longer in reality */
#define NECX_REPEAT_BITS 1
enum nec_state {
STATE_INACTIVE,
@ -67,8 +68,12 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
if (!ev.pulse)
break;
if (!eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2) &&
!eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2)) {
data->is_nec_x = false;
data->necx_repeat = false;
} else if (eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
data->is_nec_x = true;
else
break;
data->count = 0;
@ -105,6 +110,17 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
if (ev.pulse)
break;
if (data->necx_repeat && data->count == NECX_REPEAT_BITS &&
geq_margin(ev.duration,
NEC_TRAILER_SPACE, NEC_UNIT / 2)) {
IR_dprintk(1, "Repeat last key\n");
ir_repeat(input_dev);
data->state = STATE_INACTIVE;
return 0;
} else if (data->count > NECX_REPEAT_BITS)
data->necx_repeat = false;
data->bits <<= 1;
if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2))
data->bits |= 1;
@ -159,6 +175,9 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
IR_dprintk(1, "NEC scancode 0x%04x\n", scancode);
}
if (data->is_nec_x)
data->necx_repeat = true;
ir_keydown(input_dev, scancode, 0);
data->state = STATE_INACTIVE;
return 0;

View File

@ -12,9 +12,10 @@
* GNU General Public License for more details.
*/
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/freezer.h>
#include "ir-core-priv.h"
/* Define the max number of pulse/space transitions to buffer */
@ -24,7 +25,7 @@
static LIST_HEAD(ir_raw_client_list);
/* Used to handle IR raw handler extensions */
static DEFINE_SPINLOCK(ir_raw_handler_lock);
static DEFINE_MUTEX(ir_raw_handler_lock);
static LIST_HEAD(ir_raw_handler_list);
static u64 available_protocols;
@ -33,20 +34,30 @@ static u64 available_protocols;
static struct work_struct wq_load;
#endif
static void ir_raw_event_work(struct work_struct *work)
static int ir_raw_event_thread(void *data)
{
struct ir_raw_event ev;
struct ir_raw_handler *handler;
struct ir_raw_event_ctrl *raw =
container_of(work, struct ir_raw_event_ctrl, rx_work);
struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data;
while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
spin_lock(&ir_raw_handler_lock);
list_for_each_entry(handler, &ir_raw_handler_list, list)
handler->decode(raw->input_dev, ev);
spin_unlock(&ir_raw_handler_lock);
raw->prev_ev = ev;
while (!kthread_should_stop()) {
try_to_freeze();
mutex_lock(&ir_raw_handler_lock);
while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
list_for_each_entry(handler, &ir_raw_handler_list, list)
handler->decode(raw->input_dev, ev);
raw->prev_ev = ev;
}
mutex_unlock(&ir_raw_handler_lock);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
}
return 0;
}
/**
@ -66,6 +77,9 @@ int ir_raw_event_store(struct input_dev *input_dev, struct ir_raw_event *ev)
if (!ir->raw)
return -EINVAL;
IR_dprintk(2, "sample: (05%dus %s)\n",
TO_US(ev->duration), TO_STR(ev->pulse));
if (kfifo_in(&ir->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))
return -ENOMEM;
@ -125,6 +139,90 @@ int ir_raw_event_store_edge(struct input_dev *input_dev, enum raw_event_type typ
}
EXPORT_SYMBOL_GPL(ir_raw_event_store_edge);
/**
* ir_raw_event_store_with_filter() - pass next pulse/space to decoders with some processing
* @input_dev: the struct input_dev device descriptor
* @type: the type of the event that has occurred
*
* This routine (which may be called from an interrupt context) works
* in similiar manner to ir_raw_event_store_edge.
* This routine is intended for devices with limited internal buffer
* It automerges samples of same type, and handles timeouts
*/
int ir_raw_event_store_with_filter(struct input_dev *input_dev,
struct ir_raw_event *ev)
{
struct ir_input_dev *ir = input_get_drvdata(input_dev);
struct ir_raw_event_ctrl *raw = ir->raw;
if (!raw || !ir->props)
return -EINVAL;
/* Ignore spaces in idle mode */
if (ir->idle && !ev->pulse)
return 0;
else if (ir->idle)
ir_raw_event_set_idle(input_dev, 0);
if (!raw->this_ev.duration) {
raw->this_ev = *ev;
} else if (ev->pulse == raw->this_ev.pulse) {
raw->this_ev.duration += ev->duration;
} else {
ir_raw_event_store(input_dev, &raw->this_ev);
raw->this_ev = *ev;
}
/* Enter idle mode if nessesary */
if (!ev->pulse && ir->props->timeout &&
raw->this_ev.duration >= ir->props->timeout)
ir_raw_event_set_idle(input_dev, 1);
return 0;
}
EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter);
void ir_raw_event_set_idle(struct input_dev *input_dev, int idle)
{
struct ir_input_dev *ir = input_get_drvdata(input_dev);
struct ir_raw_event_ctrl *raw = ir->raw;
ktime_t now;
u64 delta;
if (!ir->props)
return;
if (!ir->raw)
goto out;
if (idle) {
IR_dprintk(2, "enter idle mode\n");
raw->last_event = ktime_get();
} else {
IR_dprintk(2, "exit idle mode\n");
now = ktime_get();
delta = ktime_to_ns(ktime_sub(now, ir->raw->last_event));
WARN_ON(raw->this_ev.pulse);
raw->this_ev.duration =
min(raw->this_ev.duration + delta,
(u64)IR_MAX_DURATION);
ir_raw_event_store(input_dev, &raw->this_ev);
if (raw->this_ev.duration == IR_MAX_DURATION)
ir_raw_event_reset(input_dev);
raw->this_ev.duration = 0;
}
out:
if (ir->props->s_idle)
ir->props->s_idle(ir->props->priv, idle);
ir->idle = idle;
}
EXPORT_SYMBOL_GPL(ir_raw_event_set_idle);
/**
* ir_raw_event_handle() - schedules the decoding of stored ir data
* @input_dev: the struct input_dev device descriptor
@ -138,7 +236,7 @@ void ir_raw_event_handle(struct input_dev *input_dev)
if (!ir->raw)
return;
schedule_work(&ir->raw->rx_work);
wake_up_process(ir->raw->thread);
}
EXPORT_SYMBOL_GPL(ir_raw_event_handle);
@ -147,9 +245,9 @@ u64
ir_raw_get_allowed_protocols()
{
u64 protocols;
spin_lock(&ir_raw_handler_lock);
mutex_lock(&ir_raw_handler_lock);
protocols = available_protocols;
spin_unlock(&ir_raw_handler_lock);
mutex_unlock(&ir_raw_handler_lock);
return protocols;
}
@ -167,7 +265,7 @@ int ir_raw_event_register(struct input_dev *input_dev)
return -ENOMEM;
ir->raw->input_dev = input_dev;
INIT_WORK(&ir->raw->rx_work, ir_raw_event_work);
ir->raw->enabled_protocols = ~0;
rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE,
GFP_KERNEL);
@ -177,12 +275,21 @@ int ir_raw_event_register(struct input_dev *input_dev)
return rc;
}
spin_lock(&ir_raw_handler_lock);
ir->raw->thread = kthread_run(ir_raw_event_thread, ir->raw,
"rc%u", (unsigned int)ir->devno);
if (IS_ERR(ir->raw->thread)) {
kfree(ir->raw);
ir->raw = NULL;
return PTR_ERR(ir->raw->thread);
}
mutex_lock(&ir_raw_handler_lock);
list_add_tail(&ir->raw->list, &ir_raw_client_list);
list_for_each_entry(handler, &ir_raw_handler_list, list)
if (handler->raw_register)
handler->raw_register(ir->raw->input_dev);
spin_unlock(&ir_raw_handler_lock);
mutex_unlock(&ir_raw_handler_lock);
return 0;
}
@ -195,14 +302,14 @@ void ir_raw_event_unregister(struct input_dev *input_dev)
if (!ir->raw)
return;
cancel_work_sync(&ir->raw->rx_work);
kthread_stop(ir->raw->thread);
spin_lock(&ir_raw_handler_lock);
mutex_lock(&ir_raw_handler_lock);
list_del(&ir->raw->list);
list_for_each_entry(handler, &ir_raw_handler_list, list)
if (handler->raw_unregister)
handler->raw_unregister(ir->raw->input_dev);
spin_unlock(&ir_raw_handler_lock);
mutex_unlock(&ir_raw_handler_lock);
kfifo_free(&ir->raw->kfifo);
kfree(ir->raw);
@ -217,13 +324,13 @@ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler)
{
struct ir_raw_event_ctrl *raw;
spin_lock(&ir_raw_handler_lock);
mutex_lock(&ir_raw_handler_lock);
list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list);
if (ir_raw_handler->raw_register)
list_for_each_entry(raw, &ir_raw_client_list, list)
ir_raw_handler->raw_register(raw->input_dev);
available_protocols |= ir_raw_handler->protocols;
spin_unlock(&ir_raw_handler_lock);
mutex_unlock(&ir_raw_handler_lock);
return 0;
}
@ -233,13 +340,13 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
{
struct ir_raw_event_ctrl *raw;
spin_lock(&ir_raw_handler_lock);
mutex_lock(&ir_raw_handler_lock);
list_del(&ir_raw_handler->list);
if (ir_raw_handler->raw_unregister)
list_for_each_entry(raw, &ir_raw_client_list, list)
ir_raw_handler->raw_unregister(raw->input_dev);
available_protocols &= ~ir_raw_handler->protocols;
spin_unlock(&ir_raw_handler_lock);
mutex_unlock(&ir_raw_handler_lock);
}
EXPORT_SYMBOL(ir_raw_handler_unregister);

View File

@ -325,6 +325,7 @@ static int __init ir_core_init(void)
/* Initialize/load the decoders/keymap code that will be used */
ir_raw_init();
ir_rcmap_init();
return 0;
}
@ -332,6 +333,7 @@ static int __init ir_core_init(void)
static void __exit ir_core_exit(void)
{
class_unregister(&ir_input_class);
ir_rcmap_cleanup();
}
module_init(ir_core_init);

View File

@ -19,7 +19,6 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-dm1105-nec.o \
rc-dntv-live-dvb-t.o \
rc-dntv-live-dvbt-pro.o \
rc-empty.o \
rc-em-terratec.o \
rc-encore-enltv2.o \
rc-encore-enltv.o \
@ -59,6 +58,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-purpletv.o \
rc-pv951.o \
rc-rc5-hauppauge-new.o \
rc-rc5-streamzap.o \
rc-rc5-tv.o \
rc-rc6-mce.o \
rc-real-audio-220-32-keys.o \

View File

@ -1,44 +0,0 @@
/* empty.h - Keytable for empty Remote Controller
*
* keymap imported from ir-keymaps.c
*
* Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.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 <media/rc-map.h>
/* empty keytable, can be used as placeholder for not-yet created keytables */
static struct ir_scancode empty[] = {
{ 0x2a, KEY_COFFEE },
};
static struct rc_keymap empty_map = {
.map = {
.scan = empty,
.size = ARRAY_SIZE(empty),
.ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */
.name = RC_MAP_EMPTY,
}
};
static int __init init_rc_map_empty(void)
{
return ir_register_map(&empty_map);
}
static void __exit exit_rc_map_empty(void)
{
ir_unregister_map(&empty_map);
}
module_init(init_rc_map_empty)
module_exit(exit_rc_map_empty)
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");

View File

@ -0,0 +1,81 @@
/* rc-rc5-streamzap.c - Keytable for Streamzap PC Remote, for use
* with the Streamzap PC Remote IR Receiver.
*
* Copyright (c) 2010 by Jarod Wilson <jarod@redhat.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 <media/rc-map.h>
static struct ir_scancode rc5_streamzap[] = {
/*
* FIXME: The Streamzap remote isn't actually true RC-5, it has an extra
* bit in it, which presently throws the in-kernel RC-5 decoder for a loop.
* We either have to enhance the decoder to support it, add a new decoder,
* or just rely on lirc userspace decoding.
*/
{ 0x00, KEY_NUMERIC_0 },
{ 0x01, KEY_NUMERIC_1 },
{ 0x02, KEY_NUMERIC_2 },
{ 0x03, KEY_NUMERIC_3 },
{ 0x04, KEY_NUMERIC_4 },
{ 0x05, KEY_NUMERIC_5 },
{ 0x06, KEY_NUMERIC_6 },
{ 0x07, KEY_NUMERIC_7 },
{ 0x08, KEY_NUMERIC_8 },
{ 0x0a, KEY_POWER },
{ 0x0b, KEY_MUTE },
{ 0x0c, KEY_CHANNELUP },
{ 0x0d, KEY_VOLUMEUP },
{ 0x0e, KEY_CHANNELDOWN },
{ 0x0f, KEY_VOLUMEDOWN },
{ 0x10, KEY_UP },
{ 0x11, KEY_LEFT },
{ 0x12, KEY_OK },
{ 0x13, KEY_RIGHT },
{ 0x14, KEY_DOWN },
{ 0x15, KEY_MENU },
{ 0x16, KEY_EXIT },
{ 0x17, KEY_PLAY },
{ 0x18, KEY_PAUSE },
{ 0x19, KEY_STOP },
{ 0x1a, KEY_BACK },
{ 0x1b, KEY_FORWARD },
{ 0x1c, KEY_RECORD },
{ 0x1d, KEY_REWIND },
{ 0x1e, KEY_FASTFORWARD },
{ 0x20, KEY_RED },
{ 0x21, KEY_GREEN },
{ 0x22, KEY_YELLOW },
{ 0x23, KEY_BLUE },
};
static struct rc_keymap rc5_streamzap_map = {
.map = {
.scan = rc5_streamzap,
.size = ARRAY_SIZE(rc5_streamzap),
.ir_type = IR_TYPE_RC5,
.name = RC_MAP_RC5_STREAMZAP,
}
};
static int __init init_rc_map_rc5_streamzap(void)
{
return ir_register_map(&rc5_streamzap_map);
}
static void __exit exit_rc_map_rc5_streamzap(void)
{
ir_unregister_map(&rc5_streamzap_map);
}
module_init(init_rc_map_rc5_streamzap)
module_exit(exit_rc_map_rc5_streamzap)
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");

View File

@ -74,6 +74,8 @@ static struct ir_scancode rc6_mce[] = {
{ 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */
{ 0x800f044d, KEY_TITLE },
{ 0x800f044e, KEY_PRINT }, /* Print - HP OEM version of remote */
{ 0x800f040c, KEY_POWER },
{ 0x800f040d, KEY_PROG1 }, /* Windows MCE button */

View File

@ -228,7 +228,6 @@ static struct usb_device_id std_tx_mask_list[] = {
/* data structure for each usb transceiver */
struct mceusb_dev {
/* ir-core bits */
struct ir_input_dev *irdev;
struct ir_dev_props *props;
struct ir_raw_event rawir;
@ -428,7 +427,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
}
}
static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
{
struct mceusb_dev *ir;
int len;
@ -477,7 +476,7 @@ static void mce_request_packet(struct mceusb_dev *ir,
/* outbound data */
usb_fill_int_urb(async_urb, ir->usbdev,
usb_sndintpipe(ir->usbdev, ep->bEndpointAddress),
async_buf, size, (usb_complete_t) usb_async_callback,
async_buf, size, (usb_complete_t)mce_async_callback,
ir, ep->bInterval);
memcpy(async_buf, data, size);
@ -739,7 +738,7 @@ static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
ir->send_flags = SEND_FLAG_COMPLETE;
dev_dbg(&ir->irdev->dev, "setup answer received %d bytes\n",
dev_dbg(ir->dev, "setup answer received %d bytes\n",
buf_len);
}
@ -861,7 +860,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
{
struct input_dev *idev;
struct ir_dev_props *props;
struct ir_input_dev *irdev;
struct device *dev = ir->dev;
int ret = -ENODEV;
@ -878,12 +876,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
goto props_alloc_failed;
}
irdev = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL);
if (!irdev) {
dev_err(dev, "remote ir input dev allocation failed\n");
goto ir_dev_alloc_failed;
}
snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome "
"Infrared Remote Transceiver (%04x:%04x)",
le16_to_cpu(ir->usbdev->descriptor.idVendor),
@ -902,9 +894,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
props->tx_ir = mceusb_tx_ir;
ir->props = props;
ir->irdev = irdev;
input_set_drvdata(idev, irdev);
ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME);
if (ret < 0) {
@ -915,8 +904,6 @@ static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
return idev;
irdev_failed:
kfree(irdev);
ir_dev_alloc_failed:
kfree(props);
props_alloc_failed:
input_free_device(idev);
@ -932,7 +919,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
struct usb_endpoint_descriptor *ep = NULL;
struct usb_endpoint_descriptor *ep_in = NULL;
struct usb_endpoint_descriptor *ep_out = NULL;
struct usb_host_config *config;
struct mceusb_dev *ir = NULL;
int pipe, maxp, i;
char buf[63], name[128] = "";
@ -942,7 +928,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, ": %s called\n", __func__);
config = dev->actconfig;
idesc = intf->cur_altsetting;
is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0;

View File

@ -82,3 +82,26 @@ void ir_unregister_map(struct rc_keymap *map)
}
EXPORT_SYMBOL_GPL(ir_unregister_map);
static struct ir_scancode empty[] = {
{ 0x2a, KEY_COFFEE },
};
static struct rc_keymap empty_map = {
.map = {
.scan = empty,
.size = ARRAY_SIZE(empty),
.ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */
.name = RC_MAP_EMPTY,
}
};
int ir_rcmap_init(void)
{
return ir_register_map(&empty_map);
}
void ir_rcmap_cleanup(void)
{
ir_unregister_map(&empty_map);
}

View File

@ -0,0 +1,741 @@
/*
* Streamzap Remote Control driver
*
* Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
* Copyright (c) 2010 Jarod Wilson <jarod@wilsonet.com>
*
* This driver was based on the work of Greg Wickham and Adrian
* Dewhurst. It was substantially rewritten to support correct signal
* gaps and now maintains a delay buffer, which is used to present
* consistent timing behaviour to user space applications. Without the
* delay buffer an ugly hack would be required in lircd, which can
* cause sluggish signal decoding in certain situations.
*
* Ported to in-kernel ir-core interface by Jarod Wilson
*
* This driver is based on the USB skeleton driver packaged with the
* kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/input.h>
#include <media/ir-core.h>
#define DRIVER_VERSION "1.60"
#define DRIVER_NAME "streamzap"
#define DRIVER_DESC "Streamzap Remote Control driver"
#ifdef CONFIG_USB_DEBUG
static int debug = 1;
#else
static int debug;
#endif
#define USB_STREAMZAP_VENDOR_ID 0x0e9c
#define USB_STREAMZAP_PRODUCT_ID 0x0000
/* table of devices that work with this driver */
static struct usb_device_id streamzap_table[] = {
/* Streamzap Remote Control */
{ USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
/* Terminating entry */
{ }
};
MODULE_DEVICE_TABLE(usb, streamzap_table);
#define STREAMZAP_PULSE_MASK 0xf0
#define STREAMZAP_SPACE_MASK 0x0f
#define STREAMZAP_TIMEOUT 0xff
#define STREAMZAP_RESOLUTION 256
/* number of samples buffered */
#define SZ_BUF_LEN 128
enum StreamzapDecoderState {
PulseSpace,
FullPulse,
FullSpace,
IgnorePulse
};
/* structure to hold our device specific stuff */
struct streamzap_ir {
/* ir-core */
struct ir_dev_props *props;
struct ir_raw_event rawir;
/* core device info */
struct device *dev;
struct input_dev *idev;
/* usb */
struct usb_device *usbdev;
struct usb_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct urb *urb_in;
/* buffer & dma */
unsigned char *buf_in;
dma_addr_t dma_in;
unsigned int buf_in_len;
/* timer used to support delay buffering */
struct timer_list delay_timer;
bool timer_running;
spinlock_t timer_lock;
struct timer_list flush_timer;
bool flush;
/* delay buffer */
struct kfifo fifo;
bool fifo_initialized;
/* track what state we're in */
enum StreamzapDecoderState decoder_state;
/* tracks whether we are currently receiving some signal */
bool idle;
/* sum of signal lengths received since signal start */
unsigned long sum;
/* start time of signal; necessary for gap tracking */
struct timeval signal_last;
struct timeval signal_start;
/* bool timeout_enabled; */
char name[128];
char phys[64];
};
/* local function prototypes */
static int streamzap_probe(struct usb_interface *interface,
const struct usb_device_id *id);
static void streamzap_disconnect(struct usb_interface *interface);
static void streamzap_callback(struct urb *urb);
static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
static int streamzap_resume(struct usb_interface *intf);
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver streamzap_driver = {
.name = DRIVER_NAME,
.probe = streamzap_probe,
.disconnect = streamzap_disconnect,
.suspend = streamzap_suspend,
.resume = streamzap_resume,
.id_table = streamzap_table,
};
static void streamzap_stop_timer(struct streamzap_ir *sz)
{
unsigned long flags;
spin_lock_irqsave(&sz->timer_lock, flags);
if (sz->timer_running) {
sz->timer_running = false;
spin_unlock_irqrestore(&sz->timer_lock, flags);
del_timer_sync(&sz->delay_timer);
} else {
spin_unlock_irqrestore(&sz->timer_lock, flags);
}
}
static void streamzap_flush_timeout(unsigned long arg)
{
struct streamzap_ir *sz = (struct streamzap_ir *)arg;
dev_info(sz->dev, "%s: callback firing\n", __func__);
/* finally start accepting data */
sz->flush = false;
}
static void streamzap_delay_timeout(unsigned long arg)
{
struct streamzap_ir *sz = (struct streamzap_ir *)arg;
struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
unsigned long flags;
int len, ret;
static unsigned long delay;
bool wake = false;
/* deliver data every 10 ms */
delay = msecs_to_jiffies(10);
spin_lock_irqsave(&sz->timer_lock, flags);
if (kfifo_len(&sz->fifo) > 0) {
ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
if (ret != sizeof(rawir))
dev_err(sz->dev, "Problem w/kfifo_out...\n");
ir_raw_event_store(sz->idev, &rawir);
wake = true;
}
len = kfifo_len(&sz->fifo);
if (len > 0) {
while ((len < SZ_BUF_LEN / 2) &&
(len < SZ_BUF_LEN * sizeof(int))) {
ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
if (ret != sizeof(rawir))
dev_err(sz->dev, "Problem w/kfifo_out...\n");
ir_raw_event_store(sz->idev, &rawir);
wake = true;
len = kfifo_len(&sz->fifo);
}
if (sz->timer_running)
mod_timer(&sz->delay_timer, jiffies + delay);
} else {
sz->timer_running = false;
}
if (wake)
ir_raw_event_handle(sz->idev);
spin_unlock_irqrestore(&sz->timer_lock, flags);
}
static void streamzap_flush_delay_buffer(struct streamzap_ir *sz)
{
struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
bool wake = false;
int ret;
while (kfifo_len(&sz->fifo) > 0) {
ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
if (ret != sizeof(rawir))
dev_err(sz->dev, "Problem w/kfifo_out...\n");
ir_raw_event_store(sz->idev, &rawir);
wake = true;
}
if (wake)
ir_raw_event_handle(sz->idev);
}
static void sz_push(struct streamzap_ir *sz)
{
struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
unsigned long flags;
int ret;
spin_lock_irqsave(&sz->timer_lock, flags);
if (kfifo_len(&sz->fifo) >= sizeof(int) * SZ_BUF_LEN) {
ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
if (ret != sizeof(rawir))
dev_err(sz->dev, "Problem w/kfifo_out...\n");
ir_raw_event_store(sz->idev, &rawir);
}
kfifo_in(&sz->fifo, &sz->rawir, sizeof(rawir));
if (!sz->timer_running) {
sz->delay_timer.expires = jiffies + (HZ / 10);
add_timer(&sz->delay_timer);
sz->timer_running = true;
}
spin_unlock_irqrestore(&sz->timer_lock, flags);
}
static void sz_push_full_pulse(struct streamzap_ir *sz,
unsigned char value)
{
if (sz->idle) {
long deltv;
sz->signal_last = sz->signal_start;
do_gettimeofday(&sz->signal_start);
deltv = sz->signal_start.tv_sec - sz->signal_last.tv_sec;
sz->rawir.pulse = false;
if (deltv > 15) {
/* really long time */
sz->rawir.duration = IR_MAX_DURATION;
} else {
sz->rawir.duration = (int)(deltv * 1000000 +
sz->signal_start.tv_usec -
sz->signal_last.tv_usec);
sz->rawir.duration -= sz->sum;
sz->rawir.duration *= 1000;
sz->rawir.duration &= IR_MAX_DURATION;
}
dev_dbg(sz->dev, "ls %u\n", sz->rawir.duration);
sz_push(sz);
sz->idle = 0;
sz->sum = 0;
}
sz->rawir.pulse = true;
sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
sz->sum += sz->rawir.duration;
sz->rawir.duration *= 1000;
sz->rawir.duration &= IR_MAX_DURATION;
dev_dbg(sz->dev, "p %u\n", sz->rawir.duration);
sz_push(sz);
}
static void sz_push_half_pulse(struct streamzap_ir *sz,
unsigned char value)
{
sz_push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK) >> 4);
}
static void sz_push_full_space(struct streamzap_ir *sz,
unsigned char value)
{
sz->rawir.pulse = false;
sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
sz->sum += sz->rawir.duration;
sz->rawir.duration *= 1000;
dev_dbg(sz->dev, "s %u\n", sz->rawir.duration);
sz_push(sz);
}
static void sz_push_half_space(struct streamzap_ir *sz,
unsigned long value)
{
sz_push_full_space(sz, value & STREAMZAP_SPACE_MASK);
}
/**
* streamzap_callback - usb IRQ handler callback
*
* This procedure is invoked on reception of data from
* the usb remote.
*/
static void streamzap_callback(struct urb *urb)
{
struct streamzap_ir *sz;
unsigned int i;
int len;
#if 0
static int timeout = (((STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION) &
IR_MAX_DURATION) | 0x03000000);
#endif
if (!urb)
return;
sz = urb->context;
len = urb->actual_length;
switch (urb->status) {
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/*
* this urb is terminated, clean up.
* sz might already be invalid at this point
*/
dev_err(sz->dev, "urb terminated, status: %d\n", urb->status);
return;
default:
break;
}
dev_dbg(sz->dev, "%s: received urb, len %d\n", __func__, len);
if (!sz->flush) {
for (i = 0; i < urb->actual_length; i++) {
dev_dbg(sz->dev, "%d: %x\n", i,
(unsigned char)sz->buf_in[i]);
switch (sz->decoder_state) {
case PulseSpace:
if ((sz->buf_in[i] & STREAMZAP_PULSE_MASK) ==
STREAMZAP_PULSE_MASK) {
sz->decoder_state = FullPulse;
continue;
} else if ((sz->buf_in[i] & STREAMZAP_SPACE_MASK)
== STREAMZAP_SPACE_MASK) {
sz_push_half_pulse(sz, sz->buf_in[i]);
sz->decoder_state = FullSpace;
continue;
} else {
sz_push_half_pulse(sz, sz->buf_in[i]);
sz_push_half_space(sz, sz->buf_in[i]);
}
break;
case FullPulse:
sz_push_full_pulse(sz, sz->buf_in[i]);
sz->decoder_state = IgnorePulse;
break;
case FullSpace:
if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
sz->idle = 1;
streamzap_stop_timer(sz);
#if 0
if (sz->timeout_enabled) {
sz->rawir.pulse = false;
sz->rawir.duration = timeout;
sz->rawir.duration *= 1000;
sz_push(sz);
}
#endif
streamzap_flush_delay_buffer(sz);
} else
sz_push_full_space(sz, sz->buf_in[i]);
sz->decoder_state = PulseSpace;
break;
case IgnorePulse:
if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
STREAMZAP_SPACE_MASK) {
sz->decoder_state = FullSpace;
continue;
}
sz_push_half_space(sz, sz->buf_in[i]);
sz->decoder_state = PulseSpace;
break;
}
}
}
usb_submit_urb(urb, GFP_ATOMIC);
return;
}
static struct input_dev *streamzap_init_input_dev(struct streamzap_ir *sz)
{
struct input_dev *idev;
struct ir_dev_props *props;
struct device *dev = sz->dev;
int ret;
idev = input_allocate_device();
if (!idev) {
dev_err(dev, "remote input dev allocation failed\n");
goto idev_alloc_failed;
}
props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
if (!props) {
dev_err(dev, "remote ir dev props allocation failed\n");
goto props_alloc_failed;
}
snprintf(sz->name, sizeof(sz->name), "Streamzap PC Remote Infrared "
"Receiver (%04x:%04x)",
le16_to_cpu(sz->usbdev->descriptor.idVendor),
le16_to_cpu(sz->usbdev->descriptor.idProduct));
idev->name = sz->name;
usb_make_path(sz->usbdev, sz->phys, sizeof(sz->phys));
strlcat(sz->phys, "/input0", sizeof(sz->phys));
idev->phys = sz->phys;
props->priv = sz;
props->driver_type = RC_DRIVER_IR_RAW;
/* FIXME: not sure about supported protocols, check on this */
props->allowed_protos = IR_TYPE_RC5 | IR_TYPE_RC6;
sz->props = props;
ret = ir_input_register(idev, RC_MAP_RC5_STREAMZAP, props, DRIVER_NAME);
if (ret < 0) {
dev_err(dev, "remote input device register failed\n");
goto irdev_failed;
}
return idev;
irdev_failed:
kfree(props);
props_alloc_failed:
input_free_device(idev);
idev_alloc_failed:
return NULL;
}
static int streamzap_delay_buf_init(struct streamzap_ir *sz)
{
int ret;
ret = kfifo_alloc(&sz->fifo, sizeof(int) * SZ_BUF_LEN,
GFP_KERNEL);
if (ret == 0)
sz->fifo_initialized = 1;
return ret;
}
static void streamzap_start_flush_timer(struct streamzap_ir *sz)
{
sz->flush_timer.expires = jiffies + HZ;
sz->flush = true;
add_timer(&sz->flush_timer);
sz->urb_in->dev = sz->usbdev;
if (usb_submit_urb(sz->urb_in, GFP_ATOMIC))
dev_err(sz->dev, "urb submit failed\n");
}
/**
* streamzap_probe
*
* Called by usb-core to associated with a candidate device
* On any failure the return value is the ERROR
* On success return 0
*/
static int __devinit streamzap_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *usbdev = interface_to_usbdev(intf);
struct usb_host_interface *iface_host;
struct streamzap_ir *sz = NULL;
char buf[63], name[128] = "";
int retval = -ENOMEM;
int pipe, maxp;
/* Allocate space for device driver specific data */
sz = kzalloc(sizeof(struct streamzap_ir), GFP_KERNEL);
if (!sz)
return -ENOMEM;
sz->usbdev = usbdev;
sz->interface = intf;
/* Check to ensure endpoint information matches requirements */
iface_host = intf->cur_altsetting;
if (iface_host->desc.bNumEndpoints != 1) {
dev_err(&intf->dev, "%s: Unexpected desc.bNumEndpoints (%d)\n",
__func__, iface_host->desc.bNumEndpoints);
retval = -ENODEV;
goto free_sz;
}
sz->endpoint = &(iface_host->endpoint[0].desc);
if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
!= USB_DIR_IN) {
dev_err(&intf->dev, "%s: endpoint doesn't match input device "
"02%02x\n", __func__, sz->endpoint->bEndpointAddress);
retval = -ENODEV;
goto free_sz;
}
if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
!= USB_ENDPOINT_XFER_INT) {
dev_err(&intf->dev, "%s: endpoint attributes don't match xfer "
"02%02x\n", __func__, sz->endpoint->bmAttributes);
retval = -ENODEV;
goto free_sz;
}
pipe = usb_rcvintpipe(usbdev, sz->endpoint->bEndpointAddress);
maxp = usb_maxpacket(usbdev, pipe, usb_pipeout(pipe));
if (maxp == 0) {
dev_err(&intf->dev, "%s: endpoint Max Packet Size is 0!?!\n",
__func__);
retval = -ENODEV;
goto free_sz;
}
/* Allocate the USB buffer and IRQ URB */
sz->buf_in = usb_alloc_coherent(usbdev, maxp, GFP_ATOMIC, &sz->dma_in);
if (!sz->buf_in)
goto free_sz;
sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
if (!sz->urb_in)
goto free_buf_in;
sz->dev = &intf->dev;
sz->buf_in_len = maxp;
if (usbdev->descriptor.iManufacturer
&& usb_string(usbdev, usbdev->descriptor.iManufacturer,
buf, sizeof(buf)) > 0)
strlcpy(name, buf, sizeof(name));
if (usbdev->descriptor.iProduct
&& usb_string(usbdev, usbdev->descriptor.iProduct,
buf, sizeof(buf)) > 0)
snprintf(name + strlen(name), sizeof(name) - strlen(name),
" %s", buf);
retval = streamzap_delay_buf_init(sz);
if (retval) {
dev_err(&intf->dev, "%s: delay buffer init failed\n", __func__);
goto free_urb_in;
}
sz->idev = streamzap_init_input_dev(sz);
if (!sz->idev)
goto input_dev_fail;
sz->idle = true;
sz->decoder_state = PulseSpace;
#if 0
/* not yet supported, depends on patches from maxim */
/* see also: LIRC_GET_REC_RESOLUTION and LIRC_SET_REC_TIMEOUT */
sz->timeout_enabled = false;
sz->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
sz->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
#endif
init_timer(&sz->delay_timer);
sz->delay_timer.function = streamzap_delay_timeout;
sz->delay_timer.data = (unsigned long)sz;
spin_lock_init(&sz->timer_lock);
init_timer(&sz->flush_timer);
sz->flush_timer.function = streamzap_flush_timeout;
sz->flush_timer.data = (unsigned long)sz;
do_gettimeofday(&sz->signal_start);
/* Complete final initialisations */
usb_fill_int_urb(sz->urb_in, usbdev, pipe, sz->buf_in,
maxp, (usb_complete_t)streamzap_callback,
sz, sz->endpoint->bInterval);
sz->urb_in->transfer_dma = sz->dma_in;
sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_set_intfdata(intf, sz);
streamzap_start_flush_timer(sz);
dev_info(sz->dev, "Registered %s on usb%d:%d\n", name,
usbdev->bus->busnum, usbdev->devnum);
return 0;
input_dev_fail:
kfifo_free(&sz->fifo);
free_urb_in:
usb_free_urb(sz->urb_in);
free_buf_in:
usb_free_coherent(usbdev, maxp, sz->buf_in, sz->dma_in);
free_sz:
kfree(sz);
return retval;
}
/**
* streamzap_disconnect
*
* Called by the usb core when the device is removed from the system.
*
* This routine guarantees that the driver will not submit any more urbs
* by clearing dev->usbdev. It is also supposed to terminate any currently
* active urbs. Unfortunately, usb_bulk_msg(), used in streamzap_read(),
* does not provide any way to do this.
*/
static void streamzap_disconnect(struct usb_interface *interface)
{
struct streamzap_ir *sz = usb_get_intfdata(interface);
struct usb_device *usbdev = interface_to_usbdev(interface);
usb_set_intfdata(interface, NULL);
if (!sz)
return;
if (sz->flush) {
sz->flush = false;
del_timer_sync(&sz->flush_timer);
}
streamzap_stop_timer(sz);
sz->usbdev = NULL;
ir_input_unregister(sz->idev);
usb_kill_urb(sz->urb_in);
usb_free_urb(sz->urb_in);
usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in);
kfree(sz);
}
static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
{
struct streamzap_ir *sz = usb_get_intfdata(intf);
if (sz->flush) {
sz->flush = false;
del_timer_sync(&sz->flush_timer);
}
streamzap_stop_timer(sz);
usb_kill_urb(sz->urb_in);
return 0;
}
static int streamzap_resume(struct usb_interface *intf)
{
struct streamzap_ir *sz = usb_get_intfdata(intf);
if (sz->fifo_initialized)
kfifo_reset(&sz->fifo);
sz->flush_timer.expires = jiffies + HZ;
sz->flush = true;
add_timer(&sz->flush_timer);
if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
dev_err(sz->dev, "Error sumbiting urb\n");
return -EIO;
}
return 0;
}
/**
* streamzap_init
*/
static int __init streamzap_init(void)
{
int ret;
/* register this driver with the USB subsystem */
ret = usb_register(&streamzap_driver);
if (ret < 0)
printk(KERN_ERR DRIVER_NAME ": usb register failed, "
"result = %d\n", ret);
return ret;
}
/**
* streamzap_exit
*/
static void __exit streamzap_exit(void)
{
usb_deregister(&streamzap_driver);
}
module_init(streamzap_init);
module_exit(streamzap_exit);
MODULE_AUTHOR("Jarod Wilson <jarod@wilsonet.com>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Enable debugging messages");

View File

@ -34,7 +34,7 @@ config MEDIA_TUNER
menuconfig MEDIA_TUNER_CUSTOMISE
bool "Customize analog and hybrid tuner modules to build"
depends on MEDIA_TUNER
default n
default y if EMBEDDED
help
This allows the user to deselect tuner drivers unnecessary
for their hardware from the build. Use this option with care

View File

@ -1763,7 +1763,15 @@ static struct dvb_frontend_ops dst_dvbt_ops = {
.frequency_min = 137000000,
.frequency_max = 858000000,
.frequency_stepsize = 166667,
.caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
.caps = FE_CAN_FEC_AUTO |
FE_CAN_QAM_AUTO |
FE_CAN_QAM_16 |
FE_CAN_QAM_32 |
FE_CAN_QAM_64 |
FE_CAN_QAM_128 |
FE_CAN_QAM_256 |
FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO
},
.release = dst_release,

View File

@ -1,7 +1,7 @@
config DVB_FE_CUSTOMISE
bool "Customise the frontend modules to build"
depends on DVB_CORE
default N
default y if EMBEDDED
help
This allows the user to select/deselect frontend drivers for their
hardware from the build.

View File

@ -1113,9 +1113,11 @@ struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev)
*/
prepare_to_wait(&coredev->buffer_mng_waitq, &wait, TASK_INTERRUPTIBLE);
if (list_empty(&coredev->buffers))
if (list_empty(&coredev->buffers)) {
spin_unlock_irqrestore(&coredev->bufferslock, flags);
schedule();
spin_lock_irqsave(&coredev->bufferslock, flags);
}
finish_wait(&coredev->buffer_mng_waitq, &wait);

View File

@ -83,7 +83,7 @@ config VIDEO_FIXED_MINOR_RANGES
config VIDEO_HELPER_CHIPS_AUTO
bool "Autoselect pertinent encoders/decoders and other helper chips"
default y
default y if !EMBEDDED
---help---
Most video cards may require additional modules to encode or
decode audio/video standards. This option will autoselect
@ -792,10 +792,11 @@ config SOC_CAMERA_MT9M001
and colour models.
config SOC_CAMERA_MT9M111
tristate "mt9m111 and mt9m112 support"
tristate "mt9m111, mt9m112 and mt9m131 support"
depends on SOC_CAMERA && I2C
help
This driver supports MT9M111 and MT9M112 cameras from Micron
This driver supports MT9M111, MT9M112 and MT9M131 cameras from
Micron/Aptina
config SOC_CAMERA_MT9T031
tristate "mt9t031 support"
@ -1016,4 +1017,13 @@ config VIDEO_MEM2MEM_TESTDEV
This is a virtual test device for the memory-to-memory driver
framework.
config VIDEO_SAMSUNG_S5P_FIMC
tristate "Samsung S5P FIMC (video postprocessor) driver"
depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
select VIDEOBUF_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
This is a v4l2 driver for the S5P camera interface
(video postprocessor)
endif # V4L_MEM2MEM_DRIVERS

View File

@ -11,7 +11,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o
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-event.o v4l2-ctrls.o
# V4L2 core modules
@ -163,6 +163,7 @@ obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
obj-$(CONFIG_ARCH_DAVINCI) += davinci/

View File

@ -26,10 +26,10 @@
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
@ -43,6 +43,21 @@ module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
struct cs53l32a_state {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
};
static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct cs53l32a_state, sd);
}
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd;
}
/* ----------------------------------------------------------------------- */
static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value)
@ -74,31 +89,20 @@ static int cs53l32a_s_routing(struct v4l2_subdev *sd,
return 0;
}
static int cs53l32a_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl)
{
if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
ctrl->value = (cs53l32a_read(sd, 0x03) & 0xc0) != 0;
return 0;
}
if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
return -EINVAL;
ctrl->value = (s8)cs53l32a_read(sd, 0x04);
return 0;
}
struct v4l2_subdev *sd = to_sd(ctrl);
static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
cs53l32a_write(sd, 0x03, ctrl->value ? 0xf0 : 0x30);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30);
return 0;
case V4L2_CID_AUDIO_VOLUME:
cs53l32a_write(sd, 0x04, (u8)ctrl->val);
cs53l32a_write(sd, 0x05, (u8)ctrl->val);
return 0;
}
if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
return -EINVAL;
if (ctrl->value > 12 || ctrl->value < -96)
return -EINVAL;
cs53l32a_write(sd, 0x04, (u8) ctrl->value);
cs53l32a_write(sd, 0x05, (u8) ctrl->value);
return 0;
return -EINVAL;
}
static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
@ -111,23 +115,30 @@ static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_id
static int cs53l32a_log_status(struct v4l2_subdev *sd)
{
struct cs53l32a_state *state = to_state(sd);
u8 v = cs53l32a_read(sd, 0x01);
u8 m = cs53l32a_read(sd, 0x03);
s8 vol = cs53l32a_read(sd, 0x04);
v4l2_info(sd, "Input: %d%s\n", (v >> 4) & 3,
(m & 0xC0) ? " (muted)" : "");
v4l2_info(sd, "Volume: %d dB\n", vol);
v4l2_info(sd, "Input: %d\n", (v >> 4) & 3);
v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = {
.s_ctrl = cs53l32a_s_ctrl,
};
static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
.log_status = cs53l32a_log_status,
.g_chip_ident = cs53l32a_g_chip_ident,
.g_ctrl = cs53l32a_g_ctrl,
.s_ctrl = cs53l32a_s_ctrl,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
@ -151,6 +162,7 @@ static const struct v4l2_subdev_ops cs53l32a_ops = {
static int cs53l32a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct cs53l32a_state *state;
struct v4l2_subdev *sd;
int i;
@ -164,9 +176,10 @@ static int cs53l32a_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
if (sd == NULL)
state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
for (i = 1; i <= 7; i++) {
@ -175,15 +188,29 @@ static int cs53l32a_probe(struct i2c_client *client,
v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
}
v4l2_ctrl_handler_init(&state->hdl, 2);
v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0);
v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
sd->ctrl_handler = &state->hdl;
if (state->hdl.error) {
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return err;
}
/* Set cs53l32a internal register for Adaptec 2010/2410 setup */
cs53l32a_write(sd, 0x01, (u8) 0x21);
cs53l32a_write(sd, 0x02, (u8) 0x29);
cs53l32a_write(sd, 0x03, (u8) 0x30);
cs53l32a_write(sd, 0x04, (u8) 0x00);
cs53l32a_write(sd, 0x05, (u8) 0x00);
cs53l32a_write(sd, 0x06, (u8) 0x00);
cs53l32a_write(sd, 0x07, (u8) 0x00);
cs53l32a_write(sd, 0x01, 0x21);
cs53l32a_write(sd, 0x02, 0x29);
cs53l32a_write(sd, 0x03, 0x30);
cs53l32a_write(sd, 0x04, 0x00);
cs53l32a_write(sd, 0x05, 0x00);
cs53l32a_write(sd, 0x06, 0x00);
cs53l32a_write(sd, 0x07, 0x00);
/* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
@ -198,9 +225,11 @@ static int cs53l32a_probe(struct i2c_client *client,
static int cs53l32a_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct cs53l32a_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
kfree(sd);
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return 0;
}

View File

@ -38,6 +38,145 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
/********************** COMMON CODE *********************/
/* definitions for audio properties bits 29-28 */
#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0
#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1
#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2
static const char *cx2341x_get_name(u32 id)
{
switch (id) {
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
return "Spatial Filter Mode";
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
return "Spatial Filter";
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
return "Spatial Luma Filter Type";
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
return "Spatial Chroma Filter Type";
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
return "Temporal Filter Mode";
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
return "Temporal Filter";
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
return "Median Filter Type";
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
return "Median Luma Filter Maximum";
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
return "Median Luma Filter Minimum";
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
return "Median Chroma Filter Maximum";
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
return "Median Chroma Filter Minimum";
case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
return "Insert Navigation Packets";
}
return NULL;
}
static const char **cx2341x_get_menu(u32 id)
{
static const char *cx2341x_video_spatial_filter_mode_menu[] = {
"Manual",
"Auto",
NULL
};
static const char *cx2341x_video_luma_spatial_filter_type_menu[] = {
"Off",
"1D Horizontal",
"1D Vertical",
"2D H/V Separable",
"2D Symmetric non-separable",
NULL
};
static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = {
"Off",
"1D Horizontal",
NULL
};
static const char *cx2341x_video_temporal_filter_mode_menu[] = {
"Manual",
"Auto",
NULL
};
static const char *cx2341x_video_median_filter_type_menu[] = {
"Off",
"Horizontal",
"Vertical",
"Horizontal/Vertical",
"Diagonal",
NULL
};
switch (id) {
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
return cx2341x_video_spatial_filter_mode_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
return cx2341x_video_luma_spatial_filter_type_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
return cx2341x_video_chroma_spatial_filter_type_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
return cx2341x_video_temporal_filter_mode_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
return cx2341x_video_median_filter_type_menu;
}
return NULL;
}
static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
{
*name = cx2341x_get_name(id);
*flags = 0;
switch (id) {
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
*type = V4L2_CTRL_TYPE_MENU;
*min = 0;
*step = 0;
break;
case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
*type = V4L2_CTRL_TYPE_BOOLEAN;
*min = 0;
*max = *step = 1;
break;
default:
*type = V4L2_CTRL_TYPE_INTEGER;
break;
}
switch (id) {
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
*flags |= V4L2_CTRL_FLAG_UPDATE;
break;
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
*flags |= V4L2_CTRL_FLAG_SLIDER;
break;
case V4L2_CID_MPEG_VIDEO_ENCODING:
*flags |= V4L2_CTRL_FLAG_READ_ONLY;
break;
}
}
/********************** OLD CODE *********************/
/* Must be sorted from low to high control ID! */
const u32 cx2341x_mpeg_ctrls[] = {
V4L2_CID_MPEG_CLASS,
@ -134,8 +273,6 @@ static const struct cx2341x_mpeg_params default_params = {
.video_chroma_median_filter_top = 255,
.video_chroma_median_filter_bottom = 0,
};
/* Map the control ID to the correct field in the cx2341x_mpeg_params
struct. Return -EINVAL if the ID is unknown, else return 0. */
static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params,
@ -415,83 +552,33 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl,
{
const char *name;
qctrl->flags = 0;
switch (qctrl->id) {
/* MPEG controls */
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
name = "Spatial Filter Mode";
break;
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
name = "Spatial Filter";
break;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
name = "Spatial Luma Filter Type";
break;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
name = "Spatial Chroma Filter Type";
break;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
name = "Temporal Filter Mode";
break;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
name = "Temporal Filter";
break;
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
name = "Median Filter Type";
break;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
name = "Median Luma Filter Maximum";
break;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
name = "Median Luma Filter Minimum";
break;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
name = "Median Chroma Filter Maximum";
break;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
name = "Median Chroma Filter Minimum";
break;
case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
name = "Insert Navigation Packets";
break;
cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type,
&min, &max, &step, &def, &qctrl->flags);
qctrl->minimum = min;
qctrl->maximum = max;
qctrl->step = step;
qctrl->default_value = def;
qctrl->reserved[0] = qctrl->reserved[1] = 0;
strlcpy(qctrl->name, name, sizeof(qctrl->name));
return 0;
default:
return v4l2_ctrl_query_fill(qctrl, min, max, step, def);
}
switch (qctrl->id) {
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
qctrl->type = V4L2_CTRL_TYPE_MENU;
min = 0;
step = 1;
break;
case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
min = 0;
max = 1;
step = 1;
break;
default:
qctrl->type = V4L2_CTRL_TYPE_INTEGER;
break;
}
switch (qctrl->id) {
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
qctrl->flags |= V4L2_CTRL_FLAG_UPDATE;
break;
}
qctrl->minimum = min;
qctrl->maximum = max;
qctrl->step = step;
qctrl->default_value = def;
qctrl->reserved[0] = qctrl->reserved[1] = 0;
snprintf(qctrl->name, sizeof(qctrl->name), name);
return 0;
}
int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
@ -797,42 +884,6 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
NULL
};
static const char *cx2341x_video_spatial_filter_mode_menu[] = {
"Manual",
"Auto",
NULL
};
static const char *cx2341x_video_luma_spatial_filter_type_menu[] = {
"Off",
"1D Horizontal",
"1D Vertical",
"2D H/V Separable",
"2D Symmetric non-separable",
NULL
};
static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = {
"Off",
"1D Horizontal",
NULL
};
static const char *cx2341x_video_temporal_filter_mode_menu[] = {
"Manual",
"Auto",
NULL
};
static const char *cx2341x_video_median_filter_type_menu[] = {
"Off",
"Horizontal",
"Vertical",
"Horizontal/Vertical",
"Diagonal",
NULL
};
switch (id) {
case V4L2_CID_MPEG_STREAM_TYPE:
return (p->capabilities & CX2341X_CAP_HAS_TS) ?
@ -844,26 +895,17 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
return NULL;
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
return cx2341x_video_spatial_filter_mode_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
return cx2341x_video_luma_spatial_filter_type_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
return cx2341x_video_chroma_spatial_filter_type_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
return cx2341x_video_temporal_filter_mode_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
return cx2341x_video_median_filter_type_menu;
return cx2341x_get_menu(id);
default:
return v4l2_ctrl_get_menu(id);
}
}
EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
/* definitions for audio properties bits 29-28 */
#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0
#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1
#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2
static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
{
params->audio_properties =
@ -1195,9 +1237,490 @@ void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix)
}
EXPORT_SYMBOL(cx2341x_log_status);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/********************** NEW CODE *********************/
static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl)
{
return container_of(ctrl->handler, struct cx2341x_handler, hdl);
}
static int cx2341x_hdl_api(struct cx2341x_handler *hdl,
u32 cmd, int args, ...)
{
u32 data[CX2341X_MBOX_MAX_DATA];
va_list vargs;
int i;
va_start(vargs, args);
for (i = 0; i < args; i++)
data[i] = va_arg(vargs, int);
va_end(vargs);
return hdl->func(hdl->priv, cmd, args, 0, data);
}
/* ctrl->handler->lock is held, so it is safe to access cur.val */
static inline int cx2341x_neq(struct v4l2_ctrl *ctrl)
{
return ctrl && ctrl->val != ctrl->cur.val;
}
static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl)
{
struct cx2341x_handler *hdl = to_cxhdl(ctrl);
s32 val = ctrl->val;
switch (ctrl->id) {
case V4L2_CID_MPEG_VIDEO_B_FRAMES: {
/* video gop cluster */
int b = val + 1;
int gop = hdl->video_gop_size->val;
gop = b * ((gop + b - 1) / b);
/* Max GOP size = 34 */
while (gop > 34)
gop -= b;
hdl->video_gop_size->val = gop;
break;
}
case V4L2_CID_MPEG_STREAM_TYPE:
/* stream type cluster */
hdl->video_encoding->val =
(hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
V4L2_MPEG_VIDEO_ENCODING_MPEG_1 :
V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
/* MPEG-1 implies CBR */
hdl->video_bitrate_mode->val =
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
/* peak bitrate shall be >= normal bitrate */
if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
hdl->video_bitrate_peak->val < hdl->video_bitrate->val)
hdl->video_bitrate_peak->val = hdl->video_bitrate->val;
break;
}
return 0;
}
static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl)
{
static const int mpeg_stream_type[] = {
0, /* MPEG-2 PS */
1, /* MPEG-2 TS */
2, /* MPEG-1 SS */
14, /* DVD */
11, /* VCD */
12, /* SVCD */
};
struct cx2341x_handler *hdl = to_cxhdl(ctrl);
s32 val = ctrl->val;
u32 props;
int err;
switch (ctrl->id) {
case V4L2_CID_MPEG_STREAM_VBI_FMT:
if (hdl->ops && hdl->ops->s_stream_vbi_fmt)
return hdl->ops->s_stream_vbi_fmt(hdl, val);
return 0;
case V4L2_CID_MPEG_VIDEO_ASPECT:
return cx2341x_hdl_api(hdl,
CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1);
case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val);
case V4L2_CID_MPEG_AUDIO_MUTE:
return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val);
case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
return cx2341x_hdl_api(hdl,
CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val);
case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val);
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
/* audio properties cluster */
props = (hdl->audio_sampling_freq->val << 0) |
(hdl->audio_mode->val << 8) |
(hdl->audio_mode_extension->val << 10) |
(hdl->audio_crc->val << 14);
if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17)
props |= 3 << 12;
else
props |= hdl->audio_emphasis->val << 12;
if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) {
props |=
#if 1
/* Not sure if this MPEG Layer II setting is required */
((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) |
#endif
(hdl->audio_ac3_bitrate->val << 4) |
(CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28);
} else {
/* Assuming MPEG Layer II */
props |=
((3 - hdl->audio_encoding->val) << 2) |
((1 + hdl->audio_l2_bitrate->val) << 4);
}
err = cx2341x_hdl_api(hdl,
CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props);
if (err)
return err;
hdl->audio_properties = props;
if (hdl->audio_ac3_bitrate) {
int is_ac3 = hdl->audio_encoding->val ==
V4L2_MPEG_AUDIO_ENCODING_AC3;
v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3);
v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3);
}
v4l2_ctrl_activate(hdl->audio_mode_extension,
hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO);
if (cx2341x_neq(hdl->audio_sampling_freq) &&
hdl->ops && hdl->ops->s_audio_sampling_freq)
return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val);
if (cx2341x_neq(hdl->audio_mode) &&
hdl->ops && hdl->ops->s_audio_mode)
return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val);
return 0;
case V4L2_CID_MPEG_VIDEO_B_FRAMES:
/* video gop cluster */
return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
hdl->video_gop_size->val,
hdl->video_b_frames->val + 1);
case V4L2_CID_MPEG_STREAM_TYPE:
/* stream type cluster */
err = cx2341x_hdl_api(hdl,
CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]);
if (err)
return err;
err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5,
hdl->video_bitrate_mode->val,
hdl->video_bitrate->val,
hdl->video_bitrate_peak->val / 400, 0, 0);
if (err)
return err;
v4l2_ctrl_activate(hdl->video_bitrate_mode,
hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1);
v4l2_ctrl_activate(hdl->video_bitrate_peak,
hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
if (cx2341x_neq(hdl->video_encoding) &&
hdl->ops && hdl->ops->s_video_encoding)
return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val);
return 0;
case V4L2_CID_MPEG_VIDEO_MUTE:
/* video mute cluster */
return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1,
hdl->video_mute->val |
(hdl->video_mute_yuv->val << 8));
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: {
int active_filter;
/* video filter mode */
err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2,
hdl->video_spatial_filter_mode->val |
(hdl->video_temporal_filter_mode->val << 1),
hdl->video_median_filter_type->val);
if (err)
return err;
active_filter = hdl->video_spatial_filter_mode->val !=
V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO;
v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter);
v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter);
v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter);
active_filter = hdl->video_temporal_filter_mode->val !=
V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO;
v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter);
active_filter = hdl->video_median_filter_type->val !=
V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF;
v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter);
v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter);
v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter);
v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter);
return 0;
}
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
/* video filter type cluster */
return cx2341x_hdl_api(hdl,
CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2,
hdl->video_luma_spatial_filter_type->val,
hdl->video_chroma_spatial_filter_type->val);
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
/* video filter cluster */
return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2,
hdl->video_spatial_filter->val,
hdl->video_temporal_filter->val);
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
/* video median cluster */
return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4,
hdl->video_luma_median_filter_bottom->val,
hdl->video_luma_median_filter_top->val,
hdl->video_chroma_median_filter_bottom->val,
hdl->video_chroma_median_filter_top->val);
}
return -EINVAL;
}
static const struct v4l2_ctrl_ops cx2341x_ops = {
.try_ctrl = cx2341x_try_ctrl,
.s_ctrl = cx2341x_s_ctrl,
};
static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
u32 id, s32 min, s32 max, s32 step, s32 def)
{
struct v4l2_ctrl_config cfg;
cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags);
cfg.ops = &cx2341x_ops;
cfg.id = id;
cfg.min = min;
cfg.max = max;
cfg.def = def;
if (cfg.type == V4L2_CTRL_TYPE_MENU) {
cfg.step = 0;
cfg.menu_skip_mask = step;
cfg.qmenu = cx2341x_get_menu(id);
} else {
cfg.step = step;
cfg.menu_skip_mask = 0;
}
return v4l2_ctrl_new_custom(hdl, &cfg, NULL);
}
static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
u32 id, s32 min, s32 max, s32 step, s32 def)
{
return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def);
}
static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl,
u32 id, s32 max, s32 mask, s32 def)
{
return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def);
}
int cx2341x_handler_init(struct cx2341x_handler *cxhdl,
unsigned nr_of_controls_hint)
{
struct v4l2_ctrl_handler *hdl = &cxhdl->hdl;
u32 caps = cxhdl->capabilities;
int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI;
int has_ac3 = caps & CX2341X_CAP_HAS_AC3;
int has_ts = caps & CX2341X_CAP_HAS_TS;
cxhdl->width = 720;
cxhdl->height = 480;
v4l2_ctrl_handler_init(hdl, nr_of_controls_hint);
/* Add controls in ascending control ID order for fastest
insertion time. */
cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_STREAM_TYPE,
V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2,
V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_STREAM_VBI_FMT,
V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2,
V4L2_MPEG_STREAM_VBI_FMT_NONE);
cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0,
V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_AUDIO_ENCODING,
V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2,
V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_AUDIO_L2_BITRATE,
V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff,
V4L2_MPEG_AUDIO_L2_BITRATE_224K);
cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_AUDIO_MODE,
V4L2_MPEG_AUDIO_MODE_MONO, 0,
V4L2_MPEG_AUDIO_MODE_STEREO);
cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0,
V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4);
cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_AUDIO_EMPHASIS,
V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0,
V4L2_MPEG_AUDIO_EMPHASIS_NONE);
cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_AUDIO_CRC,
V4L2_MPEG_AUDIO_CRC_CRC16, 0,
V4L2_MPEG_AUDIO_CRC_NONE);
cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0);
if (has_ac3)
cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_AUDIO_AC3_BITRATE,
V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03,
V4L2_MPEG_AUDIO_AC3_BITRATE_224K);
cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_VIDEO_ENCODING,
V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0,
V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_VIDEO_ASPECT,
V4L2_MPEG_VIDEO_ASPECT_221x100, 0,
V4L2_MPEG_VIDEO_ASPECT_4x3);
cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl,
V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2);
cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl,
V4L2_CID_MPEG_VIDEO_GOP_SIZE,
1, 34, 1, cxhdl->is_50hz ? 12 : 15);
cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1);
cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl,
V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl,
V4L2_CID_MPEG_VIDEO_BITRATE,
0, 27000000, 1, 6000000);
cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl,
V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
0, 27000000, 1, 8000000);
cx2341x_ctrl_new_std(hdl,
V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0);
cxhdl->video_mute = cx2341x_ctrl_new_std(hdl,
V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0);
cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl,
V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080);
/* CX23415/6 specific */
cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl,
V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0,
V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl,
V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
0, 15, 1, 0);
cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
0,
V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR);
cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
0,
V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR);
cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl,
V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO,
0,
V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl,
V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
0, 31, 1, 8);
cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl,
V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG,
0,
V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
0, 255, 1, 0);
cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
0, 255, 1, 255);
cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
0, 255, 1, 0);
cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
0, 255, 1, 255);
cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
0, 1, 1, 0);
if (hdl->error) {
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
return err;
}
v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq);
v4l2_ctrl_cluster(2, &cxhdl->video_b_frames);
v4l2_ctrl_cluster(5, &cxhdl->stream_type);
v4l2_ctrl_cluster(2, &cxhdl->video_mute);
v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode);
v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type);
v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter);
v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top);
return 0;
}
EXPORT_SYMBOL(cx2341x_handler_init);
void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz)
{
cxhdl->is_50hz = is_50hz;
cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15;
}
EXPORT_SYMBOL(cx2341x_handler_set_50hz);
int cx2341x_handler_setup(struct cx2341x_handler *cxhdl)
{
int h = cxhdl->height;
int w = cxhdl->width;
int err;
err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0);
if (err)
return err;
err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz);
if (err)
return err;
if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) {
w /= 2;
h /= 2;
}
err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w);
if (err)
return err;
return v4l2_ctrl_handler_setup(&cxhdl->hdl);
}
EXPORT_SYMBOL(cx2341x_handler_setup);
void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy)
{
v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy);
v4l2_ctrl_grab(cxhdl->audio_encoding, busy);
v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy);
v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy);
v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy);
v4l2_ctrl_grab(cxhdl->stream_type, busy);
v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy);
v4l2_ctrl_grab(cxhdl->video_bitrate, busy);
v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy);
}
EXPORT_SYMBOL(cx2341x_handler_set_busy);

View File

@ -5,7 +5,7 @@ config VIDEO_CX23885
select VIDEO_BTCX
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
select IR_CORE
select VIDEOBUF_DVB
select VIDEOBUF_DMA_SG
select VIDEO_CX25840

View File

@ -1,7 +1,8 @@
cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
cx23885-ioctl.o cx23885-ir.o cx23885-input.o cx23888-ir.o \
netup-init.o cimax2.o netup-eeprom.o cx23885-f300.o
cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \
cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \
cx23885-f300.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o

View File

@ -0,0 +1,35 @@
/*
* Driver for the Conexant CX23885/7/8 PCIe bridge
*
* AV device support routines - non-input, non-vl42_subdev routines
*
* Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "cx23885.h"
void cx23885_av_work_handler(struct work_struct *work)
{
struct cx23885_dev *dev =
container_of(work, struct cx23885_dev, cx25840_work);
bool handled;
v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine,
PCI_MSK_AV_CORE, &handled);
cx23885_irq_enable(dev, PCI_MSK_AV_CORE);
}

View File

@ -0,0 +1,27 @@
/*
* Driver for the Conexant CX23885/7/8 PCIe bridge
*
* AV device support routines - non-input, non-vl42_subdev routines
*
* Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef _CX23885_AV_H_
#define _CX23885_AV_H_
void cx23885_av_work_handler(struct work_struct *work);
#endif

View File

@ -30,6 +30,16 @@
#include "netup-init.h"
#include "cx23888-ir.h"
static unsigned int enable_885_ir;
module_param(enable_885_ir, int, 0644);
MODULE_PARM_DESC(enable_885_ir,
"Enable integrated IR controller for supported\n"
"\t\t CX2388[57] boards that are wired for it:\n"
"\t\t\tHVR-1250 (reported safe)\n"
"\t\t\tTeVii S470 (reported unsafe)\n"
"\t\t This can cause an interrupt storm with some cards.\n"
"\t\t Default: 0 [Disabled]");
/* ------------------------------------------------------------------ */
/* board config info */
@ -626,6 +636,9 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
case 79101:
/* WinTV-HVR1250 (PCIe, Retail, IR, half height,
ATSC and Basic analog */
case 79501:
/* WinTV-HVR1250 (PCIe, No IR, half height,
ATSC [at least] and Basic analog) */
case 79561:
/* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
ATSC and Basic analog */
@ -959,9 +972,37 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
int cx23885_ir_init(struct cx23885_dev *dev)
{
static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = {
{
.flags = V4L2_SUBDEV_IO_PIN_INPUT,
.pin = CX23885_PIN_IR_RX_GPIO19,
.function = CX23885_PAD_IR_RX,
.value = 0,
.strength = CX25840_PIN_DRIVE_MEDIUM,
}, {
.flags = V4L2_SUBDEV_IO_PIN_OUTPUT,
.pin = CX23885_PIN_IR_TX_GPIO20,
.function = CX23885_PAD_IR_TX,
.value = 0,
.strength = CX25840_PIN_DRIVE_MEDIUM,
}
};
const size_t ir_rxtx_pin_cfg_count = ARRAY_SIZE(ir_rxtx_pin_cfg);
static struct v4l2_subdev_io_pin_config ir_rx_pin_cfg[] = {
{
.flags = V4L2_SUBDEV_IO_PIN_INPUT,
.pin = CX23885_PIN_IR_RX_GPIO19,
.function = CX23885_PAD_IR_RX,
.value = 0,
.strength = CX25840_PIN_DRIVE_MEDIUM,
}
};
const size_t ir_rx_pin_cfg_count = ARRAY_SIZE(ir_rx_pin_cfg);
struct v4l2_subdev_ir_parameters params;
int ret = 0;
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
@ -979,7 +1020,41 @@ int cx23885_ir_init(struct cx23885_dev *dev)
if (ret)
break;
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
dev->pci_irqmask |= PCI_MSK_IR;
v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
/*
* For these boards we need to invert the Tx output via the
* IR controller to have the LED off while idle
*/
v4l2_subdev_call(dev->sd_ir, ir, tx_g_parameters, &params);
params.enable = false;
params.shutdown = false;
params.invert_level = true;
v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
params.shutdown = true;
v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
break;
case CX23885_BOARD_TEVII_S470:
if (!enable_885_ir)
break;
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
if (dev->sd_ir == NULL) {
ret = -ENODEV;
break;
}
v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
ir_rx_pin_cfg_count, ir_rx_pin_cfg);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1250:
if (!enable_885_ir)
break;
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
if (dev->sd_ir == NULL) {
ret = -ENODEV;
break;
}
v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
break;
case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
request_module("ir-kbd-i2c");
@ -994,11 +1069,16 @@ void cx23885_ir_fini(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
dev->pci_irqmask &= ~PCI_MSK_IR;
cx_clear(PCI_INT_MSK, PCI_MSK_IR);
cx23885_irq_remove(dev, PCI_MSK_IR);
cx23888_ir_remove(dev);
dev->sd_ir = NULL;
break;
case CX23885_BOARD_TEVII_S470:
case CX23885_BOARD_HAUPPAUGE_HVR1250:
cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
/* sd_ir is a duplicate pointer to the AV Core, just clear it */
dev->sd_ir = NULL;
break;
}
}
@ -1007,8 +1087,13 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR))
cx_set(PCI_INT_MSK, PCI_MSK_IR);
if (dev->sd_ir)
cx23885_irq_add_enable(dev, PCI_MSK_IR);
break;
case CX23885_BOARD_TEVII_S470:
case CX23885_BOARD_HAUPPAUGE_HVR1250:
if (dev->sd_ir)
cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
break;
}
}
@ -1028,6 +1113,13 @@ void cx23885_card_setup(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
if (dev->i2c_bus[0].i2c_rc == 0) {
if (eeprom[0x80] != 0x84)
hauppauge_eeprom(dev, eeprom+0xc0);
else
hauppauge_eeprom(dev, eeprom+0x80);
}
break;
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1400:
@ -1136,6 +1228,11 @@ void cx23885_card_setup(struct cx23885_dev *dev)
* loaded, ensure this happens.
*/
switch (dev->board) {
case CX23885_BOARD_TEVII_S470:
case CX23885_BOARD_HAUPPAUGE_HVR1250:
/* Currently only enabled for the integrated IR controller */
if (!enable_885_ir)
break;
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
case CX23885_BOARD_HAUPPAUGE_HVR1700:
@ -1151,7 +1248,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1, NULL);
v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
if (dev->sd_cx25840) {
dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
}
break;
}

View File

@ -34,6 +34,7 @@
#include "cimax2.h"
#include "cx23888-ir.h"
#include "cx23885-ir.h"
#include "cx23885-av.h"
#include "cx23885-input.h"
MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
@ -299,6 +300,83 @@ static struct sram_channel cx23887_sram_channels[] = {
},
};
void cx23885_irq_add(struct cx23885_dev *dev, u32 mask)
{
unsigned long flags;
spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
dev->pci_irqmask |= mask;
spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
}
void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask)
{
unsigned long flags;
spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
dev->pci_irqmask |= mask;
cx_set(PCI_INT_MSK, mask);
spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
}
void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask)
{
u32 v;
unsigned long flags;
spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
v = mask & dev->pci_irqmask;
if (v)
cx_set(PCI_INT_MSK, v);
spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
}
static inline void cx23885_irq_enable_all(struct cx23885_dev *dev)
{
cx23885_irq_enable(dev, 0xffffffff);
}
void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask)
{
unsigned long flags;
spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
cx_clear(PCI_INT_MSK, mask);
spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
}
static inline void cx23885_irq_disable_all(struct cx23885_dev *dev)
{
cx23885_irq_disable(dev, 0xffffffff);
}
void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask)
{
unsigned long flags;
spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
dev->pci_irqmask &= ~mask;
cx_clear(PCI_INT_MSK, mask);
spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
}
static u32 cx23885_irq_get_mask(struct cx23885_dev *dev)
{
u32 v;
unsigned long flags;
spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
v = cx_read(PCI_INT_MSK);
spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
return v;
}
static int cx23885_risc_decode(u32 risc)
{
static char *instr[16] = {
@ -548,7 +626,7 @@ static void cx23885_shutdown(struct cx23885_dev *dev)
cx_write(UART_CTL, 0);
/* Disable Interrupts */
cx_write(PCI_INT_MSK, 0);
cx23885_irq_disable_all(dev);
cx_write(VID_A_INT_MSK, 0);
cx_write(VID_B_INT_MSK, 0);
cx_write(VID_C_INT_MSK, 0);
@ -774,6 +852,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
{
int i;
spin_lock_init(&dev->pci_irqmask_lock);
mutex_init(&dev->lock);
mutex_init(&dev->gpio_lock);
@ -820,9 +900,9 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
dev->pci_irqmask = 0x001f00;
cx23885_irq_add(dev, 0x001f00);
if (cx23885_boards[dev->board].cimax > 0)
dev->pci_irqmask |= 0x01800000; /* for CiMaxes */
cx23885_irq_add(dev, 0x01800000); /* for CiMaxes */
/* External Master 1 Bus */
dev->i2c_bus[0].nr = 0;
@ -1156,7 +1236,7 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __func__,
cx_read(DEV_CNTRL2));
dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __func__,
cx_read(PCI_INT_MSK));
cx23885_irq_get_mask(dev));
dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __func__,
cx_read(AUDIO_INT_INT_MSK));
dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __func__,
@ -1292,7 +1372,8 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
dprintk(1, "%s() enabling TS int's and DMA\n", __func__);
cx_set(port->reg_ts_int_msk, port->ts_int_msk_val);
cx_set(port->reg_dma_ctl, port->dma_ctl_val);
cx_set(PCI_INT_MSK, dev->pci_irqmask | port->pci_irqmask);
cx23885_irq_add(dev, port->pci_irqmask);
cx23885_irq_enable_all(dev);
break;
default:
BUG();
@ -1650,10 +1731,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
u32 ts1_status, ts1_mask;
u32 ts2_status, ts2_mask;
int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
bool ir_handled = false;
bool subdev_handled;
pci_status = cx_read(PCI_INT_STAT);
pci_mask = cx_read(PCI_INT_MSK);
pci_mask = cx23885_irq_get_mask(dev);
vida_status = cx_read(VID_A_INT_STAT);
vida_mask = cx_read(VID_A_INT_MSK);
ts1_status = cx_read(VID_B_INT_STAT);
@ -1681,7 +1762,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
PCI_MSK_VID_C | PCI_MSK_VID_B | PCI_MSK_VID_A |
PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
PCI_MSK_GPIO0 | PCI_MSK_GPIO1 |
PCI_MSK_IR)) {
PCI_MSK_AV_CORE | PCI_MSK_IR)) {
if (pci_status & PCI_MSK_RISC_RD)
dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n",
@ -1731,6 +1812,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n",
PCI_MSK_GPIO1);
if (pci_status & PCI_MSK_AV_CORE)
dprintk(7, " (PCI_MSK_AV_CORE 0x%08x)\n",
PCI_MSK_AV_CORE);
if (pci_status & PCI_MSK_IR)
dprintk(7, " (PCI_MSK_IR 0x%08x)\n",
PCI_MSK_IR);
@ -1765,12 +1850,22 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
handled += cx23885_video_irq(dev, vida_status);
if (pci_status & PCI_MSK_IR) {
v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine,
pci_status, &ir_handled);
if (ir_handled)
subdev_handled = false;
v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine,
pci_status, &subdev_handled);
if (subdev_handled)
handled++;
}
if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) {
cx23885_irq_disable(dev, PCI_MSK_AV_CORE);
if (!schedule_work(&dev->cx25840_work))
printk(KERN_ERR "%s: failed to set up deferred work for"
" AV Core/IR interrupt. Interrupt is disabled"
" and won't be re-enabled\n", dev->name);
handled++;
}
if (handled)
cx_write(PCI_INT_STAT, pci_status);
out:
@ -1788,11 +1883,11 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
dev = to_cx23885(sd->v4l2_dev);
switch (notification) {
case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */
case V4L2_SUBDEV_IR_RX_NOTIFY: /* Possibly called in an IRQ context */
if (sd == dev->sd_ir)
cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
break;
case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */
case V4L2_SUBDEV_IR_TX_NOTIFY: /* Possibly called in an IRQ context */
if (sd == dev->sd_ir)
cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
break;
@ -1801,6 +1896,7 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev)
{
INIT_WORK(&dev->cx25840_work, cx23885_av_work_handler);
INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler);
INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
dev->v4l2_dev.notify = cx23885_v4l2_dev_notify;
@ -1967,7 +2063,7 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
switch (dev->board) {
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */
cx23885_irq_add_enable(dev, 0x01800000); /* for NetUP */
break;
}

View File

@ -99,7 +99,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
return -EIO;
return -ENXIO;
dprintk(1, "%s() returns 0\n", __func__);
return 0;
@ -120,11 +120,12 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
retval = i2c_wait_done(i2c_adap);
if (retval < 0)
goto err;
if (retval == 0)
if (!i2c_wait_done(i2c_adap))
goto eio;
if (!i2c_slave_did_ack(i2c_adap)) {
retval = -ENXIO;
goto err;
}
if (i2c_debug) {
printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
if (!(ctrl & I2C_NOSTOP))
@ -145,10 +146,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
retval = i2c_wait_done(i2c_adap);
if (retval < 0)
goto err;
if (retval == 0)
if (!i2c_wait_done(i2c_adap))
goto eio;
if (i2c_debug) {
dprintk(1, " %02x", msg->buf[cnt]);
@ -185,7 +183,7 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
return -EIO;
return -ENXIO;
dprintk(1, "%s() returns 0\n", __func__);
@ -209,11 +207,12 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
cx_write(bus->reg_addr, msg->addr << 25);
cx_write(bus->reg_ctrl, ctrl);
retval = i2c_wait_done(i2c_adap);
if (retval < 0)
goto err;
if (retval == 0)
if (!i2c_wait_done(i2c_adap))
goto eio;
if (cnt == 0 && !i2c_slave_did_ack(i2c_adap)) {
retval = -ENXIO;
goto err;
}
msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
if (i2c_debug) {
dprintk(1, " %02x", msg->buf[cnt]);

View File

@ -44,40 +44,26 @@
#define MODULE_NAME "cx23885"
static void convert_measurement(u32 x, struct ir_raw_event *y)
{
if (x == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) {
y->pulse = false;
y->duration = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
return;
}
y->pulse = (x & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? true : false;
y->duration = x & V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
}
static void cx23885_input_process_measurements(struct cx23885_dev *dev,
bool overrun)
{
struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
struct ir_raw_event kernel_ir_event;
u32 sd_ir_data[64];
ssize_t num;
int count, i;
bool handle = false;
struct ir_raw_event ir_core_event[64];
do {
num = 0;
v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) sd_ir_data,
sizeof(sd_ir_data), &num);
v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event,
sizeof(ir_core_event), &num);
count = num / sizeof(u32);
count = num / sizeof(struct ir_raw_event);
for (i = 0; i < count; i++) {
convert_measurement(sd_ir_data[i], &kernel_ir_event);
ir_raw_event_store(kernel_ir->inp_dev,
&kernel_ir_event);
&ir_core_event[i]);
handle = true;
}
} while (num != 0);
@ -99,8 +85,10 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
case CX23885_BOARD_TEVII_S470:
case CX23885_BOARD_HAUPPAUGE_HVR1250:
/*
* The only board we handle right now. However other boards
* The only boards we handle right now. However other boards
* using the CX2388x integrated IR controller should be similar
*/
break;
@ -148,6 +136,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
case CX23885_BOARD_HAUPPAUGE_HVR1250:
/*
* The IR controller on this board only returns pulse widths.
* Any other mode setting will fail to set up the device.
@ -170,7 +159,38 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
* mark is received as low logic level;
* falling edges are detected as rising edges; etc.
*/
params.invert = true;
params.invert_level = true;
break;
case CX23885_BOARD_TEVII_S470:
/*
* The IR controller on this board only returns pulse widths.
* Any other mode setting will fail to set up the device.
*/
params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
params.enable = true;
params.interrupt_enable = true;
params.shutdown = false;
/* Setup for a standard NEC protocol */
params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */
params.carrier_range_lower = 33000; /* Hz */
params.carrier_range_upper = 43000; /* Hz */
params.duty_cycle = 33; /* percent, 33 percent for NEC */
/*
* NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units
* (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns
*/
params.max_pulse_width = 12378022; /* ns */
/*
* NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit
* (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns
*/
params.noise_filter_min_width = 351648; /* ns */
params.modulation = false;
params.invert_level = true;
break;
}
v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
@ -244,12 +264,20 @@ int cx23885_input_init(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
/* Integrated CX23888 IR controller */
case CX23885_BOARD_HAUPPAUGE_HVR1250:
/* Integrated CX2388[58] IR controller */
driver_type = RC_DRIVER_IR_RAW;
allowed_protos = IR_TYPE_ALL;
/* The grey Hauppauge RC-5 remote */
rc_map = RC_MAP_RC5_HAUPPAUGE_NEW;
break;
case CX23885_BOARD_TEVII_S470:
/* Integrated CX23885 IR controller */
driver_type = RC_DRIVER_IR_RAW;
allowed_protos = IR_TYPE_ALL;
/* A guess at the remote */
rc_map = RC_MAP_TEVII_NEC;
break;
default:
return -ENODEV;
}

View File

@ -72,7 +72,7 @@ void cx23885_ir_tx_work_handler(struct work_struct *work)
}
/* Called in an IRQ context */
/* Possibly called in an IRQ context */
void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
{
struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
@ -86,10 +86,18 @@ void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
schedule_work(&dev->ir_rx_work);
/*
* For the integrated AV core, we are already in a workqueue context.
* For the CX23888 integrated IR, we are in an interrupt context.
*/
if (sd == dev->sd_cx25840)
cx23885_ir_rx_work_handler(&dev->ir_rx_work);
else
schedule_work(&dev->ir_rx_work);
}
/* Called in an IRQ context */
/* Possibly called in an IRQ context */
void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
{
struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
@ -97,5 +105,13 @@ void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
schedule_work(&dev->ir_tx_work);
/*
* For the integrated AV core, we are already in a workqueue context.
* For the CX23888 integrated IR, we are in an interrupt context.
*/
if (sd == dev->sd_cx25840)
cx23885_ir_tx_work_handler(&dev->ir_tx_work);
else
schedule_work(&dev->ir_tx_work);
}

View File

@ -213,6 +213,7 @@ Channel manager Data Structure entry = 20 DWORD
#define DEV_CNTRL2 0x00040000
#define PCI_MSK_IR (1 << 28)
#define PCI_MSK_AV_CORE (1 << 27)
#define PCI_MSK_GPIO1 (1 << 24)
#define PCI_MSK_GPIO0 (1 << 23)
#define PCI_MSK_APB_DMA (1 << 12)

View File

@ -74,7 +74,7 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev,
q->count = 1;
/* enable irqs */
cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
cx23885_irq_add_enable(dev, 0x01);
cx_set(VID_A_INT_MSK, 0x000022);
/* start dma */

View File

@ -441,7 +441,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev,
q->count = 1;
/* enable irq */
cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
cx23885_irq_add_enable(dev, 0x01);
cx_set(VID_A_INT_MSK, 0x000011);
/* start dma */
@ -1205,6 +1205,21 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
return 0;
}
static int vidioc_log_status(struct file *file, void *priv)
{
struct cx23885_fh *fh = priv;
struct cx23885_dev *dev = fh->dev;
printk(KERN_INFO
"%s/0: ============ START LOG STATUS ============\n",
dev->name);
call_all(dev, core, log_status);
printk(KERN_INFO
"%s/0: ============= END LOG STATUS =============\n",
dev->name);
return 0;
}
static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qctrl)
{
@ -1410,6 +1425,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_log_status = vidioc_log_status,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
@ -1449,7 +1465,7 @@ static const struct v4l2_file_operations radio_fops = {
void cx23885_video_unregister(struct cx23885_dev *dev)
{
dprintk(1, "%s()\n", __func__);
cx_clear(PCI_INT_MSK, 1);
cx23885_irq_remove(dev, 0x01);
if (dev->video_dev) {
if (video_is_registered(dev->video_dev))
@ -1486,7 +1502,8 @@ int cx23885_video_register(struct cx23885_dev *dev)
VID_A_DMA_CTL, 0x11, 0x00);
/* Don't enable VBI yet */
cx_set(PCI_INT_MSK, 1);
cx23885_irq_add_enable(dev, 0x01);
if (TUNER_ABSENT != dev->tuner_type) {
struct v4l2_subdev *sd = NULL;

View File

@ -325,6 +325,7 @@ struct cx23885_dev {
u32 __iomem *lmmio;
u8 __iomem *bmmio;
int pci_irqmask;
spinlock_t pci_irqmask_lock; /* protects mask reg too */
int hwrevision;
/* This valud is board specific and is used to configure the
@ -365,6 +366,7 @@ struct cx23885_dev {
unsigned char radio_addr;
unsigned int has_radio;
struct v4l2_subdev *sd_cx25840;
struct work_struct cx25840_work;
/* Infrared */
struct v4l2_subdev *sd_ir;
@ -403,7 +405,8 @@ static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
#define call_all(dev, o, f, args...) \
v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
#define CX23885_HW_888_IR (1 << 0)
#define CX23885_HW_888_IR (1 << 0)
#define CX23885_HW_AV_CORE (1 << 1)
#define call_hw(dev, grpid, o, f, args...) \
v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args)
@ -484,6 +487,10 @@ extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask);
extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
int asoutput);
extern void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask);
extern void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask);
extern void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask);
extern void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask);
/* ----------------------------------------------------------- */
/* cx23885-cards.c */

View File

@ -26,6 +26,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/ir-core.h>
#include "cx23885.h"
@ -60,6 +61,8 @@ MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
#define CNTRL_CPL 0x00001000
#define CNTRL_LBM 0x00002000
#define CNTRL_R 0x00004000
/* CX23888 specific control flag */
#define CNTRL_IVO 0x00008000
#define CX23888_IR_TXCLK_REG 0x170004
#define TXCLK_TCD 0x0000FFFF
@ -111,8 +114,18 @@ MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
#define CX23888_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */
#define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2)
#define CX23888_IR_RX_KFIFO_SIZE (512 * sizeof(u32))
#define CX23888_IR_TX_KFIFO_SIZE (512 * sizeof(u32))
/*
* We use this union internally for convenience, but callers to tx_write
* and rx_read will be expecting records of type struct ir_raw_event.
* Always ensure the size of this union is dictated by struct ir_raw_event.
*/
union cx23888_ir_fifo_rec {
u32 hw_fifo_data;
struct ir_raw_event ir_core_data;
};
#define CX23888_IR_RX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
#define CX23888_IR_TX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
struct cx23888_ir_state {
struct v4l2_subdev sd;
@ -423,6 +436,13 @@ static inline void control_tx_polarity_invert(struct cx23885_dev *dev,
invert ? CNTRL_CPL : 0);
}
static inline void control_tx_level_invert(struct cx23885_dev *dev,
bool invert)
{
cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_IVO,
invert ? CNTRL_IVO : 0);
}
/*
* IR Rx & Tx Clock Register helpers
*/
@ -449,8 +469,8 @@ static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
{
u64 pulse_clocks;
if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
if (ns > IR_MAX_DURATION)
ns = IR_MAX_DURATION;
pulse_clocks = ns_to_pulse_clocks(ns);
*divider = pulse_clocks_to_clock_divider(pulse_clocks);
cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
@ -462,8 +482,8 @@ static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
{
u64 pulse_clocks;
if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
if (ns > IR_MAX_DURATION)
ns = IR_MAX_DURATION;
pulse_clocks = ns_to_pulse_clocks(ns);
*divider = pulse_clocks_to_clock_divider(pulse_clocks);
cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
@ -526,8 +546,8 @@ static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
u32 rx_data[FIFO_RX_DEPTH];
int i, j, k;
union cx23888_ir_fifo_rec rx_data[FIFO_RX_DEPTH];
unsigned int i, j, k;
u32 events, v;
int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
@ -588,11 +608,12 @@ static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
for (j = 0;
(v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG);
rx_data[i++] = v & ~FIFO_RX_NDV;
rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
i++;
}
if (i == 0)
break;
j = i * sizeof(u32);
j = i * sizeof(union cx23888_ir_fifo_rec);
k = kfifo_in_locked(&state->rx_kfifo,
(unsigned char *) rx_data, j,
&state->rx_kfifo_lock);
@ -651,10 +672,11 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
u16 divider = (u16) atomic_read(&state->rxclk_divider);
unsigned int i, n;
u32 *p;
u32 u, v;
union cx23888_ir_fifo_rec *p;
unsigned u, v;
n = count / sizeof(u32) * sizeof(u32);
n = count / sizeof(union cx23888_ir_fifo_rec)
* sizeof(union cx23888_ir_fifo_rec);
if (n == 0) {
*num = 0;
return 0;
@ -662,26 +684,28 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
n = kfifo_out_locked(&state->rx_kfifo, buf, n, &state->rx_kfifo_lock);
n /= sizeof(u32);
*num = n * sizeof(u32);
n /= sizeof(union cx23888_ir_fifo_rec);
*num = n * sizeof(union cx23888_ir_fifo_rec);
for (p = (u32 *) buf, i = 0; i < n; p++, i++) {
if ((*p & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
*p = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END;
for (p = (union cx23888_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
/* Assume RTO was because of no IR light input */
u = 0;
v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
continue;
} else {
u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
if (invert)
u = u ? 0 : 1;
}
u = (*p & FIFO_RXTX_LVL) ? V4L2_SUBDEV_IR_PULSE_LEVEL_MASK : 0;
if (invert)
u = u ? 0 : V4L2_SUBDEV_IR_PULSE_LEVEL_MASK;
v = (unsigned) pulse_width_count_to_ns(
(u16) (p->hw_fifo_data & FIFO_RXTX), divider);
if (v > IR_MAX_DURATION)
v = IR_MAX_DURATION;
v = (u32) pulse_width_count_to_ns((u16) (*p & FIFO_RXTX),
divider);
if (v >= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
v = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS - 1;
*p = u | v;
p->ir_core_data.pulse = u;
p->ir_core_data.duration = v;
v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s\n",
v, u ? "mark" : "space");
@ -740,7 +764,8 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
o->bytes_per_data_element = p->bytes_per_data_element
= sizeof(union cx23888_ir_fifo_rec);
/* Before we tweak the hardware, we have to disable the receiver */
irqenable_rx(dev, 0);
@ -762,12 +787,15 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
&p->carrier_range_upper);
o->carrier_range_lower = p->carrier_range_lower;
o->carrier_range_upper = p->carrier_range_upper;
p->max_pulse_width =
(u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
} else {
p->max_pulse_width =
rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width,
&rxclk_divider);
o->max_pulse_width = p->max_pulse_width;
}
o->max_pulse_width = p->max_pulse_width;
atomic_set(&state->rxclk_divider, rxclk_divider);
p->noise_filter_min_width =
@ -782,8 +810,8 @@ static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH);
o->invert = p->invert;
atomic_set(&state->rx_invert, p->invert);
o->invert_level = p->invert_level;
atomic_set(&state->rx_invert, p->invert_level);
o->interrupt_enable = p->interrupt_enable;
o->enable = p->enable;
@ -864,7 +892,8 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
o->bytes_per_data_element = p->bytes_per_data_element
= sizeof(union cx23888_ir_fifo_rec);
/* Before we tweak the hardware, we have to disable the transmitter */
irqenable_tx(dev, 0);
@ -880,12 +909,15 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle);
o->duty_cycle = p->duty_cycle;
p->max_pulse_width =
(u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
} else {
p->max_pulse_width =
txclk_tx_s_max_pulse_width(dev, p->max_pulse_width,
&txclk_divider);
o->max_pulse_width = p->max_pulse_width;
}
o->max_pulse_width = p->max_pulse_width;
atomic_set(&state->txclk_divider, txclk_divider);
p->resolution = clock_divider_to_resolution(txclk_divider);
@ -894,8 +926,11 @@ static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
/* FIXME - make this dependent on resolution for better performance */
control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY);
control_tx_polarity_invert(dev, p->invert);
o->invert = p->invert;
control_tx_polarity_invert(dev, p->invert_carrier_sense);
o->invert_carrier_sense = p->invert_carrier_sense;
control_tx_level_invert(dev, p->invert_level);
o->invert_level = p->invert_level;
o->interrupt_enable = p->interrupt_enable;
o->enable = p->enable;
@ -988,12 +1023,10 @@ static int cx23888_ir_log_status(struct v4l2_subdev *sd)
"-%1d/+%1d, %u to %u Hz\n", i, j,
clock_divider_to_freq(rxclk, 16 + j),
clock_divider_to_freq(rxclk, 16 - i));
} else {
v4l2_info(sd, "\tMax measurable pulse width: %u us, "
"%llu ns\n",
pulse_width_count_to_us(FIFO_RXTX, rxclk),
pulse_width_count_to_ns(FIFO_RXTX, rxclk));
}
v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n",
pulse_width_count_to_us(FIFO_RXTX, rxclk),
pulse_width_count_to_ns(FIFO_RXTX, rxclk));
v4l2_info(sd, "\tLow pass filter: %s\n",
filtr ? "enabled" : "disabled");
if (filtr)
@ -1025,19 +1058,20 @@ static int cx23888_ir_log_status(struct v4l2_subdev *sd)
cntrl & CNTRL_TFE ? "enabled" : "disabled");
v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
cntrl & CNTRL_TIC ? "not empty" : "half full or less");
v4l2_info(sd, "\tSignal polarity: %s\n",
cntrl & CNTRL_CPL ? "0:mark 1:space" : "0:space 1:mark");
v4l2_info(sd, "\tOutput pin level inversion %s\n",
cntrl & CNTRL_IVO ? "yes" : "no");
v4l2_info(sd, "\tCarrier polarity: %s\n",
cntrl & CNTRL_CPL ? "space:burst mark:noburst"
: "space:noburst mark:burst");
if (cntrl & CNTRL_MOD) {
v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n",
clock_divider_to_carrier_freq(txclk));
v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n",
cduty + 1);
} else {
v4l2_info(sd, "\tMax pulse width: %u us, "
"%llu ns\n",
pulse_width_count_to_us(FIFO_RXTX, txclk),
pulse_width_count_to_ns(FIFO_RXTX, txclk));
}
v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n",
pulse_width_count_to_us(FIFO_RXTX, txclk),
pulse_width_count_to_ns(FIFO_RXTX, txclk));
v4l2_info(sd, "\tBusy: %s\n",
stats & STATS_TBY ? "yes" : "no");
v4l2_info(sd, "\tFIFO service requested: %s\n",
@ -1111,11 +1145,10 @@ static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = {
.g_register = cx23888_ir_g_register,
.s_register = cx23888_ir_s_register,
#endif
.interrupt_service_routine = cx23888_ir_irq_handler,
};
static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = {
.interrupt_service_routine = cx23888_ir_irq_handler,
.rx_read = cx23888_ir_rx_read,
.rx_g_parameters = cx23888_ir_rx_g_parameters,
.rx_s_parameters = cx23888_ir_rx_s_parameters,
@ -1131,7 +1164,7 @@ static const struct v4l2_subdev_ops cx23888_ir_controller_ops = {
};
static const struct v4l2_subdev_ir_parameters default_rx_params = {
.bytes_per_data_element = sizeof(u32),
.bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
.enable = false,
@ -1146,11 +1179,11 @@ static const struct v4l2_subdev_ir_parameters default_rx_params = {
.noise_filter_min_width = 333333, /* ns */
.carrier_range_lower = 35000,
.carrier_range_upper = 37000,
.invert = false,
.invert_level = false,
};
static const struct v4l2_subdev_ir_parameters default_tx_params = {
.bytes_per_data_element = sizeof(u32),
.bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
.enable = false,
@ -1160,7 +1193,8 @@ static const struct v4l2_subdev_ir_parameters default_tx_params = {
.modulation = true,
.carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
.duty_cycle = 25, /* 25 % - RC-5 carrier */
.invert = false,
.invert_level = false,
.invert_carrier_sense = false,
};
int cx23888_ir_probe(struct cx23885_dev *dev)

View File

@ -1,5 +1,5 @@
cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
cx25840-vbi.o
cx25840-vbi.o cx25840-ir.o
obj-$(CONFIG_VIDEO_CX25840) += cx25840.o

View File

@ -474,33 +474,10 @@ void cx25840_audio_set_path(struct i2c_client *client)
cx25840_and_or(client, 0x803, ~0x10, 0x10);
}
static int get_volume(struct i2c_client *client)
{
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
if (state->unmute_volume >= 0)
return state->unmute_volume;
/* Volume runs +18dB to -96dB in 1/2dB steps
* change to fit the msp3400 -114dB to +12dB range */
/* check PATH1_VOLUME */
vol = 228 - cx25840_read(client, 0x8d4);
vol = (vol / 2) + 23;
return vol << 9;
}
static void set_volume(struct i2c_client *client, int volume)
{
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
if (state->unmute_volume >= 0) {
state->unmute_volume = volume;
return;
}
/* Convert the volume to msp3400 values (0-127) */
vol = volume >> 9;
@ -517,52 +494,6 @@ static void set_volume(struct i2c_client *client, int volume)
cx25840_write(client, 0x8d4, 228 - (vol * 2));
}
static int get_bass(struct i2c_client *client)
{
/* bass is 49 steps +12dB to -12dB */
/* check PATH1_EQ_BASS_VOL */
int bass = cx25840_read(client, 0x8d9) & 0x3f;
bass = (((48 - bass) * 0xffff) + 47) / 48;
return bass;
}
static void set_bass(struct i2c_client *client, int bass)
{
/* PATH1_EQ_BASS_VOL */
cx25840_and_or(client, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
}
static int get_treble(struct i2c_client *client)
{
/* treble is 49 steps +12dB to -12dB */
/* check PATH1_EQ_TREBLE_VOL */
int treble = cx25840_read(client, 0x8db) & 0x3f;
treble = (((48 - treble) * 0xffff) + 47) / 48;
return treble;
}
static void set_treble(struct i2c_client *client, int treble)
{
/* PATH1_EQ_TREBLE_VOL */
cx25840_and_or(client, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
}
static int get_balance(struct i2c_client *client)
{
/* balance is 7 bit, 0 to -96dB */
/* check PATH1_BAL_LEVEL */
int balance = cx25840_read(client, 0x8d5) & 0x7f;
/* check PATH1_BAL_LEFT */
if ((cx25840_read(client, 0x8d5) & 0x80) == 0)
balance = 0x80 - balance;
else
balance = 0x80 + balance;
return balance << 8;
}
static void set_balance(struct i2c_client *client, int balance)
{
int bal = balance >> 8;
@ -579,31 +510,6 @@ static void set_balance(struct i2c_client *client, int balance)
}
}
static int get_mute(struct i2c_client *client)
{
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
return state->unmute_volume >= 0;
}
static void set_mute(struct i2c_client *client, int mute)
{
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
if (mute && state->unmute_volume == -1) {
int vol = get_volume(client);
set_volume(client, 0);
state->unmute_volume = vol;
}
else if (!mute && state->unmute_volume != -1) {
int vol = state->unmute_volume;
state->unmute_volume = -1;
set_volume(client, vol);
}
}
int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@ -624,25 +530,31 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
return retval;
}
int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = get_volume(client);
if (state->mute->val)
set_volume(client, 0);
else
set_volume(client, state->volume->val);
break;
case V4L2_CID_AUDIO_BASS:
ctrl->value = get_bass(client);
/* PATH1_EQ_BASS_VOL */
cx25840_and_or(client, 0x8d9, ~0x3f,
48 - (ctrl->val * 48 / 0xffff));
break;
case V4L2_CID_AUDIO_TREBLE:
ctrl->value = get_treble(client);
/* PATH1_EQ_TREBLE_VOL */
cx25840_and_or(client, 0x8db, ~0x3f,
48 - (ctrl->val * 48 / 0xffff));
break;
case V4L2_CID_AUDIO_BALANCE:
ctrl->value = get_balance(client);
break;
case V4L2_CID_AUDIO_MUTE:
ctrl->value = get_mute(client);
set_balance(client, ctrl->val);
break;
default:
return -EINVAL;
@ -650,28 +562,6 @@ int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
return 0;
}
int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
set_volume(client, ctrl->value);
break;
case V4L2_CID_AUDIO_BASS:
set_bass(client, ctrl->value);
break;
case V4L2_CID_AUDIO_TREBLE:
set_treble(client, ctrl->value);
break;
case V4L2_CID_AUDIO_BALANCE:
set_balance(client, ctrl->value);
break;
case V4L2_CID_AUDIO_MUTE:
set_mute(client, ctrl->value);
break;
default:
return -EINVAL;
}
return 0;
}
const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = {
.s_ctrl = cx25840_audio_s_ctrl,
};

View File

@ -15,6 +15,9 @@
*
* CX23885 support by Steven Toth <stoth@linuxtv.org>.
*
* CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are
* Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@ -48,6 +51,28 @@ MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver");
MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford");
MODULE_LICENSE("GPL");
#define CX25840_VID_INT_STAT_REG 0x410
#define CX25840_VID_INT_STAT_BITS 0x0000ffff
#define CX25840_VID_INT_MASK_BITS 0xffff0000
#define CX25840_VID_INT_MASK_SHFT 16
#define CX25840_VID_INT_MASK_REG 0x412
#define CX23885_AUD_MC_INT_MASK_REG 0x80c
#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000
#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff
#define CX23885_AUD_MC_INT_STAT_SHFT 16
#define CX25840_AUD_INT_CTRL_REG 0x812
#define CX25840_AUD_INT_STAT_REG 0x813
#define CX23885_PIN_CTRL_IRQ_REG 0x123
#define CX23885_PIN_CTRL_IRQ_IR_STAT 0x40
#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20
#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10
#define CX25840_IR_STATS_REG 0x210
#define CX25840_IR_IRQEN_REG 0x214
static int cx25840_debug;
module_param_named(debug,cx25840_debug, int, 0644);
@ -80,33 +105,53 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value)
u8 cx25840_read(struct i2c_client * client, u16 addr)
{
u8 buffer[2];
buffer[0] = addr >> 8;
buffer[1] = addr & 0xff;
struct i2c_msg msgs[2];
u8 tx_buf[2], rx_buf[1];
if (i2c_master_send(client, buffer, 2) < 2)
/* Write register address */
tx_buf[0] = addr >> 8;
tx_buf[1] = addr & 0xff;
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = 2;
msgs[0].buf = (char *) tx_buf;
/* Read data from register */
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = 1;
msgs[1].buf = (char *) rx_buf;
if (i2c_transfer(client->adapter, msgs, 2) < 2)
return 0;
if (i2c_master_recv(client, buffer, 1) < 1)
return 0;
return buffer[0];
return rx_buf[0];
}
u32 cx25840_read4(struct i2c_client * client, u16 addr)
{
u8 buffer[4];
buffer[0] = addr >> 8;
buffer[1] = addr & 0xff;
struct i2c_msg msgs[2];
u8 tx_buf[2], rx_buf[4];
if (i2c_master_send(client, buffer, 2) < 2)
/* Write register address */
tx_buf[0] = addr >> 8;
tx_buf[1] = addr & 0xff;
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = 2;
msgs[0].buf = (char *) tx_buf;
/* Read data from registers */
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = 4;
msgs[1].buf = (char *) rx_buf;
if (i2c_transfer(client->adapter, msgs, 2) < 2)
return 0;
if (i2c_master_recv(client, buffer, 4) < 4)
return 0;
return (buffer[3] << 24) | (buffer[2] << 16) |
(buffer[1] << 8) | buffer[0];
return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) |
rx_buf[0];
}
int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
@ -117,6 +162,14 @@ int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
or_value);
}
int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
u32 or_value)
{
return cx25840_write4(client, addr,
(cx25840_read4(client, addr) & and_mask) |
or_value);
}
/* ----------------------------------------------------------------------- */
static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
@ -124,6 +177,158 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
/* ----------------------------------------------------------------------- */
static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
struct v4l2_subdev_io_pin_config *p)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
u32 pin_ctrl;
u8 gpio_oe, gpio_data, strength;
pin_ctrl = cx25840_read4(client, 0x120);
gpio_oe = cx25840_read(client, 0x160);
gpio_data = cx25840_read(client, 0x164);
for (i = 0; i < n; i++) {
strength = p[i].strength;
if (strength > CX25840_PIN_DRIVE_FAST)
strength = CX25840_PIN_DRIVE_FAST;
switch (p[i].pin) {
case CX23885_PIN_IRQ_N_GPIO16:
if (p[i].function != CX23885_PAD_IRQ_N) {
/* GPIO16 */
pin_ctrl &= ~(0x1 << 25);
} else {
/* IRQ_N */
if (p[i].flags &
(V4L2_SUBDEV_IO_PIN_DISABLE |
V4L2_SUBDEV_IO_PIN_INPUT)) {
pin_ctrl &= ~(0x1 << 25);
} else {
pin_ctrl |= (0x1 << 25);
}
if (p[i].flags &
V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) {
pin_ctrl &= ~(0x1 << 24);
} else {
pin_ctrl |= (0x1 << 24);
}
}
break;
case CX23885_PIN_IR_RX_GPIO19:
if (p[i].function != CX23885_PAD_GPIO19) {
/* IR_RX */
gpio_oe |= (0x1 << 0);
pin_ctrl &= ~(0x3 << 18);
pin_ctrl |= (strength << 18);
} else {
/* GPIO19 */
gpio_oe &= ~(0x1 << 0);
if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
gpio_data &= ~(0x1 << 0);
gpio_data |= ((p[i].value & 0x1) << 0);
}
pin_ctrl &= ~(0x3 << 12);
pin_ctrl |= (strength << 12);
}
break;
case CX23885_PIN_IR_TX_GPIO20:
if (p[i].function != CX23885_PAD_GPIO20) {
/* IR_TX */
gpio_oe |= (0x1 << 1);
if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE)
pin_ctrl &= ~(0x1 << 10);
else
pin_ctrl |= (0x1 << 10);
pin_ctrl &= ~(0x3 << 18);
pin_ctrl |= (strength << 18);
} else {
/* GPIO20 */
gpio_oe &= ~(0x1 << 1);
if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
gpio_data &= ~(0x1 << 1);
gpio_data |= ((p[i].value & 0x1) << 1);
}
pin_ctrl &= ~(0x3 << 12);
pin_ctrl |= (strength << 12);
}
break;
case CX23885_PIN_I2S_SDAT_GPIO21:
if (p[i].function != CX23885_PAD_GPIO21) {
/* I2S_SDAT */
/* TODO: Input or Output config */
gpio_oe |= (0x1 << 2);
pin_ctrl &= ~(0x3 << 22);
pin_ctrl |= (strength << 22);
} else {
/* GPIO21 */
gpio_oe &= ~(0x1 << 2);
if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
gpio_data &= ~(0x1 << 2);
gpio_data |= ((p[i].value & 0x1) << 2);
}
pin_ctrl &= ~(0x3 << 12);
pin_ctrl |= (strength << 12);
}
break;
case CX23885_PIN_I2S_WCLK_GPIO22:
if (p[i].function != CX23885_PAD_GPIO22) {
/* I2S_WCLK */
/* TODO: Input or Output config */
gpio_oe |= (0x1 << 3);
pin_ctrl &= ~(0x3 << 22);
pin_ctrl |= (strength << 22);
} else {
/* GPIO22 */
gpio_oe &= ~(0x1 << 3);
if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
gpio_data &= ~(0x1 << 3);
gpio_data |= ((p[i].value & 0x1) << 3);
}
pin_ctrl &= ~(0x3 << 12);
pin_ctrl |= (strength << 12);
}
break;
case CX23885_PIN_I2S_BCLK_GPIO23:
if (p[i].function != CX23885_PAD_GPIO23) {
/* I2S_BCLK */
/* TODO: Input or Output config */
gpio_oe |= (0x1 << 4);
pin_ctrl &= ~(0x3 << 22);
pin_ctrl |= (strength << 22);
} else {
/* GPIO23 */
gpio_oe &= ~(0x1 << 4);
if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
gpio_data &= ~(0x1 << 4);
gpio_data |= ((p[i].value & 0x1) << 4);
}
pin_ctrl &= ~(0x3 << 12);
pin_ctrl |= (strength << 12);
}
break;
}
}
cx25840_write(client, 0x164, gpio_data);
cx25840_write(client, 0x160, gpio_oe);
cx25840_write4(client, 0x120, pin_ctrl);
return 0;
}
static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
struct v4l2_subdev_io_pin_config *pincfg)
{
struct cx25840_state *state = to_state(sd);
if (is_cx2388x(state))
return cx23885_s_io_pin_config(sd, n, pincfg);
return 0;
}
/* ----------------------------------------------------------------------- */
static void init_dll1(struct i2c_client *client)
{
/* This is the Hauppauge sequence used to
@ -420,6 +625,13 @@ static void cx23885_initialize(struct i2c_client *client)
/* start microcontroller */
cx25840_and_or(client, 0x803, ~0x10, 0x10);
/* Disable and clear video interrupts - we don't use them */
cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff);
/* Disable and clear audio interrupts - we don't use them */
cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff);
cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff);
}
/* ----------------------------------------------------------------------- */
@ -909,102 +1121,29 @@ static int set_v4lstd(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct cx25840_state *state = to_state(sd);
struct v4l2_subdev *sd = to_sd(ctrl);
struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case CX25840_CID_ENABLE_PVR150_WORKAROUND:
state->pvr150_workaround = ctrl->value;
set_input(client, state->vid_input, state->aud_input);
break;
case V4L2_CID_BRIGHTNESS:
if (ctrl->value < 0 || ctrl->value > 255) {
v4l_err(client, "invalid brightness setting %d\n",
ctrl->value);
return -ERANGE;
}
cx25840_write(client, 0x414, ctrl->value - 128);
cx25840_write(client, 0x414, ctrl->val - 128);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value < 0 || ctrl->value > 127) {
v4l_err(client, "invalid contrast setting %d\n",
ctrl->value);
return -ERANGE;
}
cx25840_write(client, 0x415, ctrl->value << 1);
cx25840_write(client, 0x415, ctrl->val << 1);
break;
case V4L2_CID_SATURATION:
if (ctrl->value < 0 || ctrl->value > 127) {
v4l_err(client, "invalid saturation setting %d\n",
ctrl->value);
return -ERANGE;
}
cx25840_write(client, 0x420, ctrl->value << 1);
cx25840_write(client, 0x421, ctrl->value << 1);
cx25840_write(client, 0x420, ctrl->val << 1);
cx25840_write(client, 0x421, ctrl->val << 1);
break;
case V4L2_CID_HUE:
if (ctrl->value < -128 || ctrl->value > 127) {
v4l_err(client, "invalid hue setting %d\n", ctrl->value);
return -ERANGE;
}
cx25840_write(client, 0x422, ctrl->value);
cx25840_write(client, 0x422, ctrl->val);
break;
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_MUTE:
if (is_cx2583x(state))
return -EINVAL;
return cx25840_audio_s_ctrl(sd, ctrl);
default:
return -EINVAL;
}
return 0;
}
static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case CX25840_CID_ENABLE_PVR150_WORKAROUND:
ctrl->value = state->pvr150_workaround;
break;
case V4L2_CID_BRIGHTNESS:
ctrl->value = (s8)cx25840_read(client, 0x414) + 128;
break;
case V4L2_CID_CONTRAST:
ctrl->value = cx25840_read(client, 0x415) >> 1;
break;
case V4L2_CID_SATURATION:
ctrl->value = cx25840_read(client, 0x420) >> 1;
break;
case V4L2_CID_HUE:
ctrl->value = (s8)cx25840_read(client, 0x422);
break;
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_MUTE:
if (is_cx2583x(state))
return -EINVAL;
return cx25840_audio_g_ctrl(sd, ctrl);
default:
return -EINVAL;
}
@ -1163,8 +1302,6 @@ static void log_audio_status(struct i2c_client *client)
default: p = "not defined";
}
v4l_info(client, "Detected audio standard: %s\n", p);
v4l_info(client, "Audio muted: %s\n",
(state->unmute_volume >= 0) ? "yes" : "no");
v4l_info(client, "Audio microcontroller: %s\n",
(download_ctl & 0x10) ?
((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
@ -1381,40 +1518,6 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
struct cx25840_state *state = to_state(sd);
switch (qc->id) {
case V4L2_CID_BRIGHTNESS:
return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
case V4L2_CID_CONTRAST:
case V4L2_CID_SATURATION:
return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
case V4L2_CID_HUE:
return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
default:
break;
}
if (is_cx2583x(state))
return -EINVAL;
switch (qc->id) {
case V4L2_CID_AUDIO_VOLUME:
return v4l2_ctrl_query_fill(qc, 0, 65535,
65535 / 100, state->default_volume);
case V4L2_CID_AUDIO_MUTE:
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
default:
return -EINVAL;
}
return -EINVAL;
}
static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct cx25840_state *state = to_state(sd);
@ -1576,24 +1679,134 @@ static int cx25840_log_status(struct v4l2_subdev *sd)
log_video_status(client);
if (!is_cx2583x(state))
log_audio_status(client);
cx25840_ir_log_status(sd);
v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
static int cx25840_s_config(struct v4l2_subdev *sd, int irq, void *platform_data)
{
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (platform_data) {
struct cx25840_platform_data *pdata = platform_data;
state->pvr150_workaround = pdata->pvr150_workaround;
set_input(client, state->vid_input, state->aud_input);
}
return 0;
}
static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status,
bool *handled)
{
struct cx25840_state *state = to_state(sd);
struct i2c_client *c = v4l2_get_subdevdata(sd);
u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en;
u32 vid_stat, aud_mc_stat;
bool block_handled;
int ret = 0;
irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n",
irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ",
irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ",
irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " ");
if ((is_cx23885(state) || is_cx23887(state))) {
ir_stat = cx25840_read(c, CX25840_IR_STATS_REG);
ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG);
v4l_dbg(2, cx25840_debug, c,
"AV Core ir IRQ status: %#04x disables: %#04x\n",
ir_stat, ir_en);
if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) {
block_handled = false;
ret = cx25840_ir_irq_handler(sd,
status, &block_handled);
if (block_handled)
*handled = true;
}
}
aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG);
aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG);
v4l_dbg(2, cx25840_debug, c,
"AV Core audio IRQ status: %#04x disables: %#04x\n",
aud_stat, aud_en);
aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG);
v4l_dbg(2, cx25840_debug, c,
"AV Core audio MC IRQ status: %#06x enables: %#06x\n",
aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT,
aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS);
if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) {
if (aud_stat) {
cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat);
*handled = true;
}
}
vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG);
v4l_dbg(2, cx25840_debug, c,
"AV Core video IRQ status: %#06x disables: %#06x\n",
vid_stat & CX25840_VID_INT_STAT_BITS,
vid_stat >> CX25840_VID_INT_MASK_SHFT);
if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) {
if (vid_stat & CX25840_VID_INT_STAT_BITS) {
cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat);
*handled = true;
}
}
irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n",
irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ",
irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ",
irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " ");
return ret;
}
static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status,
bool *handled)
{
struct cx25840_state *state = to_state(sd);
*handled = false;
/* Only support the CX2388[578] AV Core for now */
if (is_cx2388x(state))
return cx23885_irq_handler(sd, status, handled);
return -ENODEV;
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops cx25840_ctrl_ops = {
.s_ctrl = cx25840_s_ctrl,
};
static const struct v4l2_subdev_core_ops cx25840_core_ops = {
.log_status = cx25840_log_status,
.s_config = cx25840_s_config,
.g_chip_ident = cx25840_g_chip_ident,
.g_ctrl = cx25840_g_ctrl,
.s_ctrl = cx25840_s_ctrl,
.queryctrl = cx25840_queryctrl,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
.s_std = cx25840_s_std,
.reset = cx25840_reset,
.load_fw = cx25840_load_fw,
.s_io_pin_config = common_s_io_pin_config,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = cx25840_g_register,
.s_register = cx25840_s_register,
#endif
.interrupt_service_routine = cx25840_irq_handler,
};
static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
@ -1628,6 +1841,7 @@ static const struct v4l2_subdev_ops cx25840_ops = {
.audio = &cx25840_audio_ops,
.video = &cx25840_video_ops,
.vbi = &cx25840_vbi_ops,
.ir = &cx25840_ir_ops,
};
/* ----------------------------------------------------------------------- */
@ -1675,6 +1889,7 @@ static int cx25840_probe(struct i2c_client *client,
{
struct cx25840_state *state;
struct v4l2_subdev *sd;
int default_volume;
u32 id = V4L2_IDENT_NONE;
u16 device_id;
@ -1718,6 +1933,7 @@ static int cx25840_probe(struct i2c_client *client,
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
switch (id) {
case V4L2_IDENT_CX23885_AV:
v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n",
@ -1762,22 +1978,62 @@ static int cx25840_probe(struct i2c_client *client,
state->audclk_freq = 48000;
state->pvr150_workaround = 0;
state->audmode = V4L2_TUNER_MODE_LANG1;
state->unmute_volume = -1;
state->default_volume = 228 - cx25840_read(client, 0x8d4);
state->default_volume = ((state->default_volume / 2) + 23) << 9;
state->vbi_line_offset = 8;
state->id = id;
state->rev = device_id;
v4l2_ctrl_handler_init(&state->hdl, 9);
v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
V4L2_CID_CONTRAST, 0, 127, 1, 64);
v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
V4L2_CID_SATURATION, 0, 127, 1, 64);
v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
if (!is_cx2583x(state)) {
default_volume = 228 - cx25840_read(client, 0x8d4);
default_volume = ((default_volume / 2) + 23) << 9;
state->volume = v4l2_ctrl_new_std(&state->hdl,
&cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME,
0, 65335, 65535 / 100, default_volume);
state->mute = v4l2_ctrl_new_std(&state->hdl,
&cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE,
0, 1, 1, 0);
v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
V4L2_CID_AUDIO_BALANCE,
0, 65535, 65535 / 100, 32768);
v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
V4L2_CID_AUDIO_BASS,
0, 65535, 65535 / 100, 32768);
v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
V4L2_CID_AUDIO_TREBLE,
0, 65535, 65535 / 100, 32768);
}
sd->ctrl_handler = &state->hdl;
if (state->hdl.error) {
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return err;
}
v4l2_ctrl_cluster(2, &state->volume);
v4l2_ctrl_handler_setup(&state->hdl);
cx25840_ir_probe(sd);
return 0;
}
static int cx25840_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct cx25840_state *state = to_state(sd);
cx25840_ir_remove(sd);
v4l2_device_unregister_subdev(sd);
kfree(to_state(sd));
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return 0;
}

View File

@ -24,19 +24,20 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <linux/i2c.h>
/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is
present in Hauppauge PVR-150 (and possibly PVR-500) cards that have
certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The
audio autodetect fails on some channels for these models and the workaround
is to select the audio standard explicitly. Many thanks to Hauppauge for
providing this information. */
#define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0)
struct cx25840_ir_state;
struct cx25840_state {
struct i2c_client *c;
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
struct {
/* volume cluster */
struct v4l2_ctrl *volume;
struct v4l2_ctrl *mute;
};
int pvr150_workaround;
int radio;
v4l2_std_id std;
@ -44,14 +45,13 @@ struct cx25840_state {
enum cx25840_audio_input aud_input;
u32 audclk_freq;
int audmode;
int unmute_volume; /* -1 if not muted */
int default_volume;
int vbi_line_offset;
u32 id;
u32 rev;
int is_initialized;
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
struct work_struct fw_work; /* work entry for fw load */
struct cx25840_ir_state *ir_state;
};
static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
@ -59,6 +59,11 @@ static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct cx25840_state, sd);
}
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd;
}
static inline bool is_cx2583x(struct cx25840_state *state)
{
return state->id == V4L2_IDENT_CX25836 ||
@ -77,6 +82,21 @@ static inline bool is_cx2388x(struct cx25840_state *state)
state->id == V4L2_IDENT_CX23888_AV;
}
static inline bool is_cx23885(struct cx25840_state *state)
{
return state->id == V4L2_IDENT_CX23885_AV;
}
static inline bool is_cx23887(struct cx25840_state *state)
{
return state->id == V4L2_IDENT_CX23887_AV;
}
static inline bool is_cx23888(struct cx25840_state *state)
{
return state->id == V4L2_IDENT_CX23888_AV;
}
/* ----------------------------------------------------------------------- */
/* cx25850-core.c */
int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
@ -84,6 +104,8 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value);
u8 cx25840_read(struct i2c_client *client, u16 addr);
u32 cx25840_read4(struct i2c_client *client, u16 addr);
int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value);
int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
u32 or_value);
void cx25840_std_setup(struct i2c_client *client);
/* ----------------------------------------------------------------------- */
@ -94,8 +116,8 @@ int cx25840_loadfw(struct i2c_client *client);
/* cx25850-audio.c */
void cx25840_audio_set_path(struct i2c_client *client);
int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops;
/* ----------------------------------------------------------------------- */
/* cx25850-vbi.c */
@ -104,4 +126,12 @@ int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *
int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi);
/* ----------------------------------------------------------------------- */
/* cx25850-ir.c */
extern const struct v4l2_subdev_ir_ops cx25840_ir_ops;
int cx25840_ir_log_status(struct v4l2_subdev *sd);
int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled);
int cx25840_ir_probe(struct v4l2_subdev *sd);
int cx25840_ir_remove(struct v4l2_subdev *sd);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -55,7 +55,7 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("GSPCA USB Camera Driver");
MODULE_LICENSE("GPL");
#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 9, 0)
#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 10, 0)
#ifdef GSPCA_DEBUG
int gspca_debug = D_ERR | D_PROBE;
@ -440,10 +440,15 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
frame->v4l2_buf.sequence = ++gspca_dev->sequence;
gspca_dev->image = frame->data;
gspca_dev->image_len = 0;
} else if (gspca_dev->last_packet_type == DISCARD_PACKET) {
if (packet_type == LAST_PACKET)
gspca_dev->last_packet_type = packet_type;
return;
} else {
switch (gspca_dev->last_packet_type) {
case DISCARD_PACKET:
if (packet_type == LAST_PACKET)
gspca_dev->last_packet_type = packet_type;
return;
case LAST_PACKET:
return;
}
}
/* append the packet to the frame buffer */
@ -454,6 +459,12 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
gspca_dev->frsz);
packet_type = DISCARD_PACKET;
} else {
/* !! image is NULL only when last pkt is LAST or DISCARD
if (gspca_dev->image == NULL) {
err("gspca_frame_add() image == NULL");
return;
}
*/
memcpy(gspca_dev->image + gspca_dev->image_len,
data, len);
gspca_dev->image_len += len;

View File

@ -66,7 +66,11 @@ struct sd {
#define BRIDGE_SN9C110 2
#define BRIDGE_SN9C120 3
u8 sensor; /* Type of image sensor chip */
enum {
u8 i2c_addr;
u8 jpeg_hdr[JPEG_HDR_SZ];
};
enum sensors {
SENSOR_ADCM1700,
SENSOR_GC0307,
SENSOR_HV7131R,
@ -81,10 +85,6 @@ enum {
SENSOR_PO2030N,
SENSOR_SOI768,
SENSOR_SP80708,
} sensors;
u8 i2c_addr;
u8 jpeg_hdr[JPEG_HDR_SZ];
};
/* V4L2 controls supported by the driver */

View File

@ -23,7 +23,6 @@
#define MODULE_NAME "sq930x"
#include "gspca.h"
#include "jpeg.h"
MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
"Gerard Klaver <gerard at gkall dot hobby dot nl\n"
@ -31,8 +30,6 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");
MODULE_LICENSE("GPL");
#define BULK_TRANSFER_LEN 5128
/* Structure to hold all of our device specific stuff */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
@ -40,28 +37,20 @@ struct sd {
u16 expo;
u8 gain;
u8 quality; /* webcam quality 0..3 */
#define QUALITY_DEF 1
u8 gpio[2];
u8 eof_len;
u8 do_ctrl;
u8 gpio[2];
u8 sensor;
enum {
SENSOR_ICX098BQ,
SENSOR_LZ24BP,
SENSOR_MI0360,
SENSOR_MT9V111,
SENSOR_OV7660,
SENSOR_OV9630,
} sensors;
u8 type;
#define Generic 0
#define Creative_live_motion 1
u8 jpeg_hdr[JPEG_HDR_SZ];
};
enum sensors {
SENSOR_ICX098BQ,
SENSOR_LZ24BP,
SENSOR_MI0360,
SENSOR_MT9V111, /* = MI360SOC */
SENSOR_OV7660,
SENSOR_OV9630,
};
static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val);
@ -78,7 +67,7 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0x0001,
.maximum = 0x0fff,
.step = 1,
#define EXPO_DEF 0x027d
#define EXPO_DEF 0x0356
.default_value = EXPO_DEF,
},
.set = sd_setexpo,
@ -92,7 +81,7 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0x01,
.maximum = 0xff,
.step = 1,
#define GAIN_DEF 0x61
#define GAIN_DEF 0x8d
.default_value = GAIN_DEF,
},
.set = sd_setgain,
@ -101,30 +90,18 @@ static const struct ctrl sd_ctrls[] = {
};
static struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 5 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 0},
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
{320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 4 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 1},
{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.sizeimage = 320 * 240,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0},
{640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
.bytesperline = 640,
.sizeimage = 640 * 480 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 2},
.sizeimage = 640 * 480,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 1},
};
/* JPEG quality indexed by webcam quality */
#define QUAL_0 90
#define QUAL_1 85
#define QUAL_2 75
#define QUAL_3 70
static const u8 quality_tb[4] = { QUAL_0, QUAL_1, QUAL_2, QUAL_3 };
/* sq930x registers */
#define SQ930_CTRL_UCBUS_IO 0x0001
#define SQ930_CTRL_I2C_IO 0x0002
@ -302,7 +279,7 @@ static const struct i2c_write_cmd mt9v111_init_0[] = {
{0x01, 0x0001}, /* select IFP/SOC registers */
{0x06, 0x300c}, /* operating mode control */
{0x08, 0xcc00}, /* output format control (RGB) */
{0x01, 0x0004}, /* select core registers */
{0x01, 0x0004}, /* select sensor core registers */
};
static const struct i2c_write_cmd mt9v111_init_1[] = {
{0x03, 0x01e5}, /* window height */
@ -330,7 +307,8 @@ static const struct i2c_write_cmd mt9v111_init_3[] = {
{0x62, 0x0405},
};
static const struct i2c_write_cmd mt9v111_init_4[] = {
{0x05, 0x00ce}, /* horizontal blanking */
/* {0x05, 0x00ce}, */
{0x05, 0x005d}, /* horizontal blanking */
};
static const struct ucbus_write_cmd ov7660_start_0[] = {
@ -343,78 +321,58 @@ static const struct ucbus_write_cmd ov9630_start_0[] = {
{0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}
};
/* start parameters indexed by [sensor][mode] */
static const struct cap_s {
u8 cc_sizeid;
u8 cc_bytes[32];
} capconfig[4][3] = {
} capconfig[4][2] = {
[SENSOR_ICX098BQ] = {
{0, /* JPEG, 160x120 */
{2, /* Bayer 320x240 */
{0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
{4, /* Bayer 640x480 */
{0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x02, 0x8b, 0x00, 0x8b, 0x00, 0x41, 0x01, 0x41,
0x01, 0x41, 0x01, 0x05, 0x40, 0x01, 0xf0, 0x00} },
{2, /* JPEG, 320x240 */
{0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
{4, /* JPEG, 640x480 */
{0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xf0,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
[SENSOR_LZ24BP] = {
{0, /* JPEG, 160x120 */
{0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x02, 0x8b, 0x00, 0x8b, 0x00, 0x41, 0x01, 0x41,
0x01, 0x41, 0x01, 0x05, 0x40, 0x01, 0xf0, 0x00} },
{2, /* JPEG, 320x240 */
{2, /* Bayer 320x240 */
{0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
{4, /* Bayer 640x480 */
{0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
{4, /* JPEG, 640x480 */
{0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xf0,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
[SENSOR_MI0360] = {
{0, /* JPEG, 160x120 */
{0x05, 0x3d, 0x20, 0x0b, 0x00, 0xbd, 0x02, 0x0b,
0x02, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x02, 0x01, 0x01, 0x01, 0x01, 0x9f, 0x00, 0x9f,
0x00, 0x9f, 0x01, 0x05, 0xa0, 0x00, 0x80, 0x00} },
{2, /* JPEG, 320x240 */
{2, /* Bayer 320x240 */
{0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
{4, /* Bayer 640x480 */
{0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
/*fixme 03 e3 */
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
{4, /* JPEG, 640x480 */
{0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe3,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
[SENSOR_MT9V111] = {
{0, /* JPEG, 160x120 */
{0x05, 0x3d, 0x20, 0x0b, 0x00, 0xbd, 0x02, 0x0b,
0x02, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x02, 0x01, 0x01, 0x01, 0x01, 0x9f, 0x00, 0x9f,
0x00, 0x9f, 0x01, 0x05, 0xa0, 0x00, 0x80, 0x00} },
{2, /* JPEG, 320x240 */
{0x01, 0x02, 0x20, 0x03, 0x20, 0x82, 0x02, 0xe3,
{2, /* Bayer 320x240 */
{0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
{4, /* Bayer 640x480 */
{0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
{4, /* JPEG, 640x480 */
{0x01, 0x02, 0x20, 0x03, 0x20, 0x82, 0x02, 0xe3,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
};
@ -864,7 +822,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
buf[i++] = 0x35; /* reg = global gain */
buf[i++] = 0x00; /* val H */
buf[i++] = sensor->i2c_dum;
buf[i++] = sd->gain; /* val L */
buf[i++] = 0x80 + sd->gain / 2; /* val L */
buf[i++] = 0x00;
buf[i++] = 0x00;
buf[i++] = 0x00;
@ -889,10 +847,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->nmodes = ARRAY_SIZE(vga_mode);
cam->bulk = 1;
cam->bulk_size = BULK_TRANSFER_LEN;
/* cam->bulk_nurbs = 2; fixme: if no setexpo sync */
sd->quality = QUALITY_DEF;
sd->gain = GAIN_DEF;
sd->expo = EXPO_DEF;
@ -945,13 +900,10 @@ static int sd_init(struct gspca_dev *gspca_dev)
if (sd->sensor == SENSOR_MI0360) {
/* no sensor probe for icam tracer */
if (gspca_dev->usb_buf[5] == 0xf6) { /* if CMOS */
if (gspca_dev->usb_buf[5] == 0xf6) /* if CMOS */
sd->sensor = SENSOR_ICX098BQ;
gspca_dev->cam.cam_mode = &vga_mode[1];
gspca_dev->cam.nmodes = 1; /* only 320x240 */
} else {
else
cmos_probe(gspca_dev);
}
}
PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name);
@ -960,51 +912,24 @@ static int sd_init(struct gspca_dev *gspca_dev)
return gspca_dev->usb_err;
}
/* special function to create the quantization tables of the JPEG header */
static void sd_jpeg_set_qual(u8 *jpeg_hdr,
int quality)
{
int i, sc1, sc2;
quality = quality_tb[quality]; /* convert to JPEG quality */
/*
* approximative qualities for Y and U/V:
* quant = 0:94%/91% 1:91%/87% 2:82%/73% 3:69%/56%
* should have:
* quant = 0:94%/91% 1:91%/87.5% 2:81.5%/72% 3:69%/54.5%
*/
sc1 = 200 - quality * 2;
quality = quality * 7 / 5 - 40; /* UV quality */
sc2 = 200 - quality * 2;
for (i = 0; i < 64; i++) {
jpeg_hdr[JPEG_QT0_OFFSET + i] =
(jpeg_head[JPEG_QT0_OFFSET + i] * sc1 + 50) / 100;
jpeg_hdr[JPEG_QT1_OFFSET + i] =
(jpeg_head[JPEG_QT1_OFFSET + i] * sc2 + 50) / 100;
}
}
/* send the start/stop commands to the webcam */
static void send_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
const struct cap_s *cap;
int mode, quality;
int mode;
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
cap = &capconfig[sd->sensor][mode];
quality = sd->quality;
reg_wb(gspca_dev, (quality << 12)
| 0x0a00 /* 900 for Bayer */
| SQ930_CTRL_CAP_START,
0x0500 /* a00 for Bayer */
| cap->cc_sizeid,
reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START,
0x0a00 | cap->cc_sizeid,
cap->cc_bytes, 32);
};
}
static void send_stop(struct gspca_dev *gspca_dev)
{
reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
};
}
/* function called at start time before URB creation */
static int sd_isoc_init(struct gspca_dev *gspca_dev)
@ -1013,6 +938,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev)
gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */
sd->do_ctrl = 0;
gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8;
return 0;
}
@ -1022,11 +948,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
int mode;
/* initialize the JPEG header */
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x21); /* JPEG 422 */
sd_jpeg_set_qual(sd->jpeg_hdr, sd->quality);
bridge_init(sd);
global_init(sd, 0);
msleep(100);
@ -1071,7 +992,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
ARRAY_SIZE(lz24bp_start_2),
6);
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
lz24bp_ppl(sd, mode == 2 ? 0x0564 : 0x0310);
lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310);
msleep(10);
break;
case SENSOR_MI0360:
@ -1095,7 +1016,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* 1st start */
send_start(gspca_dev);
msleep(60);
reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);
send_stop(gspca_dev);
i2c_write(sd,
mi0360_start_4, ARRAY_SIZE(mi0360_start_4));
@ -1113,7 +1034,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
ARRAY_SIZE(mt9v111_init_2));
ucbus_write(gspca_dev, mt9v111_start_1,
ARRAY_SIZE(mt9v111_start_1),
8);
5);
i2c_write(sd, mt9v111_init_3,
ARRAY_SIZE(mt9v111_init_3));
i2c_write(sd, mt9v111_init_4,
@ -1125,8 +1046,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
out:
msleep(1000);
sd->eof_len = 0; /* init packet scan */
if (sd->sensor == SENSOR_MT9V111)
gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);
@ -1166,94 +1085,17 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
msleep(100);
}
/* move a packet adding 0x00 after 0xff */
static void add_packet(struct gspca_dev *gspca_dev,
u8 *data,
int len)
{
int i;
i = 0;
do {
if (data[i] == 0xff) {
gspca_frame_add(gspca_dev, INTER_PACKET,
data, i + 1);
len -= i;
data += i;
*data = 0x00;
i = 0;
}
} while (++i < len);
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
/* end a frame and start a new one */
static void eof_sof(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
static const u8 ffd9[] = {0xff, 0xd9};
/* if control set, stop bulk transfer */
if (sd->do_ctrl
&& gspca_dev->last_packet_type == INTER_PACKET)
gspca_dev->cam.bulk_nurbs = 0;
gspca_frame_add(gspca_dev, LAST_PACKET,
ffd9, 2);
gspca_frame_add(gspca_dev, FIRST_PACKET,
sd->jpeg_hdr, JPEG_HDR_SZ);
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
u8 *p;
int l;
len -= 8; /* ignore last 8 bytes (00 00 55 aa 55 aa 00 00) */
/*
* the end/start of frame is indicated by
* 0x00 * 16 - 0xab * 8
* aligned on 8 bytes boundary
*/
if (sd->eof_len != 0) { /* if 'abababab' in previous pkt */
if (*((u32 *) data) == 0xabababab) {
/*fixme: should remove previous 0000ababab*/
eof_sof(gspca_dev);
data += 4;
len -= 4;
}
sd->eof_len = 0;
}
p = data;
l = len;
for (;;) {
if (*((u32 *) p) == 0xabababab) {
if (l < 8) { /* (may be 4 only) */
sd->eof_len = 1;
break;
}
if (*((u32 *) p + 1) == 0xabababab) {
add_packet(gspca_dev, data, p - data - 16);
/* remove previous zeros */
eof_sof(gspca_dev);
p += 8;
l -= 8;
if (l <= 0)
return;
len = l;
data = p;
continue;
}
}
p += 4;
l -= 4;
if (l <= 0)
break;
}
add_packet(gspca_dev, data, len);
if (sd->do_ctrl)
gspca_dev->cam.bulk_nurbs = 0;
gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8);
gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
}
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
@ -1291,45 +1133,6 @@ static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
struct v4l2_jpegcompression *jcomp)
{
struct sd *sd = (struct sd *) gspca_dev;
int quality;
if (jcomp->quality >= (QUAL_0 + QUAL_1) / 2)
quality = 0;
else if (jcomp->quality >= (QUAL_1 + QUAL_2) / 2)
quality = 1;
else if (jcomp->quality >= (QUAL_2 + QUAL_3) / 2)
quality = 2;
else
quality = 3;
if (quality != sd->quality) {
sd->quality = quality;
if (gspca_dev->streaming) {
send_stop(gspca_dev);
sd_jpeg_set_qual(sd->jpeg_hdr, sd->quality);
msleep(70);
send_start(gspca_dev);
}
}
return gspca_dev->usb_err;
}
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
struct v4l2_jpegcompression *jcomp)
{
struct sd *sd = (struct sd *) gspca_dev;
memset(jcomp, 0, sizeof *jcomp);
jcomp->quality = quality_tb[sd->quality];
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
| V4L2_JPEG_MARKER_DQT;
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
@ -1342,8 +1145,6 @@ static const struct sd_desc sd_desc = {
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_dq_callback,
.get_jcomp = sd_get_jcomp,
.set_jcomp = sd_set_jcomp,
};
/* Table of supported USB devices */

View File

@ -55,12 +55,12 @@ struct sd {
u8 effect;
u8 sensor;
enum {
};
enum sensors {
SENSOR_OM6802,
SENSOR_OTHER,
SENSOR_TAS5130A,
SENSOR_LT168G, /* must verify if this is the actual model */
} sensors;
};
/* V4L2 controls supported by the driver */

View File

@ -39,6 +39,10 @@ struct sd {
u8 vflip;
u8 lightfreq;
s8 sharpness;
u16 exposure;
u8 gain;
u8 autogain;
u8 backlight;
u8 image_offset;
@ -77,6 +81,14 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val);
static const struct ctrl sd_ctrls[] = {
#define BRIGHTNESS_IDX 0
@ -185,6 +197,66 @@ static const struct ctrl sd_ctrls[] = {
.set = sd_setsharpness,
.get = sd_getsharpness,
},
#define GAIN_IDX 7
{
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Gain",
.minimum = 0,
.maximum = 78,
.step = 1,
#define GAIN_DEF 0
.default_value = GAIN_DEF,
},
.set = sd_setgain,
.get = sd_getgain,
},
#define EXPOSURE_IDX 8
{
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Exposure",
#define EXPOSURE_DEF 450
.minimum = 0,
.maximum = 4095,
.step = 1,
.default_value = EXPOSURE_DEF,
},
.set = sd_setexposure,
.get = sd_getexposure,
},
#define AUTOGAIN_IDX 9
{
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Automatic Gain and Exposure",
.minimum = 0,
.maximum = 1,
.step = 1,
#define AUTOGAIN_DEF 1
.default_value = AUTOGAIN_DEF,
},
.set = sd_setautogain,
.get = sd_getautogain,
},
#define BACKLIGHT_IDX 10
{
{
.id = V4L2_CID_BACKLIGHT_COMPENSATION,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Backlight Compensation",
.minimum = 0,
.maximum = 15,
.step = 1,
#define BACKLIGHT_DEF 15
.default_value = BACKLIGHT_DEF,
},
.set = sd_setbacklight,
.get = sd_getbacklight,
},
};
/* table of the disabled controls */
@ -192,33 +264,51 @@ static u32 ctrl_dis[] = {
/* SENSOR_HV7131R 0 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
| (1 << SHARPNESS_IDX),
| (1 << SHARPNESS_IDX)
| (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
| (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_MI0360 1 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
| (1 << SHARPNESS_IDX),
| (1 << SHARPNESS_IDX)
| (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
| (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_MI1310_SOC 2 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
| (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
| (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
| (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_MI1320 3 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
| (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
| (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
| (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_MI1320_SOC 4 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
| (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
| (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
| (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_OV7660 5 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
| (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
| (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
| (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_OV7670 6 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << SHARPNESS_IDX),
| (1 << SHARPNESS_IDX)
| (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
| (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_PO1200 7 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << LIGHTFREQ_IDX),
| (1 << LIGHTFREQ_IDX)
| (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
| (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_PO3130NC 8 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
| (1 << SHARPNESS_IDX),
| (1 << SHARPNESS_IDX)
| (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
| (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_POxxxx 9 */
(1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX),
};
@ -2825,7 +2915,9 @@ static const u8 poxxxx_init_common[][4] = {
{0x00, 0x1e, 0xc6, 0xaa},
{0x00, 0x00, 0x40, 0xdd},
{0x00, 0x1d, 0x05, 0xaa},
{}
};
static const u8 poxxxx_gamma[][4] = {
{0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */
{0x00, 0x73, 0x00, 0xaa},
{0x00, 0x74, 0x0a, 0xaa},
@ -2867,19 +2959,9 @@ static const u8 poxxxx_init_common[][4] = {
{0x00, 0x7c, 0xba, 0xaa},
{0x00, 0x7d, 0xd4, 0xaa},
{0x00, 0x7e, 0xea, 0xaa},
{0x00, 0xaa, 0xff, 0xaa}, /* back light comp */
{0x00, 0xc4, 0x03, 0xaa},
{0x00, 0xc5, 0x19, 0xaa},
{0x00, 0xc6, 0x03, 0xaa},
{0x00, 0xc7, 0x91, 0xaa},
{0x00, 0xc8, 0x01, 0xaa},
{0x00, 0xc9, 0xdd, 0xaa},
{0x00, 0xca, 0x02, 0xaa},
{0x00, 0xcb, 0x37, 0xaa},
/* read d1 */
{0x00, 0xd1, 0x3c, 0xaa},
{}
};
static const u8 poxxxx_init_start_3[][4] = {
{0x00, 0xb8, 0x28, 0xaa},
{0x00, 0xb9, 0x1e, 0xaa},
{0x00, 0xb6, 0x14, 0xaa},
@ -2959,9 +3041,6 @@ static const u8 poxxxx_init_end_1[][4] = {
{0x00, 0xb3, 0x08, 0xaa},
{0x00, 0xb4, 0x0b, 0xaa},
{0x00, 0xb5, 0x0d, 0xaa},
{0x00, 0x59, 0x7e, 0xaa}, /* sharpness */
{0x00, 0x16, 0x00, 0xaa}, /* white balance */
{0x00, 0x18, 0x00, 0xaa},
{}
};
static const u8 poxxxx_init_end_2[][4] = {
@ -3310,6 +3389,33 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->bridge = id->driver_info >> 8;
sd->flags = id->driver_info & 0xff;
if (id->idVendor == 0x046d &&
(id->idProduct == 0x0892 || id->idProduct == 0x0896))
sd->sensor = SENSOR_POxxxx; /* no probe */
sd->brightness = BRIGHTNESS_DEF;
sd->contrast = CONTRAST_DEF;
sd->colors = COLOR_DEF;
sd->hflip = HFLIP_DEF;
sd->vflip = VFLIP_DEF;
sd->lightfreq = FREQ_DEF;
sd->sharpness = SHARPNESS_DEF;
sd->gain = GAIN_DEF;
sd->exposure = EXPOSURE_DEF;
sd->autogain = AUTOGAIN_DEF;
sd->backlight = BACKLIGHT_DEF;
return 0;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
@ -3327,14 +3433,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
128, /* POxxxx 9 */
};
cam = &gspca_dev->cam;
sd->bridge = id->driver_info >> 8;
sd->flags = id->driver_info & 0xff;
if (id->idVendor == 0x046d &&
(id->idProduct == 0x0892 || id->idProduct == 0x0896))
sensor = SENSOR_POxxxx;
else
if (sd->sensor != SENSOR_POxxxx)
sensor = vc032x_probe_sensor(gspca_dev);
else
sensor = sd->sensor;
switch (sensor) {
case -1:
PDEBUG(D_PROBE, "Unknown sensor...");
@ -3373,6 +3476,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
}
sd->sensor = sensor;
cam = &gspca_dev->cam;
if (sd->bridge == BRIDGE_VC0321) {
cam->cam_mode = vc0321_mode;
cam->nmodes = ARRAY_SIZE(vc0321_mode);
@ -3401,28 +3505,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
}
}
cam->npkt = npkt[sd->sensor];
sd->brightness = BRIGHTNESS_DEF;
sd->contrast = CONTRAST_DEF;
sd->colors = COLOR_DEF;
sd->hflip = HFLIP_DEF;
sd->vflip = VFLIP_DEF;
sd->lightfreq = FREQ_DEF;
sd->sharpness = SHARPNESS_DEF;
gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
if (sd->sensor == SENSOR_OV7670)
sd->flags |= FL_HFLIP | FL_VFLIP;
return 0;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->bridge == BRIDGE_VC0321) {
reg_r(gspca_dev, 0x8a, 0, 3);
reg_w(gspca_dev, 0x87, 0x00, 0x0f0f);
@ -3433,8 +3520,8 @@ static int sd_init(struct gspca_dev *gspca_dev)
if (gspca_dev->usb_buf[0] != 0) {
reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
}
reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
}
}
return gspca_dev->usb_err;
@ -3551,6 +3638,82 @@ static void setsharpness(struct gspca_dev *gspca_dev)
break;
}
}
static void setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
return;
i2c_write(gspca_dev, 0x15, &sd->gain, 1);
}
static void setexposure(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 data;
if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
return;
data = sd->exposure >> 8;
i2c_write(gspca_dev, 0x1a, &data, 1);
data = sd->exposure;
i2c_write(gspca_dev, 0x1b, &data, 1);
}
static void setautogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
static const u8 data[2] = {0x28, 0x3c};
if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
return;
i2c_write(gspca_dev, 0xd1, &data[sd->autogain], 1);
}
static void setgamma(struct gspca_dev *gspca_dev)
{
/*fixme:to do */
usb_exchange(gspca_dev, poxxxx_gamma);
}
static void setbacklight(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u16 v;
u8 data;
data = (sd->backlight << 4) | 0x0f;
i2c_write(gspca_dev, 0xaa, &data, 1);
v = 613 + 12 * sd->backlight;
data = v >> 8;
i2c_write(gspca_dev, 0xc4, &data, 1);
data = v;
i2c_write(gspca_dev, 0xc5, &data, 1);
v = 1093 - 12 * sd->backlight;
data = v >> 8;
i2c_write(gspca_dev, 0xc6, &data, 1);
data = v;
i2c_write(gspca_dev, 0xc7, &data, 1);
v = 342 + 9 * sd->backlight;
data = v >> 8;
i2c_write(gspca_dev, 0xc8, &data, 1);
data = v;
i2c_write(gspca_dev, 0xc9, &data, 1);
v = 702 - 9 * sd->backlight;
data = v >> 8;
i2c_write(gspca_dev, 0xca, &data, 1);
data = v;
i2c_write(gspca_dev, 0xcb, &data, 1);
}
static void setwb(struct gspca_dev *gspca_dev)
{
/*fixme:to do - valid when reg d1 = 0x1c - (reg16 + reg15 = 0xa3)*/
static const u8 data[2] = {0x00, 0x00};
i2c_write(gspca_dev, 0x16, &data[0], 1);
i2c_write(gspca_dev, 0x18, &data[1], 1);
}
static int sd_start(struct gspca_dev *gspca_dev)
{
@ -3662,6 +3825,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
default:
/* case SENSOR_POxxxx: */
usb_exchange(gspca_dev, poxxxx_init_common);
setgamma(gspca_dev);
setbacklight(gspca_dev);
setbrightness(gspca_dev);
setcontrast(gspca_dev);
setcolors(gspca_dev);
setsharpness(gspca_dev);
setautogain(gspca_dev);
setexposure(gspca_dev);
setgain(gspca_dev);
usb_exchange(gspca_dev, poxxxx_init_start_3);
if (mode)
init = poxxxx_initQVGA;
else
@ -3693,7 +3866,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
}
msleep(100);
setsharpness(gspca_dev);
sethvflip(gspca_dev);
setlightfreq(gspca_dev);
}
@ -3704,14 +3876,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff);
break;
case SENSOR_POxxxx:
setcolors(gspca_dev);
setbrightness(gspca_dev);
setcontrast(gspca_dev);
/* led on */
msleep(80);
reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
usb_exchange(gspca_dev, poxxxx_init_end_2);
setwb(gspca_dev);
msleep(80); /* led on */
reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
break;
}
return gspca_dev->usb_err;
@ -3911,6 +4079,80 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->gain = val;
if (gspca_dev->streaming)
setgain(gspca_dev);
return gspca_dev->usb_err;
}
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->gain;
return 0;
}
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->exposure = val;
if (gspca_dev->streaming)
setexposure(gspca_dev);
return gspca_dev->usb_err;
}
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->exposure;
return 0;
}
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->autogain = val;
if (gspca_dev->streaming)
setautogain(gspca_dev);
return gspca_dev->usb_err;
}
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->autogain;
return 0;
}
static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->backlight = val;
if (gspca_dev->streaming)
setbacklight(gspca_dev);
return gspca_dev->usb_err;
}
static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
*val = sd->backlight;
return 0;
}
static int sd_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu)
{

File diff suppressed because it is too large Load Diff

View File

@ -17,163 +17,14 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include "ivtv-driver.h"
#include "ivtv-cards.h"
#include "ivtv-ioctl.h"
#include "ivtv-routing.h"
#include "ivtv-i2c.h"
#include "ivtv-mailbox.h"
#include "ivtv-controls.h"
/* Must be sorted from low to high control ID! */
static const u32 user_ctrls[] = {
V4L2_CID_USER_CLASS,
V4L2_CID_BRIGHTNESS,
V4L2_CID_CONTRAST,
V4L2_CID_SATURATION,
V4L2_CID_HUE,
V4L2_CID_AUDIO_VOLUME,
V4L2_CID_AUDIO_BALANCE,
V4L2_CID_AUDIO_BASS,
V4L2_CID_AUDIO_TREBLE,
V4L2_CID_AUDIO_MUTE,
V4L2_CID_AUDIO_LOUDNESS,
0
};
static const u32 *ctrl_classes[] = {
user_ctrls,
cx2341x_mpeg_ctrls,
NULL
};
int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
{
struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
const char *name;
qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
if (qctrl->id == 0)
return -EINVAL;
switch (qctrl->id) {
/* Standard V4L2 controls */
case V4L2_CID_USER_CLASS:
return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0);
case V4L2_CID_BRIGHTNESS:
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
default:
if (cx2341x_ctrl_query(&itv->params, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
}
strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
qctrl->name[sizeof(qctrl->name) - 1] = 0;
return 0;
}
int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu)
{
struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
struct v4l2_queryctrl qctrl;
qctrl.id = qmenu->id;
ivtv_queryctrl(file, fh, &qctrl);
return v4l2_ctrl_query_menu(qmenu, &qctrl,
cx2341x_ctrl_get_menu(&itv->params, qmenu->id));
}
static int ivtv_try_ctrl(struct file *file, void *fh,
struct v4l2_ext_control *vctrl)
{
struct v4l2_queryctrl qctrl;
const char **menu_items = NULL;
int err;
qctrl.id = vctrl->id;
err = ivtv_queryctrl(file, fh, &qctrl);
if (err)
return err;
if (qctrl.type == V4L2_CTRL_TYPE_MENU)
menu_items = v4l2_ctrl_get_menu(qctrl.id);
return v4l2_ctrl_check(vctrl, &qctrl, menu_items);
}
static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
{
switch (vctrl->id) {
/* Standard V4L2 controls */
case V4L2_CID_BRIGHTNESS:
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl);
default:
IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
return -EINVAL;
}
return 0;
}
static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
{
switch (vctrl->id) {
/* Standard V4L2 controls */
case V4L2_CID_BRIGHTNESS:
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl);
default:
IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
return -EINVAL;
}
return 0;
}
static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt)
{
if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
return -EINVAL;
if (atomic_read(&itv->capturing) > 0)
return -EBUSY;
struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
/* First try to allocate sliced VBI buffers if needed. */
if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
@ -208,106 +59,43 @@ static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fm
return 0;
}
int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
{
struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
struct v4l2_control ctrl;
struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
struct v4l2_mbus_framefmt fmt;
if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
int i;
int err = 0;
for (i = 0; i < c->count; i++) {
ctrl.id = c->controls[i].id;
ctrl.value = c->controls[i].value;
err = ivtv_g_ctrl(itv, &ctrl);
c->controls[i].value = ctrl.value;
if (err) {
c->error_idx = i;
break;
}
}
return err;
}
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
return cx2341x_ext_ctrls(&itv->params, 0, c, VIDIOC_G_EXT_CTRLS);
return -EINVAL;
/* fix videodecoder resolution */
fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
fmt.height = cxhdl->height;
fmt.code = V4L2_MBUS_FMT_FIXED;
v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
return 0;
}
int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
static int ivtv_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
{
struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
struct v4l2_control ctrl;
static const u32 freqs[3] = { 44100, 48000, 32000 };
struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
int i;
int err = 0;
for (i = 0; i < c->count; i++) {
ctrl.id = c->controls[i].id;
ctrl.value = c->controls[i].value;
err = ivtv_s_ctrl(itv, &ctrl);
c->controls[i].value = ctrl.value;
if (err) {
c->error_idx = i;
break;
}
}
return err;
}
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
static u32 freqs[3] = { 44100, 48000, 32000 };
struct cx2341x_mpeg_params p = itv->params;
int err = cx2341x_ext_ctrls(&p, atomic_read(&itv->capturing), c, VIDIOC_S_EXT_CTRLS);
unsigned idx;
if (err)
return err;
if (p.video_encoding != itv->params.video_encoding) {
int is_mpeg1 = p.video_encoding ==
V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
struct v4l2_mbus_framefmt fmt;
/* fix videodecoder resolution */
fmt.width = itv->params.width / (is_mpeg1 ? 2 : 1);
fmt.height = itv->params.height;
fmt.code = V4L2_MBUS_FMT_FIXED;
v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
}
err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt)
err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt);
itv->params = p;
itv->dualwatch_stereo_mode = p.audio_properties & 0x0300;
idx = p.audio_properties & 0x03;
/* The audio clock of the digitizer must match the codec sample
rate otherwise you get some very strange effects. */
if (idx < ARRAY_SIZE(freqs))
ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
return err;
}
return -EINVAL;
/* The audio clock of the digitizer must match the codec sample
rate otherwise you get some very strange effects. */
if (idx < ARRAY_SIZE(freqs))
ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
return 0;
}
int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
static int ivtv_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
{
struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
int i;
int err = 0;
for (i = 0; i < c->count; i++) {
err = ivtv_try_ctrl(file, fh, &c->controls[i]);
if (err) {
c->error_idx = i;
break;
}
}
return err;
}
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
return cx2341x_ext_ctrls(&itv->params, atomic_read(&itv->capturing), c, VIDIOC_TRY_EXT_CTRLS);
return -EINVAL;
itv->dualwatch_stereo_mode = val;
return 0;
}
struct cx2341x_handler_ops ivtv_cxhdl_ops = {
.s_audio_mode = ivtv_s_audio_mode,
.s_audio_sampling_freq = ivtv_s_audio_sampling_freq,
.s_video_encoding = ivtv_s_video_encoding,
.s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt,
};

View File

@ -21,10 +21,6 @@
#ifndef IVTV_CONTROLS_H
#define IVTV_CONTROLS_H
int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a);
int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a);
extern struct cx2341x_handler_ops ivtv_cxhdl_ops;
#endif

View File

@ -53,6 +53,7 @@
#include "ivtv-cards.h"
#include "ivtv-vbi.h"
#include "ivtv-routing.h"
#include "ivtv-controls.h"
#include "ivtv-gpio.h"
#include <media/tveeprom.h>
@ -734,9 +735,8 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
itv->open_id = 1;
/* Initial settings */
cx2341x_fill_defaults(&itv->params);
itv->params.port = CX2341X_PORT_MEMORY;
itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
itv->cxhdl.port = CX2341X_PORT_MEMORY;
itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
init_waitqueue_head(&itv->eos_waitq);
init_waitqueue_head(&itv->event_waitq);
init_waitqueue_head(&itv->vsync_waitq);
@ -1006,6 +1006,13 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
retval = -ENOMEM;
goto err;
}
retval = cx2341x_handler_init(&itv->cxhdl, 50);
if (retval)
goto err;
itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl;
itv->cxhdl.ops = &ivtv_cxhdl_ops;
itv->cxhdl.priv = itv;
itv->cxhdl.func = ivtv_api_func;
IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr);
@ -1127,7 +1134,7 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w;
itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h;
itv->params.video_gop_size = itv->is_60hz ? 15 : 12;
cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
@ -1269,15 +1276,8 @@ int ivtv_init_on_first_open(struct ivtv *itv)
IVTV_DEBUG_INFO("Getting firmware version..\n");
ivtv_firmware_versions(itv);
if (itv->card->hw_all & IVTV_HW_CX25840) {
struct v4l2_control ctrl;
if (itv->card->hw_all & IVTV_HW_CX25840)
v4l2_subdev_call(itv->sd_video, core, load_fw);
/* CX25840_CID_ENABLE_PVR150_WORKAROUND */
ctrl.id = V4L2_CID_PRIVATE_BASE;
ctrl.value = itv->pvr150_workaround;
v4l2_subdev_call(itv->sd_video, core, s_ctrl, &ctrl);
}
vf.tuner = 0;
vf.type = V4L2_TUNER_ANALOG_TV;
@ -1329,6 +1329,8 @@ int ivtv_init_on_first_open(struct ivtv *itv)
/* For cards with video out, this call needs interrupts enabled */
ivtv_s_std(NULL, &fh, &itv->tuner_std);
/* Setup initial controls */
cx2341x_handler_setup(&itv->cxhdl);
return 0;
}

View File

@ -62,6 +62,7 @@
#include <linux/dvb/audio.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/tuner.h>
@ -631,6 +632,8 @@ struct ivtv {
struct ivtv_options options; /* user options */
struct v4l2_device v4l2_dev;
struct cx2341x_handler cxhdl;
struct v4l2_ctrl_handler hdl_gpio;
struct v4l2_subdev sd_gpio; /* GPIO sub-device */
u16 instance;
@ -648,7 +651,6 @@ struct ivtv {
v4l2_std_id std_out; /* current TV output standard */
u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */
u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */
struct cx2341x_mpeg_params params; /* current encoder parameters */
/* Locking */

View File

@ -150,12 +150,10 @@ void ivtv_release_stream(struct ivtv_stream *s)
static void ivtv_dualwatch(struct ivtv *itv)
{
struct v4l2_tuner vt;
u32 new_bitmap;
u32 new_stereo_mode;
const u32 stereo_mask = 0x0300;
const u32 dual = 0x0200;
const u32 dual = 0x02;
new_stereo_mode = itv->params.audio_properties & stereo_mask;
new_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
memset(&vt, 0, sizeof(vt));
ivtv_call_all(itv, tuner, g_tuner, &vt);
if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
@ -164,16 +162,10 @@ static void ivtv_dualwatch(struct ivtv *itv)
if (new_stereo_mode == itv->dualwatch_stereo_mode)
return;
new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask);
IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) {
itv->dualwatch_stereo_mode = new_stereo_mode;
return;
}
IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
itv->dualwatch_stereo_mode, new_stereo_mode);
if (v4l2_ctrl_s_ctrl(itv->cxhdl.audio_mode, new_stereo_mode))
IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
}
static void ivtv_update_pgm_info(struct ivtv *itv)
@ -894,7 +886,8 @@ int ivtv_v4l2_close(struct file *filp)
if (atomic_read(&itv->capturing) > 0) {
/* Undo video mute */
ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
itv->params.video_mute | (itv->params.video_mute_yuv << 8));
v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) |
(v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
}
/* Done! Unmute and continue. */
ivtv_unmute(itv);

View File

@ -248,9 +248,9 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv)
volatile u8 __iomem *mem_offset;
data[0] = 0;
data[1] = itv->params.width; /* YUV source width */
data[2] = itv->params.height;
data[3] = itv->params.audio_properties; /* Audio settings to use,
data[1] = itv->cxhdl.width; /* YUV source width */
data[2] = itv->cxhdl.height;
data[3] = itv->cxhdl.audio_properties; /* Audio settings to use,
bitmap. see docs. */
if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");

View File

@ -24,6 +24,7 @@
#include "ivtv-gpio.h"
#include "tuner-xc2028.h"
#include <media/tuner.h>
#include <media/v4l2-ctrls.h>
/*
* GPIO assignment of Yuan MPG600/MPG160
@ -149,16 +150,10 @@ static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd)
return container_of(sd, struct ivtv, sd_gpio);
}
static struct v4l2_queryctrl gpio_ctrl_mute = {
.id = V4L2_CID_AUDIO_MUTE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Mute",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
.flags = 0,
};
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &container_of(ctrl->handler, struct ivtv, hdl_gpio)->sd_gpio;
}
static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
@ -262,40 +257,24 @@ static int subdev_s_audio_routing(struct v4l2_subdev *sd,
return 0;
}
static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
static int subdev_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
struct ivtv *itv = sd_to_ivtv(sd);
u16 mask, data;
if (ctrl->id != V4L2_CID_AUDIO_MUTE)
return -EINVAL;
mask = itv->card->gpio_audio_mute.mask;
data = itv->card->gpio_audio_mute.mute;
ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
return 0;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
mask = itv->card->gpio_audio_mute.mask;
data = ctrl->val ? itv->card->gpio_audio_mute.mute : 0;
if (mask)
write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) |
(data & mask), IVTV_REG_GPIO_OUT);
return 0;
}
return -EINVAL;
}
static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct ivtv *itv = sd_to_ivtv(sd);
u16 mask, data;
if (ctrl->id != V4L2_CID_AUDIO_MUTE)
return -EINVAL;
mask = itv->card->gpio_audio_mute.mask;
data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
if (mask)
write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
return 0;
}
static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
if (qc->id != V4L2_CID_AUDIO_MUTE)
return -EINVAL;
*qc = gpio_ctrl_mute;
return 0;
}
static int subdev_log_status(struct v4l2_subdev *sd)
{
@ -304,6 +283,7 @@ static int subdev_log_status(struct v4l2_subdev *sd)
IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
read_reg(IVTV_REG_GPIO_IN));
v4l2_ctrl_handler_log_status(&itv->hdl_gpio, sd->name);
return 0;
}
@ -327,11 +307,19 @@ static int subdev_s_video_routing(struct v4l2_subdev *sd,
return 0;
}
static const struct v4l2_ctrl_ops gpio_ctrl_ops = {
.s_ctrl = subdev_s_ctrl,
};
static const struct v4l2_subdev_core_ops subdev_core_ops = {
.log_status = subdev_log_status,
.g_ctrl = subdev_g_ctrl,
.s_ctrl = subdev_s_ctrl,
.queryctrl = subdev_queryctrl,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = {
@ -375,5 +363,12 @@ int ivtv_gpio_init(struct ivtv *itv)
v4l2_subdev_init(&itv->sd_gpio, &subdev_ops);
snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name);
itv->sd_gpio.grp_id = IVTV_HW_GPIO;
v4l2_ctrl_handler_init(&itv->hdl_gpio, 1);
v4l2_ctrl_new_std(&itv->hdl_gpio, &gpio_ctrl_ops,
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
if (itv->hdl_gpio.error)
return itv->hdl_gpio.error;
itv->sd_gpio.ctrl_handler = &itv->hdl_gpio;
v4l2_ctrl_handler_setup(&itv->hdl_gpio);
return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio);
}

View File

@ -63,6 +63,7 @@
#include "ivtv-cards.h"
#include "ivtv-gpio.h"
#include "ivtv-i2c.h"
#include <media/cx25840.h>
/* i2c implementation for cx23415/6 chip, ivtv project.
* Author: Kevin Thayer (nufan_wfk at yahoo.com)
@ -292,6 +293,12 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx]));
} else if (hw == IVTV_HW_CX25840) {
struct cx25840_platform_data pdata;
pdata.pvr150_workaround = itv->pvr150_workaround;
sd = v4l2_i2c_new_subdev_cfg(&itv->v4l2_dev,
adap, mod, type, 0, &pdata, hw_addrs[idx], NULL);
} else {
sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
adap, mod, type, hw_addrs[idx], NULL);

View File

@ -162,7 +162,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed)
data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
data[1] = (speed < 0);
data[2] = speed < 0 ? 3 : 7;
data[3] = itv->params.video_b_frames;
data[3] = v4l2_ctrl_g_ctrl(itv->cxhdl.video_b_frames);
data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
data[5] = 0;
data[6] = 0;
@ -339,8 +339,8 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
struct ivtv *itv = id->itv;
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
pixfmt->width = itv->params.width;
pixfmt->height = itv->params.height;
pixfmt->width = itv->cxhdl.width;
pixfmt->height = itv->cxhdl.height;
pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
pixfmt->field = V4L2_FIELD_INTERLACED;
pixfmt->priv = 0;
@ -568,7 +568,6 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
{
struct ivtv_open_id *id = fh;
struct ivtv *itv = id->itv;
struct cx2341x_mpeg_params *p = &itv->params;
struct v4l2_mbus_framefmt mbus_fmt;
int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
int w = fmt->fmt.pix.width;
@ -577,15 +576,15 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
if (ret)
return ret;
if (p->width == w && p->height == h)
if (itv->cxhdl.width == w && itv->cxhdl.height == h)
return 0;
if (atomic_read(&itv->capturing) > 0)
return -EBUSY;
p->width = w;
p->height = h;
if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
itv->cxhdl.width = w;
itv->cxhdl.height = h;
if (v4l2_ctrl_g_ctrl(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
fmt->fmt.pix.width /= 2;
mbus_fmt.width = fmt->fmt.pix.width;
mbus_fmt.height = h;
@ -1114,9 +1113,10 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
itv->std = *std;
itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
itv->params.is_50hz = itv->is_50hz = !itv->is_60hz;
itv->params.width = 720;
itv->params.height = itv->is_50hz ? 576 : 480;
itv->is_50hz = !itv->is_60hz;
cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
itv->cxhdl.width = 720;
itv->cxhdl.height = itv->is_50hz ? 576 : 480;
itv->vbi.count = itv->is_50hz ? 18 : 12;
itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
@ -1157,7 +1157,7 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
itv->main_rect.left = itv->main_rect.top = 0;
itv->main_rect.width = 720;
itv->main_rect.height = itv->params.height;
itv->main_rect.height = itv->cxhdl.height;
ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
720, itv->main_rect.height, 0, 0);
yi->main_rect = itv->main_rect;
@ -1554,7 +1554,7 @@ static int ivtv_log_status(struct file *file, void *fh)
}
IVTV_INFO("Tuner: %s\n",
test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
cx2341x_log_status(&itv->params, itv->v4l2_dev.name);
v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name);
IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
for (i = 0; i < IVTV_MAX_STREAMS; i++) {
struct ivtv_stream *s = &itv->streams[i];
@ -1942,11 +1942,6 @@ static const struct v4l2_ioctl_ops ivtv_ioctl_ops = {
.vidioc_s_register = ivtv_s_register,
#endif
.vidioc_default = ivtv_default,
.vidioc_queryctrl = ivtv_queryctrl,
.vidioc_querymenu = ivtv_querymenu,
.vidioc_g_ext_ctrls = ivtv_g_ext_ctrls,
.vidioc_s_ext_ctrls = ivtv_s_ext_ctrls,
.vidioc_try_ext_ctrls = ivtv_try_ext_ctrls,
.vidioc_subscribe_event = ivtv_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};

View File

@ -210,6 +210,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
s->vdev->num = num;
s->vdev->v4l2_dev = &itv->v4l2_dev;
s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
s->vdev->fops = ivtv_stream_info[type].fops;
s->vdev->release = video_device_release;
s->vdev->tvnorms = V4L2_STD_ALL;
@ -451,7 +452,6 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv *itv = s->itv;
struct cx2341x_mpeg_params *p = &itv->params;
int captype = 0, subtype = 0;
int enable_passthrough = 0;
@ -472,7 +472,7 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
}
itv->mpg_data_received = itv->vbi_data_inserted = 0;
itv->dualwatch_jiffies = jiffies;
itv->dualwatch_stereo_mode = p->audio_properties & 0x0300;
itv->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
itv->search_pack_header = 0;
break;
@ -560,12 +560,12 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
itv->pgm_info_offset, itv->pgm_info_num);
/* Setup API for Stream */
cx2341x_update(itv, ivtv_api_func, NULL, p);
cx2341x_handler_setup(&itv->cxhdl);
/* mute if capturing radio */
if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
1 | (p->video_mute_yuv << 8));
1 | (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
}
/* Vsync Setup */
@ -581,6 +581,8 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
clear_bit(IVTV_F_I_EOS, &itv->i_flags);
cx2341x_handler_set_busy(&itv->cxhdl, 1);
/* Initialize Digitizer for Capture */
/* Avoid tinny audio problem - ensure audio clocks are going */
v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1);
@ -617,7 +619,6 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv *itv = s->itv;
struct cx2341x_mpeg_params *p = &itv->params;
int datatype;
u16 width;
u16 height;
@ -627,8 +628,8 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
width = p->width;
height = p->height;
width = itv->cxhdl.width;
height = itv->cxhdl.height;
/* set audio mode to left/stereo for dual/stereo mode. */
ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
@ -668,7 +669,7 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
break;
}
if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
width, height, p->audio_properties)) {
width, height, itv->cxhdl.audio_properties)) {
IVTV_DEBUG_WARN("Couldn't initialize decoder source\n");
}
@ -847,6 +848,8 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
return 0;
}
cx2341x_handler_set_busy(&itv->cxhdl, 0);
/* Set the following Interrupt mask bits for capture */
ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
del_timer(&itv->dma_timer);
@ -967,7 +970,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)
/* Setup capture if not already done */
if (atomic_read(&itv->capturing) == 0) {
cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
cx2341x_handler_setup(&itv->cxhdl);
cx2341x_handler_set_busy(&itv->cxhdl, 1);
}
/* Start Passthrough Mode */
@ -988,6 +992,8 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)
clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
itv->output_mode = OUT_NONE;
if (atomic_read(&itv->capturing) == 0)
cx2341x_handler_set_busy(&itv->cxhdl, 0);
return 0;
}

View File

@ -283,51 +283,6 @@ void msp_set_scart(struct i2c_client *client, int in, int out)
msp_write_dem(client, 0x40, state->i2s_mode);
}
void msp_set_audio(struct i2c_client *client)
{
struct msp_state *state = to_state(i2c_get_clientdata(client));
int bal = 0, bass, treble, loudness;
int val = 0;
int reallymuted = state->muted | state->scan_in_progress;
if (!reallymuted)
val = (state->volume * 0x7f / 65535) << 8;
v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
state->muted ? "on" : "off",
state->scan_in_progress ? "yes" : "no",
state->volume);
msp_write_dsp(client, 0x0000, val);
msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
if (state->has_scart2_out_volume)
msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
if (state->has_headphones)
msp_write_dsp(client, 0x0006, val);
if (!state->has_sound_processing)
return;
if (val)
bal = (u8)((state->balance / 256) - 128);
bass = ((state->bass - 32768) * 0x60 / 65535) << 8;
treble = ((state->treble - 32768) * 0x60 / 65535) << 8;
loudness = state->loudness ? ((5 * 4) << 8) : 0;
v4l_dbg(1, msp_debug, client, "balance=%d bass=%d treble=%d loudness=%d\n",
state->balance, state->bass, state->treble, state->loudness);
msp_write_dsp(client, 0x0001, bal << 8);
msp_write_dsp(client, 0x0002, bass);
msp_write_dsp(client, 0x0003, treble);
msp_write_dsp(client, 0x0004, loudness);
if (!state->has_headphones)
return;
msp_write_dsp(client, 0x0030, bal << 8);
msp_write_dsp(client, 0x0031, bass);
msp_write_dsp(client, 0x0032, treble);
msp_write_dsp(client, 0x0033, loudness);
}
/* ------------------------------------------------------------------------ */
static void msp_wake_thread(struct i2c_client *client)
@ -363,41 +318,60 @@ int msp_sleep(struct msp_state *state, int timeout)
/* ------------------------------------------------------------------------ */
static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
static int msp_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct msp_state *state = to_state(sd);
struct msp_state *state = ctrl_to_state(ctrl);
struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
int val = ctrl->val;
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = state->volume;
break;
case V4L2_CID_AUDIO_VOLUME: {
/* audio volume cluster */
int reallymuted = state->muted->val | state->scan_in_progress;
case V4L2_CID_AUDIO_MUTE:
ctrl->value = state->muted;
break;
if (!reallymuted)
val = (val * 0x7f / 65535) << 8;
case V4L2_CID_AUDIO_BALANCE:
if (!state->has_sound_processing)
return -EINVAL;
ctrl->value = state->balance;
v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
state->muted->val ? "on" : "off",
state->scan_in_progress ? "yes" : "no",
state->volume->val);
msp_write_dsp(client, 0x0000, val);
msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
if (state->has_scart2_out_volume)
msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
if (state->has_headphones)
msp_write_dsp(client, 0x0006, val);
break;
}
case V4L2_CID_AUDIO_BASS:
if (!state->has_sound_processing)
return -EINVAL;
ctrl->value = state->bass;
val = ((val - 32768) * 0x60 / 65535) << 8;
msp_write_dsp(client, 0x0002, val);
if (state->has_headphones)
msp_write_dsp(client, 0x0031, val);
break;
case V4L2_CID_AUDIO_TREBLE:
if (!state->has_sound_processing)
return -EINVAL;
ctrl->value = state->treble;
val = ((val - 32768) * 0x60 / 65535) << 8;
msp_write_dsp(client, 0x0003, val);
if (state->has_headphones)
msp_write_dsp(client, 0x0032, val);
break;
case V4L2_CID_AUDIO_LOUDNESS:
if (!state->has_sound_processing)
return -EINVAL;
ctrl->value = state->loudness;
val = val ? ((5 * 4) << 8) : 0;
msp_write_dsp(client, 0x0004, val);
if (state->has_headphones)
msp_write_dsp(client, 0x0033, val);
break;
case V4L2_CID_AUDIO_BALANCE:
val = (u8)((val / 256) - 128);
msp_write_dsp(client, 0x0001, val << 8);
if (state->has_headphones)
msp_write_dsp(client, 0x0030, val << 8);
break;
default:
@ -406,53 +380,9 @@ static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
return 0;
}
static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
void msp_update_volume(struct msp_state *state)
{
struct msp_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
state->volume = ctrl->value;
if (state->volume == 0)
state->balance = 32768;
break;
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value < 0 || ctrl->value >= 2)
return -ERANGE;
state->muted = ctrl->value;
break;
case V4L2_CID_AUDIO_BASS:
if (!state->has_sound_processing)
return -EINVAL;
state->bass = ctrl->value;
break;
case V4L2_CID_AUDIO_TREBLE:
if (!state->has_sound_processing)
return -EINVAL;
state->treble = ctrl->value;
break;
case V4L2_CID_AUDIO_LOUDNESS:
if (!state->has_sound_processing)
return -EINVAL;
state->loudness = ctrl->value;
break;
case V4L2_CID_AUDIO_BALANCE:
if (!state->has_sound_processing)
return -EINVAL;
state->balance = ctrl->value;
break;
default:
return -EINVAL;
}
msp_set_audio(client);
return 0;
v4l2_ctrl_s_ctrl(state->volume, v4l2_ctrl_g_ctrl(state->volume));
}
/* --- v4l2 ioctls --- */
@ -472,7 +402,7 @@ static int msp_s_radio(struct v4l2_subdev *sd)
msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
msp3400c_set_carrier(client, MSP_CARRIER(10.7),
MSP_CARRIER(10.7));
msp_set_audio(client);
msp_update_volume(state);
break;
case OPMODE_AUTODETECT:
case OPMODE_AUTOSELECT:
@ -592,33 +522,6 @@ static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
return 0;
}
static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
struct msp_state *state = to_state(sd);
switch (qc->id) {
case V4L2_CID_AUDIO_VOLUME:
return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
case V4L2_CID_AUDIO_MUTE:
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
default:
break;
}
if (!state->has_sound_processing)
return -EINVAL;
switch (qc->id) {
case V4L2_CID_AUDIO_LOUDNESS:
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
default:
return -EINVAL;
}
return 0;
}
static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct msp_state *state = to_state(sd);
@ -633,19 +536,14 @@ static int msp_log_status(struct v4l2_subdev *sd)
struct msp_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
const char *p;
char prefix[V4L2_SUBDEV_NAME_SIZE + 20];
if (state->opmode == OPMODE_AUTOSELECT)
msp_detect_stereo(client);
v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
client->name, state->rev1, state->rev2);
v4l_info(client, "Audio: volume %d%s\n",
state->volume, state->muted ? " (muted)" : "");
if (state->has_sound_processing) {
v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
state->balance, state->bass,
state->treble,
state->loudness ? "on" : "off");
}
snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name);
v4l2_ctrl_handler_log_status(&state->hdl, prefix);
switch (state->mode) {
case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
@ -695,12 +593,20 @@ static int msp_resume(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops msp_ctrl_ops = {
.s_ctrl = msp_s_ctrl,
};
static const struct v4l2_subdev_core_ops msp_core_ops = {
.log_status = msp_log_status,
.g_chip_ident = msp_g_chip_ident,
.g_ctrl = msp_g_ctrl,
.s_ctrl = msp_s_ctrl,
.queryctrl = msp_queryctrl,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
.s_std = msp_s_std,
};
@ -728,6 +634,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct msp_state *state;
struct v4l2_subdev *sd;
struct v4l2_ctrl_handler *hdl;
int (*thread_func)(void *data) = NULL;
int msp_hard;
int msp_family;
@ -752,13 +659,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
state->v4l2_std = V4L2_STD_NTSC;
state->audmode = V4L2_TUNER_MODE_STEREO;
state->volume = 58880; /* 0db gain */
state->balance = 32768; /* 0db gain */
state->bass = 32768;
state->treble = 32768;
state->loudness = 0;
state->input = -1;
state->muted = 0;
state->i2s_mode = 0;
init_waitqueue_head(&state->wq);
/* These are the reset input/output positions */
@ -777,8 +678,6 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENODEV;
}
msp_set_audio(client);
msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
msp_product = (state->rev2 >> 8) & 0xff;
msp_prod_hi = msp_product / 10;
@ -849,6 +748,34 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
state->opmode = OPMODE_MANUAL;
}
hdl = &state->hdl;
v4l2_ctrl_handler_init(hdl, 6);
if (state->has_sound_processing) {
v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768);
v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768);
v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0);
}
state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880);
v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
sd->ctrl_handler = hdl;
if (hdl->error) {
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
kfree(state);
return err;
}
v4l2_ctrl_cluster(2, &state->volume);
v4l2_ctrl_handler_setup(hdl);
/* hello world :-) */
v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n",
msp_family, msp_product,
@ -903,6 +830,7 @@ static int msp_remove(struct i2c_client *client)
}
msp_reset(client);
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return 0;
}

View File

@ -6,6 +6,7 @@
#include <media/msp3400.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
/* ---------------------------------------------------------------------- */
@ -51,6 +52,7 @@ extern int msp_stereo_thresh;
struct msp_state {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
int rev1, rev2;
int ident;
u8 has_nicam;
@ -87,9 +89,12 @@ struct msp_state {
int audmode;
int rxsubchans;
int volume, muted;
int balance, loudness;
int bass, treble;
struct {
/* volume cluster */
struct v4l2_ctrl *volume;
struct v4l2_ctrl *muted;
};
int scan_in_progress;
/* thread */
@ -104,6 +109,11 @@ static inline struct msp_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct msp_state, sd);
}
static inline struct msp_state *ctrl_to_state(struct v4l2_ctrl *ctrl)
{
return container_of(ctrl->handler, struct msp_state, hdl);
}
/* msp3400-driver.c */
int msp_write_dem(struct i2c_client *client, int addr, int val);
int msp_write_dsp(struct i2c_client *client, int addr, int val);
@ -111,7 +121,7 @@ int msp_read_dem(struct i2c_client *client, int addr);
int msp_read_dsp(struct i2c_client *client, int addr);
int msp_reset(struct i2c_client *client);
void msp_set_scart(struct i2c_client *client, int in, int out);
void msp_set_audio(struct i2c_client *client);
void msp_update_volume(struct msp_state *state);
int msp_sleep(struct msp_state *state, int timeout);
/* msp3400-kthreads.c */

View File

@ -496,13 +496,13 @@ restart:
v4l_dbg(1, msp_debug, client,
"thread: no carrier scan\n");
state->scan_in_progress = 0;
msp_set_audio(client);
msp_update_volume(state);
continue;
}
/* mute audio */
state->scan_in_progress = 1;
msp_set_audio(client);
msp_update_volume(state);
msp3400c_set_mode(client, MSP_MODE_AM_DETECT);
val1 = val2 = 0;
@ -634,7 +634,7 @@ no_second:
/* unmute */
state->scan_in_progress = 0;
msp3400c_set_audmode(client);
msp_set_audio(client);
msp_update_volume(state);
if (msp_debug)
msp3400c_print_mode(client);
@ -679,13 +679,13 @@ restart:
v4l_dbg(1, msp_debug, client,
"thread: no carrier scan\n");
state->scan_in_progress = 0;
msp_set_audio(client);
msp_update_volume(state);
continue;
}
/* mute audio */
state->scan_in_progress = 1;
msp_set_audio(client);
msp_update_volume(state);
/* start autodetect. Note: autodetect is not supported for
NTSC-M and radio, hence we force the standard in those
@ -797,7 +797,7 @@ restart:
/* unmute */
msp3400c_set_audmode(client);
state->scan_in_progress = 0;
msp_set_audio(client);
msp_update_volume(state);
/* monitor tv audio mode, the first time don't wait
so long to get a quick stereo/bilingual result */
@ -974,7 +974,7 @@ restart:
v4l_dbg(1, msp_debug, client,
"thread: no carrier scan\n");
state->scan_in_progress = 0;
msp_set_audio(client);
msp_update_volume(state);
continue;
}
@ -1020,7 +1020,7 @@ unmute:
}
/* unmute: dispatch sound to scart output, set scart volume */
msp_set_audio(client);
msp_update_volume(state);
/* restore ACB */
if (msp_write_dsp(client, 0x13, state->acb))

View File

@ -1,5 +1,5 @@
/*
* Driver for MT9M111/MT9M112 CMOS Image Sensor from Micron
* Driver for MT9M111/MT9M112/MT9M131 CMOS Image Sensor from Micron/Aptina
*
* Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
*
@ -19,11 +19,14 @@
#include <media/soc_camera.h>
/*
* mt9m111 and mt9m112 i2c address is 0x5d or 0x48 (depending on SAddr pin)
* MT9M111, MT9M112 and MT9M131:
* i2c address is 0x48 or 0x5d (depending on SADDR pin)
* The platform has to define i2c_board_info and call i2c_register_board_info()
*/
/* mt9m111: Sensor register addresses */
/*
* Sensor core register addresses (0x000..0x0ff)
*/
#define MT9M111_CHIP_VERSION 0x000
#define MT9M111_ROW_START 0x001
#define MT9M111_COLUMN_START 0x002
@ -72,8 +75,9 @@
#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2)
#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1)
#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0)
/*
* mt9m111: Colorpipe register addresses (0x100..0x1ff)
* Colorpipe register addresses (0x100..0x1ff)
*/
#define MT9M111_OPER_MODE_CTRL 0x106
#define MT9M111_OUTPUT_FORMAT_CTRL 0x108
@ -109,8 +113,9 @@
#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y (1 << 1)
#define MT9M111_OUTFMT_SWAP_RGB_EVEN (1 << 1)
#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr (1 << 0)
/*
* mt9m111: Camera control register addresses (0x200..0x2ff not implemented)
* Camera control register addresses (0x200..0x2ff not implemented)
*/
#define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg)
@ -160,7 +165,8 @@ enum mt9m111_context {
struct mt9m111 {
struct v4l2_subdev subdev;
int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */
int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
* from v4l2-chip-ident.h */
enum mt9m111_context context;
struct v4l2_rect rect;
const struct mt9m111_datafmt *fmt;
@ -934,7 +940,7 @@ static int mt9m111_init(struct i2c_client *client)
if (!ret)
ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure);
if (ret)
dev_err(&client->dev, "mt9m11x init failed: %d\n", ret);
dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
return ret;
}
@ -963,27 +969,27 @@ static int mt9m111_video_probe(struct soc_camera_device *icd,
mt9m111->swap_rgb_even_odd = 1;
mt9m111->swap_rgb_red_blue = 1;
ret = mt9m111_init(client);
if (ret)
goto ei2c;
data = reg_read(CHIP_VERSION);
switch (data) {
case 0x143a: /* MT9M111 */
case 0x143a: /* MT9M111 or MT9M131 */
mt9m111->model = V4L2_IDENT_MT9M111;
dev_info(&client->dev,
"Detected a MT9M111/MT9M131 chip ID %x\n", data);
break;
case 0x148c: /* MT9M112 */
mt9m111->model = V4L2_IDENT_MT9M112;
dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
break;
default:
ret = -ENODEV;
dev_err(&client->dev,
"No MT9M11x chip detected, register read %x\n", data);
"No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
data);
goto ei2c;
}
dev_info(&client->dev, "Detected a MT9M11x chip ID %x\n", data);
ret = mt9m111_init(client);
ei2c:
return ret;
@ -1034,13 +1040,13 @@ static int mt9m111_probe(struct i2c_client *client,
int ret;
if (!icd) {
dev_err(&client->dev, "MT9M11x: missing soc-camera data!\n");
dev_err(&client->dev, "mt9m111: soc-camera data missing!\n");
return -EINVAL;
}
icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "MT9M11x driver needs platform data\n");
dev_err(&client->dev, "mt9m111: driver needs platform data\n");
return -EINVAL;
}
@ -1114,6 +1120,6 @@ static void __exit mt9m111_mod_exit(void)
module_init(mt9m111_mod_init);
module_exit(mt9m111_mod_exit);
MODULE_DESCRIPTION("Micron MT9M111/MT9M112 Camera driver");
MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver");
MODULE_AUTHOR("Robert Jarzmik");
MODULE_LICENSE("GPL");

View File

@ -785,6 +785,8 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
if (ret < 0)
return ret;
if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
csicr1 |= CSICR1_REDGE;
if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
csicr1 |= CSICR1_INV_PCLK;
if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
@ -1201,7 +1203,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
buf = list_entry(pcdev->capture.next,
struct mx2_buffer, vb.queue);
buf->bufnum = bufnum;
buf->bufnum = !bufnum;
list_move_tail(pcdev->capture.next, &pcdev->active_bufs);

View File

@ -94,8 +94,6 @@ static int debugifc_parse_unsigned_number(const char *buf,unsigned int count,
u32 *num_ptr)
{
u32 result = 0;
u32 val;
int ch;
int radix = 10;
if ((count >= 2) && (buf[0] == '0') &&
((buf[1] == 'x') || (buf[1] == 'X'))) {
@ -107,17 +105,9 @@ static int debugifc_parse_unsigned_number(const char *buf,unsigned int count,
}
while (count--) {
ch = *buf++;
if ((ch >= '0') && (ch <= '9')) {
val = ch - '0';
} else if ((ch >= 'a') && (ch <= 'f')) {
val = ch - 'a' + 10;
} else if ((ch >= 'A') && (ch <= 'F')) {
val = ch - 'A' + 10;
} else {
int val = hex_to_bin(*buf++);
if (val < 0 || val >= radix)
return -EINVAL;
}
if (val >= radix) return -EINVAL;
result *= radix;
result += val;
}

View File

@ -0,0 +1,3 @@
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) := s5p-fimc.o
s5p-fimc-y := fimc-core.o fimc-reg.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,471 @@
/*
* Copyright (c) 2010 Samsung Electronics
*
* Sylwester Nawrocki, <s.nawrocki@samsung.com>
*
* 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.
*/
#ifndef FIMC_CORE_H_
#define FIMC_CORE_H_
#include <linux/types.h>
#include <media/videobuf-core.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <linux/videodev2.h>
#include "regs-fimc.h"
#define err(fmt, args...) \
printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
#ifdef DEBUG
#define dbg(fmt, args...) \
printk(KERN_DEBUG "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
#else
#define dbg(fmt, args...)
#endif
#define NUM_FIMC_CLOCKS 2
#define MODULE_NAME "s5p-fimc"
#define FIMC_MAX_DEVS 3
#define FIMC_MAX_OUT_BUFS 4
#define SCALER_MAX_HRATIO 64
#define SCALER_MAX_VRATIO 64
enum {
ST_IDLE,
ST_OUTDMA_RUN,
ST_M2M_PEND,
};
#define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state)
#define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
enum fimc_datapath {
FIMC_ITU_CAM_A,
FIMC_ITU_CAM_B,
FIMC_MIPI_CAM,
FIMC_DMA,
FIMC_LCDFIFO,
FIMC_WRITEBACK
};
enum fimc_color_fmt {
S5P_FIMC_RGB565,
S5P_FIMC_RGB666,
S5P_FIMC_RGB888,
S5P_FIMC_YCBCR420,
S5P_FIMC_YCBCR422,
S5P_FIMC_YCBYCR422,
S5P_FIMC_YCRYCB422,
S5P_FIMC_CBYCRY422,
S5P_FIMC_CRYCBY422,
S5P_FIMC_RGB30_LOCAL,
S5P_FIMC_YCBCR444_LOCAL,
S5P_FIMC_MAX_COLOR = S5P_FIMC_YCBCR444_LOCAL,
S5P_FIMC_COLOR_MASK = 0x0F,
};
/* Y/Cb/Cr components order at DMA output for 1 plane YCbCr 4:2:2 formats. */
#define S5P_FIMC_OUT_CRYCBY S5P_CIOCTRL_ORDER422_CRYCBY
#define S5P_FIMC_OUT_CBYCRY S5P_CIOCTRL_ORDER422_YCRYCB
#define S5P_FIMC_OUT_YCRYCB S5P_CIOCTRL_ORDER422_CBYCRY
#define S5P_FIMC_OUT_YCBYCR S5P_CIOCTRL_ORDER422_YCBYCR
/* Input Y/Cb/Cr components order for 1 plane YCbCr 4:2:2 color formats. */
#define S5P_FIMC_IN_CRYCBY S5P_MSCTRL_ORDER422_CRYCBY
#define S5P_FIMC_IN_CBYCRY S5P_MSCTRL_ORDER422_YCRYCB
#define S5P_FIMC_IN_YCRYCB S5P_MSCTRL_ORDER422_CBYCRY
#define S5P_FIMC_IN_YCBYCR S5P_MSCTRL_ORDER422_YCBYCR
/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */
#define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB
/* The embedded image effect selection */
#define S5P_FIMC_EFFECT_ORIGINAL S5P_CIIMGEFF_FIN_BYPASS
#define S5P_FIMC_EFFECT_ARBITRARY S5P_CIIMGEFF_FIN_ARBITRARY
#define S5P_FIMC_EFFECT_NEGATIVE S5P_CIIMGEFF_FIN_NEGATIVE
#define S5P_FIMC_EFFECT_ARTFREEZE S5P_CIIMGEFF_FIN_ARTFREEZE
#define S5P_FIMC_EFFECT_EMBOSSING S5P_CIIMGEFF_FIN_EMBOSSING
#define S5P_FIMC_EFFECT_SIKHOUETTE S5P_CIIMGEFF_FIN_SILHOUETTE
/* The hardware context state. */
#define FIMC_PARAMS (1 << 0)
#define FIMC_SRC_ADDR (1 << 1)
#define FIMC_DST_ADDR (1 << 2)
#define FIMC_SRC_FMT (1 << 3)
#define FIMC_DST_FMT (1 << 4)
/* Image conversion flags */
#define FIMC_IN_DMA_ACCESS_TILED (1 << 0)
#define FIMC_IN_DMA_ACCESS_LINEAR (0 << 0)
#define FIMC_OUT_DMA_ACCESS_TILED (1 << 1)
#define FIMC_OUT_DMA_ACCESS_LINEAR (0 << 1)
#define FIMC_SCAN_MODE_PROGRESSIVE (0 << 2)
#define FIMC_SCAN_MODE_INTERLACED (1 << 2)
/* YCbCr data dynamic range for RGB-YUV color conversion. Y/Cb/Cr: (0 ~ 255) */
#define FIMC_COLOR_RANGE_WIDE (0 << 3)
/* Y (16 ~ 235), Cb/Cr (16 ~ 240) */
#define FIMC_COLOR_RANGE_NARROW (1 << 3)
#define FLIP_NONE 0
#define FLIP_X_AXIS 1
#define FLIP_Y_AXIS 2
#define FLIP_XY_AXIS (FLIP_X_AXIS | FLIP_Y_AXIS)
/**
* struct fimc_fmt - the driver's internal color format data
* @name: format description
* @fourcc: the fourcc code for this format
* @color: the corresponding fimc_color_fmt
* @depth: number of bits per pixel
* @buff_cnt: number of physically non-contiguous data planes
* @planes_cnt: number of physically contiguous data planes
*/
struct fimc_fmt {
char *name;
u32 fourcc;
u32 color;
u32 depth;
u16 buff_cnt;
u16 planes_cnt;
};
/**
* struct fimc_dma_offset - pixel offset information for DMA
* @y_h: y value horizontal offset
* @y_v: y value vertical offset
* @cb_h: cb value horizontal offset
* @cb_v: cb value vertical offset
* @cr_h: cr value horizontal offset
* @cr_v: cr value vertical offset
*/
struct fimc_dma_offset {
int y_h;
int y_v;
int cb_h;
int cb_v;
int cr_h;
int cr_v;
};
/**
* struct fimc_effect - the configuration data for the "Arbitrary" image effect
* @type: effect type
* @pat_cb: cr value when type is "arbitrary"
* @pat_cr: cr value when type is "arbitrary"
*/
struct fimc_effect {
u32 type;
u8 pat_cb;
u8 pat_cr;
};
/**
* struct fimc_scaler - the configuration data for FIMC inetrnal scaler
*
* @enabled: the flag set when the scaler is used
* @hfactor: horizontal shift factor
* @vfactor: vertical shift factor
* @pre_hratio: horizontal ratio of the prescaler
* @pre_vratio: vertical ratio of the prescaler
* @pre_dst_width: the prescaler's destination width
* @pre_dst_height: the prescaler's destination height
* @scaleup_h: flag indicating scaling up horizontally
* @scaleup_v: flag indicating scaling up vertically
* @main_hratio: the main scaler's horizontal ratio
* @main_vratio: the main scaler's vertical ratio
* @real_width: source width - offset
* @real_height: source height - offset
* @copy_mode: flag set if one-to-one mode is used, i.e. no scaling
* and color format conversion
*/
struct fimc_scaler {
u32 enabled;
u32 hfactor;
u32 vfactor;
u32 pre_hratio;
u32 pre_vratio;
u32 pre_dst_width;
u32 pre_dst_height;
u32 scaleup_h;
u32 scaleup_v;
u32 main_hratio;
u32 main_vratio;
u32 real_width;
u32 real_height;
u32 copy_mode;
};
/**
* struct fimc_addr - the FIMC physical address set for DMA
*
* @y: luminance plane physical address
* @cb: Cb plane physical address
* @cr: Cr plane physical address
*/
struct fimc_addr {
u32 y;
u32 cb;
u32 cr;
};
/**
* struct fimc_vid_buffer - the driver's video buffer
* @vb: v4l videobuf buffer
*/
struct fimc_vid_buffer {
struct videobuf_buffer vb;
};
/**
* struct fimc_frame - input/output frame format properties
*
* @f_width: image full width (virtual screen size)
* @f_height: image full height (virtual screen size)
* @o_width: original image width as set by S_FMT
* @o_height: original image height as set by S_FMT
* @offs_h: image horizontal pixel offset
* @offs_v: image vertical pixel offset
* @width: image pixel width
* @height: image pixel weight
* @paddr: image frame buffer physical addresses
* @buf_cnt: number of buffers depending on a color format
* @size: image size in bytes
* @color: color format
* @dma_offset: DMA offset in bytes
*/
struct fimc_frame {
u32 f_width;
u32 f_height;
u32 o_width;
u32 o_height;
u32 offs_h;
u32 offs_v;
u32 width;
u32 height;
u32 size;
struct fimc_addr paddr;
struct fimc_dma_offset dma_offset;
struct fimc_fmt *fmt;
};
/**
* struct fimc_m2m_device - v4l2 memory-to-memory device data
* @vfd: the video device node for v4l2 m2m mode
* @v4l2_dev: v4l2 device for m2m mode
* @m2m_dev: v4l2 memory-to-memory device data
* @ctx: hardware context data
* @refcnt: the reference counter
*/
struct fimc_m2m_device {
struct video_device *vfd;
struct v4l2_device v4l2_dev;
struct v4l2_m2m_dev *m2m_dev;
struct fimc_ctx *ctx;
int refcnt;
};
/**
* struct samsung_fimc_variant - camera interface variant information
*
* @pix_hoff: indicate whether horizontal offset is in pixels or in bytes
* @has_inp_rot: set if has input rotator
* @has_out_rot: set if has output rotator
* @min_inp_pixsize: minimum input pixel size
* @min_out_pixsize: minimum output pixel size
* @scaler_en_w: maximum input pixel width when the scaler is enabled
* @scaler_dis_w: maximum input pixel width when the scaler is disabled
* @in_rot_en_h: maximum input width when the input rotator is used
* @in_rot_dis_w: maximum input width when the input rotator is used
* @out_rot_en_w: maximum output width for the output rotator enabled
* @out_rot_dis_w: maximum output width for the output rotator enabled
*/
struct samsung_fimc_variant {
unsigned int pix_hoff:1;
unsigned int has_inp_rot:1;
unsigned int has_out_rot:1;
u16 min_inp_pixsize;
u16 min_out_pixsize;
u16 scaler_en_w;
u16 scaler_dis_w;
u16 in_rot_en_h;
u16 in_rot_dis_w;
u16 out_rot_en_w;
u16 out_rot_dis_w;
};
/**
* struct samsung_fimc_driverdata - per-device type driver data for init time.
*
* @variant: the variant information for this driver.
* @dev_cnt: number of fimc sub-devices available in SoC
*/
struct samsung_fimc_driverdata {
struct samsung_fimc_variant *variant[FIMC_MAX_DEVS];
int devs_cnt;
};
struct fimc_ctx;
/**
* struct fimc_subdev - abstraction for a FIMC entity
*
* @slock: the spinlock protecting this data structure
* @lock: the mutex protecting this data structure
* @pdev: pointer to the FIMC platform device
* @id: FIMC device index (0..2)
* @clock[]: the clocks required for FIMC operation
* @regs: the mapped hardware registers
* @regs_res: the resource claimed for IO registers
* @irq: interrupt number of the FIMC subdevice
* @irqlock: spinlock protecting videbuffer queue
* @m2m: memory-to-memory V4L2 device information
* @state: the FIMC device state flags
*/
struct fimc_dev {
spinlock_t slock;
struct mutex lock;
struct platform_device *pdev;
struct samsung_fimc_variant *variant;
int id;
struct clk *clock[NUM_FIMC_CLOCKS];
void __iomem *regs;
struct resource *regs_res;
int irq;
spinlock_t irqlock;
struct workqueue_struct *work_queue;
struct fimc_m2m_device m2m;
unsigned long state;
};
/**
* fimc_ctx - the device context data
*
* @lock: mutex protecting this data structure
* @s_frame: source frame properties
* @d_frame: destination frame properties
* @out_order_1p: output 1-plane YCBCR order
* @out_order_2p: output 2-plane YCBCR order
* @in_order_1p input 1-plane YCBCR order
* @in_order_2p: input 2-plane YCBCR order
* @in_path: input mode (DMA or camera)
* @out_path: output mode (DMA or FIFO)
* @scaler: image scaler properties
* @effect: image effect
* @rotation: image clockwise rotation in degrees
* @flip: image flip mode
* @flags: an additional flags for image conversion
* @state: flags to keep track of user configuration
* @fimc_dev: the FIMC device this context applies to
* @m2m_ctx: memory-to-memory device context
*/
struct fimc_ctx {
spinlock_t slock;
struct fimc_frame s_frame;
struct fimc_frame d_frame;
u32 out_order_1p;
u32 out_order_2p;
u32 in_order_1p;
u32 in_order_2p;
enum fimc_datapath in_path;
enum fimc_datapath out_path;
struct fimc_scaler scaler;
struct fimc_effect effect;
int rotation;
u32 flip;
u32 flags;
u32 state;
struct fimc_dev *fimc_dev;
struct v4l2_m2m_ctx *m2m_ctx;
};
static inline int tiled_fmt(struct fimc_fmt *fmt)
{
return 0;
}
static inline void fimc_hw_clear_irq(struct fimc_dev *dev)
{
u32 cfg = readl(dev->regs + S5P_CIGCTRL);
cfg |= S5P_CIGCTRL_IRQ_CLR;
writel(cfg, dev->regs + S5P_CIGCTRL);
}
static inline void fimc_hw_start_scaler(struct fimc_dev *dev)
{
u32 cfg = readl(dev->regs + S5P_CISCCTRL);
cfg |= S5P_CISCCTRL_SCALERSTART;
writel(cfg, dev->regs + S5P_CISCCTRL);
}
static inline void fimc_hw_stop_scaler(struct fimc_dev *dev)
{
u32 cfg = readl(dev->regs + S5P_CISCCTRL);
cfg &= ~S5P_CISCCTRL_SCALERSTART;
writel(cfg, dev->regs + S5P_CISCCTRL);
}
static inline void fimc_hw_dis_capture(struct fimc_dev *dev)
{
u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
cfg &= ~(S5P_CIIMGCPT_IMGCPTEN | S5P_CIIMGCPT_IMGCPTEN_SC);
writel(cfg, dev->regs + S5P_CIIMGCPT);
}
static inline void fimc_hw_start_in_dma(struct fimc_dev *dev)
{
u32 cfg = readl(dev->regs + S5P_MSCTRL);
cfg |= S5P_MSCTRL_ENVID;
writel(cfg, dev->regs + S5P_MSCTRL);
}
static inline void fimc_hw_stop_in_dma(struct fimc_dev *dev)
{
u32 cfg = readl(dev->regs + S5P_MSCTRL);
cfg &= ~S5P_MSCTRL_ENVID;
writel(cfg, dev->regs + S5P_MSCTRL);
}
static inline struct fimc_frame *ctx_m2m_get_frame(struct fimc_ctx *ctx,
enum v4l2_buf_type type)
{
struct fimc_frame *frame;
if (V4L2_BUF_TYPE_VIDEO_OUTPUT == type) {
frame = &ctx->s_frame;
} else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) {
frame = &ctx->d_frame;
} else {
v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
"Wrong buffer/video queue type (%d)\n", type);
return ERR_PTR(-EINVAL);
}
return frame;
}
/* -----------------------------------------------------*/
/* fimc-reg.c */
void fimc_hw_reset(struct fimc_dev *dev);
void fimc_hw_set_rotation(struct fimc_ctx *ctx);
void fimc_hw_set_target_format(struct fimc_ctx *ctx);
void fimc_hw_set_out_dma(struct fimc_ctx *ctx);
void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable);
void fimc_hw_en_irq(struct fimc_dev *dev, int enable);
void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
void fimc_hw_set_scaler(struct fimc_ctx *ctx);
void fimc_hw_en_capture(struct fimc_ctx *ctx);
void fimc_hw_set_effect(struct fimc_ctx *ctx);
void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
void fimc_hw_set_input_path(struct fimc_ctx *ctx);
void fimc_hw_set_output_path(struct fimc_ctx *ctx);
void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr);
void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr);
#endif /* FIMC_CORE_H_ */

View File

@ -0,0 +1,527 @@
/*
* Register interface file for Samsung Camera Interface (FIMC) driver
*
* Copyright (c) 2010 Samsung Electronics
*
* Sylwester Nawrocki, s.nawrocki@samsung.com
*
* 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.
*/
#include <linux/io.h>
#include <linux/delay.h>
#include <mach/map.h>
#include "fimc-core.h"
void fimc_hw_reset(struct fimc_dev *dev)
{
u32 cfg;
cfg = readl(dev->regs + S5P_CISRCFMT);
cfg |= S5P_CISRCFMT_ITU601_8BIT;
writel(cfg, dev->regs + S5P_CISRCFMT);
/* Software reset. */
cfg = readl(dev->regs + S5P_CIGCTRL);
cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
writel(cfg, dev->regs + S5P_CIGCTRL);
msleep(1);
cfg = readl(dev->regs + S5P_CIGCTRL);
cfg &= ~S5P_CIGCTRL_SWRST;
writel(cfg, dev->regs + S5P_CIGCTRL);
}
void fimc_hw_set_rotation(struct fimc_ctx *ctx)
{
u32 cfg, flip;
struct fimc_dev *dev = ctx->fimc_dev;
cfg = readl(dev->regs + S5P_CITRGFMT);
cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90);
flip = readl(dev->regs + S5P_MSCTRL);
flip &= ~S5P_MSCTRL_FLIP_MASK;
/*
* The input and output rotator cannot work simultaneously.
* Use the output rotator in output DMA mode or the input rotator
* in direct fifo output mode.
*/
if (ctx->rotation == 90 || ctx->rotation == 270) {
if (ctx->out_path == FIMC_LCDFIFO) {
cfg |= S5P_CITRGFMT_INROT90;
if (ctx->rotation == 270)
flip |= S5P_MSCTRL_FLIP_180;
} else {
cfg |= S5P_CITRGFMT_OUTROT90;
if (ctx->rotation == 270)
cfg |= S5P_CITRGFMT_FLIP_180;
}
} else if (ctx->rotation == 180) {
if (ctx->out_path == FIMC_LCDFIFO)
flip |= S5P_MSCTRL_FLIP_180;
else
cfg |= S5P_CITRGFMT_FLIP_180;
}
if (ctx->rotation == 180 || ctx->rotation == 270)
writel(flip, dev->regs + S5P_MSCTRL);
writel(cfg, dev->regs + S5P_CITRGFMT);
}
static u32 fimc_hw_get_in_flip(u32 ctx_flip)
{
u32 flip = S5P_MSCTRL_FLIP_NORMAL;
switch (ctx_flip) {
case FLIP_X_AXIS:
flip = S5P_MSCTRL_FLIP_X_MIRROR;
break;
case FLIP_Y_AXIS:
flip = S5P_MSCTRL_FLIP_Y_MIRROR;
break;
case FLIP_XY_AXIS:
flip = S5P_MSCTRL_FLIP_180;
break;
}
return flip;
}
static u32 fimc_hw_get_target_flip(u32 ctx_flip)
{
u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
switch (ctx_flip) {
case FLIP_X_AXIS:
flip = S5P_CITRGFMT_FLIP_X_MIRROR;
break;
case FLIP_Y_AXIS:
flip = S5P_CITRGFMT_FLIP_Y_MIRROR;
break;
case FLIP_XY_AXIS:
flip = S5P_CITRGFMT_FLIP_180;
break;
case FLIP_NONE:
break;
}
return flip;
}
void fimc_hw_set_target_format(struct fimc_ctx *ctx)
{
u32 cfg;
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_frame *frame = &ctx->d_frame;
dbg("w= %d, h= %d color: %d", frame->width,
frame->height, frame->fmt->color);
cfg = readl(dev->regs + S5P_CITRGFMT);
cfg &= ~(S5P_CITRGFMT_FMT_MASK | S5P_CITRGFMT_HSIZE_MASK |
S5P_CITRGFMT_VSIZE_MASK);
switch (frame->fmt->color) {
case S5P_FIMC_RGB565:
case S5P_FIMC_RGB666:
case S5P_FIMC_RGB888:
cfg |= S5P_CITRGFMT_RGB;
break;
case S5P_FIMC_YCBCR420:
cfg |= S5P_CITRGFMT_YCBCR420;
break;
case S5P_FIMC_YCBYCR422:
case S5P_FIMC_YCRYCB422:
case S5P_FIMC_CBYCRY422:
case S5P_FIMC_CRYCBY422:
if (frame->fmt->planes_cnt == 1)
cfg |= S5P_CITRGFMT_YCBCR422_1P;
else
cfg |= S5P_CITRGFMT_YCBCR422;
break;
default:
break;
}
cfg |= S5P_CITRGFMT_HSIZE(frame->width);
cfg |= S5P_CITRGFMT_VSIZE(frame->height);
if (ctx->rotation == 0) {
cfg &= ~S5P_CITRGFMT_FLIP_MASK;
cfg |= fimc_hw_get_target_flip(ctx->flip);
}
writel(cfg, dev->regs + S5P_CITRGFMT);
cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK;
cfg |= (frame->width * frame->height);
writel(cfg, dev->regs + S5P_CITAREA);
}
static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_frame *frame = &ctx->d_frame;
u32 cfg = 0;
if (ctx->rotation == 90 || ctx->rotation == 270) {
cfg |= S5P_ORIG_SIZE_HOR(frame->f_height);
cfg |= S5P_ORIG_SIZE_VER(frame->f_width);
} else {
cfg |= S5P_ORIG_SIZE_HOR(frame->f_width);
cfg |= S5P_ORIG_SIZE_VER(frame->f_height);
}
writel(cfg, dev->regs + S5P_ORGOSIZE);
}
void fimc_hw_set_out_dma(struct fimc_ctx *ctx)
{
u32 cfg;
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_frame *frame = &ctx->d_frame;
struct fimc_dma_offset *offset = &frame->dma_offset;
/* Set the input dma offsets. */
cfg = 0;
cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
cfg |= S5P_CIO_OFFS_VER(offset->y_v);
writel(cfg, dev->regs + S5P_CIOYOFF);
cfg = 0;
cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
writel(cfg, dev->regs + S5P_CIOCBOFF);
cfg = 0;
cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
writel(cfg, dev->regs + S5P_CIOCROFF);
fimc_hw_set_out_dma_size(ctx);
/* Configure chroma components order. */
cfg = readl(dev->regs + S5P_CIOCTRL);
cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK |
S5P_CIOCTRL_YCBCR_PLANE_MASK);
if (frame->fmt->planes_cnt == 1)
cfg |= ctx->out_order_1p;
else if (frame->fmt->planes_cnt == 2)
cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE;
else if (frame->fmt->planes_cnt == 3)
cfg |= S5P_CIOCTRL_YCBCR_3PLANE;
writel(cfg, dev->regs + S5P_CIOCTRL);
}
static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable)
{
u32 cfg = readl(dev->regs + S5P_ORGISIZE);
if (enable)
cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
else
cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_EN;
writel(cfg, dev->regs + S5P_ORGISIZE);
}
void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable)
{
unsigned long flags;
u32 cfg;
spin_lock_irqsave(&dev->slock, flags);
cfg = readl(dev->regs + S5P_CIOCTRL);
if (enable)
cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE;
else
cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE;
writel(cfg, dev->regs + S5P_CIOCTRL);
spin_unlock_irqrestore(&dev->slock, flags);
}
void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_scaler *sc = &ctx->scaler;
u32 cfg = 0, shfactor;
shfactor = 10 - (sc->hfactor + sc->vfactor);
cfg |= S5P_CISCPRERATIO_SHFACTOR(shfactor);
cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio);
cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio);
writel(cfg, dev->regs + S5P_CISCPRERATIO);
cfg = 0;
cfg |= S5P_CISCPREDST_WIDTH(sc->pre_dst_width);
cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height);
writel(cfg, dev->regs + S5P_CISCPREDST);
}
void fimc_hw_set_scaler(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_scaler *sc = &ctx->scaler;
struct fimc_frame *src_frame = &ctx->s_frame;
struct fimc_frame *dst_frame = &ctx->d_frame;
u32 cfg = 0;
if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW))
cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE);
if (!sc->enabled)
cfg |= S5P_CISCCTRL_SCALERBYPASS;
if (sc->scaleup_h)
cfg |= S5P_CISCCTRL_SCALEUP_H;
if (sc->scaleup_v)
cfg |= S5P_CISCCTRL_SCALEUP_V;
if (sc->copy_mode)
cfg |= S5P_CISCCTRL_ONE2ONE;
if (ctx->in_path == FIMC_DMA) {
if (src_frame->fmt->color == S5P_FIMC_RGB565)
cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565;
else if (src_frame->fmt->color == S5P_FIMC_RGB666)
cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666;
else if (src_frame->fmt->color == S5P_FIMC_RGB888)
cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888;
}
if (ctx->out_path == FIMC_DMA) {
if (dst_frame->fmt->color == S5P_FIMC_RGB565)
cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565;
else if (dst_frame->fmt->color == S5P_FIMC_RGB666)
cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666;
else if (dst_frame->fmt->color == S5P_FIMC_RGB888)
cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
} else {
cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
if (ctx->flags & FIMC_SCAN_MODE_INTERLACED)
cfg |= S5P_CISCCTRL_INTERLACE;
}
dbg("main_hratio= 0x%X main_vratio= 0x%X",
sc->main_hratio, sc->main_vratio);
cfg |= S5P_CISCCTRL_SC_HORRATIO(sc->main_hratio);
cfg |= S5P_CISCCTRL_SC_VERRATIO(sc->main_vratio);
writel(cfg, dev->regs + S5P_CISCCTRL);
}
void fimc_hw_en_capture(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
u32 cfg;
cfg = readl(dev->regs + S5P_CIIMGCPT);
/* One shot mode for output DMA or freerun for FIFO. */
if (ctx->out_path == FIMC_DMA)
cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE;
else
cfg &= ~S5P_CIIMGCPT_CPT_FREN_ENABLE;
if (ctx->scaler.enabled)
cfg |= S5P_CIIMGCPT_IMGCPTEN_SC;
writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT);
}
void fimc_hw_set_effect(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_effect *effect = &ctx->effect;
u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER);
cfg |= effect->type;
if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
}
writel(cfg, dev->regs + S5P_CIIMGEFF);
}
static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_frame *frame = &ctx->s_frame;
u32 cfg_o = 0;
u32 cfg_r = 0;
if (FIMC_LCDFIFO == ctx->out_path)
cfg_r |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width);
cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height);
cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width);
cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height);
writel(cfg_o, dev->regs + S5P_ORGISIZE);
writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE);
}
void fimc_hw_set_in_dma(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_frame *frame = &ctx->s_frame;
struct fimc_dma_offset *offset = &frame->dma_offset;
u32 cfg = 0;
/* Set the pixel offsets. */
cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
cfg |= S5P_CIO_OFFS_VER(offset->y_v);
writel(cfg, dev->regs + S5P_CIIYOFF);
cfg = 0;
cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
writel(cfg, dev->regs + S5P_CIICBOFF);
cfg = 0;
cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
writel(cfg, dev->regs + S5P_CIICROFF);
/* Input original and real size. */
fimc_hw_set_in_dma_size(ctx);
/* Autoload is used currently only in FIFO mode. */
fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO);
/* Set the input DMA to process single frame only. */
cfg = readl(dev->regs + S5P_MSCTRL);
cfg &= ~(S5P_MSCTRL_FLIP_MASK
| S5P_MSCTRL_INFORMAT_MASK
| S5P_MSCTRL_IN_BURST_COUNT_MASK
| S5P_MSCTRL_INPUT_MASK
| S5P_MSCTRL_C_INT_IN_MASK
| S5P_MSCTRL_2P_IN_ORDER_MASK);
cfg |= (S5P_MSCTRL_FRAME_COUNT(1) | S5P_MSCTRL_INPUT_MEMORY);
switch (frame->fmt->color) {
case S5P_FIMC_RGB565:
case S5P_FIMC_RGB666:
case S5P_FIMC_RGB888:
cfg |= S5P_MSCTRL_INFORMAT_RGB;
break;
case S5P_FIMC_YCBCR420:
cfg |= S5P_MSCTRL_INFORMAT_YCBCR420;
if (frame->fmt->planes_cnt == 2)
cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE;
else
cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
break;
case S5P_FIMC_YCBYCR422:
case S5P_FIMC_YCRYCB422:
case S5P_FIMC_CBYCRY422:
case S5P_FIMC_CRYCBY422:
if (frame->fmt->planes_cnt == 1) {
cfg |= ctx->in_order_1p
| S5P_MSCTRL_INFORMAT_YCBCR422_1P;
} else {
cfg |= S5P_MSCTRL_INFORMAT_YCBCR422;
if (frame->fmt->planes_cnt == 2)
cfg |= ctx->in_order_2p
| S5P_MSCTRL_C_INT_IN_2PLANE;
else
cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
}
break;
default:
break;
}
/*
* Input DMA flip mode (and rotation).
* Do not allow simultaneous rotation and flipping.
*/
if (!ctx->rotation && ctx->out_path == FIMC_LCDFIFO)
cfg |= fimc_hw_get_in_flip(ctx->flip);
writel(cfg, dev->regs + S5P_MSCTRL);
/* Input/output DMA linear/tiled mode. */
cfg = readl(dev->regs + S5P_CIDMAPARAM);
cfg &= ~S5P_CIDMAPARAM_TILE_MASK;
if (tiled_fmt(ctx->s_frame.fmt))
cfg |= S5P_CIDMAPARAM_R_64X32;
if (tiled_fmt(ctx->d_frame.fmt))
cfg |= S5P_CIDMAPARAM_W_64X32;
writel(cfg, dev->regs + S5P_CIDMAPARAM);
}
void fimc_hw_set_input_path(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
u32 cfg = readl(dev->regs + S5P_MSCTRL);
cfg &= ~S5P_MSCTRL_INPUT_MASK;
if (ctx->in_path == FIMC_DMA)
cfg |= S5P_MSCTRL_INPUT_MEMORY;
else
cfg |= S5P_MSCTRL_INPUT_EXTCAM;
writel(cfg, dev->regs + S5P_MSCTRL);
}
void fimc_hw_set_output_path(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
u32 cfg = readl(dev->regs + S5P_CISCCTRL);
cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO;
if (ctx->out_path == FIMC_LCDFIFO)
cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO;
writel(cfg, dev->regs + S5P_CISCCTRL);
}
void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
{
u32 cfg = 0;
cfg = readl(dev->regs + S5P_CIREAL_ISIZE);
cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS;
writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
writel(paddr->y, dev->regs + S5P_CIIYSA0);
writel(paddr->cb, dev->regs + S5P_CIICBSA0);
writel(paddr->cr, dev->regs + S5P_CIICRSA0);
cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS;
writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
}
void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
{
int i;
/* Set all the output register sets to point to single video buffer. */
for (i = 0; i < FIMC_MAX_OUT_BUFS; i++) {
writel(paddr->y, dev->regs + S5P_CIOYSA(i));
writel(paddr->cb, dev->regs + S5P_CIOCBSA(i));
writel(paddr->cr, dev->regs + S5P_CIOCRSA(i));
}
}

View File

@ -0,0 +1,293 @@
/*
* Register definition file for Samsung Camera Interface (FIMC) driver
*
* Copyright (c) 2010 Samsung Electronics
*
* 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.
*/
#ifndef REGS_FIMC_H_
#define REGS_FIMC_H_
#define S5P_CIOYSA(__x) (0x18 + (__x) * 4)
#define S5P_CIOCBSA(__x) (0x28 + (__x) * 4)
#define S5P_CIOCRSA(__x) (0x38 + (__x) * 4)
/* Input source format */
#define S5P_CISRCFMT 0x00
#define S5P_CISRCFMT_ITU601_8BIT (1 << 31)
#define S5P_CISRCFMT_ITU601_16BIT (1 << 29)
#define S5P_CISRCFMT_ORDER422_YCBYCR (0 << 14)
#define S5P_CISRCFMT_ORDER422_YCRYCB (1 << 14)
#define S5P_CISRCFMT_ORDER422_CBYCRY (2 << 14)
#define S5P_CISRCFMT_ORDER422_CRYCBY (3 << 14)
#define S5P_CISRCFMT_HSIZE(x) ((x) << 16)
#define S5P_CISRCFMT_VSIZE(x) ((x) << 0)
/* Window offset */
#define S5P_CIWDOFST 0x04
#define S5P_CIWDOFST_WINOFSEN (1 << 31)
#define S5P_CIWDOFST_CLROVFIY (1 << 30)
#define S5P_CIWDOFST_CLROVRLB (1 << 29)
#define S5P_CIWDOFST_WINHOROFST_MASK (0x7ff << 16)
#define S5P_CIWDOFST_CLROVFICB (1 << 15)
#define S5P_CIWDOFST_CLROVFICR (1 << 14)
#define S5P_CIWDOFST_WINHOROFST(x) ((x) << 16)
#define S5P_CIWDOFST_WINVEROFST(x) ((x) << 0)
#define S5P_CIWDOFST_WINVEROFST_MASK (0xfff << 0)
/* Global control */
#define S5P_CIGCTRL 0x08
#define S5P_CIGCTRL_SWRST (1 << 31)
#define S5P_CIGCTRL_CAMRST_A (1 << 30)
#define S5P_CIGCTRL_SELCAM_ITU_A (1 << 29)
#define S5P_CIGCTRL_SELCAM_ITU_MASK (1 << 29)
#define S5P_CIGCTRL_TESTPAT_NORMAL (0 << 27)
#define S5P_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27)
#define S5P_CIGCTRL_TESTPAT_HOR_INC (2 << 27)
#define S5P_CIGCTRL_TESTPAT_VER_INC (3 << 27)
#define S5P_CIGCTRL_TESTPAT_MASK (3 << 27)
#define S5P_CIGCTRL_TESTPAT_SHIFT (27)
#define S5P_CIGCTRL_INVPOLPCLK (1 << 26)
#define S5P_CIGCTRL_INVPOLVSYNC (1 << 25)
#define S5P_CIGCTRL_INVPOLHREF (1 << 24)
#define S5P_CIGCTRL_IRQ_OVFEN (1 << 22)
#define S5P_CIGCTRL_HREF_MASK (1 << 21)
#define S5P_CIGCTRL_IRQ_LEVEL (1 << 20)
#define S5P_CIGCTRL_IRQ_CLR (1 << 19)
#define S5P_CIGCTRL_IRQ_ENABLE (1 << 16)
#define S5P_CIGCTRL_SHDW_DISABLE (1 << 12)
#define S5P_CIGCTRL_SELCAM_MIPI_A (1 << 7)
#define S5P_CIGCTRL_CAMIF_SELWB (1 << 6)
#define S5P_CIGCTRL_INVPOLHSYNC (1 << 4)
#define S5P_CIGCTRL_SELCAM_MIPI (1 << 3)
#define S5P_CIGCTRL_INTERLACE (1 << 0)
/* Window offset 2 */
#define S5P_CIWDOFST2 0x14
#define S5P_CIWDOFST2_HOROFF_MASK (0xfff << 16)
#define S5P_CIWDOFST2_VEROFF_MASK (0xfff << 0)
#define S5P_CIWDOFST2_HOROFF(x) ((x) << 16)
#define S5P_CIWDOFST2_VEROFF(x) ((x) << 0)
/* Output DMA Y plane start address */
#define S5P_CIOYSA1 0x18
#define S5P_CIOYSA2 0x1c
#define S5P_CIOYSA3 0x20
#define S5P_CIOYSA4 0x24
/* Output DMA Cb plane start address */
#define S5P_CIOCBSA1 0x28
#define S5P_CIOCBSA2 0x2c
#define S5P_CIOCBSA3 0x30
#define S5P_CIOCBSA4 0x34
/* Output DMA Cr plane start address */
#define S5P_CIOCRSA1 0x38
#define S5P_CIOCRSA2 0x3c
#define S5P_CIOCRSA3 0x40
#define S5P_CIOCRSA4 0x44
/* Target image format */
#define S5P_CITRGFMT 0x48
#define S5P_CITRGFMT_INROT90 (1 << 31)
#define S5P_CITRGFMT_YCBCR420 (0 << 29)
#define S5P_CITRGFMT_YCBCR422 (1 << 29)
#define S5P_CITRGFMT_YCBCR422_1P (2 << 29)
#define S5P_CITRGFMT_RGB (3 << 29)
#define S5P_CITRGFMT_FMT_MASK (3 << 29)
#define S5P_CITRGFMT_HSIZE_MASK (0xfff << 16)
#define S5P_CITRGFMT_FLIP_SHIFT (14)
#define S5P_CITRGFMT_FLIP_NORMAL (0 << 14)
#define S5P_CITRGFMT_FLIP_X_MIRROR (1 << 14)
#define S5P_CITRGFMT_FLIP_Y_MIRROR (2 << 14)
#define S5P_CITRGFMT_FLIP_180 (3 << 14)
#define S5P_CITRGFMT_FLIP_MASK (3 << 14)
#define S5P_CITRGFMT_OUTROT90 (1 << 13)
#define S5P_CITRGFMT_VSIZE_MASK (0xfff << 0)
#define S5P_CITRGFMT_HSIZE(x) ((x) << 16)
#define S5P_CITRGFMT_VSIZE(x) ((x) << 0)
/* Output DMA control */
#define S5P_CIOCTRL 0x4c
#define S5P_CIOCTRL_ORDER422_MASK (3 << 0)
#define S5P_CIOCTRL_ORDER422_CRYCBY (0 << 0)
#define S5P_CIOCTRL_ORDER422_YCRYCB (1 << 0)
#define S5P_CIOCTRL_ORDER422_CBYCRY (2 << 0)
#define S5P_CIOCTRL_ORDER422_YCBYCR (3 << 0)
#define S5P_CIOCTRL_LASTIRQ_ENABLE (1 << 2)
#define S5P_CIOCTRL_YCBCR_3PLANE (0 << 3)
#define S5P_CIOCTRL_YCBCR_2PLANE (1 << 3)
#define S5P_CIOCTRL_YCBCR_PLANE_MASK (1 << 3)
#define S5P_CIOCTRL_ORDER2P_SHIFT (24)
#define S5P_CIOCTRL_ORDER2P_MASK (3 << 24)
#define S5P_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24)
/* Pre-scaler control 1 */
#define S5P_CISCPRERATIO 0x50
#define S5P_CISCPRERATIO_SHFACTOR(x) ((x) << 28)
#define S5P_CISCPRERATIO_HOR(x) ((x) << 16)
#define S5P_CISCPRERATIO_VER(x) ((x) << 0)
#define S5P_CISCPREDST 0x54
#define S5P_CISCPREDST_WIDTH(x) ((x) << 16)
#define S5P_CISCPREDST_HEIGHT(x) ((x) << 0)
/* Main scaler control */
#define S5P_CISCCTRL 0x58
#define S5P_CISCCTRL_SCALERBYPASS (1 << 31)
#define S5P_CISCCTRL_SCALEUP_H (1 << 30)
#define S5P_CISCCTRL_SCALEUP_V (1 << 29)
#define S5P_CISCCTRL_CSCR2Y_WIDE (1 << 28)
#define S5P_CISCCTRL_CSCY2R_WIDE (1 << 27)
#define S5P_CISCCTRL_LCDPATHEN_FIFO (1 << 26)
#define S5P_CISCCTRL_INTERLACE (1 << 25)
#define S5P_CISCCTRL_SCALERSTART (1 << 15)
#define S5P_CISCCTRL_INRGB_FMT_RGB565 (0 << 13)
#define S5P_CISCCTRL_INRGB_FMT_RGB666 (1 << 13)
#define S5P_CISCCTRL_INRGB_FMT_RGB888 (2 << 13)
#define S5P_CISCCTRL_INRGB_FMT_MASK (3 << 13)
#define S5P_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11)
#define S5P_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11)
#define S5P_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11)
#define S5P_CISCCTRL_OUTRGB_FMT_MASK (3 << 11)
#define S5P_CISCCTRL_RGB_EXT (1 << 10)
#define S5P_CISCCTRL_ONE2ONE (1 << 9)
#define S5P_CISCCTRL_SC_HORRATIO(x) ((x) << 16)
#define S5P_CISCCTRL_SC_VERRATIO(x) ((x) << 0)
/* Target area */
#define S5P_CITAREA 0x5c
#define S5P_CITAREA_MASK 0x0fffffff
/* General status */
#define S5P_CISTATUS 0x64
#define S5P_CISTATUS_OVFIY (1 << 31)
#define S5P_CISTATUS_OVFICB (1 << 30)
#define S5P_CISTATUS_OVFICR (1 << 29)
#define S5P_CISTATUS_VSYNC (1 << 28)
#define S5P_CISTATUS_WINOFF_EN (1 << 25)
#define S5P_CISTATUS_IMGCPT_EN (1 << 22)
#define S5P_CISTATUS_IMGCPT_SCEN (1 << 21)
#define S5P_CISTATUS_VSYNC_A (1 << 20)
#define S5P_CISTATUS_VSYNC_B (1 << 19)
#define S5P_CISTATUS_OVRLB (1 << 18)
#define S5P_CISTATUS_FRAME_END (1 << 17)
#define S5P_CISTATUS_LASTCAPT_END (1 << 16)
#define S5P_CISTATUS_VVALID_A (1 << 15)
#define S5P_CISTATUS_VVALID_B (1 << 14)
/* Image capture control */
#define S5P_CIIMGCPT 0xc0
#define S5P_CIIMGCPT_IMGCPTEN (1 << 31)
#define S5P_CIIMGCPT_IMGCPTEN_SC (1 << 30)
#define S5P_CIIMGCPT_CPT_FREN_ENABLE (1 << 25)
#define S5P_CIIMGCPT_CPT_FRMOD_CNT (1 << 18)
/* Frame capture sequence */
#define S5P_CICPTSEQ 0xc4
/* Image effect */
#define S5P_CIIMGEFF 0xd0
#define S5P_CIIMGEFF_IE_DISABLE (0 << 30)
#define S5P_CIIMGEFF_IE_ENABLE (1 << 30)
#define S5P_CIIMGEFF_IE_SC_BEFORE (0 << 29)
#define S5P_CIIMGEFF_IE_SC_AFTER (1 << 29)
#define S5P_CIIMGEFF_FIN_BYPASS (0 << 26)
#define S5P_CIIMGEFF_FIN_ARBITRARY (1 << 26)
#define S5P_CIIMGEFF_FIN_NEGATIVE (2 << 26)
#define S5P_CIIMGEFF_FIN_ARTFREEZE (3 << 26)
#define S5P_CIIMGEFF_FIN_EMBOSSING (4 << 26)
#define S5P_CIIMGEFF_FIN_SILHOUETTE (5 << 26)
#define S5P_CIIMGEFF_FIN_MASK (7 << 26)
#define S5P_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0))
#define S5P_CIIMGEFF_PAT_CB(x) ((x) << 13)
#define S5P_CIIMGEFF_PAT_CR(x) ((x) << 0)
/* Input DMA Y/Cb/Cr plane start address 0 */
#define S5P_CIIYSA0 0xd4
#define S5P_CIICBSA0 0xd8
#define S5P_CIICRSA0 0xdc
/* Real input DMA image size */
#define S5P_CIREAL_ISIZE 0xf8
#define S5P_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31)
#define S5P_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30)
#define S5P_CIREAL_ISIZE_HEIGHT(x) ((x) << 16)
#define S5P_CIREAL_ISIZE_WIDTH(x) ((x) << 0)
/* Input DMA control */
#define S5P_MSCTRL 0xfc
#define S5P_MSCTRL_IN_BURST_COUNT_MASK (3 << 24)
#define S5P_MSCTRL_2P_IN_ORDER_MASK (3 << 16)
#define S5P_MSCTRL_2P_IN_ORDER_SHIFT 16
#define S5P_MSCTRL_C_INT_IN_3PLANE (0 << 15)
#define S5P_MSCTRL_C_INT_IN_2PLANE (1 << 15)
#define S5P_MSCTRL_C_INT_IN_MASK (1 << 15)
#define S5P_MSCTRL_FLIP_SHIFT 13
#define S5P_MSCTRL_FLIP_MASK (3 << 13)
#define S5P_MSCTRL_FLIP_NORMAL (0 << 13)
#define S5P_MSCTRL_FLIP_X_MIRROR (1 << 13)
#define S5P_MSCTRL_FLIP_Y_MIRROR (2 << 13)
#define S5P_MSCTRL_FLIP_180 (3 << 13)
#define S5P_MSCTRL_ORDER422_SHIFT 4
#define S5P_MSCTRL_ORDER422_CRYCBY (0 << 4)
#define S5P_MSCTRL_ORDER422_YCRYCB (1 << 4)
#define S5P_MSCTRL_ORDER422_CBYCRY (2 << 4)
#define S5P_MSCTRL_ORDER422_YCBYCR (3 << 4)
#define S5P_MSCTRL_ORDER422_MASK (3 << 4)
#define S5P_MSCTRL_INPUT_EXTCAM (0 << 3)
#define S5P_MSCTRL_INPUT_MEMORY (1 << 3)
#define S5P_MSCTRL_INPUT_MASK (1 << 3)
#define S5P_MSCTRL_INFORMAT_YCBCR420 (0 << 1)
#define S5P_MSCTRL_INFORMAT_YCBCR422 (1 << 1)
#define S5P_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1)
#define S5P_MSCTRL_INFORMAT_RGB (3 << 1)
#define S5P_MSCTRL_INFORMAT_MASK (3 << 1)
#define S5P_MSCTRL_ENVID (1 << 0)
#define S5P_MSCTRL_FRAME_COUNT(x) ((x) << 24)
/* Input DMA Y/Cb/Cr plane start address 1 */
#define S5P_CIIYSA1 0x144
#define S5P_CIICBSA1 0x148
#define S5P_CIICRSA1 0x14c
/* Output DMA Y/Cb/Cr offset */
#define S5P_CIOYOFF 0x168
#define S5P_CIOCBOFF 0x16c
#define S5P_CIOCROFF 0x170
/* Input DMA Y/Cb/Cr offset */
#define S5P_CIIYOFF 0x174
#define S5P_CIICBOFF 0x178
#define S5P_CIICROFF 0x17c
#define S5P_CIO_OFFS_VER(x) ((x) << 16)
#define S5P_CIO_OFFS_HOR(x) ((x) << 0)
/* Input DMA original image size */
#define S5P_ORGISIZE 0x180
/* Output DMA original image size */
#define S5P_ORGOSIZE 0x184
#define S5P_ORIG_SIZE_VER(x) ((x) << 16)
#define S5P_ORIG_SIZE_HOR(x) ((x) << 0)
/* Real output DMA image size (extension register) */
#define S5P_CIEXTEN 0x188
#define S5P_CIDMAPARAM 0x18c
#define S5P_CIDMAPARAM_R_LINEAR (0 << 29)
#define S5P_CIDMAPARAM_R_64X32 (3 << 29)
#define S5P_CIDMAPARAM_W_LINEAR (0 << 13)
#define S5P_CIDMAPARAM_W_64X32 (3 << 13)
#define S5P_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13))
/* MIPI CSI image format */
#define S5P_CSIIMGFMT 0x194
#endif /* REGS_FIMC_H_ */

View File

@ -45,6 +45,7 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/saa7115.h>
@ -65,16 +66,19 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
struct saa711x_state {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
struct {
/* chroma gain control cluster */
struct v4l2_ctrl *agc;
struct v4l2_ctrl *gain;
};
v4l2_std_id std;
int input;
int output;
int enable;
int radio;
int bright;
int contrast;
int hue;
int sat;
int chroma_agc;
int width;
int height;
u32 ident;
@ -90,6 +94,11 @@ static inline struct saa711x_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct saa711x_state, sd);
}
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd;
}
/* ----------------------------------------------------------------------- */
static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
@ -741,96 +750,53 @@ static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
return 0;
}
static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
struct saa711x_state *state = to_state(sd);
u8 val;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value < 0 || ctrl->value > 255) {
v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
return -ERANGE;
}
state->bright = ctrl->value;
saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value < 0 || ctrl->value > 127) {
v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
return -ERANGE;
}
state->contrast = ctrl->value;
saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
break;
case V4L2_CID_SATURATION:
if (ctrl->value < 0 || ctrl->value > 127) {
v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
return -ERANGE;
}
state->sat = ctrl->value;
saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat);
break;
case V4L2_CID_HUE:
if (ctrl->value < -128 || ctrl->value > 127) {
v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
return -ERANGE;
}
state->hue = ctrl->value;
saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue);
break;
case V4L2_CID_CHROMA_AGC:
val = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL);
state->chroma_agc = ctrl->value;
if (ctrl->value)
val &= 0x7f;
else
val |= 0x80;
saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, val);
/* chroma gain cluster */
if (state->agc->cur.val)
state->gain->cur.val =
saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
break;
case V4L2_CID_CHROMA_GAIN:
/* Chroma gain cannot be set when AGC is enabled */
if (state->chroma_agc == 1)
return -EINVAL;
saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, ctrl->value | 0x80);
break;
default:
return -EINVAL;
}
return 0;
}
static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
struct saa711x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->value = state->bright;
saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val);
break;
case V4L2_CID_CONTRAST:
ctrl->value = state->contrast;
saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val);
break;
case V4L2_CID_SATURATION:
ctrl->value = state->sat;
saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val);
break;
case V4L2_CID_HUE:
ctrl->value = state->hue;
saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val);
break;
case V4L2_CID_CHROMA_AGC:
ctrl->value = state->chroma_agc;
break;
case V4L2_CID_CHROMA_GAIN:
ctrl->value = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
/* chroma gain cluster */
if (state->agc->val)
saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
else
saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
v4l2_ctrl_activate(state->gain, !state->agc->val);
break;
default:
return -EINVAL;
}
@ -1223,25 +1189,6 @@ static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
return 0;
}
static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
switch (qc->id) {
case V4L2_CID_BRIGHTNESS:
return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
case V4L2_CID_CONTRAST:
case V4L2_CID_SATURATION:
return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
case V4L2_CID_HUE:
return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
case V4L2_CID_CHROMA_AGC:
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
case V4L2_CID_CHROMA_GAIN:
return v4l2_ctrl_query_fill(qc, 0, 127, 1, 48);
default:
return -EINVAL;
}
}
static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct saa711x_state *state = to_state(sd);
@ -1518,17 +1465,27 @@ static int saa711x_log_status(struct v4l2_subdev *sd)
break;
}
v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height);
v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
.s_ctrl = saa711x_s_ctrl,
.g_volatile_ctrl = saa711x_g_volatile_ctrl,
};
static const struct v4l2_subdev_core_ops saa711x_core_ops = {
.log_status = saa711x_log_status,
.g_chip_ident = saa711x_g_chip_ident,
.g_ctrl = saa711x_g_ctrl,
.s_ctrl = saa711x_s_ctrl,
.queryctrl = saa711x_queryctrl,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
.s_std = saa711x_s_std,
.reset = saa711x_reset,
.s_gpio = saa711x_s_gpio,
@ -1579,8 +1536,9 @@ static int saa711x_probe(struct i2c_client *client,
{
struct saa711x_state *state;
struct v4l2_subdev *sd;
int i;
char name[17];
struct v4l2_ctrl_handler *hdl;
int i;
char name[17];
char chip_id;
int autodetect = !id || id->driver_data == 1;
@ -1619,15 +1577,38 @@ static int saa711x_probe(struct i2c_client *client,
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
hdl = &state->hdl;
v4l2_ctrl_handler_init(hdl, 6);
/* add in ascending ID order */
v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
V4L2_CID_CONTRAST, 0, 127, 1, 64);
v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
V4L2_CID_SATURATION, 0, 127, 1, 64);
v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
state->gain->is_volatile = 1;
sd->ctrl_handler = hdl;
if (hdl->error) {
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
kfree(state);
return err;
}
state->agc->flags |= V4L2_CTRL_FLAG_UPDATE;
v4l2_ctrl_cluster(2, &state->agc);
state->input = -1;
state->output = SAA7115_IPORT_ON;
state->enable = 1;
state->radio = 0;
state->bright = 128;
state->contrast = 64;
state->hue = 0;
state->sat = 64;
state->chroma_agc = 1;
switch (chip_id) {
case '1':
state->ident = V4L2_IDENT_SAA7111;
@ -1675,6 +1656,7 @@ static int saa711x_probe(struct i2c_client *client,
if (state->ident > V4L2_IDENT_SAA7111A)
saa711x_writeregs(sd, saa7115_init_misc);
saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
v4l2_ctrl_handler_setup(hdl);
v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
@ -1689,6 +1671,7 @@ static int saa711x_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
kfree(to_state(sd));
return 0;
}

View File

@ -38,6 +38,7 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver");
@ -55,14 +56,11 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
struct saa717x_state {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
v4l2_std_id std;
int input;
int enable;
int radio;
int bright;
int contrast;
int hue;
int sat;
int playback;
int audio;
int tuner_audio_mode;
@ -81,6 +79,11 @@ static inline struct saa717x_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct saa717x_state, sd);
}
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd;
}
/* ----------------------------------------------------------------------- */
/* for audio mode */
@ -774,29 +777,6 @@ static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode)
saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]);
}
/* write regs to video output level (bright,contrast,hue,sat) */
static void set_video_output_level_regs(struct v4l2_subdev *sd,
struct saa717x_state *decoder)
{
/* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */
saa717x_write(sd, 0x10a, decoder->bright);
/* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) -
0h (luminance off) 40: i2c dump
c0h (-1.0 inverse chrominance)
80h (-2.0 inverse chrominance) */
saa717x_write(sd, 0x10b, decoder->contrast);
/* saturation? 7fh(max)-40h(ITU)-0h(color off)
c0h (-1.0 inverse chrominance)
80h (-2.0 inverse chrominance) */
saa717x_write(sd, 0x10c, decoder->sat);
/* color hue (phase) control
7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */
saa717x_write(sd, 0x10d, decoder->hue);
}
/* write regs to set audio volume, bass and treble */
static int set_audio_regs(struct v4l2_subdev *sd,
struct saa717x_state *decoder)
@ -829,9 +809,9 @@ static int set_audio_regs(struct v4l2_subdev *sd,
saa717x_write(sd, 0x480, val);
/* bass and treble; go to another function */
/* set bass and treble */
val = decoder->audio_main_bass | (decoder->audio_main_treble << 8);
val = decoder->audio_main_bass & 0x1f;
val |= (decoder->audio_main_treble & 0x1f) << 5;
saa717x_write(sd, 0x488, val);
return 0;
}
@ -893,218 +873,55 @@ static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale)
saa717x_write(sd, 0x71 + task_shift, yscale >> 8);
}
static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
static int saa717x_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
struct saa717x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value < 0 || ctrl->value > 255) {
v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
return -ERANGE;
}
state->bright = ctrl->value;
v4l2_dbg(1, debug, sd, "bright:%d\n", state->bright);
saa717x_write(sd, 0x10a, state->bright);
break;
saa717x_write(sd, 0x10a, ctrl->val);
return 0;
case V4L2_CID_CONTRAST:
if (ctrl->value < 0 || ctrl->value > 127) {
v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
return -ERANGE;
}
state->contrast = ctrl->value;
v4l2_dbg(1, debug, sd, "contrast:%d\n", state->contrast);
saa717x_write(sd, 0x10b, state->contrast);
break;
saa717x_write(sd, 0x10b, ctrl->val);
return 0;
case V4L2_CID_SATURATION:
if (ctrl->value < 0 || ctrl->value > 127) {
v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
return -ERANGE;
}
state->sat = ctrl->value;
v4l2_dbg(1, debug, sd, "sat:%d\n", state->sat);
saa717x_write(sd, 0x10c, state->sat);
break;
saa717x_write(sd, 0x10c, ctrl->val);
return 0;
case V4L2_CID_HUE:
if (ctrl->value < -128 || ctrl->value > 127) {
v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
return -ERANGE;
}
state->hue = ctrl->value;
v4l2_dbg(1, debug, sd, "hue:%d\n", state->hue);
saa717x_write(sd, 0x10d, state->hue);
break;
saa717x_write(sd, 0x10d, ctrl->val);
return 0;
case V4L2_CID_AUDIO_MUTE:
state->audio_main_mute = ctrl->value;
set_audio_regs(sd, state);
state->audio_main_mute = ctrl->val;
break;
case V4L2_CID_AUDIO_VOLUME:
state->audio_main_volume = ctrl->value;
set_audio_regs(sd, state);
state->audio_main_volume = ctrl->val;
break;
case V4L2_CID_AUDIO_BALANCE:
state->audio_main_balance = ctrl->value;
set_audio_regs(sd, state);
state->audio_main_balance = ctrl->val;
break;
case V4L2_CID_AUDIO_TREBLE:
state->audio_main_treble = ctrl->value;
set_audio_regs(sd, state);
state->audio_main_treble = ctrl->val;
break;
case V4L2_CID_AUDIO_BASS:
state->audio_main_bass = ctrl->value;
set_audio_regs(sd, state);
state->audio_main_bass = ctrl->val;
break;
default:
return -EINVAL;
return 0;
}
set_audio_regs(sd, state);
return 0;
}
static int saa717x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct saa717x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->value = state->bright;
break;
case V4L2_CID_CONTRAST:
ctrl->value = state->contrast;
break;
case V4L2_CID_SATURATION:
ctrl->value = state->sat;
break;
case V4L2_CID_HUE:
ctrl->value = state->hue;
break;
case V4L2_CID_AUDIO_MUTE:
ctrl->value = state->audio_main_mute;
break;
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = state->audio_main_volume;
break;
case V4L2_CID_AUDIO_BALANCE:
ctrl->value = state->audio_main_balance;
break;
case V4L2_CID_AUDIO_TREBLE:
ctrl->value = state->audio_main_treble;
break;
case V4L2_CID_AUDIO_BASS:
ctrl->value = state->audio_main_bass;
break;
default:
return -EINVAL;
}
return 0;
}
static struct v4l2_queryctrl saa717x_qctrl[] = {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 0,
.maximum = 255,
.step = 1,
.default_value = 128,
.flags = 0,
}, {
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contrast",
.minimum = 0,
.maximum = 255,
.step = 1,
.default_value = 64,
.flags = 0,
}, {
.id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Saturation",
.minimum = 0,
.maximum = 255,
.step = 1,
.default_value = 64,
.flags = 0,
}, {
.id = V4L2_CID_HUE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Hue",
.minimum = -128,
.maximum = 127,
.step = 1,
.default_value = 0,
.flags = 0,
}, {
.id = V4L2_CID_AUDIO_VOLUME,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Volume",
.minimum = 0,
.maximum = 65535,
.step = 65535 / 100,
.default_value = 58880,
.flags = 0,
}, {
.id = V4L2_CID_AUDIO_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Balance",
.minimum = 0,
.maximum = 65535,
.step = 65535 / 100,
.default_value = 32768,
.flags = 0,
}, {
.id = V4L2_CID_AUDIO_MUTE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Mute",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
.flags = 0,
}, {
.id = V4L2_CID_AUDIO_BASS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Bass",
.minimum = 0,
.maximum = 65535,
.step = 65535 / 100,
.default_value = 32768,
}, {
.id = V4L2_CID_AUDIO_TREBLE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Treble",
.minimum = 0,
.maximum = 65535,
.step = 65535 / 100,
.default_value = 32768,
},
};
static int saa717x_s_video_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
@ -1158,18 +975,6 @@ static int saa717x_s_video_routing(struct v4l2_subdev *sd,
return 0;
}
static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
int i;
for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++)
if (qc->id && qc->id == saa717x_qctrl[i].id) {
memcpy(qc, &saa717x_qctrl[i], sizeof(*qc));
return 0;
}
return -EINVAL;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
@ -1386,17 +1191,34 @@ static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
return 0;
}
static int saa717x_log_status(struct v4l2_subdev *sd)
{
struct saa717x_state *state = to_state(sd);
v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops saa717x_ctrl_ops = {
.s_ctrl = saa717x_s_ctrl,
};
static const struct v4l2_subdev_core_ops saa717x_core_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = saa717x_g_register,
.s_register = saa717x_s_register,
#endif
.queryctrl = saa717x_queryctrl,
.g_ctrl = saa717x_g_ctrl,
.s_ctrl = saa717x_s_ctrl,
.s_std = saa717x_s_std,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
.log_status = saa717x_log_status,
};
static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
@ -1432,6 +1254,7 @@ static int saa717x_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct saa717x_state *decoder;
struct v4l2_ctrl_handler *hdl;
struct v4l2_subdev *sd;
u8 id = 0;
char *p = "";
@ -1467,16 +1290,41 @@ static int saa717x_probe(struct i2c_client *client,
p = "saa7171";
v4l2_info(sd, "%s found @ 0x%x (%s)\n", p,
client->addr << 1, client->adapter->name);
hdl = &decoder->hdl;
v4l2_ctrl_handler_init(hdl, 9);
/* add in ascending ID order */
v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 68);
v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
V4L2_CID_SATURATION, 0, 255, 1, 64);
v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000);
v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
V4L2_CID_AUDIO_BASS, -16, 15, 1, 0);
v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0);
v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
sd->ctrl_handler = hdl;
if (hdl->error) {
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
kfree(decoder);
return err;
}
decoder->std = V4L2_STD_NTSC;
decoder->input = -1;
decoder->enable = 1;
/* tune these parameters */
decoder->bright = 0x80;
decoder->contrast = 0x44;
decoder->sat = 0x40;
decoder->hue = 0x00;
/* FIXME!! */
decoder->playback = 0; /* initially capture mode used */
decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */
@ -1487,23 +1335,13 @@ static int saa717x_probe(struct i2c_client *client,
/* set volume, bass and treble */
decoder->audio_main_vol_l = 6;
decoder->audio_main_vol_r = 6;
decoder->audio_main_bass = 0;
decoder->audio_main_treble = 0;
decoder->audio_main_mute = 0;
decoder->audio_main_balance = 32768;
/* normalize (24 to -40 (not -84) -> 65535 to 0) */
decoder->audio_main_volume =
(decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40));
v4l2_dbg(1, debug, sd, "writing init values\n");
/* FIXME!! */
saa717x_write_regs(sd, reg_init_initialize);
set_video_output_level_regs(sd, decoder);
/* set bass,treble to 0db 20041101 K.Ohta */
decoder->audio_main_bass = 0;
decoder->audio_main_treble = 0;
set_audio_regs(sd, decoder);
v4l2_ctrl_handler_setup(hdl);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2*HZ);
@ -1515,6 +1353,7 @@ static int saa717x_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
kfree(to_state(sd));
return 0;
}

View File

@ -779,9 +779,12 @@ static int soc_camera_s_crop(struct file *file, void *fh,
ret = ici->ops->get_crop(icd, &current_crop);
/* Prohibit window size change with initialised buffers */
if (icf->vb_vidq.bufs[0] && !ret &&
(a->c.width != current_crop.c.width ||
a->c.height != current_crop.c.height)) {
if (ret < 0) {
dev_err(&icd->dev,
"S_CROP denied: getting current crop failed\n");
} else if (icf->vb_vidq.bufs[0] &&
(a->c.width != current_crop.c.width ||
a->c.height != current_crop.c.height)) {
dev_err(&icd->dev,
"S_CROP denied: queue initialised and sizes differ\n");
ret = -EBUSY;

View File

@ -179,7 +179,7 @@ static const struct i2c_reg_value tvp7002_init_default[] = {
/* Register parameters for 480P */
static const struct i2c_reg_value tvp7002_parms_480P[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x0a, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE },
@ -223,7 +223,7 @@ static const struct i2c_reg_value tvp7002_parms_576P[] = {
/* Register parameters for 1080I60 */
static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
@ -245,7 +245,7 @@ static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
/* Register parameters for 1080P60 */
static const struct i2c_reg_value tvp7002_parms_1080P60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
@ -289,7 +289,7 @@ static const struct i2c_reg_value tvp7002_parms_1080I50[] = {
/* Register parameters for 720P60 */
static const struct i2c_reg_value tvp7002_parms_720P60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x02, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
@ -311,7 +311,7 @@ static const struct i2c_reg_value tvp7002_parms_720P60[] = {
/* Register parameters for 720P50 */
static const struct i2c_reg_value tvp7002_parms_720P50[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0x0c, TVP7002_WRITE },
{ TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },

View File

@ -282,19 +282,15 @@ static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame,
};
unsigned short digit;
int ix, iy;
int value;
if ((uvd == NULL) || (frame == NULL))
return;
if (ch >= '0' && ch <= '9')
ch -= '0';
else if (ch >= 'A' && ch <= 'F')
ch = 10 + (ch - 'A');
else if (ch >= 'a' && ch <= 'f')
ch = 10 + (ch - 'a');
else
value = hex_to_bin(ch);
if (value < 0)
return;
digit = digits[ch];
digit = digits[value];
for (iy=0; iy < 5; iy++) {
for (ix=0; ix < 3; ix++) {

View File

@ -2145,6 +2145,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Miricle 307K */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x17dc,
.idProduct = 0x0202,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Lenovo Thinkpad SL400/SL500 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,

View File

@ -78,12 +78,14 @@
*
*/
void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
int drop_corrupted)
{
mutex_init(&queue->mutex);
spin_lock_init(&queue->irqlock);
INIT_LIST_HEAD(&queue->mainqueue);
INIT_LIST_HEAD(&queue->irqqueue);
queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;
queue->type = type;
}
@ -435,8 +437,10 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
uvc_queue_cancel(queue, 0);
INIT_LIST_HEAD(&queue->mainqueue);
for (i = 0; i < queue->count; ++i)
for (i = 0; i < queue->count; ++i) {
queue->buffer[i].error = 0;
queue->buffer[i].state = UVC_BUF_STATE_IDLE;
}
queue->flags &= ~UVC_QUEUE_STREAMING;
}
@ -488,8 +492,8 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *nextbuf;
unsigned long flags;
if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
buf->buf.length != buf->buf.bytesused) {
if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) {
buf->error = 0;
buf->state = UVC_BUF_STATE_QUEUED;
buf->buf.bytesused = 0;
return buf;
@ -497,6 +501,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
spin_lock_irqsave(&queue->irqlock, flags);
list_del(&buf->queue);
buf->error = 0;
buf->state = UVC_BUF_STATE_DONE;
if (!list_empty(&queue->irqqueue))
nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,

View File

@ -555,6 +555,9 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
if (urb->iso_frame_desc[i].status < 0) {
uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
"lost (%d).\n", urb->iso_frame_desc[i].status);
/* Mark the buffer as faulty. */
if (buf != NULL)
buf->error = 1;
continue;
}
@ -579,8 +582,14 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
uvc_video_decode_end(stream, buf, mem,
urb->iso_frame_desc[i].actual_length);
if (buf->state == UVC_BUF_STATE_READY)
if (buf->state == UVC_BUF_STATE_READY) {
if (buf->buf.length != buf->buf.bytesused &&
!(stream->cur_format->flags &
UVC_FMT_FLAG_COMPRESSED))
buf->error = 1;
buf = uvc_queue_next_buffer(&stream->queue, buf);
}
}
}
@ -1104,7 +1113,7 @@ int uvc_video_init(struct uvc_streaming *stream)
atomic_set(&stream->active, 0);
/* Initialize the video buffers queue. */
uvc_queue_init(&stream->queue, stream->type);
uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
/* Alternate setting 0 should be the default, yet the XBox Live Vision
* Cam (and possibly other devices) crash or otherwise misbehave if
@ -1197,12 +1206,6 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable)
return 0;
}
if ((stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) ||
uvc_no_drop_param)
stream->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
else
stream->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
ret = uvc_queue_enable(&stream->queue, 1);
if (ret < 0)
return ret;

View File

@ -379,11 +379,12 @@ struct uvc_buffer {
struct list_head queue;
wait_queue_head_t wait;
enum uvc_buffer_state state;
unsigned int error;
};
#define UVC_QUEUE_STREAMING (1 << 0)
#define UVC_QUEUE_DISCONNECTED (1 << 1)
#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2)
#define UVC_QUEUE_DROP_CORRUPTED (1 << 2)
struct uvc_video_queue {
enum v4l2_buf_type type;
@ -562,7 +563,7 @@ extern struct uvc_driver uvc_driver;
/* Video buffers queue management. */
extern void uvc_queue_init(struct uvc_video_queue *queue,
enum v4l2_buf_type type);
enum v4l2_buf_type type, int drop_corrupted);
extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
unsigned int nbuffers, unsigned int buflength);
extern int uvc_free_buffers(struct uvc_video_queue *queue);

View File

@ -62,6 +62,7 @@
#define __OLD_VIDIOC_ /* To allow fixing old calls*/
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
#include <linux/videodev2.h>
@ -172,487 +173,17 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
}
EXPORT_SYMBOL(v4l2_ctrl_check);
/* Returns NULL or a character pointer array containing the menu for
the given control ID. The pointer array ends with a NULL pointer.
An empty string signifies a menu entry that is invalid. This allows
drivers to disable certain options if it is not supported. */
const char **v4l2_ctrl_get_menu(u32 id)
{
static const char *mpeg_audio_sampling_freq[] = {
"44.1 kHz",
"48 kHz",
"32 kHz",
NULL
};
static const char *mpeg_audio_encoding[] = {
"MPEG-1/2 Layer I",
"MPEG-1/2 Layer II",
"MPEG-1/2 Layer III",
"MPEG-2/4 AAC",
"AC-3",
NULL
};
static const char *mpeg_audio_l1_bitrate[] = {
"32 kbps",
"64 kbps",
"96 kbps",
"128 kbps",
"160 kbps",
"192 kbps",
"224 kbps",
"256 kbps",
"288 kbps",
"320 kbps",
"352 kbps",
"384 kbps",
"416 kbps",
"448 kbps",
NULL
};
static const char *mpeg_audio_l2_bitrate[] = {
"32 kbps",
"48 kbps",
"56 kbps",
"64 kbps",
"80 kbps",
"96 kbps",
"112 kbps",
"128 kbps",
"160 kbps",
"192 kbps",
"224 kbps",
"256 kbps",
"320 kbps",
"384 kbps",
NULL
};
static const char *mpeg_audio_l3_bitrate[] = {
"32 kbps",
"40 kbps",
"48 kbps",
"56 kbps",
"64 kbps",
"80 kbps",
"96 kbps",
"112 kbps",
"128 kbps",
"160 kbps",
"192 kbps",
"224 kbps",
"256 kbps",
"320 kbps",
NULL
};
static const char *mpeg_audio_ac3_bitrate[] = {
"32 kbps",
"40 kbps",
"48 kbps",
"56 kbps",
"64 kbps",
"80 kbps",
"96 kbps",
"112 kbps",
"128 kbps",
"160 kbps",
"192 kbps",
"224 kbps",
"256 kbps",
"320 kbps",
"384 kbps",
"448 kbps",
"512 kbps",
"576 kbps",
"640 kbps",
NULL
};
static const char *mpeg_audio_mode[] = {
"Stereo",
"Joint Stereo",
"Dual",
"Mono",
NULL
};
static const char *mpeg_audio_mode_extension[] = {
"Bound 4",
"Bound 8",
"Bound 12",
"Bound 16",
NULL
};
static const char *mpeg_audio_emphasis[] = {
"No Emphasis",
"50/15 us",
"CCITT J17",
NULL
};
static const char *mpeg_audio_crc[] = {
"No CRC",
"16-bit CRC",
NULL
};
static const char *mpeg_video_encoding[] = {
"MPEG-1",
"MPEG-2",
"MPEG-4 AVC",
NULL
};
static const char *mpeg_video_aspect[] = {
"1x1",
"4x3",
"16x9",
"2.21x1",
NULL
};
static const char *mpeg_video_bitrate_mode[] = {
"Variable Bitrate",
"Constant Bitrate",
NULL
};
static const char *mpeg_stream_type[] = {
"MPEG-2 Program Stream",
"MPEG-2 Transport Stream",
"MPEG-1 System Stream",
"MPEG-2 DVD-compatible Stream",
"MPEG-1 VCD-compatible Stream",
"MPEG-2 SVCD-compatible Stream",
NULL
};
static const char *mpeg_stream_vbi_fmt[] = {
"No VBI",
"Private packet, IVTV format",
NULL
};
static const char *camera_power_line_frequency[] = {
"Disabled",
"50 Hz",
"60 Hz",
NULL
};
static const char *camera_exposure_auto[] = {
"Auto Mode",
"Manual Mode",
"Shutter Priority Mode",
"Aperture Priority Mode",
NULL
};
static const char *colorfx[] = {
"None",
"Black & White",
"Sepia",
"Negative",
"Emboss",
"Sketch",
"Sky blue",
"Grass green",
"Skin whiten",
"Vivid",
NULL
};
static const char *tune_preemphasis[] = {
"No preemphasis",
"50 useconds",
"75 useconds",
NULL,
};
switch (id) {
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
return mpeg_audio_sampling_freq;
case V4L2_CID_MPEG_AUDIO_ENCODING:
return mpeg_audio_encoding;
case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
return mpeg_audio_l1_bitrate;
case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
return mpeg_audio_l2_bitrate;
case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
return mpeg_audio_l3_bitrate;
case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
return mpeg_audio_ac3_bitrate;
case V4L2_CID_MPEG_AUDIO_MODE:
return mpeg_audio_mode;
case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
return mpeg_audio_mode_extension;
case V4L2_CID_MPEG_AUDIO_EMPHASIS:
return mpeg_audio_emphasis;
case V4L2_CID_MPEG_AUDIO_CRC:
return mpeg_audio_crc;
case V4L2_CID_MPEG_VIDEO_ENCODING:
return mpeg_video_encoding;
case V4L2_CID_MPEG_VIDEO_ASPECT:
return mpeg_video_aspect;
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
return mpeg_video_bitrate_mode;
case V4L2_CID_MPEG_STREAM_TYPE:
return mpeg_stream_type;
case V4L2_CID_MPEG_STREAM_VBI_FMT:
return mpeg_stream_vbi_fmt;
case V4L2_CID_POWER_LINE_FREQUENCY:
return camera_power_line_frequency;
case V4L2_CID_EXPOSURE_AUTO:
return camera_exposure_auto;
case V4L2_CID_COLORFX:
return colorfx;
case V4L2_CID_TUNE_PREEMPHASIS:
return tune_preemphasis;
default:
return NULL;
}
}
EXPORT_SYMBOL(v4l2_ctrl_get_menu);
/* Return the control name. */
const char *v4l2_ctrl_get_name(u32 id)
{
switch (id) {
/* USER controls */
case V4L2_CID_USER_CLASS: return "User Controls";
case V4L2_CID_BRIGHTNESS: return "Brightness";
case V4L2_CID_CONTRAST: return "Contrast";
case V4L2_CID_SATURATION: return "Saturation";
case V4L2_CID_HUE: return "Hue";
case V4L2_CID_AUDIO_VOLUME: return "Volume";
case V4L2_CID_AUDIO_BALANCE: return "Balance";
case V4L2_CID_AUDIO_BASS: return "Bass";
case V4L2_CID_AUDIO_TREBLE: return "Treble";
case V4L2_CID_AUDIO_MUTE: return "Mute";
case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
case V4L2_CID_BLACK_LEVEL: return "Black Level";
case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
case V4L2_CID_RED_BALANCE: return "Red Balance";
case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
case V4L2_CID_GAMMA: return "Gamma";
case V4L2_CID_EXPOSURE: return "Exposure";
case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
case V4L2_CID_GAIN: return "Gain";
case V4L2_CID_HFLIP: return "Horizontal Flip";
case V4L2_CID_VFLIP: return "Vertical Flip";
case V4L2_CID_HCENTER: return "Horizontal Center";
case V4L2_CID_VCENTER: return "Vertical Center";
case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
case V4L2_CID_SHARPNESS: return "Sharpness";
case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
case V4L2_CID_CHROMA_GAIN: return "Chroma Gain";
case V4L2_CID_COLOR_KILLER: return "Color Killer";
case V4L2_CID_COLORFX: return "Color Effects";
case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic";
case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter";
case V4L2_CID_ROTATE: return "Rotate";
case V4L2_CID_BG_COLOR: return "Background Color";
/* MPEG controls */
case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding";
case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate";
case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate";
case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate";
case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate";
case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate";
case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode";
case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis";
case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC";
case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute";
case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding";
case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect";
case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames";
case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size";
case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure";
case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown";
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode";
case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate";
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate";
case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute";
case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV";
case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type";
case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID";
case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID";
case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID";
case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID";
case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
/* CAMERA controls */
case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure";
case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute";
case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
case V4L2_CID_PAN_RESET: return "Pan, Reset";
case V4L2_CID_TILT_RESET: return "Tilt, Reset";
case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute";
case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative";
case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic";
case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute";
case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative";
case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute";
case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative";
case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous";
case V4L2_CID_PRIVACY: return "Privacy";
/* FM Radio Modulator control */
case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls";
case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation";
case V4L2_CID_RDS_TX_PI: return "RDS Program ID";
case V4L2_CID_RDS_TX_PTY: return "RDS Program Type";
case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name";
case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text";
case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled";
case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation";
case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled";
case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain";
case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled";
case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation";
case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency";
case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings";
case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level";
case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor";
default:
return NULL;
}
}
EXPORT_SYMBOL(v4l2_ctrl_get_name);
/* Fill in a struct v4l2_queryctrl */
int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
{
const char *name = v4l2_ctrl_get_name(qctrl->id);
const char *name;
v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type,
&min, &max, &step, &def, &qctrl->flags);
qctrl->flags = 0;
if (name == NULL)
return -EINVAL;
switch (qctrl->id) {
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_LOUDNESS:
case V4L2_CID_AUTO_WHITE_BALANCE:
case V4L2_CID_AUTOGAIN:
case V4L2_CID_HFLIP:
case V4L2_CID_VFLIP:
case V4L2_CID_HUE_AUTO:
case V4L2_CID_CHROMA_AGC:
case V4L2_CID_COLOR_KILLER:
case V4L2_CID_MPEG_AUDIO_MUTE:
case V4L2_CID_MPEG_VIDEO_MUTE:
case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
case V4L2_CID_MPEG_VIDEO_PULLDOWN:
case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
case V4L2_CID_FOCUS_AUTO:
case V4L2_CID_PRIVACY:
case V4L2_CID_AUDIO_LIMITER_ENABLED:
case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
case V4L2_CID_PILOT_TONE_ENABLED:
qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
min = 0;
max = step = 1;
break;
case V4L2_CID_PAN_RESET:
case V4L2_CID_TILT_RESET:
qctrl->type = V4L2_CTRL_TYPE_BUTTON;
qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
min = max = step = def = 0;
break;
case V4L2_CID_POWER_LINE_FREQUENCY:
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
case V4L2_CID_MPEG_AUDIO_ENCODING:
case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
case V4L2_CID_MPEG_AUDIO_MODE:
case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
case V4L2_CID_MPEG_AUDIO_EMPHASIS:
case V4L2_CID_MPEG_AUDIO_CRC:
case V4L2_CID_MPEG_VIDEO_ENCODING:
case V4L2_CID_MPEG_VIDEO_ASPECT:
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
case V4L2_CID_MPEG_STREAM_TYPE:
case V4L2_CID_MPEG_STREAM_VBI_FMT:
case V4L2_CID_EXPOSURE_AUTO:
case V4L2_CID_COLORFX:
case V4L2_CID_TUNE_PREEMPHASIS:
qctrl->type = V4L2_CTRL_TYPE_MENU;
step = 1;
break;
case V4L2_CID_RDS_TX_PS_NAME:
case V4L2_CID_RDS_TX_RADIO_TEXT:
qctrl->type = V4L2_CTRL_TYPE_STRING;
break;
case V4L2_CID_USER_CLASS:
case V4L2_CID_CAMERA_CLASS:
case V4L2_CID_MPEG_CLASS:
case V4L2_CID_FM_TX_CLASS:
qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;
qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
min = max = step = def = 0;
break;
case V4L2_CID_BG_COLOR:
qctrl->type = V4L2_CTRL_TYPE_INTEGER;
step = 1;
min = 0;
/* Max is calculated as RGB888 that is 2^24 */
max = 0xFFFFFF;
break;
default:
qctrl->type = V4L2_CTRL_TYPE_INTEGER;
break;
}
switch (qctrl->id) {
case V4L2_CID_MPEG_AUDIO_ENCODING:
case V4L2_CID_MPEG_AUDIO_MODE:
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
case V4L2_CID_MPEG_VIDEO_B_FRAMES:
case V4L2_CID_MPEG_STREAM_TYPE:
qctrl->flags |= V4L2_CTRL_FLAG_UPDATE;
break;
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_BRIGHTNESS:
case V4L2_CID_CONTRAST:
case V4L2_CID_SATURATION:
case V4L2_CID_HUE:
case V4L2_CID_RED_BALANCE:
case V4L2_CID_BLUE_BALANCE:
case V4L2_CID_GAMMA:
case V4L2_CID_SHARPNESS:
case V4L2_CID_CHROMA_GAIN:
case V4L2_CID_RDS_TX_DEVIATION:
case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
case V4L2_CID_AUDIO_LIMITER_DEVIATION:
case V4L2_CID_AUDIO_COMPRESSION_GAIN:
case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
case V4L2_CID_PILOT_TONE_DEVIATION:
case V4L2_CID_PILOT_TONE_FREQUENCY:
case V4L2_CID_TUNE_POWER_LEVEL:
case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
break;
case V4L2_CID_PAN_RELATIVE:
case V4L2_CID_TILT_RELATIVE:
case V4L2_CID_FOCUS_RELATIVE:
case V4L2_CID_IRIS_RELATIVE:
case V4L2_CID_ZOOM_RELATIVE:
qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
break;
}
qctrl->minimum = min;
qctrl->maximum = max;
qctrl->step = step;

File diff suppressed because it is too large Load Diff

View File

@ -428,8 +428,12 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
vdev->vfl_type = type;
vdev->cdev = NULL;
if (vdev->v4l2_dev && vdev->v4l2_dev->dev)
vdev->parent = vdev->v4l2_dev->dev;
if (vdev->v4l2_dev) {
if (vdev->v4l2_dev->dev)
vdev->parent = vdev->v4l2_dev->dev;
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
}
/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES

View File

@ -26,6 +26,7 @@
#endif
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
@ -115,6 +116,8 @@ EXPORT_SYMBOL_GPL(v4l2_device_unregister);
int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
struct v4l2_subdev *sd)
{
int err;
/* Check for valid input */
if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
return -EINVAL;
@ -122,6 +125,10 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
WARN_ON(sd->v4l2_dev != NULL);
if (!try_module_get(sd->owner))
return -ENODEV;
/* This just returns 0 if either of the two args is NULL */
err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
if (err)
return err;
sd->v4l2_dev = v4l2_dev;
spin_lock(&v4l2_dev->lock);
list_add_tail(&sd->list, &v4l2_dev->subdevs);

View File

@ -26,6 +26,7 @@
#endif
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-chip-ident.h>
@ -1259,9 +1260,12 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_queryctrl *p = arg;
if (!ops->vidioc_queryctrl)
if (vfd->ctrl_handler)
ret = v4l2_queryctrl(vfd->ctrl_handler, p);
else if (ops->vidioc_queryctrl)
ret = ops->vidioc_queryctrl(file, fh, p);
else
break;
ret = ops->vidioc_queryctrl(file, fh, p);
if (!ret)
dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, "
"step=%d, default=%d, flags=0x%08x\n",
@ -1276,7 +1280,9 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_control *p = arg;
if (ops->vidioc_g_ctrl)
if (vfd->ctrl_handler)
ret = v4l2_g_ctrl(vfd->ctrl_handler, p);
else if (ops->vidioc_g_ctrl)
ret = ops->vidioc_g_ctrl(file, fh, p);
else if (ops->vidioc_g_ext_ctrls) {
struct v4l2_ext_controls ctrls;
@ -1306,11 +1312,16 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls ctrls;
struct v4l2_ext_control ctrl;
if (!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
if (!vfd->ctrl_handler &&
!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
break;
dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
if (vfd->ctrl_handler) {
ret = v4l2_s_ctrl(vfd->ctrl_handler, p);
break;
}
if (ops->vidioc_s_ctrl) {
ret = ops->vidioc_s_ctrl(file, fh, p);
break;
@ -1332,10 +1343,12 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
if (!ops->vidioc_g_ext_ctrls)
break;
if (check_ext_ctrls(p, 0))
if (vfd->ctrl_handler)
ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
else if (ops->vidioc_g_ext_ctrls && check_ext_ctrls(p, 0))
ret = ops->vidioc_g_ext_ctrls(file, fh, p);
else
break;
v4l_print_ext_ctrls(cmd, vfd, p, !ret);
break;
}
@ -1344,10 +1357,12 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
if (!ops->vidioc_s_ext_ctrls)
if (!vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls)
break;
v4l_print_ext_ctrls(cmd, vfd, p, 1);
if (check_ext_ctrls(p, 0))
if (vfd->ctrl_handler)
ret = v4l2_s_ext_ctrls(vfd->ctrl_handler, p);
else if (check_ext_ctrls(p, 0))
ret = ops->vidioc_s_ext_ctrls(file, fh, p);
break;
}
@ -1356,10 +1371,12 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
if (!ops->vidioc_try_ext_ctrls)
if (!vfd->ctrl_handler && !ops->vidioc_try_ext_ctrls)
break;
v4l_print_ext_ctrls(cmd, vfd, p, 1);
if (check_ext_ctrls(p, 0))
if (vfd->ctrl_handler)
ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
else if (check_ext_ctrls(p, 0))
ret = ops->vidioc_try_ext_ctrls(file, fh, p);
break;
}
@ -1367,9 +1384,12 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_querymenu *p = arg;
if (!ops->vidioc_querymenu)
if (vfd->ctrl_handler)
ret = v4l2_querymenu(vfd->ctrl_handler, p);
else if (ops->vidioc_querymenu)
ret = ops->vidioc_querymenu(file, fh, p);
else
break;
ret = ops->vidioc_querymenu(file, fh, p);
if (!ret)
dbgarg(cmd, "id=0x%x, index=%d, name=%s\n",
p->id, p->index, p->name);

View File

@ -27,11 +27,11 @@
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("wm8739 driver");
MODULE_AUTHOR("T. Adachi, Hans Verkuil");
@ -54,12 +54,14 @@ enum {
struct wm8739_state {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
struct {
/* audio cluster */
struct v4l2_ctrl *volume;
struct v4l2_ctrl *mute;
struct v4l2_ctrl *balance;
};
u32 clock_freq;
u8 muted;
u16 volume;
u16 balance;
u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
};
static inline struct wm8739_state *to_state(struct v4l2_subdev *sd)
@ -67,6 +69,11 @@ static inline struct wm8739_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct wm8739_state, sd);
}
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd;
}
/* ------------------------------------------------------------------------ */
static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)
@ -89,58 +96,17 @@ static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)
return -1;
}
/* write regs to set audio volume etc */
static void wm8739_set_audio(struct v4l2_subdev *sd)
{
struct wm8739_state *state = to_state(sd);
u16 mute = state->muted ? 0x80 : 0;
/* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
* Default setting: 0x17 = 0 dB
*/
wm8739_write(sd, R0, (state->vol_l & 0x1f) | mute);
wm8739_write(sd, R1, (state->vol_r & 0x1f) | mute);
}
static int wm8739_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct wm8739_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
ctrl->value = state->muted;
break;
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = state->volume;
break;
case V4L2_CID_AUDIO_BALANCE:
ctrl->value = state->balance;
break;
default:
return -EINVAL;
}
return 0;
}
static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
static int wm8739_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
struct wm8739_state *state = to_state(sd);
unsigned int work_l, work_r;
u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
u16 mute;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
state->muted = ctrl->value;
break;
case V4L2_CID_AUDIO_VOLUME:
state->volume = ctrl->value;
break;
case V4L2_CID_AUDIO_BALANCE:
state->balance = ctrl->value;
break;
default:
@ -148,52 +114,25 @@ static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
}
/* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */
work_l = (min(65536 - state->balance, 32768) * state->volume) / 32768;
work_r = (min(state->balance, (u16)32768) * state->volume) / 32768;
work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768;
work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768;
state->vol_l = (long)work_l * 31 / 65535;
state->vol_r = (long)work_r * 31 / 65535;
vol_l = (long)work_l * 31 / 65535;
vol_r = (long)work_r * 31 / 65535;
/* set audio volume etc. */
wm8739_set_audio(sd);
mute = state->mute->val ? 0x80 : 0;
/* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
* Default setting: 0x17 = 0 dB
*/
wm8739_write(sd, R0, (vol_l & 0x1f) | mute);
wm8739_write(sd, R1, (vol_r & 0x1f) | mute);
return 0;
}
/* ------------------------------------------------------------------------ */
static struct v4l2_queryctrl wm8739_qctrl[] = {
{
.id = V4L2_CID_AUDIO_VOLUME,
.name = "Volume",
.minimum = 0,
.maximum = 65535,
.step = 65535/100,
.default_value = 58880,
.flags = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
}, {
.id = V4L2_CID_AUDIO_MUTE,
.name = "Mute",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
.flags = 0,
.type = V4L2_CTRL_TYPE_BOOLEAN,
}, {
.id = V4L2_CID_AUDIO_BALANCE,
.name = "Balance",
.minimum = 0,
.maximum = 65535,
.step = 65535/100,
.default_value = 32768,
.flags = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
}
};
/* ------------------------------------------------------------------------ */
static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
{
struct wm8739_state *state = to_state(sd);
@ -222,18 +161,6 @@ static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
return 0;
}
static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
int i;
for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++)
if (qc->id && qc->id == wm8739_qctrl[i].id) {
memcpy(qc, &wm8739_qctrl[i], sizeof(*qc));
return 0;
}
return -EINVAL;
}
static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@ -246,21 +173,26 @@ static int wm8739_log_status(struct v4l2_subdev *sd)
struct wm8739_state *state = to_state(sd);
v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq);
v4l2_info(sd, "Volume L: %02x%s\n", state->vol_l & 0x1f,
state->muted ? " (muted)" : "");
v4l2_info(sd, "Volume R: %02x%s\n", state->vol_r & 0x1f,
state->muted ? " (muted)" : "");
v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops wm8739_ctrl_ops = {
.s_ctrl = wm8739_s_ctrl,
};
static const struct v4l2_subdev_core_ops wm8739_core_ops = {
.log_status = wm8739_log_status,
.g_chip_ident = wm8739_g_chip_ident,
.queryctrl = wm8739_queryctrl,
.g_ctrl = wm8739_g_ctrl,
.s_ctrl = wm8739_s_ctrl,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_audio_ops wm8739_audio_ops = {
@ -289,17 +221,28 @@ static int wm8739_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL);
state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &wm8739_ops);
state->vol_l = 0x17; /* 0dB */
state->vol_r = 0x17; /* 0dB */
state->muted = 0;
state->balance = 32768;
/* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */
state->volume = ((long)state->vol_l + 1) * 65535 / 31;
v4l2_ctrl_handler_init(&state->hdl, 2);
state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736);
state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
sd->ctrl_handler = &state->hdl;
if (state->hdl.error) {
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return err;
}
v4l2_ctrl_cluster(3, &state->volume);
state->clock_freq = 48000;
/* Initialize wm8739 */
@ -318,15 +261,17 @@ static int wm8739_probe(struct i2c_client *client,
/* activate */
wm8739_write(sd, R9, 0x001);
/* set volume/mute */
wm8739_set_audio(sd);
v4l2_ctrl_handler_setup(&state->hdl);
return 0;
}
static int wm8739_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct wm8739_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&state->hdl);
kfree(to_state(sd));
return 0;
}

View File

@ -35,6 +35,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("wm8775 driver");
@ -53,8 +54,9 @@ enum {
struct wm8775_state {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
struct v4l2_ctrl *mute;
u8 input; /* Last selected input (0-0xf) */
u8 muted;
};
static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
@ -62,6 +64,11 @@ static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct wm8775_state, sd);
}
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd;
}
static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@ -95,7 +102,7 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,
return -EINVAL;
}
state->input = input;
if (state->muted)
if (!v4l2_ctrl_g_ctrl(state->mute))
return 0;
wm8775_write(sd, R21, 0x0c0);
wm8775_write(sd, R14, 0x1d4);
@ -104,29 +111,21 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,
return 0;
}
static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
struct wm8775_state *state = to_state(sd);
if (ctrl->id != V4L2_CID_AUDIO_MUTE)
return -EINVAL;
ctrl->value = state->muted;
return 0;
}
static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct wm8775_state *state = to_state(sd);
if (ctrl->id != V4L2_CID_AUDIO_MUTE)
return -EINVAL;
state->muted = ctrl->value;
wm8775_write(sd, R21, 0x0c0);
wm8775_write(sd, R14, 0x1d4);
wm8775_write(sd, R15, 0x1d4);
if (!state->muted)
wm8775_write(sd, R21, 0x100 + state->input);
return 0;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
wm8775_write(sd, R21, 0x0c0);
wm8775_write(sd, R14, 0x1d4);
wm8775_write(sd, R15, 0x1d4);
if (!ctrl->val)
wm8775_write(sd, R21, 0x100 + state->input);
return 0;
}
return -EINVAL;
}
static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
@ -140,8 +139,8 @@ static int wm8775_log_status(struct v4l2_subdev *sd)
{
struct wm8775_state *state = to_state(sd);
v4l2_info(sd, "Input: %d%s\n", state->input,
state->muted ? " (muted)" : "");
v4l2_info(sd, "Input: %d\n", state->input);
v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
@ -162,11 +161,20 @@ static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fre
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops wm8775_ctrl_ops = {
.s_ctrl = wm8775_s_ctrl,
};
static const struct v4l2_subdev_core_ops wm8775_core_ops = {
.log_status = wm8775_log_status,
.g_chip_ident = wm8775_g_chip_ident,
.g_ctrl = wm8775_g_ctrl,
.s_ctrl = wm8775_s_ctrl,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
@ -205,13 +213,24 @@ static int wm8775_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL);
state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
state->input = 2;
state->muted = 0;
v4l2_ctrl_handler_init(&state->hdl, 1);
state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
sd->ctrl_handler = &state->hdl;
if (state->hdl.error) {
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return err;
}
/* Initialize wm8775 */
@ -248,9 +267,11 @@ static int wm8775_probe(struct i2c_client *client,
static int wm8775_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct wm8775_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
kfree(to_state(sd));
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return 0;
}

View File

@ -3,6 +3,7 @@
#
menuconfig LIRC_STAGING
bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
depends on LIRC
help
Say Y here, and all supported Linux Infrared Remote Control IR and
RF receiver and transmitter drivers will be displayed. When paired
@ -13,21 +14,13 @@ if LIRC_STAGING
config LIRC_BT829
tristate "BT829 based hardware"
depends on LIRC_STAGING
depends on LIRC_STAGING && PCI
help
Driver for the IR interface on BT829-based hardware
config LIRC_ENE0100
tristate "ENE KB3924/ENE0100 CIR Port Reciever"
depends on LIRC_STAGING
help
This is a driver for CIR port handled by ENE KB3924 embedded
controller found on some notebooks.
It appears on PNP list as ENE0100.
config LIRC_I2C
tristate "I2C Based IR Receivers"
depends on LIRC_STAGING
depends on LIRC_STAGING && I2C
help
Driver for I2C-based IR receivers, such as those commonly
found onboard Hauppauge PVR-150/250/350 video capture cards
@ -40,7 +33,7 @@ config LIRC_IGORPLUGUSB
config LIRC_IMON
tristate "Legacy SoundGraph iMON Receiver and Display"
depends on LIRC_STAGING
depends on LIRC_STAGING && USB
help
Driver for the original SoundGraph iMON IR Receiver and Display
@ -48,7 +41,7 @@ config LIRC_IMON
config LIRC_IT87
tristate "ITE IT87XX CIR Port Receiver"
depends on LIRC_STAGING
depends on LIRC_STAGING && PNP
help
Driver for the ITE IT87xx IR Receiver
@ -60,13 +53,13 @@ config LIRC_ITE8709
config LIRC_PARALLEL
tristate "Homebrew Parallel Port Receiver"
depends on LIRC_STAGING && !SMP
depends on LIRC_STAGING && PARPORT && !SMP
help
Driver for Homebrew Parallel Port Receivers
config LIRC_SASEM
tristate "Sasem USB IR Remote"
depends on LIRC_STAGING
depends on LIRC_STAGING && USB
help
Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
@ -89,12 +82,6 @@ config LIRC_SIR
help
Driver for the SIR IrDA port
config LIRC_STREAMZAP
tristate "Streamzap PC Receiver"
depends on LIRC_STAGING
help
Driver for the Streamzap PC Receiver
config LIRC_TTUSBIR
tristate "Technotrend USB IR Receiver"
depends on LIRC_STAGING && USB
@ -103,7 +90,7 @@ config LIRC_TTUSBIR
config LIRC_ZILOG
tristate "Zilog/Hauppauge IR Transmitter"
depends on LIRC_STAGING
depends on LIRC_STAGING && I2C
help
Driver for the Zilog/Hauppauge IR Transmitter, found on
PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards

View File

@ -4,7 +4,6 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
obj-$(CONFIG_LIRC_ENE0100) += lirc_ene0100.o
obj-$(CONFIG_LIRC_I2C) += lirc_i2c.o
obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
@ -14,6 +13,5 @@ obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
obj-$(CONFIG_LIRC_STREAMZAP) += lirc_streamzap.o
obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o
obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o

View File

@ -1,646 +0,0 @@
/*
* driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
*
* Copyright (C) 2009 Maxim Levitsky <maximlevitsky@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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pnp.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include "lirc_ene0100.h"
static int sample_period = 75;
static int enable_idle = 1;
static int enable_learning;
static void ene_set_idle(struct ene_device *dev, int idle);
static void ene_set_inputs(struct ene_device *dev, int enable);
/* read a hardware register */
static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
{
outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
return inb(dev->hw_io + ENE_IO);
}
/* write a hardware register */
static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
{
outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
outb(value, dev->hw_io + ENE_IO);
}
/* change specific bits in hardware register */
static void ene_hw_write_reg_mask(struct ene_device *dev,
u16 reg, u8 value, u8 mask)
{
u8 regvalue;
outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
regvalue |= (value & mask);
outb(regvalue, dev->hw_io + ENE_IO);
}
/* read irq status and ack it */
static int ene_hw_irq_status(struct ene_device *dev, int *buffer_pointer)
{
u8 irq_status;
u8 fw_flags1, fw_flags2;
fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
if (buffer_pointer)
*buffer_pointer = 4 * (fw_flags2 & ENE_FW2_BUF_HIGH);
if (dev->hw_revision < ENE_HW_C) {
irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
if (!(irq_status & ENEB_IRQ_STATUS_IR))
return 0;
ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
irq_status & ~ENEB_IRQ_STATUS_IR);
/* rev B support only recieving */
return ENE_IRQ_RX;
}
irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
if (!(irq_status & ENEC_IRQ_STATUS))
return 0;
/* original driver does that twice - a workaround ? */
ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
/* clear unknown flag in F8F9 */
if (fw_flags2 & ENE_FW2_IRQ_CLR)
ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
/* check if this is a TX interrupt */
fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
if (fw_flags1 & ENE_FW1_TXIRQ) {
ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
return ENE_IRQ_TX;
} else
return ENE_IRQ_RX;
}
static int ene_hw_detect(struct ene_device *dev)
{
u8 chip_major, chip_minor;
u8 hw_revision, old_ver;
u8 tmp;
u8 fw_capabilities;
tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
if (hw_revision == 0xFF) {
ene_printk(KERN_WARNING, "device seems to be disabled\n");
ene_printk(KERN_WARNING,
"send a mail to lirc-list@lists.sourceforge.net\n");
ene_printk(KERN_WARNING, "please attach output of acpidump\n");
return -ENODEV;
}
if (chip_major == 0x33) {
ene_printk(KERN_WARNING, "chips 0x33xx aren't supported yet\n");
return -ENODEV;
}
if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_C;
ene_printk(KERN_WARNING,
"KB3926C detected, driver support is not complete!\n");
} else if (old_ver == 0x24 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_B;
ene_printk(KERN_NOTICE, "KB3926B detected\n");
} else {
dev->hw_revision = ENE_HW_D;
ene_printk(KERN_WARNING,
"unknown ENE chip detected, assuming KB3926D\n");
ene_printk(KERN_WARNING, "driver support incomplete");
}
ene_printk(KERN_DEBUG, "chip is 0x%02x%02x - 0x%02x, 0x%02x\n",
chip_major, chip_minor, old_ver, hw_revision);
/* detect features hardware supports */
if (dev->hw_revision < ENE_HW_C)
return 0;
fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
fw_capabilities & ENE_FW2_FAN_AS_NRML_IN;
ene_printk(KERN_NOTICE, "hardware features:\n");
ene_printk(KERN_NOTICE,
"learning and tx %s, gpio40_learn %s, fan_in %s\n",
dev->hw_learning_and_tx_capable ? "on" : "off",
dev->hw_gpio40_learning ? "on" : "off",
dev->hw_fan_as_normal_input ? "on" : "off");
if (!dev->hw_learning_and_tx_capable && enable_learning)
enable_learning = 0;
if (dev->hw_learning_and_tx_capable) {
ene_printk(KERN_WARNING,
"Device supports transmitting, but the driver doesn't\n");
ene_printk(KERN_WARNING,
"due to lack of hardware to test against.\n");
ene_printk(KERN_WARNING,
"Send a mail to: lirc-list@lists.sourceforge.net\n");
}
return 0;
}
/* hardware initialization */
static int ene_hw_init(void *data)
{
u8 reg_value;
struct ene_device *dev = (struct ene_device *)data;
dev->in_use = 1;
if (dev->hw_revision < ENE_HW_C) {
ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
} else {
reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
reg_value |= ENEC_IRQ_UNK_EN;
reg_value &= ~ENEC_IRQ_STATUS;
reg_value |= (dev->irq & ENEC_IRQ_MASK);
ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
}
ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
ene_set_inputs(dev, enable_learning);
/* set sampling period */
ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
/* ack any pending irqs - just in case */
ene_hw_irq_status(dev, NULL);
/* enter idle mode */
ene_set_idle(dev, 1);
/* enable firmware bits */
ene_hw_write_reg_mask(dev, ENE_FW1,
ENE_FW1_ENABLE | ENE_FW1_IRQ,
ENE_FW1_ENABLE | ENE_FW1_IRQ);
/* clear stats */
dev->sample = 0;
return 0;
}
/* this enables gpio40 signal, used if connected to wide band input*/
static void ene_enable_gpio40(struct ene_device *dev, int enable)
{
ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, enable ?
0 : ENE_CIR_CONF2_GPIO40DIS,
ENE_CIR_CONF2_GPIO40DIS);
}
/* this enables the classic sampler */
static void ene_enable_normal_recieve(struct ene_device *dev, int enable)
{
ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_ADC_ON : 0);
}
/* this enables recieve via fan input */
static void ene_enable_fan_recieve(struct ene_device *dev, int enable)
{
if (!enable)
ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
else {
ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
}
dev->fan_input_inuse = enable;
}
/* determine which input to use*/
static void ene_set_inputs(struct ene_device *dev, int learning_enable)
{
ene_enable_normal_recieve(dev, 1);
/* old hardware doesn't support learning mode for sure */
if (dev->hw_revision <= ENE_HW_B)
return;
/* reciever not learning capable, still set gpio40 correctly */
if (!dev->hw_learning_and_tx_capable) {
ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
return;
}
/* enable learning mode */
if (learning_enable) {
ene_enable_gpio40(dev, dev->hw_gpio40_learning);
/* fan input is not used for learning */
if (dev->hw_fan_as_normal_input)
ene_enable_fan_recieve(dev, 0);
/* disable learning mode */
} else {
if (dev->hw_fan_as_normal_input) {
ene_enable_fan_recieve(dev, 1);
ene_enable_normal_recieve(dev, 0);
} else
ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
}
/* set few additional settings for this mode */
ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_enable ?
ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_enable ?
ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
}
/* deinitialization */
static void ene_hw_deinit(void *data)
{
struct ene_device *dev = (struct ene_device *)data;
/* disable samplers */
ene_enable_normal_recieve(dev, 0);
if (dev->hw_fan_as_normal_input)
ene_enable_fan_recieve(dev, 0);
/* disable hardware IRQ and firmware flag */
ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
ene_set_idle(dev, 1);
dev->in_use = 0;
}
/* sends current sample to userspace */
static void send_sample(struct ene_device *dev)
{
int value = abs(dev->sample) & PULSE_MASK;
if (dev->sample > 0)
value |= PULSE_BIT;
if (!lirc_buffer_full(dev->lirc_driver->rbuf)) {
lirc_buffer_write(dev->lirc_driver->rbuf, (void *)&value);
wake_up(&dev->lirc_driver->rbuf->wait_poll);
}
dev->sample = 0;
}
/* this updates current sample */
static void update_sample(struct ene_device *dev, int sample)
{
if (!dev->sample)
dev->sample = sample;
else if (same_sign(dev->sample, sample))
dev->sample += sample;
else {
send_sample(dev);
dev->sample = sample;
}
}
/* enable or disable idle mode */
static void ene_set_idle(struct ene_device *dev, int idle)
{
struct timeval now;
int disable = idle && enable_idle && (dev->hw_revision < ENE_HW_C);
ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
disable ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
ENE_CIR_SAMPLE_OVERFLOW);
dev->idle = idle;
/* remember when we have entered the idle mode */
if (idle) {
do_gettimeofday(&dev->gap_start);
return;
}
/* send the gap between keypresses now */
do_gettimeofday(&now);
if (now.tv_sec - dev->gap_start.tv_sec > 16)
dev->sample = space(PULSE_MASK);
else
dev->sample = dev->sample +
space(1000000ull * (now.tv_sec - dev->gap_start.tv_sec))
+ space(now.tv_usec - dev->gap_start.tv_usec);
if (abs(dev->sample) > PULSE_MASK)
dev->sample = space(PULSE_MASK);
send_sample(dev);
}
/* interrupt handler */
static irqreturn_t ene_hw_irq(int irq, void *data)
{
u16 hw_value;
int i, hw_sample;
int space;
int buffer_pointer;
int irq_status;
struct ene_device *dev = (struct ene_device *)data;
irq_status = ene_hw_irq_status(dev, &buffer_pointer);
if (!irq_status)
return IRQ_NONE;
/* TODO: only RX for now */
if (irq_status == ENE_IRQ_TX)
return IRQ_HANDLED;
for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
hw_value = ene_hw_read_reg(dev,
ENE_SAMPLE_BUFFER + buffer_pointer + i);
if (dev->fan_input_inuse) {
/* read high part of the sample */
hw_value |= ene_hw_read_reg(dev,
ENE_SAMPLE_BUFFER_FAN + buffer_pointer + i) << 8;
/* test for _space_ bit */
space = !(hw_value & ENE_FAN_SMPL_PULS_MSK);
/* clear space bit, and other unused bits */
hw_value &= ENE_FAN_VALUE_MASK;
hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
} else {
space = hw_value & ENE_SAMPLE_SPC_MASK;
hw_value &= ENE_SAMPLE_VALUE_MASK;
hw_sample = hw_value * sample_period;
}
/* no more data */
if (!(hw_value))
break;
if (space)
hw_sample *= -1;
/* overflow sample recieved, handle it */
if (!dev->fan_input_inuse && hw_value == ENE_SAMPLE_OVERFLOW) {
if (dev->idle)
continue;
if (dev->sample > 0 || abs(dev->sample) <= ENE_MAXGAP)
update_sample(dev, hw_sample);
else
ene_set_idle(dev, 1);
continue;
}
/* normal first sample recieved */
if (!dev->fan_input_inuse && dev->idle) {
ene_set_idle(dev, 0);
/* discard first recieved value, its random
since its the time signal was off before
first pulse if idle mode is enabled, HW
does that for us */
if (!enable_idle)
continue;
}
update_sample(dev, hw_sample);
send_sample(dev);
}
return IRQ_HANDLED;
}
static int ene_probe(struct pnp_dev *pnp_dev,
const struct pnp_device_id *dev_id)
{
struct ene_device *dev;
struct lirc_driver *lirc_driver;
int error = -ENOMEM;
dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
if (!dev)
goto err1;
dev->pnp_dev = pnp_dev;
pnp_set_drvdata(pnp_dev, dev);
/* prepare lirc interface */
error = -ENOMEM;
lirc_driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
if (!lirc_driver)
goto err2;
dev->lirc_driver = lirc_driver;
strcpy(lirc_driver->name, ENE_DRIVER_NAME);
lirc_driver->minor = -1;
lirc_driver->code_length = sizeof(int) * 8;
lirc_driver->features = LIRC_CAN_REC_MODE2;
lirc_driver->data = dev;
lirc_driver->set_use_inc = ene_hw_init;
lirc_driver->set_use_dec = ene_hw_deinit;
lirc_driver->dev = &pnp_dev->dev;
lirc_driver->owner = THIS_MODULE;
lirc_driver->rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
if (!lirc_driver->rbuf)
goto err3;
if (lirc_buffer_init(lirc_driver->rbuf, sizeof(int), sizeof(int) * 256))
goto err4;
error = -ENODEV;
if (lirc_register_driver(lirc_driver))
goto err5;
/* validate resources */
if (!pnp_port_valid(pnp_dev, 0) ||
pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
goto err6;
if (!pnp_irq_valid(pnp_dev, 0))
goto err6;
dev->hw_io = pnp_port_start(pnp_dev, 0);
dev->irq = pnp_irq(pnp_dev, 0);
/* claim the resources */
error = -EBUSY;
if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
goto err6;
if (request_irq(dev->irq, ene_hw_irq,
IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
goto err7;
/* detect hardware version and features */
error = ene_hw_detect(dev);
if (error)
goto err8;
ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
return 0;
err8:
free_irq(dev->irq, dev);
err7:
release_region(dev->hw_io, ENE_MAX_IO);
err6:
lirc_unregister_driver(lirc_driver->minor);
err5:
lirc_buffer_free(lirc_driver->rbuf);
err4:
kfree(lirc_driver->rbuf);
err3:
kfree(lirc_driver);
err2:
kfree(dev);
err1:
return error;
}
static void ene_remove(struct pnp_dev *pnp_dev)
{
struct ene_device *dev = pnp_get_drvdata(pnp_dev);
ene_hw_deinit(dev);
free_irq(dev->irq, dev);
release_region(dev->hw_io, ENE_MAX_IO);
lirc_unregister_driver(dev->lirc_driver->minor);
lirc_buffer_free(dev->lirc_driver->rbuf);
kfree(dev->lirc_driver);
kfree(dev);
}
#ifdef CONFIG_PM
/* TODO: make 'wake on IR' configurable and add .shutdown */
/* currently impossible due to lack of kernel support */
static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
{
struct ene_device *dev = pnp_get_drvdata(pnp_dev);
ene_hw_write_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, ENE_FW1_WAKE);
return 0;
}
static int ene_resume(struct pnp_dev *pnp_dev)
{
struct ene_device *dev = pnp_get_drvdata(pnp_dev);
if (dev->in_use)
ene_hw_init(dev);
ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_WAKE);
return 0;
}
#endif
static const struct pnp_device_id ene_ids[] = {
{.id = "ENE0100",},
{},
};
static struct pnp_driver ene_driver = {
.name = ENE_DRIVER_NAME,
.id_table = ene_ids,
.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
.probe = ene_probe,
.remove = __devexit_p(ene_remove),
#ifdef CONFIG_PM
.suspend = ene_suspend,
.resume = ene_resume,
#endif
};
static int __init ene_init(void)
{
if (sample_period < 5) {
ene_printk(KERN_ERR, "sample period must be at\n");
ene_printk(KERN_ERR, "least 5 us, (at least 30 recommended)\n");
return -EINVAL;
}
return pnp_register_driver(&ene_driver);
}
static void ene_exit(void)
{
pnp_unregister_driver(&ene_driver);
}
module_param(sample_period, int, S_IRUGO);
MODULE_PARM_DESC(sample_period, "Hardware sample period (75 us default)");
module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(enable_idle,
"Enables turning off signal sampling after long inactivity time; "
"if disabled might help detecting input signal (default: enabled)");
module_param(enable_learning, bool, S_IRUGO);
MODULE_PARM_DESC(enable_learning, "Use wide band (learning) reciever");
MODULE_DEVICE_TABLE(pnp, ene_ids);
MODULE_DESCRIPTION
("LIRC driver for KB3926B/KB3926C/KB3926D (aka ENE0100) CIR port");
MODULE_AUTHOR("Maxim Levitsky");
MODULE_LICENSE("GPL");
module_init(ene_init);
module_exit(ene_exit);

View File

@ -109,6 +109,7 @@ static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
static DEFINE_SPINLOCK(hardware_lock);
static DEFINE_SPINLOCK(dev_lock);
static bool device_open;
static int rx_buf[RBUF_LEN];
unsigned int rx_tail, rx_head;
@ -147,10 +148,11 @@ static void drop_port(void);
static int lirc_open(struct inode *inode, struct file *file)
{
spin_lock(&dev_lock);
if (module_refcount(THIS_MODULE)) {
if (device_open) {
spin_unlock(&dev_lock);
return -EBUSY;
}
device_open = true;
spin_unlock(&dev_lock);
return 0;
}
@ -158,6 +160,9 @@ static int lirc_open(struct inode *inode, struct file *file)
static int lirc_close(struct inode *inode, struct file *file)
{
spin_lock(&dev_lock);
device_open = false;
spin_unlock(&dev_lock);
return 0;
}
@ -363,7 +368,6 @@ static struct lirc_driver driver = {
};
#ifdef MODULE
static int init_chrdev(void)
{
driver.minor = lirc_register_driver(&driver);
@ -380,7 +384,6 @@ static void drop_chrdev(void)
{
lirc_unregister_driver(driver.minor);
}
#endif
/* SECTION: Hardware */

View File

@ -240,7 +240,7 @@ static void irq_handler(void *blah)
unsigned int level, newlevel;
unsigned int timeout;
if (!module_refcount(THIS_MODULE))
if (!is_open)
return;
if (!is_claimed)
@ -515,7 +515,7 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
static int lirc_open(struct inode *node, struct file *filep)
{
if (module_refcount(THIS_MODULE) || !lirc_claim())
if (is_open || !lirc_claim())
return -EBUSY;
parport_enable_irq(pport);

View File

@ -1,821 +0,0 @@
/*
* Streamzap Remote Control driver
*
* Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
*
* This driver was based on the work of Greg Wickham and Adrian
* Dewhurst. It was substantially rewritten to support correct signal
* gaps and now maintains a delay buffer, which is used to present
* consistent timing behaviour to user space applications. Without the
* delay buffer an ugly hack would be required in lircd, which can
* cause sluggish signal decoding in certain situations.
*
* This driver is based on the USB skeleton driver packaged with the
* kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <media/lirc.h>
#include <media/lirc_dev.h>
#define DRIVER_VERSION "1.28"
#define DRIVER_NAME "lirc_streamzap"
#define DRIVER_DESC "Streamzap Remote Control driver"
static int debug;
#define USB_STREAMZAP_VENDOR_ID 0x0e9c
#define USB_STREAMZAP_PRODUCT_ID 0x0000
/* Use our own dbg macro */
#define dprintk(fmt, args...) \
do { \
if (debug) \
printk(KERN_DEBUG DRIVER_NAME "[%d]: " \
fmt "\n", ## args); \
} while (0)
/* table of devices that work with this driver */
static struct usb_device_id streamzap_table[] = {
/* Streamzap Remote Control */
{ USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
/* Terminating entry */
{ }
};
MODULE_DEVICE_TABLE(usb, streamzap_table);
#define STREAMZAP_PULSE_MASK 0xf0
#define STREAMZAP_SPACE_MASK 0x0f
#define STREAMZAP_TIMEOUT 0xff
#define STREAMZAP_RESOLUTION 256
/* number of samples buffered */
#define STREAMZAP_BUF_LEN 128
enum StreamzapDecoderState {
PulseSpace,
FullPulse,
FullSpace,
IgnorePulse
};
/* Structure to hold all of our device specific stuff
*
* some remarks regarding locking:
* theoretically this struct can be accessed from three threads:
*
* - from lirc_dev through set_use_inc/set_use_dec
*
* - from the USB layer throuh probe/disconnect/irq
*
* Careful placement of lirc_register_driver/lirc_unregister_driver
* calls will prevent conflicts. lirc_dev makes sure that
* set_use_inc/set_use_dec are not being executed and will not be
* called after lirc_unregister_driver returns.
*
* - by the timer callback
*
* The timer is only running when the device is connected and the
* LIRC device is open. Making sure the timer is deleted by
* set_use_dec will make conflicts impossible.
*/
struct usb_streamzap {
/* usb */
/* save off the usb device pointer */
struct usb_device *udev;
/* the interface for this device */
struct usb_interface *interface;
/* buffer & dma */
unsigned char *buf_in;
dma_addr_t dma_in;
unsigned int buf_in_len;
struct usb_endpoint_descriptor *endpoint;
/* IRQ */
struct urb *urb_in;
/* lirc */
struct lirc_driver *driver;
struct lirc_buffer *delay_buf;
/* timer used to support delay buffering */
struct timer_list delay_timer;
int timer_running;
spinlock_t timer_lock;
/* tracks whether we are currently receiving some signal */
int idle;
/* sum of signal lengths received since signal start */
unsigned long sum;
/* start time of signal; necessary for gap tracking */
struct timeval signal_last;
struct timeval signal_start;
enum StreamzapDecoderState decoder_state;
struct timer_list flush_timer;
int flush;
int in_use;
int timeout_enabled;
};
/* local function prototypes */
static int streamzap_probe(struct usb_interface *interface,
const struct usb_device_id *id);
static void streamzap_disconnect(struct usb_interface *interface);
static void usb_streamzap_irq(struct urb *urb);
static int streamzap_use_inc(void *data);
static void streamzap_use_dec(void *data);
static long streamzap_ioctl(struct file *filep, unsigned int cmd,
unsigned long arg);
static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
static int streamzap_resume(struct usb_interface *intf);
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver streamzap_driver = {
.name = DRIVER_NAME,
.probe = streamzap_probe,
.disconnect = streamzap_disconnect,
.suspend = streamzap_suspend,
.resume = streamzap_resume,
.id_table = streamzap_table,
};
static void stop_timer(struct usb_streamzap *sz)
{
unsigned long flags;
spin_lock_irqsave(&sz->timer_lock, flags);
if (sz->timer_running) {
sz->timer_running = 0;
spin_unlock_irqrestore(&sz->timer_lock, flags);
del_timer_sync(&sz->delay_timer);
} else {
spin_unlock_irqrestore(&sz->timer_lock, flags);
}
}
static void flush_timeout(unsigned long arg)
{
struct usb_streamzap *sz = (struct usb_streamzap *) arg;
/* finally start accepting data */
sz->flush = 0;
}
static void delay_timeout(unsigned long arg)
{
unsigned long flags;
/* deliver data every 10 ms */
static unsigned long timer_inc =
(10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ));
struct usb_streamzap *sz = (struct usb_streamzap *) arg;
int data;
spin_lock_irqsave(&sz->timer_lock, flags);
if (!lirc_buffer_empty(sz->delay_buf) &&
!lirc_buffer_full(sz->driver->rbuf)) {
lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
lirc_buffer_write(sz->driver->rbuf, (unsigned char *) &data);
}
if (!lirc_buffer_empty(sz->delay_buf)) {
while (lirc_buffer_available(sz->delay_buf) <
STREAMZAP_BUF_LEN / 2 &&
!lirc_buffer_full(sz->driver->rbuf)) {
lirc_buffer_read(sz->delay_buf,
(unsigned char *) &data);
lirc_buffer_write(sz->driver->rbuf,
(unsigned char *) &data);
}
if (sz->timer_running) {
sz->delay_timer.expires = jiffies + timer_inc;
add_timer(&sz->delay_timer);
}
} else {
sz->timer_running = 0;
}
if (!lirc_buffer_empty(sz->driver->rbuf))
wake_up(&sz->driver->rbuf->wait_poll);
spin_unlock_irqrestore(&sz->timer_lock, flags);
}
static void flush_delay_buffer(struct usb_streamzap *sz)
{
int data;
int empty = 1;
while (!lirc_buffer_empty(sz->delay_buf)) {
empty = 0;
lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
if (!lirc_buffer_full(sz->driver->rbuf)) {
lirc_buffer_write(sz->driver->rbuf,
(unsigned char *) &data);
} else {
dprintk("buffer overflow", sz->driver->minor);
}
}
if (!empty)
wake_up(&sz->driver->rbuf->wait_poll);
}
static void push(struct usb_streamzap *sz, unsigned char *data)
{
unsigned long flags;
spin_lock_irqsave(&sz->timer_lock, flags);
if (lirc_buffer_full(sz->delay_buf)) {
int read_data;
lirc_buffer_read(sz->delay_buf,
(unsigned char *) &read_data);
if (!lirc_buffer_full(sz->driver->rbuf)) {
lirc_buffer_write(sz->driver->rbuf,
(unsigned char *) &read_data);
} else {
dprintk("buffer overflow", sz->driver->minor);
}
}
lirc_buffer_write(sz->delay_buf, data);
if (!sz->timer_running) {
sz->delay_timer.expires = jiffies + HZ/10;
add_timer(&sz->delay_timer);
sz->timer_running = 1;
}
spin_unlock_irqrestore(&sz->timer_lock, flags);
}
static void push_full_pulse(struct usb_streamzap *sz,
unsigned char value)
{
int pulse;
if (sz->idle) {
long deltv;
int tmp;
sz->signal_last = sz->signal_start;
do_gettimeofday(&sz->signal_start);
deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec;
if (deltv > 15) {
/* really long time */
tmp = LIRC_SPACE(LIRC_VALUE_MASK);
} else {
tmp = (int) (deltv*1000000+
sz->signal_start.tv_usec -
sz->signal_last.tv_usec);
tmp -= sz->sum;
tmp = LIRC_SPACE(tmp);
}
dprintk("ls %u", sz->driver->minor, tmp);
push(sz, (char *)&tmp);
sz->idle = 0;
sz->sum = 0;
}
pulse = ((int) value) * STREAMZAP_RESOLUTION;
pulse += STREAMZAP_RESOLUTION / 2;
sz->sum += pulse;
pulse = LIRC_PULSE(pulse);
dprintk("p %u", sz->driver->minor, pulse & PULSE_MASK);
push(sz, (char *)&pulse);
}
static void push_half_pulse(struct usb_streamzap *sz,
unsigned char value)
{
push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4);
}
static void push_full_space(struct usb_streamzap *sz,
unsigned char value)
{
int space;
space = ((int) value)*STREAMZAP_RESOLUTION;
space += STREAMZAP_RESOLUTION/2;
sz->sum += space;
space = LIRC_SPACE(space);
dprintk("s %u", sz->driver->minor, space);
push(sz, (char *)&space);
}
static void push_half_space(struct usb_streamzap *sz,
unsigned char value)
{
push_full_space(sz, value & STREAMZAP_SPACE_MASK);
}
/**
* usb_streamzap_irq - IRQ handler
*
* This procedure is invoked on reception of data from
* the usb remote.
*/
static void usb_streamzap_irq(struct urb *urb)
{
struct usb_streamzap *sz;
int len;
unsigned int i = 0;
if (!urb)
return;
sz = urb->context;
len = urb->actual_length;
switch (urb->status) {
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/*
* this urb is terminated, clean up.
* sz might already be invalid at this point
*/
dprintk("urb status: %d", -1, urb->status);
return;
default:
break;
}
dprintk("received %d", sz->driver->minor, urb->actual_length);
if (!sz->flush) {
for (i = 0; i < urb->actual_length; i++) {
dprintk("%d: %x", sz->driver->minor,
i, (unsigned char) sz->buf_in[i]);
switch (sz->decoder_state) {
case PulseSpace:
if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) ==
STREAMZAP_PULSE_MASK) {
sz->decoder_state = FullPulse;
continue;
} else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK)
== STREAMZAP_SPACE_MASK) {
push_half_pulse(sz, sz->buf_in[i]);
sz->decoder_state = FullSpace;
continue;
} else {
push_half_pulse(sz, sz->buf_in[i]);
push_half_space(sz, sz->buf_in[i]);
}
break;
case FullPulse:
push_full_pulse(sz, sz->buf_in[i]);
sz->decoder_state = IgnorePulse;
break;
case FullSpace:
if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
sz->idle = 1;
stop_timer(sz);
if (sz->timeout_enabled) {
int timeout =
LIRC_TIMEOUT
(STREAMZAP_TIMEOUT *
STREAMZAP_RESOLUTION);
push(sz, (char *)&timeout);
}
flush_delay_buffer(sz);
} else
push_full_space(sz, sz->buf_in[i]);
sz->decoder_state = PulseSpace;
break;
case IgnorePulse:
if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
STREAMZAP_SPACE_MASK) {
sz->decoder_state = FullSpace;
continue;
}
push_half_space(sz, sz->buf_in[i]);
sz->decoder_state = PulseSpace;
break;
}
}
}
usb_submit_urb(urb, GFP_ATOMIC);
return;
}
static const struct file_operations streamzap_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = streamzap_ioctl,
.read = lirc_dev_fop_read,
.write = lirc_dev_fop_write,
.poll = lirc_dev_fop_poll,
.open = lirc_dev_fop_open,
.release = lirc_dev_fop_close,
};
/**
* streamzap_probe
*
* Called by usb-core to associated with a candidate device
* On any failure the return value is the ERROR
* On success return 0
*/
static int streamzap_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_host_interface *iface_host;
struct usb_streamzap *sz;
struct lirc_driver *driver;
struct lirc_buffer *lirc_buf;
struct lirc_buffer *delay_buf;
char buf[63], name[128] = "";
int retval = -ENOMEM;
int minor = 0;
/* Allocate space for device driver specific data */
sz = kzalloc(sizeof(struct usb_streamzap), GFP_KERNEL);
if (sz == NULL)
return -ENOMEM;
sz->udev = udev;
sz->interface = interface;
/* Check to ensure endpoint information matches requirements */
iface_host = interface->cur_altsetting;
if (iface_host->desc.bNumEndpoints != 1) {
err("%s: Unexpected desc.bNumEndpoints (%d)", __func__,
iface_host->desc.bNumEndpoints);
retval = -ENODEV;
goto free_sz;
}
sz->endpoint = &(iface_host->endpoint[0].desc);
if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
!= USB_DIR_IN) {
err("%s: endpoint doesn't match input device 02%02x",
__func__, sz->endpoint->bEndpointAddress);
retval = -ENODEV;
goto free_sz;
}
if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
!= USB_ENDPOINT_XFER_INT) {
err("%s: endpoint attributes don't match xfer 02%02x",
__func__, sz->endpoint->bmAttributes);
retval = -ENODEV;
goto free_sz;
}
if (sz->endpoint->wMaxPacketSize == 0) {
err("%s: endpoint message size==0? ", __func__);
retval = -ENODEV;
goto free_sz;
}
/* Allocate the USB buffer and IRQ URB */
sz->buf_in_len = sz->endpoint->wMaxPacketSize;
sz->buf_in = usb_alloc_coherent(sz->udev, sz->buf_in_len,
GFP_ATOMIC, &sz->dma_in);
if (sz->buf_in == NULL)
goto free_sz;
sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
if (sz->urb_in == NULL)
goto free_sz;
/* Connect this device to the LIRC sub-system */
driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
if (!driver)
goto free_sz;
lirc_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
if (!lirc_buf)
goto free_driver;
if (lirc_buffer_init(lirc_buf, sizeof(int), STREAMZAP_BUF_LEN))
goto kfree_lirc_buf;
delay_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
if (!delay_buf)
goto free_lirc_buf;
if (lirc_buffer_init(delay_buf, sizeof(int), STREAMZAP_BUF_LEN))
goto kfree_delay_buf;
sz->driver = driver;
strcpy(sz->driver->name, DRIVER_NAME);
sz->driver->minor = -1;
sz->driver->sample_rate = 0;
sz->driver->code_length = sizeof(int) * 8;
sz->driver->features = LIRC_CAN_REC_MODE2 |
LIRC_CAN_GET_REC_RESOLUTION |
LIRC_CAN_SET_REC_TIMEOUT;
sz->driver->data = sz;
sz->driver->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
sz->driver->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
sz->driver->rbuf = lirc_buf;
sz->delay_buf = delay_buf;
sz->driver->set_use_inc = &streamzap_use_inc;
sz->driver->set_use_dec = &streamzap_use_dec;
sz->driver->fops = &streamzap_fops;
sz->driver->dev = &interface->dev;
sz->driver->owner = THIS_MODULE;
sz->idle = 1;
sz->decoder_state = PulseSpace;
init_timer(&sz->delay_timer);
sz->delay_timer.function = delay_timeout;
sz->delay_timer.data = (unsigned long) sz;
sz->timer_running = 0;
spin_lock_init(&sz->timer_lock);
init_timer(&sz->flush_timer);
sz->flush_timer.function = flush_timeout;
sz->flush_timer.data = (unsigned long) sz;
/* Complete final initialisations */
usb_fill_int_urb(sz->urb_in, udev,
usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress),
sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz,
sz->endpoint->bInterval);
sz->urb_in->transfer_dma = sz->dma_in;
sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
if (udev->descriptor.iManufacturer
&& usb_string(udev, udev->descriptor.iManufacturer,
buf, sizeof(buf)) > 0)
strlcpy(name, buf, sizeof(name));
if (udev->descriptor.iProduct
&& usb_string(udev, udev->descriptor.iProduct,
buf, sizeof(buf)) > 0)
snprintf(name + strlen(name), sizeof(name) - strlen(name),
" %s", buf);
minor = lirc_register_driver(driver);
if (minor < 0)
goto free_delay_buf;
sz->driver->minor = minor;
usb_set_intfdata(interface, sz);
printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n",
sz->driver->minor, name,
udev->bus->busnum, sz->udev->devnum);
return 0;
free_delay_buf:
lirc_buffer_free(sz->delay_buf);
kfree_delay_buf:
kfree(delay_buf);
free_lirc_buf:
lirc_buffer_free(sz->driver->rbuf);
kfree_lirc_buf:
kfree(lirc_buf);
free_driver:
kfree(driver);
free_sz:
if (retval == -ENOMEM)
err("Out of memory");
if (sz) {
usb_free_urb(sz->urb_in);
usb_free_coherent(udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
kfree(sz);
}
return retval;
}
static int streamzap_use_inc(void *data)
{
struct usb_streamzap *sz = data;
if (!sz) {
dprintk("%s called with no context", -1, __func__);
return -EINVAL;
}
dprintk("set use inc", sz->driver->minor);
lirc_buffer_clear(sz->driver->rbuf);
lirc_buffer_clear(sz->delay_buf);
sz->flush_timer.expires = jiffies + HZ;
sz->flush = 1;
add_timer(&sz->flush_timer);
sz->urb_in->dev = sz->udev;
if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
dprintk("open result = -EIO error submitting urb",
sz->driver->minor);
return -EIO;
}
sz->in_use++;
return 0;
}
static void streamzap_use_dec(void *data)
{
struct usb_streamzap *sz = data;
if (!sz) {
dprintk("%s called with no context", -1, __func__);
return;
}
dprintk("set use dec", sz->driver->minor);
if (sz->flush) {
sz->flush = 0;
del_timer_sync(&sz->flush_timer);
}
usb_kill_urb(sz->urb_in);
stop_timer(sz);
sz->in_use--;
}
static long streamzap_ioctl(struct file *filep, unsigned int cmd,
unsigned long arg)
{
int result = 0;
int val;
struct usb_streamzap *sz = lirc_get_pdata(filep);
switch (cmd) {
case LIRC_GET_REC_RESOLUTION:
result = put_user(STREAMZAP_RESOLUTION, (unsigned int *) arg);
break;
case LIRC_SET_REC_TIMEOUT:
result = get_user(val, (int *)arg);
if (result == 0) {
if (val == STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION)
sz->timeout_enabled = 1;
else if (val == 0)
sz->timeout_enabled = 0;
else
result = -EINVAL;
}
break;
default:
return lirc_dev_fop_ioctl(filep, cmd, arg);
}
return result;
}
/**
* streamzap_disconnect
*
* Called by the usb core when the device is removed from the system.
*
* This routine guarantees that the driver will not submit any more urbs
* by clearing dev->udev. It is also supposed to terminate any currently
* active urbs. Unfortunately, usb_bulk_msg(), used in streamzap_read(),
* does not provide any way to do this.
*/
static void streamzap_disconnect(struct usb_interface *interface)
{
struct usb_streamzap *sz;
int errnum;
int minor;
sz = usb_get_intfdata(interface);
/* unregister from the LIRC sub-system */
errnum = lirc_unregister_driver(sz->driver->minor);
if (errnum != 0)
dprintk("error in lirc_unregister: (returned %d)",
sz->driver->minor, errnum);
lirc_buffer_free(sz->delay_buf);
lirc_buffer_free(sz->driver->rbuf);
/* unregister from the USB sub-system */
usb_free_urb(sz->urb_in);
usb_free_coherent(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
minor = sz->driver->minor;
kfree(sz->driver->rbuf);
kfree(sz->driver);
kfree(sz->delay_buf);
kfree(sz);
printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
}
static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usb_streamzap *sz = usb_get_intfdata(intf);
printk(KERN_INFO DRIVER_NAME "[%d]: suspend\n", sz->driver->minor);
if (sz->in_use) {
if (sz->flush) {
sz->flush = 0;
del_timer_sync(&sz->flush_timer);
}
stop_timer(sz);
usb_kill_urb(sz->urb_in);
}
return 0;
}
static int streamzap_resume(struct usb_interface *intf)
{
struct usb_streamzap *sz = usb_get_intfdata(intf);
lirc_buffer_clear(sz->driver->rbuf);
lirc_buffer_clear(sz->delay_buf);
if (sz->in_use) {
sz->flush_timer.expires = jiffies + HZ;
sz->flush = 1;
add_timer(&sz->flush_timer);
sz->urb_in->dev = sz->udev;
if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
dprintk("open result = -EIO error submitting urb",
sz->driver->minor);
return -EIO;
}
}
return 0;
}
/**
* usb_streamzap_init
*/
static int __init usb_streamzap_init(void)
{
int result;
/* register this driver with the USB subsystem */
result = usb_register(&streamzap_driver);
if (result) {
err("usb_register failed. Error number %d",
result);
return result;
}
printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n");
return 0;
}
/**
* usb_streamzap_exit
*/
static void __exit usb_streamzap_exit(void)
{
usb_deregister(&streamzap_driver);
}
module_init(usb_streamzap_init);
module_exit(usb_streamzap_exit);
MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Enable debugging messages");

View File

@ -277,6 +277,7 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R', 'G', 'B', 'P') /* 16 RGB-5-6-5 */
#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16 RGB-5-5-5 BE */
#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */
#define V4L2_PIX_FMT_BGR666 v4l2_fourcc('B', 'G', 'R', 'H') /* 18 BGR-6-6-6 */
#define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */
#define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */
#define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B', 'G', 'R', '4') /* 32 BGR-8-8-8-8 */

Some files were not shown because too many files have changed in this diff Show More