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:
Linus Torvalds 2022-10-22 15:30:15 -07:00
commit 3272eb1ace
132 changed files with 3215 additions and 3895 deletions

View File

@ -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

View File

@ -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 = <&reg_csi_1v8>;
};
};

View File

@ -214,18 +214,29 @@ Link properties can be modified at runtime by calling
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
prevent link states from being modified during streaming by calling
:c:func:`media_pipeline_start()`.
The function will mark all entities connected to the given entity through
enabled links, either directly or indirectly, as streaming.
The function will mark all the pads which are part of the pipeline as streaming.
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
in higher-level pipeline structures and can then access the
pipeline through the struct media_entity
pipeline through the struct media_pad
pipe field.
Calls to :c:func:`media_pipeline_start()` can be nested.

View File

@ -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_SINK_HAS_ARC_TX
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
@ -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_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_CXT

View File

@ -136,9 +136,9 @@ V4L2 functions
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);

View File

@ -6284,7 +6284,7 @@ M: Sakari Ailus <sakari.ailus@linux.intel.com>
L: linux-media@vger.kernel.org
S: Maintained
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
DONGWOON DW9768 LENS VOICE COIL DRIVER
@ -18141,7 +18141,6 @@ L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
F: drivers/staging/media/deprecated/saa7146/
F: include/media/drv-intf/saa7146*
SAFESETID SECURITY MODULE
M: Micah Morton <mortonm@chromium.org>
@ -22771,7 +22770,7 @@ S: Maintained
W: http://mjpeg.sourceforge.net/driver-zoran/
Q: https://patchwork.linuxtv.org/project/linux-media/list/
F: Documentation/driver-api/media/drivers/zoran.rst
F: drivers/staging/media/zoran/
F: drivers/media/pci/zoran/
ZRAM COMPRESSED RAM BLOCK DEVICE DRVIER
M: Minchan Kim <minchan@kernel.org>

View File

@ -24,7 +24,7 @@ if MEDIA_SUPPORT
config MEDIA_SUPPORT_FILTER
bool "Filter media drivers"
default y if !EMBEDDED && !EXPERT
default y if !EXPERT
help
Configuring the media subsystem can be complex, as there are
hundreds of drivers and other config options.

View File

@ -1027,6 +1027,7 @@ static const u8 cec_msg_size[256] = {
[CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR] = 2 | DIRECTED,
[CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR] = 2 | DIRECTED,
[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_STATUS] = 3 | DIRECTED,
[CEC_MSG_SET_AUDIO_RATE] = 3 | DIRECTED,

View File

@ -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;
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;
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 Kinox */
{ "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,

View File

@ -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");
cec->rx = STATE_BUSY;
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;
s5p_cec_get_rx_buf(cec, cec->msg.len,
cec->msg.msg);

View File

@ -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)
{
struct drxk_state *state = fe->demodulator_priv;
u16 err;
u16 err = 0;
dprintk(1, "\n");

View File

@ -406,7 +406,6 @@ static int ar0521_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_format *format)
{
struct ar0521_dev *sensor = to_ar0521_dev(sd);
int ret = 0;
ar0521_adj_fmt(&format->format);
@ -423,7 +422,7 @@ static int ar0521_set_fmt(struct v4l2_subdev *sd,
}
mutex_unlock(&sensor->lock);
return ret;
return 0;
}
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);
usleep_range(4500, 5000); /* min 45000 clocks */
for (cnt = 0; cnt < ARRAY_SIZE(initial_regs); cnt++)
if (ar0521_write_regs(sensor, initial_regs[cnt].data,
initial_regs[cnt].count))
for (cnt = 0; cnt < ARRAY_SIZE(initial_regs); cnt++) {
ret = ar0521_write_regs(sensor, initial_regs[cnt].data,
initial_regs[cnt].count);
if (ret)
goto off;
}
ret = ar0521_write_reg(sensor, AR0521_REG_SERIAL_FORMAT,
AR0521_REG_SERIAL_FORMAT_MIPI |

View File

@ -238,6 +238,43 @@ static int get_key_knc1(struct IR_i2c *ir, enum rc_proto *protocol,
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,
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;
ir_codes = RC_MAP_EMPTY;
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:
name = "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:
ir->get_key = get_key_knc1;
break;
case IR_KBD_GET_KEY_GENIATECH:
ir->get_key = get_key_geniatech;
break;
case IR_KBD_GET_KEY_FUSIONHDTV:
ir->get_key = get_key_fusionhdtv;
break;

View File

@ -8,7 +8,7 @@
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_graph.h>

View File

@ -633,7 +633,7 @@ static int mt9v111_hw_config(struct mt9v111_dev *mt9v111)
/*
* 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
* frame rate.
*/

View File

@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/types.h>
@ -447,8 +448,6 @@ struct ov5640_dev {
/* lock to protect all members below */
struct mutex lock;
int power_count;
struct v4l2_mbus_framefmt fmt;
bool pending_fmt_change;
@ -2696,38 +2695,23 @@ power_off:
return ret;
}
/* --------------- Subdev Operations --------------- */
static int ov5640_s_power(struct v4l2_subdev *sd, int on)
static int ov5640_sensor_suspend(struct device *dev)
{
struct ov5640_dev *sensor = to_ov5640_dev(sd);
int ret = 0;
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov5640_dev *ov5640 = to_ov5640_dev(sd);
mutex_lock(&sensor->lock);
/*
* 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;
return ov5640_set_power(ov5640, false);
}
/* Update the power count. */
sensor->power_count += on ? 1 : -1;
WARN_ON(sensor->power_count < 0);
out:
mutex_unlock(&sensor->lock);
static int ov5640_sensor_resume(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov5640_dev *ov5640 = to_ov5640_dev(sd);
if (on && !ret && sensor->power_count == 1) {
/* restore controls */
ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
return ov5640_set_power(ov5640, true);
}
return ret;
}
/* --------------- Subdev Operations --------------- */
static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
struct v4l2_fract *fi,
@ -3314,6 +3298,9 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
/* v4l2_ctrl_lock() locks our own mutex */
if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev))
return 0;
switch (ctrl->id) {
case V4L2_CID_AUTOGAIN:
val = ov5640_get_gain(sensor);
@ -3329,6 +3316,8 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
break;
}
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
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
* 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;
switch (ctrl->id) {
@ -3402,6 +3391,8 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
break;
}
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
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);
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);
if (sensor->streaming == !enable) {
@ -3701,8 +3704,13 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
if (!ret)
sensor->streaming = enable;
}
out:
mutex_unlock(&sensor->lock);
if (!enable || ret)
pm_runtime_put_autosuspend(&sensor->i2c_client->dev);
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 = {
.s_power = ov5640_s_power,
.log_status = v4l2_ctrl_subdev_log_status,
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
@ -3770,26 +3777,20 @@ static int ov5640_check_chip_id(struct ov5640_dev *sensor)
int ret = 0;
u16 chip_id;
ret = ov5640_set_power_on(sensor);
if (ret)
return ret;
ret = ov5640_read_reg16(sensor, OV5640_REG_CHIP_ID, &chip_id);
if (ret) {
dev_err(&client->dev, "%s: failed to read chip identifier\n",
__func__);
goto power_off;
return ret;
}
if (chip_id != 0x5640) {
dev_err(&client->dev, "%s: wrong chip identifier, expected 0x5640, got 0x%x\n",
__func__, chip_id);
ret = -ENXIO;
return -ENXIO;
}
power_off:
ov5640_set_power_off(sensor);
return ret;
return 0;
}
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);
if (ret)
return ret;
goto entity_cleanup;
mutex_init(&sensor->lock);
ret = ov5640_check_chip_id(sensor);
if (ret)
goto entity_cleanup;
ret = ov5640_init_controls(sensor);
if (ret)
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);
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;
free_ctrls:
err_pm_runtime:
pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
v4l2_ctrl_handler_free(&sensor->ctrls.handler);
ov5640_sensor_suspend(dev);
entity_cleanup:
media_entity_cleanup(&sensor->sd.entity);
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 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);
media_entity_cleanup(&sensor->sd.entity);
@ -3917,6 +3941,10 @@ static void ov5640_remove(struct i2c_client *client)
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[] = {
{"ov5640", 0},
{},
@ -3933,6 +3961,7 @@ static struct i2c_driver ov5640_i2c_driver = {
.driver = {
.name = "ov5640",
.of_match_table = ov5640_dt_ids,
.pm = &ov5640_pm_ops,
},
.id_table = ov5640_id,
.probe_new = ov5640_probe,

View File

@ -3034,11 +3034,13 @@ static int ov8865_probe(struct i2c_client *client)
&rate);
if (!ret && sensor->extclk) {
ret = clk_set_rate(sensor->extclk, rate);
if (ret)
return dev_err_probe(dev, ret,
"failed to set clock rate\n");
if (ret) {
dev_err_probe(dev, ret, "failed to set clock rate\n");
goto error_endpoint;
}
} 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);

View File

@ -581,7 +581,7 @@ static void __media_device_unregister_entity(struct media_entity *entity)
struct media_device *mdev = entity->graph_obj.mdev;
struct media_link *link, *tmp;
struct media_interface *intf;
unsigned int i;
struct media_pad *iter;
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);
/* Remove all pads that belong to this entity */
for (i = 0; i < entity->num_pads; i++)
media_gobj_destroy(&entity->pads[i].graph_obj);
media_entity_for_each_pad(entity, iter)
media_gobj_destroy(&iter->graph_obj);
/* Remove the entity */
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_notify *notify, *next;
unsigned int i;
struct media_pad *iter;
int ret;
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);
/* Initialize objects at the pads */
for (i = 0; i < entity->num_pads; i++)
media_gobj_create(mdev, MEDIA_GRAPH_PAD,
&entity->pads[i].graph_obj);
media_entity_for_each_pad(entity, iter)
media_gobj_create(mdev, MEDIA_GRAPH_PAD, &iter->graph_obj);
/* invoke entity_notify callbacks */
list_for_each_entry_safe(notify, next, &mdev->entity_notify, list)

View File

