forked from Minki/linux
media fixes for v6.1-rc2
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+QmuaPwR3wnBdVwACF8+vY7k4RUFAmNTCJ8ACgkQCF8+vY7k 4RV/Ag//Ws8bIdedAQsbakBOq9JPOMiqHZnBde5DWn3XqU8aAY9bby70Rf2OTbl7 8mpqzeJY6wFfvesGCJ8L2hprpVqNW1KCrNAxSyaq/8Wau1D77wyEofqPNarNJzqn oKbH0JWd8hfStJpgmwyxUXjLanDXNx2s4lRm6R1WMWPH6dLeHydx4CtFMbmOn1L8 +jTtLK6631plWw/Kkp1A9z8N1D/9b4iMOgpoQZZLuzL1DouoYWlltz+Kw9HU7rsQ 1/wGmMwTwiV6Zt2UPwB4qudq3UpUMB3tm0KWprkmSx3Xv14Rr1o3zdwALTXib0Ez wZuzWzWaf9Fjp7CHOfEpm4x3+kU9181iw4ACk34cq7SglMYCdQ2hiwW5b9hhTN2m tYxv78fXJD2lHyxZQAHNN7XRmiWfMWMA0Z7GwCLVFXJ24Vjzv5AfuD3rJEE6Fv3X UOjPTNdNt4tpxX8A2Yd7WlfIBBGm2h63MVIYh50R54JCdLLLB8vhtob7pP2Y94pg FqXxfwc216cArKVsIjmUUkJs153IlQPYzBv9xXBBbD2DXhguWhLQnf9L/KdCnFkF 6NTULAHNezkss6dbLPIL08lCEIvTqeQabPBlCEtXNqqxBWfJwdwLbeS8mg2dTxao wwR5D37JbNuDSj0/4N/DlvVJozcCLJ2ZZ9R3c2j8/4Z0HERIhqA= =gJf4 -----END PGP SIGNATURE----- Merge tag 'media/v6.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media Pull missed media updates from Mauro Carvalho Chehab: "It seems I screwed-up my previous pull request: it ends up that only half of the media patches that were in linux-next got merged in -rc1. The script which creates the signed tags silently failed due to 5.19->6.0 so it ended generating a tag with incomplete stuff. So here are the missing parts: - a DVB core security fix - lots of fixes and cleanups for atomisp staging driver - old drivers that are VB1 are being moved to staging to be deprecated - several driver updates - mostly for embedded systems, but there are also some things addressing issues with some PC webcams, in the UVC video driver" * tag 'media/v6.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (163 commits) media: sun6i-csi: Move csi buffer definition to main header file media: sun6i-csi: Introduce and use video helper functions media: sun6i-csi: Add media ops with link notify callback media: sun6i-csi: Remove controls handler from the driver media: sun6i-csi: Register the media device after creation media: sun6i-csi: Pass and store csi device directly in video code media: sun6i-csi: Tidy up video code media: sun6i-csi: Tidy up v4l2 code media: sun6i-csi: Tidy up Kconfig media: sun6i-csi: Use runtime pm for clocks and reset media: sun6i-csi: Define and use variant to get module clock rate media: sun6i-csi: Always set exclusive module clock rate media: sun6i-csi: Tidy up platform code media: sun6i-csi: Refactor main driver data structures media: sun6i-csi: Define and use driver name and (reworked) description media: cedrus: Add a Kconfig dependency on RESET_CONTROLLER media: sun8i-rotate: Add a Kconfig dependency on RESET_CONTROLLER media: sun8i-di: Add a Kconfig dependency on RESET_CONTROLLER media: sun4i-csi: Add a Kconfig dependency on RESET_CONTROLLER media: sun6i-csi: Add a Kconfig dependency on RESET_CONTROLLER ...
This commit is contained in:
commit
3272eb1ace
@ -1,9 +0,0 @@
|
|||||||
Dongwoon Anatech DW9714 camera voice coil lens driver
|
|
||||||
|
|
||||||
DW9174 is a 10-bit DAC with current sink capability. It is intended
|
|
||||||
for driving voice coil lenses in camera modules.
|
|
||||||
|
|
||||||
Mandatory properties:
|
|
||||||
|
|
||||||
- compatible: "dongwoon,dw9714"
|
|
||||||
- reg: I²C slave address
|
|
@ -0,0 +1,47 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/media/i2c/dongwoon,dw9714.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Dongwoon Anatech DW9714 camera voice coil lens driver
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||||
|
|
||||||
|
description:
|
||||||
|
DW9174 is a 10-bit DAC with current sink capability. It is intended for
|
||||||
|
driving voice coil lenses in camera modules.
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: dongwoon,dw9714
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
powerdown-gpios:
|
||||||
|
description:
|
||||||
|
XSD pin for shutdown (active low)
|
||||||
|
|
||||||
|
vcc-supply:
|
||||||
|
description: VDD power supply
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
i2c {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
camera-lens@c {
|
||||||
|
compatible = "dongwoon,dw9714";
|
||||||
|
reg = <0x0c>;
|
||||||
|
vcc-supply = <®_csi_1v8>;
|
||||||
|
};
|
||||||
|
};
|
@ -214,18 +214,29 @@ Link properties can be modified at runtime by calling
|
|||||||
Pipelines and media streams
|
Pipelines and media streams
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A media stream is a stream of pixels or metadata originating from one or more
|
||||||
|
source devices (such as a sensors) and flowing through media entity pads
|
||||||
|
towards the final sinks. The stream can be modified on the route by the
|
||||||
|
devices (e.g. scaling or pixel format conversions), or it can be split into
|
||||||
|
multiple branches, or multiple branches can be merged.
|
||||||
|
|
||||||
|
A media pipeline is a set of media streams which are interdependent. This
|
||||||
|
interdependency can be caused by the hardware (e.g. configuration of a second
|
||||||
|
stream cannot be changed if the first stream has been enabled) or by the driver
|
||||||
|
due to the software design. Most commonly a media pipeline consists of a single
|
||||||
|
stream which does not branch.
|
||||||
|
|
||||||
When starting streaming, drivers must notify all entities in the pipeline to
|
When starting streaming, drivers must notify all entities in the pipeline to
|
||||||
prevent link states from being modified during streaming by calling
|
prevent link states from being modified during streaming by calling
|
||||||
:c:func:`media_pipeline_start()`.
|
:c:func:`media_pipeline_start()`.
|
||||||
|
|
||||||
The function will mark all entities connected to the given entity through
|
The function will mark all the pads which are part of the pipeline as streaming.
|
||||||
enabled links, either directly or indirectly, as streaming.
|
|
||||||
|
|
||||||
The struct media_pipeline instance pointed to by
|
The struct media_pipeline instance pointed to by
|
||||||
the pipe argument will be stored in every entity in the pipeline.
|
the pipe argument will be stored in every pad in the pipeline.
|
||||||
Drivers should embed the struct media_pipeline
|
Drivers should embed the struct media_pipeline
|
||||||
in higher-level pipeline structures and can then access the
|
in higher-level pipeline structures and can then access the
|
||||||
pipeline through the struct media_entity
|
pipeline through the struct media_pad
|
||||||
pipe field.
|
pipe field.
|
||||||
|
|
||||||
Calls to :c:func:`media_pipeline_start()` can be nested.
|
Calls to :c:func:`media_pipeline_start()` can be nested.
|
||||||
|
@ -239,6 +239,7 @@ ignore define CEC_OP_FEAT_DEV_HAS_DECK_CONTROL
|
|||||||
ignore define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE
|
ignore define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE
|
||||||
ignore define CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX
|
ignore define CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX
|
||||||
ignore define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX
|
ignore define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX
|
||||||
|
ignore define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_VOLUME_LEVEL
|
||||||
|
|
||||||
ignore define CEC_MSG_GIVE_FEATURES
|
ignore define CEC_MSG_GIVE_FEATURES
|
||||||
|
|
||||||
@ -487,6 +488,7 @@ ignore define CEC_OP_SYS_AUD_STATUS_ON
|
|||||||
|
|
||||||
ignore define CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST
|
ignore define CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST
|
||||||
ignore define CEC_MSG_SYSTEM_AUDIO_MODE_STATUS
|
ignore define CEC_MSG_SYSTEM_AUDIO_MODE_STATUS
|
||||||
|
ignore define CEC_MSG_SET_AUDIO_VOLUME_LEVEL
|
||||||
|
|
||||||
ignore define CEC_OP_AUD_FMT_ID_CEA861
|
ignore define CEC_OP_AUD_FMT_ID_CEA861
|
||||||
ignore define CEC_OP_AUD_FMT_ID_CEA861_CXT
|
ignore define CEC_OP_AUD_FMT_ID_CEA861_CXT
|
||||||
|
@ -136,9 +136,9 @@ V4L2 functions
|
|||||||
|
|
||||||
operates like the :c:func:`read()` function.
|
operates like the :c:func:`read()` function.
|
||||||
|
|
||||||
.. c:function:: void v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, int64_t offset);
|
.. c:function:: void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, int64_t offset);
|
||||||
|
|
||||||
operates like the :c:func:`munmap()` function.
|
operates like the :c:func:`mmap()` function.
|
||||||
|
|
||||||
.. c:function:: int v4l2_munmap(void *_start, size_t length);
|
.. c:function:: int v4l2_munmap(void *_start, size_t length);
|
||||||
|
|
||||||
|
@ -6284,7 +6284,7 @@ M: Sakari Ailus <sakari.ailus@linux.intel.com>
|
|||||||
L: linux-media@vger.kernel.org
|
L: linux-media@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
T: git git://linuxtv.org/media_tree.git
|
T: git git://linuxtv.org/media_tree.git
|
||||||
F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9714.txt
|
F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9714.yaml
|
||||||
F: drivers/media/i2c/dw9714.c
|
F: drivers/media/i2c/dw9714.c
|
||||||
|
|
||||||
DONGWOON DW9768 LENS VOICE COIL DRIVER
|
DONGWOON DW9768 LENS VOICE COIL DRIVER
|
||||||
@ -18141,7 +18141,6 @@ L: linux-media@vger.kernel.org
|
|||||||
S: Maintained
|
S: Maintained
|
||||||
T: git git://linuxtv.org/media_tree.git
|
T: git git://linuxtv.org/media_tree.git
|
||||||
F: drivers/staging/media/deprecated/saa7146/
|
F: drivers/staging/media/deprecated/saa7146/
|
||||||
F: include/media/drv-intf/saa7146*
|
|
||||||
|
|
||||||
SAFESETID SECURITY MODULE
|
SAFESETID SECURITY MODULE
|
||||||
M: Micah Morton <mortonm@chromium.org>
|
M: Micah Morton <mortonm@chromium.org>
|
||||||
@ -22771,7 +22770,7 @@ S: Maintained
|
|||||||
W: http://mjpeg.sourceforge.net/driver-zoran/
|
W: http://mjpeg.sourceforge.net/driver-zoran/
|
||||||
Q: https://patchwork.linuxtv.org/project/linux-media/list/
|
Q: https://patchwork.linuxtv.org/project/linux-media/list/
|
||||||
F: Documentation/driver-api/media/drivers/zoran.rst
|
F: Documentation/driver-api/media/drivers/zoran.rst
|
||||||
F: drivers/staging/media/zoran/
|
F: drivers/media/pci/zoran/
|
||||||
|
|
||||||
ZRAM COMPRESSED RAM BLOCK DEVICE DRVIER
|
ZRAM COMPRESSED RAM BLOCK DEVICE DRVIER
|
||||||
M: Minchan Kim <minchan@kernel.org>
|
M: Minchan Kim <minchan@kernel.org>
|
||||||
|
@ -24,7 +24,7 @@ if MEDIA_SUPPORT
|
|||||||
|
|
||||||
config MEDIA_SUPPORT_FILTER
|
config MEDIA_SUPPORT_FILTER
|
||||||
bool "Filter media drivers"
|
bool "Filter media drivers"
|
||||||
default y if !EMBEDDED && !EXPERT
|
default y if !EXPERT
|
||||||
help
|
help
|
||||||
Configuring the media subsystem can be complex, as there are
|
Configuring the media subsystem can be complex, as there are
|
||||||
hundreds of drivers and other config options.
|
hundreds of drivers and other config options.
|
||||||
|
@ -1027,6 +1027,7 @@ static const u8 cec_msg_size[256] = {
|
|||||||
[CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR] = 2 | DIRECTED,
|
[CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR] = 2 | DIRECTED,
|
||||||
[CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR] = 2 | DIRECTED,
|
[CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR] = 2 | DIRECTED,
|
||||||
[CEC_MSG_SET_SYSTEM_AUDIO_MODE] = 3 | BOTH,
|
[CEC_MSG_SET_SYSTEM_AUDIO_MODE] = 3 | BOTH,
|
||||||
|
[CEC_MSG_SET_AUDIO_VOLUME_LEVEL] = 3 | DIRECTED,
|
||||||
[CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST] = 2 | DIRECTED,
|
[CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST] = 2 | DIRECTED,
|
||||||
[CEC_MSG_SYSTEM_AUDIO_MODE_STATUS] = 3 | DIRECTED,
|
[CEC_MSG_SYSTEM_AUDIO_MODE_STATUS] = 3 | DIRECTED,
|
||||||
[CEC_MSG_SET_AUDIO_RATE] = 3 | DIRECTED,
|
[CEC_MSG_SET_AUDIO_RATE] = 3 | DIRECTED,
|
||||||
|
@ -44,6 +44,8 @@ static void handle_cec_message(struct cros_ec_cec *cros_ec_cec)
|
|||||||
uint8_t *cec_message = cros_ec->event_data.data.cec_message;
|
uint8_t *cec_message = cros_ec->event_data.data.cec_message;
|
||||||
unsigned int len = cros_ec->event_size;
|
unsigned int len = cros_ec->event_size;
|
||||||
|
|
||||||
|
if (len > CEC_MAX_MSG_SIZE)
|
||||||
|
len = CEC_MAX_MSG_SIZE;
|
||||||
cros_ec_cec->rx_msg.len = len;
|
cros_ec_cec->rx_msg.len = len;
|
||||||
memcpy(cros_ec_cec->rx_msg.msg, cec_message, len);
|
memcpy(cros_ec_cec->rx_msg.msg, cec_message, len);
|
||||||
|
|
||||||
@ -221,6 +223,8 @@ static const struct cec_dmi_match cec_dmi_match_table[] = {
|
|||||||
{ "Google", "Moli", "0000:00:02.0", "Port B" },
|
{ "Google", "Moli", "0000:00:02.0", "Port B" },
|
||||||
/* Google Kinox */
|
/* Google Kinox */
|
||||||
{ "Google", "Kinox", "0000:00:02.0", "Port B" },
|
{ "Google", "Kinox", "0000:00:02.0", "Port B" },
|
||||||
|
/* Google Kuldax */
|
||||||
|
{ "Google", "Kuldax", "0000:00:02.0", "Port B" },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
|
static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
|
||||||
|
@ -115,6 +115,8 @@ static irqreturn_t s5p_cec_irq_handler(int irq, void *priv)
|
|||||||
dev_dbg(cec->dev, "Buffer overrun (worker did not process previous message)\n");
|
dev_dbg(cec->dev, "Buffer overrun (worker did not process previous message)\n");
|
||||||
cec->rx = STATE_BUSY;
|
cec->rx = STATE_BUSY;
|
||||||
cec->msg.len = status >> 24;
|
cec->msg.len = status >> 24;
|
||||||
|
if (cec->msg.len > CEC_MAX_MSG_SIZE)
|
||||||
|
cec->msg.len = CEC_MAX_MSG_SIZE;
|
||||||
cec->msg.rx_status = CEC_RX_STATUS_OK;
|
cec->msg.rx_status = CEC_RX_STATUS_OK;
|
||||||
s5p_cec_get_rx_buf(cec, cec->msg.len,
|
s5p_cec_get_rx_buf(cec, cec->msg.len,
|
||||||
cec->msg.msg);
|
cec->msg.msg);
|
||||||
|
@ -6660,7 +6660,7 @@ static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr)
|
|||||||
static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
|
static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
|
||||||
{
|
{
|
||||||
struct drxk_state *state = fe->demodulator_priv;
|
struct drxk_state *state = fe->demodulator_priv;
|
||||||
u16 err;
|
u16 err = 0;
|
||||||
|
|
||||||
dprintk(1, "\n");
|
dprintk(1, "\n");
|
||||||
|
|
||||||
|
@ -406,7 +406,6 @@ static int ar0521_set_fmt(struct v4l2_subdev *sd,
|
|||||||
struct v4l2_subdev_format *format)
|
struct v4l2_subdev_format *format)
|
||||||
{
|
{
|
||||||
struct ar0521_dev *sensor = to_ar0521_dev(sd);
|
struct ar0521_dev *sensor = to_ar0521_dev(sd);
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
ar0521_adj_fmt(&format->format);
|
ar0521_adj_fmt(&format->format);
|
||||||
|
|
||||||
@ -423,7 +422,7 @@ static int ar0521_set_fmt(struct v4l2_subdev *sd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&sensor->lock);
|
mutex_unlock(&sensor->lock);
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ar0521_s_ctrl(struct v4l2_ctrl *ctrl)
|
static int ar0521_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||||
@ -756,10 +755,12 @@ static int ar0521_power_on(struct device *dev)
|
|||||||
gpiod_set_value(sensor->reset_gpio, 0);
|
gpiod_set_value(sensor->reset_gpio, 0);
|
||||||
usleep_range(4500, 5000); /* min 45000 clocks */
|
usleep_range(4500, 5000); /* min 45000 clocks */
|
||||||
|
|
||||||
for (cnt = 0; cnt < ARRAY_SIZE(initial_regs); cnt++)
|
for (cnt = 0; cnt < ARRAY_SIZE(initial_regs); cnt++) {
|
||||||
if (ar0521_write_regs(sensor, initial_regs[cnt].data,
|
ret = ar0521_write_regs(sensor, initial_regs[cnt].data,
|
||||||
initial_regs[cnt].count))
|
initial_regs[cnt].count);
|
||||||
|
if (ret)
|
||||||
goto off;
|
goto off;
|
||||||
|
}
|
||||||
|
|
||||||
ret = ar0521_write_reg(sensor, AR0521_REG_SERIAL_FORMAT,
|
ret = ar0521_write_reg(sensor, AR0521_REG_SERIAL_FORMAT,
|
||||||
AR0521_REG_SERIAL_FORMAT_MIPI |
|
AR0521_REG_SERIAL_FORMAT_MIPI |
|
||||||
|
@ -238,6 +238,43 @@ static int get_key_knc1(struct IR_i2c *ir, enum rc_proto *protocol,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int get_key_geniatech(struct IR_i2c *ir, enum rc_proto *protocol,
|
||||||
|
u32 *scancode, u8 *toggle)
|
||||||
|
{
|
||||||
|
int i, rc;
|
||||||
|
unsigned char b;
|
||||||
|
|
||||||
|
/* poll IR chip */
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
rc = i2c_master_recv(ir->c, &b, 1);
|
||||||
|
if (rc == 1)
|
||||||
|
break;
|
||||||
|
msleep(20);
|
||||||
|
}
|
||||||
|
if (rc != 1) {
|
||||||
|
dev_dbg(&ir->rc->dev, "read error\n");
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* don't repeat the key */
|
||||||
|
if (ir->old == b)
|
||||||
|
return 0;
|
||||||
|
ir->old = b;
|
||||||
|
|
||||||
|
/* decode to RC5 */
|
||||||
|
b &= 0x7f;
|
||||||
|
b = (b - 1) / 2;
|
||||||
|
|
||||||
|
dev_dbg(&ir->rc->dev, "key %02x\n", b);
|
||||||
|
|
||||||
|
*protocol = RC_PROTO_RC5;
|
||||||
|
*scancode = b;
|
||||||
|
*toggle = ir->old >> 7;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int get_key_avermedia_cardbus(struct IR_i2c *ir, enum rc_proto *protocol,
|
static int get_key_avermedia_cardbus(struct IR_i2c *ir, enum rc_proto *protocol,
|
||||||
u32 *scancode, u8 *toggle)
|
u32 *scancode, u8 *toggle)
|
||||||
{
|
{
|
||||||
@ -766,6 +803,13 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||||||
rc_proto = RC_PROTO_BIT_OTHER;
|
rc_proto = RC_PROTO_BIT_OTHER;
|
||||||
ir_codes = RC_MAP_EMPTY;
|
ir_codes = RC_MAP_EMPTY;
|
||||||
break;
|
break;
|
||||||
|
case 0x33:
|
||||||
|
name = "Geniatech";
|
||||||
|
ir->get_key = get_key_geniatech;
|
||||||
|
rc_proto = RC_PROTO_BIT_RC5;
|
||||||
|
ir_codes = RC_MAP_TOTAL_MEDIA_IN_HAND_02;
|
||||||
|
ir->old = 0xfc;
|
||||||
|
break;
|
||||||
case 0x6b:
|
case 0x6b:
|
||||||
name = "FusionHDTV";
|
name = "FusionHDTV";
|
||||||
ir->get_key = get_key_fusionhdtv;
|
ir->get_key = get_key_fusionhdtv;
|
||||||
@ -825,6 +869,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||||||
case IR_KBD_GET_KEY_KNC1:
|
case IR_KBD_GET_KEY_KNC1:
|
||||||
ir->get_key = get_key_knc1;
|
ir->get_key = get_key_knc1;
|
||||||
break;
|
break;
|
||||||
|
case IR_KBD_GET_KEY_GENIATECH:
|
||||||
|
ir->get_key = get_key_geniatech;
|
||||||
|
break;
|
||||||
case IR_KBD_GET_KEY_FUSIONHDTV:
|
case IR_KBD_GET_KEY_FUSIONHDTV:
|
||||||
ir->get_key = get_key_fusionhdtv;
|
ir->get_key = get_key_fusionhdtv;
|
||||||
break;
|
break;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_graph.h>
|
#include <linux/of_graph.h>
|
||||||
|
@ -633,7 +633,7 @@ static int mt9v111_hw_config(struct mt9v111_dev *mt9v111)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Set pixel integration time to the whole frame time.
|
* Set pixel integration time to the whole frame time.
|
||||||
* This value controls the the shutter delay when running with AE
|
* This value controls the shutter delay when running with AE
|
||||||
* disabled. If longer than frame time, it affects the output
|
* disabled. If longer than frame time, it affects the output
|
||||||
* frame rate.
|
* frame rate.
|
||||||
*/
|
*/
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
@ -447,8 +448,6 @@ struct ov5640_dev {
|
|||||||
/* lock to protect all members below */
|
/* lock to protect all members below */
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
|
||||||
int power_count;
|
|
||||||
|
|
||||||
struct v4l2_mbus_framefmt fmt;
|
struct v4l2_mbus_framefmt fmt;
|
||||||
bool pending_fmt_change;
|
bool pending_fmt_change;
|
||||||
|
|
||||||
@ -2696,39 +2695,24 @@ power_off:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------- Subdev Operations --------------- */
|
static int ov5640_sensor_suspend(struct device *dev)
|
||||||
|
|
||||||
static int ov5640_s_power(struct v4l2_subdev *sd, int on)
|
|
||||||
{
|
{
|
||||||
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
||||||
int ret = 0;
|
struct ov5640_dev *ov5640 = to_ov5640_dev(sd);
|
||||||
|
|
||||||
mutex_lock(&sensor->lock);
|
return ov5640_set_power(ov5640, false);
|
||||||
|
|
||||||
/*
|
|
||||||
* If the power count is modified from 0 to != 0 or from != 0 to 0,
|
|
||||||
* update the power state.
|
|
||||||
*/
|
|
||||||
if (sensor->power_count == !on) {
|
|
||||||
ret = ov5640_set_power(sensor, !!on);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update the power count. */
|
|
||||||
sensor->power_count += on ? 1 : -1;
|
|
||||||
WARN_ON(sensor->power_count < 0);
|
|
||||||
out:
|
|
||||||
mutex_unlock(&sensor->lock);
|
|
||||||
|
|
||||||
if (on && !ret && sensor->power_count == 1) {
|
|
||||||
/* restore controls */
|
|
||||||
ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ov5640_sensor_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
||||||
|
struct ov5640_dev *ov5640 = to_ov5640_dev(sd);
|
||||||
|
|
||||||
|
return ov5640_set_power(ov5640, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------- Subdev Operations --------------- */
|
||||||
|
|
||||||
static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
|
static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
|
||||||
struct v4l2_fract *fi,
|
struct v4l2_fract *fi,
|
||||||
u32 width, u32 height)
|
u32 width, u32 height)
|
||||||
@ -3314,6 +3298,9 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
|||||||
|
|
||||||
/* v4l2_ctrl_lock() locks our own mutex */
|
/* v4l2_ctrl_lock() locks our own mutex */
|
||||||
|
|
||||||
|
if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
switch (ctrl->id) {
|
switch (ctrl->id) {
|
||||||
case V4L2_CID_AUTOGAIN:
|
case V4L2_CID_AUTOGAIN:
|
||||||
val = ov5640_get_gain(sensor);
|
val = ov5640_get_gain(sensor);
|
||||||
@ -3329,6 +3316,8 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3358,9 +3347,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
|
|||||||
/*
|
/*
|
||||||
* If the device is not powered up by the host driver do
|
* If the device is not powered up by the host driver do
|
||||||
* not apply any controls to H/W at this time. Instead
|
* not apply any controls to H/W at this time. Instead
|
||||||
* the controls will be restored right after power-up.
|
* the controls will be restored at start streaming time.
|
||||||
*/
|
*/
|
||||||
if (sensor->power_count == 0)
|
if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
switch (ctrl->id) {
|
switch (ctrl->id) {
|
||||||
@ -3402,6 +3391,8 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3677,6 +3668,18 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
|
|||||||
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
ret = pm_runtime_resume_and_get(&sensor->i2c_client->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
|
||||||
|
if (ret) {
|
||||||
|
pm_runtime_put(&sensor->i2c_client->dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&sensor->lock);
|
mutex_lock(&sensor->lock);
|
||||||
|
|
||||||
if (sensor->streaming == !enable) {
|
if (sensor->streaming == !enable) {
|
||||||
@ -3701,8 +3704,13 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
|
|||||||
if (!ret)
|
if (!ret)
|
||||||
sensor->streaming = enable;
|
sensor->streaming = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&sensor->lock);
|
mutex_unlock(&sensor->lock);
|
||||||
|
|
||||||
|
if (!enable || ret)
|
||||||
|
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3724,7 +3732,6 @@ static int ov5640_init_cfg(struct v4l2_subdev *sd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const struct v4l2_subdev_core_ops ov5640_core_ops = {
|
static const struct v4l2_subdev_core_ops ov5640_core_ops = {
|
||||||
.s_power = ov5640_s_power,
|
|
||||||
.log_status = v4l2_ctrl_subdev_log_status,
|
.log_status = v4l2_ctrl_subdev_log_status,
|
||||||
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
|
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
|
||||||
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
|
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
|
||||||
@ -3770,26 +3777,20 @@ static int ov5640_check_chip_id(struct ov5640_dev *sensor)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
u16 chip_id;
|
u16 chip_id;
|
||||||
|
|
||||||
ret = ov5640_set_power_on(sensor);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = ov5640_read_reg16(sensor, OV5640_REG_CHIP_ID, &chip_id);
|
ret = ov5640_read_reg16(sensor, OV5640_REG_CHIP_ID, &chip_id);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&client->dev, "%s: failed to read chip identifier\n",
|
dev_err(&client->dev, "%s: failed to read chip identifier\n",
|
||||||
__func__);
|
__func__);
|
||||||
goto power_off;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chip_id != 0x5640) {
|
if (chip_id != 0x5640) {
|
||||||
dev_err(&client->dev, "%s: wrong chip identifier, expected 0x5640, got 0x%x\n",
|
dev_err(&client->dev, "%s: wrong chip identifier, expected 0x5640, got 0x%x\n",
|
||||||
__func__, chip_id);
|
__func__, chip_id);
|
||||||
ret = -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
power_off:
|
return 0;
|
||||||
ov5640_set_power_off(sensor);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ov5640_probe(struct i2c_client *client)
|
static int ov5640_probe(struct i2c_client *client)
|
||||||
@ -3880,26 +3881,43 @@ static int ov5640_probe(struct i2c_client *client)
|
|||||||
|
|
||||||
ret = ov5640_get_regulators(sensor);
|
ret = ov5640_get_regulators(sensor);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto entity_cleanup;
|
||||||
|
|
||||||
mutex_init(&sensor->lock);
|
mutex_init(&sensor->lock);
|
||||||
|
|
||||||
ret = ov5640_check_chip_id(sensor);
|
|
||||||
if (ret)
|
|
||||||
goto entity_cleanup;
|
|
||||||
|
|
||||||
ret = ov5640_init_controls(sensor);
|
ret = ov5640_init_controls(sensor);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto entity_cleanup;
|
goto entity_cleanup;
|
||||||
|
|
||||||
|
ret = ov5640_sensor_resume(dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to power on\n");
|
||||||
|
goto entity_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_runtime_set_active(dev);
|
||||||
|
pm_runtime_get_noresume(dev);
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
|
||||||
|
ret = ov5640_check_chip_id(sensor);
|
||||||
|
if (ret)
|
||||||
|
goto err_pm_runtime;
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev_sensor(&sensor->sd);
|
ret = v4l2_async_register_subdev_sensor(&sensor->sd);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto free_ctrls;
|
goto err_pm_runtime;
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(dev, 1000);
|
||||||
|
pm_runtime_use_autosuspend(dev);
|
||||||
|
pm_runtime_put_autosuspend(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free_ctrls:
|
err_pm_runtime:
|
||||||
|
pm_runtime_put_noidle(dev);
|
||||||
|
pm_runtime_disable(dev);
|
||||||
v4l2_ctrl_handler_free(&sensor->ctrls.handler);
|
v4l2_ctrl_handler_free(&sensor->ctrls.handler);
|
||||||
|
ov5640_sensor_suspend(dev);
|
||||||
entity_cleanup:
|
entity_cleanup:
|
||||||
media_entity_cleanup(&sensor->sd.entity);
|
media_entity_cleanup(&sensor->sd.entity);
|
||||||
mutex_destroy(&sensor->lock);
|
mutex_destroy(&sensor->lock);
|
||||||
@ -3910,6 +3928,12 @@ static void ov5640_remove(struct i2c_client *client)
|
|||||||
{
|
{
|
||||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||||
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
||||||
|
struct device *dev = &client->dev;
|
||||||
|
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
if (!pm_runtime_status_suspended(dev))
|
||||||
|
ov5640_sensor_suspend(dev);
|
||||||
|
pm_runtime_set_suspended(dev);
|
||||||
|
|
||||||
v4l2_async_unregister_subdev(&sensor->sd);
|
v4l2_async_unregister_subdev(&sensor->sd);
|
||||||
media_entity_cleanup(&sensor->sd.entity);
|
media_entity_cleanup(&sensor->sd.entity);
|
||||||
@ -3917,6 +3941,10 @@ static void ov5640_remove(struct i2c_client *client)
|
|||||||
mutex_destroy(&sensor->lock);
|
mutex_destroy(&sensor->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops ov5640_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(ov5640_sensor_suspend, ov5640_sensor_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static const struct i2c_device_id ov5640_id[] = {
|
static const struct i2c_device_id ov5640_id[] = {
|
||||||
{"ov5640", 0},
|
{"ov5640", 0},
|
||||||
{},
|
{},
|
||||||
@ -3933,6 +3961,7 @@ static struct i2c_driver ov5640_i2c_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "ov5640",
|
.name = "ov5640",
|
||||||
.of_match_table = ov5640_dt_ids,
|
.of_match_table = ov5640_dt_ids,
|
||||||
|
.pm = &ov5640_pm_ops,
|
||||||
},
|
},
|
||||||
.id_table = ov5640_id,
|
.id_table = ov5640_id,
|
||||||
.probe_new = ov5640_probe,
|
.probe_new = ov5640_probe,
|
||||||
|
@ -3034,11 +3034,13 @@ static int ov8865_probe(struct i2c_client *client)
|
|||||||
&rate);
|
&rate);
|
||||||
if (!ret && sensor->extclk) {
|
if (!ret && sensor->extclk) {
|
||||||
ret = clk_set_rate(sensor->extclk, rate);
|
ret = clk_set_rate(sensor->extclk, rate);
|
||||||
if (ret)
|
if (ret) {
|
||||||
return dev_err_probe(dev, ret,
|
dev_err_probe(dev, ret, "failed to set clock rate\n");
|
||||||
"failed to set clock rate\n");
|
goto error_endpoint;
|
||||||
|
}
|
||||||
} else if (ret && !sensor->extclk) {
|
} else if (ret && !sensor->extclk) {
|
||||||
return dev_err_probe(dev, ret, "invalid clock config\n");
|
dev_err_probe(dev, ret, "invalid clock config\n");
|
||||||
|
goto error_endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
sensor->extclk_rate = rate ? rate : clk_get_rate(sensor->extclk);
|
sensor->extclk_rate = rate ? rate : clk_get_rate(sensor->extclk);
|
||||||
|
@ -581,7 +581,7 @@ static void __media_device_unregister_entity(struct media_entity *entity)
|
|||||||
struct media_device *mdev = entity->graph_obj.mdev;
|
struct media_device *mdev = entity->graph_obj.mdev;
|
||||||
struct media_link *link, *tmp;
|
struct media_link *link, *tmp;
|
||||||
struct media_interface *intf;
|
struct media_interface *intf;
|
||||||
unsigned int i;
|
struct media_pad *iter;
|
||||||
|
|
||||||
ida_free(&mdev->entity_internal_idx, entity->internal_idx);
|
ida_free(&mdev->entity_internal_idx, entity->internal_idx);
|
||||||
|
|
||||||
@ -597,8 +597,8 @@ static void __media_device_unregister_entity(struct media_entity *entity)
|
|||||||
__media_entity_remove_links(entity);
|
__media_entity_remove_links(entity);
|
||||||
|
|
||||||
/* Remove all pads that belong to this entity */
|
/* Remove all pads that belong to this entity */
|
||||||
for (i = 0; i < entity->num_pads; i++)
|
media_entity_for_each_pad(entity, iter)
|
||||||
media_gobj_destroy(&entity->pads[i].graph_obj);
|
media_gobj_destroy(&iter->graph_obj);
|
||||||
|
|
||||||
/* Remove the entity */
|
/* Remove the entity */
|
||||||
media_gobj_destroy(&entity->graph_obj);
|
media_gobj_destroy(&entity->graph_obj);
|
||||||
@ -610,7 +610,7 @@ int __must_check media_device_register_entity(struct media_device *mdev,
|
|||||||
struct media_entity *entity)
|
struct media_entity *entity)
|
||||||
{
|
{
|
||||||
struct media_entity_notify *notify, *next;
|
struct media_entity_notify *notify, *next;
|
||||||
unsigned int i;
|
struct media_pad *iter;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (entity->function == MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN ||
|
if (entity->function == MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN ||
|
||||||
@ -639,9 +639,8 @@ int __must_check media_device_register_entity(struct media_device *mdev,
|
|||||||
media_gobj_create(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj);
|
media_gobj_create(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj);
|
||||||
|
|
||||||
/* Initialize objects at the pads */
|
/* Initialize objects at the pads */
|
||||||
for (i = 0; i < entity->num_pads; i++)
|
media_entity_for_each_pad(entity, iter)
|
||||||
media_gobj_create(mdev, MEDIA_GRAPH_PAD,
|
media_gobj_create(mdev, MEDIA_GRAPH_PAD, &iter->graph_obj);
|
||||||
&entity->pads[i].graph_obj);
|
|
||||||
|
|
||||||
/* invoke entity_notify callbacks */
|
/* invoke entity_notify callbacks */
|
||||||
list_for_each_entry_safe(notify, next, &mdev->entity_notify, list)
|
list_for_each_entry_safe(notify, next, &mdev->entity_notify, list)
|
||||||
|
@ -59,10 +59,12 @@ static inline const char *link_type_name(struct media_link *link)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum,
|
__must_check int media_entity_enum_init(struct media_entity_enum *ent_enum,
|
||||||
int idx_max)
|
struct media_device *mdev)
|
||||||
{
|
{
|
||||||
idx_max = ALIGN(idx_max, BITS_PER_LONG);
|
int idx_max;
|
||||||
|
|
||||||
|
idx_max = ALIGN(mdev->entity_internal_idx_max + 1, BITS_PER_LONG);
|
||||||
ent_enum->bmap = bitmap_zalloc(idx_max, GFP_KERNEL);
|
ent_enum->bmap = bitmap_zalloc(idx_max, GFP_KERNEL);
|
||||||
if (!ent_enum->bmap)
|
if (!ent_enum->bmap)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -71,7 +73,7 @@ __must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum,
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__media_entity_enum_init);
|
EXPORT_SYMBOL_GPL(media_entity_enum_init);
|
||||||
|
|
||||||
void media_entity_enum_cleanup(struct media_entity_enum *ent_enum)
|
void media_entity_enum_cleanup(struct media_entity_enum *ent_enum)
|
||||||
{
|
{
|
||||||
@ -193,7 +195,8 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
|
|||||||
struct media_pad *pads)
|
struct media_pad *pads)
|
||||||
{
|
{
|
||||||
struct media_device *mdev = entity->graph_obj.mdev;
|
struct media_device *mdev = entity->graph_obj.mdev;
|
||||||
unsigned int i;
|
struct media_pad *iter;
|
||||||
|
unsigned int i = 0;
|
||||||
|
|
||||||
if (num_pads >= MEDIA_ENTITY_MAX_PADS)
|
if (num_pads >= MEDIA_ENTITY_MAX_PADS)
|
||||||
return -E2BIG;
|
return -E2BIG;
|
||||||
@ -204,12 +207,12 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
|
|||||||
if (mdev)
|
if (mdev)
|
||||||
mutex_lock(&mdev->graph_mutex);
|
mutex_lock(&mdev->graph_mutex);
|
||||||
|
|
||||||
for (i = 0; i < num_pads; i++) {
|
media_entity_for_each_pad(entity, iter) {
|
||||||
pads[i].entity = entity;
|
iter->entity = entity;
|
||||||
pads[i].index = i;
|
iter->index = i++;
|
||||||
if (mdev)
|
if (mdev)
|
||||||
media_gobj_create(mdev, MEDIA_GRAPH_PAD,
|
media_gobj_create(mdev, MEDIA_GRAPH_PAD,
|
||||||
&entity->pads[i].graph_obj);
|
&iter->graph_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mdev)
|
if (mdev)
|
||||||
@ -223,6 +226,33 @@ EXPORT_SYMBOL_GPL(media_entity_pads_init);
|
|||||||
* Graph traversal
|
* Graph traversal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function checks the interdependency inside the entity between @pad0
|
||||||
|
* and @pad1. If two pads are interdependent they are part of the same pipeline
|
||||||
|
* and enabling one of the pads means that the other pad will become "locked"
|
||||||
|
* and doesn't allow configuration changes.
|
||||||
|
*
|
||||||
|
* This function uses the &media_entity_operations.has_pad_interdep() operation
|
||||||
|
* to check the dependency inside the entity between @pad0 and @pad1. If the
|
||||||
|
* has_pad_interdep operation is not implemented, all pads of the entity are
|
||||||
|
* considered to be interdependent.
|
||||||
|
*/
|
||||||
|
static bool media_entity_has_pad_interdep(struct media_entity *entity,
|
||||||
|
unsigned int pad0, unsigned int pad1)
|
||||||
|
{
|
||||||
|
if (pad0 >= entity->num_pads || pad1 >= entity->num_pads)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (entity->pads[pad0].flags & entity->pads[pad1].flags &
|
||||||
|
(MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!entity->ops || !entity->ops->has_pad_interdep)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return entity->ops->has_pad_interdep(entity, pad0, pad1);
|
||||||
|
}
|
||||||
|
|
||||||
static struct media_entity *
|
static struct media_entity *
|
||||||
media_entity_other(struct media_entity *entity, struct media_link *link)
|
media_entity_other(struct media_entity *entity, struct media_link *link)
|
||||||
{
|
{
|
||||||
@ -367,139 +397,435 @@ struct media_entity *media_graph_walk_next(struct media_graph *graph)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(media_graph_walk_next);
|
EXPORT_SYMBOL_GPL(media_graph_walk_next);
|
||||||
|
|
||||||
int media_entity_get_fwnode_pad(struct media_entity *entity,
|
|
||||||
struct fwnode_handle *fwnode,
|
|
||||||
unsigned long direction_flags)
|
|
||||||
{
|
|
||||||
struct fwnode_endpoint endpoint;
|
|
||||||
unsigned int i;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!entity->ops || !entity->ops->get_fwnode_pad) {
|
|
||||||
for (i = 0; i < entity->num_pads; i++) {
|
|
||||||
if (entity->pads[i].flags & direction_flags)
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = fwnode_graph_parse_endpoint(fwnode, &endpoint);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = entity->ops->get_fwnode_pad(entity, &endpoint);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (ret >= entity->num_pads)
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
if (!(entity->pads[ret].flags & direction_flags))
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(media_entity_get_fwnode_pad);
|
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
* Pipeline management
|
* Pipeline management
|
||||||
*/
|
*/
|
||||||
|
|
||||||
__must_check int __media_pipeline_start(struct media_entity *entity,
|
/*
|
||||||
struct media_pipeline *pipe)
|
* The pipeline traversal stack stores pads that are reached during graph
|
||||||
|
* traversal, with a list of links to be visited to continue the traversal.
|
||||||
|
* When a new pad is reached, an entry is pushed on the top of the stack and
|
||||||
|
* points to the incoming pad and the first link of the entity.
|
||||||
|
*
|
||||||
|
* To find further pads in the pipeline, the traversal algorithm follows
|
||||||
|
* internal pad dependencies in the entity, and then links in the graph. It
|
||||||
|
* does so by iterating over all links of the entity, and following enabled
|
||||||
|
* links that originate from a pad that is internally connected to the incoming
|
||||||
|
* pad, as reported by the media_entity_has_pad_interdep() function.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct media_pipeline_walk_entry - Entry in the pipeline traversal stack
|
||||||
|
*
|
||||||
|
* @pad: The media pad being visited
|
||||||
|
* @links: Links left to be visited
|
||||||
|
*/
|
||||||
|
struct media_pipeline_walk_entry {
|
||||||
|
struct media_pad *pad;
|
||||||
|
struct list_head *links;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct media_pipeline_walk - State used by the media pipeline traversal
|
||||||
|
* algorithm
|
||||||
|
*
|
||||||
|
* @mdev: The media device
|
||||||
|
* @stack: Depth-first search stack
|
||||||
|
* @stack.size: Number of allocated entries in @stack.entries
|
||||||
|
* @stack.top: Index of the top stack entry (-1 if the stack is empty)
|
||||||
|
* @stack.entries: Stack entries
|
||||||
|
*/
|
||||||
|
struct media_pipeline_walk {
|
||||||
|
struct media_device *mdev;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
unsigned int size;
|
||||||
|
int top;
|
||||||
|
struct media_pipeline_walk_entry *entries;
|
||||||
|
} stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MEDIA_PIPELINE_STACK_GROW_STEP 16
|
||||||
|
|
||||||
|
static struct media_pipeline_walk_entry *
|
||||||
|
media_pipeline_walk_top(struct media_pipeline_walk *walk)
|
||||||
{
|
{
|
||||||
struct media_device *mdev = entity->graph_obj.mdev;
|
return &walk->stack.entries[walk->stack.top];
|
||||||
struct media_graph *graph = &pipe->graph;
|
}
|
||||||
struct media_entity *entity_err = entity;
|
|
||||||
struct media_link *link;
|
static bool media_pipeline_walk_empty(struct media_pipeline_walk *walk)
|
||||||
|
{
|
||||||
|
return walk->stack.top == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Increase the stack size by MEDIA_PIPELINE_STACK_GROW_STEP elements. */
|
||||||
|
static int media_pipeline_walk_resize(struct media_pipeline_walk *walk)
|
||||||
|
{
|
||||||
|
struct media_pipeline_walk_entry *entries;
|
||||||
|
unsigned int new_size;
|
||||||
|
|
||||||
|
/* Safety check, to avoid stack overflows in case of bugs. */
|
||||||
|
if (walk->stack.size >= 256)
|
||||||
|
return -E2BIG;
|
||||||
|
|
||||||
|
new_size = walk->stack.size + MEDIA_PIPELINE_STACK_GROW_STEP;
|
||||||
|
|
||||||
|
entries = krealloc(walk->stack.entries,
|
||||||
|
new_size * sizeof(*walk->stack.entries),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!entries)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
walk->stack.entries = entries;
|
||||||
|
walk->stack.size = new_size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push a new entry on the stack. */
|
||||||
|
static int media_pipeline_walk_push(struct media_pipeline_walk *walk,
|
||||||
|
struct media_pad *pad)
|
||||||
|
{
|
||||||
|
struct media_pipeline_walk_entry *entry;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (pipe->streaming_count) {
|
if (walk->stack.top + 1 >= walk->stack.size) {
|
||||||
pipe->streaming_count++;
|
ret = media_pipeline_walk_resize(walk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
walk->stack.top++;
|
||||||
|
entry = media_pipeline_walk_top(walk);
|
||||||
|
entry->pad = pad;
|
||||||
|
entry->links = pad->entity->links.next;
|
||||||
|
|
||||||
|
dev_dbg(walk->mdev->dev,
|
||||||
|
"media pipeline: pushed entry %u: '%s':%u\n",
|
||||||
|
walk->stack.top, pad->entity->name, pad->index);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move the top entry link cursor to the next link. If all links of the entry
|
||||||
|
* have been visited, pop the entry itself.
|
||||||
|
*/
|
||||||
|
static void media_pipeline_walk_pop(struct media_pipeline_walk *walk)
|
||||||
|
{
|
||||||
|
struct media_pipeline_walk_entry *entry;
|
||||||
|
|
||||||
|
if (WARN_ON(walk->stack.top < 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
entry = media_pipeline_walk_top(walk);
|
||||||
|
|
||||||
|
if (entry->links->next == &entry->pad->entity->links) {
|
||||||
|
dev_dbg(walk->mdev->dev,
|
||||||
|
"media pipeline: entry %u has no more links, popping\n",
|
||||||
|
walk->stack.top);
|
||||||
|
|
||||||
|
walk->stack.top--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->links = entry->links->next;
|
||||||
|
|
||||||
|
dev_dbg(walk->mdev->dev,
|
||||||
|
"media pipeline: moved entry %u to next link\n",
|
||||||
|
walk->stack.top);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free all memory allocated while walking the pipeline. */
|
||||||
|
static void media_pipeline_walk_destroy(struct media_pipeline_walk *walk)
|
||||||
|
{
|
||||||
|
kfree(walk->stack.entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a pad to the pipeline and push it to the stack. */
|
||||||
|
static int media_pipeline_add_pad(struct media_pipeline *pipe,
|
||||||
|
struct media_pipeline_walk *walk,
|
||||||
|
struct media_pad *pad)
|
||||||
|
{
|
||||||
|
struct media_pipeline_pad *ppad;
|
||||||
|
|
||||||
|
list_for_each_entry(ppad, &pipe->pads, list) {
|
||||||
|
if (ppad->pad == pad) {
|
||||||
|
dev_dbg(pad->graph_obj.mdev->dev,
|
||||||
|
"media pipeline: already contains pad '%s':%u\n",
|
||||||
|
pad->entity->name, pad->index);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ppad = kzalloc(sizeof(*ppad), GFP_KERNEL);
|
||||||
|
if (!ppad)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ppad->pipe = pipe;
|
||||||
|
ppad->pad = pad;
|
||||||
|
|
||||||
|
list_add_tail(&ppad->list, &pipe->pads);
|
||||||
|
|
||||||
|
dev_dbg(pad->graph_obj.mdev->dev,
|
||||||
|
"media pipeline: added pad '%s':%u\n",
|
||||||
|
pad->entity->name, pad->index);
|
||||||
|
|
||||||
|
return media_pipeline_walk_push(walk, pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Explore the next link of the entity at the top of the stack. */
|
||||||
|
static int media_pipeline_explore_next_link(struct media_pipeline *pipe,
|
||||||
|
struct media_pipeline_walk *walk)
|
||||||
|
{
|
||||||
|
struct media_pipeline_walk_entry *entry = media_pipeline_walk_top(walk);
|
||||||
|
struct media_pad *pad;
|
||||||
|
struct media_link *link;
|
||||||
|
struct media_pad *local;
|
||||||
|
struct media_pad *remote;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pad = entry->pad;
|
||||||
|
link = list_entry(entry->links, typeof(*link), list);
|
||||||
|
media_pipeline_walk_pop(walk);
|
||||||
|
|
||||||
|
dev_dbg(walk->mdev->dev,
|
||||||
|
"media pipeline: exploring link '%s':%u -> '%s':%u\n",
|
||||||
|
link->source->entity->name, link->source->index,
|
||||||
|
link->sink->entity->name, link->sink->index);
|
||||||
|
|
||||||
|
/* Skip links that are not enabled. */
|
||||||
|
if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {
|
||||||
|
dev_dbg(walk->mdev->dev,
|
||||||
|
"media pipeline: skipping link (disabled)\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = media_graph_walk_init(&pipe->graph, mdev);
|
/* Get the local pad and remote pad. */
|
||||||
|
if (link->source->entity == pad->entity) {
|
||||||
|
local = link->source;
|
||||||
|
remote = link->sink;
|
||||||
|
} else {
|
||||||
|
local = link->sink;
|
||||||
|
remote = link->source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip links that originate from a different pad than the incoming pad
|
||||||
|
* that is not connected internally in the entity to the incoming pad.
|
||||||
|
*/
|
||||||
|
if (pad != local &&
|
||||||
|
!media_entity_has_pad_interdep(pad->entity, pad->index, local->index)) {
|
||||||
|
dev_dbg(walk->mdev->dev,
|
||||||
|
"media pipeline: skipping link (no route)\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the local and remote pads of the link to the pipeline and push
|
||||||
|
* them to the stack, if they're not already present.
|
||||||
|
*/
|
||||||
|
ret = media_pipeline_add_pad(pipe, walk, local);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
media_graph_walk_start(&pipe->graph, entity);
|
ret = media_pipeline_add_pad(pipe, walk, remote);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
while ((entity = media_graph_walk_next(graph))) {
|
return 0;
|
||||||
DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS);
|
}
|
||||||
DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS);
|
|
||||||
|
|
||||||
if (entity->pipe && entity->pipe != pipe) {
|
static void media_pipeline_cleanup(struct media_pipeline *pipe)
|
||||||
pr_err("Pipe active for %s. Can't start for %s\n",
|
{
|
||||||
entity->name,
|
while (!list_empty(&pipe->pads)) {
|
||||||
entity_err->name);
|
struct media_pipeline_pad *ppad;
|
||||||
|
|
||||||
|
ppad = list_first_entry(&pipe->pads, typeof(*ppad), list);
|
||||||
|
list_del(&ppad->list);
|
||||||
|
kfree(ppad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int media_pipeline_populate(struct media_pipeline *pipe,
|
||||||
|
struct media_pad *pad)
|
||||||
|
{
|
||||||
|
struct media_pipeline_walk walk = { };
|
||||||
|
struct media_pipeline_pad *ppad;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Populate the media pipeline by walking the media graph, starting
|
||||||
|
* from @pad.
|
||||||
|
*/
|
||||||
|
INIT_LIST_HEAD(&pipe->pads);
|
||||||
|
pipe->mdev = pad->graph_obj.mdev;
|
||||||
|
|
||||||
|
walk.mdev = pipe->mdev;
|
||||||
|
walk.stack.top = -1;
|
||||||
|
ret = media_pipeline_add_pad(pipe, &walk, pad);
|
||||||
|
if (ret)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use a depth-first search algorithm: as long as the stack is not
|
||||||
|
* empty, explore the next link of the top entry. The
|
||||||
|
* media_pipeline_explore_next_link() function will either move to the
|
||||||
|
* next link, pop the entry if fully visited, or add new entries on
|
||||||
|
* top.
|
||||||
|
*/
|
||||||
|
while (!media_pipeline_walk_empty(&walk)) {
|
||||||
|
ret = media_pipeline_explore_next_link(pipe, &walk);
|
||||||
|
if (ret)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(pad->graph_obj.mdev->dev,
|
||||||
|
"media pipeline populated, found pads:\n");
|
||||||
|
|
||||||
|
list_for_each_entry(ppad, &pipe->pads, list)
|
||||||
|
dev_dbg(pad->graph_obj.mdev->dev, "- '%s':%u\n",
|
||||||
|
ppad->pad->entity->name, ppad->pad->index);
|
||||||
|
|
||||||
|
WARN_ON(walk.stack.top != -1);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
done:
|
||||||
|
media_pipeline_walk_destroy(&walk);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
media_pipeline_cleanup(pipe);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
__must_check int __media_pipeline_start(struct media_pad *pad,
|
||||||
|
struct media_pipeline *pipe)
|
||||||
|
{
|
||||||
|
struct media_device *mdev = pad->entity->graph_obj.mdev;
|
||||||
|
struct media_pipeline_pad *err_ppad;
|
||||||
|
struct media_pipeline_pad *ppad;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
lockdep_assert_held(&mdev->graph_mutex);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the entity is already part of a pipeline, that pipeline must
|
||||||
|
* be the same as the pipe given to media_pipeline_start().
|
||||||
|
*/
|
||||||
|
if (WARN_ON(pad->pipe && pad->pipe != pipe))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the pipeline has already been started, it is guaranteed to be
|
||||||
|
* valid, so just increase the start count.
|
||||||
|
*/
|
||||||
|
if (pipe->start_count) {
|
||||||
|
pipe->start_count++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Populate the pipeline. This populates the media_pipeline pads list
|
||||||
|
* with media_pipeline_pad instances for each pad found during graph
|
||||||
|
* walk.
|
||||||
|
*/
|
||||||
|
ret = media_pipeline_populate(pipe, pad);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now that all the pads in the pipeline have been gathered, perform
|
||||||
|
* the validation steps.
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_for_each_entry(ppad, &pipe->pads, list) {
|
||||||
|
struct media_pad *pad = ppad->pad;
|
||||||
|
struct media_entity *entity = pad->entity;
|
||||||
|
bool has_enabled_link = false;
|
||||||
|
bool has_link = false;
|
||||||
|
struct media_link *link;
|
||||||
|
|
||||||
|
dev_dbg(mdev->dev, "Validating pad '%s':%u\n", pad->entity->name,
|
||||||
|
pad->index);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 1. Ensure that the pad doesn't already belong to a different
|
||||||
|
* pipeline.
|
||||||
|
*/
|
||||||
|
if (pad->pipe) {
|
||||||
|
dev_dbg(mdev->dev, "Failed to start pipeline: pad '%s':%u busy\n",
|
||||||
|
pad->entity->name, pad->index);
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Already streaming --- no need to check. */
|
/*
|
||||||
if (entity->pipe)
|
* 2. Validate all active links whose sink is the current pad.
|
||||||
continue;
|
* Validation of the source pads is performed in the context of
|
||||||
|
* the connected sink pad to avoid duplicating checks.
|
||||||
entity->pipe = pipe;
|
*/
|
||||||
|
|
||||||
if (!entity->ops || !entity->ops->link_validate)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bitmap_zero(active, entity->num_pads);
|
|
||||||
bitmap_fill(has_no_links, entity->num_pads);
|
|
||||||
|
|
||||||
for_each_media_entity_data_link(entity, link) {
|
for_each_media_entity_data_link(entity, link) {
|
||||||
struct media_pad *pad = link->sink->entity == entity
|
/* Skip links unrelated to the current pad. */
|
||||||
? link->sink : link->source;
|
if (link->sink != pad && link->source != pad)
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Mark that a pad is connected by a link. */
|
/* Record if the pad has links and enabled links. */
|
||||||
bitmap_clear(has_no_links, pad->index, 1);
|
if (link->flags & MEDIA_LNK_FL_ENABLED)
|
||||||
|
has_enabled_link = true;
|
||||||
|
has_link = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pads that either do not need to connect or
|
* Validate the link if it's enabled and has the
|
||||||
* are connected through an enabled link are
|
* current pad as its sink.
|
||||||
* fine.
|
|
||||||
*/
|
*/
|
||||||
if (!(pad->flags & MEDIA_PAD_FL_MUST_CONNECT) ||
|
if (!(link->flags & MEDIA_LNK_FL_ENABLED))
|
||||||
link->flags & MEDIA_LNK_FL_ENABLED)
|
continue;
|
||||||
bitmap_set(active, pad->index, 1);
|
|
||||||
|
|
||||||
/*
|
if (link->sink != pad)
|
||||||
* Link validation will only take place for
|
continue;
|
||||||
* sink ends of the link that are enabled.
|
|
||||||
*/
|
if (!entity->ops || !entity->ops->link_validate)
|
||||||
if (link->sink != pad ||
|
|
||||||
!(link->flags & MEDIA_LNK_FL_ENABLED))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ret = entity->ops->link_validate(link);
|
ret = entity->ops->link_validate(link);
|
||||||
if (ret < 0 && ret != -ENOIOCTLCMD) {
|
if (ret) {
|
||||||
dev_dbg(entity->graph_obj.mdev->dev,
|
dev_dbg(mdev->dev,
|
||||||
"link validation failed for '%s':%u -> '%s':%u, error %d\n",
|
"Link '%s':%u -> '%s':%u failed validation: %d\n",
|
||||||
link->source->entity->name,
|
link->source->entity->name,
|
||||||
link->source->index,
|
link->source->index,
|
||||||
entity->name, link->sink->index, ret);
|
link->sink->entity->name,
|
||||||
|
link->sink->index, ret);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev_dbg(mdev->dev,
|
||||||
|
"Link '%s':%u -> '%s':%u is valid\n",
|
||||||
|
link->source->entity->name,
|
||||||
|
link->source->index,
|
||||||
|
link->sink->entity->name,
|
||||||
|
link->sink->index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Either no links or validated links are fine. */
|
/*
|
||||||
bitmap_or(active, active, has_no_links, entity->num_pads);
|
* 3. If the pad has the MEDIA_PAD_FL_MUST_CONNECT flag set,
|
||||||
|
* ensure that it has either no link or an enabled link.
|
||||||
if (!bitmap_full(active, entity->num_pads)) {
|
*/
|
||||||
|
if ((pad->flags & MEDIA_PAD_FL_MUST_CONNECT) && has_link &&
|
||||||
|
!has_enabled_link) {
|
||||||
|
dev_dbg(mdev->dev,
|
||||||
|
"Pad '%s':%u must be connected by an enabled link\n",
|
||||||
|
pad->entity->name, pad->index);
|
||||||
ret = -ENOLINK;
|
ret = -ENOLINK;
|
||||||
dev_dbg(entity->graph_obj.mdev->dev,
|
|
||||||
"'%s':%u must be connected by an enabled link\n",
|
|
||||||
entity->name,
|
|
||||||
(unsigned)find_first_zero_bit(
|
|
||||||
active, entity->num_pads));
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Validation passed, store the pipe pointer in the pad. */
|
||||||
|
pad->pipe = pipe;
|
||||||
}
|
}
|
||||||
|
|
||||||
pipe->streaming_count++;
|
pipe->start_count++;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -508,42 +834,37 @@ error:
|
|||||||
* Link validation on graph failed. We revert what we did and
|
* Link validation on graph failed. We revert what we did and
|
||||||
* return the error.
|
* return the error.
|
||||||
*/
|
*/
|
||||||
media_graph_walk_start(graph, entity_err);
|
|
||||||
|
|
||||||
while ((entity_err = media_graph_walk_next(graph))) {
|
list_for_each_entry(err_ppad, &pipe->pads, list) {
|
||||||
entity_err->pipe = NULL;
|
if (err_ppad == ppad)
|
||||||
|
|
||||||
/*
|
|
||||||
* We haven't started entities further than this so we quit
|
|
||||||
* here.
|
|
||||||
*/
|
|
||||||
if (entity_err == entity)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
err_ppad->pad->pipe = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
media_graph_walk_cleanup(graph);
|
media_pipeline_cleanup(pipe);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__media_pipeline_start);
|
EXPORT_SYMBOL_GPL(__media_pipeline_start);
|
||||||
|
|
||||||
__must_check int media_pipeline_start(struct media_entity *entity,
|
__must_check int media_pipeline_start(struct media_pad *pad,
|
||||||
struct media_pipeline *pipe)
|
struct media_pipeline *pipe)
|
||||||
{
|
{
|
||||||
struct media_device *mdev = entity->graph_obj.mdev;
|
struct media_device *mdev = pad->entity->graph_obj.mdev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&mdev->graph_mutex);
|
mutex_lock(&mdev->graph_mutex);
|
||||||
ret = __media_pipeline_start(entity, pipe);
|
ret = __media_pipeline_start(pad, pipe);
|
||||||
mutex_unlock(&mdev->graph_mutex);
|
mutex_unlock(&mdev->graph_mutex);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(media_pipeline_start);
|
EXPORT_SYMBOL_GPL(media_pipeline_start);
|
||||||
|
|
||||||
void __media_pipeline_stop(struct media_entity *entity)
|
void __media_pipeline_stop(struct media_pad *pad)
|
||||||
{
|
{
|
||||||
struct media_graph *graph = &entity->pipe->graph;
|
struct media_pipeline *pipe = pad->pipe;
|
||||||
struct media_pipeline *pipe = entity->pipe;
|
struct media_pipeline_pad *ppad;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the following check fails, the driver has performed an
|
* If the following check fails, the driver has performed an
|
||||||
@ -552,29 +873,65 @@ void __media_pipeline_stop(struct media_entity *entity)
|
|||||||
if (WARN_ON(!pipe))
|
if (WARN_ON(!pipe))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (--pipe->streaming_count)
|
if (--pipe->start_count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
media_graph_walk_start(graph, entity);
|
list_for_each_entry(ppad, &pipe->pads, list)
|
||||||
|
ppad->pad->pipe = NULL;
|
||||||
|
|
||||||
while ((entity = media_graph_walk_next(graph)))
|
media_pipeline_cleanup(pipe);
|
||||||
entity->pipe = NULL;
|
|
||||||
|
|
||||||
media_graph_walk_cleanup(graph);
|
|
||||||
|
|
||||||
|
if (pipe->allocated)
|
||||||
|
kfree(pipe);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__media_pipeline_stop);
|
EXPORT_SYMBOL_GPL(__media_pipeline_stop);
|
||||||
|
|
||||||
void media_pipeline_stop(struct media_entity *entity)
|
void media_pipeline_stop(struct media_pad *pad)
|
||||||
{
|
{
|
||||||
struct media_device *mdev = entity->graph_obj.mdev;
|
struct media_device *mdev = pad->entity->graph_obj.mdev;
|
||||||
|
|
||||||
mutex_lock(&mdev->graph_mutex);
|
mutex_lock(&mdev->graph_mutex);
|
||||||
__media_pipeline_stop(entity);
|
__media_pipeline_stop(pad);
|
||||||
mutex_unlock(&mdev->graph_mutex);
|
mutex_unlock(&mdev->graph_mutex);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(media_pipeline_stop);
|
EXPORT_SYMBOL_GPL(media_pipeline_stop);
|
||||||
|
|
||||||
|
__must_check int media_pipeline_alloc_start(struct media_pad *pad)
|
||||||
|
{
|
||||||
|
struct media_device *mdev = pad->entity->graph_obj.mdev;
|
||||||
|
struct media_pipeline *new_pipe = NULL;
|
||||||
|
struct media_pipeline *pipe;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&mdev->graph_mutex);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is the entity already part of a pipeline? If not, we need to allocate
|
||||||
|
* a pipe.
|
||||||
|
*/
|
||||||
|
pipe = media_pad_pipeline(pad);
|
||||||
|
if (!pipe) {
|
||||||
|
new_pipe = kzalloc(sizeof(*new_pipe), GFP_KERNEL);
|
||||||
|
if (!new_pipe) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe = new_pipe;
|
||||||
|
pipe->allocated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = __media_pipeline_start(pad, pipe);
|
||||||
|
if (ret)
|
||||||
|
kfree(new_pipe);
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&mdev->graph_mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(media_pipeline_alloc_start);
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
* Links management
|
* Links management
|
||||||
*/
|
*/
|
||||||
@ -829,7 +1186,7 @@ int __media_entity_setup_link(struct media_link *link, u32 flags)
|
|||||||
{
|
{
|
||||||
const u32 mask = MEDIA_LNK_FL_ENABLED;
|
const u32 mask = MEDIA_LNK_FL_ENABLED;
|
||||||
struct media_device *mdev;
|
struct media_device *mdev;
|
||||||
struct media_entity *source, *sink;
|
struct media_pad *source, *sink;
|
||||||
int ret = -EBUSY;
|
int ret = -EBUSY;
|
||||||
|
|
||||||
if (link == NULL)
|
if (link == NULL)
|
||||||
@ -845,12 +1202,11 @@ int __media_entity_setup_link(struct media_link *link, u32 flags)
|
|||||||
if (link->flags == flags)
|
if (link->flags == flags)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
source = link->source->entity;
|
source = link->source;
|
||||||
sink = link->sink->entity;
|
sink = link->sink;
|
||||||
|
|
||||||
if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) &&
|
if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) &&
|
||||||
(media_entity_is_streaming(source) ||
|
(media_pad_is_streaming(source) || media_pad_is_streaming(sink)))
|
||||||
media_entity_is_streaming(sink)))
|
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
mdev = source->graph_obj.mdev;
|
mdev = source->graph_obj.mdev;
|
||||||
@ -991,6 +1347,60 @@ struct media_pad *media_pad_remote_pad_unique(const struct media_pad *pad)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(media_pad_remote_pad_unique);
|
EXPORT_SYMBOL_GPL(media_pad_remote_pad_unique);
|
||||||
|
|
||||||
|
int media_entity_get_fwnode_pad(struct media_entity *entity,
|
||||||
|
struct fwnode_handle *fwnode,
|
||||||
|
unsigned long direction_flags)
|
||||||
|
{
|
||||||
|
struct fwnode_endpoint endpoint;
|
||||||
|
unsigned int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!entity->ops || !entity->ops->get_fwnode_pad) {
|
||||||
|
for (i = 0; i < entity->num_pads; i++) {
|
||||||
|
if (entity->pads[i].flags & direction_flags)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fwnode_graph_parse_endpoint(fwnode, &endpoint);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = entity->ops->get_fwnode_pad(entity, &endpoint);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (ret >= entity->num_pads)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
if (!(entity->pads[ret].flags & direction_flags))
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(media_entity_get_fwnode_pad);
|
||||||
|
|
||||||
|
struct media_pipeline *media_entity_pipeline(struct media_entity *entity)
|
||||||
|
{
|
||||||
|
struct media_pad *pad;
|
||||||
|
|
||||||
|
media_entity_for_each_pad(entity, pad) {
|
||||||
|
if (pad->pipe)
|
||||||
|
return pad->pipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(media_entity_pipeline);
|
||||||
|
|
||||||
|
struct media_pipeline *media_pad_pipeline(struct media_pad *pad)
|
||||||
|
{
|
||||||
|
return pad->pipe;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(media_pad_pipeline);
|
||||||
|
|
||||||
static void media_interface_init(struct media_device *mdev,
|
static void media_interface_init(struct media_device *mdev,
|
||||||
struct media_interface *intf,
|
struct media_interface *intf,
|
||||||
u32 gobj_type,
|
u32 gobj_type,
|
||||||
|
@ -339,7 +339,7 @@ void cx18_av_std_setup(struct cx18 *cx)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* For a 13.5 Mpps clock and 15,625 Hz line rate, a line is
|
* For a 13.5 Mpps clock and 15,625 Hz line rate, a line is
|
||||||
* is 864 pixels = 720 active + 144 blanking. ITU-R BT.601
|
* 864 pixels = 720 active + 144 blanking. ITU-R BT.601
|
||||||
* specifies 12 luma clock periods or ~ 0.9 * 13.5 Mpps after
|
* specifies 12 luma clock periods or ~ 0.9 * 13.5 Mpps after
|
||||||
* the end of active video to start a horizontal line, so that
|
* the end of active video to start a horizontal line, so that
|
||||||
* leaves 132 pixels of hblank to ignore.
|
* leaves 132 pixels of hblank to ignore.
|
||||||
@ -399,7 +399,7 @@ void cx18_av_std_setup(struct cx18 *cx)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* For a 13.5 Mpps clock and 15,734.26 Hz line rate, a line is
|
* For a 13.5 Mpps clock and 15,734.26 Hz line rate, a line is
|
||||||
* is 858 pixels = 720 active + 138 blanking. The Hsync leading
|
* 858 pixels = 720 active + 138 blanking. The Hsync leading
|
||||||
* edge should happen 1.2 us * 13.5 Mpps ~= 16 pixels after the
|
* edge should happen 1.2 us * 13.5 Mpps ~= 16 pixels after the
|
||||||
* end of active video, leaving 122 pixels of hblank to ignore
|
* end of active video, leaving 122 pixels of hblank to ignore
|
||||||
* before active video starts.
|
* before active video starts.
|
||||||
|
@ -586,7 +586,7 @@ void cx88_i2c_init_ir(struct cx88_core *core)
|
|||||||
{
|
{
|
||||||
struct i2c_board_info info;
|
struct i2c_board_info info;
|
||||||
static const unsigned short default_addr_list[] = {
|
static const unsigned short default_addr_list[] = {
|
||||||
0x18, 0x6b, 0x71,
|
0x18, 0x33, 0x6b, 0x71,
|
||||||
I2C_CLIENT_END
|
I2C_CLIENT_END
|
||||||
};
|
};
|
||||||
static const unsigned short pvr2000_addr_list[] = {
|
static const unsigned short pvr2000_addr_list[] = {
|
||||||
|
@ -1388,6 +1388,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
|
|||||||
}
|
}
|
||||||
fallthrough;
|
fallthrough;
|
||||||
case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
|
case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
|
||||||
|
case CX88_BOARD_NOTONLYTV_LV3H:
|
||||||
request_module("ir-kbd-i2c");
|
request_module("ir-kbd-i2c");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -989,7 +989,7 @@ static int cio2_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = media_pipeline_start(&q->vdev.entity, &q->pipe);
|
r = video_device_pipeline_start(&q->vdev, &q->pipe);
|
||||||
if (r)
|
if (r)
|
||||||
goto fail_pipeline;
|
goto fail_pipeline;
|
||||||
|
|
||||||
@ -1009,7 +1009,7 @@ static int cio2_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|||||||
fail_csi2_subdev:
|
fail_csi2_subdev:
|
||||||
cio2_hw_exit(cio2, q);
|
cio2_hw_exit(cio2, q);
|
||||||
fail_hw:
|
fail_hw:
|
||||||
media_pipeline_stop(&q->vdev.entity);
|
video_device_pipeline_stop(&q->vdev);
|
||||||
fail_pipeline:
|
fail_pipeline:
|
||||||
dev_dbg(dev, "failed to start streaming (%d)\n", r);
|
dev_dbg(dev, "failed to start streaming (%d)\n", r);
|
||||||
cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_QUEUED);
|
cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_QUEUED);
|
||||||
@ -1030,7 +1030,7 @@ static void cio2_vb2_stop_streaming(struct vb2_queue *vq)
|
|||||||
cio2_hw_exit(cio2, q);
|
cio2_hw_exit(cio2, q);
|
||||||
synchronize_irq(cio2->pci_dev->irq);
|
synchronize_irq(cio2->pci_dev->irq);
|
||||||
cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_ERROR);
|
cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_ERROR);
|
||||||
media_pipeline_stop(&q->vdev.entity);
|
video_device_pipeline_stop(&q->vdev);
|
||||||
pm_runtime_put(dev);
|
pm_runtime_put(dev);
|
||||||
cio2->streaming = false;
|
cio2->streaming = false;
|
||||||
}
|
}
|
||||||
|
@ -603,6 +603,10 @@ static int vpu_v4l2_release(struct vpu_inst *inst)
|
|||||||
inst->workqueue = NULL;
|
inst->workqueue = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inst->fh.m2m_ctx) {
|
||||||
|
v4l2_m2m_ctx_release(inst->fh.m2m_ctx);
|
||||||
|
inst->fh.m2m_ctx = NULL;
|
||||||
|
}
|
||||||
v4l2_ctrl_handler_free(&inst->ctrl_handler);
|
v4l2_ctrl_handler_free(&inst->ctrl_handler);
|
||||||
mutex_destroy(&inst->lock);
|
mutex_destroy(&inst->lock);
|
||||||
v4l2_fh_del(&inst->fh);
|
v4l2_fh_del(&inst->fh);
|
||||||
@ -685,13 +689,6 @@ int vpu_v4l2_close(struct file *file)
|
|||||||
|
|
||||||
vpu_trace(vpu->dev, "tgid = %d, pid = %d, inst = %p\n", inst->tgid, inst->pid, inst);
|
vpu_trace(vpu->dev, "tgid = %d, pid = %d, inst = %p\n", inst->tgid, inst->pid, inst);
|
||||||
|
|
||||||
vpu_inst_lock(inst);
|
|
||||||
if (inst->fh.m2m_ctx) {
|
|
||||||
v4l2_m2m_ctx_release(inst->fh.m2m_ctx);
|
|
||||||
inst->fh.m2m_ctx = NULL;
|
|
||||||
}
|
|
||||||
vpu_inst_unlock(inst);
|
|
||||||
|
|
||||||
call_void_vop(inst, release);
|
call_void_vop(inst, release);
|
||||||
vpu_inst_unregister(inst);
|
vpu_inst_unregister(inst);
|
||||||
vpu_inst_put(inst);
|
vpu_inst_put(inst);
|
||||||
|
@ -421,7 +421,7 @@ static inline void coda9_jpeg_write_huff_values(struct coda_dev *dev, u8 *bits,
|
|||||||
coda_write(dev, (s32)values[i], CODA9_REG_JPEG_HUFF_DATA);
|
coda_write(dev, (s32)values[i], CODA9_REG_JPEG_HUFF_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coda9_jpeg_dec_huff_setup(struct coda_ctx *ctx)
|
static void coda9_jpeg_dec_huff_setup(struct coda_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab;
|
struct coda_huff_tab *huff_tab = ctx->params.jpeg_huff_tab;
|
||||||
struct coda_dev *dev = ctx->dev;
|
struct coda_dev *dev = ctx->dev;
|
||||||
@ -455,7 +455,6 @@ static int coda9_jpeg_dec_huff_setup(struct coda_ctx *ctx)
|
|||||||
coda9_jpeg_write_huff_values(dev, huff_tab->luma_ac, 162);
|
coda9_jpeg_write_huff_values(dev, huff_tab->luma_ac, 162);
|
||||||
coda9_jpeg_write_huff_values(dev, huff_tab->chroma_ac, 162);
|
coda9_jpeg_write_huff_values(dev, huff_tab->chroma_ac, 162);
|
||||||
coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_CTRL);
|
coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_CTRL);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void coda9_jpeg_write_qmat_tab(struct coda_dev *dev,
|
static inline void coda9_jpeg_write_qmat_tab(struct coda_dev *dev,
|
||||||
@ -1394,14 +1393,8 @@ static int coda9_jpeg_prepare_decode(struct coda_ctx *ctx)
|
|||||||
coda_write(dev, ctx->params.jpeg_restart_interval,
|
coda_write(dev, ctx->params.jpeg_restart_interval,
|
||||||
CODA9_REG_JPEG_RST_INTVAL);
|
CODA9_REG_JPEG_RST_INTVAL);
|
||||||
|
|
||||||
if (ctx->params.jpeg_huff_tab) {
|
if (ctx->params.jpeg_huff_tab)
|
||||||
ret = coda9_jpeg_dec_huff_setup(ctx);
|
coda9_jpeg_dec_huff_setup(ctx);
|
||||||
if (ret < 0) {
|
|
||||||
v4l2_err(&dev->v4l2_dev,
|
|
||||||
"failed to set up Huffman tables: %d\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
coda9_jpeg_qmat_setup(ctx);
|
coda9_jpeg_qmat_setup(ctx);
|
||||||
|
|
||||||
|
@ -457,7 +457,7 @@ err_cmdq_data:
|
|||||||
kfree(path);
|
kfree(path);
|
||||||
atomic_dec(&mdp->job_count);
|
atomic_dec(&mdp->job_count);
|
||||||
wake_up(&mdp->callback_wq);
|
wake_up(&mdp->callback_wq);
|
||||||
if (cmd->pkt.buf_size > 0)
|
if (cmd && cmd->pkt.buf_size > 0)
|
||||||
mdp_cmdq_pkt_destroy(&cmd->pkt);
|
mdp_cmdq_pkt_destroy(&cmd->pkt);
|
||||||
kfree(comps);
|
kfree(comps);
|
||||||
kfree(cmd);
|
kfree(cmd);
|
||||||
|
@ -682,7 +682,7 @@ int mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp)
|
|||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
if (comp->comp_dev) {
|
if (comp->comp_dev) {
|
||||||
ret = pm_runtime_get_sync(comp->comp_dev);
|
ret = pm_runtime_resume_and_get(comp->comp_dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev,
|
dev_err(dev,
|
||||||
"Failed to get power, err %d. type:%d id:%d\n",
|
"Failed to get power, err %d. type:%d id:%d\n",
|
||||||
@ -699,6 +699,7 @@ int mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp)
|
|||||||
dev_err(dev,
|
dev_err(dev,
|
||||||
"Failed to enable clk %d. type:%d id:%d\n",
|
"Failed to enable clk %d. type:%d id:%d\n",
|
||||||
i, comp->type, comp->id);
|
i, comp->type, comp->id);
|
||||||
|
pm_runtime_put(comp->comp_dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -869,7 +870,7 @@ static struct mdp_comp *mdp_comp_create(struct mdp_dev *mdp,
|
|||||||
|
|
||||||
ret = mdp_comp_init(mdp, node, comp, id);
|
ret = mdp_comp_init(mdp, node, comp, id);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kfree(comp);
|
devm_kfree(dev, comp);
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
mdp->comp[id] = comp;
|
mdp->comp[id] = comp;
|
||||||
@ -930,7 +931,7 @@ void mdp_comp_destroy(struct mdp_dev *mdp)
|
|||||||
if (mdp->comp[i]) {
|
if (mdp->comp[i]) {
|
||||||
pm_runtime_disable(mdp->comp[i]->comp_dev);
|
pm_runtime_disable(mdp->comp[i]->comp_dev);
|
||||||
mdp_comp_deinit(mdp->comp[i]);
|
mdp_comp_deinit(mdp->comp[i]);
|
||||||
kfree(mdp->comp[i]);
|
devm_kfree(mdp->comp[i]->comp_dev, mdp->comp[i]);
|
||||||
mdp->comp[i] = NULL;
|
mdp->comp[i] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,7 +289,8 @@ err_deinit_comp:
|
|||||||
mdp_comp_destroy(mdp);
|
mdp_comp_destroy(mdp);
|
||||||
err_return:
|
err_return:
|
||||||
for (i = 0; i < MDP_PIPE_MAX; i++)
|
for (i = 0; i < MDP_PIPE_MAX; i++)
|
||||||
mtk_mutex_put(mdp->mdp_mutex[i]);
|
if (mdp)
|
||||||
|
mtk_mutex_put(mdp->mdp_mutex[i]);
|
||||||
kfree(mdp);
|
kfree(mdp);
|
||||||
dev_dbg(dev, "Errno %d\n", ret);
|
dev_dbg(dev, "Errno %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -173,7 +173,8 @@ int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct mtk_scp *scp,
|
|||||||
/* vpu work_size was set in mdp_vpu_ipi_handle_init_ack */
|
/* vpu work_size was set in mdp_vpu_ipi_handle_init_ack */
|
||||||
|
|
||||||
mem_size = vpu_alloc_size;
|
mem_size = vpu_alloc_size;
|
||||||
if (mdp_vpu_shared_mem_alloc(vpu)) {
|
err = mdp_vpu_shared_mem_alloc(vpu);
|
||||||
|
if (err) {
|
||||||
dev_err(&mdp->pdev->dev, "VPU memory alloc fail!");
|
dev_err(&mdp->pdev->dev, "VPU memory alloc fail!");
|
||||||
goto err_mem_alloc;
|
goto err_mem_alloc;
|
||||||
}
|
}
|
||||||
|
@ -373,7 +373,7 @@ static const struct v4l2_ctrl_ops dw100_ctrl_ops = {
|
|||||||
* The coordinates are saved in UQ12.4 fixed point format.
|
* The coordinates are saved in UQ12.4 fixed point format.
|
||||||
*/
|
*/
|
||||||
static void dw100_ctrl_dewarping_map_init(const struct v4l2_ctrl *ctrl,
|
static void dw100_ctrl_dewarping_map_init(const struct v4l2_ctrl *ctrl,
|
||||||
u32 from_idx, u32 elems,
|
u32 from_idx,
|
||||||
union v4l2_ctrl_ptr ptr)
|
union v4l2_ctrl_ptr ptr)
|
||||||
{
|
{
|
||||||
struct dw100_ctx *ctx =
|
struct dw100_ctx *ctx =
|
||||||
@ -398,7 +398,7 @@ static void dw100_ctrl_dewarping_map_init(const struct v4l2_ctrl *ctrl,
|
|||||||
ctx->map_height = mh;
|
ctx->map_height = mh;
|
||||||
ctx->map_size = mh * mw * sizeof(u32);
|
ctx->map_size = mh * mw * sizeof(u32);
|
||||||
|
|
||||||
for (idx = from_idx; idx < elems; idx++) {
|
for (idx = from_idx; idx < ctrl->elems; idx++) {
|
||||||
qy = min_t(u32, (idx / mw) * qdy, qsh);
|
qy = min_t(u32, (idx / mw) * qdy, qsh);
|
||||||
qx = min_t(u32, (idx % mw) * qdx, qsw);
|
qx = min_t(u32, (idx % mw) * qdx, qsw);
|
||||||
map[idx] = dw100_map_format_coordinates(qx, qy);
|
map[idx] = dw100_map_format_coordinates(qx, qy);
|
||||||
|
@ -493,7 +493,7 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
|
|||||||
struct v4l2_subdev *subdev;
|
struct v4l2_subdev *subdev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = media_pipeline_start(&vdev->entity, &video->pipe);
|
ret = video_device_pipeline_start(vdev, &video->pipe);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -522,7 +522,7 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
media_pipeline_stop(&vdev->entity);
|
video_device_pipeline_stop(vdev);
|
||||||
|
|
||||||
video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
|
video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
|
||||||
|
|
||||||
@ -553,7 +553,7 @@ static void video_stop_streaming(struct vb2_queue *q)
|
|||||||
v4l2_subdev_call(subdev, video, s_stream, 0);
|
v4l2_subdev_call(subdev, video, s_stream, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
media_pipeline_stop(&vdev->entity);
|
video_device_pipeline_stop(vdev);
|
||||||
|
|
||||||
video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
|
video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
|
||||||
}
|
}
|
||||||
|
@ -1800,7 +1800,7 @@ bool venus_helper_check_format(struct venus_inst *inst, u32 v4l2_pixfmt)
|
|||||||
struct venus_core *core = inst->core;
|
struct venus_core *core = inst->core;
|
||||||
u32 fmt = to_hfi_raw_fmt(v4l2_pixfmt);
|
u32 fmt = to_hfi_raw_fmt(v4l2_pixfmt);
|
||||||
struct hfi_plat_caps *caps;
|
struct hfi_plat_caps *caps;
|
||||||
u32 buftype;
|
bool found;
|
||||||
|
|
||||||
if (!fmt)
|
if (!fmt)
|
||||||
return false;
|
return false;
|
||||||
@ -1809,12 +1809,13 @@ bool venus_helper_check_format(struct venus_inst *inst, u32 v4l2_pixfmt)
|
|||||||
if (!caps)
|
if (!caps)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (inst->session_type == VIDC_SESSION_TYPE_DEC)
|
found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, fmt);
|
||||||
buftype = HFI_BUFFER_OUTPUT2;
|
if (found)
|
||||||
else
|
goto done;
|
||||||
buftype = HFI_BUFFER_OUTPUT;
|
|
||||||
|
|
||||||
return find_fmt_from_caps(caps, buftype, fmt);
|
found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, fmt);
|
||||||
|
done:
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(venus_helper_check_format);
|
EXPORT_SYMBOL_GPL(venus_helper_check_format);
|
||||||
|
|
||||||
|
@ -569,8 +569,6 @@ irqreturn_t hfi_isr(int irq, void *dev)
|
|||||||
|
|
||||||
int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops)
|
int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!ops)
|
if (!ops)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -579,9 +577,8 @@ int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops)
|
|||||||
core->state = CORE_UNINIT;
|
core->state = CORE_UNINIT;
|
||||||
init_completion(&core->done);
|
init_completion(&core->done);
|
||||||
pkt_set_version(core->res->hfi_version);
|
pkt_set_version(core->res->hfi_version);
|
||||||
ret = venus_hfi_create(core);
|
|
||||||
|
|
||||||
return ret;
|
return venus_hfi_create(core);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hfi_destroy(struct venus_core *core)
|
void hfi_destroy(struct venus_core *core)
|
||||||
|
@ -183,6 +183,8 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
|
|||||||
else
|
else
|
||||||
return NULL;
|
return NULL;
|
||||||
fmt = find_format(inst, pixmp->pixelformat, f->type);
|
fmt = find_format(inst, pixmp->pixelformat, f->type);
|
||||||
|
if (!fmt)
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pixmp->width = clamp(pixmp->width, frame_width_min(inst),
|
pixmp->width = clamp(pixmp->width, frame_width_min(inst),
|
||||||
|
@ -192,10 +192,8 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
|
|||||||
pixmp->height = clamp(pixmp->height, frame_height_min(inst),
|
pixmp->height = clamp(pixmp->height, frame_height_min(inst),
|
||||||
frame_height_max(inst));
|
frame_height_max(inst));
|
||||||
|
|
||||||
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
|
pixmp->width = ALIGN(pixmp->width, 128);
|
||||||
pixmp->width = ALIGN(pixmp->width, 128);
|
pixmp->height = ALIGN(pixmp->height, 32);
|
||||||
pixmp->height = ALIGN(pixmp->height, 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
pixmp->width = ALIGN(pixmp->width, 2);
|
pixmp->width = ALIGN(pixmp->width, 2);
|
||||||
pixmp->height = ALIGN(pixmp->height, 2);
|
pixmp->height = ALIGN(pixmp->height, 2);
|
||||||
@ -392,7 +390,7 @@ static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
|
|||||||
struct v4l2_fract *timeperframe = &out->timeperframe;
|
struct v4l2_fract *timeperframe = &out->timeperframe;
|
||||||
u64 us_per_frame, fps;
|
u64 us_per_frame, fps;
|
||||||
|
|
||||||
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
|
if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
|
||||||
a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
|
a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -424,7 +422,7 @@ static int venc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
|
|||||||
{
|
{
|
||||||
struct venus_inst *inst = to_inst(file);
|
struct venus_inst *inst = to_inst(file);
|
||||||
|
|
||||||
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
|
if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
|
||||||
a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
|
a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -509,6 +507,19 @@ static int venc_enum_frameintervals(struct file *file, void *fh,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int venc_subscribe_event(struct v4l2_fh *fh,
|
||||||
|
const struct v4l2_event_subscription *sub)
|
||||||
|
{
|
||||||
|
switch (sub->type) {
|
||||||
|
case V4L2_EVENT_EOS:
|
||||||
|
return v4l2_event_subscribe(fh, sub, 2, NULL);
|
||||||
|
case V4L2_EVENT_CTRL:
|
||||||
|
return v4l2_ctrl_subscribe_event(fh, sub);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const struct v4l2_ioctl_ops venc_ioctl_ops = {
|
static const struct v4l2_ioctl_ops venc_ioctl_ops = {
|
||||||
.vidioc_querycap = venc_querycap,
|
.vidioc_querycap = venc_querycap,
|
||||||
.vidioc_enum_fmt_vid_cap = venc_enum_fmt,
|
.vidioc_enum_fmt_vid_cap = venc_enum_fmt,
|
||||||
@ -534,8 +545,9 @@ static const struct v4l2_ioctl_ops venc_ioctl_ops = {
|
|||||||
.vidioc_g_parm = venc_g_parm,
|
.vidioc_g_parm = venc_g_parm,
|
||||||
.vidioc_enum_framesizes = venc_enum_framesizes,
|
.vidioc_enum_framesizes = venc_enum_framesizes,
|
||||||
.vidioc_enum_frameintervals = venc_enum_frameintervals,
|
.vidioc_enum_frameintervals = venc_enum_frameintervals,
|
||||||
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
.vidioc_subscribe_event = venc_subscribe_event,
|
||||||
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
||||||
|
.vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int venc_pm_get(struct venus_inst *inst)
|
static int venc_pm_get(struct venus_inst *inst)
|
||||||
@ -686,7 +698,8 @@ static int venc_set_properties(struct venus_inst *inst)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) {
|
if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC &&
|
||||||
|
ctr->profile.hevc == V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10) {
|
||||||
struct hfi_hdr10_pq_sei hdr10;
|
struct hfi_hdr10_pq_sei hdr10;
|
||||||
unsigned int c;
|
unsigned int c;
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "venc.h"
|
#include "venc.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
#define BITRATE_MIN 32000
|
#define BITRATE_MIN 32000
|
||||||
#define BITRATE_MAX 160000000
|
#define BITRATE_MAX 160000000
|
||||||
@ -336,8 +337,6 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
|
|||||||
* if we disable 8x8 transform for HP.
|
* if we disable 8x8 transform for HP.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (ctrl->val == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
ctr->h264_8x8_transform = ctrl->val;
|
ctr->h264_8x8_transform = ctrl->val;
|
||||||
break;
|
break;
|
||||||
@ -348,15 +347,41 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int venc_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
||||||
|
{
|
||||||
|
struct venus_inst *inst = ctrl_to_inst(ctrl);
|
||||||
|
struct hfi_buffer_requirements bufreq;
|
||||||
|
enum hfi_version ver = inst->core->res->hfi_version;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (ctrl->id) {
|
||||||
|
case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
|
||||||
|
ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq);
|
||||||
|
if (!ret)
|
||||||
|
ctrl->val = HFI_BUFREQ_COUNT_MIN(&bufreq, ver);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct v4l2_ctrl_ops venc_ctrl_ops = {
|
static const struct v4l2_ctrl_ops venc_ctrl_ops = {
|
||||||
.s_ctrl = venc_op_s_ctrl,
|
.s_ctrl = venc_op_s_ctrl,
|
||||||
|
.g_volatile_ctrl = venc_op_g_volatile_ctrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
int venc_ctrl_init(struct venus_inst *inst)
|
int venc_ctrl_init(struct venus_inst *inst)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
struct v4l2_ctrl_hdr10_mastering_display p_hdr10_mastering = {
|
||||||
|
{ 34000, 13250, 7500 },
|
||||||
|
{ 16000, 34500, 3000 }, 15635, 16450, 10000000, 500,
|
||||||
|
};
|
||||||
|
struct v4l2_ctrl_hdr10_cll_info p_hdr10_cll = { 1000, 400 };
|
||||||
|
|
||||||
ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 58);
|
ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 59);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -436,6 +461,9 @@ int venc_ctrl_init(struct venus_inst *inst)
|
|||||||
V4L2_MPEG_VIDEO_VP8_PROFILE_3,
|
V4L2_MPEG_VIDEO_VP8_PROFILE_3,
|
||||||
0, V4L2_MPEG_VIDEO_VP8_PROFILE_0);
|
0, V4L2_MPEG_VIDEO_VP8_PROFILE_0);
|
||||||
|
|
||||||
|
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
|
||||||
|
V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 4, 11, 1, 4);
|
||||||
|
|
||||||
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
|
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
|
||||||
V4L2_CID_MPEG_VIDEO_BITRATE, BITRATE_MIN, BITRATE_MAX,
|
V4L2_CID_MPEG_VIDEO_BITRATE, BITRATE_MIN, BITRATE_MAX,
|
||||||
BITRATE_STEP, BITRATE_DEFAULT);
|
BITRATE_STEP, BITRATE_DEFAULT);
|
||||||
@ -579,11 +607,11 @@ int venc_ctrl_init(struct venus_inst *inst)
|
|||||||
|
|
||||||
v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
|
v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
|
||||||
V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
|
V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
|
||||||
v4l2_ctrl_ptr_create(NULL));
|
v4l2_ctrl_ptr_create(&p_hdr10_cll));
|
||||||
|
|
||||||
v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
|
v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
|
||||||
V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
|
V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
|
||||||
v4l2_ctrl_ptr_create(NULL));
|
v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering));
|
||||||
|
|
||||||
v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
|
v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
|
||||||
V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
|
V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
|
||||||
|
@ -786,9 +786,8 @@ static int rvin_csi2_link_notify(struct media_link *link, u32 flags,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't allow link changes if any entity in the graph is
|
* Don't allow link changes if any stream in the graph is active as
|
||||||
* streaming, modifying the CHSEL register fields can disrupt
|
* modifying the CHSEL register fields can disrupt running streams.
|
||||||
* running streams.
|
|
||||||
*/
|
*/
|
||||||
media_device_for_each_entity(entity, &group->mdev)
|
media_device_for_each_entity(entity, &group->mdev)
|
||||||
if (media_entity_is_streaming(entity))
|
if (media_entity_is_streaming(entity))
|
||||||
|
@ -1244,8 +1244,6 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd,
|
|||||||
|
|
||||||
static int rvin_set_stream(struct rvin_dev *vin, int on)
|
static int rvin_set_stream(struct rvin_dev *vin, int on)
|
||||||
{
|
{
|
||||||
struct media_pipeline *pipe;
|
|
||||||
struct media_device *mdev;
|
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
struct media_pad *pad;
|
struct media_pad *pad;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1265,7 +1263,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on)
|
|||||||
sd = media_entity_to_v4l2_subdev(pad->entity);
|
sd = media_entity_to_v4l2_subdev(pad->entity);
|
||||||
|
|
||||||
if (!on) {
|
if (!on) {
|
||||||
media_pipeline_stop(&vin->vdev.entity);
|
video_device_pipeline_stop(&vin->vdev);
|
||||||
return v4l2_subdev_call(sd, video, s_stream, 0);
|
return v4l2_subdev_call(sd, video, s_stream, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1273,17 +1271,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/*
|
ret = video_device_pipeline_alloc_start(&vin->vdev);
|
||||||
* The graph lock needs to be taken to protect concurrent
|
|
||||||
* starts of multiple VIN instances as they might share
|
|
||||||
* a common subdevice down the line and then should use
|
|
||||||
* the same pipe.
|
|
||||||
*/
|
|
||||||
mdev = vin->vdev.entity.graph_obj.mdev;
|
|
||||||
mutex_lock(&mdev->graph_mutex);
|
|
||||||
pipe = sd->entity.pipe ? sd->entity.pipe : &vin->vdev.pipe;
|
|
||||||
ret = __media_pipeline_start(&vin->vdev.entity, pipe);
|
|
||||||
mutex_unlock(&mdev->graph_mutex);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1291,7 +1279,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on)
|
|||||||
if (ret == -ENOIOCTLCMD)
|
if (ret == -ENOIOCTLCMD)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
if (ret)
|
if (ret)
|
||||||
media_pipeline_stop(&vin->vdev.entity);
|
video_device_pipeline_stop(&vin->vdev);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -927,7 +927,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
|
|||||||
}
|
}
|
||||||
mutex_unlock(&pipe->lock);
|
mutex_unlock(&pipe->lock);
|
||||||
|
|
||||||
media_pipeline_stop(&video->video.entity);
|
video_device_pipeline_stop(&video->video);
|
||||||
vsp1_video_release_buffers(video);
|
vsp1_video_release_buffers(video);
|
||||||
vsp1_video_pipeline_put(pipe);
|
vsp1_video_pipeline_put(pipe);
|
||||||
}
|
}
|
||||||
@ -1046,7 +1046,7 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
|
|||||||
return PTR_ERR(pipe);
|
return PTR_ERR(pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = __media_pipeline_start(&video->video.entity, &pipe->pipe);
|
ret = __video_device_pipeline_start(&video->video, &pipe->pipe);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
mutex_unlock(&mdev->graph_mutex);
|
mutex_unlock(&mdev->graph_mutex);
|
||||||
goto err_pipe;
|
goto err_pipe;
|
||||||
@ -1070,7 +1070,7 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_stop:
|
err_stop:
|
||||||
media_pipeline_stop(&video->video.entity);
|
video_device_pipeline_stop(&video->video);
|
||||||
err_pipe:
|
err_pipe:
|
||||||
vsp1_video_pipeline_put(pipe);
|
vsp1_video_pipeline_put(pipe);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -913,7 +913,7 @@ static void rkisp1_cap_stream_disable(struct rkisp1_capture *cap)
|
|||||||
*
|
*
|
||||||
* Call s_stream(false) in the reverse order from
|
* Call s_stream(false) in the reverse order from
|
||||||
* rkisp1_pipeline_stream_enable() and disable the DMA engine.
|
* rkisp1_pipeline_stream_enable() and disable the DMA engine.
|
||||||
* Should be called before media_pipeline_stop()
|
* Should be called before video_device_pipeline_stop()
|
||||||
*/
|
*/
|
||||||
static void rkisp1_pipeline_stream_disable(struct rkisp1_capture *cap)
|
static void rkisp1_pipeline_stream_disable(struct rkisp1_capture *cap)
|
||||||
__must_hold(&cap->rkisp1->stream_lock)
|
__must_hold(&cap->rkisp1->stream_lock)
|
||||||
@ -926,7 +926,7 @@ static void rkisp1_pipeline_stream_disable(struct rkisp1_capture *cap)
|
|||||||
* If the other capture is streaming, isp and sensor nodes shouldn't
|
* If the other capture is streaming, isp and sensor nodes shouldn't
|
||||||
* be disabled, skip them.
|
* be disabled, skip them.
|
||||||
*/
|
*/
|
||||||
if (rkisp1->pipe.streaming_count < 2)
|
if (rkisp1->pipe.start_count < 2)
|
||||||
v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, false);
|
v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, false);
|
||||||
|
|
||||||
v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video, s_stream,
|
v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video, s_stream,
|
||||||
@ -937,7 +937,7 @@ static void rkisp1_pipeline_stream_disable(struct rkisp1_capture *cap)
|
|||||||
* rkisp1_pipeline_stream_enable - enable nodes in the pipeline
|
* rkisp1_pipeline_stream_enable - enable nodes in the pipeline
|
||||||
*
|
*
|
||||||
* Enable the DMA Engine and call s_stream(true) through the pipeline.
|
* Enable the DMA Engine and call s_stream(true) through the pipeline.
|
||||||
* Should be called after media_pipeline_start()
|
* Should be called after video_device_pipeline_start()
|
||||||
*/
|
*/
|
||||||
static int rkisp1_pipeline_stream_enable(struct rkisp1_capture *cap)
|
static int rkisp1_pipeline_stream_enable(struct rkisp1_capture *cap)
|
||||||
__must_hold(&cap->rkisp1->stream_lock)
|
__must_hold(&cap->rkisp1->stream_lock)
|
||||||
@ -956,7 +956,7 @@ static int rkisp1_pipeline_stream_enable(struct rkisp1_capture *cap)
|
|||||||
* If the other capture is streaming, isp and sensor nodes are already
|
* If the other capture is streaming, isp and sensor nodes are already
|
||||||
* enabled, skip them.
|
* enabled, skip them.
|
||||||
*/
|
*/
|
||||||
if (rkisp1->pipe.streaming_count > 1)
|
if (rkisp1->pipe.start_count > 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, true);
|
ret = v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, true);
|
||||||
@ -994,7 +994,7 @@ static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue)
|
|||||||
|
|
||||||
rkisp1_dummy_buf_destroy(cap);
|
rkisp1_dummy_buf_destroy(cap);
|
||||||
|
|
||||||
media_pipeline_stop(&node->vdev.entity);
|
video_device_pipeline_stop(&node->vdev);
|
||||||
|
|
||||||
mutex_unlock(&cap->rkisp1->stream_lock);
|
mutex_unlock(&cap->rkisp1->stream_lock);
|
||||||
}
|
}
|
||||||
@ -1008,7 +1008,7 @@ rkisp1_vb2_start_streaming(struct vb2_queue *queue, unsigned int count)
|
|||||||
|
|
||||||
mutex_lock(&cap->rkisp1->stream_lock);
|
mutex_lock(&cap->rkisp1->stream_lock);
|
||||||
|
|
||||||
ret = media_pipeline_start(entity, &cap->rkisp1->pipe);
|
ret = video_device_pipeline_start(&cap->vnode.vdev, &cap->rkisp1->pipe);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(cap->rkisp1->dev, "start pipeline failed %d\n", ret);
|
dev_err(cap->rkisp1->dev, "start pipeline failed %d\n", ret);
|
||||||
goto err_ret_buffers;
|
goto err_ret_buffers;
|
||||||
@ -1044,7 +1044,7 @@ err_pipe_pm_put:
|
|||||||
err_destroy_dummy:
|
err_destroy_dummy:
|
||||||
rkisp1_dummy_buf_destroy(cap);
|
rkisp1_dummy_buf_destroy(cap);
|
||||||
err_pipeline_stop:
|
err_pipeline_stop:
|
||||||
media_pipeline_stop(entity);
|
video_device_pipeline_stop(&cap->vnode.vdev);
|
||||||
err_ret_buffers:
|
err_ret_buffers:
|
||||||
rkisp1_return_all_buffers(cap, VB2_BUF_STATE_QUEUED);
|
rkisp1_return_all_buffers(cap, VB2_BUF_STATE_QUEUED);
|
||||||
mutex_unlock(&cap->rkisp1->stream_lock);
|
mutex_unlock(&cap->rkisp1->stream_lock);
|
||||||
@ -1273,11 +1273,12 @@ static int rkisp1_capture_link_validate(struct media_link *link)
|
|||||||
struct rkisp1_capture *cap = video_get_drvdata(vdev);
|
struct rkisp1_capture *cap = video_get_drvdata(vdev);
|
||||||
const struct rkisp1_capture_fmt_cfg *fmt =
|
const struct rkisp1_capture_fmt_cfg *fmt =
|
||||||
rkisp1_find_fmt_cfg(cap, cap->pix.fmt.pixelformat);
|
rkisp1_find_fmt_cfg(cap, cap->pix.fmt.pixelformat);
|
||||||
struct v4l2_subdev_format sd_fmt;
|
struct v4l2_subdev_format sd_fmt = {
|
||||||
|
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
||||||
|
.pad = link->source->index,
|
||||||
|
};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
|
||||||
sd_fmt.pad = link->source->index;
|
|
||||||
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt);
|
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -378,6 +378,7 @@ struct rkisp1_params {
|
|||||||
struct v4l2_format vdev_fmt;
|
struct v4l2_format vdev_fmt;
|
||||||
|
|
||||||
enum v4l2_quantization quantization;
|
enum v4l2_quantization quantization;
|
||||||
|
enum v4l2_ycbcr_encoding ycbcr_encoding;
|
||||||
enum rkisp1_fmt_raw_pat_type raw_type;
|
enum rkisp1_fmt_raw_pat_type raw_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -556,17 +557,32 @@ void rkisp1_sd_adjust_crop(struct v4l2_rect *crop,
|
|||||||
*/
|
*/
|
||||||
const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_code(u32 mbus_code);
|
const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_code(u32 mbus_code);
|
||||||
|
|
||||||
/* rkisp1_params_configure - configure the params when stream starts.
|
/*
|
||||||
* This function is called by the isp entity upon stream starts.
|
* rkisp1_params_pre_configure - Configure the params before stream start
|
||||||
* The function applies the initial configuration of the parameters.
|
|
||||||
*
|
*
|
||||||
* @params: pointer to rkisp1_params.
|
* @params: pointer to rkisp1_params
|
||||||
* @bayer_pat: the bayer pattern on the isp video sink pad
|
* @bayer_pat: the bayer pattern on the isp video sink pad
|
||||||
* @quantization: the quantization configured on the isp's src pad
|
* @quantization: the quantization configured on the isp's src pad
|
||||||
|
* @ycbcr_encoding: the ycbcr_encoding configured on the isp's src pad
|
||||||
|
*
|
||||||
|
* This function is called by the ISP entity just before the ISP gets started.
|
||||||
|
* It applies the initial ISP parameters from the first params buffer, but
|
||||||
|
* skips LSC as it needs to be configured after the ISP is started.
|
||||||
*/
|
*/
|
||||||
void rkisp1_params_configure(struct rkisp1_params *params,
|
void rkisp1_params_pre_configure(struct rkisp1_params *params,
|
||||||
enum rkisp1_fmt_raw_pat_type bayer_pat,
|
enum rkisp1_fmt_raw_pat_type bayer_pat,
|
||||||
enum v4l2_quantization quantization);
|
enum v4l2_quantization quantization,
|
||||||
|
enum v4l2_ycbcr_encoding ycbcr_encoding);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rkisp1_params_post_configure - Configure the params after stream start
|
||||||
|
*
|
||||||
|
* @params: pointer to rkisp1_params
|
||||||
|
*
|
||||||
|
* This function is called by the ISP entity just after the ISP gets started.
|
||||||
|
* It applies the initial ISP LSC parameters from the first params buffer.
|
||||||
|
*/
|
||||||
|
void rkisp1_params_post_configure(struct rkisp1_params *params);
|
||||||
|
|
||||||
/* rkisp1_params_disable - disable all parameters.
|
/* rkisp1_params_disable - disable all parameters.
|
||||||
* This function is called by the isp entity upon stream start
|
* This function is called by the isp entity upon stream start
|
||||||
|
@ -231,10 +231,11 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp,
|
|||||||
struct v4l2_mbus_framefmt *src_frm;
|
struct v4l2_mbus_framefmt *src_frm;
|
||||||
|
|
||||||
src_frm = rkisp1_isp_get_pad_fmt(isp, NULL,
|
src_frm = rkisp1_isp_get_pad_fmt(isp, NULL,
|
||||||
RKISP1_ISP_PAD_SINK_VIDEO,
|
RKISP1_ISP_PAD_SOURCE_VIDEO,
|
||||||
V4L2_SUBDEV_FORMAT_ACTIVE);
|
V4L2_SUBDEV_FORMAT_ACTIVE);
|
||||||
rkisp1_params_configure(&rkisp1->params, sink_fmt->bayer_pat,
|
rkisp1_params_pre_configure(&rkisp1->params, sink_fmt->bayer_pat,
|
||||||
src_frm->quantization);
|
src_frm->quantization,
|
||||||
|
src_frm->ycbcr_enc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -340,6 +341,9 @@ static void rkisp1_isp_start(struct rkisp1_isp *isp)
|
|||||||
RKISP1_CIF_ISP_CTRL_ISP_ENABLE |
|
RKISP1_CIF_ISP_CTRL_ISP_ENABLE |
|
||||||
RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE;
|
RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE;
|
||||||
rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val);
|
||||||
|
|
||||||
|
if (isp->src_fmt->pixel_enc != V4L2_PIXEL_ENC_BAYER)
|
||||||
|
rkisp1_params_post_configure(&rkisp1->params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
@ -431,12 +435,17 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd,
|
|||||||
struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
|
struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
|
||||||
struct v4l2_rect *sink_crop, *src_crop;
|
struct v4l2_rect *sink_crop, *src_crop;
|
||||||
|
|
||||||
|
/* Video. */
|
||||||
sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
|
sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
|
||||||
RKISP1_ISP_PAD_SINK_VIDEO);
|
RKISP1_ISP_PAD_SINK_VIDEO);
|
||||||
sink_fmt->width = RKISP1_DEFAULT_WIDTH;
|
sink_fmt->width = RKISP1_DEFAULT_WIDTH;
|
||||||
sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
|
sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
|
||||||
sink_fmt->field = V4L2_FIELD_NONE;
|
sink_fmt->field = V4L2_FIELD_NONE;
|
||||||
sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT;
|
sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT;
|
||||||
|
sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
|
||||||
|
sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
|
||||||
|
sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
|
||||||
|
sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
|
||||||
|
|
||||||
sink_crop = v4l2_subdev_get_try_crop(sd, sd_state,
|
sink_crop = v4l2_subdev_get_try_crop(sd, sd_state,
|
||||||
RKISP1_ISP_PAD_SINK_VIDEO);
|
RKISP1_ISP_PAD_SINK_VIDEO);
|
||||||
@ -449,11 +458,16 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd,
|
|||||||
RKISP1_ISP_PAD_SOURCE_VIDEO);
|
RKISP1_ISP_PAD_SOURCE_VIDEO);
|
||||||
*src_fmt = *sink_fmt;
|
*src_fmt = *sink_fmt;
|
||||||
src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
|
src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
|
||||||
|
src_fmt->colorspace = V4L2_COLORSPACE_SRGB;
|
||||||
|
src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
|
||||||
|
src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
|
||||||
|
src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
|
||||||
|
|
||||||
src_crop = v4l2_subdev_get_try_crop(sd, sd_state,
|
src_crop = v4l2_subdev_get_try_crop(sd, sd_state,
|
||||||
RKISP1_ISP_PAD_SOURCE_VIDEO);
|
RKISP1_ISP_PAD_SOURCE_VIDEO);
|
||||||
*src_crop = *sink_crop;
|
*src_crop = *sink_crop;
|
||||||
|
|
||||||
|
/* Parameters and statistics. */
|
||||||
sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
|
sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
|
||||||
RKISP1_ISP_PAD_SINK_PARAMS);
|
RKISP1_ISP_PAD_SINK_PARAMS);
|
||||||
src_fmt = v4l2_subdev_get_try_format(sd, sd_state,
|
src_fmt = v4l2_subdev_get_try_format(sd, sd_state,
|
||||||
@ -472,40 +486,105 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp,
|
|||||||
struct v4l2_mbus_framefmt *format,
|
struct v4l2_mbus_framefmt *format,
|
||||||
unsigned int which)
|
unsigned int which)
|
||||||
{
|
{
|
||||||
const struct rkisp1_mbus_info *mbus_info;
|
const struct rkisp1_mbus_info *sink_info;
|
||||||
|
const struct rkisp1_mbus_info *src_info;
|
||||||
|
struct v4l2_mbus_framefmt *sink_fmt;
|
||||||
struct v4l2_mbus_framefmt *src_fmt;
|
struct v4l2_mbus_framefmt *src_fmt;
|
||||||
const struct v4l2_rect *src_crop;
|
const struct v4l2_rect *src_crop;
|
||||||
|
bool set_csc;
|
||||||
|
|
||||||
|
sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
|
||||||
|
RKISP1_ISP_PAD_SINK_VIDEO, which);
|
||||||
src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
|
src_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
|
||||||
RKISP1_ISP_PAD_SOURCE_VIDEO, which);
|
RKISP1_ISP_PAD_SOURCE_VIDEO, which);
|
||||||
src_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
|
src_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
|
||||||
RKISP1_ISP_PAD_SOURCE_VIDEO, which);
|
RKISP1_ISP_PAD_SOURCE_VIDEO, which);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Media bus code. The ISP can operate in pass-through mode (Bayer in,
|
||||||
|
* Bayer out or YUV in, YUV out) or process Bayer data to YUV, but
|
||||||
|
* can't convert from YUV to Bayer.
|
||||||
|
*/
|
||||||
|
sink_info = rkisp1_mbus_info_get_by_code(sink_fmt->code);
|
||||||
|
|
||||||
src_fmt->code = format->code;
|
src_fmt->code = format->code;
|
||||||
mbus_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
|
src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
|
||||||
if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) {
|
if (!src_info || !(src_info->direction & RKISP1_ISP_SD_SRC)) {
|
||||||
src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
|
src_fmt->code = RKISP1_DEF_SRC_PAD_FMT;
|
||||||
mbus_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
|
src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
|
||||||
}
|
}
|
||||||
if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
|
|
||||||
isp->src_fmt = mbus_info;
|
if (sink_info->pixel_enc == V4L2_PIXEL_ENC_YUV &&
|
||||||
|
src_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
|
||||||
|
src_fmt->code = sink_fmt->code;
|
||||||
|
src_info = sink_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The source width and height must be identical to the source crop
|
||||||
|
* size.
|
||||||
|
*/
|
||||||
src_fmt->width = src_crop->width;
|
src_fmt->width = src_crop->width;
|
||||||
src_fmt->height = src_crop->height;
|
src_fmt->height = src_crop->height;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The CSC API is used to allow userspace to force full
|
* Copy the color space for the sink pad. When converting from Bayer to
|
||||||
* quantization on YUV formats.
|
* YUV, default to a limited quantization range.
|
||||||
*/
|
*/
|
||||||
if (format->flags & V4L2_MBUS_FRAMEFMT_SET_CSC &&
|
src_fmt->colorspace = sink_fmt->colorspace;
|
||||||
format->quantization == V4L2_QUANTIZATION_FULL_RANGE &&
|
src_fmt->xfer_func = sink_fmt->xfer_func;
|
||||||
mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV)
|
src_fmt->ycbcr_enc = sink_fmt->ycbcr_enc;
|
||||||
src_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
|
|
||||||
else if (mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV)
|
if (sink_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&
|
||||||
|
src_info->pixel_enc == V4L2_PIXEL_ENC_YUV)
|
||||||
src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
|
src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
|
||||||
else
|
else
|
||||||
src_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
|
src_fmt->quantization = sink_fmt->quantization;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allow setting the source color space fields when the SET_CSC flag is
|
||||||
|
* set and the source format is YUV. If the sink format is YUV, don't
|
||||||
|
* set the color primaries, transfer function or YCbCr encoding as the
|
||||||
|
* ISP is bypassed in that case and passes YUV data through without
|
||||||
|
* modifications.
|
||||||
|
*
|
||||||
|
* The color primaries and transfer function are configured through the
|
||||||
|
* cross-talk matrix and tone curve respectively. Settings for those
|
||||||
|
* hardware blocks are conveyed through the ISP parameters buffer, as
|
||||||
|
* they need to combine color space information with other image tuning
|
||||||
|
* characteristics and can't thus be computed by the kernel based on the
|
||||||
|
* color space. The source pad colorspace and xfer_func fields are thus
|
||||||
|
* ignored by the driver, but can be set by userspace to propagate
|
||||||
|
* accurate color space information down the pipeline.
|
||||||
|
*/
|
||||||
|
set_csc = format->flags & V4L2_MBUS_FRAMEFMT_SET_CSC;
|
||||||
|
|
||||||
|
if (set_csc && src_info->pixel_enc == V4L2_PIXEL_ENC_YUV) {
|
||||||
|
if (sink_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
|
||||||
|
if (format->colorspace != V4L2_COLORSPACE_DEFAULT)
|
||||||
|
src_fmt->colorspace = format->colorspace;
|
||||||
|
if (format->xfer_func != V4L2_XFER_FUNC_DEFAULT)
|
||||||
|
src_fmt->xfer_func = format->xfer_func;
|
||||||
|
if (format->ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT)
|
||||||
|
src_fmt->ycbcr_enc = format->ycbcr_enc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format->quantization != V4L2_QUANTIZATION_DEFAULT)
|
||||||
|
src_fmt->quantization = format->quantization;
|
||||||
|
}
|
||||||
|
|
||||||
*format = *src_fmt;
|
*format = *src_fmt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore the SET_CSC flag if it was set to indicate support for the
|
||||||
|
* CSC setting API.
|
||||||
|
*/
|
||||||
|
if (set_csc)
|
||||||
|
format->flags |= V4L2_MBUS_FRAMEFMT_SET_CSC;
|
||||||
|
|
||||||
|
/* Store the source format info when setting the active format. */
|
||||||
|
if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
|
||||||
|
isp->src_fmt = src_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp,
|
static void rkisp1_isp_set_src_crop(struct rkisp1_isp *isp,
|
||||||
@ -573,6 +652,7 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp,
|
|||||||
const struct rkisp1_mbus_info *mbus_info;
|
const struct rkisp1_mbus_info *mbus_info;
|
||||||
struct v4l2_mbus_framefmt *sink_fmt;
|
struct v4l2_mbus_framefmt *sink_fmt;
|
||||||
struct v4l2_rect *sink_crop;
|
struct v4l2_rect *sink_crop;
|
||||||
|
bool is_yuv;
|
||||||
|
|
||||||
sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
|
sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
|
||||||
RKISP1_ISP_PAD_SINK_VIDEO,
|
RKISP1_ISP_PAD_SINK_VIDEO,
|
||||||
@ -593,6 +673,36 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp,
|
|||||||
RKISP1_ISP_MIN_HEIGHT,
|
RKISP1_ISP_MIN_HEIGHT,
|
||||||
RKISP1_ISP_MAX_HEIGHT);
|
RKISP1_ISP_MAX_HEIGHT);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adjust the color space fields. Accept any color primaries and
|
||||||
|
* transfer function for both YUV and Bayer. For YUV any YCbCr encoding
|
||||||
|
* and quantization range is also accepted. For Bayer formats, the YCbCr
|
||||||
|
* encoding isn't applicable, and the quantization range can only be
|
||||||
|
* full.
|
||||||
|
*/
|
||||||
|
is_yuv = mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV;
|
||||||
|
|
||||||
|
sink_fmt->colorspace = format->colorspace ? :
|
||||||
|
(is_yuv ? V4L2_COLORSPACE_SRGB :
|
||||||
|
V4L2_COLORSPACE_RAW);
|
||||||
|
sink_fmt->xfer_func = format->xfer_func ? :
|
||||||
|
V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace);
|
||||||
|
if (is_yuv) {
|
||||||
|
sink_fmt->ycbcr_enc = format->ycbcr_enc ? :
|
||||||
|
V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace);
|
||||||
|
sink_fmt->quantization = format->quantization ? :
|
||||||
|
V4L2_MAP_QUANTIZATION_DEFAULT(false, sink_fmt->colorspace,
|
||||||
|
sink_fmt->ycbcr_enc);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* The YCbCr encoding isn't applicable for non-YUV formats, but
|
||||||
|
* V4L2 has no "no encoding" value. Hardcode it to Rec. 601, it
|
||||||
|
* should be ignored by userspace.
|
||||||
|
*/
|
||||||
|
sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
|
||||||
|
sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
|
||||||
|
}
|
||||||
|
|
||||||
*format = *sink_fmt;
|
*format = *sink_fmt;
|
||||||
|
|
||||||
/* Propagate to in crop */
|
/* Propagate to in crop */
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
#define RKISP1_ISP_PARAMS_REQ_BUFS_MIN 2
|
#define RKISP1_ISP_PARAMS_REQ_BUFS_MIN 2
|
||||||
#define RKISP1_ISP_PARAMS_REQ_BUFS_MAX 8
|
#define RKISP1_ISP_PARAMS_REQ_BUFS_MAX 8
|
||||||
|
|
||||||
|
#define RKISP1_ISP_DPCC_METHODS_SET(n) \
|
||||||
|
(RKISP1_CIF_ISP_DPCC_METHODS_SET_1 + 0x4 * (n))
|
||||||
#define RKISP1_ISP_DPCC_LINE_THRESH(n) \
|
#define RKISP1_ISP_DPCC_LINE_THRESH(n) \
|
||||||
(RKISP1_CIF_ISP_DPCC_LINE_THRESH_1 + 0x14 * (n))
|
(RKISP1_CIF_ISP_DPCC_LINE_THRESH_1 + 0x14 * (n))
|
||||||
#define RKISP1_ISP_DPCC_LINE_MAD_FAC(n) \
|
#define RKISP1_ISP_DPCC_LINE_MAD_FAC(n) \
|
||||||
@ -56,39 +58,47 @@ static void rkisp1_dpcc_config(struct rkisp1_params *params,
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
u32 mode;
|
u32 mode;
|
||||||
|
|
||||||
/* avoid to override the old enable value */
|
/*
|
||||||
|
* The enable bit is controlled in rkisp1_isp_isr_other_config() and
|
||||||
|
* must be preserved. The grayscale mode should be configured
|
||||||
|
* automatically based on the media bus code on the ISP sink pad, so
|
||||||
|
* only the STAGE1_ENABLE bit can be set by userspace.
|
||||||
|
*/
|
||||||
mode = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_DPCC_MODE);
|
mode = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_DPCC_MODE);
|
||||||
mode &= RKISP1_CIF_ISP_DPCC_ENA;
|
mode &= RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE;
|
||||||
mode |= arg->mode & ~RKISP1_CIF_ISP_DPCC_ENA;
|
mode |= arg->mode & RKISP1_CIF_ISP_DPCC_MODE_STAGE1_ENABLE;
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_MODE, mode);
|
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_MODE, mode);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_OUTPUT_MODE,
|
|
||||||
arg->output_mode);
|
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_SET_USE,
|
|
||||||
arg->set_use);
|
|
||||||
|
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_METHODS_SET_1,
|
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_OUTPUT_MODE,
|
||||||
arg->methods[0].method);
|
arg->output_mode & RKISP1_CIF_ISP_DPCC_OUTPUT_MODE_MASK);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_METHODS_SET_2,
|
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_SET_USE,
|
||||||
arg->methods[1].method);
|
arg->set_use & RKISP1_CIF_ISP_DPCC_SET_USE_MASK);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_METHODS_SET_3,
|
|
||||||
arg->methods[2].method);
|
|
||||||
for (i = 0; i < RKISP1_CIF_ISP_DPCC_METHODS_MAX; i++) {
|
for (i = 0; i < RKISP1_CIF_ISP_DPCC_METHODS_MAX; i++) {
|
||||||
|
rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_METHODS_SET(i),
|
||||||
|
arg->methods[i].method &
|
||||||
|
RKISP1_CIF_ISP_DPCC_METHODS_SET_MASK);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_LINE_THRESH(i),
|
rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_LINE_THRESH(i),
|
||||||
arg->methods[i].line_thresh);
|
arg->methods[i].line_thresh &
|
||||||
|
RKISP1_CIF_ISP_DPCC_LINE_THRESH_MASK);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_LINE_MAD_FAC(i),
|
rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_LINE_MAD_FAC(i),
|
||||||
arg->methods[i].line_mad_fac);
|
arg->methods[i].line_mad_fac &
|
||||||
|
RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_MASK);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_PG_FAC(i),
|
rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_PG_FAC(i),
|
||||||
arg->methods[i].pg_fac);
|
arg->methods[i].pg_fac &
|
||||||
|
RKISP1_CIF_ISP_DPCC_PG_FAC_MASK);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_RND_THRESH(i),
|
rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_RND_THRESH(i),
|
||||||
arg->methods[i].rnd_thresh);
|
arg->methods[i].rnd_thresh &
|
||||||
|
RKISP1_CIF_ISP_DPCC_RND_THRESH_MASK);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_RG_FAC(i),
|
rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_RG_FAC(i),
|
||||||
arg->methods[i].rg_fac);
|
arg->methods[i].rg_fac &
|
||||||
|
RKISP1_CIF_ISP_DPCC_RG_FAC_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_RND_OFFS,
|
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_RND_OFFS,
|
||||||
arg->rnd_offs);
|
arg->rnd_offs & RKISP1_CIF_ISP_DPCC_RND_OFFS_MASK);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_RO_LIMITS,
|
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_RO_LIMITS,
|
||||||
arg->ro_limits);
|
arg->ro_limits & RKISP1_CIF_ISP_DPCC_RO_LIMIT_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ISP black level subtraction interface function */
|
/* ISP black level subtraction interface function */
|
||||||
@ -188,149 +198,131 @@ static void
|
|||||||
rkisp1_lsc_matrix_config_v10(struct rkisp1_params *params,
|
rkisp1_lsc_matrix_config_v10(struct rkisp1_params *params,
|
||||||
const struct rkisp1_cif_isp_lsc_config *pconfig)
|
const struct rkisp1_cif_isp_lsc_config *pconfig)
|
||||||
{
|
{
|
||||||
unsigned int isp_lsc_status, sram_addr, isp_lsc_table_sel, i, j, data;
|
struct rkisp1_device *rkisp1 = params->rkisp1;
|
||||||
|
u32 lsc_status, sram_addr, lsc_table_sel;
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
isp_lsc_status = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_LSC_STATUS);
|
lsc_status = rkisp1_read(rkisp1, RKISP1_CIF_ISP_LSC_STATUS);
|
||||||
|
|
||||||
/* RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153 = ( 17 * 18 ) >> 1 */
|
/* RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153 = ( 17 * 18 ) >> 1 */
|
||||||
sram_addr = (isp_lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE) ?
|
sram_addr = lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE ?
|
||||||
RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_0 :
|
RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_0 :
|
||||||
RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153;
|
RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153;
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_ADDR, sram_addr);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_ADDR, sram_addr);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR, sram_addr);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR, sram_addr);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_ADDR, sram_addr);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_ADDR, sram_addr);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_ADDR, sram_addr);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_ADDR, sram_addr);
|
||||||
|
|
||||||
/* program data tables (table size is 9 * 17 = 153) */
|
/* program data tables (table size is 9 * 17 = 153) */
|
||||||
for (i = 0; i < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; i++) {
|
for (i = 0; i < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; i++) {
|
||||||
|
const __u16 *r_tbl = pconfig->r_data_tbl[i];
|
||||||
|
const __u16 *gr_tbl = pconfig->gr_data_tbl[i];
|
||||||
|
const __u16 *gb_tbl = pconfig->gb_data_tbl[i];
|
||||||
|
const __u16 *b_tbl = pconfig->b_data_tbl[i];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 17 sectors with 2 values in one DWORD = 9
|
* 17 sectors with 2 values in one DWORD = 9
|
||||||
* DWORDs (2nd value of last DWORD unused)
|
* DWORDs (2nd value of last DWORD unused)
|
||||||
*/
|
*/
|
||||||
for (j = 0; j < RKISP1_CIF_ISP_LSC_SAMPLES_MAX - 1; j += 2) {
|
for (j = 0; j < RKISP1_CIF_ISP_LSC_SAMPLES_MAX - 1; j += 2) {
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->r_data_tbl[i][j],
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
|
||||||
pconfig->r_data_tbl[i][j + 1]);
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
|
||||||
rkisp1_write(params->rkisp1,
|
r_tbl[j], r_tbl[j + 1]));
|
||||||
RKISP1_CIF_ISP_LSC_R_TABLE_DATA, data);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
|
||||||
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->gr_data_tbl[i][j],
|
gr_tbl[j], gr_tbl[j + 1]));
|
||||||
pconfig->gr_data_tbl[i][j + 1]);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
|
||||||
rkisp1_write(params->rkisp1,
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
|
||||||
RKISP1_CIF_ISP_LSC_GR_TABLE_DATA, data);
|
gb_tbl[j], gb_tbl[j + 1]));
|
||||||
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->gb_data_tbl[i][j],
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
|
||||||
pconfig->gb_data_tbl[i][j + 1]);
|
b_tbl[j], b_tbl[j + 1]));
|
||||||
rkisp1_write(params->rkisp1,
|
|
||||||
RKISP1_CIF_ISP_LSC_GB_TABLE_DATA, data);
|
|
||||||
|
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->b_data_tbl[i][j],
|
|
||||||
pconfig->b_data_tbl[i][j + 1]);
|
|
||||||
rkisp1_write(params->rkisp1,
|
|
||||||
RKISP1_CIF_ISP_LSC_B_TABLE_DATA, data);
|
|
||||||
}
|
}
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->r_data_tbl[i][j], 0);
|
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
|
|
||||||
data);
|
|
||||||
|
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->gr_data_tbl[i][j], 0);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(r_tbl[j], 0));
|
||||||
data);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
|
||||||
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(gr_tbl[j], 0));
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->gb_data_tbl[i][j], 0);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(gb_tbl[j], 0));
|
||||||
data);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
|
||||||
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(b_tbl[j], 0));
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->b_data_tbl[i][j], 0);
|
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
|
|
||||||
data);
|
|
||||||
}
|
}
|
||||||
isp_lsc_table_sel = (isp_lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE) ?
|
|
||||||
RKISP1_CIF_ISP_LSC_TABLE_0 :
|
lsc_table_sel = lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE ?
|
||||||
RKISP1_CIF_ISP_LSC_TABLE_1;
|
RKISP1_CIF_ISP_LSC_TABLE_0 : RKISP1_CIF_ISP_LSC_TABLE_1;
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL,
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL, lsc_table_sel);
|
||||||
isp_lsc_table_sel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rkisp1_lsc_matrix_config_v12(struct rkisp1_params *params,
|
rkisp1_lsc_matrix_config_v12(struct rkisp1_params *params,
|
||||||
const struct rkisp1_cif_isp_lsc_config *pconfig)
|
const struct rkisp1_cif_isp_lsc_config *pconfig)
|
||||||
{
|
{
|
||||||
unsigned int isp_lsc_status, sram_addr, isp_lsc_table_sel, i, j, data;
|
struct rkisp1_device *rkisp1 = params->rkisp1;
|
||||||
|
u32 lsc_status, sram_addr, lsc_table_sel;
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
isp_lsc_status = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_LSC_STATUS);
|
lsc_status = rkisp1_read(rkisp1, RKISP1_CIF_ISP_LSC_STATUS);
|
||||||
|
|
||||||
/* RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153 = ( 17 * 18 ) >> 1 */
|
/* RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153 = ( 17 * 18 ) >> 1 */
|
||||||
sram_addr = (isp_lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE) ?
|
sram_addr = lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE ?
|
||||||
RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_0 :
|
RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_0 :
|
||||||
RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153;
|
RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153;
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_ADDR, sram_addr);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_ADDR, sram_addr);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR, sram_addr);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR, sram_addr);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_ADDR, sram_addr);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_ADDR, sram_addr);
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_ADDR, sram_addr);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_ADDR, sram_addr);
|
||||||
|
|
||||||
/* program data tables (table size is 9 * 17 = 153) */
|
/* program data tables (table size is 9 * 17 = 153) */
|
||||||
for (i = 0; i < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; i++) {
|
for (i = 0; i < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; i++) {
|
||||||
|
const __u16 *r_tbl = pconfig->r_data_tbl[i];
|
||||||
|
const __u16 *gr_tbl = pconfig->gr_data_tbl[i];
|
||||||
|
const __u16 *gb_tbl = pconfig->gb_data_tbl[i];
|
||||||
|
const __u16 *b_tbl = pconfig->b_data_tbl[i];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 17 sectors with 2 values in one DWORD = 9
|
* 17 sectors with 2 values in one DWORD = 9
|
||||||
* DWORDs (2nd value of last DWORD unused)
|
* DWORDs (2nd value of last DWORD unused)
|
||||||
*/
|
*/
|
||||||
for (j = 0; j < RKISP1_CIF_ISP_LSC_SAMPLES_MAX - 1; j += 2) {
|
for (j = 0; j < RKISP1_CIF_ISP_LSC_SAMPLES_MAX - 1; j += 2) {
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
|
||||||
pconfig->r_data_tbl[i][j],
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
|
||||||
pconfig->r_data_tbl[i][j + 1]);
|
r_tbl[j], r_tbl[j + 1]));
|
||||||
rkisp1_write(params->rkisp1,
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
|
||||||
RKISP1_CIF_ISP_LSC_R_TABLE_DATA, data);
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
|
||||||
|
gr_tbl[j], gr_tbl[j + 1]));
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
|
||||||
pconfig->gr_data_tbl[i][j],
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
|
||||||
pconfig->gr_data_tbl[i][j + 1]);
|
gb_tbl[j], gb_tbl[j + 1]));
|
||||||
rkisp1_write(params->rkisp1,
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
|
||||||
RKISP1_CIF_ISP_LSC_GR_TABLE_DATA, data);
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
|
||||||
|
b_tbl[j], b_tbl[j + 1]));
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
|
|
||||||
pconfig->gb_data_tbl[i][j],
|
|
||||||
pconfig->gb_data_tbl[i][j + 1]);
|
|
||||||
rkisp1_write(params->rkisp1,
|
|
||||||
RKISP1_CIF_ISP_LSC_GB_TABLE_DATA, data);
|
|
||||||
|
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
|
|
||||||
pconfig->b_data_tbl[i][j],
|
|
||||||
pconfig->b_data_tbl[i][j + 1]);
|
|
||||||
rkisp1_write(params->rkisp1,
|
|
||||||
RKISP1_CIF_ISP_LSC_B_TABLE_DATA, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(pconfig->r_data_tbl[i][j], 0);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(r_tbl[j], 0));
|
||||||
data);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
|
||||||
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(gr_tbl[j], 0));
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(pconfig->gr_data_tbl[i][j], 0);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(gb_tbl[j], 0));
|
||||||
data);
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
|
||||||
|
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(b_tbl[j], 0));
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(pconfig->gb_data_tbl[i][j], 0);
|
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
|
|
||||||
data);
|
|
||||||
|
|
||||||
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(pconfig->b_data_tbl[i][j], 0);
|
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
|
|
||||||
data);
|
|
||||||
}
|
}
|
||||||
isp_lsc_table_sel = (isp_lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE) ?
|
|
||||||
RKISP1_CIF_ISP_LSC_TABLE_0 :
|
lsc_table_sel = lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE ?
|
||||||
RKISP1_CIF_ISP_LSC_TABLE_1;
|
RKISP1_CIF_ISP_LSC_TABLE_0 : RKISP1_CIF_ISP_LSC_TABLE_1;
|
||||||
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL,
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL, lsc_table_sel);
|
||||||
isp_lsc_table_sel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rkisp1_lsc_config(struct rkisp1_params *params,
|
static void rkisp1_lsc_config(struct rkisp1_params *params,
|
||||||
const struct rkisp1_cif_isp_lsc_config *arg)
|
const struct rkisp1_cif_isp_lsc_config *arg)
|
||||||
{
|
{
|
||||||
unsigned int i, data;
|
struct rkisp1_device *rkisp1 = params->rkisp1;
|
||||||
u32 lsc_ctrl;
|
u32 lsc_ctrl, data;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
/* To config must be off , store the current status firstly */
|
/* To config must be off , store the current status firstly */
|
||||||
lsc_ctrl = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_LSC_CTRL);
|
lsc_ctrl = rkisp1_read(rkisp1, RKISP1_CIF_ISP_LSC_CTRL);
|
||||||
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
|
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
|
||||||
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
||||||
params->ops->lsc_matrix_config(params, arg);
|
params->ops->lsc_matrix_config(params, arg);
|
||||||
@ -339,38 +331,31 @@ static void rkisp1_lsc_config(struct rkisp1_params *params,
|
|||||||
/* program x size tables */
|
/* program x size tables */
|
||||||
data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->x_size_tbl[i * 2],
|
data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->x_size_tbl[i * 2],
|
||||||
arg->x_size_tbl[i * 2 + 1]);
|
arg->x_size_tbl[i * 2 + 1]);
|
||||||
rkisp1_write(params->rkisp1,
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_XSIZE(i), data);
|
||||||
RKISP1_CIF_ISP_LSC_XSIZE_01 + i * 4, data);
|
|
||||||
|
|
||||||
/* program x grad tables */
|
/* program x grad tables */
|
||||||
data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->x_grad_tbl[i * 2],
|
data = RKISP1_CIF_ISP_LSC_SECT_GRAD(arg->x_grad_tbl[i * 2],
|
||||||
arg->x_grad_tbl[i * 2 + 1]);
|
arg->x_grad_tbl[i * 2 + 1]);
|
||||||
rkisp1_write(params->rkisp1,
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_XGRAD(i), data);
|
||||||
RKISP1_CIF_ISP_LSC_XGRAD_01 + i * 4, data);
|
|
||||||
|
|
||||||
/* program y size tables */
|
/* program y size tables */
|
||||||
data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->y_size_tbl[i * 2],
|
data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->y_size_tbl[i * 2],
|
||||||
arg->y_size_tbl[i * 2 + 1]);
|
arg->y_size_tbl[i * 2 + 1]);
|
||||||
rkisp1_write(params->rkisp1,
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_YSIZE(i), data);
|
||||||
RKISP1_CIF_ISP_LSC_YSIZE_01 + i * 4, data);
|
|
||||||
|
|
||||||
/* program y grad tables */
|
/* program y grad tables */
|
||||||
data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->y_grad_tbl[i * 2],
|
data = RKISP1_CIF_ISP_LSC_SECT_GRAD(arg->y_grad_tbl[i * 2],
|
||||||
arg->y_grad_tbl[i * 2 + 1]);
|
arg->y_grad_tbl[i * 2 + 1]);
|
||||||
rkisp1_write(params->rkisp1,
|
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_YGRAD(i), data);
|
||||||
RKISP1_CIF_ISP_LSC_YGRAD_01 + i * 4, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* restore the lsc ctrl status */
|
/* restore the lsc ctrl status */
|
||||||
if (lsc_ctrl & RKISP1_CIF_ISP_LSC_CTRL_ENA) {
|
if (lsc_ctrl & RKISP1_CIF_ISP_LSC_CTRL_ENA)
|
||||||
rkisp1_param_set_bits(params,
|
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
|
||||||
RKISP1_CIF_ISP_LSC_CTRL,
|
|
||||||
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
||||||
} else {
|
else
|
||||||
rkisp1_param_clear_bits(params,
|
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
|
||||||
RKISP1_CIF_ISP_LSC_CTRL,
|
|
||||||
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ISP Filtering function */
|
/* ISP Filtering function */
|
||||||
@ -1066,39 +1051,96 @@ static void rkisp1_ie_enable(struct rkisp1_params *params, bool en)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rkisp1_csm_config(struct rkisp1_params *params, bool full_range)
|
static void rkisp1_csm_config(struct rkisp1_params *params)
|
||||||
{
|
{
|
||||||
static const u16 full_range_coeff[] = {
|
struct csm_coeffs {
|
||||||
0x0026, 0x004b, 0x000f,
|
u16 limited[9];
|
||||||
0x01ea, 0x01d6, 0x0040,
|
u16 full[9];
|
||||||
0x0040, 0x01ca, 0x01f6
|
|
||||||
};
|
};
|
||||||
static const u16 limited_range_coeff[] = {
|
static const struct csm_coeffs rec601_coeffs = {
|
||||||
0x0021, 0x0040, 0x000d,
|
.limited = {
|
||||||
0x01ed, 0x01db, 0x0038,
|
0x0021, 0x0042, 0x000d,
|
||||||
0x0038, 0x01d1, 0x01f7,
|
0x01ed, 0x01db, 0x0038,
|
||||||
|
0x0038, 0x01d1, 0x01f7,
|
||||||
|
},
|
||||||
|
.full = {
|
||||||
|
0x0026, 0x004b, 0x000f,
|
||||||
|
0x01ea, 0x01d6, 0x0040,
|
||||||
|
0x0040, 0x01ca, 0x01f6,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
static const struct csm_coeffs rec709_coeffs = {
|
||||||
|
.limited = {
|
||||||
|
0x0018, 0x0050, 0x0008,
|
||||||
|
0x01f3, 0x01d5, 0x0038,
|
||||||
|
0x0038, 0x01cd, 0x01fb,
|
||||||
|
},
|
||||||
|
.full = {
|
||||||
|
0x001b, 0x005c, 0x0009,
|
||||||
|
0x01f1, 0x01cf, 0x0040,
|
||||||
|
0x0040, 0x01c6, 0x01fa,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
static const struct csm_coeffs rec2020_coeffs = {
|
||||||
|
.limited = {
|
||||||
|
0x001d, 0x004c, 0x0007,
|
||||||
|
0x01f0, 0x01d8, 0x0038,
|
||||||
|
0x0038, 0x01cd, 0x01fb,
|
||||||
|
},
|
||||||
|
.full = {
|
||||||
|
0x0022, 0x0057, 0x0008,
|
||||||
|
0x01ee, 0x01d2, 0x0040,
|
||||||
|
0x0040, 0x01c5, 0x01fb,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
static const struct csm_coeffs smpte240m_coeffs = {
|
||||||
|
.limited = {
|
||||||
|
0x0018, 0x004f, 0x000a,
|
||||||
|
0x01f3, 0x01d5, 0x0038,
|
||||||
|
0x0038, 0x01ce, 0x01fa,
|
||||||
|
},
|
||||||
|
.full = {
|
||||||
|
0x001b, 0x005a, 0x000b,
|
||||||
|
0x01f1, 0x01cf, 0x0040,
|
||||||
|
0x0040, 0x01c7, 0x01f9,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct csm_coeffs *coeffs;
|
||||||
|
const u16 *csm;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
if (full_range) {
|
switch (params->ycbcr_encoding) {
|
||||||
for (i = 0; i < ARRAY_SIZE(full_range_coeff); i++)
|
case V4L2_YCBCR_ENC_601:
|
||||||
rkisp1_write(params->rkisp1,
|
default:
|
||||||
RKISP1_CIF_ISP_CC_COEFF_0 + i * 4,
|
coeffs = &rec601_coeffs;
|
||||||
full_range_coeff[i]);
|
break;
|
||||||
|
case V4L2_YCBCR_ENC_709:
|
||||||
|
coeffs = &rec709_coeffs;
|
||||||
|
break;
|
||||||
|
case V4L2_YCBCR_ENC_BT2020:
|
||||||
|
coeffs = &rec2020_coeffs;
|
||||||
|
break;
|
||||||
|
case V4L2_YCBCR_ENC_SMPTE240M:
|
||||||
|
coeffs = &smpte240m_coeffs;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params->quantization == V4L2_QUANTIZATION_FULL_RANGE) {
|
||||||
|
csm = coeffs->full;
|
||||||
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
|
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
|
||||||
RKISP1_CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
|
RKISP1_CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
|
||||||
RKISP1_CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA);
|
RKISP1_CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA);
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < ARRAY_SIZE(limited_range_coeff); i++)
|
csm = coeffs->limited;
|
||||||
rkisp1_write(params->rkisp1,
|
|
||||||
RKISP1_CIF_ISP_CC_COEFF_0 + i * 4,
|
|
||||||
limited_range_coeff[i]);
|
|
||||||
|
|
||||||
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
|
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
|
||||||
RKISP1_CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
|
RKISP1_CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
|
||||||
RKISP1_CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA);
|
RKISP1_CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 9; i++)
|
||||||
|
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CC_COEFF_0 + i * 4,
|
||||||
|
csm[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ISP De-noise Pre-Filter(DPF) function */
|
/* ISP De-noise Pre-Filter(DPF) function */
|
||||||
@ -1216,11 +1258,11 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params,
|
|||||||
if (module_ens & RKISP1_CIF_ISP_MODULE_DPCC)
|
if (module_ens & RKISP1_CIF_ISP_MODULE_DPCC)
|
||||||
rkisp1_param_set_bits(params,
|
rkisp1_param_set_bits(params,
|
||||||
RKISP1_CIF_ISP_DPCC_MODE,
|
RKISP1_CIF_ISP_DPCC_MODE,
|
||||||
RKISP1_CIF_ISP_DPCC_ENA);
|
RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE);
|
||||||
else
|
else
|
||||||
rkisp1_param_clear_bits(params,
|
rkisp1_param_clear_bits(params,
|
||||||
RKISP1_CIF_ISP_DPCC_MODE,
|
RKISP1_CIF_ISP_DPCC_MODE,
|
||||||
RKISP1_CIF_ISP_DPCC_ENA);
|
RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update bls config */
|
/* update bls config */
|
||||||
@ -1255,22 +1297,6 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params,
|
|||||||
RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
|
RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update lsc config */
|
|
||||||
if (module_cfg_update & RKISP1_CIF_ISP_MODULE_LSC)
|
|
||||||
rkisp1_lsc_config(params,
|
|
||||||
&new_params->others.lsc_config);
|
|
||||||
|
|
||||||
if (module_en_update & RKISP1_CIF_ISP_MODULE_LSC) {
|
|
||||||
if (module_ens & RKISP1_CIF_ISP_MODULE_LSC)
|
|
||||||
rkisp1_param_set_bits(params,
|
|
||||||
RKISP1_CIF_ISP_LSC_CTRL,
|
|
||||||
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
|
||||||
else
|
|
||||||
rkisp1_param_clear_bits(params,
|
|
||||||
RKISP1_CIF_ISP_LSC_CTRL,
|
|
||||||
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* update awb gains */
|
/* update awb gains */
|
||||||
if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN)
|
if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN)
|
||||||
params->ops->awb_gain_config(params, &new_params->others.awb_gain_config);
|
params->ops->awb_gain_config(params, &new_params->others.awb_gain_config);
|
||||||
@ -1387,6 +1413,33 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rkisp1_isp_isr_lsc_config(struct rkisp1_params *params,
|
||||||
|
const struct rkisp1_params_cfg *new_params)
|
||||||
|
{
|
||||||
|
unsigned int module_en_update, module_cfg_update, module_ens;
|
||||||
|
|
||||||
|
module_en_update = new_params->module_en_update;
|
||||||
|
module_cfg_update = new_params->module_cfg_update;
|
||||||
|
module_ens = new_params->module_ens;
|
||||||
|
|
||||||
|
/* update lsc config */
|
||||||
|
if (module_cfg_update & RKISP1_CIF_ISP_MODULE_LSC)
|
||||||
|
rkisp1_lsc_config(params,
|
||||||
|
&new_params->others.lsc_config);
|
||||||
|
|
||||||
|
if (module_en_update & RKISP1_CIF_ISP_MODULE_LSC) {
|
||||||
|
if (module_ens & RKISP1_CIF_ISP_MODULE_LSC)
|
||||||
|
rkisp1_param_set_bits(params,
|
||||||
|
RKISP1_CIF_ISP_LSC_CTRL,
|
||||||
|
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
||||||
|
else
|
||||||
|
rkisp1_param_clear_bits(params,
|
||||||
|
RKISP1_CIF_ISP_LSC_CTRL,
|
||||||
|
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params,
|
static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params,
|
||||||
struct rkisp1_params_cfg *new_params)
|
struct rkisp1_params_cfg *new_params)
|
||||||
{
|
{
|
||||||
@ -1448,47 +1501,60 @@ static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rkisp1_params_apply_params_cfg(struct rkisp1_params *params,
|
static bool rkisp1_params_get_buffer(struct rkisp1_params *params,
|
||||||
unsigned int frame_sequence)
|
struct rkisp1_buffer **buf,
|
||||||
|
struct rkisp1_params_cfg **cfg)
|
||||||
{
|
{
|
||||||
struct rkisp1_params_cfg *new_params;
|
|
||||||
struct rkisp1_buffer *cur_buf = NULL;
|
|
||||||
|
|
||||||
if (list_empty(¶ms->params))
|
if (list_empty(¶ms->params))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
cur_buf = list_first_entry(¶ms->params,
|
*buf = list_first_entry(¶ms->params, struct rkisp1_buffer, queue);
|
||||||
struct rkisp1_buffer, queue);
|
*cfg = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0);
|
||||||
|
|
||||||
new_params = (struct rkisp1_params_cfg *)vb2_plane_vaddr(&cur_buf->vb.vb2_buf, 0);
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
rkisp1_isp_isr_other_config(params, new_params);
|
static void rkisp1_params_complete_buffer(struct rkisp1_params *params,
|
||||||
rkisp1_isp_isr_meas_config(params, new_params);
|
struct rkisp1_buffer *buf,
|
||||||
|
unsigned int frame_sequence)
|
||||||
|
{
|
||||||
|
list_del(&buf->queue);
|
||||||
|
|
||||||
/* update shadow register immediately */
|
buf->vb.sequence = frame_sequence;
|
||||||
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD);
|
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
|
||||||
|
|
||||||
list_del(&cur_buf->queue);
|
|
||||||
|
|
||||||
cur_buf->vb.sequence = frame_sequence;
|
|
||||||
vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rkisp1_params_isr(struct rkisp1_device *rkisp1)
|
void rkisp1_params_isr(struct rkisp1_device *rkisp1)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* This isr is called when the ISR finishes processing a frame (RKISP1_CIF_ISP_FRAME).
|
|
||||||
* Configurations performed here will be applied on the next frame.
|
|
||||||
* Since frame_sequence is updated on the vertical sync signal, we should use
|
|
||||||
* frame_sequence + 1 here to indicate to userspace on which frame these parameters
|
|
||||||
* are being applied.
|
|
||||||
*/
|
|
||||||
unsigned int frame_sequence = rkisp1->isp.frame_sequence + 1;
|
|
||||||
struct rkisp1_params *params = &rkisp1->params;
|
struct rkisp1_params *params = &rkisp1->params;
|
||||||
|
struct rkisp1_params_cfg *new_params;
|
||||||
|
struct rkisp1_buffer *cur_buf;
|
||||||
|
|
||||||
spin_lock(¶ms->config_lock);
|
spin_lock(¶ms->config_lock);
|
||||||
rkisp1_params_apply_params_cfg(params, frame_sequence);
|
|
||||||
|
|
||||||
|
if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params))
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
rkisp1_isp_isr_other_config(params, new_params);
|
||||||
|
rkisp1_isp_isr_lsc_config(params, new_params);
|
||||||
|
rkisp1_isp_isr_meas_config(params, new_params);
|
||||||
|
|
||||||
|
/* update shadow register immediately */
|
||||||
|
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
|
||||||
|
RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This isr is called when the ISR finishes processing a frame
|
||||||
|
* (RKISP1_CIF_ISP_FRAME). Configurations performed here will be
|
||||||
|
* applied on the next frame. Since frame_sequence is updated on the
|
||||||
|
* vertical sync signal, we should use frame_sequence + 1 here to
|
||||||
|
* indicate to userspace on which frame these parameters are being
|
||||||
|
* applied.
|
||||||
|
*/
|
||||||
|
rkisp1_params_complete_buffer(params, cur_buf,
|
||||||
|
rkisp1->isp.frame_sequence + 1);
|
||||||
|
|
||||||
|
unlock:
|
||||||
spin_unlock(¶ms->config_lock);
|
spin_unlock(¶ms->config_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1531,9 +1597,18 @@ static const struct rkisp1_cif_isp_afc_config rkisp1_afc_params_default_config =
|
|||||||
14
|
14
|
||||||
};
|
};
|
||||||
|
|
||||||
static void rkisp1_params_config_parameter(struct rkisp1_params *params)
|
void rkisp1_params_pre_configure(struct rkisp1_params *params,
|
||||||
|
enum rkisp1_fmt_raw_pat_type bayer_pat,
|
||||||
|
enum v4l2_quantization quantization,
|
||||||
|
enum v4l2_ycbcr_encoding ycbcr_encoding)
|
||||||
{
|
{
|
||||||
struct rkisp1_cif_isp_hst_config hst = rkisp1_hst_params_default_config;
|
struct rkisp1_cif_isp_hst_config hst = rkisp1_hst_params_default_config;
|
||||||
|
struct rkisp1_params_cfg *new_params;
|
||||||
|
struct rkisp1_buffer *cur_buf;
|
||||||
|
|
||||||
|
params->quantization = quantization;
|
||||||
|
params->ycbcr_encoding = ycbcr_encoding;
|
||||||
|
params->raw_type = bayer_pat;
|
||||||
|
|
||||||
params->ops->awb_meas_config(params, &rkisp1_awb_params_default_config);
|
params->ops->awb_meas_config(params, &rkisp1_awb_params_default_config);
|
||||||
params->ops->awb_meas_enable(params, &rkisp1_awb_params_default_config,
|
params->ops->awb_meas_enable(params, &rkisp1_awb_params_default_config,
|
||||||
@ -1552,27 +1627,55 @@ static void rkisp1_params_config_parameter(struct rkisp1_params *params)
|
|||||||
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_HIST_PROP_V10,
|
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_HIST_PROP_V10,
|
||||||
rkisp1_hst_params_default_config.mode);
|
rkisp1_hst_params_default_config.mode);
|
||||||
|
|
||||||
/* set the range */
|
rkisp1_csm_config(params);
|
||||||
if (params->quantization == V4L2_QUANTIZATION_FULL_RANGE)
|
|
||||||
rkisp1_csm_config(params, true);
|
|
||||||
else
|
|
||||||
rkisp1_csm_config(params, false);
|
|
||||||
|
|
||||||
spin_lock_irq(¶ms->config_lock);
|
spin_lock_irq(¶ms->config_lock);
|
||||||
|
|
||||||
/* apply the first buffer if there is one already */
|
/* apply the first buffer if there is one already */
|
||||||
rkisp1_params_apply_params_cfg(params, 0);
|
|
||||||
|
|
||||||
|
if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params))
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
rkisp1_isp_isr_other_config(params, new_params);
|
||||||
|
rkisp1_isp_isr_meas_config(params, new_params);
|
||||||
|
|
||||||
|
/* update shadow register immediately */
|
||||||
|
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
|
||||||
|
RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD);
|
||||||
|
|
||||||
|
unlock:
|
||||||
spin_unlock_irq(¶ms->config_lock);
|
spin_unlock_irq(¶ms->config_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rkisp1_params_configure(struct rkisp1_params *params,
|
void rkisp1_params_post_configure(struct rkisp1_params *params)
|
||||||
enum rkisp1_fmt_raw_pat_type bayer_pat,
|
|
||||||
enum v4l2_quantization quantization)
|
|
||||||
{
|
{
|
||||||
params->quantization = quantization;
|
struct rkisp1_params_cfg *new_params;
|
||||||
params->raw_type = bayer_pat;
|
struct rkisp1_buffer *cur_buf;
|
||||||
rkisp1_params_config_parameter(params);
|
|
||||||
|
spin_lock_irq(¶ms->config_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apply LSC parameters from the first buffer (if any is already
|
||||||
|
* available. This must be done after the ISP gets started in the
|
||||||
|
* ISP8000Nano v18.02 (found in the i.MX8MP) as access to the LSC RAM
|
||||||
|
* is gated by the ISP_CTRL.ISP_ENABLE bit. As this initialization
|
||||||
|
* ordering doesn't affect other ISP versions negatively, do so
|
||||||
|
* unconditionally.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params))
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
rkisp1_isp_isr_lsc_config(params, new_params);
|
||||||
|
|
||||||
|
/* update shadow register immediately */
|
||||||
|
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
|
||||||
|
RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD);
|
||||||
|
|
||||||
|
rkisp1_params_complete_buffer(params, cur_buf, 0);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
spin_unlock_irq(¶ms->config_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1582,7 +1685,7 @@ void rkisp1_params_configure(struct rkisp1_params *params,
|
|||||||
void rkisp1_params_disable(struct rkisp1_params *params)
|
void rkisp1_params_disable(struct rkisp1_params *params)
|
||||||
{
|
{
|
||||||
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPCC_MODE,
|
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPCC_MODE,
|
||||||
RKISP1_CIF_ISP_DPCC_ENA);
|
RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE);
|
||||||
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
|
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
|
||||||
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
RKISP1_CIF_ISP_LSC_CTRL_ENA);
|
||||||
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_BLS_CTRL,
|
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_BLS_CTRL,
|
||||||
|
@ -576,7 +576,7 @@
|
|||||||
(((v0) & 0x1FFF) | (((v1) & 0x1FFF) << 13))
|
(((v0) & 0x1FFF) | (((v1) & 0x1FFF) << 13))
|
||||||
#define RKISP1_CIF_ISP_LSC_SECT_SIZE(v0, v1) \
|
#define RKISP1_CIF_ISP_LSC_SECT_SIZE(v0, v1) \
|
||||||
(((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
|
(((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
|
||||||
#define RKISP1_CIF_ISP_LSC_GRAD_SIZE(v0, v1) \
|
#define RKISP1_CIF_ISP_LSC_SECT_GRAD(v0, v1) \
|
||||||
(((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
|
(((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
|
||||||
|
|
||||||
/* LSC: ISP_LSC_TABLE_SEL */
|
/* LSC: ISP_LSC_TABLE_SEL */
|
||||||
@ -618,19 +618,18 @@
|
|||||||
#define RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA_READ(x) (((x) >> 11) & 1)
|
#define RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA_READ(x) (((x) >> 11) & 1)
|
||||||
|
|
||||||
/* DPCC */
|
/* DPCC */
|
||||||
/* ISP_DPCC_MODE */
|
#define RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE BIT(0)
|
||||||
#define RKISP1_CIF_ISP_DPCC_ENA BIT(0)
|
#define RKISP1_CIF_ISP_DPCC_MODE_GRAYSCALE_MODE BIT(1)
|
||||||
#define RKISP1_CIF_ISP_DPCC_MODE_MAX 0x07
|
#define RKISP1_CIF_ISP_DPCC_OUTPUT_MODE_MASK GENMASK(3, 0)
|
||||||
#define RKISP1_CIF_ISP_DPCC_OUTPUTMODE_MAX 0x0F
|
#define RKISP1_CIF_ISP_DPCC_SET_USE_MASK GENMASK(3, 0)
|
||||||
#define RKISP1_CIF_ISP_DPCC_SETUSE_MAX 0x0F
|
#define RKISP1_CIF_ISP_DPCC_METHODS_SET_MASK 0x00001f1f
|
||||||
#define RKISP1_CIF_ISP_DPCC_METHODS_SET_RESERVED 0xFFFFE000
|
#define RKISP1_CIF_ISP_DPCC_LINE_THRESH_MASK 0x0000ffff
|
||||||
#define RKISP1_CIF_ISP_DPCC_LINE_THRESH_RESERVED 0xFFFF0000
|
#define RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_MASK 0x00003f3f
|
||||||
#define RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_RESERVED 0xFFFFC0C0
|
#define RKISP1_CIF_ISP_DPCC_PG_FAC_MASK 0x00003f3f
|
||||||
#define RKISP1_CIF_ISP_DPCC_PG_FAC_RESERVED 0xFFFFC0C0
|
#define RKISP1_CIF_ISP_DPCC_RND_THRESH_MASK 0x0000ffff
|
||||||
#define RKISP1_CIF_ISP_DPCC_RND_THRESH_RESERVED 0xFFFF0000
|
#define RKISP1_CIF_ISP_DPCC_RG_FAC_MASK 0x00003f3f
|
||||||
#define RKISP1_CIF_ISP_DPCC_RG_FAC_RESERVED 0xFFFFC0C0
|
#define RKISP1_CIF_ISP_DPCC_RO_LIMIT_MASK 0x00000fff
|
||||||
#define RKISP1_CIF_ISP_DPCC_RO_LIMIT_RESERVED 0xFFFFF000
|
#define RKISP1_CIF_ISP_DPCC_RND_OFFS_MASK 0x00000fff
|
||||||
#define RKISP1_CIF_ISP_DPCC_RND_OFFS_RESERVED 0xFFFFF000
|
|
||||||
|
|
||||||
/* BLS */
|
/* BLS */
|
||||||
/* ISP_BLS_CTRL */
|
/* ISP_BLS_CTRL */
|
||||||
@ -1073,22 +1072,10 @@
|
|||||||
#define RKISP1_CIF_ISP_LSC_GR_TABLE_DATA (RKISP1_CIF_ISP_LSC_BASE + 0x00000018)
|
#define RKISP1_CIF_ISP_LSC_GR_TABLE_DATA (RKISP1_CIF_ISP_LSC_BASE + 0x00000018)
|
||||||
#define RKISP1_CIF_ISP_LSC_B_TABLE_DATA (RKISP1_CIF_ISP_LSC_BASE + 0x0000001C)
|
#define RKISP1_CIF_ISP_LSC_B_TABLE_DATA (RKISP1_CIF_ISP_LSC_BASE + 0x0000001C)
|
||||||
#define RKISP1_CIF_ISP_LSC_GB_TABLE_DATA (RKISP1_CIF_ISP_LSC_BASE + 0x00000020)
|
#define RKISP1_CIF_ISP_LSC_GB_TABLE_DATA (RKISP1_CIF_ISP_LSC_BASE + 0x00000020)
|
||||||
#define RKISP1_CIF_ISP_LSC_XGRAD_01 (RKISP1_CIF_ISP_LSC_BASE + 0x00000024)
|
#define RKISP1_CIF_ISP_LSC_XGRAD(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000024 + (n) * 4)
|
||||||
#define RKISP1_CIF_ISP_LSC_XGRAD_23 (RKISP1_CIF_ISP_LSC_BASE + 0x00000028)
|
#define RKISP1_CIF_ISP_LSC_YGRAD(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000034 + (n) * 4)
|
||||||
#define RKISP1_CIF_ISP_LSC_XGRAD_45 (RKISP1_CIF_ISP_LSC_BASE + 0x0000002C)
|
#define RKISP1_CIF_ISP_LSC_XSIZE(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000044 + (n) * 4)
|
||||||
#define RKISP1_CIF_ISP_LSC_XGRAD_67 (RKISP1_CIF_ISP_LSC_BASE + 0x00000030)
|
#define RKISP1_CIF_ISP_LSC_YSIZE(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000054 + (n) * 4)
|
||||||
#define RKISP1_CIF_ISP_LSC_YGRAD_01 (RKISP1_CIF_ISP_LSC_BASE + 0x00000034)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_YGRAD_23 (RKISP1_CIF_ISP_LSC_BASE + 0x00000038)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_YGRAD_45 (RKISP1_CIF_ISP_LSC_BASE + 0x0000003C)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_YGRAD_67 (RKISP1_CIF_ISP_LSC_BASE + 0x00000040)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_XSIZE_01 (RKISP1_CIF_ISP_LSC_BASE + 0x00000044)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_XSIZE_23 (RKISP1_CIF_ISP_LSC_BASE + 0x00000048)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_XSIZE_45 (RKISP1_CIF_ISP_LSC_BASE + 0x0000004C)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_XSIZE_67 (RKISP1_CIF_ISP_LSC_BASE + 0x00000050)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_YSIZE_01 (RKISP1_CIF_ISP_LSC_BASE + 0x00000054)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_YSIZE_23 (RKISP1_CIF_ISP_LSC_BASE + 0x00000058)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_YSIZE_45 (RKISP1_CIF_ISP_LSC_BASE + 0x0000005C)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_YSIZE_67 (RKISP1_CIF_ISP_LSC_BASE + 0x00000060)
|
|
||||||
#define RKISP1_CIF_ISP_LSC_TABLE_SEL (RKISP1_CIF_ISP_LSC_BASE + 0x00000064)
|
#define RKISP1_CIF_ISP_LSC_TABLE_SEL (RKISP1_CIF_ISP_LSC_BASE + 0x00000064)
|
||||||
#define RKISP1_CIF_ISP_LSC_STATUS (RKISP1_CIF_ISP_LSC_BASE + 0x00000068)
|
#define RKISP1_CIF_ISP_LSC_STATUS (RKISP1_CIF_ISP_LSC_BASE + 0x00000068)
|
||||||
|
|
||||||
|
@ -411,6 +411,10 @@ static int rkisp1_rsz_init_config(struct v4l2_subdev *sd,
|
|||||||
sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
|
sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
|
||||||
sink_fmt->field = V4L2_FIELD_NONE;
|
sink_fmt->field = V4L2_FIELD_NONE;
|
||||||
sink_fmt->code = RKISP1_DEF_FMT;
|
sink_fmt->code = RKISP1_DEF_FMT;
|
||||||
|
sink_fmt->colorspace = V4L2_COLORSPACE_SRGB;
|
||||||
|
sink_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
|
||||||
|
sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
|
||||||
|
sink_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
|
||||||
|
|
||||||
sink_crop = v4l2_subdev_get_try_crop(sd, sd_state,
|
sink_crop = v4l2_subdev_get_try_crop(sd, sd_state,
|
||||||
RKISP1_RSZ_PAD_SINK);
|
RKISP1_RSZ_PAD_SINK);
|
||||||
@ -503,6 +507,7 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz,
|
|||||||
const struct rkisp1_mbus_info *mbus_info;
|
const struct rkisp1_mbus_info *mbus_info;
|
||||||
struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
|
struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
|
||||||
struct v4l2_rect *sink_crop;
|
struct v4l2_rect *sink_crop;
|
||||||
|
bool is_yuv;
|
||||||
|
|
||||||
sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK,
|
sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK,
|
||||||
which);
|
which);
|
||||||
@ -524,9 +529,6 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz,
|
|||||||
if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
|
if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
|
||||||
rsz->pixel_enc = mbus_info->pixel_enc;
|
rsz->pixel_enc = mbus_info->pixel_enc;
|
||||||
|
|
||||||
/* Propagete to source pad */
|
|
||||||
src_fmt->code = sink_fmt->code;
|
|
||||||
|
|
||||||
sink_fmt->width = clamp_t(u32, format->width,
|
sink_fmt->width = clamp_t(u32, format->width,
|
||||||
RKISP1_ISP_MIN_WIDTH,
|
RKISP1_ISP_MIN_WIDTH,
|
||||||
RKISP1_ISP_MAX_WIDTH);
|
RKISP1_ISP_MAX_WIDTH);
|
||||||
@ -534,8 +536,45 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz,
|
|||||||
RKISP1_ISP_MIN_HEIGHT,
|
RKISP1_ISP_MIN_HEIGHT,
|
||||||
RKISP1_ISP_MAX_HEIGHT);
|
RKISP1_ISP_MAX_HEIGHT);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adjust the color space fields. Accept any color primaries and
|
||||||
|
* transfer function for both YUV and Bayer. For YUV any YCbCr encoding
|
||||||
|
* and quantization range is also accepted. For Bayer formats, the YCbCr
|
||||||
|
* encoding isn't applicable, and the quantization range can only be
|
||||||
|
* full.
|
||||||
|
*/
|
||||||
|
is_yuv = mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV;
|
||||||
|
|
||||||
|
sink_fmt->colorspace = format->colorspace ? :
|
||||||
|
(is_yuv ? V4L2_COLORSPACE_SRGB :
|
||||||
|
V4L2_COLORSPACE_RAW);
|
||||||
|
sink_fmt->xfer_func = format->xfer_func ? :
|
||||||
|
V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace);
|
||||||
|
if (is_yuv) {
|
||||||
|
sink_fmt->ycbcr_enc = format->ycbcr_enc ? :
|
||||||
|
V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace);
|
||||||
|
sink_fmt->quantization = format->quantization ? :
|
||||||
|
V4L2_MAP_QUANTIZATION_DEFAULT(false, sink_fmt->colorspace,
|
||||||
|
sink_fmt->ycbcr_enc);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* The YCbCr encoding isn't applicable for non-YUV formats, but
|
||||||
|
* V4L2 has no "no encoding" value. Hardcode it to Rec. 601, it
|
||||||
|
* should be ignored by userspace.
|
||||||
|
*/
|
||||||
|
sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
|
||||||
|
sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
|
||||||
|
}
|
||||||
|
|
||||||
*format = *sink_fmt;
|
*format = *sink_fmt;
|
||||||
|
|
||||||
|
/* Propagate the media bus code and color space to the source pad. */
|
||||||
|
src_fmt->code = sink_fmt->code;
|
||||||
|
src_fmt->colorspace = sink_fmt->colorspace;
|
||||||
|
src_fmt->xfer_func = sink_fmt->xfer_func;
|
||||||
|
src_fmt->ycbcr_enc = sink_fmt->ycbcr_enc;
|
||||||
|
src_fmt->quantization = sink_fmt->quantization;
|
||||||
|
|
||||||
/* Update sink crop */
|
/* Update sink crop */
|
||||||
rkisp1_rsz_set_sink_crop(rsz, sd_state, sink_crop, which);
|
rkisp1_rsz_set_sink_crop(rsz, sd_state, sink_crop, which);
|
||||||
}
|
}
|
||||||
|
@ -524,7 +524,7 @@ static int fimc_capture_release(struct file *file)
|
|||||||
mutex_lock(&fimc->lock);
|
mutex_lock(&fimc->lock);
|
||||||
|
|
||||||
if (close && vc->streaming) {
|
if (close && vc->streaming) {
|
||||||
media_pipeline_stop(&vc->ve.vdev.entity);
|
video_device_pipeline_stop(&vc->ve.vdev);
|
||||||
vc->streaming = false;
|
vc->streaming = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1176,7 +1176,6 @@ static int fimc_cap_streamon(struct file *file, void *priv,
|
|||||||
{
|
{
|
||||||
struct fimc_dev *fimc = video_drvdata(file);
|
struct fimc_dev *fimc = video_drvdata(file);
|
||||||
struct fimc_vid_cap *vc = &fimc->vid_cap;
|
struct fimc_vid_cap *vc = &fimc->vid_cap;
|
||||||
struct media_entity *entity = &vc->ve.vdev.entity;
|
|
||||||
struct fimc_source_info *si = NULL;
|
struct fimc_source_info *si = NULL;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1184,7 +1183,7 @@ static int fimc_cap_streamon(struct file *file, void *priv,
|
|||||||
if (fimc_capture_active(fimc))
|
if (fimc_capture_active(fimc))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
ret = media_pipeline_start(entity, &vc->ve.pipe->mp);
|
ret = video_device_pipeline_start(&vc->ve.vdev, &vc->ve.pipe->mp);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1218,7 +1217,7 @@ static int fimc_cap_streamon(struct file *file, void *priv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
err_p_stop:
|
err_p_stop:
|
||||||
media_pipeline_stop(entity);
|
video_device_pipeline_stop(&vc->ve.vdev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1234,7 +1233,7 @@ static int fimc_cap_streamoff(struct file *file, void *priv,
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (vc->streaming) {
|
if (vc->streaming) {
|
||||||
media_pipeline_stop(&vc->ve.vdev.entity);
|
video_device_pipeline_stop(&vc->ve.vdev);
|
||||||
vc->streaming = false;
|
vc->streaming = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +312,7 @@ static int isp_video_release(struct file *file)
|
|||||||
is_singular_file = v4l2_fh_is_singular_file(file);
|
is_singular_file = v4l2_fh_is_singular_file(file);
|
||||||
|
|
||||||
if (is_singular_file && ivc->streaming) {
|
if (is_singular_file && ivc->streaming) {
|
||||||
media_pipeline_stop(entity);
|
video_device_pipeline_stop(&ivc->ve.vdev);
|
||||||
ivc->streaming = 0;
|
ivc->streaming = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,10 +490,9 @@ static int isp_video_streamon(struct file *file, void *priv,
|
|||||||
{
|
{
|
||||||
struct fimc_isp *isp = video_drvdata(file);
|
struct fimc_isp *isp = video_drvdata(file);
|
||||||
struct exynos_video_entity *ve = &isp->video_capture.ve;
|
struct exynos_video_entity *ve = &isp->video_capture.ve;
|
||||||
struct media_entity *me = &ve->vdev.entity;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = media_pipeline_start(me, &ve->pipe->mp);
|
ret = video_device_pipeline_start(&ve->vdev, &ve->pipe->mp);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -508,7 +507,7 @@ static int isp_video_streamon(struct file *file, void *priv,
|
|||||||
isp->video_capture.streaming = 1;
|
isp->video_capture.streaming = 1;
|
||||||
return 0;
|
return 0;
|
||||||
p_stop:
|
p_stop:
|
||||||
media_pipeline_stop(me);
|
video_device_pipeline_stop(&ve->vdev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,7 +522,7 @@ static int isp_video_streamoff(struct file *file, void *priv,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
media_pipeline_stop(&video->ve.vdev.entity);
|
video_device_pipeline_stop(&video->ve.vdev);
|
||||||
video->streaming = 0;
|
video->streaming = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -516,7 +516,7 @@ static int fimc_lite_release(struct file *file)
|
|||||||
if (v4l2_fh_is_singular_file(file) &&
|
if (v4l2_fh_is_singular_file(file) &&
|
||||||
atomic_read(&fimc->out_path) == FIMC_IO_DMA) {
|
atomic_read(&fimc->out_path) == FIMC_IO_DMA) {
|
||||||
if (fimc->streaming) {
|
if (fimc->streaming) {
|
||||||
media_pipeline_stop(entity);
|
video_device_pipeline_stop(&fimc->ve.vdev);
|
||||||
fimc->streaming = false;
|
fimc->streaming = false;
|
||||||
}
|
}
|
||||||
fimc_lite_stop_capture(fimc, false);
|
fimc_lite_stop_capture(fimc, false);
|
||||||
@ -812,13 +812,12 @@ static int fimc_lite_streamon(struct file *file, void *priv,
|
|||||||
enum v4l2_buf_type type)
|
enum v4l2_buf_type type)
|
||||||
{
|
{
|
||||||
struct fimc_lite *fimc = video_drvdata(file);
|
struct fimc_lite *fimc = video_drvdata(file);
|
||||||
struct media_entity *entity = &fimc->ve.vdev.entity;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (fimc_lite_active(fimc))
|
if (fimc_lite_active(fimc))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
ret = media_pipeline_start(entity, &fimc->ve.pipe->mp);
|
ret = video_device_pipeline_start(&fimc->ve.vdev, &fimc->ve.pipe->mp);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -835,7 +834,7 @@ static int fimc_lite_streamon(struct file *file, void *priv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
err_p_stop:
|
err_p_stop:
|
||||||
media_pipeline_stop(entity);
|
video_device_pipeline_stop(&fimc->ve.vdev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -849,7 +848,7 @@ static int fimc_lite_streamoff(struct file *file, void *priv,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
media_pipeline_stop(&fimc->ve.vdev.entity);
|
video_device_pipeline_stop(&fimc->ve.vdev);
|
||||||
fimc->streaming = false;
|
fimc->streaming = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -848,13 +848,13 @@ static int s3c_camif_streamon(struct file *file, void *priv,
|
|||||||
if (s3c_vp_active(vp))
|
if (s3c_vp_active(vp))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = media_pipeline_start(sensor, camif->m_pipeline);
|
ret = media_pipeline_start(sensor->pads, camif->m_pipeline);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = camif_pipeline_validate(camif);
|
ret = camif_pipeline_validate(camif);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
media_pipeline_stop(sensor);
|
media_pipeline_stop(sensor->pads);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,7 +878,7 @@ static int s3c_camif_streamoff(struct file *file, void *priv,
|
|||||||
|
|
||||||
ret = vb2_streamoff(&vp->vb_queue, type);
|
ret = vb2_streamoff(&vp->vb_queue, type);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
media_pipeline_stop(&camif->sensor.sd->entity);
|
media_pipeline_stop(camif->sensor.sd->entity.pads);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,7 +751,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|||||||
goto err_unlocked;
|
goto err_unlocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = media_pipeline_start(&dcmi->vdev->entity, &dcmi->pipeline);
|
ret = video_device_pipeline_start(dcmi->vdev, &dcmi->pipeline);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dcmi->dev, "%s: Failed to start streaming, media pipeline start error (%d)\n",
|
dev_err(dcmi->dev, "%s: Failed to start streaming, media pipeline start error (%d)\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
@ -865,7 +865,7 @@ err_pipeline_stop:
|
|||||||
dcmi_pipeline_stop(dcmi);
|
dcmi_pipeline_stop(dcmi);
|
||||||
|
|
||||||
err_media_pipeline_stop:
|
err_media_pipeline_stop:
|
||||||
media_pipeline_stop(&dcmi->vdev->entity);
|
video_device_pipeline_stop(dcmi->vdev);
|
||||||
|
|
||||||
err_pm_put:
|
err_pm_put:
|
||||||
pm_runtime_put(dcmi->dev);
|
pm_runtime_put(dcmi->dev);
|
||||||
@ -892,7 +892,7 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
|
|||||||
|
|
||||||
dcmi_pipeline_stop(dcmi);
|
dcmi_pipeline_stop(dcmi);
|
||||||
|
|
||||||
media_pipeline_stop(&dcmi->vdev->entity);
|
video_device_pipeline_stop(dcmi->vdev);
|
||||||
|
|
||||||
spin_lock_irq(&dcmi->irqlock);
|
spin_lock_irq(&dcmi->irqlock);
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
config VIDEO_SUN4I_CSI
|
config VIDEO_SUN4I_CSI
|
||||||
tristate "Allwinner A10 CMOS Sensor Interface Support"
|
tristate "Allwinner A10 CMOS Sensor Interface Support"
|
||||||
depends on V4L_PLATFORM_DRIVERS
|
depends on V4L_PLATFORM_DRIVERS
|
||||||
depends on VIDEO_DEV && COMMON_CLK && HAS_DMA
|
depends on VIDEO_DEV && COMMON_CLK && RESET_CONTROLLER && HAS_DMA
|
||||||
depends on ARCH_SUNXI || COMPILE_TEST
|
depends on ARCH_SUNXI || COMPILE_TEST
|
||||||
select MEDIA_CONTROLLER
|
select MEDIA_CONTROLLER
|
||||||
select VIDEO_V4L2_SUBDEV_API
|
select VIDEO_V4L2_SUBDEV_API
|
||||||
|
@ -266,7 +266,7 @@ static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|||||||
goto err_clear_dma_queue;
|
goto err_clear_dma_queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = media_pipeline_start(&csi->vdev.entity, &csi->vdev.pipe);
|
ret = video_device_pipeline_alloc_start(&csi->vdev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_free_scratch_buffer;
|
goto err_free_scratch_buffer;
|
||||||
|
|
||||||
@ -330,7 +330,7 @@ err_disable_device:
|
|||||||
sun4i_csi_capture_stop(csi);
|
sun4i_csi_capture_stop(csi);
|
||||||
|
|
||||||
err_disable_pipeline:
|
err_disable_pipeline:
|
||||||
media_pipeline_stop(&csi->vdev.entity);
|
video_device_pipeline_stop(&csi->vdev);
|
||||||
|
|
||||||
err_free_scratch_buffer:
|
err_free_scratch_buffer:
|
||||||
dma_free_coherent(csi->dev, csi->scratch.size, csi->scratch.vaddr,
|
dma_free_coherent(csi->dev, csi->scratch.size, csi->scratch.vaddr,
|
||||||
@ -359,7 +359,7 @@ static void sun4i_csi_stop_streaming(struct vb2_queue *vq)
|
|||||||
return_all_buffers(csi, VB2_BUF_STATE_ERROR);
|
return_all_buffers(csi, VB2_BUF_STATE_ERROR);
|
||||||
spin_unlock_irqrestore(&csi->qlock, flags);
|
spin_unlock_irqrestore(&csi->qlock, flags);
|
||||||
|
|
||||||
media_pipeline_stop(&csi->vdev.entity);
|
video_device_pipeline_stop(&csi->vdev);
|
||||||
|
|
||||||
dma_free_coherent(csi->dev, csi->scratch.size, csi->scratch.vaddr,
|
dma_free_coherent(csi->dev, csi->scratch.size, csi->scratch.vaddr,
|
||||||
csi->scratch.paddr);
|
csi->scratch.paddr);
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
config VIDEO_SUN6I_CSI
|
config VIDEO_SUN6I_CSI
|
||||||
tristate "Allwinner V3s Camera Sensor Interface driver"
|
tristate "Allwinner A31 Camera Sensor Interface (CSI) Driver"
|
||||||
depends on V4L_PLATFORM_DRIVERS
|
depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV
|
||||||
depends on VIDEO_DEV && COMMON_CLK && HAS_DMA
|
|
||||||
depends on ARCH_SUNXI || COMPILE_TEST
|
depends on ARCH_SUNXI || COMPILE_TEST
|
||||||
|
depends on PM && COMMON_CLK && RESET_CONTROLLER && HAS_DMA
|
||||||
select MEDIA_CONTROLLER
|
select MEDIA_CONTROLLER
|
||||||
select VIDEO_V4L2_SUBDEV_API
|
select VIDEO_V4L2_SUBDEV_API
|
||||||
select VIDEOBUF2_DMA_CONTIG
|
select VIDEOBUF2_DMA_CONTIG
|
||||||
select REGMAP_MMIO
|
|
||||||
select V4L2_FWNODE
|
select V4L2_FWNODE
|
||||||
|
select REGMAP_MMIO
|
||||||
help
|
help
|
||||||
Support for the Allwinner Camera Sensor Interface Controller on V3s.
|
Support for the Allwinner A31 Camera Sensor Interface (CSI)
|
||||||
|
controller, also found on other platforms such as the A83T, H3,
|
||||||
|
V3/V3s or A64.
|
||||||
|
@ -23,43 +23,27 @@
|
|||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <media/v4l2-mc.h>
|
||||||
|
|
||||||
#include "sun6i_csi.h"
|
#include "sun6i_csi.h"
|
||||||
#include "sun6i_csi_reg.h"
|
#include "sun6i_csi_reg.h"
|
||||||
|
|
||||||
#define MODULE_NAME "sun6i-csi"
|
/* Helpers */
|
||||||
|
|
||||||
struct sun6i_csi_dev {
|
|
||||||
struct sun6i_csi csi;
|
|
||||||
struct device *dev;
|
|
||||||
|
|
||||||
struct regmap *regmap;
|
|
||||||
struct clk *clk_mod;
|
|
||||||
struct clk *clk_ram;
|
|
||||||
struct reset_control *rstc_bus;
|
|
||||||
|
|
||||||
int planar_offset[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi)
|
|
||||||
{
|
|
||||||
return container_of(csi, struct sun6i_csi_dev, csi);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO add 10&12 bit YUV, RGB support */
|
/* TODO add 10&12 bit YUV, RGB support */
|
||||||
bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
|
bool sun6i_csi_is_format_supported(struct sun6i_csi_device *csi_dev,
|
||||||
u32 pixformat, u32 mbus_code)
|
u32 pixformat, u32 mbus_code)
|
||||||
{
|
{
|
||||||
struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
|
struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some video receivers have the ability to be compatible with
|
* Some video receivers have the ability to be compatible with
|
||||||
* 8bit and 16bit bus width.
|
* 8bit and 16bit bus width.
|
||||||
* Identify the media bus format from device tree.
|
* Identify the media bus format from device tree.
|
||||||
*/
|
*/
|
||||||
if ((sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_PARALLEL
|
if ((v4l2->v4l2_ep.bus_type == V4L2_MBUS_PARALLEL
|
||||||
|| sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_BT656)
|
|| v4l2->v4l2_ep.bus_type == V4L2_MBUS_BT656)
|
||||||
&& sdev->csi.v4l2_ep.bus.parallel.bus_width == 16) {
|
&& v4l2->v4l2_ep.bus.parallel.bus_width == 16) {
|
||||||
switch (pixformat) {
|
switch (pixformat) {
|
||||||
case V4L2_PIX_FMT_NV12_16L16:
|
case V4L2_PIX_FMT_NV12_16L16:
|
||||||
case V4L2_PIX_FMT_NV12:
|
case V4L2_PIX_FMT_NV12:
|
||||||
@ -76,13 +60,14 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
|
|||||||
case MEDIA_BUS_FMT_YVYU8_1X16:
|
case MEDIA_BUS_FMT_YVYU8_1X16:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
|
dev_dbg(csi_dev->dev,
|
||||||
|
"Unsupported mbus code: 0x%x\n",
|
||||||
mbus_code);
|
mbus_code);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n",
|
dev_dbg(csi_dev->dev, "Unsupported pixformat: 0x%x\n",
|
||||||
pixformat);
|
pixformat);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -139,7 +124,7 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
|
|||||||
case MEDIA_BUS_FMT_YVYU8_2X8:
|
case MEDIA_BUS_FMT_YVYU8_2X8:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
|
dev_dbg(csi_dev->dev, "Unsupported mbus code: 0x%x\n",
|
||||||
mbus_code);
|
mbus_code);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -154,67 +139,37 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
|
|||||||
return (mbus_code == MEDIA_BUS_FMT_JPEG_1X8);
|
return (mbus_code == MEDIA_BUS_FMT_JPEG_1X8);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
|
dev_dbg(csi_dev->dev, "Unsupported pixformat: 0x%x\n",
|
||||||
|
pixformat);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
|
int sun6i_csi_set_power(struct sun6i_csi_device *csi_dev, bool enable)
|
||||||
{
|
{
|
||||||
struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
|
struct device *dev = csi_dev->dev;
|
||||||
struct device *dev = sdev->dev;
|
struct regmap *regmap = csi_dev->regmap;
|
||||||
struct regmap *regmap = sdev->regmap;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!enable) {
|
if (!enable) {
|
||||||
regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
|
regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
|
||||||
|
pm_runtime_put(dev);
|
||||||
|
|
||||||
clk_disable_unprepare(sdev->clk_ram);
|
|
||||||
if (of_device_is_compatible(dev->of_node,
|
|
||||||
"allwinner,sun50i-a64-csi"))
|
|
||||||
clk_rate_exclusive_put(sdev->clk_mod);
|
|
||||||
clk_disable_unprepare(sdev->clk_mod);
|
|
||||||
reset_control_assert(sdev->rstc_bus);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_prepare_enable(sdev->clk_mod);
|
ret = pm_runtime_resume_and_get(dev);
|
||||||
if (ret) {
|
if (ret < 0)
|
||||||
dev_err(sdev->dev, "Enable csi clk err %d\n", ret);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi"))
|
|
||||||
clk_set_rate_exclusive(sdev->clk_mod, 300000000);
|
|
||||||
|
|
||||||
ret = clk_prepare_enable(sdev->clk_ram);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(sdev->dev, "Enable clk_dram_csi clk err %d\n", ret);
|
|
||||||
goto clk_mod_disable;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = reset_control_deassert(sdev->rstc_bus);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(sdev->dev, "reset err %d\n", ret);
|
|
||||||
goto clk_ram_disable;
|
|
||||||
}
|
|
||||||
|
|
||||||
regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN);
|
regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
clk_ram_disable:
|
|
||||||
clk_disable_unprepare(sdev->clk_ram);
|
|
||||||
clk_mod_disable:
|
|
||||||
if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi"))
|
|
||||||
clk_rate_exclusive_put(sdev->clk_mod);
|
|
||||||
clk_disable_unprepare(sdev->clk_mod);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum csi_input_fmt get_csi_input_format(struct sun6i_csi_dev *sdev,
|
static enum csi_input_fmt get_csi_input_format(struct sun6i_csi_device *csi_dev,
|
||||||
u32 mbus_code, u32 pixformat)
|
u32 mbus_code, u32 pixformat)
|
||||||
{
|
{
|
||||||
/* non-YUV */
|
/* non-YUV */
|
||||||
@ -232,12 +187,13 @@ static enum csi_input_fmt get_csi_input_format(struct sun6i_csi_dev *sdev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* not support YUV420 input format yet */
|
/* not support YUV420 input format yet */
|
||||||
dev_dbg(sdev->dev, "Select YUV422 as default input format of CSI.\n");
|
dev_dbg(csi_dev->dev, "Select YUV422 as default input format of CSI.\n");
|
||||||
return CSI_INPUT_FORMAT_YUV422;
|
return CSI_INPUT_FORMAT_YUV422;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev,
|
static enum csi_output_fmt
|
||||||
u32 pixformat, u32 field)
|
get_csi_output_format(struct sun6i_csi_device *csi_dev, u32 pixformat,
|
||||||
|
u32 field)
|
||||||
{
|
{
|
||||||
bool buf_interlaced = false;
|
bool buf_interlaced = false;
|
||||||
|
|
||||||
@ -296,14 +252,14 @@ static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev,
|
|||||||
return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
|
return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
|
dev_warn(csi_dev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CSI_FIELD_RAW_8;
|
return CSI_FIELD_RAW_8;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
|
static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_device *csi_dev,
|
||||||
u32 mbus_code, u32 pixformat)
|
u32 mbus_code, u32 pixformat)
|
||||||
{
|
{
|
||||||
/* Input sequence does not apply to non-YUV formats */
|
/* Input sequence does not apply to non-YUV formats */
|
||||||
@ -330,7 +286,7 @@ static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
|
|||||||
case MEDIA_BUS_FMT_YVYU8_2X8:
|
case MEDIA_BUS_FMT_YVYU8_2X8:
|
||||||
return CSI_INPUT_SEQ_YVYU;
|
return CSI_INPUT_SEQ_YVYU;
|
||||||
default:
|
default:
|
||||||
dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n",
|
dev_warn(csi_dev->dev, "Unsupported mbus code: 0x%x\n",
|
||||||
mbus_code);
|
mbus_code);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -352,7 +308,7 @@ static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
|
|||||||
case MEDIA_BUS_FMT_YVYU8_2X8:
|
case MEDIA_BUS_FMT_YVYU8_2X8:
|
||||||
return CSI_INPUT_SEQ_YUYV;
|
return CSI_INPUT_SEQ_YUYV;
|
||||||
default:
|
default:
|
||||||
dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n",
|
dev_warn(csi_dev->dev, "Unsupported mbus code: 0x%x\n",
|
||||||
mbus_code);
|
mbus_code);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -362,7 +318,7 @@ static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
|
|||||||
return CSI_INPUT_SEQ_YUYV;
|
return CSI_INPUT_SEQ_YUYV;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dev_warn(sdev->dev, "Unsupported pixformat: 0x%x, defaulting to YUYV\n",
|
dev_warn(csi_dev->dev, "Unsupported pixformat: 0x%x, defaulting to YUYV\n",
|
||||||
pixformat);
|
pixformat);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -370,23 +326,23 @@ static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
|
|||||||
return CSI_INPUT_SEQ_YUYV;
|
return CSI_INPUT_SEQ_YUYV;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
|
static void sun6i_csi_setup_bus(struct sun6i_csi_device *csi_dev)
|
||||||
{
|
{
|
||||||
struct v4l2_fwnode_endpoint *endpoint = &sdev->csi.v4l2_ep;
|
struct v4l2_fwnode_endpoint *endpoint = &csi_dev->v4l2.v4l2_ep;
|
||||||
struct sun6i_csi *csi = &sdev->csi;
|
struct sun6i_csi_config *config = &csi_dev->config;
|
||||||
unsigned char bus_width;
|
unsigned char bus_width;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
u32 cfg;
|
u32 cfg;
|
||||||
bool input_interlaced = false;
|
bool input_interlaced = false;
|
||||||
|
|
||||||
if (csi->config.field == V4L2_FIELD_INTERLACED
|
if (config->field == V4L2_FIELD_INTERLACED
|
||||||
|| csi->config.field == V4L2_FIELD_INTERLACED_TB
|
|| config->field == V4L2_FIELD_INTERLACED_TB
|
||||||
|| csi->config.field == V4L2_FIELD_INTERLACED_BT)
|
|| config->field == V4L2_FIELD_INTERLACED_BT)
|
||||||
input_interlaced = true;
|
input_interlaced = true;
|
||||||
|
|
||||||
bus_width = endpoint->bus.parallel.bus_width;
|
bus_width = endpoint->bus.parallel.bus_width;
|
||||||
|
|
||||||
regmap_read(sdev->regmap, CSI_IF_CFG_REG, &cfg);
|
regmap_read(csi_dev->regmap, CSI_IF_CFG_REG, &cfg);
|
||||||
|
|
||||||
cfg &= ~(CSI_IF_CFG_CSI_IF_MASK | CSI_IF_CFG_MIPI_IF_MASK |
|
cfg &= ~(CSI_IF_CFG_CSI_IF_MASK | CSI_IF_CFG_MIPI_IF_MASK |
|
||||||
CSI_IF_CFG_IF_DATA_WIDTH_MASK |
|
CSI_IF_CFG_IF_DATA_WIDTH_MASK |
|
||||||
@ -434,7 +390,7 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
|
|||||||
cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
|
cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_warn(sdev->dev, "Unsupported bus type: %d\n",
|
dev_warn(csi_dev->dev, "Unsupported bus type: %d\n",
|
||||||
endpoint->bus_type);
|
endpoint->bus_type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -452,54 +408,54 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
|
|||||||
case 16: /* No need to configure DATA_WIDTH for 16bit */
|
case 16: /* No need to configure DATA_WIDTH for 16bit */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width);
|
dev_warn(csi_dev->dev, "Unsupported bus width: %u\n", bus_width);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg);
|
regmap_write(csi_dev->regmap, CSI_IF_CFG_REG, cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sun6i_csi_set_format(struct sun6i_csi_dev *sdev)
|
static void sun6i_csi_set_format(struct sun6i_csi_device *csi_dev)
|
||||||
{
|
{
|
||||||
struct sun6i_csi *csi = &sdev->csi;
|
struct sun6i_csi_config *config = &csi_dev->config;
|
||||||
u32 cfg;
|
u32 cfg;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
regmap_read(sdev->regmap, CSI_CH_CFG_REG, &cfg);
|
regmap_read(csi_dev->regmap, CSI_CH_CFG_REG, &cfg);
|
||||||
|
|
||||||
cfg &= ~(CSI_CH_CFG_INPUT_FMT_MASK |
|
cfg &= ~(CSI_CH_CFG_INPUT_FMT_MASK |
|
||||||
CSI_CH_CFG_OUTPUT_FMT_MASK | CSI_CH_CFG_VFLIP_EN |
|
CSI_CH_CFG_OUTPUT_FMT_MASK | CSI_CH_CFG_VFLIP_EN |
|
||||||
CSI_CH_CFG_HFLIP_EN | CSI_CH_CFG_FIELD_SEL_MASK |
|
CSI_CH_CFG_HFLIP_EN | CSI_CH_CFG_FIELD_SEL_MASK |
|
||||||
CSI_CH_CFG_INPUT_SEQ_MASK);
|
CSI_CH_CFG_INPUT_SEQ_MASK);
|
||||||
|
|
||||||
val = get_csi_input_format(sdev, csi->config.code,
|
val = get_csi_input_format(csi_dev, config->code,
|
||||||
csi->config.pixelformat);
|
config->pixelformat);
|
||||||
cfg |= CSI_CH_CFG_INPUT_FMT(val);
|
cfg |= CSI_CH_CFG_INPUT_FMT(val);
|
||||||
|
|
||||||
val = get_csi_output_format(sdev, csi->config.pixelformat,
|
val = get_csi_output_format(csi_dev, config->pixelformat,
|
||||||
csi->config.field);
|
config->field);
|
||||||
cfg |= CSI_CH_CFG_OUTPUT_FMT(val);
|
cfg |= CSI_CH_CFG_OUTPUT_FMT(val);
|
||||||
|
|
||||||
val = get_csi_input_seq(sdev, csi->config.code,
|
val = get_csi_input_seq(csi_dev, config->code,
|
||||||
csi->config.pixelformat);
|
config->pixelformat);
|
||||||
cfg |= CSI_CH_CFG_INPUT_SEQ(val);
|
cfg |= CSI_CH_CFG_INPUT_SEQ(val);
|
||||||
|
|
||||||
if (csi->config.field == V4L2_FIELD_TOP)
|
if (config->field == V4L2_FIELD_TOP)
|
||||||
cfg |= CSI_CH_CFG_FIELD_SEL_FIELD0;
|
cfg |= CSI_CH_CFG_FIELD_SEL_FIELD0;
|
||||||
else if (csi->config.field == V4L2_FIELD_BOTTOM)
|
else if (config->field == V4L2_FIELD_BOTTOM)
|
||||||
cfg |= CSI_CH_CFG_FIELD_SEL_FIELD1;
|
cfg |= CSI_CH_CFG_FIELD_SEL_FIELD1;
|
||||||
else
|
else
|
||||||
cfg |= CSI_CH_CFG_FIELD_SEL_BOTH;
|
cfg |= CSI_CH_CFG_FIELD_SEL_BOTH;
|
||||||
|
|
||||||
regmap_write(sdev->regmap, CSI_CH_CFG_REG, cfg);
|
regmap_write(csi_dev->regmap, CSI_CH_CFG_REG, cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sun6i_csi_set_window(struct sun6i_csi_dev *sdev)
|
static void sun6i_csi_set_window(struct sun6i_csi_device *csi_dev)
|
||||||
{
|
{
|
||||||
struct sun6i_csi_config *config = &sdev->csi.config;
|
struct sun6i_csi_config *config = &csi_dev->config;
|
||||||
u32 bytesperline_y;
|
u32 bytesperline_y;
|
||||||
u32 bytesperline_c;
|
u32 bytesperline_c;
|
||||||
int *planar_offset = sdev->planar_offset;
|
int *planar_offset = csi_dev->planar_offset;
|
||||||
u32 width = config->width;
|
u32 width = config->width;
|
||||||
u32 height = config->height;
|
u32 height = config->height;
|
||||||
u32 hor_len = width;
|
u32 hor_len = width;
|
||||||
@ -509,7 +465,7 @@ static void sun6i_csi_set_window(struct sun6i_csi_dev *sdev)
|
|||||||
case V4L2_PIX_FMT_YVYU:
|
case V4L2_PIX_FMT_YVYU:
|
||||||
case V4L2_PIX_FMT_UYVY:
|
case V4L2_PIX_FMT_UYVY:
|
||||||
case V4L2_PIX_FMT_VYUY:
|
case V4L2_PIX_FMT_VYUY:
|
||||||
dev_dbg(sdev->dev,
|
dev_dbg(csi_dev->dev,
|
||||||
"Horizontal length should be 2 times of width for packed YUV formats!\n");
|
"Horizontal length should be 2 times of width for packed YUV formats!\n");
|
||||||
hor_len = width * 2;
|
hor_len = width * 2;
|
||||||
break;
|
break;
|
||||||
@ -517,10 +473,10 @@ static void sun6i_csi_set_window(struct sun6i_csi_dev *sdev)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
regmap_write(sdev->regmap, CSI_CH_HSIZE_REG,
|
regmap_write(csi_dev->regmap, CSI_CH_HSIZE_REG,
|
||||||
CSI_CH_HSIZE_HOR_LEN(hor_len) |
|
CSI_CH_HSIZE_HOR_LEN(hor_len) |
|
||||||
CSI_CH_HSIZE_HOR_START(0));
|
CSI_CH_HSIZE_HOR_START(0));
|
||||||
regmap_write(sdev->regmap, CSI_CH_VSIZE_REG,
|
regmap_write(csi_dev->regmap, CSI_CH_VSIZE_REG,
|
||||||
CSI_CH_VSIZE_VER_LEN(height) |
|
CSI_CH_VSIZE_VER_LEN(height) |
|
||||||
CSI_CH_VSIZE_VER_START(0));
|
CSI_CH_VSIZE_VER_START(0));
|
||||||
|
|
||||||
@ -552,7 +508,7 @@ static void sun6i_csi_set_window(struct sun6i_csi_dev *sdev)
|
|||||||
bytesperline_c * height;
|
bytesperline_c * height;
|
||||||
break;
|
break;
|
||||||
default: /* raw */
|
default: /* raw */
|
||||||
dev_dbg(sdev->dev,
|
dev_dbg(csi_dev->dev,
|
||||||
"Calculating pixelformat(0x%x)'s bytesperline as a packed format\n",
|
"Calculating pixelformat(0x%x)'s bytesperline as a packed format\n",
|
||||||
config->pixelformat);
|
config->pixelformat);
|
||||||
bytesperline_y = (sun6i_csi_get_bpp(config->pixelformat) *
|
bytesperline_y = (sun6i_csi_get_bpp(config->pixelformat) *
|
||||||
@ -563,46 +519,42 @@ static void sun6i_csi_set_window(struct sun6i_csi_dev *sdev)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
regmap_write(sdev->regmap, CSI_CH_BUF_LEN_REG,
|
regmap_write(csi_dev->regmap, CSI_CH_BUF_LEN_REG,
|
||||||
CSI_CH_BUF_LEN_BUF_LEN_C(bytesperline_c) |
|
CSI_CH_BUF_LEN_BUF_LEN_C(bytesperline_c) |
|
||||||
CSI_CH_BUF_LEN_BUF_LEN_Y(bytesperline_y));
|
CSI_CH_BUF_LEN_BUF_LEN_Y(bytesperline_y));
|
||||||
}
|
}
|
||||||
|
|
||||||
int sun6i_csi_update_config(struct sun6i_csi *csi,
|
int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev,
|
||||||
struct sun6i_csi_config *config)
|
struct sun6i_csi_config *config)
|
||||||
{
|
{
|
||||||
struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
|
|
||||||
|
|
||||||
if (!config)
|
if (!config)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
memcpy(&csi->config, config, sizeof(csi->config));
|
memcpy(&csi_dev->config, config, sizeof(csi_dev->config));
|
||||||
|
|
||||||
sun6i_csi_setup_bus(sdev);
|
sun6i_csi_setup_bus(csi_dev);
|
||||||
sun6i_csi_set_format(sdev);
|
sun6i_csi_set_format(csi_dev);
|
||||||
sun6i_csi_set_window(sdev);
|
sun6i_csi_set_window(csi_dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sun6i_csi_update_buf_addr(struct sun6i_csi *csi, dma_addr_t addr)
|
void sun6i_csi_update_buf_addr(struct sun6i_csi_device *csi_dev,
|
||||||
|
dma_addr_t addr)
|
||||||
{
|
{
|
||||||
struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
|
regmap_write(csi_dev->regmap, CSI_CH_F0_BUFA_REG,
|
||||||
|
(addr + csi_dev->planar_offset[0]) >> 2);
|
||||||
regmap_write(sdev->regmap, CSI_CH_F0_BUFA_REG,
|
if (csi_dev->planar_offset[1] != -1)
|
||||||
(addr + sdev->planar_offset[0]) >> 2);
|
regmap_write(csi_dev->regmap, CSI_CH_F1_BUFA_REG,
|
||||||
if (sdev->planar_offset[1] != -1)
|
(addr + csi_dev->planar_offset[1]) >> 2);
|
||||||
regmap_write(sdev->regmap, CSI_CH_F1_BUFA_REG,
|
if (csi_dev->planar_offset[2] != -1)
|
||||||
(addr + sdev->planar_offset[1]) >> 2);
|
regmap_write(csi_dev->regmap, CSI_CH_F2_BUFA_REG,
|
||||||
if (sdev->planar_offset[2] != -1)
|
(addr + csi_dev->planar_offset[2]) >> 2);
|
||||||
regmap_write(sdev->regmap, CSI_CH_F2_BUFA_REG,
|
|
||||||
(addr + sdev->planar_offset[2]) >> 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable)
|
void sun6i_csi_set_stream(struct sun6i_csi_device *csi_dev, bool enable)
|
||||||
{
|
{
|
||||||
struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
|
struct regmap *regmap = csi_dev->regmap;
|
||||||
struct regmap *regmap = sdev->regmap;
|
|
||||||
|
|
||||||
if (!enable) {
|
if (!enable) {
|
||||||
regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0);
|
regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0);
|
||||||
@ -623,10 +575,15 @@ void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable)
|
|||||||
CSI_CAP_CH0_VCAP_ON);
|
CSI_CAP_CH0_VCAP_ON);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* Media */
|
||||||
* Media Controller and V4L2
|
|
||||||
*/
|
static const struct media_device_ops sun6i_csi_media_ops = {
|
||||||
static int sun6i_csi_link_entity(struct sun6i_csi *csi,
|
.link_notify = v4l2_pipeline_link_notify,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* V4L2 */
|
||||||
|
|
||||||
|
static int sun6i_csi_link_entity(struct sun6i_csi_device *csi_dev,
|
||||||
struct media_entity *entity,
|
struct media_entity *entity,
|
||||||
struct fwnode_handle *fwnode)
|
struct fwnode_handle *fwnode)
|
||||||
{
|
{
|
||||||
@ -637,24 +594,25 @@ static int sun6i_csi_link_entity(struct sun6i_csi *csi,
|
|||||||
|
|
||||||
ret = media_entity_get_fwnode_pad(entity, fwnode, MEDIA_PAD_FL_SOURCE);
|
ret = media_entity_get_fwnode_pad(entity, fwnode, MEDIA_PAD_FL_SOURCE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(csi->dev, "%s: no source pad in external entity %s\n",
|
dev_err(csi_dev->dev,
|
||||||
__func__, entity->name);
|
"%s: no source pad in external entity %s\n", __func__,
|
||||||
|
entity->name);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
src_pad_index = ret;
|
src_pad_index = ret;
|
||||||
|
|
||||||
sink = &csi->video.vdev.entity;
|
sink = &csi_dev->video.video_dev.entity;
|
||||||
sink_pad = &csi->video.pad;
|
sink_pad = &csi_dev->video.pad;
|
||||||
|
|
||||||
dev_dbg(csi->dev, "creating %s:%u -> %s:%u link\n",
|
dev_dbg(csi_dev->dev, "creating %s:%u -> %s:%u link\n",
|
||||||
entity->name, src_pad_index, sink->name, sink_pad->index);
|
entity->name, src_pad_index, sink->name, sink_pad->index);
|
||||||
ret = media_create_pad_link(entity, src_pad_index, sink,
|
ret = media_create_pad_link(entity, src_pad_index, sink,
|
||||||
sink_pad->index,
|
sink_pad->index,
|
||||||
MEDIA_LNK_FL_ENABLED |
|
MEDIA_LNK_FL_ENABLED |
|
||||||
MEDIA_LNK_FL_IMMUTABLE);
|
MEDIA_LNK_FL_IMMUTABLE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(csi->dev, "failed to create %s:%u -> %s:%u link\n",
|
dev_err(csi_dev->dev, "failed to create %s:%u -> %s:%u link\n",
|
||||||
entity->name, src_pad_index,
|
entity->name, src_pad_index,
|
||||||
sink->name, sink_pad->index);
|
sink->name, sink_pad->index);
|
||||||
return ret;
|
return ret;
|
||||||
@ -665,27 +623,29 @@ static int sun6i_csi_link_entity(struct sun6i_csi *csi,
|
|||||||
|
|
||||||
static int sun6i_subdev_notify_complete(struct v4l2_async_notifier *notifier)
|
static int sun6i_subdev_notify_complete(struct v4l2_async_notifier *notifier)
|
||||||
{
|
{
|
||||||
struct sun6i_csi *csi = container_of(notifier, struct sun6i_csi,
|
struct sun6i_csi_device *csi_dev =
|
||||||
notifier);
|
container_of(notifier, struct sun6i_csi_device,
|
||||||
struct v4l2_device *v4l2_dev = &csi->v4l2_dev;
|
v4l2.notifier);
|
||||||
|
struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2;
|
||||||
|
struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dev_dbg(csi->dev, "notify complete, all subdevs registered\n");
|
dev_dbg(csi_dev->dev, "notify complete, all subdevs registered\n");
|
||||||
|
|
||||||
sd = list_first_entry(&v4l2_dev->subdevs, struct v4l2_subdev, list);
|
sd = list_first_entry(&v4l2_dev->subdevs, struct v4l2_subdev, list);
|
||||||
if (!sd)
|
if (!sd)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = sun6i_csi_link_entity(csi, &sd->entity, sd->fwnode);
|
ret = sun6i_csi_link_entity(csi_dev, &sd->entity, sd->fwnode);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
|
ret = v4l2_device_register_subdev_nodes(v4l2_dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return media_device_register(&csi->media_dev);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct v4l2_async_notifier_operations sun6i_csi_async_ops = {
|
static const struct v4l2_async_notifier_operations sun6i_csi_async_ops = {
|
||||||
@ -696,7 +656,7 @@ static int sun6i_csi_fwnode_parse(struct device *dev,
|
|||||||
struct v4l2_fwnode_endpoint *vep,
|
struct v4l2_fwnode_endpoint *vep,
|
||||||
struct v4l2_async_subdev *asd)
|
struct v4l2_async_subdev *asd)
|
||||||
{
|
{
|
||||||
struct sun6i_csi *csi = dev_get_drvdata(dev);
|
struct sun6i_csi_device *csi_dev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (vep->base.port || vep->base.id) {
|
if (vep->base.port || vep->base.id) {
|
||||||
dev_warn(dev, "Only support a single port with one endpoint\n");
|
dev_warn(dev, "Only support a single port with one endpoint\n");
|
||||||
@ -706,7 +666,7 @@ static int sun6i_csi_fwnode_parse(struct device *dev,
|
|||||||
switch (vep->bus_type) {
|
switch (vep->bus_type) {
|
||||||
case V4L2_MBUS_PARALLEL:
|
case V4L2_MBUS_PARALLEL:
|
||||||
case V4L2_MBUS_BT656:
|
case V4L2_MBUS_BT656:
|
||||||
csi->v4l2_ep = *vep;
|
csi_dev->v4l2.v4l2_ep = *vep;
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
dev_err(dev, "Unsupported media bus type\n");
|
dev_err(dev, "Unsupported media bus type\n");
|
||||||
@ -714,87 +674,102 @@ static int sun6i_csi_fwnode_parse(struct device *dev,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sun6i_csi_v4l2_cleanup(struct sun6i_csi *csi)
|
static int sun6i_csi_v4l2_setup(struct sun6i_csi_device *csi_dev)
|
||||||
{
|
|
||||||
media_device_unregister(&csi->media_dev);
|
|
||||||
v4l2_async_nf_unregister(&csi->notifier);
|
|
||||||
v4l2_async_nf_cleanup(&csi->notifier);
|
|
||||||
sun6i_video_cleanup(&csi->video);
|
|
||||||
v4l2_device_unregister(&csi->v4l2_dev);
|
|
||||||
v4l2_ctrl_handler_free(&csi->ctrl_handler);
|
|
||||||
media_device_cleanup(&csi->media_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sun6i_csi_v4l2_init(struct sun6i_csi *csi)
|
|
||||||
{
|
{
|
||||||
|
struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2;
|
||||||
|
struct media_device *media_dev = &v4l2->media_dev;
|
||||||
|
struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev;
|
||||||
|
struct v4l2_async_notifier *notifier = &v4l2->notifier;
|
||||||
|
struct device *dev = csi_dev->dev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
csi->media_dev.dev = csi->dev;
|
/* Media Device */
|
||||||
strscpy(csi->media_dev.model, "Allwinner Video Capture Device",
|
|
||||||
sizeof(csi->media_dev.model));
|
|
||||||
csi->media_dev.hw_revision = 0;
|
|
||||||
|
|
||||||
media_device_init(&csi->media_dev);
|
strscpy(media_dev->model, SUN6I_CSI_DESCRIPTION,
|
||||||
v4l2_async_nf_init(&csi->notifier);
|
sizeof(media_dev->model));
|
||||||
|
media_dev->hw_revision = 0;
|
||||||
|
media_dev->ops = &sun6i_csi_media_ops;
|
||||||
|
media_dev->dev = dev;
|
||||||
|
|
||||||
ret = v4l2_ctrl_handler_init(&csi->ctrl_handler, 0);
|
media_device_init(media_dev);
|
||||||
|
|
||||||
|
ret = media_device_register(media_dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(csi->dev, "V4L2 controls handler init failed (%d)\n",
|
dev_err(dev, "failed to register media device: %d\n", ret);
|
||||||
ret);
|
goto error_media;
|
||||||
goto clean_media;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
csi->v4l2_dev.mdev = &csi->media_dev;
|
/* V4L2 Device */
|
||||||
csi->v4l2_dev.ctrl_handler = &csi->ctrl_handler;
|
|
||||||
ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
|
v4l2_dev->mdev = media_dev;
|
||||||
|
|
||||||
|
ret = v4l2_device_register(dev, v4l2_dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(csi->dev, "V4L2 device registration failed (%d)\n",
|
dev_err(dev, "failed to register v4l2 device: %d\n", ret);
|
||||||
ret);
|
goto error_media;
|
||||||
goto free_ctrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sun6i_video_init(&csi->video, csi, "sun6i-csi");
|
/* Video */
|
||||||
|
|
||||||
|
ret = sun6i_video_setup(csi_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto unreg_v4l2;
|
goto error_v4l2_device;
|
||||||
|
|
||||||
ret = v4l2_async_nf_parse_fwnode_endpoints(csi->dev,
|
/* V4L2 Async */
|
||||||
&csi->notifier,
|
|
||||||
|
v4l2_async_nf_init(notifier);
|
||||||
|
notifier->ops = &sun6i_csi_async_ops;
|
||||||
|
|
||||||
|
ret = v4l2_async_nf_parse_fwnode_endpoints(dev, notifier,
|
||||||
sizeof(struct
|
sizeof(struct
|
||||||
v4l2_async_subdev),
|
v4l2_async_subdev),
|
||||||
sun6i_csi_fwnode_parse);
|
sun6i_csi_fwnode_parse);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clean_video;
|
goto error_video;
|
||||||
|
|
||||||
csi->notifier.ops = &sun6i_csi_async_ops;
|
ret = v4l2_async_nf_register(v4l2_dev, notifier);
|
||||||
|
|
||||||
ret = v4l2_async_nf_register(&csi->v4l2_dev, &csi->notifier);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(csi->dev, "notifier registration failed\n");
|
dev_err(dev, "failed to register v4l2 async notifier: %d\n",
|
||||||
goto clean_video;
|
ret);
|
||||||
|
goto error_v4l2_async_notifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
clean_video:
|
error_v4l2_async_notifier:
|
||||||
sun6i_video_cleanup(&csi->video);
|
v4l2_async_nf_cleanup(notifier);
|
||||||
unreg_v4l2:
|
|
||||||
v4l2_device_unregister(&csi->v4l2_dev);
|
error_video:
|
||||||
free_ctrl:
|
sun6i_video_cleanup(csi_dev);
|
||||||
v4l2_ctrl_handler_free(&csi->ctrl_handler);
|
|
||||||
clean_media:
|
error_v4l2_device:
|
||||||
v4l2_async_nf_cleanup(&csi->notifier);
|
v4l2_device_unregister(&v4l2->v4l2_dev);
|
||||||
media_device_cleanup(&csi->media_dev);
|
|
||||||
|
error_media:
|
||||||
|
media_device_unregister(media_dev);
|
||||||
|
media_device_cleanup(media_dev);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
static void sun6i_csi_v4l2_cleanup(struct sun6i_csi_device *csi_dev)
|
||||||
* Resources and IRQ
|
|
||||||
*/
|
|
||||||
static irqreturn_t sun6i_csi_isr(int irq, void *dev_id)
|
|
||||||
{
|
{
|
||||||
struct sun6i_csi_dev *sdev = (struct sun6i_csi_dev *)dev_id;
|
struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2;
|
||||||
struct regmap *regmap = sdev->regmap;
|
|
||||||
|
media_device_unregister(&v4l2->media_dev);
|
||||||
|
v4l2_async_nf_unregister(&v4l2->notifier);
|
||||||
|
v4l2_async_nf_cleanup(&v4l2->notifier);
|
||||||
|
sun6i_video_cleanup(csi_dev);
|
||||||
|
v4l2_device_unregister(&v4l2->v4l2_dev);
|
||||||
|
media_device_cleanup(&v4l2->media_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Platform */
|
||||||
|
|
||||||
|
static irqreturn_t sun6i_csi_interrupt(int irq, void *private)
|
||||||
|
{
|
||||||
|
struct sun6i_csi_device *csi_dev = private;
|
||||||
|
struct regmap *regmap = csi_dev->regmap;
|
||||||
u32 status;
|
u32 status;
|
||||||
|
|
||||||
regmap_read(regmap, CSI_CH_INT_STA_REG, &status);
|
regmap_read(regmap, CSI_CH_INT_STA_REG, &status);
|
||||||
@ -814,13 +789,63 @@ static irqreturn_t sun6i_csi_isr(int irq, void *dev_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status & CSI_CH_INT_STA_FD_PD)
|
if (status & CSI_CH_INT_STA_FD_PD)
|
||||||
sun6i_video_frame_done(&sdev->csi.video);
|
sun6i_video_frame_done(csi_dev);
|
||||||
|
|
||||||
regmap_write(regmap, CSI_CH_INT_STA_REG, status);
|
regmap_write(regmap, CSI_CH_INT_STA_REG, status);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sun6i_csi_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct sun6i_csi_device *csi_dev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
reset_control_assert(csi_dev->reset);
|
||||||
|
clk_disable_unprepare(csi_dev->clock_ram);
|
||||||
|
clk_disable_unprepare(csi_dev->clock_mod);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun6i_csi_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct sun6i_csi_device *csi_dev = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = reset_control_deassert(csi_dev->reset);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to deassert reset\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(csi_dev->clock_mod);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to enable module clock\n");
|
||||||
|
goto error_reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(csi_dev->clock_ram);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to enable ram clock\n");
|
||||||
|
goto error_clock_mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error_clock_mod:
|
||||||
|
clk_disable_unprepare(csi_dev->clock_mod);
|
||||||
|
|
||||||
|
error_reset:
|
||||||
|
reset_control_assert(csi_dev->reset);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops sun6i_csi_pm_ops = {
|
||||||
|
.runtime_suspend = sun6i_csi_suspend,
|
||||||
|
.runtime_resume = sun6i_csi_resume,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct regmap_config sun6i_csi_regmap_config = {
|
static const struct regmap_config sun6i_csi_regmap_config = {
|
||||||
.reg_bits = 32,
|
.reg_bits = 32,
|
||||||
.reg_stride = 4,
|
.reg_stride = 4,
|
||||||
@ -828,106 +853,181 @@ static const struct regmap_config sun6i_csi_regmap_config = {
|
|||||||
.max_register = 0x9c,
|
.max_register = 0x9c,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
|
static int sun6i_csi_resources_setup(struct sun6i_csi_device *csi_dev,
|
||||||
struct platform_device *pdev)
|
struct platform_device *platform_dev)
|
||||||
{
|
{
|
||||||
|
struct device *dev = csi_dev->dev;
|
||||||
|
const struct sun6i_csi_variant *variant;
|
||||||
void __iomem *io_base;
|
void __iomem *io_base;
|
||||||
int ret;
|
int ret;
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
io_base = devm_platform_ioremap_resource(pdev, 0);
|
variant = of_device_get_match_data(dev);
|
||||||
|
if (!variant)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Registers */
|
||||||
|
|
||||||
|
io_base = devm_platform_ioremap_resource(platform_dev, 0);
|
||||||
if (IS_ERR(io_base))
|
if (IS_ERR(io_base))
|
||||||
return PTR_ERR(io_base);
|
return PTR_ERR(io_base);
|
||||||
|
|
||||||
sdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", io_base,
|
csi_dev->regmap = devm_regmap_init_mmio_clk(dev, "bus", io_base,
|
||||||
&sun6i_csi_regmap_config);
|
&sun6i_csi_regmap_config);
|
||||||
if (IS_ERR(sdev->regmap)) {
|
if (IS_ERR(csi_dev->regmap)) {
|
||||||
dev_err(&pdev->dev, "Failed to init register map\n");
|
dev_err(dev, "failed to init register map\n");
|
||||||
return PTR_ERR(sdev->regmap);
|
return PTR_ERR(csi_dev->regmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
sdev->clk_mod = devm_clk_get(&pdev->dev, "mod");
|
/* Clocks */
|
||||||
if (IS_ERR(sdev->clk_mod)) {
|
|
||||||
dev_err(&pdev->dev, "Unable to acquire csi clock\n");
|
csi_dev->clock_mod = devm_clk_get(dev, "mod");
|
||||||
return PTR_ERR(sdev->clk_mod);
|
if (IS_ERR(csi_dev->clock_mod)) {
|
||||||
|
dev_err(dev, "failed to acquire module clock\n");
|
||||||
|
return PTR_ERR(csi_dev->clock_mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
sdev->clk_ram = devm_clk_get(&pdev->dev, "ram");
|
csi_dev->clock_ram = devm_clk_get(dev, "ram");
|
||||||
if (IS_ERR(sdev->clk_ram)) {
|
if (IS_ERR(csi_dev->clock_ram)) {
|
||||||
dev_err(&pdev->dev, "Unable to acquire dram-csi clock\n");
|
dev_err(dev, "failed to acquire ram clock\n");
|
||||||
return PTR_ERR(sdev->clk_ram);
|
return PTR_ERR(csi_dev->clock_ram);
|
||||||
}
|
}
|
||||||
|
|
||||||
sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev, NULL);
|
ret = clk_set_rate_exclusive(csi_dev->clock_mod,
|
||||||
if (IS_ERR(sdev->rstc_bus)) {
|
variant->clock_mod_rate);
|
||||||
dev_err(&pdev->dev, "Cannot get reset controller\n");
|
|
||||||
return PTR_ERR(sdev->rstc_bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
|
||||||
if (irq < 0)
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, irq, sun6i_csi_isr, 0, MODULE_NAME,
|
|
||||||
sdev);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Cannot request csi IRQ\n");
|
dev_err(dev, "failed to set mod clock rate\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reset */
|
||||||
|
|
||||||
|
csi_dev->reset = devm_reset_control_get_shared(dev, NULL);
|
||||||
|
if (IS_ERR(csi_dev->reset)) {
|
||||||
|
dev_err(dev, "failed to acquire reset\n");
|
||||||
|
ret = PTR_ERR(csi_dev->reset);
|
||||||
|
goto error_clock_rate_exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interrupt */
|
||||||
|
|
||||||
|
irq = platform_get_irq(platform_dev, 0);
|
||||||
|
if (irq < 0) {
|
||||||
|
dev_err(dev, "failed to get interrupt\n");
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto error_clock_rate_exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(dev, irq, sun6i_csi_interrupt, 0, SUN6I_CSI_NAME,
|
||||||
|
csi_dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to request interrupt\n");
|
||||||
|
goto error_clock_rate_exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Runtime PM */
|
||||||
|
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error_clock_rate_exclusive:
|
||||||
|
clk_rate_exclusive_put(csi_dev->clock_mod);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun6i_csi_probe(struct platform_device *pdev)
|
static void sun6i_csi_resources_cleanup(struct sun6i_csi_device *csi_dev)
|
||||||
{
|
{
|
||||||
struct sun6i_csi_dev *sdev;
|
pm_runtime_disable(csi_dev->dev);
|
||||||
|
clk_rate_exclusive_put(csi_dev->clock_mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun6i_csi_probe(struct platform_device *platform_dev)
|
||||||
|
{
|
||||||
|
struct sun6i_csi_device *csi_dev;
|
||||||
|
struct device *dev = &platform_dev->dev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
|
csi_dev = devm_kzalloc(dev, sizeof(*csi_dev), GFP_KERNEL);
|
||||||
if (!sdev)
|
if (!csi_dev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
sdev->dev = &pdev->dev;
|
csi_dev->dev = &platform_dev->dev;
|
||||||
|
platform_set_drvdata(platform_dev, csi_dev);
|
||||||
|
|
||||||
ret = sun6i_csi_resource_request(sdev, pdev);
|
ret = sun6i_csi_resources_setup(csi_dev, platform_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, sdev);
|
ret = sun6i_csi_v4l2_setup(csi_dev);
|
||||||
|
if (ret)
|
||||||
|
goto error_resources;
|
||||||
|
|
||||||
sdev->csi.dev = &pdev->dev;
|
return 0;
|
||||||
return sun6i_csi_v4l2_init(&sdev->csi);
|
|
||||||
|
error_resources:
|
||||||
|
sun6i_csi_resources_cleanup(csi_dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun6i_csi_remove(struct platform_device *pdev)
|
static int sun6i_csi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sun6i_csi_dev *sdev = platform_get_drvdata(pdev);
|
struct sun6i_csi_device *csi_dev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
sun6i_csi_v4l2_cleanup(&sdev->csi);
|
sun6i_csi_v4l2_cleanup(csi_dev);
|
||||||
|
sun6i_csi_resources_cleanup(csi_dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct sun6i_csi_variant sun6i_a31_csi_variant = {
|
||||||
|
.clock_mod_rate = 297000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sun6i_csi_variant sun50i_a64_csi_variant = {
|
||||||
|
.clock_mod_rate = 300000000,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id sun6i_csi_of_match[] = {
|
static const struct of_device_id sun6i_csi_of_match[] = {
|
||||||
{ .compatible = "allwinner,sun6i-a31-csi", },
|
{
|
||||||
{ .compatible = "allwinner,sun8i-a83t-csi", },
|
.compatible = "allwinner,sun6i-a31-csi",
|
||||||
{ .compatible = "allwinner,sun8i-h3-csi", },
|
.data = &sun6i_a31_csi_variant,
|
||||||
{ .compatible = "allwinner,sun8i-v3s-csi", },
|
},
|
||||||
{ .compatible = "allwinner,sun50i-a64-csi", },
|
{
|
||||||
|
.compatible = "allwinner,sun8i-a83t-csi",
|
||||||
|
.data = &sun6i_a31_csi_variant,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "allwinner,sun8i-h3-csi",
|
||||||
|
.data = &sun6i_a31_csi_variant,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "allwinner,sun8i-v3s-csi",
|
||||||
|
.data = &sun6i_a31_csi_variant,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "allwinner,sun50i-a64-csi",
|
||||||
|
.data = &sun50i_a64_csi_variant,
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(of, sun6i_csi_of_match);
|
MODULE_DEVICE_TABLE(of, sun6i_csi_of_match);
|
||||||
|
|
||||||
static struct platform_driver sun6i_csi_platform_driver = {
|
static struct platform_driver sun6i_csi_platform_driver = {
|
||||||
.probe = sun6i_csi_probe,
|
.probe = sun6i_csi_probe,
|
||||||
.remove = sun6i_csi_remove,
|
.remove = sun6i_csi_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = MODULE_NAME,
|
.name = SUN6I_CSI_NAME,
|
||||||
.of_match_table = of_match_ptr(sun6i_csi_of_match),
|
.of_match_table = of_match_ptr(sun6i_csi_of_match),
|
||||||
|
.pm = &sun6i_csi_pm_ops,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module_platform_driver(sun6i_csi_platform_driver);
|
module_platform_driver(sun6i_csi_platform_driver);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("Allwinner V3s Camera Sensor Interface driver");
|
MODULE_DESCRIPTION("Allwinner A31 Camera Sensor Interface driver");
|
||||||
MODULE_AUTHOR("Yong Deng <yong.deng@magewell.com>");
|
MODULE_AUTHOR("Yong Deng <yong.deng@magewell.com>");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
@ -8,13 +8,22 @@
|
|||||||
#ifndef __SUN6I_CSI_H__
|
#ifndef __SUN6I_CSI_H__
|
||||||
#define __SUN6I_CSI_H__
|
#define __SUN6I_CSI_H__
|
||||||
|
|
||||||
#include <media/v4l2-ctrls.h>
|
|
||||||
#include <media/v4l2-device.h>
|
#include <media/v4l2-device.h>
|
||||||
#include <media/v4l2-fwnode.h>
|
#include <media/v4l2-fwnode.h>
|
||||||
|
#include <media/videobuf2-v4l2.h>
|
||||||
|
|
||||||
#include "sun6i_video.h"
|
#include "sun6i_video.h"
|
||||||
|
|
||||||
struct sun6i_csi;
|
#define SUN6I_CSI_NAME "sun6i-csi"
|
||||||
|
#define SUN6I_CSI_DESCRIPTION "Allwinner A31 CSI Device"
|
||||||
|
|
||||||
|
struct sun6i_csi_buffer {
|
||||||
|
struct vb2_v4l2_buffer v4l2_buffer;
|
||||||
|
struct list_head list;
|
||||||
|
|
||||||
|
dma_addr_t dma_addr;
|
||||||
|
bool queued_to_csi;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct sun6i_csi_config - configs for sun6i csi
|
* struct sun6i_csi_config - configs for sun6i csi
|
||||||
@ -32,59 +41,78 @@ struct sun6i_csi_config {
|
|||||||
u32 height;
|
u32 height;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sun6i_csi {
|
struct sun6i_csi_v4l2 {
|
||||||
struct device *dev;
|
|
||||||
struct v4l2_ctrl_handler ctrl_handler;
|
|
||||||
struct v4l2_device v4l2_dev;
|
struct v4l2_device v4l2_dev;
|
||||||
struct media_device media_dev;
|
struct media_device media_dev;
|
||||||
|
|
||||||
struct v4l2_async_notifier notifier;
|
struct v4l2_async_notifier notifier;
|
||||||
|
|
||||||
/* video port settings */
|
/* video port settings */
|
||||||
struct v4l2_fwnode_endpoint v4l2_ep;
|
struct v4l2_fwnode_endpoint v4l2_ep;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sun6i_csi_device {
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
struct sun6i_csi_config config;
|
struct sun6i_csi_config config;
|
||||||
|
struct sun6i_csi_v4l2 v4l2;
|
||||||
struct sun6i_video video;
|
struct sun6i_video video;
|
||||||
|
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct clk *clock_mod;
|
||||||
|
struct clk *clock_ram;
|
||||||
|
struct reset_control *reset;
|
||||||
|
|
||||||
|
int planar_offset[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sun6i_csi_variant {
|
||||||
|
unsigned long clock_mod_rate;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sun6i_csi_is_format_supported() - check if the format supported by csi
|
* sun6i_csi_is_format_supported() - check if the format supported by csi
|
||||||
* @csi: pointer to the csi
|
* @csi_dev: pointer to the csi device
|
||||||
* @pixformat: v4l2 pixel format (V4L2_PIX_FMT_*)
|
* @pixformat: v4l2 pixel format (V4L2_PIX_FMT_*)
|
||||||
* @mbus_code: media bus format code (MEDIA_BUS_FMT_*)
|
* @mbus_code: media bus format code (MEDIA_BUS_FMT_*)
|
||||||
|
*
|
||||||
|
* Return: true if format is supported, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool sun6i_csi_is_format_supported(struct sun6i_csi *csi, u32 pixformat,
|
bool sun6i_csi_is_format_supported(struct sun6i_csi_device *csi_dev,
|
||||||
u32 mbus_code);
|
u32 pixformat, u32 mbus_code);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sun6i_csi_set_power() - power on/off the csi
|
* sun6i_csi_set_power() - power on/off the csi
|
||||||
* @csi: pointer to the csi
|
* @csi_dev: pointer to the csi device
|
||||||
* @enable: on/off
|
* @enable: on/off
|
||||||
|
*
|
||||||
|
* Return: 0 if successful, error code otherwise.
|
||||||
*/
|
*/
|
||||||
int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable);
|
int sun6i_csi_set_power(struct sun6i_csi_device *csi_dev, bool enable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sun6i_csi_update_config() - update the csi register settings
|
* sun6i_csi_update_config() - update the csi register settings
|
||||||
* @csi: pointer to the csi
|
* @csi_dev: pointer to the csi device
|
||||||
* @config: see struct sun6i_csi_config
|
* @config: see struct sun6i_csi_config
|
||||||
|
*
|
||||||
|
* Return: 0 if successful, error code otherwise.
|
||||||
*/
|
*/
|
||||||
int sun6i_csi_update_config(struct sun6i_csi *csi,
|
int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev,
|
||||||
struct sun6i_csi_config *config);
|
struct sun6i_csi_config *config);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sun6i_csi_update_buf_addr() - update the csi frame buffer address
|
* sun6i_csi_update_buf_addr() - update the csi frame buffer address
|
||||||
* @csi: pointer to the csi
|
* @csi_dev: pointer to the csi device
|
||||||
* @addr: frame buffer's physical address
|
* @addr: frame buffer's physical address
|
||||||
*/
|
*/
|
||||||
void sun6i_csi_update_buf_addr(struct sun6i_csi *csi, dma_addr_t addr);
|
void sun6i_csi_update_buf_addr(struct sun6i_csi_device *csi_dev,
|
||||||
|
dma_addr_t addr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sun6i_csi_set_stream() - start/stop csi streaming
|
* sun6i_csi_set_stream() - start/stop csi streaming
|
||||||
* @csi: pointer to the csi
|
* @csi_dev: pointer to the csi device
|
||||||
* @enable: start/stop
|
* @enable: start/stop
|
||||||
*/
|
*/
|
||||||
void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable);
|
void sun6i_csi_set_stream(struct sun6i_csi_device *csi_dev, bool enable);
|
||||||
|
|
||||||
/* get bpp form v4l2 pixformat */
|
/* get bpp form v4l2 pixformat */
|
||||||
static inline int sun6i_csi_get_bpp(unsigned int pixformat)
|
static inline int sun6i_csi_get_bpp(unsigned int pixformat)
|
||||||
|
@ -23,15 +23,27 @@
|
|||||||
#define MAX_WIDTH (4800)
|
#define MAX_WIDTH (4800)
|
||||||
#define MAX_HEIGHT (4800)
|
#define MAX_HEIGHT (4800)
|
||||||
|
|
||||||
struct sun6i_csi_buffer {
|
/* Helpers */
|
||||||
struct vb2_v4l2_buffer vb;
|
|
||||||
struct list_head list;
|
|
||||||
|
|
||||||
dma_addr_t dma_addr;
|
static struct v4l2_subdev *
|
||||||
bool queued_to_csi;
|
sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad)
|
||||||
};
|
{
|
||||||
|
struct media_pad *remote;
|
||||||
|
|
||||||
static const u32 supported_pixformats[] = {
|
remote = media_pad_remote_pad_first(&video->pad);
|
||||||
|
|
||||||
|
if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (pad)
|
||||||
|
*pad = remote->index;
|
||||||
|
|
||||||
|
return media_entity_to_v4l2_subdev(remote->entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format */
|
||||||
|
|
||||||
|
static const u32 sun6i_video_formats[] = {
|
||||||
V4L2_PIX_FMT_SBGGR8,
|
V4L2_PIX_FMT_SBGGR8,
|
||||||
V4L2_PIX_FMT_SGBRG8,
|
V4L2_PIX_FMT_SGBRG8,
|
||||||
V4L2_PIX_FMT_SGRBG8,
|
V4L2_PIX_FMT_SGRBG8,
|
||||||
@ -61,119 +73,138 @@ static const u32 supported_pixformats[] = {
|
|||||||
V4L2_PIX_FMT_JPEG,
|
V4L2_PIX_FMT_JPEG,
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool is_pixformat_valid(unsigned int pixformat)
|
static bool sun6i_video_format_check(u32 format)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(supported_pixformats); i++)
|
for (i = 0; i < ARRAY_SIZE(sun6i_video_formats); i++)
|
||||||
if (supported_pixformats[i] == pixformat)
|
if (sun6i_video_formats[i] == format)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct v4l2_subdev *
|
/* Video */
|
||||||
sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad)
|
|
||||||
|
static void sun6i_video_buffer_configure(struct sun6i_csi_device *csi_dev,
|
||||||
|
struct sun6i_csi_buffer *csi_buffer)
|
||||||
{
|
{
|
||||||
struct media_pad *remote;
|
csi_buffer->queued_to_csi = true;
|
||||||
|
sun6i_csi_update_buf_addr(csi_dev, csi_buffer->dma_addr);
|
||||||
remote = media_pad_remote_pad_first(&video->pad);
|
|
||||||
|
|
||||||
if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (pad)
|
|
||||||
*pad = remote->index;
|
|
||||||
|
|
||||||
return media_entity_to_v4l2_subdev(remote->entity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun6i_video_queue_setup(struct vb2_queue *vq,
|
static void sun6i_video_configure(struct sun6i_csi_device *csi_dev)
|
||||||
unsigned int *nbuffers,
|
{
|
||||||
unsigned int *nplanes,
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
|
struct sun6i_csi_config config = { 0 };
|
||||||
|
|
||||||
|
config.pixelformat = video->format.fmt.pix.pixelformat;
|
||||||
|
config.code = video->mbus_code;
|
||||||
|
config.field = video->format.fmt.pix.field;
|
||||||
|
config.width = video->format.fmt.pix.width;
|
||||||
|
config.height = video->format.fmt.pix.height;
|
||||||
|
|
||||||
|
sun6i_csi_update_config(csi_dev, &config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Queue */
|
||||||
|
|
||||||
|
static int sun6i_video_queue_setup(struct vb2_queue *queue,
|
||||||
|
unsigned int *buffers_count,
|
||||||
|
unsigned int *planes_count,
|
||||||
unsigned int sizes[],
|
unsigned int sizes[],
|
||||||
struct device *alloc_devs[])
|
struct device *alloc_devs[])
|
||||||
{
|
{
|
||||||
struct sun6i_video *video = vb2_get_drv_priv(vq);
|
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
|
||||||
unsigned int size = video->fmt.fmt.pix.sizeimage;
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
|
unsigned int size = video->format.fmt.pix.sizeimage;
|
||||||
|
|
||||||
if (*nplanes)
|
if (*planes_count)
|
||||||
return sizes[0] < size ? -EINVAL : 0;
|
return sizes[0] < size ? -EINVAL : 0;
|
||||||
|
|
||||||
*nplanes = 1;
|
*planes_count = 1;
|
||||||
sizes[0] = size;
|
sizes[0] = size;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun6i_video_buffer_prepare(struct vb2_buffer *vb)
|
static int sun6i_video_buffer_prepare(struct vb2_buffer *buffer)
|
||||||
{
|
{
|
||||||
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);
|
||||||
struct sun6i_csi_buffer *buf =
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
container_of(vbuf, struct sun6i_csi_buffer, vb);
|
struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
|
||||||
struct sun6i_video *video = vb2_get_drv_priv(vb->vb2_queue);
|
struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);
|
||||||
unsigned long size = video->fmt.fmt.pix.sizeimage;
|
struct sun6i_csi_buffer *csi_buffer =
|
||||||
|
container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer);
|
||||||
|
unsigned long size = video->format.fmt.pix.sizeimage;
|
||||||
|
|
||||||
if (vb2_plane_size(vb, 0) < size) {
|
if (vb2_plane_size(buffer, 0) < size) {
|
||||||
v4l2_err(video->vdev.v4l2_dev, "buffer too small (%lu < %lu)\n",
|
v4l2_err(v4l2_dev, "buffer too small (%lu < %lu)\n",
|
||||||
vb2_plane_size(vb, 0), size);
|
vb2_plane_size(buffer, 0), size);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
vb2_set_plane_payload(vb, 0, size);
|
vb2_set_plane_payload(buffer, 0, size);
|
||||||
|
|
||||||
buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
|
csi_buffer->dma_addr = vb2_dma_contig_plane_dma_addr(buffer, 0);
|
||||||
|
v4l2_buffer->field = video->format.fmt.pix.field;
|
||||||
vbuf->field = video->fmt.fmt.pix.field;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun6i_video_start_streaming(struct vb2_queue *vq, unsigned int count)
|
static void sun6i_video_buffer_queue(struct vb2_buffer *buffer)
|
||||||
{
|
{
|
||||||
struct sun6i_video *video = vb2_get_drv_priv(vq);
|
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);
|
||||||
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
|
struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer);
|
||||||
|
struct sun6i_csi_buffer *csi_buffer =
|
||||||
|
container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&video->dma_queue_lock, flags);
|
||||||
|
csi_buffer->queued_to_csi = false;
|
||||||
|
list_add_tail(&csi_buffer->list, &video->dma_queue);
|
||||||
|
spin_unlock_irqrestore(&video->dma_queue_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun6i_video_start_streaming(struct vb2_queue *queue,
|
||||||
|
unsigned int count)
|
||||||
|
{
|
||||||
|
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
|
||||||
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
|
struct video_device *video_dev = &video->video_dev;
|
||||||
struct sun6i_csi_buffer *buf;
|
struct sun6i_csi_buffer *buf;
|
||||||
struct sun6i_csi_buffer *next_buf;
|
struct sun6i_csi_buffer *next_buf;
|
||||||
struct sun6i_csi_config config;
|
|
||||||
struct v4l2_subdev *subdev;
|
struct v4l2_subdev *subdev;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
video->sequence = 0;
|
video->sequence = 0;
|
||||||
|
|
||||||
ret = media_pipeline_start(&video->vdev.entity, &video->vdev.pipe);
|
ret = video_device_pipeline_alloc_start(video_dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto clear_dma_queue;
|
goto error_dma_queue_flush;
|
||||||
|
|
||||||
if (video->mbus_code == 0) {
|
if (video->mbus_code == 0) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto stop_media_pipeline;
|
goto error_media_pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
subdev = sun6i_video_remote_subdev(video, NULL);
|
subdev = sun6i_video_remote_subdev(video, NULL);
|
||||||
if (!subdev) {
|
if (!subdev) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto stop_media_pipeline;
|
goto error_media_pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.pixelformat = video->fmt.fmt.pix.pixelformat;
|
sun6i_video_configure(csi_dev);
|
||||||
config.code = video->mbus_code;
|
|
||||||
config.field = video->fmt.fmt.pix.field;
|
|
||||||
config.width = video->fmt.fmt.pix.width;
|
|
||||||
config.height = video->fmt.fmt.pix.height;
|
|
||||||
|
|
||||||
ret = sun6i_csi_update_config(video->csi, &config);
|
|
||||||
if (ret < 0)
|
|
||||||
goto stop_media_pipeline;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&video->dma_queue_lock, flags);
|
spin_lock_irqsave(&video->dma_queue_lock, flags);
|
||||||
|
|
||||||
buf = list_first_entry(&video->dma_queue,
|
buf = list_first_entry(&video->dma_queue,
|
||||||
struct sun6i_csi_buffer, list);
|
struct sun6i_csi_buffer, list);
|
||||||
buf->queued_to_csi = true;
|
sun6i_video_buffer_configure(csi_dev, buf);
|
||||||
sun6i_csi_update_buf_addr(video->csi, buf->dma_addr);
|
|
||||||
|
|
||||||
sun6i_csi_set_stream(video->csi, true);
|
sun6i_csi_set_stream(csi_dev, true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CSI will lookup the next dma buffer for next frame before the
|
* CSI will lookup the next dma buffer for next frame before the
|
||||||
@ -193,34 +224,37 @@ static int sun6i_video_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|||||||
* would also drop frame when lacking of queued buffer.
|
* would also drop frame when lacking of queued buffer.
|
||||||
*/
|
*/
|
||||||
next_buf = list_next_entry(buf, list);
|
next_buf = list_next_entry(buf, list);
|
||||||
next_buf->queued_to_csi = true;
|
sun6i_video_buffer_configure(csi_dev, next_buf);
|
||||||
sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&video->dma_queue_lock, flags);
|
spin_unlock_irqrestore(&video->dma_queue_lock, flags);
|
||||||
|
|
||||||
ret = v4l2_subdev_call(subdev, video, s_stream, 1);
|
ret = v4l2_subdev_call(subdev, video, s_stream, 1);
|
||||||
if (ret && ret != -ENOIOCTLCMD)
|
if (ret && ret != -ENOIOCTLCMD)
|
||||||
goto stop_csi_stream;
|
goto error_stream;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
stop_csi_stream:
|
error_stream:
|
||||||
sun6i_csi_set_stream(video->csi, false);
|
sun6i_csi_set_stream(csi_dev, false);
|
||||||
stop_media_pipeline:
|
|
||||||
media_pipeline_stop(&video->vdev.entity);
|
error_media_pipeline:
|
||||||
clear_dma_queue:
|
video_device_pipeline_stop(video_dev);
|
||||||
|
|
||||||
|
error_dma_queue_flush:
|
||||||
spin_lock_irqsave(&video->dma_queue_lock, flags);
|
spin_lock_irqsave(&video->dma_queue_lock, flags);
|
||||||
list_for_each_entry(buf, &video->dma_queue, list)
|
list_for_each_entry(buf, &video->dma_queue, list)
|
||||||
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
|
vb2_buffer_done(&buf->v4l2_buffer.vb2_buf,
|
||||||
|
VB2_BUF_STATE_QUEUED);
|
||||||
INIT_LIST_HEAD(&video->dma_queue);
|
INIT_LIST_HEAD(&video->dma_queue);
|
||||||
spin_unlock_irqrestore(&video->dma_queue_lock, flags);
|
spin_unlock_irqrestore(&video->dma_queue_lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sun6i_video_stop_streaming(struct vb2_queue *vq)
|
static void sun6i_video_stop_streaming(struct vb2_queue *queue)
|
||||||
{
|
{
|
||||||
struct sun6i_video *video = vb2_get_drv_priv(vq);
|
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
|
||||||
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
struct v4l2_subdev *subdev;
|
struct v4l2_subdev *subdev;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct sun6i_csi_buffer *buf;
|
struct sun6i_csi_buffer *buf;
|
||||||
@ -229,45 +263,32 @@ static void sun6i_video_stop_streaming(struct vb2_queue *vq)
|
|||||||
if (subdev)
|
if (subdev)
|
||||||
v4l2_subdev_call(subdev, video, s_stream, 0);
|
v4l2_subdev_call(subdev, video, s_stream, 0);
|
||||||
|
|
||||||
sun6i_csi_set_stream(video->csi, false);
|
sun6i_csi_set_stream(csi_dev, false);
|
||||||
|
|
||||||
media_pipeline_stop(&video->vdev.entity);
|
video_device_pipeline_stop(&video->video_dev);
|
||||||
|
|
||||||
/* Release all active buffers */
|
/* Release all active buffers */
|
||||||
spin_lock_irqsave(&video->dma_queue_lock, flags);
|
spin_lock_irqsave(&video->dma_queue_lock, flags);
|
||||||
list_for_each_entry(buf, &video->dma_queue, list)
|
list_for_each_entry(buf, &video->dma_queue, list)
|
||||||
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
|
vb2_buffer_done(&buf->v4l2_buffer.vb2_buf, VB2_BUF_STATE_ERROR);
|
||||||
INIT_LIST_HEAD(&video->dma_queue);
|
INIT_LIST_HEAD(&video->dma_queue);
|
||||||
spin_unlock_irqrestore(&video->dma_queue_lock, flags);
|
spin_unlock_irqrestore(&video->dma_queue_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sun6i_video_buffer_queue(struct vb2_buffer *vb)
|
void sun6i_video_frame_done(struct sun6i_csi_device *csi_dev)
|
||||||
{
|
|
||||||
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
|
||||||
struct sun6i_csi_buffer *buf =
|
|
||||||
container_of(vbuf, struct sun6i_csi_buffer, vb);
|
|
||||||
struct sun6i_video *video = vb2_get_drv_priv(vb->vb2_queue);
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&video->dma_queue_lock, flags);
|
|
||||||
buf->queued_to_csi = false;
|
|
||||||
list_add_tail(&buf->list, &video->dma_queue);
|
|
||||||
spin_unlock_irqrestore(&video->dma_queue_lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sun6i_video_frame_done(struct sun6i_video *video)
|
|
||||||
{
|
{
|
||||||
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
struct sun6i_csi_buffer *buf;
|
struct sun6i_csi_buffer *buf;
|
||||||
struct sun6i_csi_buffer *next_buf;
|
struct sun6i_csi_buffer *next_buf;
|
||||||
struct vb2_v4l2_buffer *vbuf;
|
struct vb2_v4l2_buffer *v4l2_buffer;
|
||||||
|
|
||||||
spin_lock(&video->dma_queue_lock);
|
spin_lock(&video->dma_queue_lock);
|
||||||
|
|
||||||
buf = list_first_entry(&video->dma_queue,
|
buf = list_first_entry(&video->dma_queue,
|
||||||
struct sun6i_csi_buffer, list);
|
struct sun6i_csi_buffer, list);
|
||||||
if (list_is_last(&buf->list, &video->dma_queue)) {
|
if (list_is_last(&buf->list, &video->dma_queue)) {
|
||||||
dev_dbg(video->csi->dev, "Frame dropped!\n");
|
dev_dbg(csi_dev->dev, "Frame dropped!\n");
|
||||||
goto unlock;
|
goto complete;
|
||||||
}
|
}
|
||||||
|
|
||||||
next_buf = list_next_entry(buf, list);
|
next_buf = list_next_entry(buf, list);
|
||||||
@ -277,200 +298,204 @@ void sun6i_video_frame_done(struct sun6i_video *video)
|
|||||||
* for next ISR call.
|
* for next ISR call.
|
||||||
*/
|
*/
|
||||||
if (!next_buf->queued_to_csi) {
|
if (!next_buf->queued_to_csi) {
|
||||||
next_buf->queued_to_csi = true;
|
sun6i_video_buffer_configure(csi_dev, next_buf);
|
||||||
sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
|
dev_dbg(csi_dev->dev, "Frame dropped!\n");
|
||||||
dev_dbg(video->csi->dev, "Frame dropped!\n");
|
goto complete;
|
||||||
goto unlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list_del(&buf->list);
|
list_del(&buf->list);
|
||||||
vbuf = &buf->vb;
|
v4l2_buffer = &buf->v4l2_buffer;
|
||||||
vbuf->vb2_buf.timestamp = ktime_get_ns();
|
v4l2_buffer->vb2_buf.timestamp = ktime_get_ns();
|
||||||
vbuf->sequence = video->sequence;
|
v4l2_buffer->sequence = video->sequence;
|
||||||
vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
|
vb2_buffer_done(&v4l2_buffer->vb2_buf, VB2_BUF_STATE_DONE);
|
||||||
|
|
||||||
/* Prepare buffer for next frame but one. */
|
/* Prepare buffer for next frame but one. */
|
||||||
if (!list_is_last(&next_buf->list, &video->dma_queue)) {
|
if (!list_is_last(&next_buf->list, &video->dma_queue)) {
|
||||||
next_buf = list_next_entry(next_buf, list);
|
next_buf = list_next_entry(next_buf, list);
|
||||||
next_buf->queued_to_csi = true;
|
sun6i_video_buffer_configure(csi_dev, next_buf);
|
||||||
sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
|
|
||||||
} else {
|
} else {
|
||||||
dev_dbg(video->csi->dev, "Next frame will be dropped!\n");
|
dev_dbg(csi_dev->dev, "Next frame will be dropped!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock:
|
complete:
|
||||||
video->sequence++;
|
video->sequence++;
|
||||||
spin_unlock(&video->dma_queue_lock);
|
spin_unlock(&video->dma_queue_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct vb2_ops sun6i_csi_vb2_ops = {
|
static const struct vb2_ops sun6i_video_queue_ops = {
|
||||||
.queue_setup = sun6i_video_queue_setup,
|
.queue_setup = sun6i_video_queue_setup,
|
||||||
.wait_prepare = vb2_ops_wait_prepare,
|
|
||||||
.wait_finish = vb2_ops_wait_finish,
|
|
||||||
.buf_prepare = sun6i_video_buffer_prepare,
|
.buf_prepare = sun6i_video_buffer_prepare,
|
||||||
|
.buf_queue = sun6i_video_buffer_queue,
|
||||||
.start_streaming = sun6i_video_start_streaming,
|
.start_streaming = sun6i_video_start_streaming,
|
||||||
.stop_streaming = sun6i_video_stop_streaming,
|
.stop_streaming = sun6i_video_stop_streaming,
|
||||||
.buf_queue = sun6i_video_buffer_queue,
|
.wait_prepare = vb2_ops_wait_prepare,
|
||||||
|
.wait_finish = vb2_ops_wait_finish,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int vidioc_querycap(struct file *file, void *priv,
|
/* V4L2 Device */
|
||||||
struct v4l2_capability *cap)
|
|
||||||
{
|
|
||||||
struct sun6i_video *video = video_drvdata(file);
|
|
||||||
|
|
||||||
strscpy(cap->driver, "sun6i-video", sizeof(cap->driver));
|
static int sun6i_video_querycap(struct file *file, void *private,
|
||||||
strscpy(cap->card, video->vdev.name, sizeof(cap->card));
|
struct v4l2_capability *capability)
|
||||||
snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
|
{
|
||||||
video->csi->dev->of_node->name);
|
struct sun6i_csi_device *csi_dev = video_drvdata(file);
|
||||||
|
struct video_device *video_dev = &csi_dev->video.video_dev;
|
||||||
|
|
||||||
|
strscpy(capability->driver, SUN6I_CSI_NAME, sizeof(capability->driver));
|
||||||
|
strscpy(capability->card, video_dev->name, sizeof(capability->card));
|
||||||
|
snprintf(capability->bus_info, sizeof(capability->bus_info),
|
||||||
|
"platform:%s", dev_name(csi_dev->dev));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
|
static int sun6i_video_enum_fmt(struct file *file, void *private,
|
||||||
struct v4l2_fmtdesc *f)
|
struct v4l2_fmtdesc *fmtdesc)
|
||||||
{
|
{
|
||||||
u32 index = f->index;
|
u32 index = fmtdesc->index;
|
||||||
|
|
||||||
if (index >= ARRAY_SIZE(supported_pixformats))
|
if (index >= ARRAY_SIZE(sun6i_video_formats))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
f->pixelformat = supported_pixformats[index];
|
fmtdesc->pixelformat = sun6i_video_formats[index];
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
|
static int sun6i_video_g_fmt(struct file *file, void *private,
|
||||||
struct v4l2_format *fmt)
|
struct v4l2_format *format)
|
||||||
{
|
{
|
||||||
struct sun6i_video *video = video_drvdata(file);
|
struct sun6i_csi_device *csi_dev = video_drvdata(file);
|
||||||
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
|
|
||||||
*fmt = video->fmt;
|
*format = video->format;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun6i_video_try_fmt(struct sun6i_video *video,
|
static int sun6i_video_format_try(struct sun6i_video *video,
|
||||||
struct v4l2_format *f)
|
struct v4l2_format *format)
|
||||||
{
|
{
|
||||||
struct v4l2_pix_format *pixfmt = &f->fmt.pix;
|
struct v4l2_pix_format *pix_format = &format->fmt.pix;
|
||||||
int bpp;
|
int bpp;
|
||||||
|
|
||||||
if (!is_pixformat_valid(pixfmt->pixelformat))
|
if (!sun6i_video_format_check(pix_format->pixelformat))
|
||||||
pixfmt->pixelformat = supported_pixformats[0];
|
pix_format->pixelformat = sun6i_video_formats[0];
|
||||||
|
|
||||||
v4l_bound_align_image(&pixfmt->width, MIN_WIDTH, MAX_WIDTH, 1,
|
v4l_bound_align_image(&pix_format->width, MIN_WIDTH, MAX_WIDTH, 1,
|
||||||
&pixfmt->height, MIN_HEIGHT, MAX_WIDTH, 1, 1);
|
&pix_format->height, MIN_HEIGHT, MAX_WIDTH, 1, 1);
|
||||||
|
|
||||||
bpp = sun6i_csi_get_bpp(pixfmt->pixelformat);
|
bpp = sun6i_csi_get_bpp(pix_format->pixelformat);
|
||||||
pixfmt->bytesperline = (pixfmt->width * bpp) >> 3;
|
pix_format->bytesperline = (pix_format->width * bpp) >> 3;
|
||||||
pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
|
pix_format->sizeimage = pix_format->bytesperline * pix_format->height;
|
||||||
|
|
||||||
if (pixfmt->field == V4L2_FIELD_ANY)
|
if (pix_format->field == V4L2_FIELD_ANY)
|
||||||
pixfmt->field = V4L2_FIELD_NONE;
|
pix_format->field = V4L2_FIELD_NONE;
|
||||||
|
|
||||||
if (pixfmt->pixelformat == V4L2_PIX_FMT_JPEG)
|
if (pix_format->pixelformat == V4L2_PIX_FMT_JPEG)
|
||||||
pixfmt->colorspace = V4L2_COLORSPACE_JPEG;
|
pix_format->colorspace = V4L2_COLORSPACE_JPEG;
|
||||||
else
|
else
|
||||||
pixfmt->colorspace = V4L2_COLORSPACE_SRGB;
|
pix_format->colorspace = V4L2_COLORSPACE_SRGB;
|
||||||
|
|
||||||
pixfmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
|
pix_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
|
||||||
pixfmt->quantization = V4L2_QUANTIZATION_DEFAULT;
|
pix_format->quantization = V4L2_QUANTIZATION_DEFAULT;
|
||||||
pixfmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
|
pix_format->xfer_func = V4L2_XFER_FUNC_DEFAULT;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun6i_video_set_fmt(struct sun6i_video *video, struct v4l2_format *f)
|
static int sun6i_video_format_set(struct sun6i_video *video,
|
||||||
|
struct v4l2_format *format)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = sun6i_video_try_fmt(video, f);
|
ret = sun6i_video_format_try(video, format);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
video->fmt = *f;
|
video->format = *format;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
|
static int sun6i_video_s_fmt(struct file *file, void *private,
|
||||||
struct v4l2_format *f)
|
struct v4l2_format *format)
|
||||||
{
|
{
|
||||||
struct sun6i_video *video = video_drvdata(file);
|
struct sun6i_csi_device *csi_dev = video_drvdata(file);
|
||||||
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
|
|
||||||
if (vb2_is_busy(&video->vb2_vidq))
|
if (vb2_is_busy(&video->queue))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
return sun6i_video_set_fmt(video, f);
|
return sun6i_video_format_set(video, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
|
static int sun6i_video_try_fmt(struct file *file, void *private,
|
||||||
struct v4l2_format *f)
|
struct v4l2_format *format)
|
||||||
{
|
{
|
||||||
struct sun6i_video *video = video_drvdata(file);
|
struct sun6i_csi_device *csi_dev = video_drvdata(file);
|
||||||
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
|
|
||||||
return sun6i_video_try_fmt(video, f);
|
return sun6i_video_format_try(video, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vidioc_enum_input(struct file *file, void *fh,
|
static int sun6i_video_enum_input(struct file *file, void *private,
|
||||||
struct v4l2_input *inp)
|
struct v4l2_input *input)
|
||||||
{
|
{
|
||||||
if (inp->index != 0)
|
if (input->index != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
strscpy(inp->name, "camera", sizeof(inp->name));
|
input->type = V4L2_INPUT_TYPE_CAMERA;
|
||||||
inp->type = V4L2_INPUT_TYPE_CAMERA;
|
strscpy(input->name, "Camera", sizeof(input->name));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
|
static int sun6i_video_g_input(struct file *file, void *private,
|
||||||
|
unsigned int *index)
|
||||||
{
|
{
|
||||||
*i = 0;
|
*index = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
|
static int sun6i_video_s_input(struct file *file, void *private,
|
||||||
|
unsigned int index)
|
||||||
{
|
{
|
||||||
if (i != 0)
|
if (index != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct v4l2_ioctl_ops sun6i_video_ioctl_ops = {
|
static const struct v4l2_ioctl_ops sun6i_video_ioctl_ops = {
|
||||||
.vidioc_querycap = vidioc_querycap,
|
.vidioc_querycap = sun6i_video_querycap,
|
||||||
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
|
|
||||||
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
|
||||||
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
|
|
||||||
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
|
|
||||||
|
|
||||||
.vidioc_enum_input = vidioc_enum_input,
|
.vidioc_enum_fmt_vid_cap = sun6i_video_enum_fmt,
|
||||||
.vidioc_s_input = vidioc_s_input,
|
.vidioc_g_fmt_vid_cap = sun6i_video_g_fmt,
|
||||||
.vidioc_g_input = vidioc_g_input,
|
.vidioc_s_fmt_vid_cap = sun6i_video_s_fmt,
|
||||||
|
.vidioc_try_fmt_vid_cap = sun6i_video_try_fmt,
|
||||||
|
|
||||||
|
.vidioc_enum_input = sun6i_video_enum_input,
|
||||||
|
.vidioc_g_input = sun6i_video_g_input,
|
||||||
|
.vidioc_s_input = sun6i_video_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_create_bufs = vb2_ioctl_create_bufs,
|
||||||
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
|
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
|
||||||
|
.vidioc_reqbufs = vb2_ioctl_reqbufs,
|
||||||
|
.vidioc_querybuf = vb2_ioctl_querybuf,
|
||||||
|
.vidioc_expbuf = vb2_ioctl_expbuf,
|
||||||
|
.vidioc_qbuf = vb2_ioctl_qbuf,
|
||||||
|
.vidioc_dqbuf = vb2_ioctl_dqbuf,
|
||||||
.vidioc_streamon = vb2_ioctl_streamon,
|
.vidioc_streamon = vb2_ioctl_streamon,
|
||||||
.vidioc_streamoff = vb2_ioctl_streamoff,
|
.vidioc_streamoff = vb2_ioctl_streamoff,
|
||||||
|
|
||||||
.vidioc_log_status = v4l2_ctrl_log_status,
|
|
||||||
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
|
||||||
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* V4L2 File */
|
||||||
* V4L2 file operations
|
|
||||||
*/
|
|
||||||
static int sun6i_video_open(struct file *file)
|
static int sun6i_video_open(struct file *file)
|
||||||
{
|
{
|
||||||
struct sun6i_video *video = video_drvdata(file);
|
struct sun6i_csi_device *csi_dev = video_drvdata(file);
|
||||||
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (mutex_lock_interruptible(&video->lock))
|
if (mutex_lock_interruptible(&video->lock))
|
||||||
@ -478,45 +503,48 @@ static int sun6i_video_open(struct file *file)
|
|||||||
|
|
||||||
ret = v4l2_fh_open(file);
|
ret = v4l2_fh_open(file);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto unlock;
|
goto error_lock;
|
||||||
|
|
||||||
ret = v4l2_pipeline_pm_get(&video->vdev.entity);
|
ret = v4l2_pipeline_pm_get(&video->video_dev.entity);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto fh_release;
|
goto error_v4l2_fh;
|
||||||
|
|
||||||
/* check if already powered */
|
/* Power on at first open. */
|
||||||
if (!v4l2_fh_is_singular_file(file))
|
if (v4l2_fh_is_singular_file(file)) {
|
||||||
goto unlock;
|
ret = sun6i_csi_set_power(csi_dev, true);
|
||||||
|
if (ret < 0)
|
||||||
ret = sun6i_csi_set_power(video->csi, true);
|
goto error_v4l2_fh;
|
||||||
if (ret < 0)
|
}
|
||||||
goto fh_release;
|
|
||||||
|
|
||||||
mutex_unlock(&video->lock);
|
mutex_unlock(&video->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fh_release:
|
error_v4l2_fh:
|
||||||
v4l2_fh_release(file);
|
v4l2_fh_release(file);
|
||||||
unlock:
|
|
||||||
|
error_lock:
|
||||||
mutex_unlock(&video->lock);
|
mutex_unlock(&video->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun6i_video_close(struct file *file)
|
static int sun6i_video_close(struct file *file)
|
||||||
{
|
{
|
||||||
struct sun6i_video *video = video_drvdata(file);
|
struct sun6i_csi_device *csi_dev = video_drvdata(file);
|
||||||
bool last_fh;
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
|
bool last_close;
|
||||||
|
|
||||||
mutex_lock(&video->lock);
|
mutex_lock(&video->lock);
|
||||||
|
|
||||||
last_fh = v4l2_fh_is_singular_file(file);
|
last_close = v4l2_fh_is_singular_file(file);
|
||||||
|
|
||||||
_vb2_fop_release(file, NULL);
|
_vb2_fop_release(file, NULL);
|
||||||
|
v4l2_pipeline_pm_put(&video->video_dev.entity);
|
||||||
|
|
||||||
v4l2_pipeline_pm_put(&video->vdev.entity);
|
/* Power off at last close. */
|
||||||
|
if (last_close)
|
||||||
if (last_fh)
|
sun6i_csi_set_power(csi_dev, false);
|
||||||
sun6i_csi_set_power(video->csi, false);
|
|
||||||
|
|
||||||
mutex_unlock(&video->lock);
|
mutex_unlock(&video->lock);
|
||||||
|
|
||||||
@ -532,9 +560,8 @@ static const struct v4l2_file_operations sun6i_video_fops = {
|
|||||||
.poll = vb2_fop_poll
|
.poll = vb2_fop_poll
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* Media Entity */
|
||||||
* Media Operations
|
|
||||||
*/
|
|
||||||
static int sun6i_video_link_validate_get_format(struct media_pad *pad,
|
static int sun6i_video_link_validate_get_format(struct media_pad *pad,
|
||||||
struct v4l2_subdev_format *fmt)
|
struct v4l2_subdev_format *fmt)
|
||||||
{
|
{
|
||||||
@ -554,15 +581,16 @@ static int sun6i_video_link_validate(struct media_link *link)
|
|||||||
{
|
{
|
||||||
struct video_device *vdev = container_of(link->sink->entity,
|
struct video_device *vdev = container_of(link->sink->entity,
|
||||||
struct video_device, entity);
|
struct video_device, entity);
|
||||||
struct sun6i_video *video = video_get_drvdata(vdev);
|
struct sun6i_csi_device *csi_dev = video_get_drvdata(vdev);
|
||||||
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
struct v4l2_subdev_format source_fmt;
|
struct v4l2_subdev_format source_fmt;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
video->mbus_code = 0;
|
video->mbus_code = 0;
|
||||||
|
|
||||||
if (!media_pad_remote_pad_first(link->sink->entity->pads)) {
|
if (!media_pad_remote_pad_first(link->sink->entity->pads)) {
|
||||||
dev_info(video->csi->dev,
|
dev_info(csi_dev->dev, "video node %s pad not connected\n",
|
||||||
"video node %s pad not connected\n", vdev->name);
|
vdev->name);
|
||||||
return -ENOLINK;
|
return -ENOLINK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,21 +598,21 @@ static int sun6i_video_link_validate(struct media_link *link)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (!sun6i_csi_is_format_supported(video->csi,
|
if (!sun6i_csi_is_format_supported(csi_dev,
|
||||||
video->fmt.fmt.pix.pixelformat,
|
video->format.fmt.pix.pixelformat,
|
||||||
source_fmt.format.code)) {
|
source_fmt.format.code)) {
|
||||||
dev_err(video->csi->dev,
|
dev_err(csi_dev->dev,
|
||||||
"Unsupported pixformat: 0x%x with mbus code: 0x%x!\n",
|
"Unsupported pixformat: 0x%x with mbus code: 0x%x!\n",
|
||||||
video->fmt.fmt.pix.pixelformat,
|
video->format.fmt.pix.pixelformat,
|
||||||
source_fmt.format.code);
|
source_fmt.format.code);
|
||||||
return -EPIPE;
|
return -EPIPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source_fmt.format.width != video->fmt.fmt.pix.width ||
|
if (source_fmt.format.width != video->format.fmt.pix.width ||
|
||||||
source_fmt.format.height != video->fmt.fmt.pix.height) {
|
source_fmt.format.height != video->format.fmt.pix.height) {
|
||||||
dev_err(video->csi->dev,
|
dev_err(csi_dev->dev,
|
||||||
"Wrong width or height %ux%u (%ux%u expected)\n",
|
"Wrong width or height %ux%u (%ux%u expected)\n",
|
||||||
video->fmt.fmt.pix.width, video->fmt.fmt.pix.height,
|
video->format.fmt.pix.width, video->format.fmt.pix.height,
|
||||||
source_fmt.format.width, source_fmt.format.height);
|
source_fmt.format.width, source_fmt.format.height);
|
||||||
return -EPIPE;
|
return -EPIPE;
|
||||||
}
|
}
|
||||||
@ -598,88 +626,108 @@ static const struct media_entity_operations sun6i_video_media_ops = {
|
|||||||
.link_validate = sun6i_video_link_validate
|
.link_validate = sun6i_video_link_validate
|
||||||
};
|
};
|
||||||
|
|
||||||
int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi,
|
/* Video */
|
||||||
const char *name)
|
|
||||||
|
int sun6i_video_setup(struct sun6i_csi_device *csi_dev)
|
||||||
{
|
{
|
||||||
struct video_device *vdev = &video->vdev;
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
struct vb2_queue *vidq = &video->vb2_vidq;
|
struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
|
||||||
struct v4l2_format fmt = { 0 };
|
struct video_device *video_dev = &video->video_dev;
|
||||||
|
struct vb2_queue *queue = &video->queue;
|
||||||
|
struct media_pad *pad = &video->pad;
|
||||||
|
struct v4l2_format format = { 0 };
|
||||||
|
struct v4l2_pix_format *pix_format = &format.fmt.pix;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
video->csi = csi;
|
/* Media Entity */
|
||||||
|
|
||||||
/* Initialize the media entity... */
|
video_dev->entity.ops = &sun6i_video_media_ops;
|
||||||
video->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
|
|
||||||
vdev->entity.ops = &sun6i_video_media_ops;
|
/* Media Pad */
|
||||||
ret = media_entity_pads_init(&vdev->entity, 1, &video->pad);
|
|
||||||
|
pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
|
||||||
|
|
||||||
|
ret = media_entity_pads_init(&video_dev->entity, 1, pad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
mutex_init(&video->lock);
|
/* DMA queue */
|
||||||
|
|
||||||
INIT_LIST_HEAD(&video->dma_queue);
|
INIT_LIST_HEAD(&video->dma_queue);
|
||||||
spin_lock_init(&video->dma_queue_lock);
|
spin_lock_init(&video->dma_queue_lock);
|
||||||
|
|
||||||
video->sequence = 0;
|
video->sequence = 0;
|
||||||
|
|
||||||
/* Setup default format */
|
/* Queue */
|
||||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
||||||
fmt.fmt.pix.pixelformat = supported_pixformats[0];
|
|
||||||
fmt.fmt.pix.width = 1280;
|
|
||||||
fmt.fmt.pix.height = 720;
|
|
||||||
fmt.fmt.pix.field = V4L2_FIELD_NONE;
|
|
||||||
sun6i_video_set_fmt(video, &fmt);
|
|
||||||
|
|
||||||
/* Initialize videobuf2 queue */
|
mutex_init(&video->lock);
|
||||||
vidq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
||||||
vidq->io_modes = VB2_MMAP | VB2_DMABUF;
|
|
||||||
vidq->drv_priv = video;
|
|
||||||
vidq->buf_struct_size = sizeof(struct sun6i_csi_buffer);
|
|
||||||
vidq->ops = &sun6i_csi_vb2_ops;
|
|
||||||
vidq->mem_ops = &vb2_dma_contig_memops;
|
|
||||||
vidq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
|
|
||||||
vidq->lock = &video->lock;
|
|
||||||
/* Make sure non-dropped frame */
|
|
||||||
vidq->min_buffers_needed = 3;
|
|
||||||
vidq->dev = csi->dev;
|
|
||||||
|
|
||||||
ret = vb2_queue_init(vidq);
|
queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
queue->io_modes = VB2_MMAP | VB2_DMABUF;
|
||||||
|
queue->buf_struct_size = sizeof(struct sun6i_csi_buffer);
|
||||||
|
queue->ops = &sun6i_video_queue_ops;
|
||||||
|
queue->mem_ops = &vb2_dma_contig_memops;
|
||||||
|
queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
|
||||||
|
queue->lock = &video->lock;
|
||||||
|
queue->dev = csi_dev->dev;
|
||||||
|
queue->drv_priv = csi_dev;
|
||||||
|
|
||||||
|
/* Make sure non-dropped frame. */
|
||||||
|
queue->min_buffers_needed = 3;
|
||||||
|
|
||||||
|
ret = vb2_queue_init(queue);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
v4l2_err(&csi->v4l2_dev, "vb2_queue_init failed: %d\n", ret);
|
v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret);
|
||||||
goto clean_entity;
|
goto error_media_entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register video device */
|
/* V4L2 Format */
|
||||||
strscpy(vdev->name, name, sizeof(vdev->name));
|
|
||||||
vdev->release = video_device_release_empty;
|
|
||||||
vdev->fops = &sun6i_video_fops;
|
|
||||||
vdev->ioctl_ops = &sun6i_video_ioctl_ops;
|
|
||||||
vdev->vfl_type = VFL_TYPE_VIDEO;
|
|
||||||
vdev->vfl_dir = VFL_DIR_RX;
|
|
||||||
vdev->v4l2_dev = &csi->v4l2_dev;
|
|
||||||
vdev->queue = vidq;
|
|
||||||
vdev->lock = &video->lock;
|
|
||||||
vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
|
|
||||||
video_set_drvdata(vdev, video);
|
|
||||||
|
|
||||||
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
|
format.type = queue->type;
|
||||||
|
pix_format->pixelformat = sun6i_video_formats[0];
|
||||||
|
pix_format->width = 1280;
|
||||||
|
pix_format->height = 720;
|
||||||
|
pix_format->field = V4L2_FIELD_NONE;
|
||||||
|
|
||||||
|
sun6i_video_format_set(video, &format);
|
||||||
|
|
||||||
|
/* Video Device */
|
||||||
|
|
||||||
|
strscpy(video_dev->name, SUN6I_CSI_NAME, sizeof(video_dev->name));
|
||||||
|
video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
|
||||||
|
video_dev->vfl_dir = VFL_DIR_RX;
|
||||||
|
video_dev->release = video_device_release_empty;
|
||||||
|
video_dev->fops = &sun6i_video_fops;
|
||||||
|
video_dev->ioctl_ops = &sun6i_video_ioctl_ops;
|
||||||
|
video_dev->v4l2_dev = v4l2_dev;
|
||||||
|
video_dev->queue = queue;
|
||||||
|
video_dev->lock = &video->lock;
|
||||||
|
|
||||||
|
video_set_drvdata(video_dev, csi_dev);
|
||||||
|
|
||||||
|
ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
v4l2_err(&csi->v4l2_dev,
|
v4l2_err(v4l2_dev, "failed to register video device: %d\n",
|
||||||
"video_register_device failed: %d\n", ret);
|
ret);
|
||||||
goto clean_entity;
|
goto error_media_entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
clean_entity:
|
error_media_entity:
|
||||||
media_entity_cleanup(&video->vdev.entity);
|
media_entity_cleanup(&video_dev->entity);
|
||||||
|
|
||||||
mutex_destroy(&video->lock);
|
mutex_destroy(&video->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sun6i_video_cleanup(struct sun6i_video *video)
|
void sun6i_video_cleanup(struct sun6i_csi_device *csi_dev)
|
||||||
{
|
{
|
||||||
vb2_video_unregister_device(&video->vdev);
|
struct sun6i_video *video = &csi_dev->video;
|
||||||
media_entity_cleanup(&video->vdev.entity);
|
struct video_device *video_dev = &video->video_dev;
|
||||||
|
|
||||||
|
vb2_video_unregister_device(video_dev);
|
||||||
|
media_entity_cleanup(&video_dev->entity);
|
||||||
mutex_destroy(&video->lock);
|
mutex_destroy(&video->lock);
|
||||||
}
|
}
|
||||||
|
@ -11,28 +11,25 @@
|
|||||||
#include <media/v4l2-dev.h>
|
#include <media/v4l2-dev.h>
|
||||||
#include <media/videobuf2-core.h>
|
#include <media/videobuf2-core.h>
|
||||||
|
|
||||||
struct sun6i_csi;
|
struct sun6i_csi_device;
|
||||||
|
|
||||||
struct sun6i_video {
|
struct sun6i_video {
|
||||||
struct video_device vdev;
|
struct video_device video_dev;
|
||||||
|
struct vb2_queue queue;
|
||||||
|
struct mutex lock; /* Queue lock. */
|
||||||
struct media_pad pad;
|
struct media_pad pad;
|
||||||
struct sun6i_csi *csi;
|
|
||||||
|
|
||||||
struct mutex lock;
|
|
||||||
|
|
||||||
struct vb2_queue vb2_vidq;
|
|
||||||
spinlock_t dma_queue_lock;
|
|
||||||
struct list_head dma_queue;
|
struct list_head dma_queue;
|
||||||
|
spinlock_t dma_queue_lock; /* DMA queue lock. */
|
||||||
|
|
||||||
unsigned int sequence;
|
struct v4l2_format format;
|
||||||
struct v4l2_format fmt;
|
|
||||||
u32 mbus_code;
|
u32 mbus_code;
|
||||||
|
unsigned int sequence;
|
||||||
};
|
};
|
||||||
|
|
||||||
int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi,
|
int sun6i_video_setup(struct sun6i_csi_device *csi_dev);
|
||||||
const char *name);
|
void sun6i_video_cleanup(struct sun6i_csi_device *csi_dev);
|
||||||
void sun6i_video_cleanup(struct sun6i_video *video);
|
|
||||||
|
|
||||||
void sun6i_video_frame_done(struct sun6i_video *video);
|
void sun6i_video_frame_done(struct sun6i_csi_device *csi_dev);
|
||||||
|
|
||||||
#endif /* __SUN6I_VIDEO_H__ */
|
#endif /* __SUN6I_VIDEO_H__ */
|
||||||
|
@ -3,11 +3,11 @@ config VIDEO_SUN6I_MIPI_CSI2
|
|||||||
tristate "Allwinner A31 MIPI CSI-2 Controller Driver"
|
tristate "Allwinner A31 MIPI CSI-2 Controller Driver"
|
||||||
depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV
|
depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV
|
||||||
depends on ARCH_SUNXI || COMPILE_TEST
|
depends on ARCH_SUNXI || COMPILE_TEST
|
||||||
depends on PM && COMMON_CLK
|
depends on PM && COMMON_CLK && RESET_CONTROLLER
|
||||||
|
depends on PHY_SUN6I_MIPI_DPHY
|
||||||
select MEDIA_CONTROLLER
|
select MEDIA_CONTROLLER
|
||||||
select VIDEO_V4L2_SUBDEV_API
|
select VIDEO_V4L2_SUBDEV_API
|
||||||
select V4L2_FWNODE
|
select V4L2_FWNODE
|
||||||
select PHY_SUN6I_MIPI_DPHY
|
|
||||||
select GENERIC_PHY_MIPI_DPHY
|
select GENERIC_PHY_MIPI_DPHY
|
||||||
select REGMAP_MMIO
|
select REGMAP_MMIO
|
||||||
help
|
help
|
||||||
|
@ -661,7 +661,8 @@ sun6i_mipi_csi2_resources_setup(struct sun6i_mipi_csi2_device *csi2_dev,
|
|||||||
csi2_dev->reset = devm_reset_control_get_shared(dev, NULL);
|
csi2_dev->reset = devm_reset_control_get_shared(dev, NULL);
|
||||||
if (IS_ERR(csi2_dev->reset)) {
|
if (IS_ERR(csi2_dev->reset)) {
|
||||||
dev_err(dev, "failed to get reset controller\n");
|
dev_err(dev, "failed to get reset controller\n");
|
||||||
return PTR_ERR(csi2_dev->reset);
|
ret = PTR_ERR(csi2_dev->reset);
|
||||||
|
goto error_clock_rate_exclusive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* D-PHY */
|
/* D-PHY */
|
||||||
@ -669,13 +670,14 @@ sun6i_mipi_csi2_resources_setup(struct sun6i_mipi_csi2_device *csi2_dev,
|
|||||||
csi2_dev->dphy = devm_phy_get(dev, "dphy");
|
csi2_dev->dphy = devm_phy_get(dev, "dphy");
|
||||||
if (IS_ERR(csi2_dev->dphy)) {
|
if (IS_ERR(csi2_dev->dphy)) {
|
||||||
dev_err(dev, "failed to get MIPI D-PHY\n");
|
dev_err(dev, "failed to get MIPI D-PHY\n");
|
||||||
return PTR_ERR(csi2_dev->dphy);
|
ret = PTR_ERR(csi2_dev->dphy);
|
||||||
|
goto error_clock_rate_exclusive;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = phy_init(csi2_dev->dphy);
|
ret = phy_init(csi2_dev->dphy);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "failed to initialize MIPI D-PHY\n");
|
dev_err(dev, "failed to initialize MIPI D-PHY\n");
|
||||||
return ret;
|
goto error_clock_rate_exclusive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Runtime PM */
|
/* Runtime PM */
|
||||||
@ -683,6 +685,11 @@ sun6i_mipi_csi2_resources_setup(struct sun6i_mipi_csi2_device *csi2_dev,
|
|||||||
pm_runtime_enable(dev);
|
pm_runtime_enable(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error_clock_rate_exclusive:
|
||||||
|
clk_rate_exclusive_put(csi2_dev->clock_mod);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -712,9 +719,14 @@ static int sun6i_mipi_csi2_probe(struct platform_device *platform_dev)
|
|||||||
|
|
||||||
ret = sun6i_mipi_csi2_bridge_setup(csi2_dev);
|
ret = sun6i_mipi_csi2_bridge_setup(csi2_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto error_resources;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error_resources:
|
||||||
|
sun6i_mipi_csi2_resources_cleanup(csi2_dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun6i_mipi_csi2_remove(struct platform_device *platform_dev)
|
static int sun6i_mipi_csi2_remove(struct platform_device *platform_dev)
|
||||||
|
@ -3,7 +3,7 @@ config VIDEO_SUN8I_A83T_MIPI_CSI2
|
|||||||
tristate "Allwinner A83T MIPI CSI-2 Controller and D-PHY Driver"
|
tristate "Allwinner A83T MIPI CSI-2 Controller and D-PHY Driver"
|
||||||
depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV
|
depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV
|
||||||
depends on ARCH_SUNXI || COMPILE_TEST
|
depends on ARCH_SUNXI || COMPILE_TEST
|
||||||
depends on PM && COMMON_CLK
|
depends on PM && COMMON_CLK && RESET_CONTROLLER
|
||||||
select MEDIA_CONTROLLER
|
select MEDIA_CONTROLLER
|
||||||
select VIDEO_V4L2_SUBDEV_API
|
select VIDEO_V4L2_SUBDEV_API
|
||||||
select V4L2_FWNODE
|
select V4L2_FWNODE
|
||||||
|
@ -719,13 +719,15 @@ sun8i_a83t_mipi_csi2_resources_setup(struct sun8i_a83t_mipi_csi2_device *csi2_de
|
|||||||
csi2_dev->clock_mipi = devm_clk_get(dev, "mipi");
|
csi2_dev->clock_mipi = devm_clk_get(dev, "mipi");
|
||||||
if (IS_ERR(csi2_dev->clock_mipi)) {
|
if (IS_ERR(csi2_dev->clock_mipi)) {
|
||||||
dev_err(dev, "failed to acquire mipi clock\n");
|
dev_err(dev, "failed to acquire mipi clock\n");
|
||||||
return PTR_ERR(csi2_dev->clock_mipi);
|
ret = PTR_ERR(csi2_dev->clock_mipi);
|
||||||
|
goto error_clock_rate_exclusive;
|
||||||
}
|
}
|
||||||
|
|
||||||
csi2_dev->clock_misc = devm_clk_get(dev, "misc");
|
csi2_dev->clock_misc = devm_clk_get(dev, "misc");
|
||||||
if (IS_ERR(csi2_dev->clock_misc)) {
|
if (IS_ERR(csi2_dev->clock_misc)) {
|
||||||
dev_err(dev, "failed to acquire misc clock\n");
|
dev_err(dev, "failed to acquire misc clock\n");
|
||||||
return PTR_ERR(csi2_dev->clock_misc);
|
ret = PTR_ERR(csi2_dev->clock_misc);
|
||||||
|
goto error_clock_rate_exclusive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset */
|
/* Reset */
|
||||||
@ -733,7 +735,8 @@ sun8i_a83t_mipi_csi2_resources_setup(struct sun8i_a83t_mipi_csi2_device *csi2_de
|
|||||||
csi2_dev->reset = devm_reset_control_get_shared(dev, NULL);
|
csi2_dev->reset = devm_reset_control_get_shared(dev, NULL);
|
||||||
if (IS_ERR(csi2_dev->reset)) {
|
if (IS_ERR(csi2_dev->reset)) {
|
||||||
dev_err(dev, "failed to get reset controller\n");
|
dev_err(dev, "failed to get reset controller\n");
|
||||||
return PTR_ERR(csi2_dev->reset);
|
ret = PTR_ERR(csi2_dev->reset);
|
||||||
|
goto error_clock_rate_exclusive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* D-PHY */
|
/* D-PHY */
|
||||||
@ -741,7 +744,7 @@ sun8i_a83t_mipi_csi2_resources_setup(struct sun8i_a83t_mipi_csi2_device *csi2_de
|
|||||||
ret = sun8i_a83t_dphy_register(csi2_dev);
|
ret = sun8i_a83t_dphy_register(csi2_dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "failed to initialize MIPI D-PHY\n");
|
dev_err(dev, "failed to initialize MIPI D-PHY\n");
|
||||||
return ret;
|
goto error_clock_rate_exclusive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Runtime PM */
|
/* Runtime PM */
|
||||||
@ -749,6 +752,11 @@ sun8i_a83t_mipi_csi2_resources_setup(struct sun8i_a83t_mipi_csi2_device *csi2_de
|
|||||||
pm_runtime_enable(dev);
|
pm_runtime_enable(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error_clock_rate_exclusive:
|
||||||
|
clk_rate_exclusive_put(csi2_dev->clock_mod);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -778,9 +786,14 @@ static int sun8i_a83t_mipi_csi2_probe(struct platform_device *platform_dev)
|
|||||||
|
|
||||||
ret = sun8i_a83t_mipi_csi2_bridge_setup(csi2_dev);
|
ret = sun8i_a83t_mipi_csi2_bridge_setup(csi2_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto error_resources;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error_resources:
|
||||||
|
sun8i_a83t_mipi_csi2_resources_cleanup(csi2_dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun8i_a83t_mipi_csi2_remove(struct platform_device *platform_dev)
|
static int sun8i_a83t_mipi_csi2_remove(struct platform_device *platform_dev)
|
||||||
|
@ -4,7 +4,7 @@ config VIDEO_SUN8I_DEINTERLACE
|
|||||||
depends on V4L_MEM2MEM_DRIVERS
|
depends on V4L_MEM2MEM_DRIVERS
|
||||||
depends on VIDEO_DEV
|
depends on VIDEO_DEV
|
||||||
depends on ARCH_SUNXI || COMPILE_TEST
|
depends on ARCH_SUNXI || COMPILE_TEST
|
||||||
depends on COMMON_CLK && OF
|
depends on COMMON_CLK && RESET_CONTROLLER && OF
|
||||||
depends on PM
|
depends on PM
|
||||||
select VIDEOBUF2_DMA_CONTIG
|
select VIDEOBUF2_DMA_CONTIG
|
||||||
select V4L2_MEM2MEM_DEV
|
select V4L2_MEM2MEM_DEV
|
||||||
|
@ -5,7 +5,7 @@ config VIDEO_SUN8I_ROTATE
|
|||||||
depends on V4L_MEM2MEM_DRIVERS
|
depends on V4L_MEM2MEM_DRIVERS
|
||||||
depends on VIDEO_DEV
|
depends on VIDEO_DEV
|
||||||
depends on ARCH_SUNXI || COMPILE_TEST
|
depends on ARCH_SUNXI || COMPILE_TEST
|
||||||
depends on COMMON_CLK && OF
|
depends on COMMON_CLK && RESET_CONTROLLER && OF
|
||||||
depends on PM
|
depends on PM
|
||||||
select VIDEOBUF2_DMA_CONTIG
|
select VIDEOBUF2_DMA_CONTIG
|
||||||
select V4L2_MEM2MEM_DEV
|
select V4L2_MEM2MEM_DEV
|
||||||
|
@ -708,7 +708,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|||||||
dma_addr_t addr;
|
dma_addr_t addr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = media_pipeline_start(&ctx->vdev.entity, &ctx->phy->pipe);
|
ret = video_device_pipeline_alloc_start(&ctx->vdev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ctx_err(ctx, "Failed to start media pipeline: %d\n", ret);
|
ctx_err(ctx, "Failed to start media pipeline: %d\n", ret);
|
||||||
goto error_release_buffers;
|
goto error_release_buffers;
|
||||||
@ -761,7 +761,7 @@ error_stop:
|
|||||||
cal_ctx_unprepare(ctx);
|
cal_ctx_unprepare(ctx);
|
||||||
|
|
||||||
error_pipeline:
|
error_pipeline:
|
||||||
media_pipeline_stop(&ctx->vdev.entity);
|
video_device_pipeline_stop(&ctx->vdev);
|
||||||
error_release_buffers:
|
error_release_buffers:
|
||||||
cal_release_buffers(ctx, VB2_BUF_STATE_QUEUED);
|
cal_release_buffers(ctx, VB2_BUF_STATE_QUEUED);
|
||||||
|
|
||||||
@ -782,7 +782,7 @@ static void cal_stop_streaming(struct vb2_queue *vq)
|
|||||||
|
|
||||||
cal_release_buffers(ctx, VB2_BUF_STATE_ERROR);
|
cal_release_buffers(ctx, VB2_BUF_STATE_ERROR);
|
||||||
|
|
||||||
media_pipeline_stop(&ctx->vdev.entity);
|
video_device_pipeline_stop(&ctx->vdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct vb2_ops cal_video_qops = {
|
static const struct vb2_ops cal_video_qops = {
|
||||||
|
@ -174,7 +174,6 @@ struct cal_camerarx {
|
|||||||
struct device_node *source_ep_node;
|
struct device_node *source_ep_node;
|
||||||
struct device_node *source_node;
|
struct device_node *source_node;
|
||||||
struct v4l2_subdev *source;
|
struct v4l2_subdev *source;
|
||||||
struct media_pipeline pipe;
|
|
||||||
|
|
||||||
struct v4l2_subdev subdev;
|
struct v4l2_subdev subdev;
|
||||||
struct media_pad pads[CAL_CAMERARX_NUM_PADS];
|
struct media_pad pads[CAL_CAMERARX_NUM_PADS];
|
||||||
|
@ -937,10 +937,8 @@ static int isp_pipeline_is_last(struct media_entity *me)
|
|||||||
struct isp_pipeline *pipe;
|
struct isp_pipeline *pipe;
|
||||||
struct media_pad *pad;
|
struct media_pad *pad;
|
||||||
|
|
||||||
if (!me->pipe)
|
|
||||||
return 0;
|
|
||||||
pipe = to_isp_pipeline(me);
|
pipe = to_isp_pipeline(me);
|
||||||
if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED)
|
if (!pipe || pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED)
|
||||||
return 0;
|
return 0;
|
||||||
pad = media_pad_remote_pad_first(&pipe->output->pad);
|
pad = media_pad_remote_pad_first(&pipe->output->pad);
|
||||||
return pad->entity == me;
|
return pad->entity == me;
|
||||||
|
@ -1093,8 +1093,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
|
|||||||
/* Start streaming on the pipeline. No link touching an entity in the
|
/* Start streaming on the pipeline. No link touching an entity in the
|
||||||
* pipeline can be activated or deactivated once streaming is started.
|
* pipeline can be activated or deactivated once streaming is started.
|
||||||
*/
|
*/
|
||||||
pipe = video->video.entity.pipe
|
pipe = to_isp_pipeline(&video->video.entity) ? : &video->pipe;
|
||||||
? to_isp_pipeline(&video->video.entity) : &video->pipe;
|
|
||||||
|
|
||||||
ret = media_entity_enum_init(&pipe->ent_enum, &video->isp->media_dev);
|
ret = media_entity_enum_init(&pipe->ent_enum, &video->isp->media_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -1104,7 +1103,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
|
|||||||
pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]);
|
pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]);
|
||||||
pipe->max_rate = pipe->l3_ick;
|
pipe->max_rate = pipe->l3_ick;
|
||||||
|
|
||||||
ret = media_pipeline_start(&video->video.entity, &pipe->pipe);
|
ret = video_device_pipeline_start(&video->video, &pipe->pipe);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_pipeline_start;
|
goto err_pipeline_start;
|
||||||
|
|
||||||
@ -1161,7 +1160,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_check_format:
|
err_check_format:
|
||||||
media_pipeline_stop(&video->video.entity);
|
video_device_pipeline_stop(&video->video);
|
||||||
err_pipeline_start:
|
err_pipeline_start:
|
||||||
/* TODO: Implement PM QoS */
|
/* TODO: Implement PM QoS */
|
||||||
/* The DMA queue must be emptied here, otherwise CCDC interrupts that
|
/* The DMA queue must be emptied here, otherwise CCDC interrupts that
|
||||||
@ -1228,7 +1227,7 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
|
|||||||
video->error = false;
|
video->error = false;
|
||||||
|
|
||||||
/* TODO: Implement PM QoS */
|
/* TODO: Implement PM QoS */
|
||||||
media_pipeline_stop(&video->video.entity);
|
video_device_pipeline_stop(&video->video);
|
||||||
|
|
||||||
media_entity_enum_cleanup(&pipe->ent_enum);
|
media_entity_enum_cleanup(&pipe->ent_enum);
|
||||||
|
|
||||||
|
@ -99,8 +99,15 @@ struct isp_pipeline {
|
|||||||
unsigned int external_width;
|
unsigned int external_width;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_isp_pipeline(__e) \
|
static inline struct isp_pipeline *to_isp_pipeline(struct media_entity *entity)
|
||||||
container_of((__e)->pipe, struct isp_pipeline, pipe)
|
{
|
||||||
|
struct media_pipeline *pipe = media_entity_pipeline(entity);
|
||||||
|
|
||||||
|
if (!pipe)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return container_of(pipe, struct isp_pipeline, pipe);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int isp_pipeline_ready(struct isp_pipeline *pipe)
|
static inline int isp_pipeline_ready(struct isp_pipeline *pipe)
|
||||||
{
|
{
|
||||||
|
@ -251,6 +251,11 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
|
|||||||
|
|
||||||
static int hantro_try_ctrl(struct v4l2_ctrl *ctrl)
|
static int hantro_try_ctrl(struct v4l2_ctrl *ctrl)
|
||||||
{
|
{
|
||||||
|
struct hantro_ctx *ctx;
|
||||||
|
|
||||||
|
ctx = container_of(ctrl->handler,
|
||||||
|
struct hantro_ctx, ctrl_handler);
|
||||||
|
|
||||||
if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) {
|
if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) {
|
||||||
const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps;
|
const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps;
|
||||||
|
|
||||||
@ -266,12 +271,11 @@ static int hantro_try_ctrl(struct v4l2_ctrl *ctrl)
|
|||||||
} else if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) {
|
} else if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) {
|
||||||
const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps;
|
const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps;
|
||||||
|
|
||||||
if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
|
if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
|
||||||
/* Luma and chroma bit depth mismatch */
|
/* Only 8-bit and 10-bit are supported */
|
||||||
return -EINVAL;
|
|
||||||
if (sps->bit_depth_luma_minus8 != 0)
|
|
||||||
/* Only 8-bit is supported */
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
ctx->bit_depth = sps->bit_depth_luma_minus8 + 8;
|
||||||
} else if (ctrl->id == V4L2_CID_STATELESS_VP9_FRAME) {
|
} else if (ctrl->id == V4L2_CID_STATELESS_VP9_FRAME) {
|
||||||
const struct v4l2_ctrl_vp9_frame *dec_params = ctrl->p_new.p_vp9_frame;
|
const struct v4l2_ctrl_vp9_frame *dec_params = ctrl->p_new.p_vp9_frame;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
static size_t hantro_hevc_chroma_offset(struct hantro_ctx *ctx)
|
static size_t hantro_hevc_chroma_offset(struct hantro_ctx *ctx)
|
||||||
{
|
{
|
||||||
return ctx->dst_fmt.width * ctx->dst_fmt.height;
|
return ctx->dst_fmt.width * ctx->dst_fmt.height * ctx->bit_depth / 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t hantro_hevc_motion_vectors_offset(struct hantro_ctx *ctx)
|
static size_t hantro_hevc_motion_vectors_offset(struct hantro_ctx *ctx)
|
||||||
@ -167,8 +167,6 @@ static void set_params(struct hantro_ctx *ctx)
|
|||||||
hantro_reg_write(vpu, &g2_bit_depth_y_minus8, sps->bit_depth_luma_minus8);
|
hantro_reg_write(vpu, &g2_bit_depth_y_minus8, sps->bit_depth_luma_minus8);
|
||||||
hantro_reg_write(vpu, &g2_bit_depth_c_minus8, sps->bit_depth_chroma_minus8);
|
hantro_reg_write(vpu, &g2_bit_depth_c_minus8, sps->bit_depth_chroma_minus8);
|
||||||
|
|
||||||
hantro_reg_write(vpu, &g2_output_8_bits, 0);
|
|
||||||
|
|
||||||
hantro_reg_write(vpu, &g2_hdr_skip_length, compute_header_skip_length(ctx));
|
hantro_reg_write(vpu, &g2_hdr_skip_length, compute_header_skip_length(ctx));
|
||||||
|
|
||||||
min_log2_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
|
min_log2_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
|
||||||
|
@ -104,7 +104,7 @@ static int tile_buffer_reallocate(struct hantro_ctx *ctx)
|
|||||||
hevc_dec->tile_bsd.cpu = NULL;
|
hevc_dec->tile_bsd.cpu = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size = VERT_FILTER_RAM_SIZE * height64 * (num_tile_cols - 1);
|
size = (VERT_FILTER_RAM_SIZE * height64 * (num_tile_cols - 1) * ctx->bit_depth) / 8;
|
||||||
hevc_dec->tile_filter.cpu = dma_alloc_coherent(vpu->dev, size,
|
hevc_dec->tile_filter.cpu = dma_alloc_coherent(vpu->dev, size,
|
||||||
&hevc_dec->tile_filter.dma,
|
&hevc_dec->tile_filter.dma,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
@ -112,7 +112,7 @@ static int tile_buffer_reallocate(struct hantro_ctx *ctx)
|
|||||||
goto err_free_tile_buffers;
|
goto err_free_tile_buffers;
|
||||||
hevc_dec->tile_filter.size = size;
|
hevc_dec->tile_filter.size = size;
|
||||||
|
|
||||||
size = VERT_SAO_RAM_SIZE * height64 * (num_tile_cols - 1);
|
size = (VERT_SAO_RAM_SIZE * height64 * (num_tile_cols - 1) * ctx->bit_depth) / 8;
|
||||||
hevc_dec->tile_sao.cpu = dma_alloc_coherent(vpu->dev, size,
|
hevc_dec->tile_sao.cpu = dma_alloc_coherent(vpu->dev, size,
|
||||||
&hevc_dec->tile_sao.dma,
|
&hevc_dec->tile_sao.dma,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
@ -114,6 +114,7 @@ static void hantro_postproc_g2_enable(struct hantro_ctx *ctx)
|
|||||||
struct hantro_dev *vpu = ctx->dev;
|
struct hantro_dev *vpu = ctx->dev;
|
||||||
struct vb2_v4l2_buffer *dst_buf;
|
struct vb2_v4l2_buffer *dst_buf;
|
||||||
int down_scale = down_scale_factor(ctx);
|
int down_scale = down_scale_factor(ctx);
|
||||||
|
int out_depth;
|
||||||
size_t chroma_offset;
|
size_t chroma_offset;
|
||||||
dma_addr_t dst_dma;
|
dma_addr_t dst_dma;
|
||||||
|
|
||||||
@ -132,8 +133,9 @@ static void hantro_postproc_g2_enable(struct hantro_ctx *ctx)
|
|||||||
hantro_write_addr(vpu, G2_RS_OUT_LUMA_ADDR, dst_dma);
|
hantro_write_addr(vpu, G2_RS_OUT_LUMA_ADDR, dst_dma);
|
||||||
hantro_write_addr(vpu, G2_RS_OUT_CHROMA_ADDR, dst_dma + chroma_offset);
|
hantro_write_addr(vpu, G2_RS_OUT_CHROMA_ADDR, dst_dma + chroma_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out_depth = hantro_get_format_depth(ctx->dst_fmt.pixelformat);
|
||||||
if (ctx->dev->variant->legacy_regs) {
|
if (ctx->dev->variant->legacy_regs) {
|
||||||
int out_depth = hantro_get_format_depth(ctx->dst_fmt.pixelformat);
|
|
||||||
u8 pp_shift = 0;
|
u8 pp_shift = 0;
|
||||||
|
|
||||||
if (out_depth > 8)
|
if (out_depth > 8)
|
||||||
@ -141,6 +143,9 @@ static void hantro_postproc_g2_enable(struct hantro_ctx *ctx)
|
|||||||
|
|
||||||
hantro_reg_write(ctx->dev, &g2_rs_out_bit_depth, out_depth);
|
hantro_reg_write(ctx->dev, &g2_rs_out_bit_depth, out_depth);
|
||||||
hantro_reg_write(ctx->dev, &g2_pp_pix_shift, pp_shift);
|
hantro_reg_write(ctx->dev, &g2_pp_pix_shift, pp_shift);
|
||||||
|
} else {
|
||||||
|
hantro_reg_write(vpu, &g2_output_8_bits, out_depth > 8 ? 0 : 1);
|
||||||
|
hantro_reg_write(vpu, &g2_output_format, out_depth > 8 ? 1 : 0);
|
||||||
}
|
}
|
||||||
hantro_reg_write(vpu, &g2_out_rs_e, 1);
|
hantro_reg_write(vpu, &g2_out_rs_e, 1);
|
||||||
}
|
}
|
||||||
|
@ -162,12 +162,39 @@ static const struct hantro_fmt imx8m_vpu_g2_postproc_fmts[] = {
|
|||||||
.step_height = MB_DIM,
|
.step_height = MB_DIM,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.fourcc = V4L2_PIX_FMT_P010,
|
||||||
|
.codec_mode = HANTRO_MODE_NONE,
|
||||||
|
.postprocessed = true,
|
||||||
|
.frmsize = {
|
||||||
|
.min_width = FMT_MIN_WIDTH,
|
||||||
|
.max_width = FMT_UHD_WIDTH,
|
||||||
|
.step_width = MB_DIM,
|
||||||
|
.min_height = FMT_MIN_HEIGHT,
|
||||||
|
.max_height = FMT_UHD_HEIGHT,
|
||||||
|
.step_height = MB_DIM,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct hantro_fmt imx8m_vpu_g2_dec_fmts[] = {
|
static const struct hantro_fmt imx8m_vpu_g2_dec_fmts[] = {
|
||||||
{
|
{
|
||||||
.fourcc = V4L2_PIX_FMT_NV12_4L4,
|
.fourcc = V4L2_PIX_FMT_NV12_4L4,
|
||||||
.codec_mode = HANTRO_MODE_NONE,
|
.codec_mode = HANTRO_MODE_NONE,
|
||||||
|
.match_depth = true,
|
||||||
|
.frmsize = {
|
||||||
|
.min_width = FMT_MIN_WIDTH,
|
||||||
|
.max_width = FMT_UHD_WIDTH,
|
||||||
|
.step_width = TILE_MB_DIM,
|
||||||
|
.min_height = FMT_MIN_HEIGHT,
|
||||||
|
.max_height = FMT_UHD_HEIGHT,
|
||||||
|
.step_height = TILE_MB_DIM,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.fourcc = V4L2_PIX_FMT_P010_4L4,
|
||||||
|
.codec_mode = HANTRO_MODE_NONE,
|
||||||
|
.match_depth = true,
|
||||||
.frmsize = {
|
.frmsize = {
|
||||||
.min_width = FMT_MIN_WIDTH,
|
.min_width = FMT_MIN_WIDTH,
|
||||||
.max_width = FMT_UHD_WIDTH,
|
.max_width = FMT_UHD_WIDTH,
|
||||||
|
@ -402,10 +402,9 @@ static int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|||||||
* Use the pipeline object embedded in the first DMA object that starts
|
* Use the pipeline object embedded in the first DMA object that starts
|
||||||
* streaming.
|
* streaming.
|
||||||
*/
|
*/
|
||||||
pipe = dma->video.entity.pipe
|
pipe = to_xvip_pipeline(&dma->video) ? : &dma->pipe;
|
||||||
? to_xvip_pipeline(&dma->video.entity) : &dma->pipe;
|
|
||||||
|
|
||||||
ret = media_pipeline_start(&dma->video.entity, &pipe->pipe);
|
ret = video_device_pipeline_start(&dma->video, &pipe->pipe);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
@ -431,7 +430,7 @@ static int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_stop:
|
error_stop:
|
||||||
media_pipeline_stop(&dma->video.entity);
|
video_device_pipeline_stop(&dma->video);
|
||||||
|
|
||||||
error:
|
error:
|
||||||
/* Give back all queued buffers to videobuf2. */
|
/* Give back all queued buffers to videobuf2. */
|
||||||
@ -448,7 +447,7 @@ error:
|
|||||||
static void xvip_dma_stop_streaming(struct vb2_queue *vq)
|
static void xvip_dma_stop_streaming(struct vb2_queue *vq)
|
||||||
{
|
{
|
||||||
struct xvip_dma *dma = vb2_get_drv_priv(vq);
|
struct xvip_dma *dma = vb2_get_drv_priv(vq);
|
||||||
struct xvip_pipeline *pipe = to_xvip_pipeline(&dma->video.entity);
|
struct xvip_pipeline *pipe = to_xvip_pipeline(&dma->video);
|
||||||
struct xvip_dma_buffer *buf, *nbuf;
|
struct xvip_dma_buffer *buf, *nbuf;
|
||||||
|
|
||||||
/* Stop the pipeline. */
|
/* Stop the pipeline. */
|
||||||
@ -459,7 +458,7 @@ static void xvip_dma_stop_streaming(struct vb2_queue *vq)
|
|||||||
|
|
||||||
/* Cleanup the pipeline and mark it as being stopped. */
|
/* Cleanup the pipeline and mark it as being stopped. */
|
||||||
xvip_pipeline_cleanup(pipe);
|
xvip_pipeline_cleanup(pipe);
|
||||||
media_pipeline_stop(&dma->video.entity);
|
video_device_pipeline_stop(&dma->video);
|
||||||
|
|
||||||
/* Give back all queued buffers to videobuf2. */
|
/* Give back all queued buffers to videobuf2. */
|
||||||
spin_lock_irq(&dma->queued_lock);
|
spin_lock_irq(&dma->queued_lock);
|
||||||
|
@ -45,9 +45,14 @@ struct xvip_pipeline {
|
|||||||
struct xvip_dma *output;
|
struct xvip_dma *output;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct xvip_pipeline *to_xvip_pipeline(struct media_entity *e)
|
static inline struct xvip_pipeline *to_xvip_pipeline(struct video_device *vdev)
|
||||||
{
|
{
|
||||||
return container_of(e->pipe, struct xvip_pipeline, pipe);
|
struct media_pipeline *pipe = video_device_pipeline(vdev);
|
||||||
|
|
||||||
|
if (!pipe)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return container_of(pipe, struct xvip_pipeline, pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1072,7 +1072,6 @@ done:
|
|||||||
|
|
||||||
static int si476x_radio_fops_release(struct file *file)
|
static int si476x_radio_fops_release(struct file *file)
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
struct si476x_radio *radio = video_drvdata(file);
|
struct si476x_radio *radio = video_drvdata(file);
|
||||||
|
|
||||||
if (v4l2_fh_is_singular_file(file) &&
|
if (v4l2_fh_is_singular_file(file) &&
|
||||||
@ -1080,9 +1079,7 @@ static int si476x_radio_fops_release(struct file *file)
|
|||||||
si476x_core_set_power_state(radio->core,
|
si476x_core_set_power_state(radio->core,
|
||||||
SI476X_POWER_DOWN);
|
SI476X_POWER_DOWN);
|
||||||
|
|
||||||
err = v4l2_fh_release(file);
|
return v4l2_fh_release(file);
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t si476x_radio_fops_read(struct file *file, char __user *buf,
|
static ssize_t si476x_radio_fops_read(struct file *file, char __user *buf,
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <media/v4l2-device.h>
|
#include <media/v4l2-device.h>
|
||||||
#include <media/v4l2-ioctl.h>
|
#include <media/v4l2-ioctl.h>
|
||||||
|
@ -684,7 +684,6 @@ static int send_packet(struct imon_context *ictx)
|
|||||||
*/
|
*/
|
||||||
static int send_associate_24g(struct imon_context *ictx)
|
static int send_associate_24g(struct imon_context *ictx)
|
||||||
{
|
{
|
||||||
int retval;
|
|
||||||
const unsigned char packet[8] = { 0x01, 0x00, 0x00, 0x00,
|
const unsigned char packet[8] = { 0x01, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x20 };
|
0x00, 0x00, 0x00, 0x20 };
|
||||||
|
|
||||||
@ -699,9 +698,8 @@ static int send_associate_24g(struct imon_context *ictx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
memcpy(ictx->usb_tx_buf, packet, sizeof(packet));
|
memcpy(ictx->usb_tx_buf, packet, sizeof(packet));
|
||||||
retval = send_packet(ictx);
|
|
||||||
|
|
||||||
return retval;
|
return send_packet(ictx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1077,7 +1077,7 @@ static int mceusb_set_timeout(struct rc_dev *dev, unsigned int timeout)
|
|||||||
struct mceusb_dev *ir = dev->priv;
|
struct mceusb_dev *ir = dev->priv;
|
||||||
unsigned int units;
|
unsigned int units;
|
||||||
|
|
||||||
units = DIV_ROUND_CLOSEST(timeout, MCE_TIME_UNIT);
|
units = DIV_ROUND_UP(timeout, MCE_TIME_UNIT);
|
||||||
|
|
||||||
cmdbuf[2] = units >> 8;
|
cmdbuf[2] = units >> 8;
|
||||||
cmdbuf[3] = units;
|
cmdbuf[3] = units;
|
||||||
|
@ -241,13 +241,12 @@ static void vimc_capture_return_all_buffers(struct vimc_capture_device *vcapture
|
|||||||
static int vimc_capture_start_streaming(struct vb2_queue *vq, unsigned int count)
|
static int vimc_capture_start_streaming(struct vb2_queue *vq, unsigned int count)
|
||||||
{
|
{
|
||||||
struct vimc_capture_device *vcapture = vb2_get_drv_priv(vq);
|
struct vimc_capture_device *vcapture = vb2_get_drv_priv(vq);
|
||||||
struct media_entity *entity = &vcapture->vdev.entity;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
vcapture->sequence = 0;
|
vcapture->sequence = 0;
|
||||||
|
|
||||||
/* Start the media pipeline */
|
/* Start the media pipeline */
|
||||||
ret = media_pipeline_start(entity, &vcapture->stream.pipe);
|
ret = video_device_pipeline_start(&vcapture->vdev, &vcapture->stream.pipe);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_QUEUED);
|
vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_QUEUED);
|
||||||
return ret;
|
return ret;
|
||||||
@ -255,7 +254,7 @@ static int vimc_capture_start_streaming(struct vb2_queue *vq, unsigned int count
|
|||||||
|
|
||||||
ret = vimc_streamer_s_stream(&vcapture->stream, &vcapture->ved, 1);
|
ret = vimc_streamer_s_stream(&vcapture->stream, &vcapture->ved, 1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
media_pipeline_stop(entity);
|
video_device_pipeline_stop(&vcapture->vdev);
|
||||||
vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_QUEUED);
|
vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_QUEUED);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -274,7 +273,7 @@ static void vimc_capture_stop_streaming(struct vb2_queue *vq)
|
|||||||
vimc_streamer_s_stream(&vcapture->stream, &vcapture->ved, 0);
|
vimc_streamer_s_stream(&vcapture->stream, &vcapture->ved, 0);
|
||||||
|
|
||||||
/* Stop the media pipeline */
|
/* Stop the media pipeline */
|
||||||
media_pipeline_stop(&vcapture->vdev.entity);
|
video_device_pipeline_stop(&vcapture->vdev);
|
||||||
|
|
||||||
/* Release all active buffers */
|
/* Release all active buffers */
|
||||||
vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_ERROR);
|
vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_ERROR);
|
||||||
|
@ -282,15 +282,13 @@ static int xc4000_tuner_reset(struct dvb_frontend *fe)
|
|||||||
static int xc_write_reg(struct xc4000_priv *priv, u16 regAddr, u16 i2cData)
|
static int xc_write_reg(struct xc4000_priv *priv, u16 regAddr, u16 i2cData)
|
||||||
{
|
{
|
||||||
u8 buf[4];
|
u8 buf[4];
|
||||||
int result;
|
|
||||||
|
|
||||||
buf[0] = (regAddr >> 8) & 0xFF;
|
buf[0] = (regAddr >> 8) & 0xFF;
|
||||||
buf[1] = regAddr & 0xFF;
|
buf[1] = regAddr & 0xFF;
|
||||||
buf[2] = (i2cData >> 8) & 0xFF;
|
buf[2] = (i2cData >> 8) & 0xFF;
|
||||||
buf[3] = i2cData & 0xFF;
|
buf[3] = i2cData & 0xFF;
|
||||||
result = xc_send_i2c_data(priv, buf, 4);
|
|
||||||
|
|
||||||
return result;
|
return xc_send_i2c_data(priv, buf, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
|
static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
|
||||||
|
@ -410,7 +410,7 @@ static int au0828_enable_source(struct media_entity *entity,
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = __media_pipeline_start(entity, pipe);
|
ret = __media_pipeline_start(entity->pads, pipe);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("Start Pipeline: %s->%s Error %d\n",
|
pr_err("Start Pipeline: %s->%s Error %d\n",
|
||||||
source->name, entity->name, ret);
|
source->name, entity->name, ret);
|
||||||
@ -501,12 +501,12 @@ static void au0828_disable_source(struct media_entity *entity)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* stop pipeline */
|
/* stop pipeline */
|
||||||
__media_pipeline_stop(dev->active_link_owner);
|
__media_pipeline_stop(dev->active_link_owner->pads);
|
||||||
pr_debug("Pipeline stop for %s\n",
|
pr_debug("Pipeline stop for %s\n",
|
||||||
dev->active_link_owner->name);
|
dev->active_link_owner->name);
|
||||||
|
|
||||||
ret = __media_pipeline_start(
|
ret = __media_pipeline_start(
|
||||||
dev->active_link_user,
|
dev->active_link_user->pads,
|
||||||
dev->active_link_user_pipe);
|
dev->active_link_user_pipe);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("Start Pipeline: %s->%s %d\n",
|
pr_err("Start Pipeline: %s->%s %d\n",
|
||||||
@ -532,7 +532,7 @@ static void au0828_disable_source(struct media_entity *entity)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* stop pipeline */
|
/* stop pipeline */
|
||||||
__media_pipeline_stop(dev->active_link_owner);
|
__media_pipeline_stop(dev->active_link_owner->pads);
|
||||||
pr_debug("Pipeline stop for %s\n",
|
pr_debug("Pipeline stop for %s\n",
|
||||||
dev->active_link_owner->name);
|
dev->active_link_owner->name);
|
||||||
|
|
||||||
|
@ -1497,7 +1497,7 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap)
|
|||||||
/*
|
/*
|
||||||
* AF9035 gpiot2 = FC0012 enable
|
* AF9035 gpiot2 = FC0012 enable
|
||||||
* XXX: there seems to be something on gpioh8 too, but on my
|
* XXX: there seems to be something on gpioh8 too, but on my
|
||||||
* my test I didn't find any difference.
|
* test I didn't find any difference.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (adap->id == 0) {
|
if (adap->id == 0) {
|
||||||
|
@ -209,7 +209,7 @@ leave:
|
|||||||
*
|
*
|
||||||
* Control bits for previous samples is 32-bit field, containing 16 x 2-bit
|
* Control bits for previous samples is 32-bit field, containing 16 x 2-bit
|
||||||
* numbers. This results one 2-bit number for 8 samples. It is likely used for
|
* numbers. This results one 2-bit number for 8 samples. It is likely used for
|
||||||
* for bit shifting sample by given bits, increasing actual sampling resolution.
|
* bit shifting sample by given bits, increasing actual sampling resolution.
|
||||||
* Number 2 (0b10) was never seen.
|
* Number 2 (0b10) was never seen.
|
||||||
*
|
*
|
||||||
* 6 * 16 * 2 * 4 = 768 samples. 768 * 4 = 3072 bytes
|
* 6 * 16 * 2 * 4 = 768 samples. 768 * 4 = 3072 bytes
|
||||||
|
@ -89,7 +89,7 @@ static int req_to_user(struct v4l2_ext_control *c,
|
|||||||
/* Helper function: copy the initial control value back to the caller */
|
/* Helper function: copy the initial control value back to the caller */
|
||||||
static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
|
static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
|
||||||
{
|
{
|
||||||
ctrl->type_ops->init(ctrl, 0, ctrl->elems, ctrl->p_new);
|
ctrl->type_ops->init(ctrl, 0, ctrl->p_new);
|
||||||
|
|
||||||
return ptr_to_user(c, ctrl, ctrl->p_new);
|
return ptr_to_user(c, ctrl, ctrl->p_new);
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
|
|||||||
if (ctrl->is_dyn_array)
|
if (ctrl->is_dyn_array)
|
||||||
ctrl->new_elems = elems;
|
ctrl->new_elems = elems;
|
||||||
else if (ctrl->is_array)
|
else if (ctrl->is_array)
|
||||||
ctrl->type_ops->init(ctrl, elems, ctrl->elems, ctrl->p_new);
|
ctrl->type_ops->init(ctrl, elems, ctrl->p_new);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,7 +494,7 @@ EXPORT_SYMBOL(v4l2_g_ext_ctrls);
|
|||||||
/* Validate a new control */
|
/* Validate a new control */
|
||||||
static int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new)
|
static int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new)
|
||||||
{
|
{
|
||||||
return ctrl->type_ops->validate(ctrl, ctrl->new_elems, p_new);
|
return ctrl->type_ops->validate(ctrl, p_new);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate controls. */
|
/* Validate controls. */
|
||||||
@ -1007,7 +1007,7 @@ int __v4l2_ctrl_modify_dimensions(struct v4l2_ctrl *ctrl,
|
|||||||
ctrl->p_cur.p = p_array + elems * ctrl->elem_size;
|
ctrl->p_cur.p = p_array + elems * ctrl->elem_size;
|
||||||
for (i = 0; i < ctrl->nr_of_dims; i++)
|
for (i = 0; i < ctrl->nr_of_dims; i++)
|
||||||
ctrl->dims[i] = dims[i];
|
ctrl->dims[i] = dims[i];
|
||||||
ctrl->type_ops->init(ctrl, 0, elems, ctrl->p_cur);
|
ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
|
||||||
cur_to_new(ctrl);
|
cur_to_new(ctrl);
|
||||||
send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_VALUE |
|
send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_VALUE |
|
||||||
V4L2_EVENT_CTRL_CH_DIMENSIONS);
|
V4L2_EVENT_CTRL_CH_DIMENSIONS);
|
||||||
|
@ -65,7 +65,7 @@ void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
|
|||||||
v4l2_event_queue_fh(sev->fh, &ev);
|
v4l2_event_queue_fh(sev->fh, &ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool v4l2_ctrl_type_op_equal(const struct v4l2_ctrl *ctrl, u32 elems,
|
bool v4l2_ctrl_type_op_equal(const struct v4l2_ctrl *ctrl,
|
||||||
union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2)
|
union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
@ -74,7 +74,7 @@ bool v4l2_ctrl_type_op_equal(const struct v4l2_ctrl *ctrl, u32 elems,
|
|||||||
case V4L2_CTRL_TYPE_BUTTON:
|
case V4L2_CTRL_TYPE_BUTTON:
|
||||||
return false;
|
return false;
|
||||||
case V4L2_CTRL_TYPE_STRING:
|
case V4L2_CTRL_TYPE_STRING:
|
||||||
for (i = 0; i < elems; i++) {
|
for (i = 0; i < ctrl->elems; i++) {
|
||||||
unsigned int idx = i * ctrl->elem_size;
|
unsigned int idx = i * ctrl->elem_size;
|
||||||
|
|
||||||
/* strings are always 0-terminated */
|
/* strings are always 0-terminated */
|
||||||
@ -84,7 +84,7 @@ bool v4l2_ctrl_type_op_equal(const struct v4l2_ctrl *ctrl, u32 elems,
|
|||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return !memcmp(ptr1.p_const, ptr2.p_const,
|
return !memcmp(ptr1.p_const, ptr2.p_const,
|
||||||
elems * ctrl->elem_size);
|
ctrl->elems * ctrl->elem_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(v4l2_ctrl_type_op_equal);
|
EXPORT_SYMBOL(v4l2_ctrl_type_op_equal);
|
||||||
@ -178,9 +178,10 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
|
void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
|
||||||
u32 tot_elems, union v4l2_ctrl_ptr ptr)
|
union v4l2_ctrl_ptr ptr)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
u32 tot_elems = ctrl->elems;
|
||||||
u32 elems = tot_elems - from_idx;
|
u32 elems = tot_elems - from_idx;
|
||||||
|
|
||||||
if (from_idx >= tot_elems)
|
if (from_idx >= tot_elems)
|
||||||
@ -995,7 +996,7 @@ static int std_validate_elem(const struct v4l2_ctrl *ctrl, u32 idx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int v4l2_ctrl_type_op_validate(const struct v4l2_ctrl *ctrl, u32 elems,
|
int v4l2_ctrl_type_op_validate(const struct v4l2_ctrl *ctrl,
|
||||||
union v4l2_ctrl_ptr ptr)
|
union v4l2_ctrl_ptr ptr)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
@ -1017,11 +1018,11 @@ int v4l2_ctrl_type_op_validate(const struct v4l2_ctrl *ctrl, u32 elems,
|
|||||||
|
|
||||||
case V4L2_CTRL_TYPE_BUTTON:
|
case V4L2_CTRL_TYPE_BUTTON:
|
||||||
case V4L2_CTRL_TYPE_CTRL_CLASS:
|
case V4L2_CTRL_TYPE_CTRL_CLASS:
|
||||||
memset(ptr.p_s32, 0, elems * sizeof(s32));
|
memset(ptr.p_s32, 0, ctrl->new_elems * sizeof(s32));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; !ret && i < elems; i++)
|
for (i = 0; !ret && i < ctrl->new_elems; i++)
|
||||||
ret = std_validate_elem(ctrl, i, ptr);
|
ret = std_validate_elem(ctrl, i, ptr);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1724,7 +1725,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
|
|||||||
memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
|
memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrl->type_ops->init(ctrl, 0, elems, ctrl->p_cur);
|
ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
|
||||||
cur_to_new(ctrl);
|
cur_to_new(ctrl);
|
||||||
|
|
||||||
if (handler_new_ref(hdl, ctrl, NULL, false, false)) {
|
if (handler_new_ref(hdl, ctrl, NULL, false, false)) {
|
||||||
@ -2069,7 +2070,7 @@ static int cluster_changed(struct v4l2_ctrl *master)
|
|||||||
ctrl_changed = true;
|
ctrl_changed = true;
|
||||||
if (!ctrl_changed)
|
if (!ctrl_changed)
|
||||||
ctrl_changed = !ctrl->type_ops->equal(ctrl,
|
ctrl_changed = !ctrl->type_ops->equal(ctrl,
|
||||||
ctrl->elems, ctrl->p_cur, ctrl->p_new);
|
ctrl->p_cur, ctrl->p_new);
|
||||||
ctrl->has_changed = ctrl_changed;
|
ctrl->has_changed = ctrl_changed;
|
||||||
changed |= ctrl->has_changed;
|
changed |= ctrl->has_changed;
|
||||||
}
|
}
|
||||||
|
@ -1095,6 +1095,78 @@ void video_unregister_device(struct video_device *vdev)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(video_unregister_device);
|
EXPORT_SYMBOL(video_unregister_device);
|
||||||
|
|
||||||
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
|
|
||||||
|
__must_check int video_device_pipeline_start(struct video_device *vdev,
|
||||||
|
struct media_pipeline *pipe)
|
||||||
|
{
|
||||||
|
struct media_entity *entity = &vdev->entity;
|
||||||
|
|
||||||
|
if (entity->num_pads != 1)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
return media_pipeline_start(&entity->pads[0], pipe);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(video_device_pipeline_start);
|
||||||
|
|
||||||
|
__must_check int __video_device_pipeline_start(struct video_device *vdev,
|
||||||
|
struct media_pipeline *pipe)
|
||||||
|
{
|
||||||
|
struct media_entity *entity = &vdev->entity;
|
||||||
|
|
||||||
|
if (entity->num_pads != 1)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
return __media_pipeline_start(&entity->pads[0], pipe);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(__video_device_pipeline_start);
|
||||||
|
|
||||||
|
void video_device_pipeline_stop(struct video_device *vdev)
|
||||||
|
{
|
||||||
|
struct media_entity *entity = &vdev->entity;
|
||||||
|
|
||||||
|
if (WARN_ON(entity->num_pads != 1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
return media_pipeline_stop(&entity->pads[0]);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(video_device_pipeline_stop);
|
||||||
|
|
||||||
|
void __video_device_pipeline_stop(struct video_device *vdev)
|
||||||
|
{
|
||||||
|
struct media_entity *entity = &vdev->entity;
|
||||||
|
|
||||||
|
if (WARN_ON(entity->num_pads != 1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
return __media_pipeline_stop(&entity->pads[0]);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(__video_device_pipeline_stop);
|
||||||
|
|
||||||
|
__must_check int video_device_pipeline_alloc_start(struct video_device *vdev)
|
||||||
|
{
|
||||||
|
struct media_entity *entity = &vdev->entity;
|
||||||
|
|
||||||
|
if (entity->num_pads != 1)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
return media_pipeline_alloc_start(&entity->pads[0]);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(video_device_pipeline_alloc_start);
|
||||||
|
|
||||||
|
struct media_pipeline *video_device_pipeline(struct video_device *vdev)
|
||||||
|
{
|
||||||
|
struct media_entity *entity = &vdev->entity;
|
||||||
|
|
||||||
|
if (WARN_ON(entity->num_pads != 1))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return media_pad_pipeline(&entity->pads[0]);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(video_device_pipeline);
|
||||||
|
|
||||||
|
#endif /* CONFIG_MEDIA_CONTROLLER */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialise video for linux
|
* Initialise video for linux
|
||||||
*/
|
*/
|
||||||
|
@ -17,7 +17,6 @@ atomisp-objs += \
|
|||||||
pci/atomisp_compat_css20.o \
|
pci/atomisp_compat_css20.o \
|
||||||
pci/atomisp_csi2.o \
|
pci/atomisp_csi2.o \
|
||||||
pci/atomisp_drvfs.o \
|
pci/atomisp_drvfs.o \
|
||||||
pci/atomisp_file.o \
|
|
||||||
pci/atomisp_fops.o \
|
pci/atomisp_fops.o \
|
||||||
pci/atomisp_ioctl.o \
|
pci/atomisp_ioctl.o \
|
||||||
pci/atomisp_subdev.o \
|
pci/atomisp_subdev.o \
|
||||||
|
@ -841,8 +841,6 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
|
|||||||
if (!ov2680_info)
|
if (!ov2680_info)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&dev->input_lock);
|
|
||||||
|
|
||||||
res = v4l2_find_nearest_size(ov2680_res_preview,
|
res = v4l2_find_nearest_size(ov2680_res_preview,
|
||||||
ARRAY_SIZE(ov2680_res_preview), width,
|
ARRAY_SIZE(ov2680_res_preview), width,
|
||||||
height, fmt->width, fmt->height);
|
height, fmt->width, fmt->height);
|
||||||
@ -855,19 +853,22 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
|
|||||||
fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
|
fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
|
||||||
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
|
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
|
||||||
sd_state->pads->try_fmt = *fmt;
|
sd_state->pads->try_fmt = *fmt;
|
||||||
mutex_unlock(&dev->input_lock);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(&client->dev, "%s: %dx%d\n",
|
dev_dbg(&client->dev, "%s: %dx%d\n",
|
||||||
__func__, fmt->width, fmt->height);
|
__func__, fmt->width, fmt->height);
|
||||||
|
|
||||||
|
mutex_lock(&dev->input_lock);
|
||||||
|
|
||||||
/* s_power has not been called yet for std v4l2 clients (camorama) */
|
/* s_power has not been called yet for std v4l2 clients (camorama) */
|
||||||
power_up(sd);
|
power_up(sd);
|
||||||
ret = ov2680_write_reg_array(client, dev->res->regs);
|
ret = ov2680_write_reg_array(client, dev->res->regs);
|
||||||
if (ret)
|
if (ret) {
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"ov2680 write resolution register err: %d\n", ret);
|
"ov2680 write resolution register err: %d\n", ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
vts = dev->res->lines_per_frame;
|
vts = dev->res->lines_per_frame;
|
||||||
|
|
||||||
@ -876,8 +877,10 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
|
|||||||
vts = dev->exposure + OV2680_INTEGRATION_TIME_MARGIN;
|
vts = dev->exposure + OV2680_INTEGRATION_TIME_MARGIN;
|
||||||
|
|
||||||
ret = ov2680_write_reg(client, 2, OV2680_TIMING_VTS_H, vts);
|
ret = ov2680_write_reg(client, 2, OV2680_TIMING_VTS_H, vts);
|
||||||
if (ret)
|
if (ret) {
|
||||||
dev_err(&client->dev, "ov2680 write vts err: %d\n", ret);
|
dev_err(&client->dev, "ov2680 write vts err: %d\n", ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
ret = ov2680_get_intg_factor(client, ov2680_info, res);
|
ret = ov2680_get_intg_factor(client, ov2680_info, res);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@ -894,11 +897,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
|
|||||||
if (v_flag)
|
if (v_flag)
|
||||||
ov2680_v_flip(sd, v_flag);
|
ov2680_v_flip(sd, v_flag);
|
||||||
|
|
||||||
/*
|
dev->res = res;
|
||||||
* ret = startup(sd);
|
|
||||||
* if (ret)
|
|
||||||
* dev_err(&client->dev, "ov2680 startup err\n");
|
|
||||||
*/
|
|
||||||
err:
|
err:
|
||||||
mutex_unlock(&dev->input_lock);
|
mutex_unlock(&dev->input_lock);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -65,9 +65,6 @@
|
|||||||
#define check_bo_null_return_void(bo) \
|
#define check_bo_null_return_void(bo) \
|
||||||
check_null_return_void(bo, "NULL hmm buffer object.\n")
|
check_null_return_void(bo, "NULL hmm buffer object.\n")
|
||||||
|
|
||||||
#define HMM_MAX_ORDER 3
|
|
||||||
#define HMM_MIN_ORDER 0
|
|
||||||
|
|
||||||
#define ISP_VM_START 0x0
|
#define ISP_VM_START 0x0
|
||||||
#define ISP_VM_SIZE (0x7FFFFFFF) /* 2G address space */
|
#define ISP_VM_SIZE (0x7FFFFFFF) /* 2G address space */
|
||||||
#define ISP_PTR_NULL NULL
|
#define ISP_PTR_NULL NULL
|
||||||
@ -89,8 +86,6 @@ enum hmm_bo_type {
|
|||||||
#define HMM_BO_VMAPED 0x10
|
#define HMM_BO_VMAPED 0x10
|
||||||
#define HMM_BO_VMAPED_CACHED 0x20
|
#define HMM_BO_VMAPED_CACHED 0x20
|
||||||
#define HMM_BO_ACTIVE 0x1000
|
#define HMM_BO_ACTIVE 0x1000
|
||||||
#define HMM_BO_MEM_TYPE_USER 0x1
|
|
||||||
#define HMM_BO_MEM_TYPE_PFN 0x2
|
|
||||||
|
|
||||||
struct hmm_bo_device {
|
struct hmm_bo_device {
|
||||||
struct isp_mmu mmu;
|
struct isp_mmu mmu;
|
||||||
@ -126,7 +121,6 @@ struct hmm_buffer_object {
|
|||||||
enum hmm_bo_type type;
|
enum hmm_bo_type type;
|
||||||
int mmap_count;
|
int mmap_count;
|
||||||
int status;
|
int status;
|
||||||
int mem_type;
|
|
||||||
void *vmap_addr; /* kernel virtual address by vmap */
|
void *vmap_addr; /* kernel virtual address by vmap */
|
||||||
|
|
||||||
struct rb_node node;
|
struct rb_node node;
|
||||||
|
@ -740,20 +740,6 @@ enum atomisp_frame_status {
|
|||||||
ATOMISP_FRAME_STATUS_FLASH_FAILED,
|
ATOMISP_FRAME_STATUS_FLASH_FAILED,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ISP memories, isp2400 */
|
|
||||||
enum atomisp_acc_memory {
|
|
||||||
ATOMISP_ACC_MEMORY_PMEM0 = 0,
|
|
||||||
ATOMISP_ACC_MEMORY_DMEM0,
|
|
||||||
/* for backward compatibility */
|
|
||||||
ATOMISP_ACC_MEMORY_DMEM = ATOMISP_ACC_MEMORY_DMEM0,
|
|
||||||
ATOMISP_ACC_MEMORY_VMEM0,
|
|
||||||
ATOMISP_ACC_MEMORY_VAMEM0,
|
|
||||||
ATOMISP_ACC_MEMORY_VAMEM1,
|
|
||||||
ATOMISP_ACC_MEMORY_VAMEM2,
|
|
||||||
ATOMISP_ACC_MEMORY_HMEM0,
|
|
||||||
ATOMISP_ACC_NR_MEMORY
|
|
||||||
};
|
|
||||||
|
|
||||||
enum atomisp_ext_isp_id {
|
enum atomisp_ext_isp_id {
|
||||||
EXT_ISP_CID_ISO = 0,
|
EXT_ISP_CID_ISO = 0,
|
||||||
EXT_ISP_CID_CAPTURE_HDR,
|
EXT_ISP_CID_CAPTURE_HDR,
|
||||||
|
@ -26,8 +26,6 @@ struct v4l2_subdev *atomisp_gmin_find_subdev(struct i2c_adapter *adapter,
|
|||||||
int atomisp_gmin_remove_subdev(struct v4l2_subdev *sd);
|
int atomisp_gmin_remove_subdev(struct v4l2_subdev *sd);
|
||||||
int gmin_get_var_int(struct device *dev, bool is_gmin,
|
int gmin_get_var_int(struct device *dev, bool is_gmin,
|
||||||
const char *var, int def);
|
const char *var, int def);
|
||||||
int camera_sensor_csi(struct v4l2_subdev *sd, u32 port,
|
|
||||||
u32 lanes, u32 format, u32 bayer_order, int flag);
|
|
||||||
struct camera_sensor_platform_data *
|
struct camera_sensor_platform_data *
|
||||||
gmin_camera_platform_data(
|
gmin_camera_platform_data(
|
||||||
struct v4l2_subdev *subdev,
|
struct v4l2_subdev *subdev,
|
||||||
|
@ -141,23 +141,6 @@ struct atomisp_platform_data {
|
|||||||
struct intel_v4l2_subdev_table *subdevs;
|
struct intel_v4l2_subdev_table *subdevs;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Describe the capacities of one single sensor. */
|
|
||||||
struct atomisp_sensor_caps {
|
|
||||||
/* The number of streams this sensor can output. */
|
|
||||||
int stream_num;
|
|
||||||
bool is_slave;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Describe the capacities of sensors connected to one camera port. */
|
|
||||||
struct atomisp_camera_caps {
|
|
||||||
/* The number of sensors connected to this camera port. */
|
|
||||||
int sensor_num;
|
|
||||||
/* The capacities of each sensor. */
|
|
||||||
struct atomisp_sensor_caps sensor[MAX_SENSORS_PER_PORT];
|
|
||||||
/* Define whether stream control is required for multiple streams. */
|
|
||||||
bool multi_stream_ctrl;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sensor of external ISP can send multiple steams with different mipi data
|
* Sensor of external ISP can send multiple steams with different mipi data
|
||||||
* type in the same virtual channel. This information needs to come from the
|
* type in the same virtual channel. This information needs to come from the
|
||||||
@ -235,7 +218,6 @@ struct camera_mipi_info {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const struct atomisp_platform_data *atomisp_get_platform_data(void);
|
const struct atomisp_platform_data *atomisp_get_platform_data(void);
|
||||||
const struct atomisp_camera_caps *atomisp_get_default_camera_caps(void);
|
|
||||||
|
|
||||||
/* API from old platform_camera.h, new CPUID implementation */
|
/* API from old platform_camera.h, new CPUID implementation */
|
||||||
#define __IS_SOC(x) (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && \
|
#define __IS_SOC(x) (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && \
|
||||||
|
@ -28,3 +28,22 @@ Since getting a picture requires multiple processing steps,
|
|||||||
this means that unlike in fixed pipelines the soft pipelines
|
this means that unlike in fixed pipelines the soft pipelines
|
||||||
on the ISP can do multiple processing steps in a single pipeline
|
on the ISP can do multiple processing steps in a single pipeline
|
||||||
element (in a single binary).
|
element (in a single binary).
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
The sensor drivers use of v4l2_get_subdev_hostdata(), which returns
|
||||||
|
a camera_mipi_info struct. This struct is allocated/managed by
|
||||||
|
the core atomisp code. The most important parts of the struct
|
||||||
|
are filled by the atomisp core itself, like e.g. the port number.
|
||||||
|
|
||||||
|
The sensor drivers on a set_fmt call do fill in camera_mipi_info.data
|
||||||
|
which is a atomisp_sensor_mode_data struct. This gets filled from
|
||||||
|
a function called <sensor_name>_get_intg_factor(). This struct is not
|
||||||
|
used by the atomisp code at all. It is returned to userspace by
|
||||||
|
a ATOMISP_IOC_G_SENSOR_MODE_DATA and the Android userspace does use this.
|
||||||
|
|
||||||
|
Other members of camera_mipi_info which are set by some drivers are:
|
||||||
|
-metadata_width, metadata_height, metadata_effective_width, set by
|
||||||
|
the ov5693 driver (and used by the atomisp core)
|
||||||
|
-raw_bayer_order, adjusted by the ov2680 driver when flipping since
|
||||||
|
flipping can change the bayer order
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -54,7 +54,6 @@ void dump_sp_dmem(struct atomisp_device *isp, unsigned int addr,
|
|||||||
unsigned int size);
|
unsigned int size);
|
||||||
struct camera_mipi_info *atomisp_to_sensor_mipi_info(struct v4l2_subdev *sd);
|
struct camera_mipi_info *atomisp_to_sensor_mipi_info(struct v4l2_subdev *sd);
|
||||||
struct atomisp_video_pipe *atomisp_to_video_pipe(struct video_device *dev);
|
struct atomisp_video_pipe *atomisp_to_video_pipe(struct video_device *dev);
|
||||||
struct atomisp_acc_pipe *atomisp_to_acc_pipe(struct video_device *dev);
|
|
||||||
int atomisp_reset(struct atomisp_device *isp);
|
int atomisp_reset(struct atomisp_device *isp);
|
||||||
void atomisp_flush_bufs_and_wakeup(struct atomisp_sub_device *asd);
|
void atomisp_flush_bufs_and_wakeup(struct atomisp_sub_device *asd);
|
||||||
void atomisp_clear_css_buffer_counters(struct atomisp_sub_device *asd);
|
void atomisp_clear_css_buffer_counters(struct atomisp_sub_device *asd);
|
||||||
@ -66,8 +65,7 @@ bool atomisp_buffers_queued_pipe(struct atomisp_video_pipe *pipe);
|
|||||||
/* Interrupt functions */
|
/* Interrupt functions */
|
||||||
void atomisp_msi_irq_init(struct atomisp_device *isp);
|
void atomisp_msi_irq_init(struct atomisp_device *isp);
|
||||||
void atomisp_msi_irq_uninit(struct atomisp_device *isp);
|
void atomisp_msi_irq_uninit(struct atomisp_device *isp);
|
||||||
void atomisp_wdt_work(struct work_struct *work);
|
void atomisp_assert_recovery_work(struct work_struct *work);
|
||||||
void atomisp_wdt(struct timer_list *t);
|
|
||||||
void atomisp_setup_flash(struct atomisp_sub_device *asd);
|
void atomisp_setup_flash(struct atomisp_sub_device *asd);
|
||||||
irqreturn_t atomisp_isr(int irq, void *dev);
|
irqreturn_t atomisp_isr(int irq, void *dev);
|
||||||
irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr);
|
irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr);
|
||||||
@ -268,8 +266,7 @@ int atomisp_get_sensor_mode_data(struct atomisp_sub_device *asd,
|
|||||||
int atomisp_try_fmt(struct video_device *vdev, struct v4l2_pix_format *f,
|
int atomisp_try_fmt(struct video_device *vdev, struct v4l2_pix_format *f,
|
||||||
bool *res_overflow);
|
bool *res_overflow);
|
||||||
|
|
||||||
int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f);
|
int atomisp_set_fmt(struct file *file, void *fh, struct v4l2_format *f);
|
||||||
int atomisp_set_fmt_file(struct video_device *vdev, struct v4l2_format *f);
|
|
||||||
|
|
||||||
int atomisp_set_shading_table(struct atomisp_sub_device *asd,
|
int atomisp_set_shading_table(struct atomisp_sub_device *asd,
|
||||||
struct atomisp_shading_table *shading_table);
|
struct atomisp_shading_table *shading_table);
|
||||||
@ -300,8 +297,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
|
|||||||
bool q_buffers, enum atomisp_input_stream_id stream_id);
|
bool q_buffers, enum atomisp_input_stream_id stream_id);
|
||||||
|
|
||||||
void atomisp_css_flush(struct atomisp_device *isp);
|
void atomisp_css_flush(struct atomisp_device *isp);
|
||||||
int atomisp_source_pad_to_stream_id(struct atomisp_sub_device *asd,
|
|
||||||
uint16_t source_pad);
|
|
||||||
|
|
||||||
/* Events. Only one event has to be exported for now. */
|
/* Events. Only one event has to be exported for now. */
|
||||||
void atomisp_eof_event(struct atomisp_sub_device *asd, uint8_t exp_id);
|
void atomisp_eof_event(struct atomisp_sub_device *asd, uint8_t exp_id);
|
||||||
@ -324,8 +319,6 @@ void atomisp_flush_params_queue(struct atomisp_video_pipe *asd);
|
|||||||
int atomisp_exp_id_unlock(struct atomisp_sub_device *asd, int *exp_id);
|
int atomisp_exp_id_unlock(struct atomisp_sub_device *asd, int *exp_id);
|
||||||
int atomisp_exp_id_capture(struct atomisp_sub_device *asd, int *exp_id);
|
int atomisp_exp_id_capture(struct atomisp_sub_device *asd, int *exp_id);
|
||||||
|
|
||||||
/* Function to update Raw Buffer bitmap */
|
|
||||||
int atomisp_set_raw_buffer_bitmap(struct atomisp_sub_device *asd, int exp_id);
|
|
||||||
void atomisp_init_raw_buffer_bitmap(struct atomisp_sub_device *asd);
|
void atomisp_init_raw_buffer_bitmap(struct atomisp_sub_device *asd);
|
||||||
|
|
||||||
/* Function to enable/disable zoom for capture pipe */
|
/* Function to enable/disable zoom for capture pipe */
|
||||||
|
@ -129,10 +129,6 @@ int atomisp_alloc_metadata_output_buf(struct atomisp_sub_device *asd);
|
|||||||
|
|
||||||
void atomisp_free_metadata_output_buf(struct atomisp_sub_device *asd);
|
void atomisp_free_metadata_output_buf(struct atomisp_sub_device *asd);
|
||||||
|
|
||||||
void atomisp_css_get_dis_statistics(struct atomisp_sub_device *asd,
|
|
||||||
struct atomisp_css_buffer *isp_css_buffer,
|
|
||||||
struct ia_css_isp_dvs_statistics_map *dvs_map);
|
|
||||||
|
|
||||||
void atomisp_css_temp_pipe_to_pipe_id(struct atomisp_sub_device *asd,
|
void atomisp_css_temp_pipe_to_pipe_id(struct atomisp_sub_device *asd,
|
||||||
struct atomisp_css_event *current_event);
|
struct atomisp_css_event *current_event);
|
||||||
|
|
||||||
@ -434,17 +430,11 @@ void atomisp_css_get_morph_table(struct atomisp_sub_device *asd,
|
|||||||
|
|
||||||
void atomisp_css_morph_table_free(struct ia_css_morph_table *table);
|
void atomisp_css_morph_table_free(struct ia_css_morph_table *table);
|
||||||
|
|
||||||
void atomisp_css_set_cont_prev_start_time(struct atomisp_device *isp,
|
|
||||||
unsigned int overlap);
|
|
||||||
|
|
||||||
int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd,
|
int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd,
|
||||||
struct atomisp_dis_statistics *stats);
|
struct atomisp_dis_statistics *stats);
|
||||||
|
|
||||||
int atomisp_css_update_stream(struct atomisp_sub_device *asd);
|
int atomisp_css_update_stream(struct atomisp_sub_device *asd);
|
||||||
|
|
||||||
struct atomisp_acc_fw;
|
|
||||||
int atomisp_css_set_acc_parameters(struct atomisp_acc_fw *acc_fw);
|
|
||||||
|
|
||||||
int atomisp_css_isr_thread(struct atomisp_device *isp,
|
int atomisp_css_isr_thread(struct atomisp_device *isp,
|
||||||
bool *frame_done_found,
|
bool *frame_done_found,
|
||||||
bool *css_pipe_done);
|
bool *css_pipe_done);
|
||||||
|
@ -1427,7 +1427,6 @@ int atomisp_css_get_grid_info(struct atomisp_sub_device *asd,
|
|||||||
struct ia_css_pipe_info p_info;
|
struct ia_css_pipe_info p_info;
|
||||||
struct ia_css_grid_info old_info;
|
struct ia_css_grid_info old_info;
|
||||||
struct atomisp_device *isp = asd->isp;
|
struct atomisp_device *isp = asd->isp;
|
||||||
int stream_index = atomisp_source_pad_to_stream_id(asd, source_pad);
|
|
||||||
int md_width = asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].
|
int md_width = asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].
|
||||||
stream_config.metadata_config.resolution.width;
|
stream_config.metadata_config.resolution.width;
|
||||||
|
|
||||||
@ -1435,7 +1434,7 @@ int atomisp_css_get_grid_info(struct atomisp_sub_device *asd,
|
|||||||
memset(&old_info, 0, sizeof(struct ia_css_grid_info));
|
memset(&old_info, 0, sizeof(struct ia_css_grid_info));
|
||||||
|
|
||||||
if (ia_css_pipe_get_info(
|
if (ia_css_pipe_get_info(
|
||||||
asd->stream_env[stream_index].pipes[pipe_id],
|
asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].pipes[pipe_id],
|
||||||
&p_info) != 0) {
|
&p_info) != 0) {
|
||||||
dev_err(isp->dev, "ia_css_pipe_get_info failed\n");
|
dev_err(isp->dev, "ia_css_pipe_get_info failed\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -1574,20 +1573,6 @@ void atomisp_free_metadata_output_buf(struct atomisp_sub_device *asd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void atomisp_css_get_dis_statistics(struct atomisp_sub_device *asd,
|
|
||||||
struct atomisp_css_buffer *isp_css_buffer,
|
|
||||||
struct ia_css_isp_dvs_statistics_map *dvs_map)
|
|
||||||
{
|
|
||||||
if (asd->params.dvs_stat) {
|
|
||||||
if (dvs_map)
|
|
||||||
ia_css_translate_dvs2_statistics(
|
|
||||||
asd->params.dvs_stat, dvs_map);
|
|
||||||
else
|
|
||||||
ia_css_get_dvs2_statistics(asd->params.dvs_stat,
|
|
||||||
isp_css_buffer->css_buffer.data.stats_dvs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void atomisp_css_temp_pipe_to_pipe_id(struct atomisp_sub_device *asd,
|
void atomisp_css_temp_pipe_to_pipe_id(struct atomisp_sub_device *asd,
|
||||||
struct atomisp_css_event *current_event)
|
struct atomisp_css_event *current_event)
|
||||||
{
|
{
|
||||||
@ -2694,11 +2679,11 @@ int atomisp_get_css_frame_info(struct atomisp_sub_device *asd,
|
|||||||
struct atomisp_device *isp = asd->isp;
|
struct atomisp_device *isp = asd->isp;
|
||||||
|
|
||||||
if (ATOMISP_SOC_CAMERA(asd)) {
|
if (ATOMISP_SOC_CAMERA(asd)) {
|
||||||
stream_index = atomisp_source_pad_to_stream_id(asd, source_pad);
|
stream_index = ATOMISP_INPUT_STREAM_GENERAL;
|
||||||
} else {
|
} else {
|
||||||
stream_index = (pipe_index == IA_CSS_PIPE_ID_YUVPP) ?
|
stream_index = (pipe_index == IA_CSS_PIPE_ID_YUVPP) ?
|
||||||
ATOMISP_INPUT_STREAM_VIDEO :
|
ATOMISP_INPUT_STREAM_VIDEO :
|
||||||
atomisp_source_pad_to_stream_id(asd, source_pad);
|
ATOMISP_INPUT_STREAM_GENERAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 != ia_css_pipe_get_info(asd->stream_env[stream_index]
|
if (0 != ia_css_pipe_get_info(asd->stream_env[stream_index]
|
||||||
@ -3626,6 +3611,8 @@ int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd,
|
|||||||
struct atomisp_dis_buf *dis_buf;
|
struct atomisp_dis_buf *dis_buf;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
lockdep_assert_held(&isp->mutex);
|
||||||
|
|
||||||
if (!asd->params.dvs_stat->hor_prod.odd_real ||
|
if (!asd->params.dvs_stat->hor_prod.odd_real ||
|
||||||
!asd->params.dvs_stat->hor_prod.odd_imag ||
|
!asd->params.dvs_stat->hor_prod.odd_imag ||
|
||||||
!asd->params.dvs_stat->hor_prod.even_real ||
|
!asd->params.dvs_stat->hor_prod.even_real ||
|
||||||
@ -3637,12 +3624,8 @@ int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* isp needs to be streaming to get DIS statistics */
|
/* isp needs to be streaming to get DIS statistics */
|
||||||
spin_lock_irqsave(&isp->lock, flags);
|
if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED)
|
||||||
if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) {
|
|
||||||
spin_unlock_irqrestore(&isp->lock, flags);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(&isp->lock, flags);
|
|
||||||
|
|
||||||
if (atomisp_compare_dvs_grid(asd, &stats->dvs2_stat.grid_info) != 0)
|
if (atomisp_compare_dvs_grid(asd, &stats->dvs2_stat.grid_info) != 0)
|
||||||
/* If the grid info in the argument differs from the current
|
/* If the grid info in the argument differs from the current
|
||||||
@ -3763,32 +3746,6 @@ void atomisp_css_morph_table_free(struct ia_css_morph_table *table)
|
|||||||
ia_css_morph_table_free(table);
|
ia_css_morph_table_free(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
void atomisp_css_set_cont_prev_start_time(struct atomisp_device *isp,
|
|
||||||
unsigned int overlap)
|
|
||||||
{
|
|
||||||
/* CSS 2.0 doesn't support this API. */
|
|
||||||
dev_dbg(isp->dev, "set cont prev start time is not supported.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the ACC binary arguments */
|
|
||||||
int atomisp_css_set_acc_parameters(struct atomisp_acc_fw *acc_fw)
|
|
||||||
{
|
|
||||||
unsigned int mem;
|
|
||||||
|
|
||||||
for (mem = 0; mem < ATOMISP_ACC_NR_MEMORY; mem++) {
|
|
||||||
if (acc_fw->args[mem].length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ia_css_isp_param_set_css_mem_init(&acc_fw->fw->mem_initializers,
|
|
||||||
IA_CSS_PARAM_CLASS_PARAM, mem,
|
|
||||||
acc_fw->args[mem].css_ptr,
|
|
||||||
acc_fw->args[mem].length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct atomisp_sub_device *__get_atomisp_subdev(
|
static struct atomisp_sub_device *__get_atomisp_subdev(
|
||||||
struct ia_css_pipe *css_pipe,
|
struct ia_css_pipe *css_pipe,
|
||||||
struct atomisp_device *isp,
|
struct atomisp_device *isp,
|
||||||
@ -3824,8 +3781,8 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
|
|||||||
enum atomisp_input_stream_id stream_id = 0;
|
enum atomisp_input_stream_id stream_id = 0;
|
||||||
struct atomisp_css_event current_event;
|
struct atomisp_css_event current_event;
|
||||||
struct atomisp_sub_device *asd;
|
struct atomisp_sub_device *asd;
|
||||||
bool reset_wdt_timer[MAX_STREAM_NUM] = {false};
|
|
||||||
int i;
|
lockdep_assert_held(&isp->mutex);
|
||||||
|
|
||||||
while (!ia_css_dequeue_psys_event(¤t_event.event)) {
|
while (!ia_css_dequeue_psys_event(¤t_event.event)) {
|
||||||
if (current_event.event.type ==
|
if (current_event.event.type ==
|
||||||
@ -3839,14 +3796,8 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
|
|||||||
__func__,
|
__func__,
|
||||||
current_event.event.fw_assert_module_id,
|
current_event.event.fw_assert_module_id,
|
||||||
current_event.event.fw_assert_line_no);
|
current_event.event.fw_assert_line_no);
|
||||||
for (i = 0; i < isp->num_of_streams; i++)
|
|
||||||
atomisp_wdt_stop(&isp->asd[i], 0);
|
|
||||||
|
|
||||||
if (!IS_ISP2401)
|
|
||||||
atomisp_wdt(&isp->asd[0].wdt);
|
|
||||||
else
|
|
||||||
queue_work(isp->wdt_work_queue, &isp->wdt_work);
|
|
||||||
|
|
||||||
|
queue_work(system_long_wq, &isp->assert_recovery_work);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
} else if (current_event.event.type == IA_CSS_EVENT_TYPE_FW_WARNING) {
|
} else if (current_event.event.type == IA_CSS_EVENT_TYPE_FW_WARNING) {
|
||||||
dev_warn(isp->dev, "%s: ISP reports warning, code is %d, exp_id %d\n",
|
dev_warn(isp->dev, "%s: ISP reports warning, code is %d, exp_id %d\n",
|
||||||
@ -3875,20 +3826,12 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
|
|||||||
frame_done_found[asd->index] = true;
|
frame_done_found[asd->index] = true;
|
||||||
atomisp_buf_done(asd, 0, IA_CSS_BUFFER_TYPE_OUTPUT_FRAME,
|
atomisp_buf_done(asd, 0, IA_CSS_BUFFER_TYPE_OUTPUT_FRAME,
|
||||||
current_event.pipe, true, stream_id);
|
current_event.pipe, true, stream_id);
|
||||||
|
|
||||||
if (!IS_ISP2401)
|
|
||||||
reset_wdt_timer[asd->index] = true; /* ISP running */
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case IA_CSS_EVENT_TYPE_SECOND_OUTPUT_FRAME_DONE:
|
case IA_CSS_EVENT_TYPE_SECOND_OUTPUT_FRAME_DONE:
|
||||||
dev_dbg(isp->dev, "event: Second output frame done");
|
dev_dbg(isp->dev, "event: Second output frame done");
|
||||||
frame_done_found[asd->index] = true;
|
frame_done_found[asd->index] = true;
|
||||||
atomisp_buf_done(asd, 0, IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME,
|
atomisp_buf_done(asd, 0, IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME,
|
||||||
current_event.pipe, true, stream_id);
|
current_event.pipe, true, stream_id);
|
||||||
|
|
||||||
if (!IS_ISP2401)
|
|
||||||
reset_wdt_timer[asd->index] = true; /* ISP running */
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case IA_CSS_EVENT_TYPE_3A_STATISTICS_DONE:
|
case IA_CSS_EVENT_TYPE_3A_STATISTICS_DONE:
|
||||||
dev_dbg(isp->dev, "event: 3A stats frame done");
|
dev_dbg(isp->dev, "event: 3A stats frame done");
|
||||||
@ -3909,19 +3852,12 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
|
|||||||
atomisp_buf_done(asd, 0,
|
atomisp_buf_done(asd, 0,
|
||||||
IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME,
|
IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME,
|
||||||
current_event.pipe, true, stream_id);
|
current_event.pipe, true, stream_id);
|
||||||
|
|
||||||
if (!IS_ISP2401)
|
|
||||||
reset_wdt_timer[asd->index] = true; /* ISP running */
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case IA_CSS_EVENT_TYPE_SECOND_VF_OUTPUT_FRAME_DONE:
|
case IA_CSS_EVENT_TYPE_SECOND_VF_OUTPUT_FRAME_DONE:
|
||||||
dev_dbg(isp->dev, "event: second VF output frame done");
|
dev_dbg(isp->dev, "event: second VF output frame done");
|
||||||
atomisp_buf_done(asd, 0,
|
atomisp_buf_done(asd, 0,
|
||||||
IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME,
|
IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME,
|
||||||
current_event.pipe, true, stream_id);
|
current_event.pipe, true, stream_id);
|
||||||
if (!IS_ISP2401)
|
|
||||||
reset_wdt_timer[asd->index] = true; /* ISP running */
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case IA_CSS_EVENT_TYPE_DIS_STATISTICS_DONE:
|
case IA_CSS_EVENT_TYPE_DIS_STATISTICS_DONE:
|
||||||
dev_dbg(isp->dev, "event: dis stats frame done");
|
dev_dbg(isp->dev, "event: dis stats frame done");
|
||||||
@ -3944,24 +3880,6 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_ISP2401)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* ISP2400: If there are no buffers queued then delete wdt timer. */
|
|
||||||
for (i = 0; i < isp->num_of_streams; i++) {
|
|
||||||
asd = &isp->asd[i];
|
|
||||||
if (!asd)
|
|
||||||
continue;
|
|
||||||
if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED)
|
|
||||||
continue;
|
|
||||||
if (!atomisp_buffers_queued(asd))
|
|
||||||
atomisp_wdt_stop(asd, false);
|
|
||||||
else if (reset_wdt_timer[i])
|
|
||||||
/* SOF irq should not reset wdt timer. */
|
|
||||||
atomisp_wdt_refresh(asd,
|
|
||||||
ATOMISP_WDT_KEEP_CURRENT_DELAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,229 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
/*
|
|
||||||
* Support for Medifield PNW Camera Imaging ISP subsystem.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010 Intel Corporation. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010 Silicon Hive www.siliconhive.com.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License version
|
|
||||||
* 2 as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <media/v4l2-event.h>
|
|
||||||
#include <media/v4l2-mediabus.h>
|
|
||||||
|
|
||||||
#include <media/videobuf-vmalloc.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
|
|
||||||
#include "ia_css.h"
|
|
||||||
|
|
||||||
#include "atomisp_cmd.h"
|
|
||||||
#include "atomisp_common.h"
|
|
||||||
#include "atomisp_file.h"
|
|
||||||
#include "atomisp_internal.h"
|
|
||||||
#include "atomisp_ioctl.h"
|
|
||||||
|
|
||||||
static void file_work(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct atomisp_file_device *file_dev =
|
|
||||||
container_of(work, struct atomisp_file_device, work);
|
|
||||||
struct atomisp_device *isp = file_dev->isp;
|
|
||||||
/* only support file injection on subdev0 */
|
|
||||||
struct atomisp_sub_device *asd = &isp->asd[0];
|
|
||||||
struct atomisp_video_pipe *out_pipe = &asd->video_in;
|
|
||||||
unsigned short *buf = videobuf_to_vmalloc(out_pipe->outq.bufs[0]);
|
|
||||||
struct v4l2_mbus_framefmt isp_sink_fmt;
|
|
||||||
|
|
||||||
if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dev_dbg(isp->dev, ">%s: ready to start streaming\n", __func__);
|
|
||||||
isp_sink_fmt = *atomisp_subdev_get_ffmt(&asd->subdev, NULL,
|
|
||||||
V4L2_SUBDEV_FORMAT_ACTIVE,
|
|
||||||
ATOMISP_SUBDEV_PAD_SINK);
|
|
||||||
|
|
||||||
while (!ia_css_isp_has_started())
|
|
||||||
usleep_range(1000, 1500);
|
|
||||||
|
|
||||||
ia_css_stream_send_input_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
|
|
||||||
buf, isp_sink_fmt.width,
|
|
||||||
isp_sink_fmt.height);
|
|
||||||
dev_dbg(isp->dev, "<%s: streaming done\n", __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int file_input_s_stream(struct v4l2_subdev *sd, int enable)
|
|
||||||
{
|
|
||||||
struct atomisp_file_device *file_dev = v4l2_get_subdevdata(sd);
|
|
||||||
struct atomisp_device *isp = file_dev->isp;
|
|
||||||
/* only support file injection on subdev0 */
|
|
||||||
struct atomisp_sub_device *asd = &isp->asd[0];
|
|
||||||
|
|
||||||
dev_dbg(isp->dev, "%s: enable %d\n", __func__, enable);
|
|
||||||
if (enable) {
|
|
||||||
if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
queue_work(file_dev->work_queue, &file_dev->work);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
cancel_work_sync(&file_dev->work);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int file_input_get_fmt(struct v4l2_subdev *sd,
|
|
||||||
struct v4l2_subdev_state *sd_state,
|
|
||||||
struct v4l2_subdev_format *format)
|
|
||||||
{
|
|
||||||
struct v4l2_mbus_framefmt *fmt = &format->format;
|
|
||||||
struct atomisp_file_device *file_dev = v4l2_get_subdevdata(sd);
|
|
||||||
struct atomisp_device *isp = file_dev->isp;
|
|
||||||
/* only support file injection on subdev0 */
|
|
||||||
struct atomisp_sub_device *asd = &isp->asd[0];
|
|
||||||
struct v4l2_mbus_framefmt *isp_sink_fmt;
|
|
||||||
|
|
||||||
if (format->pad)
|
|
||||||
return -EINVAL;
|
|
||||||
isp_sink_fmt = atomisp_subdev_get_ffmt(&asd->subdev, NULL,
|
|
||||||
V4L2_SUBDEV_FORMAT_ACTIVE,
|
|
||||||
ATOMISP_SUBDEV_PAD_SINK);
|
|
||||||
|
|
||||||
fmt->width = isp_sink_fmt->width;
|
|
||||||
fmt->height = isp_sink_fmt->height;
|
|
||||||
fmt->code = isp_sink_fmt->code;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int file_input_set_fmt(struct v4l2_subdev *sd,
|
|
||||||
struct v4l2_subdev_state *sd_state,
|
|
||||||
struct v4l2_subdev_format *format)
|
|
||||||
{
|
|
||||||
struct v4l2_mbus_framefmt *fmt = &format->format;
|
|
||||||
|
|
||||||
if (format->pad)
|
|
||||||
return -EINVAL;
|
|
||||||
file_input_get_fmt(sd, sd_state, format);
|
|
||||||
if (format->which == V4L2_SUBDEV_FORMAT_TRY)
|
|
||||||
sd_state->pads->try_fmt = *fmt;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int file_input_log_status(struct v4l2_subdev *sd)
|
|
||||||
{
|
|
||||||
/*to fake*/
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int file_input_s_power(struct v4l2_subdev *sd, int on)
|
|
||||||
{
|
|
||||||
/* to fake */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int file_input_enum_mbus_code(struct v4l2_subdev *sd,
|
|
||||||
struct v4l2_subdev_state *sd_state,
|
|
||||||
struct v4l2_subdev_mbus_code_enum *code)
|
|
||||||
{
|
|
||||||
/*to fake*/
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int file_input_enum_frame_size(struct v4l2_subdev *sd,
|
|
||||||
struct v4l2_subdev_state *sd_state,
|
|
||||||
struct v4l2_subdev_frame_size_enum *fse)
|
|
||||||
{
|
|
||||||
/*to fake*/
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int file_input_enum_frame_ival(struct v4l2_subdev *sd,
|
|
||||||
struct v4l2_subdev_state *sd_state,
|
|
||||||
struct v4l2_subdev_frame_interval_enum
|
|
||||||
*fie)
|
|
||||||
{
|
|
||||||
/*to fake*/
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops file_input_video_ops = {
|
|
||||||
.s_stream = file_input_s_stream,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct v4l2_subdev_core_ops file_input_core_ops = {
|
|
||||||
.log_status = file_input_log_status,
|
|
||||||
.s_power = file_input_s_power,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct v4l2_subdev_pad_ops file_input_pad_ops = {
|
|
||||||
.enum_mbus_code = file_input_enum_mbus_code,
|
|
||||||
.enum_frame_size = file_input_enum_frame_size,
|
|
||||||
.enum_frame_interval = file_input_enum_frame_ival,
|
|
||||||
.get_fmt = file_input_get_fmt,
|
|
||||||
.set_fmt = file_input_set_fmt,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct v4l2_subdev_ops file_input_ops = {
|
|
||||||
.core = &file_input_core_ops,
|
|
||||||
.video = &file_input_video_ops,
|
|
||||||
.pad = &file_input_pad_ops,
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
atomisp_file_input_unregister_entities(struct atomisp_file_device *file_dev)
|
|
||||||
{
|
|
||||||
media_entity_cleanup(&file_dev->sd.entity);
|
|
||||||
v4l2_device_unregister_subdev(&file_dev->sd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int atomisp_file_input_register_entities(struct atomisp_file_device *file_dev,
|
|
||||||
struct v4l2_device *vdev)
|
|
||||||
{
|
|
||||||
/* Register the subdev and video nodes. */
|
|
||||||
return v4l2_device_register_subdev(vdev, &file_dev->sd);
|
|
||||||
}
|
|
||||||
|
|
||||||
void atomisp_file_input_cleanup(struct atomisp_device *isp)
|
|
||||||
{
|
|
||||||
struct atomisp_file_device *file_dev = &isp->file_dev;
|
|
||||||
|
|
||||||
if (file_dev->work_queue) {
|
|
||||||
destroy_workqueue(file_dev->work_queue);
|
|
||||||
file_dev->work_queue = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int atomisp_file_input_init(struct atomisp_device *isp)
|
|
||||||
{
|
|
||||||
struct atomisp_file_device *file_dev = &isp->file_dev;
|
|
||||||
struct v4l2_subdev *sd = &file_dev->sd;
|
|
||||||
struct media_pad *pads = file_dev->pads;
|
|
||||||
struct media_entity *me = &sd->entity;
|
|
||||||
|
|
||||||
file_dev->isp = isp;
|
|
||||||
file_dev->work_queue = alloc_workqueue(isp->v4l2_dev.name, 0, 1);
|
|
||||||
if (!file_dev->work_queue) {
|
|
||||||
dev_err(isp->dev, "Failed to initialize file inject workq\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
INIT_WORK(&file_dev->work, file_work);
|
|
||||||
|
|
||||||
v4l2_subdev_init(sd, &file_input_ops);
|
|
||||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
||||||
strscpy(sd->name, "file_input_subdev", sizeof(sd->name));
|
|
||||||
v4l2_set_subdevdata(sd, file_dev);
|
|
||||||
|
|
||||||
pads[0].flags = MEDIA_PAD_FL_SINK;
|
|
||||||
me->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
|
|
||||||
|
|
||||||
return media_entity_pads_init(me, 1, pads);
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
|
||||||
/*
|
|
||||||
* Support for Medifield PNW Camera Imaging ISP subsystem.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010 Intel Corporation. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010 Silicon Hive www.siliconhive.com.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License version
|
|
||||||
* 2 as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __ATOMISP_FILE_H__
|
|
||||||
#define __ATOMISP_FILE_H__
|
|
||||||
|
|
||||||
#include <media/media-entity.h>
|
|
||||||
#include <media/v4l2-subdev.h>
|
|
||||||
|
|
||||||
struct atomisp_device;
|
|
||||||
|
|
||||||
struct atomisp_file_device {
|
|
||||||
struct v4l2_subdev sd;
|
|
||||||
struct atomisp_device *isp;
|
|
||||||
struct media_pad pads[1];
|
|
||||||
|
|
||||||
struct workqueue_struct *work_queue;
|
|
||||||
struct work_struct work;
|
|
||||||
};
|
|
||||||
|
|
||||||
void atomisp_file_input_cleanup(struct atomisp_device *isp);
|
|
||||||
int atomisp_file_input_init(struct atomisp_device *isp);
|
|
||||||
void atomisp_file_input_unregister_entities(
|
|
||||||
struct atomisp_file_device *file_dev);
|
|
||||||
int atomisp_file_input_register_entities(struct atomisp_file_device *file_dev,
|
|
||||||
struct v4l2_device *vdev);
|
|
||||||
#endif /* __ATOMISP_FILE_H__ */
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user