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:
commit
7ae0dea900
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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.
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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>");
|
|
@ -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>");
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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, ¶ms);
|
||||
params.enable = false;
|
||||
params.shutdown = false;
|
||||
params.invert_level = true;
|
||||
v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms);
|
||||
params.shutdown = true;
|
||||
v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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, ¶ms);
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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_ */
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -779,9 +779,12 @@ static int soc_camera_s_crop(struct file *file, void *fh,
|
|||
ret = ici->ops->get_crop(icd, ¤t_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;
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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
Loading…
Reference in New Issue