@ -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,
int idx_max)
__must_check int media_entity_enum_init(struct media_entity_enum *ent_enum,
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);
if (!ent_enum->bmap)
return -ENOMEM;
@ -71,7 +73,7 @@ __must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum,
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)
{
@ -193,7 +195,8 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
struct media_pad *pads)
{
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)
return -E2BIG;
@ -204,12 +207,12 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
if (mdev)
mutex_lock(&mdev->graph_mutex);
for (i = 0; i < num_pads; i++) {
pads[i].entity = entity;
pads[i].index = i;
media_entity_for_each_pad(entity, iter) {
iter->entity = entity;
iter->index = i++;
if (mdev)
media_gobj_create(mdev, MEDIA_GRAPH_PAD,
&entity->pads[i].graph_obj);
&iter->graph_obj);
}
if (mdev)
@ -223,6 +226,33 @@ EXPORT_SYMBOL_GPL(media_entity_pads_init);
* 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 *
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);
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
*/
__must_check int __media_pipeline_start(struct media_entity *entity,
struct media_pipeline *pipe)
{
struct media_device *mdev = entity->graph_obj.mdev;
struct media_graph *graph = &pipe->graph;
struct media_entity *entity_err = entity;
struct media_link *link;
int ret;
/*
* 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)
{
return &walk->stack.entries[walk->stack.top];
}
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;
if (pipe->streaming_count) {
pipe->streaming_count++;
return 0;
}
ret = media_graph_walk_init(&pipe->graph, mdev);
/* 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;
if (walk->stack.top + 1 >= walk->stack.size) {
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;
}
/* 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)
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))) {
DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS);
DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS);
return 0;
}
if (entity->pipe && entity->pipe != pipe) {
pr_err("Pipe active for %s. Can't start for %s\n",
entity->name,
entity_err->name);
static void media_pipeline_cleanup(struct media_pipeline *pipe)
{
while (!list_empty(&pipe->pads)) {
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;
goto error;
}
/* Already streaming --- no need to check. */
if (entity->pipe)
/*
* 2. Validate all active links whose sink is the current pad.
* Validation of the source pads is performed in the context of
* the connected sink pad to avoid duplicating checks.
*/
for_each_media_entity_data_link(entity, link) {
/* Skip links unrelated to the current pad. */
if (link->sink != pad && link->source != pad)
continue;
entity->pipe = pipe;
/* Record if the pad has links and enabled links. */
if (link->flags & MEDIA_LNK_FL_ENABLED)
has_enabled_link = true;
has_link = true;
/*
* Validate the link if it's enabled and has the
* current pad as its sink.
*/
if (!(link->flags & MEDIA_LNK_FL_ENABLED))
continue;
if (link->sink != pad)
continue;
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) {
struct media_pad *pad = link->sink->entity == entity
? link->sink : link->source;
/* Mark that a pad is connected by a link. */
bitmap_clear(has_no_links, pad->index, 1);
/*
* Pads that either do not need to connect or
* are connected through an enabled link are
* fine.
*/
if (!(pad->flags & MEDIA_PAD_FL_MUST_CONNECT) ||
link->flags & MEDIA_LNK_FL_ENABLED)
bitmap_set(active, pad->index, 1);
/*
* Link validation will only take place for
* sink ends of the link that are enabled.
*/
if (link->sink != pad ||
!(link->flags & MEDIA_LNK_FL_ENABLED))
continue;
ret = entity->ops->link_validate(link);
if (ret < 0 && ret != -ENOIOCTLCMD) {
dev_dbg(entity->graph_obj.mdev->dev,
"link validation failed for '%s':%u -> '%s':%u, error %d\n",
if (ret) {
dev_dbg(mdev->dev,
"Link '%s':%u -> '%s':%u failed validation: %d\n",
link->source->entity->name,
link->source->index,
entity->name, link->sink->index, ret);
link->sink->entity->name,
link->sink->index, ret);
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);
if (!bitmap_full(active, 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 ((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;
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;
}
/* Validation passed, store the pipe pointer in the pad. */
pad->pipe = pipe;
}
pipe->streaming_count++;
pipe->start_count++;
return 0;
@ -508,42 +834,37 @@ error:
* Link validation on graph failed. We revert what we did and
* return the error.
*/
media_graph_walk_start(graph, entity_err);
while ((entity_err = media_graph_walk_next(graph))) {
entity_err->pipe = NULL;
/*
* We haven't started entities further than this so we quit
* here.
*/
if (entity_err == entity)
list_for_each_entry(err_ppad, &pipe->pads, list) {
if (err_ppad == ppad)
break;
err_ppad->pad->pipe = NULL;
}
media_graph_walk_cleanup(graph);
media_pipeline_cleanup(pipe);
return ret;
}
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_device *mdev = entity->graph_obj.mdev;
struct media_device *mdev = pad->entity->graph_obj.mdev;
int ret;
mutex_lock(&mdev->graph_mutex);
ret = __media_pipeline_start(entity, pipe);
ret = __media_pipeline_start(pad, pipe);
mutex_unlock(&mdev->graph_mutex);
return ret;
}
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 = entity->pipe;
struct media_pipeline *pipe = pad->pipe;
struct media_pipeline_pad *ppad;
/*
* 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))
return;
if (--pipe->streaming_count)
if (--pipe->start_count)
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)))
entity->pipe = NULL;
media_graph_walk_cleanup(graph);
media_pipeline_cleanup(pipe);
if (pipe->allocated)
kfree(pipe);
}
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);
__media_pipeline_stop(entity);
__media_pipeline_stop(pad);
mutex_unlock(&mdev->graph_mutex);
}
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
*/
@ -829,7 +1186,7 @@ int __media_entity_setup_link(struct media_link *link, u32 flags)
{
const u32 mask = MEDIA_LNK_FL_ENABLED;
struct media_device *mdev;
struct media_entity *source, *sink;
struct media_pad *source, *sink;
int ret = -EBUSY;
if (link == NULL)
@ -845,12 +1202,11 @@ int __media_entity_setup_link(struct media_link *link, u32 flags)
if (link->flags == flags)
return 0;
source = link->source->entity;
sink = link->sink->entity;
source = link->source;
sink = link->sink;
if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) &&
(media_entity_is_streaming(source) ||
media_entity_is_streaming(sink)))
(media_pad_is_streaming(source) || media_pad_is_streaming(sink)))
return -EBUSY;
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);
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,
struct media_interface *intf,
u32 gobj_type,

View File

@ -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
* 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
* the end of active video to start a horizontal line, so that
* 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
* 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
* end of active video, leaving 122 pixels of hblank to ignore
* before active video starts.

View File

@ -586,7 +586,7 @@ void cx88_i2c_init_ir(struct cx88_core *core)
{
struct i2c_board_info info;
static const unsigned short default_addr_list[] = {
0x18, 0x6b, 0x71,
0x18, 0x33, 0x6b, 0x71,
I2C_CLIENT_END
};
static const unsigned short pvr2000_addr_list[] = {

View File

@ -1388,6 +1388,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
}
fallthrough;
case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
case CX88_BOARD_NOTONLYTV_LV3H:
request_module("ir-kbd-i2c");
}

View File

@ -989,7 +989,7 @@ static int cio2_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
return r;
}
r = media_pipeline_start(&q->vdev.entity, &q->pipe);
r = video_device_pipeline_start(&q->vdev, &q->pipe);
if (r)
goto fail_pipeline;
@ -1009,7 +1009,7 @@ static int cio2_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
fail_csi2_subdev:
cio2_hw_exit(cio2, q);
fail_hw:
media_pipeline_stop(&q->vdev.entity);
video_device_pipeline_stop(&q->vdev);
fail_pipeline:
dev_dbg(dev, "failed to start streaming (%d)\n", r);
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);
synchronize_irq(cio2->pci_dev->irq);
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);
cio2->streaming = false;
}

View File

@ -603,6 +603,10 @@ static int vpu_v4l2_release(struct vpu_inst *inst)
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);
mutex_destroy(&inst->lock);
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_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);
vpu_inst_unregister(inst);
vpu_inst_put(inst);

View File

@ -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);
}
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_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->chroma_ac, 162);
coda_write(dev, 0x000, CODA9_REG_JPEG_HUFF_CTRL);
return 0;
}
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,
CODA9_REG_JPEG_RST_INTVAL);
if (ctx->params.jpeg_huff_tab) {
ret = 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;
}
}
if (ctx->params.jpeg_huff_tab)
coda9_jpeg_dec_huff_setup(ctx);
coda9_jpeg_qmat_setup(ctx);

View File

@ -457,7 +457,7 @@ err_cmdq_data:
kfree(path);
atomic_dec(&mdp->job_count);
wake_up(&mdp->callback_wq);
if (cmd->pkt.buf_size > 0)
if (cmd && cmd->pkt.buf_size > 0)
mdp_cmdq_pkt_destroy(&cmd->pkt);
kfree(comps);
kfree(cmd);

View File

@ -682,7 +682,7 @@ int mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp)
int i, ret;
if (comp->comp_dev) {
ret = pm_runtime_get_sync(comp->comp_dev);
ret = pm_runtime_resume_and_get(comp->comp_dev);
if (ret < 0) {
dev_err(dev,
"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,
"Failed to enable clk %d. type:%d id:%d\n",
i, comp->type, comp->id);
pm_runtime_put(comp->comp_dev);
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);
if (ret) {
kfree(comp);
devm_kfree(dev, comp);
return ERR_PTR(ret);
}
mdp->comp[id] = comp;
@ -930,7 +931,7 @@ void mdp_comp_destroy(struct mdp_dev *mdp)
if (mdp->comp[i]) {
pm_runtime_disable(mdp->comp[i]->comp_dev);
mdp_comp_deinit(mdp->comp[i]);
kfree(mdp->comp[i]);
devm_kfree(mdp->comp[i]->comp_dev, mdp->comp[i]);
mdp->comp[i] = NULL;
}
}

View File

@ -289,6 +289,7 @@ err_deinit_comp:
mdp_comp_destroy(mdp);
err_return:
for (i = 0; i < MDP_PIPE_MAX; i++)
if (mdp)
mtk_mutex_put(mdp->mdp_mutex[i]);
kfree(mdp);
dev_dbg(dev, "Errno %d\n", ret);

View File

@ -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 */
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!");
goto err_mem_alloc;
}

View File

@ -373,7 +373,7 @@ static const struct v4l2_ctrl_ops dw100_ctrl_ops = {
* The coordinates are saved in UQ12.4 fixed point format.
*/
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)
{
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_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);
qx = min_t(u32, (idx % mw) * qdx, qsw);
map[idx] = dw100_map_format_coordinates(qx, qy);

View File

@ -493,7 +493,7 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
struct v4l2_subdev *subdev;
int ret;
ret = media_pipeline_start(&vdev->entity, &video->pipe);
ret = video_device_pipeline_start(vdev, &video->pipe);
if (ret < 0)
return ret;
@ -522,7 +522,7 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
return 0;
error:
media_pipeline_stop(&vdev->entity);
video_device_pipeline_stop(vdev);
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);
}
media_pipeline_stop(&vdev->entity);
video_device_pipeline_stop(vdev);
video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
}

View File

@ -1800,7 +1800,7 @@ bool venus_helper_check_format(struct venus_inst *inst, u32 v4l2_pixfmt)
struct venus_core *core = inst->core;
u32 fmt = to_hfi_raw_fmt(v4l2_pixfmt);
struct hfi_plat_caps *caps;
u32 buftype;
bool found;
if (!fmt)
return false;
@ -1809,12 +1809,13 @@ bool venus_helper_check_format(struct venus_inst *inst, u32 v4l2_pixfmt)
if (!caps)
return false;
if (inst->session_type == VIDC_SESSION_TYPE_DEC)
buftype = HFI_BUFFER_OUTPUT2;
else
buftype = HFI_BUFFER_OUTPUT;
found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, fmt);
if (found)
goto done;
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);

View File

@ -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 ret;
if (!ops)
return -EINVAL;
@ -579,9 +577,8 @@ int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops)
core->state = CORE_UNINIT;
init_completion(&core->done);
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)

View File

@ -183,6 +183,8 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
else
return NULL;
fmt = find_format(inst, pixmp->pixelformat, f->type);
if (!fmt)
return NULL;
}
pixmp->width = clamp(pixmp->width, frame_width_min(inst),

View File

@ -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),
frame_height_max(inst));
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
pixmp->width = ALIGN(pixmp->width, 128);
pixmp->height = ALIGN(pixmp->height, 32);
}
pixmp->width = ALIGN(pixmp->width, 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;
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)
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);
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)
return -EINVAL;
@ -509,6 +507,19 @@ static int venc_enum_frameintervals(struct file *file, void *fh,
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 = {
.vidioc_querycap = venc_querycap,
.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_enum_framesizes = venc_enum_framesizes,
.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_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
};
static int venc_pm_get(struct venus_inst *inst)
@ -686,7 +698,8 @@ static int venc_set_properties(struct venus_inst *inst)
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;
unsigned int c;

View File

@ -8,6 +8,7 @@
#include "core.h"
#include "venc.h"
#include "helpers.h"
#define BITRATE_MIN 32000
#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 (ctrl->val == 0)
return -EINVAL;
ctr->h264_8x8_transform = ctrl->val;
break;
@ -348,15 +347,41 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
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 = {
.s_ctrl = venc_op_s_ctrl,
.g_volatile_ctrl = venc_op_g_volatile_ctrl,
};
int venc_ctrl_init(struct venus_inst *inst)
{
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)
return ret;
@ -436,6 +461,9 @@ int venc_ctrl_init(struct venus_inst *inst)
V4L2_MPEG_VIDEO_VP8_PROFILE_3,
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_CID_MPEG_VIDEO_BITRATE, BITRATE_MIN, BITRATE_MAX,
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_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_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_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,

View File

@ -786,9 +786,8 @@ static int rvin_csi2_link_notify(struct media_link *link, u32 flags,
return 0;
/*
* Don't allow link changes if any entity in the graph is
* streaming, modifying the CHSEL register fields can disrupt
* running streams.
* Don't allow link changes if any stream in the graph is active as
* modifying the CHSEL register fields can disrupt running streams.
*/
media_device_for_each_entity(entity, &group->mdev)
if (media_entity_is_streaming(entity))

View File

@ -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)
{
struct media_pipeline *pipe;
struct media_device *mdev;
struct v4l2_subdev *sd;
struct media_pad *pad;
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);
if (!on) {
media_pipeline_stop(&vin->vdev.entity);
video_device_pipeline_stop(&vin->vdev);
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)
return ret;
/*
* 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);
ret = video_device_pipeline_alloc_start(&vin->vdev);
if (ret)
return ret;
@ -1291,7 +1279,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on)
if (ret == -ENOIOCTLCMD)
ret = 0;
if (ret)
media_pipeline_stop(&vin->vdev.entity);
video_device_pipeline_stop(&vin->vdev);
return ret;
}

View File

@ -927,7 +927,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
}
mutex_unlock(&pipe->lock);
media_pipeline_stop(&video->video.entity);
video_device_pipeline_stop(&video->video);
vsp1_video_release_buffers(video);
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);
}
ret = __media_pipeline_start(&video->video.entity, &pipe->pipe);
ret = __video_device_pipeline_start(&video->video, &pipe->pipe);
if (ret < 0) {
mutex_unlock(&mdev->graph_mutex);
goto err_pipe;
@ -1070,7 +1070,7 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
return 0;
err_stop:
media_pipeline_stop(&video->video.entity);
video_device_pipeline_stop(&video->video);
err_pipe:
vsp1_video_pipeline_put(pipe);
return ret;

View File

@ -913,7 +913,7 @@ static void rkisp1_cap_stream_disable(struct rkisp1_capture *cap)
*
* Call s_stream(false) in the reverse order from
* 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)
__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
* 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->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
*
* 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)
__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
* enabled, skip them.
*/
if (rkisp1->pipe.streaming_count > 1)
if (rkisp1->pipe.start_count > 1)
return 0;
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);
media_pipeline_stop(&node->vdev.entity);
video_device_pipeline_stop(&node->vdev);
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);
ret = media_pipeline_start(entity, &cap->rkisp1->pipe);
ret = video_device_pipeline_start(&cap->vnode.vdev, &cap->rkisp1->pipe);
if (ret) {
dev_err(cap->rkisp1->dev, "start pipeline failed %d\n", ret);
goto err_ret_buffers;
@ -1044,7 +1044,7 @@ err_pipe_pm_put:
err_destroy_dummy:
rkisp1_dummy_buf_destroy(cap);
err_pipeline_stop:
media_pipeline_stop(entity);
video_device_pipeline_stop(&cap->vnode.vdev);
err_ret_buffers:
rkisp1_return_all_buffers(cap, VB2_BUF_STATE_QUEUED);
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);
const struct rkisp1_capture_fmt_cfg *fmt =
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;
sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sd_fmt.pad = link->source->index;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt);
if (ret)
return ret;

View File

