2019-06-01 16:09:02 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2016-08-17 14:05:27 +08:00
|
|
|
/*
|
2019-06-12 20:00:31 +08:00
|
|
|
* Microchip Image Sensor Controller (ISC) common driver base
|
2016-08-17 14:05:27 +08:00
|
|
|
*
|
2019-06-12 20:00:31 +08:00
|
|
|
* Copyright (C) 2016-2019 Microchip Technology, Inc.
|
2016-08-17 14:05:27 +08:00
|
|
|
*
|
2019-06-12 20:00:31 +08:00
|
|
|
* Author: Songjun Wu
|
|
|
|
* Author: Eugen Hristev <eugen.hristev@microchip.com>
|
2016-08-17 14:05:27 +08:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/clkdev.h>
|
|
|
|
#include <linux/clk-provider.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/interrupt.h>
|
2017-01-24 16:05:57 +08:00
|
|
|
#include <linux/math64.h>
|
2016-08-17 14:05:27 +08:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/of.h>
|
2016-08-27 07:17:25 +08:00
|
|
|
#include <linux/of_graph.h>
|
2016-08-17 14:05:27 +08:00
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/pm_runtime.h>
|
|
|
|
#include <linux/regmap.h>
|
|
|
|
#include <linux/videodev2.h>
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
#include <linux/atmel-isc-media.h>
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
#include <media/v4l2-ctrls.h>
|
2016-08-17 14:05:27 +08:00
|
|
|
#include <media/v4l2-device.h>
|
2017-01-24 16:05:57 +08:00
|
|
|
#include <media/v4l2-event.h>
|
2016-08-17 14:05:27 +08:00
|
|
|
#include <media/v4l2-image-sizes.h>
|
|
|
|
#include <media/v4l2-ioctl.h>
|
2016-08-27 07:17:25 +08:00
|
|
|
#include <media/v4l2-fwnode.h>
|
2016-08-17 14:05:27 +08:00
|
|
|
#include <media/v4l2-subdev.h>
|
|
|
|
#include <media/videobuf2-dma-contig.h>
|
|
|
|
|
|
|
|
#include "atmel-isc-regs.h"
|
2019-06-12 20:00:31 +08:00
|
|
|
#include "atmel-isc.h"
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-06-19 15:24:41 +08:00
|
|
|
static unsigned int debug;
|
2019-06-12 20:00:31 +08:00
|
|
|
module_param(debug, int, 0644);
|
|
|
|
MODULE_PARM_DESC(debug, "debug level (0-2)");
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-06-19 15:24:41 +08:00
|
|
|
static unsigned int sensor_preferred = 1;
|
2019-06-12 20:00:31 +08:00
|
|
|
module_param(sensor_preferred, uint, 0644);
|
|
|
|
MODULE_PARM_DESC(sensor_preferred,
|
|
|
|
"Sensor is preferred to output the specified format (1-on 0-off), default 1");
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
#define ISC_IS_FORMAT_RAW(mbus_code) \
|
|
|
|
(((mbus_code) & 0xf000) == 0x3000)
|
|
|
|
|
2019-11-25 16:32:49 +08:00
|
|
|
#define ISC_IS_FORMAT_GREY(mbus_code) \
|
|
|
|
(((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \
|
|
|
|
(((mbus_code) == MEDIA_BUS_FMT_Y8_1X8)))
|
|
|
|
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
static inline void isc_update_v4l2_ctrls(struct isc_device *isc)
|
|
|
|
{
|
|
|
|
struct isc_ctrls *ctrls = &isc->ctrls;
|
|
|
|
|
|
|
|
/* In here we set the v4l2 controls w.r.t. our pipeline config */
|
|
|
|
v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]);
|
|
|
|
v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]);
|
|
|
|
v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]);
|
|
|
|
v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]);
|
|
|
|
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]);
|
|
|
|
v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]);
|
|
|
|
v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]);
|
|
|
|
v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]);
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
}
|
|
|
|
|
2019-04-15 22:13:49 +08:00
|
|
|
static inline void isc_update_awb_ctrls(struct isc_device *isc)
|
|
|
|
{
|
|
|
|
struct isc_ctrls *ctrls = &isc->ctrls;
|
|
|
|
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
/* In here we set our actual hw pipeline config */
|
|
|
|
|
2019-04-15 22:13:49 +08:00
|
|
|
regmap_write(isc->regmap, ISC_WB_O_RGR,
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
((ctrls->offset[ISC_HIS_CFG_MODE_R])) |
|
|
|
|
((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16));
|
2019-04-15 22:13:49 +08:00
|
|
|
regmap_write(isc->regmap, ISC_WB_O_BGB,
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
((ctrls->offset[ISC_HIS_CFG_MODE_B])) |
|
|
|
|
((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16));
|
2019-04-15 22:13:49 +08:00
|
|
|
regmap_write(isc->regmap, ISC_WB_G_RGR,
|
|
|
|
ctrls->gain[ISC_HIS_CFG_MODE_R] |
|
|
|
|
(ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16));
|
|
|
|
regmap_write(isc->regmap, ISC_WB_G_BGB,
|
|
|
|
ctrls->gain[ISC_HIS_CFG_MODE_B] |
|
|
|
|
(ctrls->gain[ISC_HIS_CFG_MODE_GB] << 16));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void isc_reset_awb_ctrls(struct isc_device *isc)
|
|
|
|
{
|
2019-06-12 20:00:35 +08:00
|
|
|
unsigned int c;
|
2019-04-15 22:13:49 +08:00
|
|
|
|
|
|
|
for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) {
|
|
|
|
/* gains have a fixed point at 9 decimals */
|
|
|
|
isc->ctrls.gain[c] = 1 << 9;
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
/* offsets are in 2's complements */
|
|
|
|
isc->ctrls.offset[c] = 0;
|
2019-04-15 22:13:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-10 10:46:37 +08:00
|
|
|
static int isc_wait_clk_stable(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct isc_clk *isc_clk = to_isc_clk(hw);
|
|
|
|
struct regmap *regmap = isc_clk->regmap;
|
|
|
|
unsigned long timeout = jiffies + usecs_to_jiffies(1000);
|
|
|
|
unsigned int status;
|
|
|
|
|
|
|
|
while (time_before(jiffies, timeout)) {
|
|
|
|
regmap_read(regmap, ISC_CLKSR, &status);
|
|
|
|
if (!(status & ISC_CLKSR_SIP))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
usleep_range(10, 250);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_clk_prepare(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct isc_clk *isc_clk = to_isc_clk(hw);
|
2021-04-23 22:47:42 +08:00
|
|
|
int ret;
|
2017-10-10 10:46:37 +08:00
|
|
|
|
2021-04-23 22:47:42 +08:00
|
|
|
if (isc_clk->id == ISC_ISPCK) {
|
|
|
|
ret = pm_runtime_resume_and_get(isc_clk->dev);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
2017-10-10 10:46:37 +08:00
|
|
|
|
|
|
|
return isc_wait_clk_stable(hw);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void isc_clk_unprepare(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct isc_clk *isc_clk = to_isc_clk(hw);
|
|
|
|
|
|
|
|
isc_wait_clk_stable(hw);
|
|
|
|
|
2017-10-30 08:46:50 +08:00
|
|
|
if (isc_clk->id == ISC_ISPCK)
|
2017-10-10 10:46:37 +08:00
|
|
|
pm_runtime_put_sync(isc_clk->dev);
|
|
|
|
}
|
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
static int isc_clk_enable(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct isc_clk *isc_clk = to_isc_clk(hw);
|
|
|
|
u32 id = isc_clk->id;
|
|
|
|
struct regmap *regmap = isc_clk->regmap;
|
2017-10-10 10:46:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
unsigned int status;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2021-04-13 18:57:05 +08:00
|
|
|
dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n",
|
|
|
|
__func__, id, isc_clk->div, isc_clk->parent_id);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2017-10-10 10:46:36 +08:00
|
|
|
spin_lock_irqsave(&isc_clk->lock, flags);
|
2016-08-17 14:05:27 +08:00
|
|
|
regmap_update_bits(regmap, ISC_CLKCFG,
|
|
|
|
ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id),
|
|
|
|
(isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) |
|
|
|
|
(isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id)));
|
|
|
|
|
|
|
|
regmap_write(regmap, ISC_CLKEN, ISC_CLK(id));
|
2017-10-10 10:46:36 +08:00
|
|
|
spin_unlock_irqrestore(&isc_clk->lock, flags);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2017-10-10 10:46:36 +08:00
|
|
|
regmap_read(regmap, ISC_CLKSR, &status);
|
|
|
|
if (status & ISC_CLK(id))
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return -EINVAL;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void isc_clk_disable(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct isc_clk *isc_clk = to_isc_clk(hw);
|
|
|
|
u32 id = isc_clk->id;
|
2017-10-10 10:46:36 +08:00
|
|
|
unsigned long flags;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2017-10-10 10:46:36 +08:00
|
|
|
spin_lock_irqsave(&isc_clk->lock, flags);
|
2016-08-17 14:05:27 +08:00
|
|
|
regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id));
|
2017-10-10 10:46:36 +08:00
|
|
|
spin_unlock_irqrestore(&isc_clk->lock, flags);
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_clk_is_enabled(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct isc_clk *isc_clk = to_isc_clk(hw);
|
|
|
|
u32 status;
|
2021-04-23 22:47:42 +08:00
|
|
|
int ret;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2021-04-23 22:47:42 +08:00
|
|
|
if (isc_clk->id == ISC_ISPCK) {
|
|
|
|
ret = pm_runtime_resume_and_get(isc_clk->dev);
|
|
|
|
if (ret < 0)
|
|
|
|
return 0;
|
|
|
|
}
|
2017-10-10 10:46:38 +08:00
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
regmap_read(isc_clk->regmap, ISC_CLKSR, &status);
|
|
|
|
|
2017-10-30 08:46:50 +08:00
|
|
|
if (isc_clk->id == ISC_ISPCK)
|
2017-10-10 10:46:38 +08:00
|
|
|
pm_runtime_put_sync(isc_clk->dev);
|
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
return status & ISC_CLK(isc_clk->id) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long
|
|
|
|
isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
|
|
|
{
|
|
|
|
struct isc_clk *isc_clk = to_isc_clk(hw);
|
|
|
|
|
|
|
|
return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_clk_determine_rate(struct clk_hw *hw,
|
|
|
|
struct clk_rate_request *req)
|
|
|
|
{
|
|
|
|
struct isc_clk *isc_clk = to_isc_clk(hw);
|
|
|
|
long best_rate = -EINVAL;
|
|
|
|
int best_diff = -1;
|
|
|
|
unsigned int i, div;
|
|
|
|
|
|
|
|
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
|
|
|
|
struct clk_hw *parent;
|
|
|
|
unsigned long parent_rate;
|
|
|
|
|
|
|
|
parent = clk_hw_get_parent_by_index(hw, i);
|
|
|
|
if (!parent)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
parent_rate = clk_hw_get_rate(parent);
|
|
|
|
if (!parent_rate)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) {
|
|
|
|
unsigned long rate;
|
|
|
|
int diff;
|
|
|
|
|
|
|
|
rate = DIV_ROUND_CLOSEST(parent_rate, div);
|
|
|
|
diff = abs(req->rate - rate);
|
|
|
|
|
|
|
|
if (best_diff < 0 || best_diff > diff) {
|
|
|
|
best_rate = rate;
|
|
|
|
best_diff = diff;
|
|
|
|
req->best_parent_rate = parent_rate;
|
|
|
|
req->best_parent_hw = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!best_diff || rate < req->rate)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!best_diff)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_dbg(isc_clk->dev,
|
|
|
|
"ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
|
|
|
|
__func__, best_rate,
|
|
|
|
__clk_get_name((req->best_parent_hw)->clk),
|
|
|
|
req->best_parent_rate);
|
|
|
|
|
|
|
|
if (best_rate < 0)
|
|
|
|
return best_rate;
|
|
|
|
|
|
|
|
req->rate = best_rate;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_clk_set_parent(struct clk_hw *hw, u8 index)
|
|
|
|
{
|
|
|
|
struct isc_clk *isc_clk = to_isc_clk(hw);
|
|
|
|
|
|
|
|
if (index >= clk_hw_get_num_parents(hw))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
isc_clk->parent_id = index;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 isc_clk_get_parent(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct isc_clk *isc_clk = to_isc_clk(hw);
|
|
|
|
|
|
|
|
return isc_clk->parent_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_clk_set_rate(struct clk_hw *hw,
|
|
|
|
unsigned long rate,
|
|
|
|
unsigned long parent_rate)
|
|
|
|
{
|
|
|
|
struct isc_clk *isc_clk = to_isc_clk(hw);
|
|
|
|
u32 div;
|
|
|
|
|
|
|
|
if (!rate)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
div = DIV_ROUND_CLOSEST(parent_rate, rate);
|
|
|
|
if (div > (ISC_CLK_MAX_DIV + 1) || !div)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
isc_clk->div = div - 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct clk_ops isc_clk_ops = {
|
2017-10-10 10:46:37 +08:00
|
|
|
.prepare = isc_clk_prepare,
|
|
|
|
.unprepare = isc_clk_unprepare,
|
2016-08-17 14:05:27 +08:00
|
|
|
.enable = isc_clk_enable,
|
|
|
|
.disable = isc_clk_disable,
|
|
|
|
.is_enabled = isc_clk_is_enabled,
|
|
|
|
.recalc_rate = isc_clk_recalc_rate,
|
|
|
|
.determine_rate = isc_clk_determine_rate,
|
|
|
|
.set_parent = isc_clk_set_parent,
|
|
|
|
.get_parent = isc_clk_get_parent,
|
|
|
|
.set_rate = isc_clk_set_rate,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int isc_clk_register(struct isc_device *isc, unsigned int id)
|
|
|
|
{
|
|
|
|
struct regmap *regmap = isc->regmap;
|
|
|
|
struct device_node *np = isc->dev->of_node;
|
|
|
|
struct isc_clk *isc_clk;
|
|
|
|
struct clk_init_data init;
|
|
|
|
const char *clk_name = np->name;
|
|
|
|
const char *parent_names[3];
|
|
|
|
int num_parents;
|
|
|
|
|
|
|
|
num_parents = of_clk_get_parent_count(np);
|
|
|
|
if (num_parents < 1 || num_parents > 3)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (num_parents > 2 && id == ISC_ISPCK)
|
|
|
|
num_parents = 2;
|
|
|
|
|
|
|
|
of_clk_parent_fill(np, parent_names, num_parents);
|
|
|
|
|
|
|
|
if (id == ISC_MCK)
|
|
|
|
of_property_read_string(np, "clock-output-names", &clk_name);
|
|
|
|
else
|
|
|
|
clk_name = "isc-ispck";
|
|
|
|
|
|
|
|
init.parent_names = parent_names;
|
|
|
|
init.num_parents = num_parents;
|
|
|
|
init.name = clk_name;
|
|
|
|
init.ops = &isc_clk_ops;
|
|
|
|
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
|
|
|
|
|
|
|
|
isc_clk = &isc->isc_clks[id];
|
|
|
|
isc_clk->hw.init = &init;
|
|
|
|
isc_clk->regmap = regmap;
|
|
|
|
isc_clk->id = id;
|
|
|
|
isc_clk->dev = isc->dev;
|
2017-10-10 10:46:36 +08:00
|
|
|
spin_lock_init(&isc_clk->lock);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
isc_clk->clk = clk_register(isc->dev, &isc_clk->hw);
|
|
|
|
if (IS_ERR(isc_clk->clk)) {
|
|
|
|
dev_err(isc->dev, "%s: clock register fail\n", clk_name);
|
|
|
|
return PTR_ERR(isc_clk->clk);
|
|
|
|
} else if (id == ISC_MCK)
|
|
|
|
of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-12 20:00:31 +08:00
|
|
|
int isc_clk_init(struct isc_device *isc)
|
2016-08-17 14:05:27 +08:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++)
|
|
|
|
isc->isc_clks[i].clk = ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
|
|
|
|
ret = isc_clk_register(isc, i);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2021-07-05 20:57:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(isc_clk_init);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-06-12 20:00:31 +08:00
|
|
|
void isc_clk_cleanup(struct isc_device *isc)
|
2016-08-17 14:05:27 +08:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
of_clk_del_provider(isc->dev->of_node);
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
|
|
|
|
struct isc_clk *isc_clk = &isc->isc_clks[i];
|
|
|
|
|
|
|
|
if (!IS_ERR(isc_clk->clk))
|
|
|
|
clk_unregister(isc_clk->clk);
|
|
|
|
}
|
|
|
|
}
|
2021-07-05 20:57:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(isc_clk_cleanup);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
static int isc_queue_setup(struct vb2_queue *vq,
|
|
|
|
unsigned int *nbuffers, unsigned int *nplanes,
|
|
|
|
unsigned int sizes[], struct device *alloc_devs[])
|
|
|
|
{
|
|
|
|
struct isc_device *isc = vb2_get_drv_priv(vq);
|
|
|
|
unsigned int size = isc->fmt.fmt.pix.sizeimage;
|
|
|
|
|
|
|
|
if (*nplanes)
|
|
|
|
return sizes[0] < size ? -EINVAL : 0;
|
|
|
|
|
|
|
|
*nplanes = 1;
|
|
|
|
sizes[0] = size;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_buffer_prepare(struct vb2_buffer *vb)
|
|
|
|
{
|
|
|
|
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
|
|
|
struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue);
|
|
|
|
unsigned long size = isc->fmt.fmt.pix.sizeimage;
|
|
|
|
|
|
|
|
if (vb2_plane_size(vb, 0) < size) {
|
|
|
|
v4l2_err(&isc->v4l2_dev, "buffer too small (%lu < %lu)\n",
|
|
|
|
vb2_plane_size(vb, 0), size);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
vb2_set_plane_payload(vb, 0, size);
|
|
|
|
|
|
|
|
vbuf->field = isc->fmt.fmt.pix.field;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
static void isc_start_dma(struct isc_device *isc)
|
|
|
|
{
|
|
|
|
struct regmap *regmap = isc->regmap;
|
2019-03-29 15:38:28 +08:00
|
|
|
u32 sizeimage = isc->fmt.fmt.pix.sizeimage;
|
2017-01-24 16:05:57 +08:00
|
|
|
u32 dctrl_dview;
|
|
|
|
dma_addr_t addr0;
|
2019-04-12 18:19:40 +08:00
|
|
|
u32 h, w;
|
|
|
|
|
|
|
|
h = isc->fmt.fmt.pix.height;
|
|
|
|
w = isc->fmt.fmt.pix.width;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In case the sensor is not RAW, it will output a pixel (12-16 bits)
|
|
|
|
* with two samples on the ISC Data bus (which is 8-12)
|
|
|
|
* ISC will count each sample, so, we need to multiply these values
|
|
|
|
* by two, to get the real number of samples for the required pixels.
|
|
|
|
*/
|
|
|
|
if (!ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) {
|
|
|
|
h <<= 1;
|
|
|
|
w <<= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We limit the column/row count that the ISC will output according
|
|
|
|
* to the configured resolution that we want.
|
|
|
|
* This will avoid the situation where the sensor is misconfigured,
|
|
|
|
* sending more data, and the ISC will just take it and DMA to memory,
|
|
|
|
* causing corruption.
|
|
|
|
*/
|
|
|
|
regmap_write(regmap, ISC_PFE_CFG1,
|
|
|
|
(ISC_PFE_CFG1_COLMIN(0) & ISC_PFE_CFG1_COLMIN_MASK) |
|
|
|
|
(ISC_PFE_CFG1_COLMAX(w - 1) & ISC_PFE_CFG1_COLMAX_MASK));
|
|
|
|
|
|
|
|
regmap_write(regmap, ISC_PFE_CFG2,
|
|
|
|
(ISC_PFE_CFG2_ROWMIN(0) & ISC_PFE_CFG2_ROWMIN_MASK) |
|
|
|
|
(ISC_PFE_CFG2_ROWMAX(h - 1) & ISC_PFE_CFG2_ROWMAX_MASK));
|
|
|
|
|
|
|
|
regmap_update_bits(regmap, ISC_PFE_CFG0,
|
|
|
|
ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN,
|
|
|
|
ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN);
|
2017-01-24 16:05:57 +08:00
|
|
|
|
|
|
|
addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0);
|
2021-04-13 18:57:12 +08:00
|
|
|
regmap_write(regmap, ISC_DAD0 + isc->offsets.dma, addr0);
|
2017-01-24 16:05:57 +08:00
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
switch (isc->config.fourcc) {
|
2017-01-24 16:05:57 +08:00
|
|
|
case V4L2_PIX_FMT_YUV420:
|
2021-04-13 18:57:12 +08:00
|
|
|
regmap_write(regmap, ISC_DAD1 + isc->offsets.dma,
|
|
|
|
addr0 + (sizeimage * 2) / 3);
|
|
|
|
regmap_write(regmap, ISC_DAD2 + isc->offsets.dma,
|
|
|
|
addr0 + (sizeimage * 5) / 6);
|
2017-01-24 16:05:57 +08:00
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_YUV422P:
|
2021-04-13 18:57:12 +08:00
|
|
|
regmap_write(regmap, ISC_DAD1 + isc->offsets.dma,
|
|
|
|
addr0 + sizeimage / 2);
|
|
|
|
regmap_write(regmap, ISC_DAD2 + isc->offsets.dma,
|
|
|
|
addr0 + (sizeimage * 3) / 4);
|
2017-01-24 16:05:57 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
dctrl_dview = isc->config.dctrl_dview;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2021-04-13 18:57:12 +08:00
|
|
|
regmap_write(regmap, ISC_DCTRL + isc->offsets.dma,
|
|
|
|
dctrl_dview | ISC_DCTRL_IE_IS);
|
2019-04-15 22:13:49 +08:00
|
|
|
spin_lock(&isc->awb_lock);
|
2016-08-17 14:05:27 +08:00
|
|
|
regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE);
|
2019-04-15 22:13:49 +08:00
|
|
|
spin_unlock(&isc->awb_lock);
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void isc_set_pipeline(struct isc_device *isc, u32 pipeline)
|
|
|
|
{
|
2017-01-24 16:05:57 +08:00
|
|
|
struct regmap *regmap = isc->regmap;
|
|
|
|
struct isc_ctrls *ctrls = &isc->ctrls;
|
|
|
|
u32 val, bay_cfg;
|
|
|
|
const u32 *gamma;
|
2016-08-17 14:05:27 +08:00
|
|
|
unsigned int i;
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
/* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */
|
2016-08-17 14:05:27 +08:00
|
|
|
for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
|
|
|
|
val = pipeline & BIT(i) ? 1 : 0;
|
|
|
|
regmap_field_write(isc->pipeline[i], val);
|
|
|
|
}
|
2017-01-24 16:05:57 +08:00
|
|
|
|
|
|
|
if (!pipeline)
|
|
|
|
return;
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
bay_cfg = isc->config.sd_format->cfa_baycfg;
|
2017-01-24 16:05:57 +08:00
|
|
|
|
|
|
|
regmap_write(regmap, ISC_WB_CFG, bay_cfg);
|
2019-04-15 22:13:49 +08:00
|
|
|
isc_update_awb_ctrls(isc);
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
isc_update_v4l2_ctrls(isc);
|
2017-01-24 16:05:57 +08:00
|
|
|
|
|
|
|
regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL);
|
|
|
|
|
2021-04-13 18:56:59 +08:00
|
|
|
gamma = &isc->gamma_table[ctrls->gamma_index][0];
|
2017-01-24 16:05:57 +08:00
|
|
|
regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES);
|
|
|
|
regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES);
|
|
|
|
regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES);
|
|
|
|
|
2021-04-13 18:57:19 +08:00
|
|
|
isc->config_dpc(isc);
|
2021-04-13 18:57:04 +08:00
|
|
|
isc->config_csc(isc);
|
2021-04-13 18:57:07 +08:00
|
|
|
isc->config_cbc(isc);
|
2021-04-13 18:57:17 +08:00
|
|
|
isc->config_cc(isc);
|
2021-04-13 18:57:20 +08:00
|
|
|
isc->config_gam(isc);
|
2017-01-24 16:05:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_update_profile(struct isc_device *isc)
|
|
|
|
{
|
|
|
|
struct regmap *regmap = isc->regmap;
|
|
|
|
u32 sr;
|
|
|
|
int counter = 100;
|
|
|
|
|
|
|
|
regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
|
|
|
|
|
|
|
|
regmap_read(regmap, ISC_CTRLSR, &sr);
|
|
|
|
while ((sr & ISC_CTRL_UPPRO) && counter--) {
|
|
|
|
usleep_range(1000, 2000);
|
|
|
|
regmap_read(regmap, ISC_CTRLSR, &sr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (counter < 0) {
|
2019-03-29 15:38:28 +08:00
|
|
|
v4l2_warn(&isc->v4l2_dev, "Time out to update profile\n");
|
2017-01-24 16:05:57 +08:00
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
static void isc_set_histogram(struct isc_device *isc, bool enable)
|
2017-01-24 16:05:57 +08:00
|
|
|
{
|
|
|
|
struct regmap *regmap = isc->regmap;
|
|
|
|
struct isc_ctrls *ctrls = &isc->ctrls;
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
if (enable) {
|
2021-04-13 18:57:11 +08:00
|
|
|
regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his,
|
2019-04-15 22:13:49 +08:00
|
|
|
ISC_HIS_CFG_MODE_GR |
|
2019-03-29 15:38:28 +08:00
|
|
|
(isc->config.sd_format->cfa_baycfg
|
|
|
|
<< ISC_HIS_CFG_BAYSEL_SHIFT) |
|
|
|
|
ISC_HIS_CFG_RAR);
|
2021-04-13 18:57:11 +08:00
|
|
|
regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his,
|
|
|
|
ISC_HIS_CTRL_EN);
|
2017-01-24 16:05:57 +08:00
|
|
|
regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE);
|
2019-04-15 22:13:49 +08:00
|
|
|
ctrls->hist_id = ISC_HIS_CFG_MODE_GR;
|
2017-01-24 16:05:57 +08:00
|
|
|
isc_update_profile(isc);
|
|
|
|
regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
|
|
|
|
|
|
|
|
ctrls->hist_stat = HIST_ENABLED;
|
2019-03-29 15:38:28 +08:00
|
|
|
} else {
|
2017-01-24 16:05:57 +08:00
|
|
|
regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE);
|
2021-04-13 18:57:11 +08:00
|
|
|
regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his,
|
|
|
|
ISC_HIS_CTRL_DIS);
|
2017-01-24 16:05:57 +08:00
|
|
|
|
|
|
|
ctrls->hist_stat = HIST_DISABLED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
static int isc_configure(struct isc_device *isc)
|
|
|
|
{
|
|
|
|
struct regmap *regmap = isc->regmap;
|
2021-04-13 18:57:21 +08:00
|
|
|
u32 pfe_cfg0, dcfg, mask, pipeline;
|
2019-03-29 15:38:28 +08:00
|
|
|
struct isc_subdev_entity *subdev = isc->current_subdev;
|
2017-01-24 16:05:57 +08:00
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
pfe_cfg0 = isc->config.sd_format->pfe_cfg0_bps;
|
|
|
|
pipeline = isc->config.bits_pipeline;
|
|
|
|
|
2021-04-13 18:57:03 +08:00
|
|
|
dcfg = isc->config.dcfg_imode | isc->dcfg;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
pfe_cfg0 |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE;
|
2016-08-17 14:05:27 +08:00
|
|
|
mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW |
|
|
|
|
ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW |
|
2019-02-04 22:18:13 +08:00
|
|
|
ISC_PFE_CFG0_MODE_MASK | ISC_PFE_CFG0_CCIR_CRC |
|
2021-04-13 18:57:29 +08:00
|
|
|
ISC_PFE_CFG0_CCIR656 | ISC_PFE_CFG0_MIPI;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2021-04-13 18:57:21 +08:00
|
|
|
isc->config_rlp(isc);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2021-04-13 18:57:12 +08:00
|
|
|
regmap_write(regmap, ISC_DCFG + isc->offsets.dma, dcfg);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
/* Set the pipeline */
|
|
|
|
isc_set_pipeline(isc, pipeline);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
/*
|
2019-04-15 22:13:49 +08:00
|
|
|
* The current implemented histogram is available for RAW R, B, GB, GR
|
2019-03-29 15:38:28 +08:00
|
|
|
* channels. We need to check if sensor is outputting RAW BAYER
|
|
|
|
*/
|
|
|
|
if (isc->ctrls.awb &&
|
|
|
|
ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
|
|
|
|
isc_set_histogram(isc, true);
|
|
|
|
else
|
|
|
|
isc_set_histogram(isc, false);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
/* Update profile */
|
|
|
|
return isc_update_profile(isc);
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = vb2_get_drv_priv(vq);
|
|
|
|
struct regmap *regmap = isc->regmap;
|
|
|
|
struct isc_buffer *buf;
|
|
|
|
unsigned long flags;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Enable stream on the sub device */
|
|
|
|
ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1);
|
|
|
|
if (ret && ret != -ENOIOCTLCMD) {
|
2019-03-29 15:38:28 +08:00
|
|
|
v4l2_err(&isc->v4l2_dev, "stream on failed in subdev %d\n",
|
|
|
|
ret);
|
2016-08-17 14:05:27 +08:00
|
|
|
goto err_start_stream;
|
|
|
|
}
|
|
|
|
|
2021-04-23 22:47:42 +08:00
|
|
|
ret = pm_runtime_resume_and_get(isc->dev);
|
|
|
|
if (ret < 0) {
|
|
|
|
v4l2_err(&isc->v4l2_dev, "RPM resume failed in subdev %d\n",
|
|
|
|
ret);
|
|
|
|
goto err_pm_get;
|
|
|
|
}
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
ret = isc_configure(isc);
|
|
|
|
if (unlikely(ret))
|
|
|
|
goto err_configure;
|
|
|
|
|
|
|
|
/* Enable DMA interrupt */
|
|
|
|
regmap_write(regmap, ISC_INTEN, ISC_INT_DDONE);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&isc->dma_queue_lock, flags);
|
|
|
|
|
|
|
|
isc->sequence = 0;
|
|
|
|
isc->stop = false;
|
|
|
|
reinit_completion(&isc->comp);
|
|
|
|
|
|
|
|
isc->cur_frm = list_first_entry(&isc->dma_queue,
|
|
|
|
struct isc_buffer, list);
|
|
|
|
list_del(&isc->cur_frm->list);
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
isc_start_dma(isc);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
|
|
|
|
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
/* if we streaming from RAW, we can do one-shot white balance adj */
|
|
|
|
if (ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
|
|
|
|
v4l2_ctrl_activate(isc->do_wb_ctrl, true);
|
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_configure:
|
|
|
|
pm_runtime_put_sync(isc->dev);
|
2021-04-23 22:47:42 +08:00
|
|
|
err_pm_get:
|
2016-08-17 14:05:27 +08:00
|
|
|
v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0);
|
|
|
|
|
|
|
|
err_start_stream:
|
|
|
|
spin_lock_irqsave(&isc->dma_queue_lock, flags);
|
|
|
|
list_for_each_entry(buf, &isc->dma_queue, list)
|
|
|
|
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
|
|
|
|
INIT_LIST_HEAD(&isc->dma_queue);
|
|
|
|
spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void isc_stop_streaming(struct vb2_queue *vq)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = vb2_get_drv_priv(vq);
|
|
|
|
unsigned long flags;
|
|
|
|
struct isc_buffer *buf;
|
|
|
|
int ret;
|
|
|
|
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
v4l2_ctrl_activate(isc->do_wb_ctrl, false);
|
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
isc->stop = true;
|
|
|
|
|
|
|
|
/* Wait until the end of the current frame */
|
|
|
|
if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ))
|
|
|
|
v4l2_err(&isc->v4l2_dev,
|
|
|
|
"Timeout waiting for end of the capture\n");
|
|
|
|
|
|
|
|
/* Disable DMA interrupt */
|
|
|
|
regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE);
|
|
|
|
|
|
|
|
pm_runtime_put_sync(isc->dev);
|
|
|
|
|
|
|
|
/* Disable stream on the sub device */
|
|
|
|
ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0);
|
|
|
|
if (ret && ret != -ENOIOCTLCMD)
|
|
|
|
v4l2_err(&isc->v4l2_dev, "stream off failed in subdev\n");
|
|
|
|
|
|
|
|
/* Release all active buffers */
|
|
|
|
spin_lock_irqsave(&isc->dma_queue_lock, flags);
|
|
|
|
if (unlikely(isc->cur_frm)) {
|
|
|
|
vb2_buffer_done(&isc->cur_frm->vb.vb2_buf,
|
|
|
|
VB2_BUF_STATE_ERROR);
|
|
|
|
isc->cur_frm = NULL;
|
|
|
|
}
|
|
|
|
list_for_each_entry(buf, &isc->dma_queue, list)
|
|
|
|
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
|
|
|
|
INIT_LIST_HEAD(&isc->dma_queue);
|
|
|
|
spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void isc_buffer_queue(struct vb2_buffer *vb)
|
|
|
|
{
|
|
|
|
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
|
|
|
struct isc_buffer *buf = container_of(vbuf, struct isc_buffer, vb);
|
|
|
|
struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue);
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&isc->dma_queue_lock, flags);
|
2016-09-28 13:28:57 +08:00
|
|
|
if (!isc->cur_frm && list_empty(&isc->dma_queue) &&
|
|
|
|
vb2_is_streaming(vb->vb2_queue)) {
|
|
|
|
isc->cur_frm = buf;
|
2017-01-24 16:05:57 +08:00
|
|
|
isc_start_dma(isc);
|
2016-09-28 13:28:57 +08:00
|
|
|
} else
|
|
|
|
list_add_tail(&buf->list, &isc->dma_queue);
|
2016-08-17 14:05:27 +08:00
|
|
|
spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
|
|
|
|
}
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
static struct isc_format *find_format_by_fourcc(struct isc_device *isc,
|
|
|
|
unsigned int fourcc)
|
|
|
|
{
|
|
|
|
unsigned int num_formats = isc->num_user_formats;
|
|
|
|
struct isc_format *fmt;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < num_formats; i++) {
|
|
|
|
fmt = isc->user_formats[i];
|
|
|
|
if (fmt->fourcc == fourcc)
|
|
|
|
return fmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-07-07 04:30:16 +08:00
|
|
|
static const struct vb2_ops isc_vb2_ops = {
|
2016-08-17 14:05:27 +08:00
|
|
|
.queue_setup = isc_queue_setup,
|
|
|
|
.wait_prepare = vb2_ops_wait_prepare,
|
|
|
|
.wait_finish = vb2_ops_wait_finish,
|
|
|
|
.buf_prepare = isc_buffer_prepare,
|
|
|
|
.start_streaming = isc_start_streaming,
|
|
|
|
.stop_streaming = isc_stop_streaming,
|
|
|
|
.buf_queue = isc_buffer_queue,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int isc_querycap(struct file *file, void *priv,
|
|
|
|
struct v4l2_capability *cap)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = video_drvdata(file);
|
|
|
|
|
2021-04-13 18:57:00 +08:00
|
|
|
strscpy(cap->driver, "microchip-isc", sizeof(cap->driver));
|
2018-09-11 04:20:42 +08:00
|
|
|
strscpy(cap->card, "Atmel Image Sensor Controller", sizeof(cap->card));
|
2016-08-17 14:05:27 +08:00
|
|
|
snprintf(cap->bus_info, sizeof(cap->bus_info),
|
|
|
|
"platform:%s", isc->v4l2_dev.name);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_enum_fmt_vid_cap(struct file *file, void *priv,
|
|
|
|
struct v4l2_fmtdesc *f)
|
|
|
|
{
|
2021-04-13 18:57:22 +08:00
|
|
|
struct isc_device *isc = video_drvdata(file);
|
2016-08-17 14:05:27 +08:00
|
|
|
u32 index = f->index;
|
2019-03-29 15:38:28 +08:00
|
|
|
u32 i, supported_index;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2021-04-13 18:57:22 +08:00
|
|
|
if (index < isc->controller_formats_size) {
|
|
|
|
f->pixelformat = isc->controller_formats[index].fourcc;
|
2019-03-29 15:38:28 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2021-04-13 18:57:22 +08:00
|
|
|
index -= isc->controller_formats_size;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
supported_index = 0;
|
|
|
|
|
2021-04-13 18:57:22 +08:00
|
|
|
for (i = 0; i < isc->formats_list_size; i++) {
|
|
|
|
if (!ISC_IS_FORMAT_RAW(isc->formats_list[i].mbus_code) ||
|
|
|
|
!isc->formats_list[i].sd_support)
|
2019-03-29 15:38:28 +08:00
|
|
|
continue;
|
|
|
|
if (supported_index == index) {
|
2021-04-13 18:57:22 +08:00
|
|
|
f->pixelformat = isc->formats_list[i].fourcc;
|
2019-03-29 15:38:28 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
supported_index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_g_fmt_vid_cap(struct file *file, void *priv,
|
|
|
|
struct v4l2_format *fmt)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = video_drvdata(file);
|
|
|
|
|
|
|
|
*fmt = isc->fmt;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
/*
|
|
|
|
* Checks the current configured format, if ISC can output it,
|
|
|
|
* considering which type of format the ISC receives from the sensor
|
|
|
|
*/
|
|
|
|
static int isc_try_validate_formats(struct isc_device *isc)
|
2016-08-17 14:05:27 +08:00
|
|
|
{
|
2019-03-29 15:38:28 +08:00
|
|
|
int ret;
|
|
|
|
bool bayer = false, yuv = false, rgb = false, grey = false;
|
|
|
|
|
|
|
|
/* all formats supported by the RLP module are OK */
|
|
|
|
switch (isc->try_config.fourcc) {
|
|
|
|
case V4L2_PIX_FMT_SBGGR8:
|
|
|
|
case V4L2_PIX_FMT_SGBRG8:
|
|
|
|
case V4L2_PIX_FMT_SGRBG8:
|
|
|
|
case V4L2_PIX_FMT_SRGGB8:
|
|
|
|
case V4L2_PIX_FMT_SBGGR10:
|
|
|
|
case V4L2_PIX_FMT_SGBRG10:
|
|
|
|
case V4L2_PIX_FMT_SGRBG10:
|
|
|
|
case V4L2_PIX_FMT_SRGGB10:
|
|
|
|
case V4L2_PIX_FMT_SBGGR12:
|
|
|
|
case V4L2_PIX_FMT_SGBRG12:
|
|
|
|
case V4L2_PIX_FMT_SGRBG12:
|
|
|
|
case V4L2_PIX_FMT_SRGGB12:
|
|
|
|
ret = 0;
|
|
|
|
bayer = true;
|
|
|
|
break;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
case V4L2_PIX_FMT_YUV420:
|
|
|
|
case V4L2_PIX_FMT_YUV422P:
|
|
|
|
case V4L2_PIX_FMT_YUYV:
|
2021-04-13 18:57:25 +08:00
|
|
|
case V4L2_PIX_FMT_UYVY:
|
|
|
|
case V4L2_PIX_FMT_VYUY:
|
2019-03-29 15:38:28 +08:00
|
|
|
ret = 0;
|
|
|
|
yuv = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case V4L2_PIX_FMT_RGB565:
|
2019-03-29 15:38:31 +08:00
|
|
|
case V4L2_PIX_FMT_ABGR32:
|
|
|
|
case V4L2_PIX_FMT_XBGR32:
|
2019-03-29 15:38:28 +08:00
|
|
|
case V4L2_PIX_FMT_ARGB444:
|
|
|
|
case V4L2_PIX_FMT_ARGB555:
|
|
|
|
ret = 0;
|
|
|
|
rgb = true;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_GREY:
|
2019-11-25 16:32:49 +08:00
|
|
|
case V4L2_PIX_FMT_Y10:
|
2021-04-13 18:57:25 +08:00
|
|
|
case V4L2_PIX_FMT_Y16:
|
2019-03-29 15:38:28 +08:00
|
|
|
ret = 0;
|
|
|
|
grey = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* any other different formats are not supported */
|
|
|
|
ret = -EINVAL;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
2019-03-29 15:38:28 +08:00
|
|
|
v4l2_dbg(1, debug, &isc->v4l2_dev,
|
|
|
|
"Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n",
|
|
|
|
rgb, yuv, grey, bayer);
|
|
|
|
|
2019-11-25 16:32:49 +08:00
|
|
|
/* we cannot output RAW if we do not receive RAW */
|
|
|
|
if ((bayer) && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* we cannot output GREY if we do not receive RAW/GREY */
|
|
|
|
if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) &&
|
|
|
|
!ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Configures the RLP and DMA modules, depending on the output format
|
|
|
|
* configured for the ISC.
|
2019-11-25 16:32:48 +08:00
|
|
|
* If direct_dump == true, just dump raw data 8/16 bits depending on format.
|
2019-03-29 15:38:28 +08:00
|
|
|
*/
|
|
|
|
static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump)
|
|
|
|
{
|
2021-04-13 18:57:25 +08:00
|
|
|
isc->try_config.rlp_cfg_mode = 0;
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
switch (isc->try_config.fourcc) {
|
|
|
|
case V4L2_PIX_FMT_SBGGR8:
|
|
|
|
case V4L2_PIX_FMT_SGBRG8:
|
|
|
|
case V4L2_PIX_FMT_SGRBG8:
|
|
|
|
case V4L2_PIX_FMT_SRGGB8:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 8;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_SBGGR10:
|
|
|
|
case V4L2_PIX_FMT_SGBRG10:
|
|
|
|
case V4L2_PIX_FMT_SGRBG10:
|
|
|
|
case V4L2_PIX_FMT_SRGGB10:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT10;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 16;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_SBGGR12:
|
|
|
|
case V4L2_PIX_FMT_SGBRG12:
|
|
|
|
case V4L2_PIX_FMT_SGRBG12:
|
|
|
|
case V4L2_PIX_FMT_SRGGB12:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT12;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 16;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_RGB565:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_RGB565;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 16;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_ARGB444:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB444;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 16;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_ARGB555:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB555;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 16;
|
|
|
|
break;
|
2019-03-29 15:38:31 +08:00
|
|
|
case V4L2_PIX_FMT_ABGR32:
|
|
|
|
case V4L2_PIX_FMT_XBGR32:
|
2019-03-29 15:38:28 +08:00
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB32;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 32;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_YUV420:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC420P;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR;
|
|
|
|
isc->try_config.bpp = 12;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_YUV422P:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC422P;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR;
|
|
|
|
isc->try_config.bpp = 16;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_YUYV:
|
2021-04-13 18:57:25 +08:00
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_YUYV;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 16;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_UYVY:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_UYVY;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 16;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_VYUY:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_VYUY;
|
2019-03-29 15:38:28 +08:00
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 16;
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_GREY:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY8;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 8;
|
|
|
|
break;
|
2021-04-13 18:57:25 +08:00
|
|
|
case V4L2_PIX_FMT_Y16:
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10 | ISC_RLP_CFG_LSH;
|
|
|
|
fallthrough;
|
2019-11-25 16:32:49 +08:00
|
|
|
case V4L2_PIX_FMT_Y10:
|
2021-04-13 18:57:25 +08:00
|
|
|
isc->try_config.rlp_cfg_mode |= ISC_RLP_CFG_MODE_DATY10;
|
2019-11-25 16:32:49 +08:00
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
isc->try_config.bpp = 16;
|
|
|
|
break;
|
2019-03-29 15:38:28 +08:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2019-11-25 16:32:48 +08:00
|
|
|
|
|
|
|
if (direct_dump) {
|
|
|
|
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8;
|
|
|
|
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
|
|
|
|
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Configuring pipeline modules, depending on which format the ISC outputs
|
|
|
|
* and considering which format it has as input from the sensor.
|
|
|
|
*/
|
|
|
|
static int isc_try_configure_pipeline(struct isc_device *isc)
|
|
|
|
{
|
|
|
|
switch (isc->try_config.fourcc) {
|
|
|
|
case V4L2_PIX_FMT_RGB565:
|
|
|
|
case V4L2_PIX_FMT_ARGB555:
|
|
|
|
case V4L2_PIX_FMT_ARGB444:
|
2019-03-29 15:38:31 +08:00
|
|
|
case V4L2_PIX_FMT_ABGR32:
|
|
|
|
case V4L2_PIX_FMT_XBGR32:
|
2019-03-29 15:38:28 +08:00
|
|
|
/* if sensor format is RAW, we convert inside ISC */
|
|
|
|
if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
|
|
|
|
isc->try_config.bits_pipeline = CFA_ENABLE |
|
2021-04-13 18:57:25 +08:00
|
|
|
WB_ENABLE | GAM_ENABLES | DPC_BLCENABLE |
|
|
|
|
CC_ENABLE;
|
2019-03-29 15:38:28 +08:00
|
|
|
} else {
|
|
|
|
isc->try_config.bits_pipeline = 0x0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_YUV420:
|
|
|
|
/* if sensor format is RAW, we convert inside ISC */
|
|
|
|
if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
|
|
|
|
isc->try_config.bits_pipeline = CFA_ENABLE |
|
2021-04-13 18:57:25 +08:00
|
|
|
CSC_ENABLE | GAM_ENABLES | WB_ENABLE |
|
|
|
|
SUB420_ENABLE | SUB422_ENABLE | CBC_ENABLE |
|
|
|
|
DPC_BLCENABLE;
|
2019-03-29 15:38:28 +08:00
|
|
|
} else {
|
|
|
|
isc->try_config.bits_pipeline = 0x0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_YUV422P:
|
|
|
|
/* if sensor format is RAW, we convert inside ISC */
|
|
|
|
if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
|
|
|
|
isc->try_config.bits_pipeline = CFA_ENABLE |
|
|
|
|
CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
|
2021-04-13 18:57:25 +08:00
|
|
|
SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE;
|
2019-03-29 15:38:28 +08:00
|
|
|
} else {
|
|
|
|
isc->try_config.bits_pipeline = 0x0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_YUYV:
|
2021-04-13 18:57:25 +08:00
|
|
|
case V4L2_PIX_FMT_UYVY:
|
|
|
|
case V4L2_PIX_FMT_VYUY:
|
2019-03-29 15:38:28 +08:00
|
|
|
/* if sensor format is RAW, we convert inside ISC */
|
|
|
|
if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
|
|
|
|
isc->try_config.bits_pipeline = CFA_ENABLE |
|
|
|
|
CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
|
2021-04-13 18:57:25 +08:00
|
|
|
SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE;
|
2019-03-29 15:38:28 +08:00
|
|
|
} else {
|
|
|
|
isc->try_config.bits_pipeline = 0x0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case V4L2_PIX_FMT_GREY:
|
2021-04-13 18:57:25 +08:00
|
|
|
case V4L2_PIX_FMT_Y16:
|
2019-03-29 15:38:28 +08:00
|
|
|
/* if sensor format is RAW, we convert inside ISC */
|
2021-04-13 18:57:25 +08:00
|
|
|
if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
|
2019-03-29 15:38:28 +08:00
|
|
|
isc->try_config.bits_pipeline = CFA_ENABLE |
|
|
|
|
CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
|
2021-04-13 18:57:25 +08:00
|
|
|
CBC_ENABLE | DPC_BLCENABLE;
|
2019-03-29 15:38:28 +08:00
|
|
|
} else {
|
|
|
|
isc->try_config.bits_pipeline = 0x0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2021-04-13 18:57:25 +08:00
|
|
|
if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code))
|
|
|
|
isc->try_config.bits_pipeline = WB_ENABLE | DPC_BLCENABLE;
|
|
|
|
else
|
|
|
|
isc->try_config.bits_pipeline = 0x0;
|
2019-03-29 15:38:28 +08:00
|
|
|
}
|
2021-04-13 18:57:23 +08:00
|
|
|
|
|
|
|
/* Tune the pipeline to product specific */
|
|
|
|
isc->adapt_pipeline(isc);
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
return 0;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
2019-11-25 16:32:54 +08:00
|
|
|
static void isc_try_fse(struct isc_device *isc,
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
struct v4l2_subdev_state *sd_state)
|
2019-11-25 16:32:54 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct v4l2_subdev_frame_size_enum fse = {};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we do not know yet which format the subdev is using, we cannot
|
|
|
|
* do anything.
|
|
|
|
*/
|
|
|
|
if (!isc->try_config.sd_format)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fse.code = isc->try_config.sd_format->mbus_code;
|
|
|
|
fse.which = V4L2_SUBDEV_FORMAT_TRY;
|
|
|
|
|
|
|
|
ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
sd_state, &fse);
|
2019-11-25 16:32:54 +08:00
|
|
|
/*
|
|
|
|
* Attempt to obtain format size from subdev. If not available,
|
|
|
|
* just use the maximum ISC can receive.
|
|
|
|
*/
|
|
|
|
if (ret) {
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
sd_state->pads->try_crop.width = isc->max_width;
|
|
|
|
sd_state->pads->try_crop.height = isc->max_height;
|
2019-11-25 16:32:54 +08:00
|
|
|
} else {
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
sd_state->pads->try_crop.width = fse.max_width;
|
|
|
|
sd_state->pads->try_crop.height = fse.max_height;
|
2019-11-25 16:32:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
|
2019-03-29 15:38:28 +08:00
|
|
|
u32 *code)
|
2016-08-17 14:05:27 +08:00
|
|
|
{
|
2019-03-29 15:38:28 +08:00
|
|
|
int i;
|
|
|
|
struct isc_format *sd_fmt = NULL, *direct_fmt = NULL;
|
2016-08-17 14:05:27 +08:00
|
|
|
struct v4l2_pix_format *pixfmt = &f->fmt.pix;
|
2019-11-25 16:32:51 +08:00
|
|
|
struct v4l2_subdev_pad_config pad_cfg = {};
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
struct v4l2_subdev_state pad_state = {
|
|
|
|
.pads = &pad_cfg
|
|
|
|
};
|
2016-08-17 14:05:27 +08:00
|
|
|
struct v4l2_subdev_format format = {
|
|
|
|
.which = V4L2_SUBDEV_FORMAT_TRY,
|
|
|
|
};
|
2017-01-24 16:05:57 +08:00
|
|
|
u32 mbus_code;
|
2016-08-17 14:05:27 +08:00
|
|
|
int ret;
|
2019-03-29 15:38:28 +08:00
|
|
|
bool rlp_dma_direct_dump = false;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
/* Step 1: find a RAW format that is supported */
|
|
|
|
for (i = 0; i < isc->num_user_formats; i++) {
|
|
|
|
if (ISC_IS_FORMAT_RAW(isc->user_formats[i]->mbus_code)) {
|
|
|
|
sd_fmt = isc->user_formats[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Step 2: We can continue with this RAW format, or we can look
|
|
|
|
* for better: maybe sensor supports directly what we need.
|
|
|
|
*/
|
|
|
|
direct_fmt = find_format_by_fourcc(isc, pixfmt->pixelformat);
|
|
|
|
|
|
|
|
/* Step 3: We have both. We decide given the module parameter which
|
|
|
|
* one to use.
|
|
|
|
*/
|
|
|
|
if (direct_fmt && sd_fmt && sensor_preferred)
|
|
|
|
sd_fmt = direct_fmt;
|
|
|
|
|
|
|
|
/* Step 4: we do not have RAW but we have a direct format. Use it. */
|
|
|
|
if (direct_fmt && !sd_fmt)
|
|
|
|
sd_fmt = direct_fmt;
|
|
|
|
|
|
|
|
/* Step 5: if we are using a direct format, we need to package
|
|
|
|
* everything as 8 bit data and just dump it
|
|
|
|
*/
|
|
|
|
if (sd_fmt == direct_fmt)
|
|
|
|
rlp_dma_direct_dump = true;
|
|
|
|
|
|
|
|
/* Step 6: We have no format. This can happen if the userspace
|
|
|
|
* requests some weird/invalid format.
|
|
|
|
* In this case, default to whatever we have
|
|
|
|
*/
|
|
|
|
if (!sd_fmt && !direct_fmt) {
|
|
|
|
sd_fmt = isc->user_formats[isc->num_user_formats - 1];
|
|
|
|
v4l2_dbg(1, debug, &isc->v4l2_dev,
|
|
|
|
"Sensor not supporting %.4s, using %.4s\n",
|
|
|
|
(char *)&pixfmt->pixelformat, (char *)&sd_fmt->fourcc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sd_fmt) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto isc_try_fmt_err;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
/* Step 7: Print out what we decided for debugging */
|
|
|
|
v4l2_dbg(1, debug, &isc->v4l2_dev,
|
|
|
|
"Preferring to have sensor using format %.4s\n",
|
|
|
|
(char *)&sd_fmt->fourcc);
|
|
|
|
|
|
|
|
/* Step 8: at this moment we decided which format the subdev will use */
|
|
|
|
isc->try_config.sd_format = sd_fmt;
|
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
/* Limit to Atmel ISC hardware capabilities */
|
2021-04-13 18:57:02 +08:00
|
|
|
if (pixfmt->width > isc->max_width)
|
|
|
|
pixfmt->width = isc->max_width;
|
|
|
|
if (pixfmt->height > isc->max_height)
|
|
|
|
pixfmt->height = isc->max_height;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
/*
|
|
|
|
* The mbus format is the one the subdev outputs.
|
|
|
|
* The pixels will be transferred in this format Sensor -> ISC
|
|
|
|
*/
|
|
|
|
mbus_code = sd_fmt->mbus_code;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate formats. If the required format is not OK, default to raw.
|
|
|
|
*/
|
|
|
|
|
|
|
|
isc->try_config.fourcc = pixfmt->pixelformat;
|
|
|
|
|
|
|
|
if (isc_try_validate_formats(isc)) {
|
|
|
|
pixfmt->pixelformat = isc->try_config.fourcc = sd_fmt->fourcc;
|
|
|
|
/* Re-try to validate the new format */
|
|
|
|
ret = isc_try_validate_formats(isc);
|
|
|
|
if (ret)
|
|
|
|
goto isc_try_fmt_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = isc_try_configure_rlp_dma(isc, rlp_dma_direct_dump);
|
|
|
|
if (ret)
|
|
|
|
goto isc_try_fmt_err;
|
|
|
|
|
|
|
|
ret = isc_try_configure_pipeline(isc);
|
|
|
|
if (ret)
|
|
|
|
goto isc_try_fmt_err;
|
2017-01-24 16:05:57 +08:00
|
|
|
|
2019-11-25 16:32:54 +08:00
|
|
|
/* Obtain frame sizes if possible to have crop requirements ready */
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
isc_try_fse(isc, &pad_state);
|
2019-11-25 16:32:54 +08:00
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code);
|
2016-08-17 14:05:27 +08:00
|
|
|
ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt,
|
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 22:55:58 +08:00
|
|
|
&pad_state, &format);
|
2016-08-17 14:05:27 +08:00
|
|
|
if (ret < 0)
|
2019-04-15 22:13:56 +08:00
|
|
|
goto isc_try_fmt_subdev_err;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
v4l2_fill_pix_format(pixfmt, &format.format);
|
|
|
|
|
2021-04-13 18:57:01 +08:00
|
|
|
/* Limit to Atmel ISC hardware capabilities */
|
2021-04-13 18:57:02 +08:00
|
|
|
if (pixfmt->width > isc->max_width)
|
|
|
|
pixfmt->width = isc->max_width;
|
|
|
|
if (pixfmt->height > isc->max_height)
|
|
|
|
pixfmt->height = isc->max_height;
|
2021-04-13 18:57:01 +08:00
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
pixfmt->field = V4L2_FIELD_NONE;
|
2019-03-29 15:38:28 +08:00
|
|
|
pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp) >> 3;
|
2016-08-17 14:05:27 +08:00
|
|
|
pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
if (code)
|
|
|
|
*code = mbus_code;
|
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
return 0;
|
2019-03-29 15:38:28 +08:00
|
|
|
|
|
|
|
isc_try_fmt_err:
|
|
|
|
v4l2_err(&isc->v4l2_dev, "Could not find any possible format for a working pipeline\n");
|
2019-04-15 22:13:56 +08:00
|
|
|
isc_try_fmt_subdev_err:
|
2019-03-29 15:38:28 +08:00
|
|
|
memset(&isc->try_config, 0, sizeof(isc->try_config));
|
|
|
|
|
|
|
|
return ret;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
|
|
|
|
{
|
|
|
|
struct v4l2_subdev_format format = {
|
|
|
|
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
|
|
|
};
|
2019-03-29 15:38:28 +08:00
|
|
|
u32 mbus_code = 0;
|
2016-08-17 14:05:27 +08:00
|
|
|
int ret;
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
ret = isc_try_fmt(isc, f, &mbus_code);
|
2016-08-17 14:05:27 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code);
|
2016-08-17 14:05:27 +08:00
|
|
|
ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
|
|
|
|
set_fmt, NULL, &format);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2021-04-13 18:57:01 +08:00
|
|
|
/* Limit to Atmel ISC hardware capabilities */
|
2021-04-13 18:57:02 +08:00
|
|
|
if (f->fmt.pix.width > isc->max_width)
|
|
|
|
f->fmt.pix.width = isc->max_width;
|
|
|
|
if (f->fmt.pix.height > isc->max_height)
|
|
|
|
f->fmt.pix.height = isc->max_height;
|
2021-04-13 18:57:01 +08:00
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
isc->fmt = *f;
|
2019-04-15 22:13:49 +08:00
|
|
|
|
|
|
|
if (isc->try_config.sd_format && isc->config.sd_format &&
|
|
|
|
isc->try_config.sd_format != isc->config.sd_format) {
|
|
|
|
isc->ctrls.hist_stat = HIST_INIT;
|
|
|
|
isc_reset_awb_ctrls(isc);
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
isc_update_v4l2_ctrls(isc);
|
2019-04-15 22:13:49 +08:00
|
|
|
}
|
2019-03-29 15:38:28 +08:00
|
|
|
/* make the try configuration active */
|
|
|
|
isc->config = isc->try_config;
|
|
|
|
|
|
|
|
v4l2_dbg(1, debug, &isc->v4l2_dev, "New ISC configuration in place\n");
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_s_fmt_vid_cap(struct file *file, void *priv,
|
|
|
|
struct v4l2_format *f)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = video_drvdata(file);
|
|
|
|
|
|
|
|
if (vb2_is_streaming(&isc->vb2_vidq))
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
return isc_set_fmt(isc, f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_try_fmt_vid_cap(struct file *file, void *priv,
|
|
|
|
struct v4l2_format *f)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = video_drvdata(file);
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
return isc_try_fmt(isc, f, NULL);
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_enum_input(struct file *file, void *priv,
|
|
|
|
struct v4l2_input *inp)
|
|
|
|
{
|
|
|
|
if (inp->index != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
inp->type = V4L2_INPUT_TYPE_CAMERA;
|
|
|
|
inp->std = 0;
|
2018-09-11 04:20:42 +08:00
|
|
|
strscpy(inp->name, "Camera", sizeof(inp->name));
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_g_input(struct file *file, void *priv, unsigned int *i)
|
|
|
|
{
|
|
|
|
*i = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_s_input(struct file *file, void *priv, unsigned int i)
|
|
|
|
{
|
|
|
|
if (i > 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = video_drvdata(file);
|
|
|
|
|
2018-01-22 17:00:45 +08:00
|
|
|
return v4l2_g_parm_cap(video_devdata(file), isc->current_subdev->sd, a);
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = video_drvdata(file);
|
|
|
|
|
2018-01-22 17:00:45 +08:00
|
|
|
return v4l2_s_parm_cap(video_devdata(file), isc->current_subdev->sd, a);
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_enum_framesizes(struct file *file, void *fh,
|
|
|
|
struct v4l2_frmsizeenum *fsize)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = video_drvdata(file);
|
|
|
|
struct v4l2_subdev_frame_size_enum fse = {
|
2019-11-25 16:32:53 +08:00
|
|
|
.code = isc->config.sd_format->mbus_code,
|
2016-08-17 14:05:27 +08:00
|
|
|
.index = fsize->index,
|
|
|
|
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
|
|
|
};
|
2019-03-29 15:38:28 +08:00
|
|
|
int ret = -EINVAL;
|
|
|
|
int i;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
for (i = 0; i < isc->num_user_formats; i++)
|
|
|
|
if (isc->user_formats[i]->fourcc == fsize->pixel_format)
|
|
|
|
ret = 0;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2021-04-13 18:57:22 +08:00
|
|
|
for (i = 0; i < isc->controller_formats_size; i++)
|
|
|
|
if (isc->controller_formats[i].fourcc == fsize->pixel_format)
|
2019-03-29 15:38:28 +08:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
|
|
|
|
NULL, &fse);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
|
|
|
|
fsize->discrete.width = fse.max_width;
|
|
|
|
fsize->discrete.height = fse.max_height;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_enum_frameintervals(struct file *file, void *fh,
|
|
|
|
struct v4l2_frmivalenum *fival)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = video_drvdata(file);
|
|
|
|
struct v4l2_subdev_frame_interval_enum fie = {
|
2019-11-25 16:32:53 +08:00
|
|
|
.code = isc->config.sd_format->mbus_code,
|
2016-08-17 14:05:27 +08:00
|
|
|
.index = fival->index,
|
|
|
|
.width = fival->width,
|
|
|
|
.height = fival->height,
|
|
|
|
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
|
|
|
};
|
2019-03-29 15:38:28 +08:00
|
|
|
int ret = -EINVAL;
|
2019-06-12 20:00:35 +08:00
|
|
|
unsigned int i;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
for (i = 0; i < isc->num_user_formats; i++)
|
|
|
|
if (isc->user_formats[i]->fourcc == fival->pixel_format)
|
|
|
|
ret = 0;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2021-04-13 18:57:22 +08:00
|
|
|
for (i = 0; i < isc->controller_formats_size; i++)
|
|
|
|
if (isc->controller_formats[i].fourcc == fival->pixel_format)
|
2019-03-29 15:38:28 +08:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
|
|
|
|
enum_frame_interval, NULL, &fie);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
|
|
|
|
fival->discrete = fie.interval;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct v4l2_ioctl_ops isc_ioctl_ops = {
|
|
|
|
.vidioc_querycap = isc_querycap,
|
|
|
|
.vidioc_enum_fmt_vid_cap = isc_enum_fmt_vid_cap,
|
|
|
|
.vidioc_g_fmt_vid_cap = isc_g_fmt_vid_cap,
|
|
|
|
.vidioc_s_fmt_vid_cap = isc_s_fmt_vid_cap,
|
|
|
|
.vidioc_try_fmt_vid_cap = isc_try_fmt_vid_cap,
|
|
|
|
|
|
|
|
.vidioc_enum_input = isc_enum_input,
|
|
|
|
.vidioc_g_input = isc_g_input,
|
|
|
|
.vidioc_s_input = isc_s_input,
|
|
|
|
|
|
|
|
.vidioc_reqbufs = vb2_ioctl_reqbufs,
|
|
|
|
.vidioc_querybuf = vb2_ioctl_querybuf,
|
|
|
|
.vidioc_qbuf = vb2_ioctl_qbuf,
|
|
|
|
.vidioc_expbuf = vb2_ioctl_expbuf,
|
|
|
|
.vidioc_dqbuf = vb2_ioctl_dqbuf,
|
|
|
|
.vidioc_create_bufs = vb2_ioctl_create_bufs,
|
|
|
|
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
|
|
|
|
.vidioc_streamon = vb2_ioctl_streamon,
|
|
|
|
.vidioc_streamoff = vb2_ioctl_streamoff,
|
|
|
|
|
|
|
|
.vidioc_g_parm = isc_g_parm,
|
|
|
|
.vidioc_s_parm = isc_s_parm,
|
|
|
|
.vidioc_enum_framesizes = isc_enum_framesizes,
|
|
|
|
.vidioc_enum_frameintervals = isc_enum_frameintervals,
|
2017-01-24 16:05:57 +08:00
|
|
|
|
|
|
|
.vidioc_log_status = v4l2_ctrl_log_status,
|
|
|
|
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
|
|
|
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
2016-08-17 14:05:27 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int isc_open(struct file *file)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = video_drvdata(file);
|
|
|
|
struct v4l2_subdev *sd = isc->current_subdev->sd;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (mutex_lock_interruptible(&isc->lock))
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
ret = v4l2_fh_open(file);
|
|
|
|
if (ret < 0)
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
if (!v4l2_fh_is_singular_file(file))
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
ret = v4l2_subdev_call(sd, core, s_power, 1);
|
2016-09-12 15:47:24 +08:00
|
|
|
if (ret < 0 && ret != -ENOIOCTLCMD) {
|
2016-08-17 14:05:27 +08:00
|
|
|
v4l2_fh_release(file);
|
2016-09-12 15:47:24 +08:00
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = isc_set_fmt(isc, &isc->fmt);
|
|
|
|
if (ret) {
|
|
|
|
v4l2_subdev_call(sd, core, s_power, 0);
|
|
|
|
v4l2_fh_release(file);
|
|
|
|
}
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
unlock:
|
|
|
|
mutex_unlock(&isc->lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_release(struct file *file)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = video_drvdata(file);
|
|
|
|
struct v4l2_subdev *sd = isc->current_subdev->sd;
|
|
|
|
bool fh_singular;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(&isc->lock);
|
|
|
|
|
|
|
|
fh_singular = v4l2_fh_is_singular_file(file);
|
|
|
|
|
|
|
|
ret = _vb2_fop_release(file, NULL);
|
|
|
|
|
|
|
|
if (fh_singular)
|
|
|
|
v4l2_subdev_call(sd, core, s_power, 0);
|
|
|
|
|
|
|
|
mutex_unlock(&isc->lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct v4l2_file_operations isc_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = isc_open,
|
|
|
|
.release = isc_release,
|
|
|
|
.unlocked_ioctl = video_ioctl2,
|
|
|
|
.read = vb2_fop_read,
|
|
|
|
.mmap = vb2_fop_mmap,
|
|
|
|
.poll = vb2_fop_poll,
|
|
|
|
};
|
|
|
|
|
2019-06-12 20:00:31 +08:00
|
|
|
irqreturn_t isc_interrupt(int irq, void *dev_id)
|
2016-08-17 14:05:27 +08:00
|
|
|
{
|
|
|
|
struct isc_device *isc = (struct isc_device *)dev_id;
|
|
|
|
struct regmap *regmap = isc->regmap;
|
|
|
|
u32 isc_intsr, isc_intmask, pending;
|
|
|
|
irqreturn_t ret = IRQ_NONE;
|
|
|
|
|
|
|
|
regmap_read(regmap, ISC_INTSR, &isc_intsr);
|
|
|
|
regmap_read(regmap, ISC_INTMASK, &isc_intmask);
|
|
|
|
|
|
|
|
pending = isc_intsr & isc_intmask;
|
|
|
|
|
|
|
|
if (likely(pending & ISC_INT_DDONE)) {
|
2017-01-24 16:05:57 +08:00
|
|
|
spin_lock(&isc->dma_queue_lock);
|
2016-08-17 14:05:27 +08:00
|
|
|
if (isc->cur_frm) {
|
|
|
|
struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb;
|
|
|
|
struct vb2_buffer *vb = &vbuf->vb2_buf;
|
|
|
|
|
|
|
|
vb->timestamp = ktime_get_ns();
|
|
|
|
vbuf->sequence = isc->sequence++;
|
|
|
|
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
|
|
|
|
isc->cur_frm = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!list_empty(&isc->dma_queue) && !isc->stop) {
|
|
|
|
isc->cur_frm = list_first_entry(&isc->dma_queue,
|
|
|
|
struct isc_buffer, list);
|
|
|
|
list_del(&isc->cur_frm->list);
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
isc_start_dma(isc);
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (isc->stop)
|
|
|
|
complete(&isc->comp);
|
|
|
|
|
|
|
|
ret = IRQ_HANDLED;
|
2017-01-24 16:05:57 +08:00
|
|
|
spin_unlock(&isc->dma_queue_lock);
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
if (pending & ISC_INT_HISDONE) {
|
|
|
|
schedule_work(&isc->awb_work);
|
|
|
|
ret = IRQ_HANDLED;
|
|
|
|
}
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2021-07-05 20:57:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(isc_interrupt);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-04-15 22:13:49 +08:00
|
|
|
static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max)
|
2017-01-24 16:05:57 +08:00
|
|
|
{
|
|
|
|
struct regmap *regmap = isc->regmap;
|
|
|
|
struct isc_ctrls *ctrls = &isc->ctrls;
|
|
|
|
u32 *hist_count = &ctrls->hist_count[ctrls->hist_id];
|
|
|
|
u32 *hist_entry = &ctrls->hist_entry[0];
|
|
|
|
u32 i;
|
|
|
|
|
2019-04-15 22:13:49 +08:00
|
|
|
*min = 0;
|
|
|
|
*max = HIST_ENTRIES;
|
|
|
|
|
2021-04-13 18:57:14 +08:00
|
|
|
regmap_bulk_read(regmap, ISC_HIS_ENTRY + isc->offsets.his_entry,
|
|
|
|
hist_entry, HIST_ENTRIES);
|
2017-01-24 16:05:57 +08:00
|
|
|
|
|
|
|
*hist_count = 0;
|
2019-04-15 22:13:49 +08:00
|
|
|
/*
|
|
|
|
* we deliberately ignore the end of the histogram,
|
|
|
|
* the most white pixels
|
|
|
|
*/
|
|
|
|
for (i = 1; i < HIST_ENTRIES; i++) {
|
|
|
|
if (*hist_entry && !*min)
|
|
|
|
*min = i;
|
|
|
|
if (*hist_entry)
|
|
|
|
*max = i;
|
2017-01-24 16:05:57 +08:00
|
|
|
*hist_count += i * (*hist_entry++);
|
2019-04-15 22:13:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!*min)
|
|
|
|
*min = 1;
|
2017-01-24 16:05:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void isc_wb_update(struct isc_ctrls *ctrls)
|
|
|
|
{
|
|
|
|
u32 *hist_count = &ctrls->hist_count[0];
|
2019-04-15 22:13:49 +08:00
|
|
|
u32 c, offset[4];
|
|
|
|
u64 avg = 0;
|
|
|
|
/* We compute two gains, stretch gain and grey world gain */
|
|
|
|
u32 s_gain[4], gw_gain[4];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* According to Grey World, we need to set gains for R/B to normalize
|
|
|
|
* them towards the green channel.
|
|
|
|
* Thus we want to keep Green as fixed and adjust only Red/Blue
|
|
|
|
* Compute the average of the both green channels first
|
|
|
|
*/
|
|
|
|
avg = (u64)hist_count[ISC_HIS_CFG_MODE_GR] +
|
|
|
|
(u64)hist_count[ISC_HIS_CFG_MODE_GB];
|
|
|
|
avg >>= 1;
|
2017-01-24 16:05:57 +08:00
|
|
|
|
2019-04-15 22:13:49 +08:00
|
|
|
/* Green histogram is null, nothing to do */
|
|
|
|
if (!avg)
|
|
|
|
return;
|
2017-01-24 16:05:57 +08:00
|
|
|
|
2019-04-15 22:13:49 +08:00
|
|
|
for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) {
|
|
|
|
/*
|
|
|
|
* the color offset is the minimum value of the histogram.
|
|
|
|
* we stretch this color to the full range by substracting
|
|
|
|
* this value from the color component.
|
|
|
|
*/
|
|
|
|
offset[c] = ctrls->hist_minmax[c][HIST_MIN_INDEX];
|
|
|
|
/*
|
|
|
|
* The offset is always at least 1. If the offset is 1, we do
|
|
|
|
* not need to adjust it, so our result must be zero.
|
|
|
|
* the offset is computed in a histogram on 9 bits (0..512)
|
|
|
|
* but the offset in register is based on
|
|
|
|
* 12 bits pipeline (0..4096).
|
|
|
|
* we need to shift with the 3 bits that the histogram is
|
|
|
|
* ignoring
|
|
|
|
*/
|
|
|
|
ctrls->offset[c] = (offset[c] - 1) << 3;
|
|
|
|
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
/*
|
|
|
|
* the offset is then taken and converted to 2's complements,
|
|
|
|
* and must be negative, as we subtract this value from the
|
|
|
|
* color components
|
|
|
|
*/
|
|
|
|
ctrls->offset[c] = -ctrls->offset[c];
|
2019-04-15 22:13:49 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* the stretch gain is the total number of histogram bins
|
|
|
|
* divided by the actual range of color component (Max - Min)
|
|
|
|
* If we compute gain like this, the actual color component
|
|
|
|
* will be stretched to the full histogram.
|
|
|
|
* We need to shift 9 bits for precision, we have 9 bits for
|
|
|
|
* decimals
|
|
|
|
*/
|
|
|
|
s_gain[c] = (HIST_ENTRIES << 9) /
|
|
|
|
(ctrls->hist_minmax[c][HIST_MAX_INDEX] -
|
|
|
|
ctrls->hist_minmax[c][HIST_MIN_INDEX] + 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now we have to compute the gain w.r.t. the average.
|
|
|
|
* Add/lose gain to the component towards the average.
|
|
|
|
* If it happens that the component is zero, use the
|
|
|
|
* fixed point value : 1.0 gain.
|
|
|
|
*/
|
|
|
|
if (hist_count[c])
|
|
|
|
gw_gain[c] = div_u64(avg << 9, hist_count[c]);
|
|
|
|
else
|
|
|
|
gw_gain[c] = 1 << 9;
|
|
|
|
|
|
|
|
/* multiply both gains and adjust for decimals */
|
|
|
|
ctrls->gain[c] = s_gain[c] * gw_gain[c];
|
|
|
|
ctrls->gain[c] >>= 9;
|
|
|
|
}
|
2017-01-24 16:05:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void isc_awb_work(struct work_struct *w)
|
|
|
|
{
|
|
|
|
struct isc_device *isc =
|
|
|
|
container_of(w, struct isc_device, awb_work);
|
|
|
|
struct regmap *regmap = isc->regmap;
|
|
|
|
struct isc_ctrls *ctrls = &isc->ctrls;
|
|
|
|
u32 hist_id = ctrls->hist_id;
|
|
|
|
u32 baysel;
|
2019-04-15 22:13:49 +08:00
|
|
|
unsigned long flags;
|
|
|
|
u32 min, max;
|
2021-04-23 22:47:42 +08:00
|
|
|
int ret;
|
2019-04-15 22:13:49 +08:00
|
|
|
|
|
|
|
/* streaming is not active anymore */
|
|
|
|
if (isc->stop)
|
|
|
|
return;
|
2017-01-24 16:05:57 +08:00
|
|
|
|
|
|
|
if (ctrls->hist_stat != HIST_ENABLED)
|
|
|
|
return;
|
|
|
|
|
2019-04-15 22:13:49 +08:00
|
|
|
isc_hist_count(isc, &min, &max);
|
|
|
|
ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min;
|
|
|
|
ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max;
|
2017-01-24 16:05:57 +08:00
|
|
|
|
|
|
|
if (hist_id != ISC_HIS_CFG_MODE_B) {
|
|
|
|
hist_id++;
|
|
|
|
} else {
|
|
|
|
isc_wb_update(ctrls);
|
2019-04-15 22:13:49 +08:00
|
|
|
hist_id = ISC_HIS_CFG_MODE_GR;
|
2017-01-24 16:05:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ctrls->hist_id = hist_id;
|
2019-03-29 15:38:28 +08:00
|
|
|
baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT;
|
2017-01-24 16:05:57 +08:00
|
|
|
|
2021-04-23 22:47:42 +08:00
|
|
|
ret = pm_runtime_resume_and_get(isc->dev);
|
|
|
|
if (ret < 0)
|
|
|
|
return;
|
2017-01-24 16:05:57 +08:00
|
|
|
|
2019-04-15 22:13:49 +08:00
|
|
|
/*
|
|
|
|
* only update if we have all the required histograms and controls
|
|
|
|
* if awb has been disabled, we need to reset registers as well.
|
|
|
|
*/
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
if (hist_id == ISC_HIS_CFG_MODE_GR || ctrls->awb == ISC_WB_NONE) {
|
2019-04-15 22:13:49 +08:00
|
|
|
/*
|
|
|
|
* It may happen that DMA Done IRQ will trigger while we are
|
|
|
|
* updating white balance registers here.
|
|
|
|
* In that case, only parts of the controls have been updated.
|
|
|
|
* We can avoid that by locking the section.
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&isc->awb_lock, flags);
|
|
|
|
isc_update_awb_ctrls(isc);
|
|
|
|
spin_unlock_irqrestore(&isc->awb_lock, flags);
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* if we are doing just the one time white balance adjustment,
|
|
|
|
* we are basically done.
|
|
|
|
*/
|
|
|
|
if (ctrls->awb == ISC_WB_ONETIME) {
|
|
|
|
v4l2_info(&isc->v4l2_dev,
|
|
|
|
"Completed one time white-balance adjustment.\n");
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
/* update the v4l2 controls values */
|
|
|
|
isc_update_v4l2_ctrls(isc);
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
ctrls->awb = ISC_WB_NONE;
|
|
|
|
}
|
2019-04-15 22:13:49 +08:00
|
|
|
}
|
2021-04-13 18:57:11 +08:00
|
|
|
regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his,
|
|
|
|
hist_id | baysel | ISC_HIS_CFG_RAR);
|
2017-01-24 16:05:57 +08:00
|
|
|
isc_update_profile(isc);
|
2019-04-15 22:13:49 +08:00
|
|
|
/* if awb has been disabled, we don't need to start another histogram */
|
|
|
|
if (ctrls->awb)
|
|
|
|
regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
|
2017-01-24 16:05:57 +08:00
|
|
|
|
|
|
|
pm_runtime_put_sync(isc->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = container_of(ctrl->handler,
|
|
|
|
struct isc_device, ctrls.handler);
|
|
|
|
struct isc_ctrls *ctrls = &isc->ctrls;
|
|
|
|
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
|
|
|
|
return 0;
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
switch (ctrl->id) {
|
|
|
|
case V4L2_CID_BRIGHTNESS:
|
|
|
|
ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
|
|
|
|
break;
|
|
|
|
case V4L2_CID_CONTRAST:
|
|
|
|
ctrls->contrast = ctrl->val & ISC_CBC_CONTRAST_MASK;
|
|
|
|
break;
|
|
|
|
case V4L2_CID_GAMMA:
|
|
|
|
ctrls->gamma_index = ctrl->val;
|
|
|
|
break;
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct v4l2_ctrl_ops isc_ctrl_ops = {
|
|
|
|
.s_ctrl = isc_s_ctrl,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = container_of(ctrl->handler,
|
|
|
|
struct isc_device, ctrls.handler);
|
|
|
|
struct isc_ctrls *ctrls = &isc->ctrls;
|
|
|
|
|
|
|
|
if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (ctrl->id) {
|
2017-01-24 16:05:57 +08:00
|
|
|
case V4L2_CID_AUTO_WHITE_BALANCE:
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
if (ctrl->val == 1)
|
|
|
|
ctrls->awb = ISC_WB_AUTO;
|
|
|
|
else
|
|
|
|
ctrls->awb = ISC_WB_NONE;
|
|
|
|
|
|
|
|
/* we did not configure ISC yet */
|
|
|
|
if (!isc->config.sd_format)
|
|
|
|
break;
|
|
|
|
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
/* configure the controls with new values from v4l2 */
|
|
|
|
if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new)
|
|
|
|
ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val;
|
|
|
|
if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new)
|
|
|
|
ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val;
|
|
|
|
if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new)
|
|
|
|
ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val;
|
|
|
|
if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new)
|
|
|
|
ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val;
|
|
|
|
|
|
|
|
if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new)
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val;
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new)
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val;
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new)
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val;
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new)
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val;
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
isc_update_awb_ctrls(isc);
|
|
|
|
|
|
|
|
if (vb2_is_streaming(&isc->vb2_vidq)) {
|
|
|
|
/*
|
|
|
|
* If we are streaming, we can update profile to
|
|
|
|
* have the new settings in place.
|
|
|
|
*/
|
|
|
|
isc_update_profile(isc);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The auto cluster will activate automatically this
|
|
|
|
* control. This has to be deactivated when not
|
|
|
|
* streaming.
|
|
|
|
*/
|
|
|
|
v4l2_ctrl_activate(isc->do_wb_ctrl, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we have autowhitebalance on, start histogram procedure */
|
|
|
|
if (ctrls->awb == ISC_WB_AUTO &&
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
vb2_is_streaming(&isc->vb2_vidq) &&
|
|
|
|
ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
|
|
|
|
isc_set_histogram(isc, true);
|
|
|
|
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
/*
|
|
|
|
* for one time whitebalance adjustment, check the button,
|
|
|
|
* if it's pressed, perform the one time operation.
|
|
|
|
*/
|
|
|
|
if (ctrls->awb == ISC_WB_NONE &&
|
|
|
|
ctrl->cluster[ISC_CTRL_DO_WB]->is_new &&
|
|
|
|
!(ctrl->cluster[ISC_CTRL_DO_WB]->flags &
|
|
|
|
V4L2_CTRL_FLAG_INACTIVE)) {
|
|
|
|
ctrls->awb = ISC_WB_ONETIME;
|
|
|
|
isc_set_histogram(isc, true);
|
|
|
|
v4l2_dbg(1, debug, &isc->v4l2_dev,
|
|
|
|
"One time white-balance started.\n");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = container_of(ctrl->handler,
|
|
|
|
struct isc_device, ctrls.handler);
|
|
|
|
struct isc_ctrls *ctrls = &isc->ctrls;
|
|
|
|
|
|
|
|
switch (ctrl->id) {
|
|
|
|
/* being a cluster, this id will be called for every control */
|
|
|
|
case V4L2_CID_AUTO_WHITE_BALANCE:
|
|
|
|
ctrl->cluster[ISC_CTRL_R_GAIN]->val =
|
|
|
|
ctrls->gain[ISC_HIS_CFG_MODE_R];
|
|
|
|
ctrl->cluster[ISC_CTRL_B_GAIN]->val =
|
|
|
|
ctrls->gain[ISC_HIS_CFG_MODE_B];
|
|
|
|
ctrl->cluster[ISC_CTRL_GR_GAIN]->val =
|
|
|
|
ctrls->gain[ISC_HIS_CFG_MODE_GR];
|
|
|
|
ctrl->cluster[ISC_CTRL_GB_GAIN]->val =
|
|
|
|
ctrls->gain[ISC_HIS_CFG_MODE_GB];
|
|
|
|
|
|
|
|
ctrl->cluster[ISC_CTRL_R_OFF]->val =
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
ctrls->offset[ISC_HIS_CFG_MODE_R];
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
ctrl->cluster[ISC_CTRL_B_OFF]->val =
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
ctrls->offset[ISC_HIS_CFG_MODE_B];
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
ctrl->cluster[ISC_CTRL_GR_OFF]->val =
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
ctrls->offset[ISC_HIS_CFG_MODE_GR];
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
ctrl->cluster[ISC_CTRL_GB_OFF]->val =
|
media: atmel: atmel-isc: rework component offsets
The component offsets were computed in a negative way: they were
subtracted from the actual color component value.
So, a higher offset was reducing the component value.
This is not really desirable, as the offset is a 2's complements
number with 1 bit for sign and 12 value bits, so we would like to be able
to also add to the component, not only subtract.
The reported number in v4l2 is fine, a range from -4095 to +4095.
However when configuring a negative value for the offset, this would in
fact not function, because with the old code, the number was subtracted
from the max value. By setting something negative, it was overflowing in
fact. Reworked the component offsets by placing the real value as the
v4l2 ctrls.
Now, the values are the real number that is added or subtracted from
the component.
The negative values received from v4l2 are already in 2's complements, so
there is no need for conversion.
This actually simplifies a lot the computation procedure, eliminating the
need for the macros that convert from v4l2 values to ISC values and
viceversa.
Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be.
Example after this change:
# v4l2-ctl --set-ctrl=red_component_offset=-150 -L
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update
do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider
red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider
red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider
blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider
The auto white balance algorithm is unchanged, but the obtained value to
'subtract' is now converted to negative and saved as a v4l2 control and
displayed properly.
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-07-09 20:06:45 +08:00
|
|
|
ctrls->offset[ISC_HIS_CFG_MODE_GB];
|
2017-01-24 16:05:57 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
static const struct v4l2_ctrl_ops isc_awb_ops = {
|
|
|
|
.s_ctrl = isc_s_awb_ctrl,
|
|
|
|
.g_volatile_ctrl = isc_g_volatile_awb_ctrl,
|
2017-01-24 16:05:57 +08:00
|
|
|
};
|
|
|
|
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
#define ISC_CTRL_OFF(_name, _id, _name_str) \
|
|
|
|
static const struct v4l2_ctrl_config _name = { \
|
|
|
|
.ops = &isc_awb_ops, \
|
|
|
|
.id = _id, \
|
|
|
|
.name = _name_str, \
|
|
|
|
.type = V4L2_CTRL_TYPE_INTEGER, \
|
|
|
|
.flags = V4L2_CTRL_FLAG_SLIDER, \
|
|
|
|
.min = -4095, \
|
|
|
|
.max = 4095, \
|
|
|
|
.step = 1, \
|
|
|
|
.def = 0, \
|
|
|
|
}
|
|
|
|
|
|
|
|
ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset");
|
|
|
|
ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset");
|
|
|
|
ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset");
|
|
|
|
ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset");
|
|
|
|
|
|
|
|
#define ISC_CTRL_GAIN(_name, _id, _name_str) \
|
|
|
|
static const struct v4l2_ctrl_config _name = { \
|
|
|
|
.ops = &isc_awb_ops, \
|
|
|
|
.id = _id, \
|
|
|
|
.name = _name_str, \
|
|
|
|
.type = V4L2_CTRL_TYPE_INTEGER, \
|
|
|
|
.flags = V4L2_CTRL_FLAG_SLIDER, \
|
|
|
|
.min = 0, \
|
|
|
|
.max = 8191, \
|
|
|
|
.step = 1, \
|
|
|
|
.def = 512, \
|
|
|
|
}
|
|
|
|
|
|
|
|
ISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain");
|
|
|
|
ISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain");
|
|
|
|
ISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain");
|
|
|
|
ISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain");
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
static int isc_ctrl_init(struct isc_device *isc)
|
|
|
|
{
|
|
|
|
const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
|
|
|
|
struct isc_ctrls *ctrls = &isc->ctrls;
|
|
|
|
struct v4l2_ctrl_handler *hdl = &ctrls->handler;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctrls->hist_stat = HIST_INIT;
|
2019-04-15 22:13:49 +08:00
|
|
|
isc_reset_awb_ctrls(isc);
|
2017-01-24 16:05:57 +08:00
|
|
|
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
ret = v4l2_ctrl_handler_init(hdl, 13);
|
2017-01-24 16:05:57 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2021-04-13 18:57:18 +08:00
|
|
|
/* Initialize product specific controls. For example, contrast */
|
|
|
|
isc->config_ctrls(isc, ops);
|
|
|
|
|
2019-04-15 22:13:49 +08:00
|
|
|
ctrls->brightness = 0;
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
|
2021-04-13 18:56:59 +08:00
|
|
|
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, isc->gamma_max, 1,
|
|
|
|
isc->gamma_max);
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
|
|
|
|
V4L2_CID_AUTO_WHITE_BALANCE,
|
|
|
|
0, 1, 1, 1);
|
2017-01-24 16:05:57 +08:00
|
|
|
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
/* do_white_balance is a button, so min,max,step,default are ignored */
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
|
|
|
|
V4L2_CID_DO_WHITE_BALANCE,
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
0, 0, 0, 0);
|
|
|
|
|
2019-06-12 20:00:35 +08:00
|
|
|
if (!isc->do_wb_ctrl) {
|
|
|
|
ret = hdl->error;
|
|
|
|
v4l2_ctrl_handler_free(hdl);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
media: atmel: atmel-isc: add support for DO_WHITE_BALANCE
This adds support for the 'button' control DO_WHITE_BALANCE
This feature will enable the ISC to compute the white balance coefficients
in a one time shot, at the user discretion.
This can be used if a color chart/grey chart is present in front of the camera.
The ISC will adjust the coefficients and have them fixed until next balance
or until sensor mode is changed.
This is particularly useful for white balance adjustment in different
lighting scenarios, and then taking photos to similar scenery.
The old auto white balance stays in place, where the ISC will adjust every
4 frames to the current scenery lighting, if the scenery is approximately
grey in average, otherwise grey world algorithm fails.
One time white balance adjustments needs streaming to be enabled, such that
capture is enabled and the histogram has data to work with.
Histogram without capture does not work in this hardware module.
To start the one time white balance procedure:
v4l2-ctl --set-ctrl=do_white_balance=1
This feature works only if the sensor is streaming RAW data, as the hardware
supports a histogram only for RAW bayer components.
If the auto white balance is enabled, do_white_balance does nothing.
If the streaming is disabled, or the sensor does not output RAW data, the
control is inactive.
User controls now include the do_white_balance ctrl:
User Controls
brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider
contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider
white_balance_automatic 0x0098090c (bool) : default=1 value=0
do_white_balance 0x0098090d (button) : flags=write-only, execute-on-write
gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-04-15 22:13:54 +08:00
|
|
|
v4l2_ctrl_activate(isc->do_wb_ctrl, false);
|
|
|
|
|
media: atmel: atmel-isc-base: expose white balance as v4l2 controls
This exposes the white balance configuration of the ISC as v4l2 controls
into userspace.
There are 8 controls available:
4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB.
These gains are multipliers for each component, in format unsigned 0:4:9
with a default value of 512 (1.0 multiplier).
4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB.
These offsets are added/substracted from each component, in format signed
1:12:0 with a default value of 0 (+/- 0)
To expose this to userspace, added 8 custom controls, in an auto cluster.
To summarize the functionality:
The auto cluster switch is the auto white balance control, and it works
like this:
AWB == 1: autowhitebalance is on, the do_white_balance button is inactive,
the gains/offsets are inactive, but volatile and readable.
Thus, the results of the whitebalance algorithm are available to userspace
to read at any time.
AWB == 0: autowhitebalance is off, cluster is in manual mode, user can
configure the gain/offsets directly. More than that, if the
do_white_balance button is pressed, the driver will perform
one-time-adjustment, (preferably with color checker card) and the userspace
can read again the new values.
With this feature, the userspace can save the coefficients and reinstall
them for example after reboot or reprobing the driver.
[hverkuil: fix checkpatch warning]
[hverkuil: minor spacing adjustments in the functionality description]
Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-01-13 17:48:53 +08:00
|
|
|
isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL);
|
|
|
|
isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL);
|
|
|
|
isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL);
|
|
|
|
isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL);
|
|
|
|
isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL);
|
|
|
|
isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL);
|
|
|
|
isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL);
|
|
|
|
isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The cluster is in auto mode with autowhitebalance enabled
|
|
|
|
* and manual mode otherwise.
|
|
|
|
*/
|
|
|
|
v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true);
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
v4l2_ctrl_handler_setup(hdl);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
static int isc_async_bound(struct v4l2_async_notifier *notifier,
|
|
|
|
struct v4l2_subdev *subdev,
|
|
|
|
struct v4l2_async_subdev *asd)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = container_of(notifier->v4l2_dev,
|
|
|
|
struct isc_device, v4l2_dev);
|
|
|
|
struct isc_subdev_entity *subdev_entity =
|
|
|
|
container_of(notifier, struct isc_subdev_entity, notifier);
|
|
|
|
|
|
|
|
if (video_is_registered(&isc->video_dev)) {
|
|
|
|
v4l2_err(&isc->v4l2_dev, "only supports one sub-device.\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
subdev_entity->sd = subdev;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void isc_async_unbind(struct v4l2_async_notifier *notifier,
|
|
|
|
struct v4l2_subdev *subdev,
|
|
|
|
struct v4l2_async_subdev *asd)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = container_of(notifier->v4l2_dev,
|
|
|
|
struct isc_device, v4l2_dev);
|
2017-01-24 16:05:57 +08:00
|
|
|
cancel_work_sync(&isc->awb_work);
|
2016-08-17 14:05:27 +08:00
|
|
|
video_unregister_device(&isc->video_dev);
|
2017-01-24 16:05:57 +08:00
|
|
|
v4l2_ctrl_handler_free(&isc->ctrls.handler);
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
2021-04-13 18:57:22 +08:00
|
|
|
static struct isc_format *find_format_by_code(struct isc_device *isc,
|
|
|
|
unsigned int code, int *index)
|
2016-08-17 14:05:27 +08:00
|
|
|
{
|
2021-04-13 18:57:22 +08:00
|
|
|
struct isc_format *fmt = &isc->formats_list[0];
|
2016-08-17 14:05:27 +08:00
|
|
|
unsigned int i;
|
|
|
|
|
2021-04-13 18:57:22 +08:00
|
|
|
for (i = 0; i < isc->formats_list_size; i++) {
|
2016-08-17 14:05:27 +08:00
|
|
|
if (fmt->mbus_code == code) {
|
|
|
|
*index = i;
|
|
|
|
return fmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_formats_init(struct isc_device *isc)
|
|
|
|
{
|
|
|
|
struct isc_format *fmt;
|
|
|
|
struct v4l2_subdev *subdev = isc->current_subdev->sd;
|
2017-01-24 16:05:57 +08:00
|
|
|
unsigned int num_fmts, i, j;
|
2021-04-13 18:57:22 +08:00
|
|
|
u32 list_size = isc->formats_list_size;
|
2016-08-17 14:05:27 +08:00
|
|
|
struct v4l2_subdev_mbus_code_enum mbus_code = {
|
|
|
|
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
|
|
|
};
|
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
num_fmts = 0;
|
2016-08-17 14:05:27 +08:00
|
|
|
while (!v4l2_subdev_call(subdev, pad, enum_mbus_code,
|
|
|
|
NULL, &mbus_code)) {
|
|
|
|
mbus_code.index++;
|
2017-10-10 10:46:40 +08:00
|
|
|
|
2021-04-13 18:57:22 +08:00
|
|
|
fmt = find_format_by_code(isc, mbus_code.code, &i);
|
2019-03-29 15:38:28 +08:00
|
|
|
if (!fmt) {
|
|
|
|
v4l2_warn(&isc->v4l2_dev, "Mbus code %x not supported\n",
|
|
|
|
mbus_code.code);
|
2016-08-17 14:05:27 +08:00
|
|
|
continue;
|
2019-03-29 15:38:28 +08:00
|
|
|
}
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
fmt->sd_support = true;
|
2019-03-29 15:38:28 +08:00
|
|
|
num_fmts++;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!num_fmts)
|
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
isc->num_user_formats = num_fmts;
|
|
|
|
isc->user_formats = devm_kcalloc(isc->dev,
|
2017-08-28 17:55:16 +08:00
|
|
|
num_fmts, sizeof(*isc->user_formats),
|
2016-08-17 14:05:27 +08:00
|
|
|
GFP_KERNEL);
|
2017-08-28 17:46:57 +08:00
|
|
|
if (!isc->user_formats)
|
2016-08-17 14:05:27 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2021-04-13 18:57:22 +08:00
|
|
|
fmt = &isc->formats_list[0];
|
2017-10-10 10:46:40 +08:00
|
|
|
for (i = 0, j = 0; i < list_size; i++) {
|
2019-03-29 15:38:28 +08:00
|
|
|
if (fmt->sd_support)
|
2016-08-17 14:05:27 +08:00
|
|
|
isc->user_formats[j++] = fmt;
|
|
|
|
fmt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_set_default_fmt(struct isc_device *isc)
|
|
|
|
{
|
|
|
|
struct v4l2_format f = {
|
|
|
|
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
|
|
|
.fmt.pix = {
|
|
|
|
.width = VGA_WIDTH,
|
|
|
|
.height = VGA_HEIGHT,
|
|
|
|
.field = V4L2_FIELD_NONE,
|
|
|
|
.pixelformat = isc->user_formats[0]->fourcc,
|
|
|
|
},
|
|
|
|
};
|
2016-09-12 15:47:24 +08:00
|
|
|
int ret;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-03-29 15:38:28 +08:00
|
|
|
ret = isc_try_fmt(isc, &f, NULL);
|
2016-09-12 15:47:24 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
isc->fmt = f;
|
|
|
|
return 0;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int isc_async_complete(struct v4l2_async_notifier *notifier)
|
|
|
|
{
|
|
|
|
struct isc_device *isc = container_of(notifier->v4l2_dev,
|
|
|
|
struct isc_device, v4l2_dev);
|
|
|
|
struct video_device *vdev = &isc->video_dev;
|
|
|
|
struct vb2_queue *q = &isc->vb2_vidq;
|
2019-06-12 20:00:35 +08:00
|
|
|
int ret = 0;
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-04-12 18:19:46 +08:00
|
|
|
INIT_WORK(&isc->awb_work, isc_awb_work);
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev);
|
|
|
|
if (ret < 0) {
|
|
|
|
v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
isc->current_subdev = container_of(notifier,
|
|
|
|
struct isc_subdev_entity, notifier);
|
|
|
|
mutex_init(&isc->lock);
|
|
|
|
init_completion(&isc->comp);
|
|
|
|
|
|
|
|
/* Initialize videobuf2 queue */
|
|
|
|
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
|
|
|
|
q->drv_priv = isc;
|
|
|
|
q->buf_struct_size = sizeof(struct isc_buffer);
|
|
|
|
q->ops = &isc_vb2_ops;
|
|
|
|
q->mem_ops = &vb2_dma_contig_memops;
|
|
|
|
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
|
|
|
|
q->lock = &isc->lock;
|
|
|
|
q->min_buffers_needed = 1;
|
|
|
|
q->dev = isc->dev;
|
|
|
|
|
|
|
|
ret = vb2_queue_init(q);
|
|
|
|
if (ret < 0) {
|
|
|
|
v4l2_err(&isc->v4l2_dev,
|
|
|
|
"vb2_queue_init() failed: %d\n", ret);
|
2019-06-12 20:00:35 +08:00
|
|
|
goto isc_async_complete_err;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Init video dma queues */
|
|
|
|
INIT_LIST_HEAD(&isc->dma_queue);
|
|
|
|
spin_lock_init(&isc->dma_queue_lock);
|
2019-04-15 22:13:49 +08:00
|
|
|
spin_lock_init(&isc->awb_lock);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
ret = isc_formats_init(isc);
|
|
|
|
if (ret < 0) {
|
|
|
|
v4l2_err(&isc->v4l2_dev,
|
|
|
|
"Init format failed: %d\n", ret);
|
2019-06-12 20:00:35 +08:00
|
|
|
goto isc_async_complete_err;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = isc_set_default_fmt(isc);
|
|
|
|
if (ret) {
|
|
|
|
v4l2_err(&isc->v4l2_dev, "Could not set default format\n");
|
2019-06-12 20:00:35 +08:00
|
|
|
goto isc_async_complete_err;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
2017-01-24 16:05:57 +08:00
|
|
|
ret = isc_ctrl_init(isc);
|
|
|
|
if (ret) {
|
|
|
|
v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret);
|
2019-06-12 20:00:35 +08:00
|
|
|
goto isc_async_complete_err;
|
2017-01-24 16:05:57 +08:00
|
|
|
}
|
|
|
|
|
2016-08-17 14:05:27 +08:00
|
|
|
/* Register video device */
|
2021-04-13 18:57:00 +08:00
|
|
|
strscpy(vdev->name, "microchip-isc", sizeof(vdev->name));
|
2016-08-17 14:05:27 +08:00
|
|
|
vdev->release = video_device_release_empty;
|
|
|
|
vdev->fops = &isc_fops;
|
|
|
|
vdev->ioctl_ops = &isc_ioctl_ops;
|
|
|
|
vdev->v4l2_dev = &isc->v4l2_dev;
|
|
|
|
vdev->vfl_dir = VFL_DIR_RX;
|
|
|
|
vdev->queue = q;
|
|
|
|
vdev->lock = &isc->lock;
|
2017-01-24 16:05:57 +08:00
|
|
|
vdev->ctrl_handler = &isc->ctrls.handler;
|
2016-08-17 14:05:27 +08:00
|
|
|
vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
|
|
|
|
video_set_drvdata(vdev, isc);
|
|
|
|
|
2020-02-03 19:41:18 +08:00
|
|
|
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
|
2016-08-17 14:05:27 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
v4l2_err(&isc->v4l2_dev,
|
|
|
|
"video_register_device failed: %d\n", ret);
|
2019-06-12 20:00:35 +08:00
|
|
|
goto isc_async_complete_err;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2019-06-12 20:00:35 +08:00
|
|
|
|
|
|
|
isc_async_complete_err:
|
|
|
|
mutex_destroy(&isc->lock);
|
|
|
|
return ret;
|
2016-08-17 14:05:27 +08:00
|
|
|
}
|
|
|
|
|
2019-06-12 20:00:31 +08:00
|
|
|
const struct v4l2_async_notifier_operations isc_async_ops = {
|
2017-08-31 01:18:04 +08:00
|
|
|
.bound = isc_async_bound,
|
|
|
|
.unbind = isc_async_unbind,
|
|
|
|
.complete = isc_async_complete,
|
|
|
|
};
|
2021-07-05 20:57:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(isc_async_ops);
|
2017-08-31 01:18:04 +08:00
|
|
|
|
2019-06-12 20:00:31 +08:00
|
|
|
void isc_subdev_cleanup(struct isc_device *isc)
|
2016-08-17 14:05:27 +08:00
|
|
|
{
|
|
|
|
struct isc_subdev_entity *subdev_entity;
|
|
|
|
|
2018-09-30 03:54:18 +08:00
|
|
|
list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
|
2016-08-17 14:05:27 +08:00
|
|
|
v4l2_async_notifier_unregister(&subdev_entity->notifier);
|
2018-09-30 03:54:18 +08:00
|
|
|
v4l2_async_notifier_cleanup(&subdev_entity->notifier);
|
|
|
|
}
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
INIT_LIST_HEAD(&isc->subdev_entities);
|
|
|
|
}
|
2021-07-05 20:57:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(isc_subdev_cleanup);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2019-06-12 20:00:31 +08:00
|
|
|
int isc_pipeline_init(struct isc_device *isc)
|
2016-08-17 14:05:27 +08:00
|
|
|
{
|
|
|
|
struct device *dev = isc->dev;
|
|
|
|
struct regmap *regmap = isc->regmap;
|
|
|
|
struct regmap_field *regs;
|
|
|
|
unsigned int i;
|
|
|
|
|
2021-04-13 18:57:16 +08:00
|
|
|
/*
|
|
|
|
* DPCEN-->GDCEN-->BLCEN-->WB-->CFA-->CC-->
|
|
|
|
* GAM-->VHXS-->CSC-->CBC-->SUB422-->SUB420
|
|
|
|
*/
|
2016-08-17 14:05:27 +08:00
|
|
|
const struct reg_field regfields[ISC_PIPE_LINE_NODE_NUM] = {
|
2021-04-13 18:57:16 +08:00
|
|
|
REG_FIELD(ISC_DPC_CTRL, 0, 0),
|
|
|
|
REG_FIELD(ISC_DPC_CTRL, 1, 1),
|
|
|
|
REG_FIELD(ISC_DPC_CTRL, 2, 2),
|
2016-08-17 14:05:27 +08:00
|
|
|
REG_FIELD(ISC_WB_CTRL, 0, 0),
|
|
|
|
REG_FIELD(ISC_CFA_CTRL, 0, 0),
|
|
|
|
REG_FIELD(ISC_CC_CTRL, 0, 0),
|
|
|
|
REG_FIELD(ISC_GAM_CTRL, 0, 0),
|
|
|
|
REG_FIELD(ISC_GAM_CTRL, 1, 1),
|
|
|
|
REG_FIELD(ISC_GAM_CTRL, 2, 2),
|
|
|
|
REG_FIELD(ISC_GAM_CTRL, 3, 3),
|
2021-04-13 18:57:16 +08:00
|
|
|
REG_FIELD(ISC_VHXS_CTRL, 0, 0),
|
2021-04-13 18:57:06 +08:00
|
|
|
REG_FIELD(ISC_CSC_CTRL + isc->offsets.csc, 0, 0),
|
2021-04-13 18:57:08 +08:00
|
|
|
REG_FIELD(ISC_CBC_CTRL + isc->offsets.cbc, 0, 0),
|
2021-04-13 18:57:09 +08:00
|
|
|
REG_FIELD(ISC_SUB422_CTRL + isc->offsets.sub422, 0, 0),
|
|
|
|
REG_FIELD(ISC_SUB420_CTRL + isc->offsets.sub420, 0, 0),
|
2016-08-17 14:05:27 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
|
|
|
|
regs = devm_regmap_field_alloc(dev, regmap, regfields[i]);
|
|
|
|
if (IS_ERR(regs))
|
|
|
|
return PTR_ERR(regs);
|
|
|
|
|
|
|
|
isc->pipeline[i] = regs;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2021-07-05 20:57:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(isc_pipeline_init);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
|
|
|
/* regmap configuration */
|
2021-04-13 18:57:16 +08:00
|
|
|
#define ATMEL_ISC_REG_MAX 0xd5c
|
2019-06-12 20:00:31 +08:00
|
|
|
const struct regmap_config isc_regmap_config = {
|
2016-08-17 14:05:27 +08:00
|
|
|
.reg_bits = 32,
|
|
|
|
.reg_stride = 4,
|
|
|
|
.val_bits = 32,
|
|
|
|
.max_register = ATMEL_ISC_REG_MAX,
|
|
|
|
};
|
2021-07-05 20:57:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(isc_regmap_config);
|
2016-08-17 14:05:27 +08:00
|
|
|
|
2021-07-05 20:57:08 +08:00
|
|
|
MODULE_AUTHOR("Songjun Wu");
|
|
|
|
MODULE_AUTHOR("Eugen Hristev");
|
|
|
|
MODULE_DESCRIPTION("Atmel ISC common code base");
|
|
|
|
MODULE_LICENSE("GPL v2");
|