@ -378,6 +378,7 @@ struct rkisp1_params {
struct v4l2_format vdev_fmt;
enum v4l2_quantization quantization;
enum v4l2_ycbcr_encoding ycbcr_encoding;
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);
/* rkisp1_params_configure - configure the params when stream starts.
* This function is called by the isp entity upon stream starts.
* The function applies the initial configuration of the parameters.
/*
* rkisp1_params_pre_configure - Configure the params before stream start
*
* @params: pointer to rkisp1_params.
* @params: pointer to rkisp1_params
* @bayer_pat: the bayer pattern on the isp video sink 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 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.
* This function is called by the isp entity upon stream start

View File

@ -231,10 +231,11 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp,
struct v4l2_mbus_framefmt *src_frm;
src_frm = rkisp1_isp_get_pad_fmt(isp, NULL,
RKISP1_ISP_PAD_SINK_VIDEO,
RKISP1_ISP_PAD_SOURCE_VIDEO,
V4L2_SUBDEV_FORMAT_ACTIVE);
rkisp1_params_configure(&rkisp1->params, sink_fmt->bayer_pat,
src_frm->quantization);
rkisp1_params_pre_configure(&rkisp1->params, sink_fmt->bayer_pat,
src_frm->quantization,
src_frm->ycbcr_enc);
}
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_INFORM_ENABLE;
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_rect *sink_crop, *src_crop;
/* Video. */
sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
RKISP1_ISP_PAD_SINK_VIDEO);
sink_fmt->width = RKISP1_DEFAULT_WIDTH;
sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
sink_fmt->field = V4L2_FIELD_NONE;
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,
RKISP1_ISP_PAD_SINK_VIDEO);
@ -449,11 +458,16 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd,
RKISP1_ISP_PAD_SOURCE_VIDEO);
*src_fmt = *sink_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,
RKISP1_ISP_PAD_SOURCE_VIDEO);
*src_crop = *sink_crop;
/* Parameters and statistics. */
sink_fmt = v4l2_subdev_get_try_format(sd, sd_state,
RKISP1_ISP_PAD_SINK_PARAMS);
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,
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;
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,
RKISP1_ISP_PAD_SOURCE_VIDEO, which);
src_crop = rkisp1_isp_get_pad_crop(isp, sd_state,
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;
mbus_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) {
src_info = rkisp1_mbus_info_get_by_code(src_fmt->code);
if (!src_info || !(src_info->direction & RKISP1_ISP_SD_SRC)) {
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->height = src_crop->height;
/*
* The CSC API is used to allow userspace to force full
* quantization on YUV formats.
* Copy the color space for the sink pad. When converting from Bayer to
* YUV, default to a limited quantization range.
*/
if (format->flags & V4L2_MBUS_FRAMEFMT_SET_CSC &&
format->quantization == V4L2_QUANTIZATION_FULL_RANGE &&
mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV)
src_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
else if (mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV)
src_fmt->colorspace = sink_fmt->colorspace;
src_fmt->xfer_func = sink_fmt->xfer_func;
src_fmt->ycbcr_enc = sink_fmt->ycbcr_enc;
if (sink_info->pixel_enc == V4L2_PIXEL_ENC_BAYER &&
src_info->pixel_enc == V4L2_PIXEL_ENC_YUV)
src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
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;
/*
* 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,
@ -573,6 +652,7 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp,
const struct rkisp1_mbus_info *mbus_info;
struct v4l2_mbus_framefmt *sink_fmt;
struct v4l2_rect *sink_crop;
bool is_yuv;
sink_fmt = rkisp1_isp_get_pad_fmt(isp, sd_state,
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_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;
/* Propagate to in crop */

View File

@ -18,6 +18,8 @@
#define RKISP1_ISP_PARAMS_REQ_BUFS_MIN 2
#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) \
(RKISP1_CIF_ISP_DPCC_LINE_THRESH_1 + 0x14 * (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;
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_CIF_ISP_DPCC_ENA;
mode |= arg->mode & ~RKISP1_CIF_ISP_DPCC_ENA;
mode &= RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE;
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_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,
arg->methods[0].method);
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_METHODS_SET_2,
arg->methods[1].method);
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_METHODS_SET_3,
arg->methods[2].method);
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_OUTPUT_MODE,
arg->output_mode & RKISP1_CIF_ISP_DPCC_OUTPUT_MODE_MASK);
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_SET_USE,
arg->set_use & RKISP1_CIF_ISP_DPCC_SET_USE_MASK);
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),
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),
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),
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),
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),
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,
arg->rnd_offs);
arg->rnd_offs & RKISP1_CIF_ISP_DPCC_RND_OFFS_MASK);
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 */
@ -188,149 +198,131 @@ static void
rkisp1_lsc_matrix_config_v10(struct rkisp1_params *params,
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 */
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_153;
rkisp1_write(params->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(params->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_R_TABLE_ADDR, sram_addr);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR, sram_addr);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_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) */
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
* DWORDs (2nd value of last DWORD unused)
*/
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],
pconfig->r_data_tbl[i][j + 1]);
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],
pconfig->gr_data_tbl[i][j + 1]);
rkisp1_write(params->rkisp1,
RKISP1_CIF_ISP_LSC_GR_TABLE_DATA, data);
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(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_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);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
r_tbl[j], r_tbl[j + 1]));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
gr_tbl[j], gr_tbl[j + 1]));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
gb_tbl[j], gb_tbl[j + 1]));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(
b_tbl[j], b_tbl[j + 1]));
}
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(params->rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
data);
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(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_V10(pconfig->b_data_tbl[i][j], 0);
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
data);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(r_tbl[j], 0));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(gr_tbl[j], 0));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(gb_tbl[j], 0));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(b_tbl[j], 0));
}
isp_lsc_table_sel = (isp_lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE) ?
RKISP1_CIF_ISP_LSC_TABLE_0 :
RKISP1_CIF_ISP_LSC_TABLE_1;
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL,
isp_lsc_table_sel);
lsc_table_sel = lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE ?
RKISP1_CIF_ISP_LSC_TABLE_0 : RKISP1_CIF_ISP_LSC_TABLE_1;
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL, lsc_table_sel);
}
static void
rkisp1_lsc_matrix_config_v12(struct rkisp1_params *params,
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 */
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_153;
rkisp1_write(params->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(params->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_R_TABLE_ADDR, sram_addr);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR, sram_addr);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_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) */
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
* DWORDs (2nd value of last DWORD unused)
*/
for (j = 0; j < RKISP1_CIF_ISP_LSC_SAMPLES_MAX - 1; j += 2) {
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
pconfig->r_data_tbl[i][j],
pconfig->r_data_tbl[i][j + 1]);
rkisp1_write(params->rkisp1,
RKISP1_CIF_ISP_LSC_R_TABLE_DATA, data);
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
pconfig->gr_data_tbl[i][j],
pconfig->gr_data_tbl[i][j + 1]);
rkisp1_write(params->rkisp1,
RKISP1_CIF_ISP_LSC_GR_TABLE_DATA, data);
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);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
r_tbl[j], r_tbl[j + 1]));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
gr_tbl[j], gr_tbl[j + 1]));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
gb_tbl[j], gb_tbl[j + 1]));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(
b_tbl[j], b_tbl[j + 1]));
}
data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(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_V12(pconfig->gr_data_tbl[i][j], 0);
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
data);
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);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(r_tbl[j], 0));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(gr_tbl[j], 0));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(gb_tbl[j], 0));
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA,
RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(b_tbl[j], 0));
}
isp_lsc_table_sel = (isp_lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE) ?
RKISP1_CIF_ISP_LSC_TABLE_0 :
RKISP1_CIF_ISP_LSC_TABLE_1;
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL,
isp_lsc_table_sel);
lsc_table_sel = lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE ?
RKISP1_CIF_ISP_LSC_TABLE_0 : RKISP1_CIF_ISP_LSC_TABLE_1;
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL, lsc_table_sel);
}
static void rkisp1_lsc_config(struct rkisp1_params *params,
const struct rkisp1_cif_isp_lsc_config *arg)
{
unsigned int i, data;
u32 lsc_ctrl;
struct rkisp1_device *rkisp1 = params->rkisp1;
u32 lsc_ctrl, data;
unsigned int i;
/* 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_CIF_ISP_LSC_CTRL_ENA);
params->ops->lsc_matrix_config(params, arg);
@ -339,39 +331,32 @@ static void rkisp1_lsc_config(struct rkisp1_params *params,
/* program x size tables */
data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->x_size_tbl[i * 2],
arg->x_size_tbl[i * 2 + 1]);
rkisp1_write(params->rkisp1,
RKISP1_CIF_ISP_LSC_XSIZE_01 + i * 4, data);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_XSIZE(i), data);
/* 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]);
rkisp1_write(params->rkisp1,
RKISP1_CIF_ISP_LSC_XGRAD_01 + i * 4, data);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_XGRAD(i), data);
/* program y size tables */
data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->y_size_tbl[i * 2],
arg->y_size_tbl[i * 2 + 1]);
rkisp1_write(params->rkisp1,
RKISP1_CIF_ISP_LSC_YSIZE_01 + i * 4, data);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_YSIZE(i), data);
/* 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]);
rkisp1_write(params->rkisp1,
RKISP1_CIF_ISP_LSC_YGRAD_01 + i * 4, data);
rkisp1_write(rkisp1, RKISP1_CIF_ISP_LSC_YGRAD(i), data);
}
/* restore the lsc ctrl status */
if (lsc_ctrl & RKISP1_CIF_ISP_LSC_CTRL_ENA) {
rkisp1_param_set_bits(params,
RKISP1_CIF_ISP_LSC_CTRL,
if (lsc_ctrl & RKISP1_CIF_ISP_LSC_CTRL_ENA)
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,
else
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
RKISP1_CIF_ISP_LSC_CTRL_ENA);
}
}
/* ISP Filtering function */
static void rkisp1_flt_config(struct rkisp1_params *params,
@ -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[] = {
0x0026, 0x004b, 0x000f,
0x01ea, 0x01d6, 0x0040,
0x0040, 0x01ca, 0x01f6
struct csm_coeffs {
u16 limited[9];
u16 full[9];
};
static const u16 limited_range_coeff[] = {
0x0021, 0x0040, 0x000d,
static const struct csm_coeffs rec601_coeffs = {
.limited = {
0x0021, 0x0042, 0x000d,
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;
if (full_range) {
for (i = 0; i < ARRAY_SIZE(full_range_coeff); i++)
rkisp1_write(params->rkisp1,
RKISP1_CIF_ISP_CC_COEFF_0 + i * 4,
full_range_coeff[i]);
switch (params->ycbcr_encoding) {
case V4L2_YCBCR_ENC_601:
default:
coeffs = &rec601_coeffs;
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_CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
RKISP1_CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA);
} else {
for (i = 0; i < ARRAY_SIZE(limited_range_coeff); i++)
rkisp1_write(params->rkisp1,
RKISP1_CIF_ISP_CC_COEFF_0 + i * 4,
limited_range_coeff[i]);
csm = coeffs->limited;
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
RKISP1_CIF_ISP_CTRL_ISP_CSM_Y_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 */
@ -1216,11 +1258,11 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params,
if (module_ens & RKISP1_CIF_ISP_MODULE_DPCC)
rkisp1_param_set_bits(params,
RKISP1_CIF_ISP_DPCC_MODE,
RKISP1_CIF_ISP_DPCC_ENA);
RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE);
else
rkisp1_param_clear_bits(params,
RKISP1_CIF_ISP_DPCC_MODE,
RKISP1_CIF_ISP_DPCC_ENA);
RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE);
}
/* update bls config */
@ -1255,22 +1297,6 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params,
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 */
if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN)
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,
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,
struct rkisp1_buffer **buf,
struct rkisp1_params_cfg **cfg)
{
if (list_empty(&params->params))
return false;
*buf = list_first_entry(&params->params, struct rkisp1_buffer, queue);
*cfg = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0);
return true;
}
static void rkisp1_params_complete_buffer(struct rkisp1_params *params,
struct rkisp1_buffer *buf,
unsigned int frame_sequence)
{
struct rkisp1_params_cfg *new_params;
struct rkisp1_buffer *cur_buf = NULL;
list_del(&buf->queue);
if (list_empty(&params->params))
return;
cur_buf = list_first_entry(&params->params,
struct rkisp1_buffer, queue);
new_params = (struct rkisp1_params_cfg *)vb2_plane_vaddr(&cur_buf->vb.vb2_buf, 0);
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);
list_del(&cur_buf->queue);
cur_buf->vb.sequence = frame_sequence;
vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
buf->vb.sequence = frame_sequence;
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
}
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_cfg *new_params;
struct rkisp1_buffer *cur_buf;
spin_lock(&params->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(&params->config_lock);
}
@ -1531,9 +1597,18 @@ static const struct rkisp1_cif_isp_afc_config rkisp1_afc_params_default_config =
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_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_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_hst_params_default_config.mode);
/* set the range */
if (params->quantization == V4L2_QUANTIZATION_FULL_RANGE)
rkisp1_csm_config(params, true);
else
rkisp1_csm_config(params, false);
rkisp1_csm_config(params);
spin_lock_irq(&params->config_lock);
/* 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(&params->config_lock);
}
void rkisp1_params_configure(struct rkisp1_params *params,
enum rkisp1_fmt_raw_pat_type bayer_pat,
enum v4l2_quantization quantization)
void rkisp1_params_post_configure(struct rkisp1_params *params)
{
params->quantization = quantization;
params->raw_type = bayer_pat;
rkisp1_params_config_parameter(params);
struct rkisp1_params_cfg *new_params;
struct rkisp1_buffer *cur_buf;
spin_lock_irq(&params->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(&params->config_lock);
}
/*
@ -1582,7 +1685,7 @@ void rkisp1_params_configure(struct rkisp1_params *params,
void rkisp1_params_disable(struct rkisp1_params *params)
{
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_CIF_ISP_LSC_CTRL_ENA);
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_BLS_CTRL,

View File

@ -576,7 +576,7 @@
(((v0) & 0x1FFF) | (((v1) & 0x1FFF) << 13))
#define RKISP1_CIF_ISP_LSC_SECT_SIZE(v0, v1) \
(((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))
/* LSC: ISP_LSC_TABLE_SEL */
@ -618,19 +618,18 @@
#define RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA_READ(x) (((x) >> 11) & 1)
/* DPCC */
/* ISP_DPCC_MODE */
#define RKISP1_CIF_ISP_DPCC_ENA BIT(0)
#define RKISP1_CIF_ISP_DPCC_MODE_MAX 0x07
#define RKISP1_CIF_ISP_DPCC_OUTPUTMODE_MAX 0x0F
#define RKISP1_CIF_ISP_DPCC_SETUSE_MAX 0x0F
#define RKISP1_CIF_ISP_DPCC_METHODS_SET_RESERVED 0xFFFFE000
#define RKISP1_CIF_ISP_DPCC_LINE_THRESH_RESERVED 0xFFFF0000
#define RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_RESERVED 0xFFFFC0C0
#define RKISP1_CIF_ISP_DPCC_PG_FAC_RESERVED 0xFFFFC0C0
#define RKISP1_CIF_ISP_DPCC_RND_THRESH_RESERVED 0xFFFF0000
#define RKISP1_CIF_ISP_DPCC_RG_FAC_RESERVED 0xFFFFC0C0
#define RKISP1_CIF_ISP_DPCC_RO_LIMIT_RESERVED 0xFFFFF000
#define RKISP1_CIF_ISP_DPCC_RND_OFFS_RESERVED 0xFFFFF000
#define RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE BIT(0)
#define RKISP1_CIF_ISP_DPCC_MODE_GRAYSCALE_MODE BIT(1)
#define RKISP1_CIF_ISP_DPCC_OUTPUT_MODE_MASK GENMASK(3, 0)
#define RKISP1_CIF_ISP_DPCC_SET_USE_MASK GENMASK(3, 0)
#define RKISP1_CIF_ISP_DPCC_METHODS_SET_MASK 0x00001f1f
#define RKISP1_CIF_ISP_DPCC_LINE_THRESH_MASK 0x0000ffff
#define RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_MASK 0x00003f3f
#define RKISP1_CIF_ISP_DPCC_PG_FAC_MASK 0x00003f3f
#define RKISP1_CIF_ISP_DPCC_RND_THRESH_MASK 0x0000ffff
#define RKISP1_CIF_ISP_DPCC_RG_FAC_MASK 0x00003f3f
#define RKISP1_CIF_ISP_DPCC_RO_LIMIT_MASK 0x00000fff
#define RKISP1_CIF_ISP_DPCC_RND_OFFS_MASK 0x00000fff
/* BLS */
/* 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_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_XGRAD_01 (RKISP1_CIF_ISP_LSC_BASE + 0x00000024)
#define RKISP1_CIF_ISP_LSC_XGRAD_23 (RKISP1_CIF_ISP_LSC_BASE + 0x00000028)
#define RKISP1_CIF_ISP_LSC_XGRAD_45 (RKISP1_CIF_ISP_LSC_BASE + 0x0000002C)
#define RKISP1_CIF_ISP_LSC_XGRAD_67 (RKISP1_CIF_ISP_LSC_BASE + 0x00000030)
#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_XGRAD(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000024 + (n) * 4)
#define RKISP1_CIF_ISP_LSC_YGRAD(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000034 + (n) * 4)
#define RKISP1_CIF_ISP_LSC_XSIZE(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000044 + (n) * 4)
#define RKISP1_CIF_ISP_LSC_YSIZE(n) (RKISP1_CIF_ISP_LSC_BASE + 0x00000054 + (n) * 4)
#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)

View File

@ -411,6 +411,10 @@ static int rkisp1_rsz_init_config(struct v4l2_subdev *sd,
sink_fmt->height = RKISP1_DEFAULT_HEIGHT;
sink_fmt->field = V4L2_FIELD_NONE;
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,
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;
struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
struct v4l2_rect *sink_crop;
bool is_yuv;
sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK,
which);
@ -524,9 +529,6 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz,
if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
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,
RKISP1_ISP_MIN_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_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;
/* 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 */
rkisp1_rsz_set_sink_crop(rsz, sd_state, sink_crop, which);
}

View File

@ -524,7 +524,7 @@ static int fimc_capture_release(struct file *file)
mutex_lock(&fimc->lock);
if (close && vc->streaming) {
media_pipeline_stop(&vc->ve.vdev.entity);
video_device_pipeline_stop(&vc->ve.vdev);
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_vid_cap *vc = &fimc->vid_cap;
struct media_entity *entity = &vc->ve.vdev.entity;
struct fimc_source_info *si = NULL;
struct v4l2_subdev *sd;
int ret;
@ -1184,7 +1183,7 @@ static int fimc_cap_streamon(struct file *file, void *priv,
if (fimc_capture_active(fimc))
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)
return ret;
@ -1218,7 +1217,7 @@ static int fimc_cap_streamon(struct file *file, void *priv,
}
err_p_stop:
media_pipeline_stop(entity);
video_device_pipeline_stop(&vc->ve.vdev);
return ret;
}
@ -1234,7 +1233,7 @@ static int fimc_cap_streamoff(struct file *file, void *priv,
return ret;
if (vc->streaming) {
media_pipeline_stop(&vc->ve.vdev.entity);
video_device_pipeline_stop(&vc->ve.vdev);
vc->streaming = false;
}

View File

@ -312,7 +312,7 @@ static int isp_video_release(struct file *file)
is_singular_file = v4l2_fh_is_singular_file(file);
if (is_singular_file && ivc->streaming) {
media_pipeline_stop(entity);
video_device_pipeline_stop(&ivc->ve.vdev);
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 exynos_video_entity *ve = &isp->video_capture.ve;
struct media_entity *me = &ve->vdev.entity;
int ret;
ret = media_pipeline_start(me, &ve->pipe->mp);
ret = video_device_pipeline_start(&ve->vdev, &ve->pipe->mp);
if (ret < 0)
return ret;
@ -508,7 +507,7 @@ static int isp_video_streamon(struct file *file, void *priv,
isp->video_capture.streaming = 1;
return 0;
p_stop:
media_pipeline_stop(me);
video_device_pipeline_stop(&ve->vdev);
return ret;
}
@ -523,7 +522,7 @@ static int isp_video_streamoff(struct file *file, void *priv,
if (ret < 0)
return ret;
media_pipeline_stop(&video->ve.vdev.entity);
video_device_pipeline_stop(&video->ve.vdev);
video->streaming = 0;
return 0;
}

View File

@ -516,7 +516,7 @@ static int fimc_lite_release(struct file *file)
if (v4l2_fh_is_singular_file(file) &&
atomic_read(&fimc->out_path) == FIMC_IO_DMA) {
if (fimc->streaming) {
media_pipeline_stop(entity);
video_device_pipeline_stop(&fimc->ve.vdev);
fimc->streaming = 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)
{
struct fimc_lite *fimc = video_drvdata(file);
struct media_entity *entity = &fimc->ve.vdev.entity;
int ret;
if (fimc_lite_active(fimc))
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)
return ret;
@ -835,7 +834,7 @@ static int fimc_lite_streamon(struct file *file, void *priv,
}
err_p_stop:
media_pipeline_stop(entity);
video_device_pipeline_stop(&fimc->ve.vdev);
return 0;
}
@ -849,7 +848,7 @@ static int fimc_lite_streamoff(struct file *file, void *priv,
if (ret < 0)
return ret;
media_pipeline_stop(&fimc->ve.vdev.entity);
video_device_pipeline_stop(&fimc->ve.vdev);
fimc->streaming = false;
return 0;
}

View File

@ -848,13 +848,13 @@ static int s3c_camif_streamon(struct file *file, void *priv,
if (s3c_vp_active(vp))
return 0;
ret = media_pipeline_start(sensor, camif->m_pipeline);
ret = media_pipeline_start(sensor->pads, camif->m_pipeline);
if (ret < 0)
return ret;
ret = camif_pipeline_validate(camif);
if (ret < 0) {
media_pipeline_stop(sensor);
media_pipeline_stop(sensor->pads);
return ret;
}
@ -878,7 +878,7 @@ static int s3c_camif_streamoff(struct file *file, void *priv,
ret = vb2_streamoff(&vp->vb_queue, type);
if (ret == 0)
media_pipeline_stop(&camif->sensor.sd->entity);
media_pipeline_stop(camif->sensor.sd->entity.pads);
return ret;
}

View File

@ -751,7 +751,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
goto err_unlocked;
}
ret = media_pipeline_start(&dcmi->vdev->entity, &dcmi->pipeline);
ret = video_device_pipeline_start(dcmi->vdev, &dcmi->pipeline);
if (ret < 0) {
dev_err(dcmi->dev, "%s: Failed to start streaming, media pipeline start error (%d)\n",
__func__, ret);
@ -865,7 +865,7 @@ err_pipeline_stop:
dcmi_pipeline_stop(dcmi);
err_media_pipeline_stop:
media_pipeline_stop(&dcmi->vdev->entity);
video_device_pipeline_stop(dcmi->vdev);
err_pm_put:
pm_runtime_put(dcmi->dev);
@ -892,7 +892,7 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
dcmi_pipeline_stop(dcmi);
media_pipeline_stop(&dcmi->vdev->entity);
video_device_pipeline_stop(dcmi->vdev);
spin_lock_irq(&dcmi->irqlock);

View File

@ -3,7 +3,7 @@
config VIDEO_SUN4I_CSI
tristate "Allwinner A10 CMOS Sensor Interface Support"
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
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API

View File

@ -266,7 +266,7 @@ static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count)
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)
goto err_free_scratch_buffer;
@ -330,7 +330,7 @@ err_disable_device:
sun4i_csi_capture_stop(csi);
err_disable_pipeline:
media_pipeline_stop(&csi->vdev.entity);
video_device_pipeline_stop(&csi->vdev);
err_free_scratch_buffer:
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);
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,
csi->scratch.paddr);

View File

@ -1,13 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_SUN6I_CSI
tristate "Allwinner V3s Camera Sensor Interface driver"
depends on V4L_PLATFORM_DRIVERS
depends on VIDEO_DEV && COMMON_CLK && HAS_DMA
tristate "Allwinner A31 Camera Sensor Interface (CSI) Driver"
depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV
depends on ARCH_SUNXI || COMPILE_TEST
depends on PM && COMMON_CLK && RESET_CONTROLLER && HAS_DMA
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select VIDEOBUF2_DMA_CONTIG
select REGMAP_MMIO
select V4L2_FWNODE
select REGMAP_MMIO
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.

View File

@ -23,43 +23,27 @@
#include <linux/sched.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <media/v4l2-mc.h>
#include "sun6i_csi.h"
#include "sun6i_csi_reg.h"
#define MODULE_NAME "sun6i-csi"
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);
}
/* Helpers */
/* 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)
{
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
* 8bit and 16bit bus width.
* Identify the media bus format from device tree.
*/
if ((sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_PARALLEL
|| sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_BT656)
&& sdev->csi.v4l2_ep.bus.parallel.bus_width == 16) {
if ((v4l2->v4l2_ep.bus_type == V4L2_MBUS_PARALLEL
|| v4l2->v4l2_ep.bus_type == V4L2_MBUS_BT656)
&& v4l2->v4l2_ep.bus.parallel.bus_width == 16) {
switch (pixformat) {
case V4L2_PIX_FMT_NV12_16L16:
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:
return true;
default:
dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
dev_dbg(csi_dev->dev,
"Unsupported mbus code: 0x%x\n",
mbus_code);
break;
}
break;
default:
dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n",
dev_dbg(csi_dev->dev, "Unsupported pixformat: 0x%x\n",
pixformat);
break;
}
@ -139,7 +124,7 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
case MEDIA_BUS_FMT_YVYU8_2X8:
return true;
default:
dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
dev_dbg(csi_dev->dev, "Unsupported mbus code: 0x%x\n",
mbus_code);
break;
}
@ -154,67 +139,37 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
return (mbus_code == MEDIA_BUS_FMT_JPEG_1X8);
default:
dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
dev_dbg(csi_dev->dev, "Unsupported pixformat: 0x%x\n",
pixformat);
break;
}
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 = sdev->dev;
struct regmap *regmap = sdev->regmap;
struct device *dev = csi_dev->dev;
struct regmap *regmap = csi_dev->regmap;
int ret;
if (!enable) {
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;
}
ret = clk_prepare_enable(sdev->clk_mod);
if (ret) {
dev_err(sdev->dev, "Enable csi clk err %d\n", ret);
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
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);
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)
{
/* 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 */
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;
}
static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev,
u32 pixformat, u32 field)
static enum csi_output_fmt
get_csi_output_format(struct sun6i_csi_device *csi_dev, u32 pixformat,
u32 field)
{
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;
default:
dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
dev_warn(csi_dev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
break;
}
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)
{
/* 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:
return CSI_INPUT_SEQ_YVYU;
default:
dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n",
dev_warn(csi_dev->dev, "Unsupported mbus code: 0x%x\n",
mbus_code);
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:
return CSI_INPUT_SEQ_YUYV;
default:
dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n",
dev_warn(csi_dev->dev, "Unsupported mbus code: 0x%x\n",
mbus_code);
break;
}
@ -362,7 +318,7 @@ static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
return CSI_INPUT_SEQ_YUYV;
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);
break;
}
@ -370,23 +326,23 @@ static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
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 sun6i_csi *csi = &sdev->csi;
struct v4l2_fwnode_endpoint *endpoint = &csi_dev->v4l2.v4l2_ep;
struct sun6i_csi_config *config = &csi_dev->config;
unsigned char bus_width;
u32 flags;
u32 cfg;
bool input_interlaced = false;
if (csi->config.field == V4L2_FIELD_INTERLACED
|| csi->config.field == V4L2_FIELD_INTERLACED_TB
|| csi->config.field == V4L2_FIELD_INTERLACED_BT)
if (config->field == V4L2_FIELD_INTERLACED
|| config->field == V4L2_FIELD_INTERLACED_TB
|| config->field == V4L2_FIELD_INTERLACED_BT)
input_interlaced = true;
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 |
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;
break;
default:
dev_warn(sdev->dev, "Unsupported bus type: %d\n",
dev_warn(csi_dev->dev, "Unsupported bus type: %d\n",
endpoint->bus_type);
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 */
break;
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;
}
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 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 |
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_INPUT_SEQ_MASK);
val = get_csi_input_format(sdev, csi->config.code,
csi->config.pixelformat);
val = get_csi_input_format(csi_dev, config->code,
config->pixelformat);
cfg |= CSI_CH_CFG_INPUT_FMT(val);
val = get_csi_output_format(sdev, csi->config.pixelformat,
csi->config.field);
val = get_csi_output_format(csi_dev, config->pixelformat,
config->field);
cfg |= CSI_CH_CFG_OUTPUT_FMT(val);
val = get_csi_input_seq(sdev, csi->config.code,
csi->config.pixelformat);
val = get_csi_input_seq(csi_dev, config->code,
config->pixelformat);
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;
else if (csi->config.field == V4L2_FIELD_BOTTOM)
else if (config->field == V4L2_FIELD_BOTTOM)
cfg |= CSI_CH_CFG_FIELD_SEL_FIELD1;
else
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_c;
int *planar_offset = sdev->planar_offset;
int *planar_offset = csi_dev->planar_offset;
u32 width = config->width;
u32 height = config->height;
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_UYVY:
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");
hor_len = width * 2;
break;
@ -517,10 +473,10 @@ static void sun6i_csi_set_window(struct sun6i_csi_dev *sdev)
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_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_START(0));
@ -552,7 +508,7 @@ static void sun6i_csi_set_window(struct sun6i_csi_dev *sdev)
bytesperline_c * height;
break;
default: /* raw */
dev_dbg(sdev->dev,
dev_dbg(csi_dev->dev,
"Calculating pixelformat(0x%x)'s bytesperline as a packed format\n",
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;
}
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_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_dev *sdev = sun6i_csi_to_dev(csi);
if (!config)
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_set_format(sdev);
sun6i_csi_set_window(sdev);
sun6i_csi_setup_bus(csi_dev);
sun6i_csi_set_format(csi_dev);
sun6i_csi_set_window(csi_dev);
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(sdev->regmap, CSI_CH_F0_BUFA_REG,
(addr + sdev->planar_offset[0]) >> 2);
if (sdev->planar_offset[1] != -1)
regmap_write(sdev->regmap, CSI_CH_F1_BUFA_REG,
(addr + sdev->planar_offset[1]) >> 2);
if (sdev->planar_offset[2] != -1)
regmap_write(sdev->regmap, CSI_CH_F2_BUFA_REG,
(addr + sdev->planar_offset[2]) >> 2);
regmap_write(csi_dev->regmap, CSI_CH_F0_BUFA_REG,
(addr + csi_dev->planar_offset[0]) >> 2);
if (csi_dev->planar_offset[1] != -1)
regmap_write(csi_dev->regmap, CSI_CH_F1_BUFA_REG,
(addr + csi_dev->planar_offset[1]) >> 2);
if (csi_dev->planar_offset[2] != -1)
regmap_write(csi_dev->regmap, CSI_CH_F2_BUFA_REG,
(addr + csi_dev->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 = sdev->regmap;
struct regmap *regmap = csi_dev->regmap;
if (!enable) {
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);
}
/* -----------------------------------------------------------------------------
* Media Controller and V4L2
*/
static int sun6i_csi_link_entity(struct sun6i_csi *csi,
/* Media */
static const struct media_device_ops sun6i_csi_media_ops = {
.link_notify = v4l2_pipeline_link_notify,
};
/* V4L2 */
static int sun6i_csi_link_entity(struct sun6i_csi_device *csi_dev,
struct media_entity *entity,
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);
if (ret < 0) {
dev_err(csi->dev, "%s: no source pad in external entity %s\n",
__func__, entity->name);
dev_err(csi_dev->dev,
"%s: no source pad in external entity %s\n", __func__,
entity->name);
return -EINVAL;
}
src_pad_index = ret;
sink = &csi->video.vdev.entity;
sink_pad = &csi->video.pad;
sink = &csi_dev->video.video_dev.entity;
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);
ret = media_create_pad_link(entity, src_pad_index, sink,
sink_pad->index,
MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE);
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,
sink->name, sink_pad->index);
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)
{
struct sun6i_csi *csi = container_of(notifier, struct sun6i_csi,
notifier);
struct v4l2_device *v4l2_dev = &csi->v4l2_dev;
struct sun6i_csi_device *csi_dev =
container_of(notifier, struct sun6i_csi_device,
v4l2.notifier);
struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2;
struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev;
struct v4l2_subdev *sd;
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);
if (!sd)
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)
return ret;
ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
ret = v4l2_device_register_subdev_nodes(v4l2_dev);
if (ret < 0)
return ret;
return media_device_register(&csi->media_dev);
return 0;
}
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_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) {
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) {
case V4L2_MBUS_PARALLEL:
case V4L2_MBUS_BT656:
csi->v4l2_ep = *vep;
csi_dev->v4l2.v4l2_ep = *vep;
return 0;
default:
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)
{
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)
static int sun6i_csi_v4l2_setup(struct sun6i_csi_device *csi_dev)
{
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;
csi->media_dev.dev = csi->dev;
strscpy(csi->media_dev.model, "Allwinner Video Capture Device",
sizeof(csi->media_dev.model));
csi->media_dev.hw_revision = 0;
/* Media Device */
media_device_init(&csi->media_dev);
v4l2_async_nf_init(&csi->notifier);
strscpy(media_dev->model, SUN6I_CSI_DESCRIPTION,
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) {
dev_err(csi->dev, "V4L2 controls handler init failed (%d)\n",
ret);
goto clean_media;
dev_err(dev, "failed to register media device: %d\n", ret);
goto error_media;
}
csi->v4l2_dev.mdev = &csi->media_dev;
csi->v4l2_dev.ctrl_handler = &csi->ctrl_handler;
ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
/* V4L2 Device */
v4l2_dev->mdev = media_dev;
ret = v4l2_device_register(dev, v4l2_dev);
if (ret) {
dev_err(csi->dev, "V4L2 device registration failed (%d)\n",
ret);
goto free_ctrl;
dev_err(dev, "failed to register v4l2 device: %d\n", ret);
goto error_media;
}
ret = sun6i_video_init(&csi->video, csi, "sun6i-csi");
/* Video */
ret = sun6i_video_setup(csi_dev);
if (ret)
goto unreg_v4l2;
goto error_v4l2_device;
ret = v4l2_async_nf_parse_fwnode_endpoints(csi->dev,
&csi->notifier,
/* V4L2 Async */
v4l2_async_nf_init(notifier);
notifier->ops = &sun6i_csi_async_ops;
ret = v4l2_async_nf_parse_fwnode_endpoints(dev, notifier,
sizeof(struct
v4l2_async_subdev),
sun6i_csi_fwnode_parse);
if (ret)
goto clean_video;
goto error_video;
csi->notifier.ops = &sun6i_csi_async_ops;
ret = v4l2_async_nf_register(&csi->v4l2_dev, &csi->notifier);
ret = v4l2_async_nf_register(v4l2_dev, notifier);
if (ret) {
dev_err(csi->dev, "notifier registration failed\n");
goto clean_video;
dev_err(dev, "failed to register v4l2 async notifier: %d\n",
ret);
goto error_v4l2_async_notifier;
}
return 0;
clean_video:
sun6i_video_cleanup(&csi->video);
unreg_v4l2:
v4l2_device_unregister(&csi->v4l2_dev);
free_ctrl:
v4l2_ctrl_handler_free(&csi->ctrl_handler);
clean_media:
v4l2_async_nf_cleanup(&csi->notifier);
media_device_cleanup(&csi->media_dev);
error_v4l2_async_notifier:
v4l2_async_nf_cleanup(notifier);
error_video:
sun6i_video_cleanup(csi_dev);
error_v4l2_device:
v4l2_device_unregister(&v4l2->v4l2_dev);
error_media:
media_device_unregister(media_dev);
media_device_cleanup(media_dev);
return ret;
}
/* -----------------------------------------------------------------------------
* Resources and IRQ
*/
static irqreturn_t sun6i_csi_isr(int irq, void *dev_id)
static void sun6i_csi_v4l2_cleanup(struct sun6i_csi_device *csi_dev)
{
struct sun6i_csi_dev *sdev = (struct sun6i_csi_dev *)dev_id;
struct regmap *regmap = sdev->regmap;
struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2;
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;
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)
sun6i_video_frame_done(&sdev->csi.video);
sun6i_video_frame_done(csi_dev);
regmap_write(regmap, CSI_CH_INT_STA_REG, status);
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 = {
.reg_bits = 32,
.reg_stride = 4,
@ -828,106 +853,181 @@ static const struct regmap_config sun6i_csi_regmap_config = {
.max_register = 0x9c,
};
static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
struct platform_device *pdev)
static int sun6i_csi_resources_setup(struct sun6i_csi_device *csi_dev,
struct platform_device *platform_dev)
{
struct device *dev = csi_dev->dev;
const struct sun6i_csi_variant *variant;
void __iomem *io_base;
int ret;
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))
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);
if (IS_ERR(sdev->regmap)) {
dev_err(&pdev->dev, "Failed to init register map\n");
return PTR_ERR(sdev->regmap);
if (IS_ERR(csi_dev->regmap)) {
dev_err(dev, "failed to init register map\n");
return PTR_ERR(csi_dev->regmap);
}
sdev->clk_mod = devm_clk_get(&pdev->dev, "mod");
if (IS_ERR(sdev->clk_mod)) {
dev_err(&pdev->dev, "Unable to acquire csi clock\n");
return PTR_ERR(sdev->clk_mod);
/* Clocks */
csi_dev->clock_mod = devm_clk_get(dev, "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");
if (IS_ERR(sdev->clk_ram)) {
dev_err(&pdev->dev, "Unable to acquire dram-csi clock\n");
return PTR_ERR(sdev->clk_ram);
csi_dev->clock_ram = devm_clk_get(dev, "ram");
if (IS_ERR(csi_dev->clock_ram)) {
dev_err(dev, "failed to acquire ram clock\n");
return PTR_ERR(csi_dev->clock_ram);
}
sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev, NULL);
if (IS_ERR(sdev->rstc_bus)) {
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);
ret = clk_set_rate_exclusive(csi_dev->clock_mod,
variant->clock_mod_rate);
if (ret) {
dev_err(&pdev->dev, "Cannot request csi IRQ\n");
dev_err(dev, "failed to set mod clock rate\n");
return ret;
}
return 0;
/* 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;
}
static int sun6i_csi_probe(struct platform_device *pdev)
/* 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;
error_clock_rate_exclusive:
clk_rate_exclusive_put(csi_dev->clock_mod);
return ret;
}
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;
sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
if (!sdev)
csi_dev = devm_kzalloc(dev, sizeof(*csi_dev), GFP_KERNEL);
if (!csi_dev)
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)
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 sun6i_csi_v4l2_init(&sdev->csi);
return 0;
error_resources:
sun6i_csi_resources_cleanup(csi_dev);
return ret;
}
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;
}
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[] = {
{ .compatible = "allwinner,sun6i-a31-csi", },
{ .compatible = "allwinner,sun8i-a83t-csi", },
{ .compatible = "allwinner,sun8i-h3-csi", },
{ .compatible = "allwinner,sun8i-v3s-csi", },
{ .compatible = "allwinner,sun50i-a64-csi", },
{
.compatible = "allwinner,sun6i-a31-csi",
.data = &sun6i_a31_csi_variant,
},
{
.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);
static struct platform_driver sun6i_csi_platform_driver = {
.probe = sun6i_csi_probe,
.remove = sun6i_csi_remove,
.driver = {
.name = MODULE_NAME,
.name = SUN6I_CSI_NAME,
.of_match_table = of_match_ptr(sun6i_csi_of_match),
.pm = &sun6i_csi_pm_ops,
},
};
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_LICENSE("GPL");

View File

@ -8,13 +8,22 @@
#ifndef __SUN6I_CSI_H__
#define __SUN6I_CSI_H__
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
#include <media/videobuf2-v4l2.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
@ -32,59 +41,78 @@ struct sun6i_csi_config {
u32 height;
};
struct sun6i_csi {
struct device *dev;
struct v4l2_ctrl_handler ctrl_handler;
struct sun6i_csi_v4l2 {
struct v4l2_device v4l2_dev;
struct media_device media_dev;
struct v4l2_async_notifier notifier;
/* video port settings */
struct v4l2_fwnode_endpoint v4l2_ep;
};
struct sun6i_csi_device {
struct device *dev;
struct sun6i_csi_config config;
struct sun6i_csi_v4l2 v4l2;
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
* @csi: pointer to the csi
* @csi_dev: pointer to the csi device
* @pixformat: v4l2 pixel format (V4L2_PIX_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,
u32 mbus_code);
bool sun6i_csi_is_format_supported(struct sun6i_csi_device *csi_dev,
u32 pixformat, u32 mbus_code);
/**
* sun6i_csi_set_power() - power on/off the csi
* @csi: pointer to the csi
* @csi_dev: pointer to the csi device
* @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
* @csi: pointer to the csi
* @csi_dev: pointer to the csi device
* @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);
/**
* 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
*/
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
* @csi: pointer to the csi
* @csi_dev: pointer to the csi device
* @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 */
static inline int sun6i_csi_get_bpp(unsigned int pixformat)

View File

@ -23,15 +23,27 @@
#define MAX_WIDTH (4800)
#define MAX_HEIGHT (4800)
struct sun6i_csi_buffer {
struct vb2_v4l2_buffer vb;
struct list_head list;
/* Helpers */
dma_addr_t dma_addr;
bool queued_to_csi;
};
static struct v4l2_subdev *
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_SGBRG8,
V4L2_PIX_FMT_SGRBG8,
@ -61,119 +73,138 @@ static const u32 supported_pixformats[] = {
V4L2_PIX_FMT_JPEG,
};
static bool is_pixformat_valid(unsigned int pixformat)
static bool sun6i_video_format_check(u32 format)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(supported_pixformats); i++)
if (supported_pixformats[i] == pixformat)
for (i = 0; i < ARRAY_SIZE(sun6i_video_formats); i++)
if (sun6i_video_formats[i] == format)
return true;
return false;
}
static struct v4l2_subdev *
sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad)
/* Video */
static void sun6i_video_buffer_configure(struct sun6i_csi_device *csi_dev,
struct sun6i_csi_buffer *csi_buffer)
{
struct media_pad *remote;
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);
csi_buffer->queued_to_csi = true;
sun6i_csi_update_buf_addr(csi_dev, csi_buffer->dma_addr);
}
static int sun6i_video_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers,
unsigned int *nplanes,
static void sun6i_video_configure(struct sun6i_csi_device *csi_dev)
{
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[],
struct device *alloc_devs[])
{
struct sun6i_video *video = vb2_get_drv_priv(vq);
unsigned int size = video->fmt.fmt.pix.sizeimage;
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue);
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;
*nplanes = 1;
*planes_count = 1;
sizes[0] = size;
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_buffer *buf =
container_of(vbuf, struct sun6i_csi_buffer, vb);
struct sun6i_video *video = vb2_get_drv_priv(vb->vb2_queue);
unsigned long size = video->fmt.fmt.pix.sizeimage;
struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue);
struct sun6i_video *video = &csi_dev->video;
struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
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 size = video->format.fmt.pix.sizeimage;
if (vb2_plane_size(vb, 0) < size) {
v4l2_err(video->vdev.v4l2_dev, "buffer too small (%lu < %lu)\n",
vb2_plane_size(vb, 0), size);
if (vb2_plane_size(buffer, 0) < size) {
v4l2_err(v4l2_dev, "buffer too small (%lu < %lu)\n",
vb2_plane_size(buffer, 0), size);
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);
vbuf->field = video->fmt.fmt.pix.field;
csi_buffer->dma_addr = vb2_dma_contig_plane_dma_addr(buffer, 0);
v4l2_buffer->field = video->format.fmt.pix.field;
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 *next_buf;
struct sun6i_csi_config config;
struct v4l2_subdev *subdev;
unsigned long flags;
int ret;
video->sequence = 0;
ret = media_pipeline_start(&video->vdev.entity, &video->vdev.pipe);
ret = video_device_pipeline_alloc_start(video_dev);
if (ret < 0)
goto clear_dma_queue;
goto error_dma_queue_flush;
if (video->mbus_code == 0) {
ret = -EINVAL;
goto stop_media_pipeline;
goto error_media_pipeline;
}
subdev = sun6i_video_remote_subdev(video, NULL);
if (!subdev) {
ret = -EINVAL;
goto stop_media_pipeline;
goto error_media_pipeline;
}
config.pixelformat = video->fmt.fmt.pix.pixelformat;
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;
sun6i_video_configure(csi_dev);
spin_lock_irqsave(&video->dma_queue_lock, flags);
buf = list_first_entry(&video->dma_queue,
struct sun6i_csi_buffer, list);
buf->queued_to_csi = true;
sun6i_csi_update_buf_addr(video->csi, buf->dma_addr);
sun6i_video_buffer_configure(csi_dev, buf);
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
@ -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.
*/
next_buf = list_next_entry(buf, list);
next_buf->queued_to_csi = true;
sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
sun6i_video_buffer_configure(csi_dev, next_buf);
spin_unlock_irqrestore(&video->dma_queue_lock, flags);
ret = v4l2_subdev_call(subdev, video, s_stream, 1);
if (ret && ret != -ENOIOCTLCMD)
goto stop_csi_stream;
goto error_stream;
return 0;
stop_csi_stream:
sun6i_csi_set_stream(video->csi, false);
stop_media_pipeline:
media_pipeline_stop(&video->vdev.entity);
clear_dma_queue:
error_stream:
sun6i_csi_set_stream(csi_dev, false);
error_media_pipeline:
video_device_pipeline_stop(video_dev);
error_dma_queue_flush:
spin_lock_irqsave(&video->dma_queue_lock, flags);
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);
spin_unlock_irqrestore(&video->dma_queue_lock, flags);
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;
unsigned long flags;
struct sun6i_csi_buffer *buf;
@ -229,45 +263,32 @@ static void sun6i_video_stop_streaming(struct vb2_queue *vq)
if (subdev)
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 */
spin_lock_irqsave(&video->dma_queue_lock, flags);
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);
spin_unlock_irqrestore(&video->dma_queue_lock, flags);
}
static void sun6i_video_buffer_queue(struct vb2_buffer *vb)
{
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)
void sun6i_video_frame_done(struct sun6i_csi_device *csi_dev)
{
struct sun6i_video *video = &csi_dev->video;
struct sun6i_csi_buffer *buf;
struct sun6i_csi_buffer *next_buf;
struct vb2_v4l2_buffer *vbuf;
struct vb2_v4l2_buffer *v4l2_buffer;
spin_lock(&video->dma_queue_lock);
buf = list_first_entry(&video->dma_queue,
struct sun6i_csi_buffer, list);
if (list_is_last(&buf->list, &video->dma_queue)) {
dev_dbg(video->csi->dev, "Frame dropped!\n");
goto unlock;
dev_dbg(csi_dev->dev, "Frame dropped!\n");
goto complete;
}
next_buf = list_next_entry(buf, list);
@ -277,200 +298,204 @@ void sun6i_video_frame_done(struct sun6i_video *video)
* for next ISR call.
*/
if (!next_buf->queued_to_csi) {
next_buf->queued_to_csi = true;
sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
dev_dbg(video->csi->dev, "Frame dropped!\n");
goto unlock;
sun6i_video_buffer_configure(csi_dev, next_buf);
dev_dbg(csi_dev->dev, "Frame dropped!\n");
goto complete;
}
list_del(&buf->list);
vbuf = &buf->vb;
vbuf->vb2_buf.timestamp = ktime_get_ns();
vbuf->sequence = video->sequence;
vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
v4l2_buffer = &buf->v4l2_buffer;
v4l2_buffer->vb2_buf.timestamp = ktime_get_ns();
v4l2_buffer->sequence = video->sequence;
vb2_buffer_done(&v4l2_buffer->vb2_buf, VB2_BUF_STATE_DONE);
/* Prepare buffer for next frame but one. */
if (!list_is_last(&next_buf->list, &video->dma_queue)) {
next_buf = list_next_entry(next_buf, list);
next_buf->queued_to_csi = true;
sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
sun6i_video_buffer_configure(csi_dev, next_buf);
} 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++;
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,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
.buf_prepare = sun6i_video_buffer_prepare,
.buf_queue = sun6i_video_buffer_queue,
.start_streaming = sun6i_video_start_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,
struct v4l2_capability *cap)
{
struct sun6i_video *video = video_drvdata(file);
/* V4L2 Device */
strscpy(cap->driver, "sun6i-video", sizeof(cap->driver));
strscpy(cap->card, video->vdev.name, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
video->csi->dev->of_node->name);
static int sun6i_video_querycap(struct file *file, void *private,
struct v4l2_capability *capability)
{
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;
}
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
static int sun6i_video_enum_fmt(struct file *file, void *private,
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;
f->pixelformat = supported_pixformats[index];
fmtdesc->pixelformat = sun6i_video_formats[index];
return 0;
}
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *fmt)
static int sun6i_video_g_fmt(struct file *file, void *private,
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;
}
static int sun6i_video_try_fmt(struct sun6i_video *video,
struct v4l2_format *f)
static int sun6i_video_format_try(struct sun6i_video *video,
struct v4l2_format *format)
{
struct v4l2_pix_format *pixfmt = &f->fmt.pix;
struct v4l2_pix_format *pix_format = &format->fmt.pix;
int bpp;
if (!is_pixformat_valid(pixfmt->pixelformat))
pixfmt->pixelformat = supported_pixformats[0];
if (!sun6i_video_format_check(pix_format->pixelformat))
pix_format->pixelformat = sun6i_video_formats[0];
v4l_bound_align_image(&pixfmt->width, MIN_WIDTH, MAX_WIDTH, 1,
&pixfmt->height, MIN_HEIGHT, MAX_WIDTH, 1, 1);
v4l_bound_align_image(&pix_format->width, MIN_WIDTH, MAX_WIDTH, 1,
&pix_format->height, MIN_HEIGHT, MAX_WIDTH, 1, 1);
bpp = sun6i_csi_get_bpp(pixfmt->pixelformat);
pixfmt->bytesperline = (pixfmt->width * bpp) >> 3;
pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
bpp = sun6i_csi_get_bpp(pix_format->pixelformat);
pix_format->bytesperline = (pix_format->width * bpp) >> 3;
pix_format->sizeimage = pix_format->bytesperline * pix_format->height;
if (pixfmt->field == V4L2_FIELD_ANY)
pixfmt->field = V4L2_FIELD_NONE;
if (pix_format->field == V4L2_FIELD_ANY)
pix_format->field = V4L2_FIELD_NONE;
if (pixfmt->pixelformat == V4L2_PIX_FMT_JPEG)
pixfmt->colorspace = V4L2_COLORSPACE_JPEG;
if (pix_format->pixelformat == V4L2_PIX_FMT_JPEG)
pix_format->colorspace = V4L2_COLORSPACE_JPEG;
else
pixfmt->colorspace = V4L2_COLORSPACE_SRGB;
pix_format->colorspace = V4L2_COLORSPACE_SRGB;
pixfmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
pixfmt->quantization = V4L2_QUANTIZATION_DEFAULT;
pixfmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
pix_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
pix_format->quantization = V4L2_QUANTIZATION_DEFAULT;
pix_format->xfer_func = V4L2_XFER_FUNC_DEFAULT;
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;
ret = sun6i_video_try_fmt(video, f);
ret = sun6i_video_format_try(video, format);
if (ret)
return ret;
video->fmt = *f;
video->format = *format;
return 0;
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
static int sun6i_video_s_fmt(struct file *file, void *private,
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 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,
struct v4l2_format *f)
static int sun6i_video_try_fmt(struct file *file, void *private,
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,
struct v4l2_input *inp)
static int sun6i_video_enum_input(struct file *file, void *private,
struct v4l2_input *input)
{
if (inp->index != 0)
if (input->index != 0)
return -EINVAL;
strscpy(inp->name, "camera", sizeof(inp->name));
inp->type = V4L2_INPUT_TYPE_CAMERA;
input->type = V4L2_INPUT_TYPE_CAMERA;
strscpy(input->name, "Camera", sizeof(input->name));
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;
}
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 0;
}
static const struct v4l2_ioctl_ops sun6i_video_ioctl_ops = {
.vidioc_querycap = vidioc_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_querycap = sun6i_video_querycap,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_enum_fmt_vid_cap = sun6i_video_enum_fmt,
.vidioc_g_fmt_vid_cap = sun6i_video_g_fmt,
.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_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_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 operations
*/
/* V4L2 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;
if (mutex_lock_interruptible(&video->lock))
@ -478,45 +503,48 @@ static int sun6i_video_open(struct file *file)
ret = v4l2_fh_open(file);
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)
goto fh_release;
goto error_v4l2_fh;
/* check if already powered */
if (!v4l2_fh_is_singular_file(file))
goto unlock;
ret = sun6i_csi_set_power(video->csi, true);
/* Power on at first open. */
if (v4l2_fh_is_singular_file(file)) {
ret = sun6i_csi_set_power(csi_dev, true);
if (ret < 0)
goto fh_release;
goto error_v4l2_fh;
}
mutex_unlock(&video->lock);
return 0;
fh_release:
error_v4l2_fh:
v4l2_fh_release(file);
unlock:
error_lock:
mutex_unlock(&video->lock);
return ret;
}
static int sun6i_video_close(struct file *file)
{
struct sun6i_video *video = video_drvdata(file);
bool last_fh;
struct sun6i_csi_device *csi_dev = video_drvdata(file);
struct sun6i_video *video = &csi_dev->video;
bool last_close;
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);
v4l2_pipeline_pm_put(&video->video_dev.entity);
v4l2_pipeline_pm_put(&video->vdev.entity);
if (last_fh)
sun6i_csi_set_power(video->csi, false);
/* Power off at last close. */
if (last_close)
sun6i_csi_set_power(csi_dev, false);
mutex_unlock(&video->lock);
@ -532,9 +560,8 @@ static const struct v4l2_file_operations sun6i_video_fops = {
.poll = vb2_fop_poll
};
/* -----------------------------------------------------------------------------
* Media Operations
*/
/* Media Entity */
static int sun6i_video_link_validate_get_format(struct media_pad *pad,
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, 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;
int ret;
video->mbus_code = 0;
if (!media_pad_remote_pad_first(link->sink->entity->pads)) {
dev_info(video->csi->dev,
"video node %s pad not connected\n", vdev->name);
dev_info(csi_dev->dev, "video node %s pad not connected\n",
vdev->name);
return -ENOLINK;
}
@ -570,21 +598,21 @@ static int sun6i_video_link_validate(struct media_link *link)
if (ret < 0)
return ret;
if (!sun6i_csi_is_format_supported(video->csi,
video->fmt.fmt.pix.pixelformat,
if (!sun6i_csi_is_format_supported(csi_dev,
video->format.fmt.pix.pixelformat,
source_fmt.format.code)) {
dev_err(video->csi->dev,
dev_err(csi_dev->dev,
"Unsupported pixformat: 0x%x with mbus code: 0x%x!\n",
video->fmt.fmt.pix.pixelformat,
video->format.fmt.pix.pixelformat,
source_fmt.format.code);
return -EPIPE;
}
if (source_fmt.format.width != video->fmt.fmt.pix.width ||
source_fmt.format.height != video->fmt.fmt.pix.height) {
dev_err(video->csi->dev,
if (source_fmt.format.width != video->format.fmt.pix.width ||
source_fmt.format.height != video->format.fmt.pix.height) {
dev_err(csi_dev->dev,
"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);
return -EPIPE;
}
@ -598,88 +626,108 @@ static const struct media_entity_operations sun6i_video_media_ops = {
.link_validate = sun6i_video_link_validate
};
int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi,
const char *name)
/* Video */
int sun6i_video_setup(struct sun6i_csi_device *csi_dev)
{
struct video_device *vdev = &video->vdev;
struct vb2_queue *vidq = &video->vb2_vidq;
struct v4l2_format fmt = { 0 };
struct sun6i_video *video = &csi_dev->video;
struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev;
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;
video->csi = csi;
/* Media Entity */
/* Initialize the media entity... */
video->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
vdev->entity.ops = &sun6i_video_media_ops;
ret = media_entity_pads_init(&vdev->entity, 1, &video->pad);
video_dev->entity.ops = &sun6i_video_media_ops;
/* Media 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)
return ret;
mutex_init(&video->lock);
/* DMA queue */
INIT_LIST_HEAD(&video->dma_queue);
spin_lock_init(&video->dma_queue_lock);
video->sequence = 0;
/* Setup default format */
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);
/* Queue */
/* Initialize videobuf2 queue */
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;
mutex_init(&video->lock);
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) {
v4l2_err(&csi->v4l2_dev, "vb2_queue_init failed: %d\n", ret);
goto clean_entity;
v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret);
goto error_media_entity;
}
/* Register video device */
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);
/* V4L2 Format */
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) {
v4l2_err(&csi->v4l2_dev,
"video_register_device failed: %d\n", ret);
goto clean_entity;
v4l2_err(v4l2_dev, "failed to register video device: %d\n",
ret);
goto error_media_entity;
}
return 0;
clean_entity:
media_entity_cleanup(&video->vdev.entity);
error_media_entity:
media_entity_cleanup(&video_dev->entity);
mutex_destroy(&video->lock);
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);
media_entity_cleanup(&video->vdev.entity);
struct sun6i_video *video = &csi_dev->video;
struct video_device *video_dev = &video->video_dev;
vb2_video_unregister_device(video_dev);
media_entity_cleanup(&video_dev->entity);
mutex_destroy(&video->lock);
}

View File

@ -11,28 +11,25 @@
#include <media/v4l2-dev.h>
#include <media/videobuf2-core.h>
struct sun6i_csi;
struct sun6i_csi_device;
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 sun6i_csi *csi;
struct mutex lock;
struct vb2_queue vb2_vidq;
spinlock_t dma_queue_lock;
struct list_head dma_queue;
spinlock_t dma_queue_lock; /* DMA queue lock. */
unsigned int sequence;
struct v4l2_format fmt;
struct v4l2_format format;
u32 mbus_code;
unsigned int sequence;
};
int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi,
const char *name);
void sun6i_video_cleanup(struct sun6i_video *video);
int sun6i_video_setup(struct sun6i_csi_device *csi_dev);
void sun6i_video_cleanup(struct sun6i_csi_device *csi_dev);
void sun6i_video_frame_done(struct sun6i_video *video);
void sun6i_video_frame_done(struct sun6i_csi_device *csi_dev);
#endif /* __SUN6I_VIDEO_H__ */

View File

@ -3,11 +3,11 @@ config VIDEO_SUN6I_MIPI_CSI2
tristate "Allwinner A31 MIPI CSI-2 Controller Driver"
depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV
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 VIDEO_V4L2_SUBDEV_API
select V4L2_FWNODE
select PHY_SUN6I_MIPI_DPHY
select GENERIC_PHY_MIPI_DPHY
select REGMAP_MMIO
help

View File

@ -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);
if (IS_ERR(csi2_dev->reset)) {
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 */
@ -669,13 +670,14 @@ sun6i_mipi_csi2_resources_setup(struct sun6i_mipi_csi2_device *csi2_dev,
csi2_dev->dphy = devm_phy_get(dev, "dphy");
if (IS_ERR(csi2_dev->dphy)) {
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);
if (ret) {
dev_err(dev, "failed to initialize MIPI D-PHY\n");
return ret;
goto error_clock_rate_exclusive;
}
/* Runtime PM */
@ -683,6 +685,11 @@ sun6i_mipi_csi2_resources_setup(struct sun6i_mipi_csi2_device *csi2_dev,
pm_runtime_enable(dev);
return 0;
error_clock_rate_exclusive:
clk_rate_exclusive_put(csi2_dev->clock_mod);
return ret;
}
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);
if (ret)
return ret;
goto error_resources;
return 0;
error_resources:
sun6i_mipi_csi2_resources_cleanup(csi2_dev);
return ret;
}
static int sun6i_mipi_csi2_remove(struct platform_device *platform_dev)

View File

@ -3,7 +3,7 @@ config VIDEO_SUN8I_A83T_MIPI_CSI2
tristate "Allwinner A83T MIPI CSI-2 Controller and D-PHY Driver"
depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV
depends on ARCH_SUNXI || COMPILE_TEST
depends on PM && COMMON_CLK
depends on PM && COMMON_CLK && RESET_CONTROLLER
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select V4L2_FWNODE

View File

@ -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");
if (IS_ERR(csi2_dev->clock_mipi)) {
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");
if (IS_ERR(csi2_dev->clock_misc)) {
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 */
@ -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);
if (IS_ERR(csi2_dev->reset)) {
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 */
@ -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);
if (ret) {
dev_err(dev, "failed to initialize MIPI D-PHY\n");
return ret;
goto error_clock_rate_exclusive;
}
/* Runtime PM */
@ -749,6 +752,11 @@ sun8i_a83t_mipi_csi2_resources_setup(struct sun8i_a83t_mipi_csi2_device *csi2_de
pm_runtime_enable(dev);
return 0;
error_clock_rate_exclusive:
clk_rate_exclusive_put(csi2_dev->clock_mod);
return ret;
}
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);
if (ret)
return ret;
goto error_resources;
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)

View File

@ -4,7 +4,7 @@ config VIDEO_SUN8I_DEINTERLACE
depends on V4L_MEM2MEM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_SUNXI || COMPILE_TEST
depends on COMMON_CLK && OF
depends on COMMON_CLK && RESET_CONTROLLER && OF
depends on PM
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV

View File

@ -5,7 +5,7 @@ config VIDEO_SUN8I_ROTATE
depends on V4L_MEM2MEM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_SUNXI || COMPILE_TEST
depends on COMMON_CLK && OF
depends on COMMON_CLK && RESET_CONTROLLER && OF
depends on PM
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV

View File

@ -708,7 +708,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
dma_addr_t addr;
int ret;
ret = media_pipeline_start(&ctx->vdev.entity, &ctx->phy->pipe);
ret = video_device_pipeline_alloc_start(&ctx->vdev);
if (ret < 0) {
ctx_err(ctx, "Failed to start media pipeline: %d\n", ret);
goto error_release_buffers;
@ -761,7 +761,7 @@ error_stop:
cal_ctx_unprepare(ctx);
error_pipeline:
media_pipeline_stop(&ctx->vdev.entity);
video_device_pipeline_stop(&ctx->vdev);
error_release_buffers:
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);
media_pipeline_stop(&ctx->vdev.entity);
video_device_pipeline_stop(&ctx->vdev);
}
static const struct vb2_ops cal_video_qops = {

View File

@ -174,7 +174,6 @@ struct cal_camerarx {
struct device_node *source_ep_node;
struct device_node *source_node;
struct v4l2_subdev *source;
struct media_pipeline pipe;
struct v4l2_subdev subdev;
struct media_pad pads[CAL_CAMERARX_NUM_PADS];

View File

@ -937,10 +937,8 @@ static int isp_pipeline_is_last(struct media_entity *me)
struct isp_pipeline *pipe;
struct media_pad *pad;
if (!me->pipe)
return 0;
pipe = to_isp_pipeline(me);
if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED)
if (!pipe || pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED)
return 0;
pad = media_pad_remote_pad_first(&pipe->output->pad);
return pad->entity == me;

View File

@ -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
* pipeline can be activated or deactivated once streaming is started.
*/
pipe = video->video.entity.pipe
? to_isp_pipeline(&video->video.entity) : &video->pipe;
pipe = to_isp_pipeline(&video->video.entity) ? : &video->pipe;
ret = media_entity_enum_init(&pipe->ent_enum, &video->isp->media_dev);
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->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)
goto err_pipeline_start;
@ -1161,7 +1160,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
return 0;
err_check_format:
media_pipeline_stop(&video->video.entity);
video_device_pipeline_stop(&video->video);
err_pipeline_start:
/* TODO: Implement PM QoS */
/* 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;
/* TODO: Implement PM QoS */
media_pipeline_stop(&video->video.entity);
video_device_pipeline_stop(&video->video);
media_entity_enum_cleanup(&pipe->ent_enum);

View File

@ -99,8 +99,15 @@ struct isp_pipeline {
unsigned int external_width;
};
#define to_isp_pipeline(__e) \
container_of((__e)->pipe, struct isp_pipeline, pipe)
static inline struct isp_pipeline *to_isp_pipeline(struct media_entity *entity)
{
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)
{

View File

@ -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)
{
struct hantro_ctx *ctx;
ctx = container_of(ctrl->handler,
struct hantro_ctx, ctrl_handler);
if (ctrl->id == V4L2_CID_STATELESS_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) {
const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps;
if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
/* Luma and chroma bit depth mismatch */
return -EINVAL;
if (sps->bit_depth_luma_minus8 != 0)
/* Only 8-bit is supported */
if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
/* Only 8-bit and 10-bit are supported */
return -EINVAL;
ctx->bit_depth = sps->bit_depth_luma_minus8 + 8;
} else if (ctrl->id == V4L2_CID_STATELESS_VP9_FRAME) {
const struct v4l2_ctrl_vp9_frame *dec_params = ctrl->p_new.p_vp9_frame;

View File

@ -12,7 +12,7 @@
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)
@ -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_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));
min_log2_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3;

View File

@ -104,7 +104,7 @@ static int tile_buffer_reallocate(struct hantro_ctx *ctx)
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.dma,
GFP_KERNEL);
@ -112,7 +112,7 @@ static int tile_buffer_reallocate(struct hantro_ctx *ctx)
goto err_free_tile_buffers;
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.dma,
GFP_KERNEL);

View File

@ -114,6 +114,7 @@ static void hantro_postproc_g2_enable(struct hantro_ctx *ctx)
struct hantro_dev *vpu = ctx->dev;
struct vb2_v4l2_buffer *dst_buf;
int down_scale = down_scale_factor(ctx);
int out_depth;
size_t chroma_offset;
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_CHROMA_ADDR, dst_dma + chroma_offset);
}
out_depth = hantro_get_format_depth(ctx->dst_fmt.pixelformat);
if (ctx->dev->variant->legacy_regs) {
int out_depth = hantro_get_format_depth(ctx->dst_fmt.pixelformat);
u8 pp_shift = 0;
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_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);
}

View File

@ -162,12 +162,39 @@ static const struct hantro_fmt imx8m_vpu_g2_postproc_fmts[] = {
.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[] = {
{
.fourcc = V4L2_PIX_FMT_NV12_4L4,
.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 = {
.min_width = FMT_MIN_WIDTH,
.max_width = FMT_UHD_WIDTH,

View File

@ -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
* streaming.
*/
pipe = dma->video.entity.pipe
? to_xvip_pipeline(&dma->video.entity) : &dma->pipe;
pipe = to_xvip_pipeline(&dma->video) ? : &dma->pipe;
ret = media_pipeline_start(&dma->video.entity, &pipe->pipe);
ret = video_device_pipeline_start(&dma->video, &pipe->pipe);
if (ret < 0)
goto error;
@ -431,7 +430,7 @@ static int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count)
return 0;
error_stop:
media_pipeline_stop(&dma->video.entity);
video_device_pipeline_stop(&dma->video);
error:
/* Give back all queued buffers to videobuf2. */
@ -448,7 +447,7 @@ error:
static void xvip_dma_stop_streaming(struct vb2_queue *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;
/* 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. */
xvip_pipeline_cleanup(pipe);
media_pipeline_stop(&dma->video.entity);
video_device_pipeline_stop(&dma->video);
/* Give back all queued buffers to videobuf2. */
spin_lock_irq(&dma->queued_lock);

View File

@ -45,9 +45,14 @@ struct xvip_pipeline {
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);
}
/**

View File

@ -1072,7 +1072,6 @@ done:
static int si476x_radio_fops_release(struct file *file)
{
int err;
struct si476x_radio *radio = video_drvdata(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_POWER_DOWN);
err = v4l2_fh_release(file);
return err;
return v4l2_fh_release(file);
}
static ssize_t si476x_radio_fops_read(struct file *file, char __user *buf,

View File

@ -14,7 +14,7 @@
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>

View File

@ -684,7 +684,6 @@ static int send_packet(struct imon_context *ictx)
*/
static int send_associate_24g(struct imon_context *ictx)
{
int retval;
const unsigned char packet[8] = { 0x01, 0x00, 0x00, 0x00,
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));
retval = send_packet(ictx);
return retval;
return send_packet(ictx);
}
/*

View File

@ -1077,7 +1077,7 @@ static int mceusb_set_timeout(struct rc_dev *dev, unsigned int timeout)
struct mceusb_dev *ir = dev->priv;
unsigned int units;
units = DIV_ROUND_CLOSEST(timeout, MCE_TIME_UNIT);
units = DIV_ROUND_UP(timeout, MCE_TIME_UNIT);
cmdbuf[2] = units >> 8;
cmdbuf[3] = units;

View File

@ -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)
{
struct vimc_capture_device *vcapture = vb2_get_drv_priv(vq);
struct media_entity *entity = &vcapture->vdev.entity;
int ret;
vcapture->sequence = 0;
/* Start the media pipeline */
ret = media_pipeline_start(entity, &vcapture->stream.pipe);
ret = video_device_pipeline_start(&vcapture->vdev, &vcapture->stream.pipe);
if (ret) {
vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_QUEUED);
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);
if (ret) {
media_pipeline_stop(entity);
video_device_pipeline_stop(&vcapture->vdev);
vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_QUEUED);
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);
/* Stop the media pipeline */
media_pipeline_stop(&vcapture->vdev.entity);
video_device_pipeline_stop(&vcapture->vdev);
/* Release all active buffers */
vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_ERROR);

View File

@ -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)
{
u8 buf[4];
int result;
buf[0] = (regAddr >> 8) & 0xFF;
buf[1] = regAddr & 0xFF;
buf[2] = (i2cData >> 8) & 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)

View File

@ -410,7 +410,7 @@ static int au0828_enable_source(struct media_entity *entity,
goto end;
}
ret = __media_pipeline_start(entity, pipe);
ret = __media_pipeline_start(entity->pads, pipe);
if (ret) {
pr_err("Start Pipeline: %s->%s Error %d\n",
source->name, entity->name, ret);
@ -501,12 +501,12 @@ static void au0828_disable_source(struct media_entity *entity)
return;
/* stop pipeline */
__media_pipeline_stop(dev->active_link_owner);
__media_pipeline_stop(dev->active_link_owner->pads);
pr_debug("Pipeline stop for %s\n",
dev->active_link_owner->name);
ret = __media_pipeline_start(
dev->active_link_user,
dev->active_link_user->pads,
dev->active_link_user_pipe);
if (ret) {
pr_err("Start Pipeline: %s->%s %d\n",
@ -532,7 +532,7 @@ static void au0828_disable_source(struct media_entity *entity)
return;
/* stop pipeline */
__media_pipeline_stop(dev->active_link_owner);
__media_pipeline_stop(dev->active_link_owner->pads);
pr_debug("Pipeline stop for %s\n",
dev->active_link_owner->name);

View File

@ -1497,7 +1497,7 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap)
/*
* AF9035 gpiot2 = FC0012 enable
* 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) {

View File

@ -209,7 +209,7 @@ leave:
*
* 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
* 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.
*
* 6 * 16 * 2 * 4 = 768 samples. 768 * 4 = 3072 bytes

View File

@ -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 */
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);
}
@ -126,7 +126,7 @@ static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
if (ctrl->is_dyn_array)
ctrl->new_elems = elems;
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;
}
@ -494,7 +494,7 @@ EXPORT_SYMBOL(v4l2_g_ext_ctrls);
/* Validate a new control */
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. */
@ -1007,7 +1007,7 @@ int __v4l2_ctrl_modify_dimensions(struct v4l2_ctrl *ctrl,
ctrl->p_cur.p = p_array + elems * ctrl->elem_size;
for (i = 0; i < ctrl->nr_of_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);
send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_VALUE |
V4L2_EVENT_CTRL_CH_DIMENSIONS);

View File

@ -65,7 +65,7 @@ void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
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)
{
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:
return false;
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;
/* strings are always 0-terminated */
@ -84,7 +84,7 @@ bool v4l2_ctrl_type_op_equal(const struct v4l2_ctrl *ctrl, u32 elems,
return true;
default:
return !memcmp(ptr1.p_const, ptr2.p_const,
elems * ctrl->elem_size);
ctrl->elems * ctrl->elem_size);
}
}
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,
u32 tot_elems, union v4l2_ctrl_ptr ptr)
union v4l2_ctrl_ptr ptr)
{
unsigned int i;
u32 tot_elems = ctrl->elems;
u32 elems = tot_elems - from_idx;
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)
{
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_CTRL_CLASS:
memset(ptr.p_s32, 0, elems * sizeof(s32));
memset(ptr.p_s32, 0, ctrl->new_elems * sizeof(s32));
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);
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);
}
ctrl->type_ops->init(ctrl, 0, elems, ctrl->p_cur);
ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
cur_to_new(ctrl);
if (handler_new_ref(hdl, ctrl, NULL, false, false)) {
@ -2069,7 +2070,7 @@ static int cluster_changed(struct v4l2_ctrl *master)
ctrl_changed = true;
if (!ctrl_changed)
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;
changed |= ctrl->has_changed;
}

View File

@ -1095,6 +1095,78 @@ void video_unregister_device(struct video_device *vdev)
}
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
*/

View File

@ -17,7 +17,6 @@ atomisp-objs += \
pci/atomisp_compat_css20.o \
pci/atomisp_csi2.o \
pci/atomisp_drvfs.o \
pci/atomisp_file.o \
pci/atomisp_fops.o \
pci/atomisp_ioctl.o \
pci/atomisp_subdev.o \

View File

@ -841,8 +841,6 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
if (!ov2680_info)
return -EINVAL;
mutex_lock(&dev->input_lock);
res = v4l2_find_nearest_size(ov2680_res_preview,
ARRAY_SIZE(ov2680_res_preview), width,
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;
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
sd_state->pads->try_fmt = *fmt;
mutex_unlock(&dev->input_lock);
return 0;
}
dev_dbg(&client->dev, "%s: %dx%d\n",
__func__, fmt->width, fmt->height);
mutex_lock(&dev->input_lock);
/* s_power has not been called yet for std v4l2 clients (camorama) */
power_up(sd);
ret = ov2680_write_reg_array(client, dev->res->regs);
if (ret)
if (ret) {
dev_err(&client->dev,
"ov2680 write resolution register err: %d\n", ret);
goto err;
}
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;
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);
goto err;
}
ret = ov2680_get_intg_factor(client, ov2680_info, res);
if (ret) {
@ -894,11 +897,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd,
if (v_flag)
ov2680_v_flip(sd, v_flag);
/*
* ret = startup(sd);
* if (ret)
* dev_err(&client->dev, "ov2680 startup err\n");
*/
dev->res = res;
err:
mutex_unlock(&dev->input_lock);
return ret;

View File

@ -65,9 +65,6 @@
#define check_bo_null_return_void(bo) \
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_SIZE (0x7FFFFFFF) /* 2G address space */
#define ISP_PTR_NULL NULL
@ -89,8 +86,6 @@ enum hmm_bo_type {
#define HMM_BO_VMAPED 0x10
#define HMM_BO_VMAPED_CACHED 0x20
#define HMM_BO_ACTIVE 0x1000
#define HMM_BO_MEM_TYPE_USER 0x1
#define HMM_BO_MEM_TYPE_PFN 0x2
struct hmm_bo_device {
struct isp_mmu mmu;
@ -126,7 +121,6 @@ struct hmm_buffer_object {
enum hmm_bo_type type;
int mmap_count;
int status;
int mem_type;
void *vmap_addr; /* kernel virtual address by vmap */
struct rb_node node;

View File

@ -740,20 +740,6 @@ enum atomisp_frame_status {
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 {
EXT_ISP_CID_ISO = 0,
EXT_ISP_CID_CAPTURE_HDR,

View File

@ -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 gmin_get_var_int(struct device *dev, bool is_gmin,
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 *
gmin_camera_platform_data(
struct v4l2_subdev *subdev,

View File

@ -141,23 +141,6 @@ struct atomisp_platform_data {
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
* 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_camera_caps *atomisp_get_default_camera_caps(void);
/* API from old platform_camera.h, new CPUID implementation */
#define __IS_SOC(x) (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && \

View File

@ -28,3 +28,22 @@ Since getting a picture requires multiple processing steps,
this means that unlike in fixed pipelines the soft pipelines
on the ISP can do multiple processing steps in a single pipeline
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

View File

@ -54,7 +54,6 @@ void dump_sp_dmem(struct atomisp_device *isp, unsigned int addr,
unsigned int size);
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_acc_pipe *atomisp_to_acc_pipe(struct video_device *dev);
int atomisp_reset(struct atomisp_device *isp);
void atomisp_flush_bufs_and_wakeup(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 */
void atomisp_msi_irq_init(struct atomisp_device *isp);
void atomisp_msi_irq_uninit(struct atomisp_device *isp);
void atomisp_wdt_work(struct work_struct *work);
void atomisp_wdt(struct timer_list *t);
void atomisp_assert_recovery_work(struct work_struct *work);
void atomisp_setup_flash(struct atomisp_sub_device *asd);
irqreturn_t atomisp_isr(int irq, void *dev);
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,
bool *res_overflow);
int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f);
int atomisp_set_fmt_file(struct video_device *vdev, struct v4l2_format *f);
int atomisp_set_fmt(struct file *file, void *fh, struct v4l2_format *f);
int atomisp_set_shading_table(struct atomisp_sub_device *asd,
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);
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. */
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_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);
/* Function to enable/disable zoom for capture pipe */

View File

@ -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_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,
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_set_cont_prev_start_time(struct atomisp_device *isp,
unsigned int overlap);
int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd,
struct atomisp_dis_statistics *stats);
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,
bool *frame_done_found,
bool *css_pipe_done);

View File

@ -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_grid_info old_info;
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].
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));
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) {
dev_err(isp->dev, "ia_css_pipe_get_info failed\n");
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,
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;
if (ATOMISP_SOC_CAMERA(asd)) {
stream_index = atomisp_source_pad_to_stream_id(asd, source_pad);
stream_index = ATOMISP_INPUT_STREAM_GENERAL;
} else {
stream_index = (pipe_index == IA_CSS_PIPE_ID_YUVPP) ?
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]
@ -3626,6 +3611,8 @@ int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd,
struct atomisp_dis_buf *dis_buf;
unsigned long flags;
lockdep_assert_held(&isp->mutex);
if (!asd->params.dvs_stat->hor_prod.odd_real ||
!asd->params.dvs_stat->hor_prod.odd_imag ||
!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;
/* isp needs to be streaming to get DIS statistics */
spin_lock_irqsave(&isp->lock, flags);
if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) {
spin_unlock_irqrestore(&isp->lock, flags);
if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED)
return -EINVAL;
}
spin_unlock_irqrestore(&isp->lock, flags);
if (atomisp_compare_dvs_grid(asd, &stats->dvs2_stat.grid_info) != 0)
/* 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);
}
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(
struct ia_css_pipe *css_pipe,
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;
struct atomisp_css_event current_event;
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(&current_event.event)) {
if (current_event.event.type ==
@ -3839,14 +3796,8 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
__func__,
current_event.event.fw_assert_module_id,
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;
} 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",
@ -3875,20 +3826,12 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
frame_done_found[asd->index] = true;
atomisp_buf_done(asd, 0, IA_CSS_BUFFER_TYPE_OUTPUT_FRAME,
current_event.pipe, true, stream_id);
if (!IS_ISP2401)
reset_wdt_timer[asd->index] = true; /* ISP running */
break;
case IA_CSS_EVENT_TYPE_SECOND_OUTPUT_FRAME_DONE:
dev_dbg(isp->dev, "event: Second output frame done");
frame_done_found[asd->index] = true;
atomisp_buf_done(asd, 0, IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME,
current_event.pipe, true, stream_id);
if (!IS_ISP2401)
reset_wdt_timer[asd->index] = true; /* ISP running */
break;
case IA_CSS_EVENT_TYPE_3A_STATISTICS_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,
IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME,
current_event.pipe, true, stream_id);
if (!IS_ISP2401)
reset_wdt_timer[asd->index] = true; /* ISP running */
break;
case IA_CSS_EVENT_TYPE_SECOND_VF_OUTPUT_FRAME_DONE:
dev_dbg(isp->dev, "event: second VF output frame done");
atomisp_buf_done(asd, 0,
IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME,
current_event.pipe, true, stream_id);
if (!IS_ISP2401)
reset_wdt_timer[asd->index] = true; /* ISP running */
break;
case IA_CSS_EVENT_TYPE_DIS_STATISTICS_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;
}

View File

@ -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);
}

View File

@ -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