mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-09 06:43:09 +00:00
- Conversion of max9286 and adv748x to V4L2 subdev active state
- Cleanups and fixes for the Renesas R-Car VSP and VIN drivers - Miscellaneous cleanups to V4L2 core -----BEGIN PGP SIGNATURE----- iJgEABYKAEAWIQTAnvhxs4J7QT+XHKnMPy2AAyfeZAUCZnNeLCIcbGF1cmVudC5w aW5jaGFydEBpZGVhc29uYm9hcmQuY29tAAoJEMw/LYADJ95kxyUBAKdQ86lXbVd1 97wtbRDP3H3MTtEowjskDGqhfH0QA9kXAP9eTf7rJixORvXFg/DaLcjdWIyfIsjY 4gz7aHO8MsXGBg== =BgVa -----END PGP SIGNATURE----- Merge tag 'tags/next-media-renesas-20240619' of git://git.kernel.org/pub/scm/linux/kernel/git/pinchartl/linux.git - Conversion of max9286 and adv748x to V4L2 subdev active state - Cleanups and fixes for the Renesas R-Car VSP and VIN drivers - Miscellaneous cleanups to V4L2 core Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
This commit is contained in:
commit
113ac3e239
@ -199,7 +199,6 @@ module_param(debug, int, 0644);
|
||||
})
|
||||
|
||||
static void __vb2_queue_cancel(struct vb2_queue *q);
|
||||
static void __enqueue_in_driver(struct vb2_buffer *vb);
|
||||
|
||||
static const char *vb2_state_name(enum vb2_buffer_state s)
|
||||
{
|
||||
|
@ -114,7 +114,7 @@ static void adv748x_afe_fill_format(struct adv748x_afe *afe,
|
||||
{
|
||||
memset(fmt, 0, sizeof(*fmt));
|
||||
|
||||
fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
|
||||
fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
|
||||
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
|
||||
fmt->field = V4L2_FIELD_ALTERNATE;
|
||||
|
||||
@ -337,7 +337,7 @@ static int adv748x_afe_enum_mbus_code(struct v4l2_subdev *sd,
|
||||
if (code->index != 0)
|
||||
return -EINVAL;
|
||||
|
||||
code->code = MEDIA_BUS_FMT_UYVY8_2X8;
|
||||
code->code = MEDIA_BUS_FMT_UYVY8_1X16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-device.h>
|
||||
@ -14,6 +13,15 @@
|
||||
|
||||
#include "adv748x.h"
|
||||
|
||||
static const unsigned int adv748x_csi2_txa_fmts[] = {
|
||||
MEDIA_BUS_FMT_UYVY8_1X16,
|
||||
MEDIA_BUS_FMT_RGB888_1X24,
|
||||
};
|
||||
|
||||
static const unsigned int adv748x_csi2_txb_fmts[] = {
|
||||
MEDIA_BUS_FMT_UYVY8_1X16,
|
||||
};
|
||||
|
||||
int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc)
|
||||
{
|
||||
return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT);
|
||||
@ -59,7 +67,33 @@ static int adv748x_csi2_register_link(struct adv748x_csi2 *tx,
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* v4l2_subdev_internal_ops
|
||||
*
|
||||
*/
|
||||
|
||||
static int adv748x_csi2_init_state(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state)
|
||||
{
|
||||
static const struct v4l2_mbus_framefmt adv748x_csi2_default_fmt = {
|
||||
.width = 1280,
|
||||
.height = 720,
|
||||
.code = MEDIA_BUS_FMT_UYVY8_1X16,
|
||||
.colorspace = V4L2_COLORSPACE_SRGB,
|
||||
.field = V4L2_FIELD_NONE,
|
||||
.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
|
||||
.quantization = V4L2_QUANTIZATION_DEFAULT,
|
||||
.xfer_func = V4L2_XFER_FUNC_DEFAULT,
|
||||
};
|
||||
struct v4l2_mbus_framefmt *fmt;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SINK);
|
||||
*fmt = adv748x_csi2_default_fmt;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SOURCE);
|
||||
*fmt = adv748x_csi2_default_fmt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We use the internal registered operation to be able to ensure that our
|
||||
* incremental subdevices (not connected in the forward path) can be registered
|
||||
* against the resulting video path and media device.
|
||||
@ -109,6 +143,7 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd)
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = {
|
||||
.init_state = adv748x_csi2_init_state,
|
||||
.registered = adv748x_csi2_registered,
|
||||
};
|
||||
|
||||
@ -139,78 +174,82 @@ static const struct v4l2_subdev_video_ops adv748x_csi2_video_ops = {
|
||||
* But we must support setting the pad formats for format propagation.
|
||||
*/
|
||||
|
||||
static struct v4l2_mbus_framefmt *
|
||||
adv748x_csi2_get_pad_format(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
unsigned int pad, u32 which)
|
||||
static int adv748x_csi2_enum_mbus_code(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_mbus_code_enum *code)
|
||||
{
|
||||
struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
|
||||
const unsigned int *codes = is_txa(tx) ?
|
||||
adv748x_csi2_txa_fmts :
|
||||
adv748x_csi2_txb_fmts;
|
||||
size_t num_fmts = is_txa(tx) ? ARRAY_SIZE(adv748x_csi2_txa_fmts)
|
||||
: ARRAY_SIZE(adv748x_csi2_txb_fmts);
|
||||
|
||||
if (which == V4L2_SUBDEV_FORMAT_TRY)
|
||||
return v4l2_subdev_state_get_format(sd_state, pad);
|
||||
/*
|
||||
* The format available on the source pad is the one applied on the sink
|
||||
* pad.
|
||||
*/
|
||||
if (code->pad == ADV748X_CSI2_SOURCE) {
|
||||
struct v4l2_mbus_framefmt *fmt;
|
||||
|
||||
return &tx->format;
|
||||
}
|
||||
if (code->index)
|
||||
return -EINVAL;
|
||||
|
||||
static int adv748x_csi2_get_format(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_format *sdformat)
|
||||
{
|
||||
struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
|
||||
struct adv748x_state *state = tx->state;
|
||||
struct v4l2_mbus_framefmt *mbusformat;
|
||||
fmt = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SINK);
|
||||
code->code = fmt->code;
|
||||
|
||||
mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, sdformat->pad,
|
||||
sdformat->which);
|
||||
if (!mbusformat)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (code->index >= num_fmts)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&state->mutex);
|
||||
|
||||
sdformat->format = *mbusformat;
|
||||
|
||||
mutex_unlock(&state->mutex);
|
||||
code->code = codes[code->index];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool adv748x_csi2_is_fmt_supported(struct adv748x_csi2 *tx, u32 code)
|
||||
{
|
||||
const unsigned int *codes = is_txa(tx) ?
|
||||
adv748x_csi2_txa_fmts :
|
||||
adv748x_csi2_txb_fmts;
|
||||
size_t num_fmts = is_txa(tx) ? ARRAY_SIZE(adv748x_csi2_txa_fmts)
|
||||
: ARRAY_SIZE(adv748x_csi2_txb_fmts);
|
||||
|
||||
for (unsigned int i = 0; i < num_fmts; i++) {
|
||||
if (codes[i] == code)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int adv748x_csi2_set_format(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_format *sdformat)
|
||||
{
|
||||
struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
|
||||
struct adv748x_state *state = tx->state;
|
||||
struct v4l2_mbus_framefmt *mbusformat;
|
||||
int ret = 0;
|
||||
|
||||
mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, sdformat->pad,
|
||||
sdformat->which);
|
||||
if (!mbusformat)
|
||||
return -EINVAL;
|
||||
if (sdformat->pad == ADV748X_CSI2_SOURCE)
|
||||
return v4l2_subdev_get_fmt(sd, sd_state, sdformat);
|
||||
|
||||
mutex_lock(&state->mutex);
|
||||
|
||||
if (sdformat->pad == ADV748X_CSI2_SOURCE) {
|
||||
const struct v4l2_mbus_framefmt *sink_fmt;
|
||||
|
||||
sink_fmt = adv748x_csi2_get_pad_format(sd, sd_state,
|
||||
ADV748X_CSI2_SINK,
|
||||
sdformat->which);
|
||||
|
||||
if (!sink_fmt) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
sdformat->format = *sink_fmt;
|
||||
}
|
||||
/*
|
||||
* Make sure the format is supported, if not default it to
|
||||
* UYVY8 as it's supported by both TXes.
|
||||
*/
|
||||
if (!adv748x_csi2_is_fmt_supported(tx, sdformat->format.code))
|
||||
sdformat->format.code = MEDIA_BUS_FMT_UYVY8_1X16;
|
||||
|
||||
mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad);
|
||||
*mbusformat = sdformat->format;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&state->mutex);
|
||||
/* Propagate format to the source pad. */
|
||||
mbusformat = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SOURCE);
|
||||
*mbusformat = sdformat->format;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
|
||||
@ -228,7 +267,8 @@ static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = {
|
||||
.get_fmt = adv748x_csi2_get_format,
|
||||
.enum_mbus_code = adv748x_csi2_enum_mbus_code,
|
||||
.get_fmt = v4l2_subdev_get_fmt,
|
||||
.set_fmt = adv748x_csi2_set_format,
|
||||
.get_mbus_config = adv748x_csi2_get_mbus_config,
|
||||
};
|
||||
@ -320,6 +360,11 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
|
||||
if (ret)
|
||||
goto err_cleanup_subdev;
|
||||
|
||||
tx->sd.state_lock = &state->mutex;
|
||||
ret = v4l2_subdev_init_finalize(&tx->sd);
|
||||
if (ret)
|
||||
goto err_free_ctrl;
|
||||
|
||||
ret = v4l2_async_register_subdev(&tx->sd);
|
||||
if (ret)
|
||||
goto err_free_ctrl;
|
||||
|
@ -75,7 +75,6 @@ enum adv748x_csi2_pads {
|
||||
|
||||
struct adv748x_csi2 {
|
||||
struct adv748x_state *state;
|
||||
struct v4l2_mbus_framefmt format;
|
||||
unsigned int page;
|
||||
unsigned int port;
|
||||
unsigned int num_lanes;
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
@ -198,12 +197,6 @@ struct max9286_priv {
|
||||
struct v4l2_ctrl *pixelrate_ctrl;
|
||||
unsigned int pixelrate;
|
||||
|
||||
struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
|
||||
struct v4l2_fract interval;
|
||||
|
||||
/* Protects controls and fmt structures */
|
||||
struct mutex mutex;
|
||||
|
||||
unsigned int nsources;
|
||||
unsigned int source_mask;
|
||||
unsigned int route_mask;
|
||||
@ -576,11 +569,14 @@ static void max9286_set_video_format(struct max9286_priv *priv,
|
||||
MAX9286_INVVS | MAX9286_HVSRC_D14);
|
||||
}
|
||||
|
||||
static void max9286_set_fsync_period(struct max9286_priv *priv)
|
||||
static void max9286_set_fsync_period(struct max9286_priv *priv,
|
||||
struct v4l2_subdev_state *state)
|
||||
{
|
||||
const struct v4l2_fract *interval;
|
||||
u32 fsync;
|
||||
|
||||
if (!priv->interval.numerator || !priv->interval.denominator) {
|
||||
interval = v4l2_subdev_state_get_interval(state, MAX9286_SRC_PAD);
|
||||
if (!interval->numerator || !interval->denominator) {
|
||||
/*
|
||||
* Special case, a null interval enables automatic FRAMESYNC
|
||||
* mode. FRAMESYNC is taken from the slowest link.
|
||||
@ -596,8 +592,8 @@ static void max9286_set_fsync_period(struct max9286_priv *priv)
|
||||
* The FRAMESYNC generator is configured with a period expressed as a
|
||||
* number of PCLK periods.
|
||||
*/
|
||||
fsync = div_u64((u64)priv->pixelrate * priv->interval.numerator,
|
||||
priv->interval.denominator);
|
||||
fsync = div_u64((u64)priv->pixelrate * interval->numerator,
|
||||
interval->denominator);
|
||||
|
||||
dev_dbg(&priv->client->dev, "fsync period %u (pclk %u)\n", fsync,
|
||||
priv->pixelrate);
|
||||
@ -788,22 +784,25 @@ static void max9286_v4l2_notifier_unregister(struct max9286_priv *priv)
|
||||
static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
{
|
||||
struct max9286_priv *priv = sd_to_max9286(sd);
|
||||
struct v4l2_subdev_state *state;
|
||||
struct max9286_source *source;
|
||||
unsigned int i;
|
||||
bool sync = false;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
state = v4l2_subdev_lock_and_get_active_state(sd);
|
||||
|
||||
if (enable) {
|
||||
const struct v4l2_mbus_framefmt *format;
|
||||
|
||||
/*
|
||||
* Get the format from the first used sink pad, as all sink
|
||||
* formats must be identical.
|
||||
* Get the format from the source pad, as all formats must be
|
||||
* identical.
|
||||
*/
|
||||
format = &priv->fmt[__ffs(priv->bound_sources)];
|
||||
format = v4l2_subdev_state_get_format(state, MAX9286_SRC_PAD);
|
||||
|
||||
max9286_set_video_format(priv, format);
|
||||
max9286_set_fsync_period(priv);
|
||||
max9286_set_fsync_period(priv, state);
|
||||
|
||||
/*
|
||||
* The frame sync between cameras is transmitted across the
|
||||
@ -816,12 +815,12 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
for_each_source(priv, source) {
|
||||
ret = v4l2_subdev_call(source->sd, video, s_stream, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = max9286_check_video_links(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto unlock;
|
||||
|
||||
/*
|
||||
* Wait until frame synchronization is locked.
|
||||
@ -842,7 +841,8 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
if (!sync) {
|
||||
dev_err(&priv->client->dev,
|
||||
"Failed to get frame synchronization\n");
|
||||
return -EXDEV; /* Invalid cross-device link */
|
||||
ret = -EXDEV; /* Invalid cross-device link */
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -865,26 +865,21 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
max9286_i2c_mux_close(priv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
unlock:
|
||||
v4l2_subdev_unlock_state(state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max9286_get_frame_interval(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_frame_interval *interval)
|
||||
{
|
||||
struct max9286_priv *priv = sd_to_max9286(sd);
|
||||
|
||||
/*
|
||||
* FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
|
||||
* subdev active state API.
|
||||
*/
|
||||
if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE)
|
||||
return -EINVAL;
|
||||
|
||||
if (interval->pad != MAX9286_SRC_PAD)
|
||||
return -EINVAL;
|
||||
|
||||
interval->interval = priv->interval;
|
||||
interval->interval = *v4l2_subdev_state_get_interval(sd_state,
|
||||
interval->pad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -893,19 +888,11 @@ static int max9286_set_frame_interval(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_frame_interval *interval)
|
||||
{
|
||||
struct max9286_priv *priv = sd_to_max9286(sd);
|
||||
|
||||
/*
|
||||
* FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
|
||||
* subdev active state API.
|
||||
*/
|
||||
if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE)
|
||||
return -EINVAL;
|
||||
|
||||
if (interval->pad != MAX9286_SRC_PAD)
|
||||
return -EINVAL;
|
||||
|
||||
priv->interval = interval->interval;
|
||||
*v4l2_subdev_state_get_interval(sd_state,
|
||||
interval->pad) = interval->interval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -914,39 +901,28 @@ static int max9286_enum_mbus_code(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_mbus_code_enum *code)
|
||||
{
|
||||
if (code->pad || code->index > 0)
|
||||
if (code->pad || code->index >= ARRAY_SIZE(max9286_formats))
|
||||
return -EINVAL;
|
||||
|
||||
code->code = MEDIA_BUS_FMT_UYVY8_1X16;
|
||||
code->code = max9286_formats[code->index].code;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_mbus_framefmt *
|
||||
max9286_get_pad_format(struct max9286_priv *priv,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
unsigned int pad, u32 which)
|
||||
{
|
||||
switch (which) {
|
||||
case V4L2_SUBDEV_FORMAT_TRY:
|
||||
return v4l2_subdev_state_get_format(sd_state, pad);
|
||||
case V4L2_SUBDEV_FORMAT_ACTIVE:
|
||||
return &priv->fmt[pad];
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int max9286_set_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct v4l2_subdev_format *format)
|
||||
{
|
||||
struct max9286_priv *priv = sd_to_max9286(sd);
|
||||
struct v4l2_mbus_framefmt *cfg_fmt;
|
||||
struct max9286_source *source;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* Disable setting format on the source pad: format is propagated
|
||||
* from the sinks.
|
||||
*/
|
||||
if (format->pad == MAX9286_SRC_PAD)
|
||||
return -EINVAL;
|
||||
return v4l2_subdev_get_fmt(sd, state, format);
|
||||
|
||||
/* Validate the format. */
|
||||
for (i = 0; i < ARRAY_SIZE(max9286_formats); ++i) {
|
||||
@ -957,42 +933,17 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
|
||||
if (i == ARRAY_SIZE(max9286_formats))
|
||||
format->format.code = max9286_formats[0].code;
|
||||
|
||||
cfg_fmt = max9286_get_pad_format(priv, sd_state, format->pad,
|
||||
format->which);
|
||||
if (!cfg_fmt)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&priv->mutex);
|
||||
*cfg_fmt = format->format;
|
||||
mutex_unlock(&priv->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max9286_get_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_format *format)
|
||||
{
|
||||
struct max9286_priv *priv = sd_to_max9286(sd);
|
||||
struct v4l2_mbus_framefmt *cfg_fmt;
|
||||
unsigned int pad = format->pad;
|
||||
|
||||
/*
|
||||
* Multiplexed Stream Support: Support link validation by returning the
|
||||
* format of the first bound link. All links must have the same format,
|
||||
* as we do not support mixing and matching of cameras connected to the
|
||||
* max9286.
|
||||
* Apply the same format on all the other pad as all links must have the
|
||||
* same format.
|
||||
*/
|
||||
if (pad == MAX9286_SRC_PAD)
|
||||
pad = __ffs(priv->bound_sources);
|
||||
for_each_source(priv, source) {
|
||||
unsigned int index = to_index(priv, source);
|
||||
|
||||
cfg_fmt = max9286_get_pad_format(priv, sd_state, pad, format->which);
|
||||
if (!cfg_fmt)
|
||||
return -EINVAL;
|
||||
*v4l2_subdev_state_get_format(state, index) = format->format;
|
||||
}
|
||||
|
||||
mutex_lock(&priv->mutex);
|
||||
format->format = *cfg_fmt;
|
||||
mutex_unlock(&priv->mutex);
|
||||
*v4l2_subdev_state_get_format(state, MAX9286_SRC_PAD) = format->format;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1003,7 +954,7 @@ static const struct v4l2_subdev_video_ops max9286_video_ops = {
|
||||
|
||||
static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
|
||||
.enum_mbus_code = max9286_enum_mbus_code,
|
||||
.get_fmt = max9286_get_fmt,
|
||||
.get_fmt = v4l2_subdev_get_fmt,
|
||||
.set_fmt = max9286_set_fmt,
|
||||
.get_frame_interval = max9286_get_frame_interval,
|
||||
.set_frame_interval = max9286_set_frame_interval,
|
||||
@ -1025,26 +976,29 @@ static const struct v4l2_mbus_framefmt max9286_default_format = {
|
||||
.xfer_func = V4L2_XFER_FUNC_DEFAULT,
|
||||
};
|
||||
|
||||
static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
|
||||
static int max9286_init_state(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state)
|
||||
{
|
||||
*fmt = max9286_default_format;
|
||||
}
|
||||
struct v4l2_fract *interval;
|
||||
|
||||
static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
|
||||
{
|
||||
struct v4l2_mbus_framefmt *format;
|
||||
unsigned int i;
|
||||
for (unsigned int i = 0; i < MAX9286_N_PADS; i++)
|
||||
*v4l2_subdev_state_get_format(state, i) = max9286_default_format;
|
||||
|
||||
for (i = 0; i < MAX9286_N_SINKS; i++) {
|
||||
format = v4l2_subdev_state_get_format(fh->state, i);
|
||||
max9286_init_format(format);
|
||||
}
|
||||
/*
|
||||
* Special case: a null interval enables automatic FRAMESYNC mode.
|
||||
*
|
||||
* FRAMESYNC is taken from the slowest link. See register 0x01
|
||||
* configuration.
|
||||
*/
|
||||
interval = v4l2_subdev_state_get_interval(state, MAX9286_SRC_PAD);
|
||||
interval->numerator = 0;
|
||||
interval->denominator = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
|
||||
.open = max9286_open,
|
||||
.init_state = max9286_init_state,
|
||||
};
|
||||
|
||||
static const struct media_entity_operations max9286_media_ops = {
|
||||
@ -1079,10 +1033,6 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
|
||||
}
|
||||
|
||||
/* Configure V4L2 for the MAX9286 itself */
|
||||
|
||||
for (i = 0; i < MAX9286_N_SINKS; i++)
|
||||
max9286_init_format(&priv->fmt[i]);
|
||||
|
||||
v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops);
|
||||
priv->sd.internal_ops = &max9286_subdev_internal_ops;
|
||||
priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
@ -1109,14 +1059,21 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
|
||||
if (ret)
|
||||
goto err_async;
|
||||
|
||||
priv->sd.state_lock = priv->ctrls.lock;
|
||||
ret = v4l2_subdev_init_finalize(&priv->sd);
|
||||
if (ret)
|
||||
goto err_async;
|
||||
|
||||
ret = v4l2_async_register_subdev(&priv->sd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Unable to register subdevice\n");
|
||||
goto err_async;
|
||||
goto err_subdev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_subdev:
|
||||
v4l2_subdev_cleanup(&priv->sd);
|
||||
err_async:
|
||||
v4l2_ctrl_handler_free(&priv->ctrls);
|
||||
max9286_v4l2_notifier_unregister(priv);
|
||||
@ -1126,6 +1083,7 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
|
||||
|
||||
static void max9286_v4l2_unregister(struct max9286_priv *priv)
|
||||
{
|
||||
v4l2_subdev_cleanup(&priv->sd);
|
||||
v4l2_ctrl_handler_free(&priv->ctrls);
|
||||
v4l2_async_unregister_subdev(&priv->sd);
|
||||
max9286_v4l2_notifier_unregister(priv);
|
||||
@ -1182,7 +1140,6 @@ static int max9286_setup(struct max9286_priv *priv)
|
||||
max9286_write(priv, 0x69, (0xf & ~priv->route_mask));
|
||||
|
||||
max9286_set_video_format(priv, &max9286_default_format);
|
||||
max9286_set_fsync_period(priv);
|
||||
|
||||
cfg = max9286_read(priv, 0x1c);
|
||||
if (cfg < 0)
|
||||
@ -1629,8 +1586,6 @@ static int max9286_probe(struct i2c_client *client)
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&priv->mutex);
|
||||
|
||||
priv->client = client;
|
||||
|
||||
/* GPIO values default to high */
|
||||
|
@ -587,7 +587,8 @@ enum rcar_csi2_pads {
|
||||
struct rcar_csi2_info {
|
||||
int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps);
|
||||
int (*phy_post_init)(struct rcar_csi2 *priv);
|
||||
int (*start_receiver)(struct rcar_csi2 *priv);
|
||||
int (*start_receiver)(struct rcar_csi2 *priv,
|
||||
struct v4l2_subdev_state *state);
|
||||
void (*enter_standby)(struct rcar_csi2 *priv);
|
||||
const struct rcsi2_mbps_reg *hsfreqrange;
|
||||
unsigned int csi0clkfreqrange;
|
||||
@ -613,8 +614,6 @@ struct rcar_csi2 {
|
||||
|
||||
int channel_vc[4];
|
||||
|
||||
struct mutex lock; /* Protects mf and stream_count. */
|
||||
struct v4l2_mbus_framefmt mf;
|
||||
int stream_count;
|
||||
|
||||
bool cphy;
|
||||
@ -632,6 +631,16 @@ static inline struct rcar_csi2 *notifier_to_csi2(struct v4l2_async_notifier *n)
|
||||
return container_of(n, struct rcar_csi2, notifier);
|
||||
}
|
||||
|
||||
static unsigned int rcsi2_num_pads(const struct rcar_csi2 *priv)
|
||||
{
|
||||
/* Used together with R-Car ISP: one sink and one source pad. */
|
||||
if (priv->info->use_isp)
|
||||
return 2;
|
||||
|
||||
/* Used together with R-Car VIN: one sink and four source pads. */
|
||||
return 5;
|
||||
}
|
||||
|
||||
static u32 rcsi2_read(struct rcar_csi2 *priv, unsigned int reg)
|
||||
{
|
||||
return ioread32(priv->base + reg);
|
||||
@ -808,20 +817,25 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv)
|
||||
static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv,
|
||||
struct v4l2_subdev_state *state)
|
||||
{
|
||||
const struct rcar_csi2_format *format;
|
||||
u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0;
|
||||
const struct v4l2_mbus_framefmt *fmt;
|
||||
unsigned int lanes;
|
||||
unsigned int i;
|
||||
int mbps, ret;
|
||||
|
||||
/* Use the format on the sink pad to compute the receiver config. */
|
||||
fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK);
|
||||
|
||||
dev_dbg(priv->dev, "Input size (%ux%u%c)\n",
|
||||
priv->mf.width, priv->mf.height,
|
||||
priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i');
|
||||
fmt->width, fmt->height,
|
||||
fmt->field == V4L2_FIELD_NONE ? 'p' : 'i');
|
||||
|
||||
/* Code is validated in set_fmt. */
|
||||
format = rcsi2_code_to_fmt(priv->mf.code);
|
||||
format = rcsi2_code_to_fmt(fmt->code);
|
||||
if (!format)
|
||||
return -EINVAL;
|
||||
|
||||
@ -849,11 +863,11 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv)
|
||||
vcdt2 |= vcdt_part << ((i % 2) * 16);
|
||||
}
|
||||
|
||||
if (priv->mf.field == V4L2_FIELD_ALTERNATE) {
|
||||
if (fmt->field == V4L2_FIELD_ALTERNATE) {
|
||||
fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2
|
||||
| FLD_FLD_EN;
|
||||
|
||||
if (priv->mf.height == 240)
|
||||
if (fmt->height == 240)
|
||||
fld |= FLD_FLD_NUM(0);
|
||||
else
|
||||
fld |= FLD_FLD_NUM(1);
|
||||
@ -1049,15 +1063,18 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv)
|
||||
static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv,
|
||||
struct v4l2_subdev_state *state)
|
||||
{
|
||||
const struct rcar_csi2_format *format;
|
||||
const struct v4l2_mbus_framefmt *fmt;
|
||||
unsigned int lanes;
|
||||
int msps;
|
||||
int ret;
|
||||
|
||||
/* Calculate parameters */
|
||||
format = rcsi2_code_to_fmt(priv->mf.code);
|
||||
/* Use the format on the sink pad to compute the receiver config. */
|
||||
fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK);
|
||||
format = rcsi2_code_to_fmt(fmt->code);
|
||||
if (!format)
|
||||
return -EINVAL;
|
||||
|
||||
@ -1114,7 +1131,7 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcsi2_start(struct rcar_csi2 *priv)
|
||||
static int rcsi2_start(struct rcar_csi2 *priv, struct v4l2_subdev_state *state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -1122,7 +1139,7 @@ static int rcsi2_start(struct rcar_csi2 *priv)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = priv->info->start_receiver(priv);
|
||||
ret = priv->info->start_receiver(priv, state);
|
||||
if (ret) {
|
||||
rcsi2_enter_standby(priv);
|
||||
return ret;
|
||||
@ -1146,17 +1163,16 @@ static void rcsi2_stop(struct rcar_csi2 *priv)
|
||||
static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
{
|
||||
struct rcar_csi2 *priv = sd_to_csi2(sd);
|
||||
struct v4l2_subdev_state *state;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
if (!priv->remote)
|
||||
return -ENODEV;
|
||||
|
||||
if (!priv->remote) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
state = v4l2_subdev_lock_and_get_active_state(&priv->subdev);
|
||||
|
||||
if (enable && priv->stream_count == 0) {
|
||||
ret = rcsi2_start(priv);
|
||||
ret = rcsi2_start(priv, state);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else if (!enable && priv->stream_count == 1) {
|
||||
@ -1165,49 +1181,29 @@ static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
|
||||
priv->stream_count += enable ? 1 : -1;
|
||||
out:
|
||||
mutex_unlock(&priv->lock);
|
||||
v4l2_subdev_unlock_state(state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcsi2_set_pad_format(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct v4l2_subdev_format *format)
|
||||
{
|
||||
struct rcar_csi2 *priv = sd_to_csi2(sd);
|
||||
struct v4l2_mbus_framefmt *framefmt;
|
||||
unsigned int num_pads = rcsi2_num_pads(priv);
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
if (format->pad > RCAR_CSI2_SINK)
|
||||
return v4l2_subdev_get_fmt(sd, state, format);
|
||||
|
||||
if (!rcsi2_code_to_fmt(format->format.code))
|
||||
format->format.code = rcar_csi2_formats[0].code;
|
||||
|
||||
if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
|
||||
priv->mf = format->format;
|
||||
} else {
|
||||
framefmt = v4l2_subdev_state_get_format(sd_state, 0);
|
||||
*framefmt = format->format;
|
||||
}
|
||||
*v4l2_subdev_state_get_format(state, format->pad) = format->format;
|
||||
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcsi2_get_pad_format(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_format *format)
|
||||
{
|
||||
struct rcar_csi2 *priv = sd_to_csi2(sd);
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
|
||||
format->format = priv->mf;
|
||||
else
|
||||
format->format = *v4l2_subdev_state_get_format(sd_state, 0);
|
||||
|
||||
mutex_unlock(&priv->lock);
|
||||
/* Propagate the format to the source pads. */
|
||||
for (unsigned int i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++)
|
||||
*v4l2_subdev_state_get_format(state, i) = format->format;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1218,7 +1214,7 @@ static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = {
|
||||
|
||||
static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = {
|
||||
.set_fmt = rcsi2_set_pad_format,
|
||||
.get_fmt = rcsi2_get_pad_format,
|
||||
.get_fmt = v4l2_subdev_get_fmt,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = {
|
||||
@ -1226,6 +1222,33 @@ static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = {
|
||||
.pad = &rcar_csi2_pad_ops,
|
||||
};
|
||||
|
||||
static int rcsi2_init_state(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state)
|
||||
{
|
||||
struct rcar_csi2 *priv = sd_to_csi2(sd);
|
||||
unsigned int num_pads = rcsi2_num_pads(priv);
|
||||
|
||||
static const struct v4l2_mbus_framefmt rcar_csi2_default_fmt = {
|
||||
.width = 1920,
|
||||
.height = 1080,
|
||||
.code = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
.colorspace = V4L2_COLORSPACE_SRGB,
|
||||
.field = V4L2_FIELD_NONE,
|
||||
.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
|
||||
.quantization = V4L2_QUANTIZATION_DEFAULT,
|
||||
.xfer_func = V4L2_XFER_FUNC_DEFAULT,
|
||||
};
|
||||
|
||||
for (unsigned int i = RCAR_CSI2_SINK; i < num_pads; i++)
|
||||
*v4l2_subdev_state_get_format(state, i) = rcar_csi2_default_fmt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_internal_ops rcar_csi2_internal_ops = {
|
||||
.init_state = rcsi2_init_state,
|
||||
};
|
||||
|
||||
static irqreturn_t rcsi2_irq(int irq, void *data)
|
||||
{
|
||||
struct rcar_csi2 *priv = data;
|
||||
@ -1251,14 +1274,17 @@ static irqreturn_t rcsi2_irq(int irq, void *data)
|
||||
|
||||
static irqreturn_t rcsi2_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct v4l2_subdev_state *state;
|
||||
struct rcar_csi2 *priv = data;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
state = v4l2_subdev_lock_and_get_active_state(&priv->subdev);
|
||||
|
||||
rcsi2_stop(priv);
|
||||
usleep_range(1000, 2000);
|
||||
if (rcsi2_start(priv))
|
||||
if (rcsi2_start(priv, state))
|
||||
dev_warn(priv->dev, "Failed to restart CSI-2 receiver\n");
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
v4l2_subdev_unlock_state(state);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -1870,23 +1896,23 @@ static int rcsi2_probe(struct platform_device *pdev)
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
|
||||
mutex_init(&priv->lock);
|
||||
priv->stream_count = 0;
|
||||
|
||||
ret = rcsi2_probe_resources(priv, pdev);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Failed to get resources\n");
|
||||
goto error_mutex;
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = rcsi2_parse_dt(priv);
|
||||
if (ret)
|
||||
goto error_mutex;
|
||||
return ret;
|
||||
|
||||
priv->subdev.owner = THIS_MODULE;
|
||||
priv->subdev.dev = &pdev->dev;
|
||||
priv->subdev.internal_ops = &rcar_csi2_internal_ops;
|
||||
v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops);
|
||||
v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
|
||||
snprintf(priv->subdev.name, sizeof(priv->subdev.name), "%s %s",
|
||||
@ -1896,7 +1922,7 @@ static int rcsi2_probe(struct platform_device *pdev)
|
||||
priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
|
||||
priv->subdev.entity.ops = &rcar_csi2_entity_ops;
|
||||
|
||||
num_pads = priv->info->use_isp ? 2 : NR_OF_RCAR_CSI2_PAD;
|
||||
num_pads = rcsi2_num_pads(priv);
|
||||
|
||||
priv->pads[RCAR_CSI2_SINK].flags = MEDIA_PAD_FL_SINK;
|
||||
for (i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++)
|
||||
@ -1912,19 +1938,25 @@ static int rcsi2_probe(struct platform_device *pdev)
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = v4l2_subdev_init_finalize(&priv->subdev);
|
||||
if (ret)
|
||||
goto error_pm_runtime;
|
||||
|
||||
ret = v4l2_async_register_subdev(&priv->subdev);
|
||||
if (ret < 0)
|
||||
goto error_async;
|
||||
goto error_subdev;
|
||||
|
||||
dev_info(priv->dev, "%d lanes found\n", priv->lanes);
|
||||
|
||||
return 0;
|
||||
|
||||
error_subdev:
|
||||
v4l2_subdev_cleanup(&priv->subdev);
|
||||
error_pm_runtime:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
error_async:
|
||||
v4l2_async_nf_unregister(&priv->notifier);
|
||||
v4l2_async_nf_cleanup(&priv->notifier);
|
||||
error_mutex:
|
||||
mutex_destroy(&priv->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1936,10 +1968,9 @@ static void rcsi2_remove(struct platform_device *pdev)
|
||||
v4l2_async_nf_unregister(&priv->notifier);
|
||||
v4l2_async_nf_cleanup(&priv->notifier);
|
||||
v4l2_async_unregister_subdev(&priv->subdev);
|
||||
v4l2_subdev_cleanup(&priv->subdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
mutex_destroy(&priv->lock);
|
||||
}
|
||||
|
||||
static struct platform_driver rcar_csi2_pdrv = {
|
||||
|
@ -742,12 +742,22 @@ static int rvin_setup(struct rvin_dev *vin)
|
||||
*/
|
||||
switch (vin->mbus_code) {
|
||||
case MEDIA_BUS_FMT_YUYV8_1X16:
|
||||
/* BT.601/BT.1358 16bit YCbCr422 */
|
||||
vnmc |= VNMC_INF_YUV16;
|
||||
if (vin->is_csi)
|
||||
/* YCbCr422 8-bit */
|
||||
vnmc |= VNMC_INF_YUV8_BT601;
|
||||
else
|
||||
/* BT.601/BT.1358 16bit YCbCr422 */
|
||||
vnmc |= VNMC_INF_YUV16;
|
||||
input_is_yuv = true;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_UYVY8_1X16:
|
||||
vnmc |= VNMC_INF_YUV16 | VNMC_YCAL;
|
||||
if (vin->is_csi)
|
||||
/* YCbCr422 8-bit */
|
||||
vnmc |= VNMC_INF_YUV8_BT601;
|
||||
else
|
||||
/* BT.601/BT.1358 16bit YCbCr422 */
|
||||
vnmc |= VNMC_INF_YUV16;
|
||||
vnmc |= VNMC_YCAL;
|
||||
input_is_yuv = true;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_UYVY8_2X8:
|
||||
|
@ -96,13 +96,6 @@ static int brx_enum_frame_size(struct v4l2_subdev *subdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
unsigned int pad)
|
||||
{
|
||||
return v4l2_subdev_state_get_compose(sd_state, pad);
|
||||
}
|
||||
|
||||
static void brx_try_format(struct vsp1_brx *brx,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
unsigned int pad, struct v4l2_mbus_framefmt *fmt)
|
||||
@ -119,8 +112,8 @@ static void brx_try_format(struct vsp1_brx *brx,
|
||||
|
||||
default:
|
||||
/* The BRx can't perform format conversion. */
|
||||
format = vsp1_entity_get_pad_format(&brx->entity, sd_state,
|
||||
BRX_PAD_SINK(0));
|
||||
format = v4l2_subdev_state_get_format(sd_state,
|
||||
BRX_PAD_SINK(0));
|
||||
fmt->code = format->code;
|
||||
break;
|
||||
}
|
||||
@ -150,14 +143,14 @@ static int brx_set_format(struct v4l2_subdev *subdev,
|
||||
|
||||
brx_try_format(brx, state, fmt->pad, &fmt->format);
|
||||
|
||||
format = vsp1_entity_get_pad_format(&brx->entity, state, fmt->pad);
|
||||
format = v4l2_subdev_state_get_format(state, fmt->pad);
|
||||
*format = fmt->format;
|
||||
|
||||
/* Reset the compose rectangle. */
|
||||
if (fmt->pad != brx->entity.source_pad) {
|
||||
struct v4l2_rect *compose;
|
||||
|
||||
compose = brx_get_compose(brx, state, fmt->pad);
|
||||
compose = v4l2_subdev_state_get_compose(state, fmt->pad);
|
||||
compose->left = 0;
|
||||
compose->top = 0;
|
||||
compose->width = format->width;
|
||||
@ -169,8 +162,7 @@ static int brx_set_format(struct v4l2_subdev *subdev,
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i <= brx->entity.source_pad; ++i) {
|
||||
format = vsp1_entity_get_pad_format(&brx->entity,
|
||||
state, i);
|
||||
format = v4l2_subdev_state_get_format(state, i);
|
||||
format->code = fmt->format.code;
|
||||
}
|
||||
}
|
||||
@ -205,7 +197,7 @@ static int brx_get_selection(struct v4l2_subdev *subdev,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&brx->entity.lock);
|
||||
sel->r = *brx_get_compose(brx, state, sel->pad);
|
||||
sel->r = *v4l2_subdev_state_get_compose(state, sel->pad);
|
||||
mutex_unlock(&brx->entity.lock);
|
||||
return 0;
|
||||
|
||||
@ -242,8 +234,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev,
|
||||
* The compose rectangle top left corner must be inside the output
|
||||
* frame.
|
||||
*/
|
||||
format = vsp1_entity_get_pad_format(&brx->entity, state,
|
||||
brx->entity.source_pad);
|
||||
format = v4l2_subdev_state_get_format(state, brx->entity.source_pad);
|
||||
sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
|
||||
sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
|
||||
|
||||
@ -251,11 +242,11 @@ static int brx_set_selection(struct v4l2_subdev *subdev,
|
||||
* Scaling isn't supported, the compose rectangle size must be identical
|
||||
* to the sink format size.
|
||||
*/
|
||||
format = vsp1_entity_get_pad_format(&brx->entity, state, sel->pad);
|
||||
format = v4l2_subdev_state_get_format(state, sel->pad);
|
||||
sel->r.width = format->width;
|
||||
sel->r.height = format->height;
|
||||
|
||||
compose = brx_get_compose(brx, state, sel->pad);
|
||||
compose = v4l2_subdev_state_get_compose(state, sel->pad);
|
||||
*compose = sel->r;
|
||||
|
||||
done:
|
||||
@ -281,6 +272,7 @@ static const struct v4l2_subdev_ops brx_ops = {
|
||||
*/
|
||||
|
||||
static void brx_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
@ -290,8 +282,7 @@ static void brx_configure_stream(struct vsp1_entity *entity,
|
||||
unsigned int flags;
|
||||
unsigned int i;
|
||||
|
||||
format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.state,
|
||||
brx->entity.source_pad);
|
||||
format = v4l2_subdev_state_get_format(state, brx->entity.source_pad);
|
||||
|
||||
/*
|
||||
* The hardware is extremely flexible but we have no userspace API to
|
||||
|
@ -170,6 +170,7 @@ static const struct v4l2_subdev_ops clu_ops = {
|
||||
*/
|
||||
|
||||
static void clu_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
@ -181,8 +182,7 @@ static void clu_configure_stream(struct vsp1_entity *entity,
|
||||
* The yuv_mode can't be changed during streaming. Cache it internally
|
||||
* for future runtime configuration calls.
|
||||
*/
|
||||
format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.state,
|
||||
CLU_PAD_SINK);
|
||||
format = v4l2_subdev_state_get_format(state, CLU_PAD_SINK);
|
||||
clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32;
|
||||
}
|
||||
|
||||
|
@ -317,7 +317,10 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1,
|
||||
list_add_tail(&released_brx->list_pipe,
|
||||
&pipe->entities);
|
||||
|
||||
/* Add the BRx to the pipeline. */
|
||||
/*
|
||||
* Add the BRx to the pipeline, inserting it just before the
|
||||
* WPF.
|
||||
*/
|
||||
dev_dbg(vsp1->dev, "%s: pipe %u: acquired %s\n",
|
||||
__func__, pipe->lif->index, BRX_NAME(brx));
|
||||
|
||||
@ -326,7 +329,8 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1,
|
||||
pipe->brx->sink = &pipe->output->entity;
|
||||
pipe->brx->sink_pad = 0;
|
||||
|
||||
list_add_tail(&pipe->brx->list_pipe, &pipe->entities);
|
||||
list_add_tail(&pipe->brx->list_pipe,
|
||||
&pipe->output->entity.list_pipe);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -420,7 +424,7 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
|
||||
|
||||
if (!rpf->entity.pipe) {
|
||||
rpf->entity.pipe = pipe;
|
||||
list_add_tail(&rpf->entity.list_pipe, &pipe->entities);
|
||||
list_add(&rpf->entity.list_pipe, &pipe->entities);
|
||||
}
|
||||
|
||||
brx->inputs[i].rpf = rpf;
|
||||
@ -546,6 +550,9 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
|
||||
struct vsp1_dl_body *dlb;
|
||||
unsigned int dl_flags = 0;
|
||||
|
||||
vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0],
|
||||
drm_pipe->width, 0);
|
||||
|
||||
if (drm_pipe->force_brx_release)
|
||||
dl_flags |= VSP1_DL_FRAME_END_INTERNAL;
|
||||
if (pipe->output->writeback)
|
||||
@ -567,9 +574,11 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
|
||||
}
|
||||
|
||||
vsp1_entity_route_setup(entity, pipe, dlb);
|
||||
vsp1_entity_configure_stream(entity, pipe, dl, dlb);
|
||||
vsp1_entity_configure_stream(entity, entity->state, pipe,
|
||||
dl, dlb);
|
||||
vsp1_entity_configure_frame(entity, pipe, dl, dlb);
|
||||
vsp1_entity_configure_partition(entity, pipe, dl, dlb);
|
||||
vsp1_entity_configure_partition(entity, pipe,
|
||||
&pipe->part_table[0], dl, dlb);
|
||||
}
|
||||
|
||||
vsp1_dl_list_commit(dl, dl_flags);
|
||||
@ -733,6 +742,8 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
vsp1_pipeline_dump(pipe, "LIF setup");
|
||||
|
||||
/* Enable the VSP1. */
|
||||
ret = vsp1_device_get(vsp1);
|
||||
if (ret < 0)
|
||||
@ -906,6 +917,9 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index,
|
||||
}
|
||||
|
||||
vsp1_du_pipeline_setup_inputs(vsp1, pipe);
|
||||
|
||||
vsp1_pipeline_dump(pipe, "atomic update");
|
||||
|
||||
vsp1_du_pipeline_configure(pipe);
|
||||
|
||||
done:
|
||||
@ -959,6 +973,9 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
|
||||
|
||||
vsp1_pipeline_init(pipe);
|
||||
|
||||
pipe->partitions = 1;
|
||||
pipe->part_table = &drm_pipe->partition;
|
||||
|
||||
pipe->frame_end = vsp1_du_pipeline_frame_end;
|
||||
|
||||
/*
|
||||
|
@ -20,6 +20,7 @@
|
||||
/**
|
||||
* struct vsp1_drm_pipeline - State for the API exposed to the DRM driver
|
||||
* @pipe: the VSP1 pipeline used for display
|
||||
* @partition: the pre-calculated partition used by the pipeline
|
||||
* @width: output display width
|
||||
* @height: output display height
|
||||
* @force_brx_release: when set, release the BRx during the next reconfiguration
|
||||
@ -31,6 +32,7 @@
|
||||
*/
|
||||
struct vsp1_drm_pipeline {
|
||||
struct vsp1_pipeline pipe;
|
||||
struct vsp1_partition partition;
|
||||
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
|
@ -70,12 +70,13 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
|
||||
}
|
||||
|
||||
void vsp1_entity_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
{
|
||||
if (entity->ops->configure_stream)
|
||||
entity->ops->configure_stream(entity, pipe, dl, dlb);
|
||||
entity->ops->configure_stream(entity, state, pipe, dl, dlb);
|
||||
}
|
||||
|
||||
void vsp1_entity_configure_frame(struct vsp1_entity *entity,
|
||||
@ -89,11 +90,13 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity,
|
||||
|
||||
void vsp1_entity_configure_partition(struct vsp1_entity *entity,
|
||||
struct vsp1_pipeline *pipe,
|
||||
const struct vsp1_partition *partition,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
{
|
||||
if (entity->ops->configure_partition)
|
||||
entity->ops->configure_partition(entity, pipe, dl, dlb);
|
||||
entity->ops->configure_partition(entity, pipe, partition,
|
||||
dl, dlb);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
@ -127,49 +130,6 @@ vsp1_entity_get_state(struct vsp1_entity *entity,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vsp1_entity_get_pad_format - Get a pad format from storage for an entity
|
||||
* @entity: the entity
|
||||
* @sd_state: the state storage
|
||||
* @pad: the pad number
|
||||
*
|
||||
* Return the format stored in the given configuration for an entity's pad. The
|
||||
* configuration can be an ACTIVE or TRY configuration.
|
||||
*/
|
||||
struct v4l2_mbus_framefmt *
|
||||
vsp1_entity_get_pad_format(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
unsigned int pad)
|
||||
{
|
||||
return v4l2_subdev_state_get_format(sd_state, pad);
|
||||
}
|
||||
|
||||
/**
|
||||
* vsp1_entity_get_pad_selection - Get a pad selection from storage for entity
|
||||
* @entity: the entity
|
||||
* @sd_state: the state storage
|
||||
* @pad: the pad number
|
||||
* @target: the selection target
|
||||
*
|
||||
* Return the selection rectangle stored in the given configuration for an
|
||||
* entity's pad. The configuration can be an ACTIVE or TRY configuration. The
|
||||
* selection target can be COMPOSE or CROP.
|
||||
*/
|
||||
struct v4l2_rect *
|
||||
vsp1_entity_get_pad_selection(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
unsigned int pad, unsigned int target)
|
||||
{
|
||||
switch (target) {
|
||||
case V4L2_SEL_TGT_COMPOSE:
|
||||
return v4l2_subdev_state_get_compose(sd_state, pad);
|
||||
case V4L2_SEL_TGT_CROP:
|
||||
return v4l2_subdev_state_get_crop(sd_state, pad);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* vsp1_subdev_get_pad_format - Subdev pad get_fmt handler
|
||||
* @subdev: V4L2 subdevice
|
||||
@ -191,7 +151,7 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&entity->lock);
|
||||
fmt->format = *vsp1_entity_get_pad_format(entity, state, fmt->pad);
|
||||
fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad);
|
||||
mutex_unlock(&entity->lock);
|
||||
|
||||
return 0;
|
||||
@ -238,7 +198,7 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&entity->lock);
|
||||
format = vsp1_entity_get_pad_format(entity, state, 0);
|
||||
format = v4l2_subdev_state_get_format(state, 0);
|
||||
code->code = format->code;
|
||||
mutex_unlock(&entity->lock);
|
||||
}
|
||||
@ -276,7 +236,7 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev,
|
||||
if (!state)
|
||||
return -EINVAL;
|
||||
|
||||
format = vsp1_entity_get_pad_format(entity, state, fse->pad);
|
||||
format = v4l2_subdev_state_get_format(state, fse->pad);
|
||||
|
||||
mutex_lock(&entity->lock);
|
||||
|
||||
@ -346,7 +306,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
|
||||
goto done;
|
||||
}
|
||||
|
||||
format = vsp1_entity_get_pad_format(entity, state, fmt->pad);
|
||||
format = v4l2_subdev_state_get_format(state, fmt->pad);
|
||||
|
||||
if (fmt->pad == entity->source_pad) {
|
||||
/* The output format can't be modified. */
|
||||
@ -374,19 +334,17 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
|
||||
fmt->format = *format;
|
||||
|
||||
/* Propagate the format to the source pad. */
|
||||
format = vsp1_entity_get_pad_format(entity, state, entity->source_pad);
|
||||
format = v4l2_subdev_state_get_format(state, entity->source_pad);
|
||||
*format = fmt->format;
|
||||
|
||||
/* Reset the crop and compose rectangles. */
|
||||
selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad,
|
||||
V4L2_SEL_TGT_CROP);
|
||||
selection = v4l2_subdev_state_get_crop(state, fmt->pad);
|
||||
selection->left = 0;
|
||||
selection->top = 0;
|
||||
selection->width = format->width;
|
||||
selection->height = format->height;
|
||||
|
||||
selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad,
|
||||
V4L2_SEL_TGT_COMPOSE);
|
||||
selection = v4l2_subdev_state_get_compose(state, fmt->pad);
|
||||
selection->left = 0;
|
||||
selection->top = 0;
|
||||
selection->width = format->width;
|
||||
|
@ -19,7 +19,6 @@ struct vsp1_dl_body;
|
||||
struct vsp1_dl_list;
|
||||
struct vsp1_pipeline;
|
||||
struct vsp1_partition;
|
||||
struct vsp1_partition_window;
|
||||
|
||||
enum vsp1_entity_type {
|
||||
VSP1_ENTITY_BRS,
|
||||
@ -78,19 +77,30 @@ struct vsp1_route {
|
||||
* configuration.
|
||||
*/
|
||||
struct vsp1_entity_operations {
|
||||
void (*destroy)(struct vsp1_entity *);
|
||||
void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *,
|
||||
struct vsp1_dl_list *, struct vsp1_dl_body *);
|
||||
void (*configure_frame)(struct vsp1_entity *, struct vsp1_pipeline *,
|
||||
struct vsp1_dl_list *, struct vsp1_dl_body *);
|
||||
void (*configure_partition)(struct vsp1_entity *,
|
||||
struct vsp1_pipeline *,
|
||||
struct vsp1_dl_list *,
|
||||
struct vsp1_dl_body *);
|
||||
unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *);
|
||||
void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *,
|
||||
struct vsp1_partition *, unsigned int,
|
||||
struct vsp1_partition_window *);
|
||||
void (*destroy)(struct vsp1_entity *entity);
|
||||
void (*configure_stream)(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb);
|
||||
void (*configure_frame)(struct vsp1_entity *entity,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb);
|
||||
void (*configure_partition)(struct vsp1_entity *entity,
|
||||
struct vsp1_pipeline *pipe,
|
||||
const struct vsp1_partition *partition,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb);
|
||||
unsigned int (*max_width)(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe);
|
||||
void (*partition)(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_partition *partition,
|
||||
unsigned int index,
|
||||
struct v4l2_rect *window);
|
||||
};
|
||||
|
||||
struct vsp1_entity {
|
||||
@ -138,20 +148,13 @@ struct v4l2_subdev_state *
|
||||
vsp1_entity_get_state(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
enum v4l2_subdev_format_whence which);
|
||||
struct v4l2_mbus_framefmt *
|
||||
vsp1_entity_get_pad_format(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
unsigned int pad);
|
||||
struct v4l2_rect *
|
||||
vsp1_entity_get_pad_selection(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
unsigned int pad, unsigned int target);
|
||||
|
||||
void vsp1_entity_route_setup(struct vsp1_entity *entity,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_body *dlb);
|
||||
|
||||
void vsp1_entity_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb);
|
||||
@ -163,6 +166,7 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity,
|
||||
|
||||
void vsp1_entity_configure_partition(struct vsp1_entity *entity,
|
||||
struct vsp1_pipeline *pipe,
|
||||
const struct vsp1_partition *partition,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb);
|
||||
|
||||
|
@ -130,6 +130,7 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = {
|
||||
*/
|
||||
|
||||
static void hgo_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
@ -140,11 +141,8 @@ static void hgo_configure_stream(struct vsp1_entity *entity,
|
||||
unsigned int hratio;
|
||||
unsigned int vratio;
|
||||
|
||||
crop = vsp1_entity_get_pad_selection(entity, entity->state,
|
||||
HISTO_PAD_SINK, V4L2_SEL_TGT_CROP);
|
||||
compose = vsp1_entity_get_pad_selection(entity, entity->state,
|
||||
HISTO_PAD_SINK,
|
||||
V4L2_SEL_TGT_COMPOSE);
|
||||
crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK);
|
||||
compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK);
|
||||
|
||||
vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA);
|
||||
|
||||
@ -194,6 +192,16 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1)
|
||||
if (hgo == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Initialize the video device and queue for statistics data. */
|
||||
ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo",
|
||||
&hgo_entity_ops, hgo_mbus_formats,
|
||||
ARRAY_SIZE(hgo_mbus_formats),
|
||||
HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO);
|
||||
if (ret < 0) {
|
||||
vsp1_entity_destroy(&hgo->histo.entity);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* Initialize the control handler. */
|
||||
v4l2_ctrl_handler_init(&hgo->ctrls.handler,
|
||||
vsp1->info->gen >= 3 ? 2 : 1);
|
||||
@ -209,15 +217,5 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1)
|
||||
|
||||
hgo->histo.entity.subdev.ctrl_handler = &hgo->ctrls.handler;
|
||||
|
||||
/* Initialize the video device and queue for statistics data. */
|
||||
ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo",
|
||||
&hgo_entity_ops, hgo_mbus_formats,
|
||||
ARRAY_SIZE(hgo_mbus_formats),
|
||||
HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO);
|
||||
if (ret < 0) {
|
||||
vsp1_entity_destroy(&hgo->histo.entity);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return hgo;
|
||||
}
|
||||
|
@ -126,6 +126,7 @@ static const struct v4l2_ctrl_config hgt_hue_areas = {
|
||||
*/
|
||||
|
||||
static void hgt_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
@ -139,11 +140,8 @@ static void hgt_configure_stream(struct vsp1_entity *entity,
|
||||
u8 upper;
|
||||
unsigned int i;
|
||||
|
||||
crop = vsp1_entity_get_pad_selection(entity, entity->state,
|
||||
HISTO_PAD_SINK, V4L2_SEL_TGT_CROP);
|
||||
compose = vsp1_entity_get_pad_selection(entity, entity->state,
|
||||
HISTO_PAD_SINK,
|
||||
V4L2_SEL_TGT_COMPOSE);
|
||||
crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK);
|
||||
compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK);
|
||||
|
||||
vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA);
|
||||
|
||||
@ -193,12 +191,6 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1)
|
||||
if (hgt == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Initialize the control handler. */
|
||||
v4l2_ctrl_handler_init(&hgt->ctrls, 1);
|
||||
v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL);
|
||||
|
||||
hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls;
|
||||
|
||||
/* Initialize the video device and queue for statistics data. */
|
||||
ret = vsp1_histogram_init(vsp1, &hgt->histo, VSP1_ENTITY_HGT, "hgt",
|
||||
&hgt_entity_ops, hgt_mbus_formats,
|
||||
@ -209,6 +201,12 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* Initialize the control handler. */
|
||||
v4l2_ctrl_handler_init(&hgt->ctrls, 1);
|
||||
v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL);
|
||||
|
||||
hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls;
|
||||
|
||||
v4l2_ctrl_handler_setup(&hgt->ctrls);
|
||||
|
||||
return hgt;
|
||||
|
@ -36,9 +36,8 @@ struct vsp1_histogram_buffer *
|
||||
vsp1_histogram_buffer_get(struct vsp1_histogram *histo)
|
||||
{
|
||||
struct vsp1_histogram_buffer *buf = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&histo->irqlock, flags);
|
||||
spin_lock(&histo->irqlock);
|
||||
|
||||
if (list_empty(&histo->irqqueue))
|
||||
goto done;
|
||||
@ -49,7 +48,7 @@ vsp1_histogram_buffer_get(struct vsp1_histogram *histo)
|
||||
histo->readout = true;
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&histo->irqlock, flags);
|
||||
spin_unlock(&histo->irqlock);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@ -58,7 +57,6 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo,
|
||||
size_t size)
|
||||
{
|
||||
struct vsp1_pipeline *pipe = histo->entity.pipe;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* The pipeline pointer is guaranteed to be valid as this function is
|
||||
@ -70,10 +68,10 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo,
|
||||
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size);
|
||||
vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
|
||||
|
||||
spin_lock_irqsave(&histo->irqlock, flags);
|
||||
spin_lock(&histo->irqlock);
|
||||
histo->readout = false;
|
||||
wake_up(&histo->wait_queue);
|
||||
spin_unlock_irqrestore(&histo->irqlock, flags);
|
||||
spin_unlock(&histo->irqlock);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
@ -124,11 +122,10 @@ static void histo_buffer_queue(struct vb2_buffer *vb)
|
||||
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
||||
struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue);
|
||||
struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&histo->irqlock, flags);
|
||||
spin_lock_irq(&histo->irqlock);
|
||||
list_add_tail(&buf->queue, &histo->irqqueue);
|
||||
spin_unlock_irqrestore(&histo->irqlock, flags);
|
||||
spin_unlock_irq(&histo->irqlock);
|
||||
}
|
||||
|
||||
static int histo_start_streaming(struct vb2_queue *vq, unsigned int count)
|
||||
@ -140,9 +137,8 @@ static void histo_stop_streaming(struct vb2_queue *vq)
|
||||
{
|
||||
struct vsp1_histogram *histo = vb2_get_drv_priv(vq);
|
||||
struct vsp1_histogram_buffer *buffer;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&histo->irqlock, flags);
|
||||
spin_lock_irq(&histo->irqlock);
|
||||
|
||||
/* Remove all buffers from the IRQ queue. */
|
||||
list_for_each_entry(buffer, &histo->irqqueue, queue)
|
||||
@ -152,7 +148,7 @@ static void histo_stop_streaming(struct vb2_queue *vq)
|
||||
/* Wait for the buffer being read out (if any) to complete. */
|
||||
wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock);
|
||||
|
||||
spin_unlock_irqrestore(&histo->irqlock, flags);
|
||||
spin_unlock_irq(&histo->irqlock);
|
||||
}
|
||||
|
||||
static const struct vb2_ops histo_video_queue_qops = {
|
||||
@ -222,9 +218,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev,
|
||||
switch (sel->target) {
|
||||
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
|
||||
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
|
||||
crop = vsp1_entity_get_pad_selection(&histo->entity, state,
|
||||
HISTO_PAD_SINK,
|
||||
V4L2_SEL_TGT_CROP);
|
||||
crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK);
|
||||
sel->r.left = 0;
|
||||
sel->r.top = 0;
|
||||
sel->r.width = crop->width;
|
||||
@ -233,8 +227,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev,
|
||||
|
||||
case V4L2_SEL_TGT_CROP_BOUNDS:
|
||||
case V4L2_SEL_TGT_CROP_DEFAULT:
|
||||
format = vsp1_entity_get_pad_format(&histo->entity, state,
|
||||
HISTO_PAD_SINK);
|
||||
format = v4l2_subdev_state_get_format(state, HISTO_PAD_SINK);
|
||||
sel->r.left = 0;
|
||||
sel->r.top = 0;
|
||||
sel->r.width = format->width;
|
||||
@ -242,9 +235,11 @@ static int histo_get_selection(struct v4l2_subdev *subdev,
|
||||
break;
|
||||
|
||||
case V4L2_SEL_TGT_COMPOSE:
|
||||
sel->r = *v4l2_subdev_state_get_compose(state, sel->pad);
|
||||
break;
|
||||
|
||||
case V4L2_SEL_TGT_CROP:
|
||||
sel->r = *vsp1_entity_get_pad_selection(&histo->entity, state,
|
||||
sel->pad, sel->target);
|
||||
sel->r = *v4l2_subdev_state_get_crop(state, sel->pad);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -261,13 +256,10 @@ static int histo_set_crop(struct v4l2_subdev *subdev,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_selection *sel)
|
||||
{
|
||||
struct vsp1_histogram *histo = subdev_to_histo(subdev);
|
||||
struct v4l2_mbus_framefmt *format;
|
||||
struct v4l2_rect *selection;
|
||||
|
||||
/* The crop rectangle must be inside the input frame. */
|
||||
format = vsp1_entity_get_pad_format(&histo->entity, sd_state,
|
||||
HISTO_PAD_SINK);
|
||||
format = v4l2_subdev_state_get_format(sd_state, HISTO_PAD_SINK);
|
||||
sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
|
||||
sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
|
||||
sel->r.width = clamp_t(unsigned int, sel->r.width, HISTO_MIN_SIZE,
|
||||
@ -276,14 +268,8 @@ static int histo_set_crop(struct v4l2_subdev *subdev,
|
||||
format->height - sel->r.top);
|
||||
|
||||
/* Set the crop rectangle and reset the compose rectangle. */
|
||||
selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state,
|
||||
sel->pad, V4L2_SEL_TGT_CROP);
|
||||
*selection = sel->r;
|
||||
|
||||
selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state,
|
||||
sel->pad,
|
||||
V4L2_SEL_TGT_COMPOSE);
|
||||
*selection = sel->r;
|
||||
*v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r;
|
||||
*v4l2_subdev_state_get_compose(sd_state, sel->pad) = sel->r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -292,7 +278,6 @@ static int histo_set_compose(struct v4l2_subdev *subdev,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_selection *sel)
|
||||
{
|
||||
struct vsp1_histogram *histo = subdev_to_histo(subdev);
|
||||
struct v4l2_rect *compose;
|
||||
struct v4l2_rect *crop;
|
||||
unsigned int ratio;
|
||||
@ -305,9 +290,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev,
|
||||
sel->r.left = 0;
|
||||
sel->r.top = 0;
|
||||
|
||||
crop = vsp1_entity_get_pad_selection(&histo->entity, sd_state,
|
||||
sel->pad,
|
||||
V4L2_SEL_TGT_CROP);
|
||||
crop = v4l2_subdev_state_get_crop(sd_state, sel->pad);
|
||||
|
||||
/*
|
||||
* Clamp the width and height to acceptable values first and then
|
||||
@ -332,9 +315,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev,
|
||||
ratio = 1 << (crop->height * 2 / sel->r.height / 3);
|
||||
sel->r.height = crop->height / ratio;
|
||||
|
||||
compose = vsp1_entity_get_pad_selection(&histo->entity, sd_state,
|
||||
sel->pad,
|
||||
V4L2_SEL_TGT_COMPOSE);
|
||||
compose = v4l2_subdev_state_get_compose(sd_state, sel->pad);
|
||||
*compose = sel->r;
|
||||
|
||||
return 0;
|
||||
@ -371,30 +352,21 @@ static int histo_set_selection(struct v4l2_subdev *subdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int histo_get_format(struct v4l2_subdev *subdev,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_format *fmt)
|
||||
{
|
||||
if (fmt->pad == HISTO_PAD_SOURCE) {
|
||||
fmt->format.code = MEDIA_BUS_FMT_FIXED;
|
||||
fmt->format.width = 0;
|
||||
fmt->format.height = 0;
|
||||
fmt->format.field = V4L2_FIELD_NONE;
|
||||
fmt->format.colorspace = V4L2_COLORSPACE_RAW;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return vsp1_subdev_get_pad_format(subdev, sd_state, fmt);
|
||||
}
|
||||
|
||||
static int histo_set_format(struct v4l2_subdev *subdev,
|
||||
struct v4l2_subdev_state *sd_state,
|
||||
struct v4l2_subdev_format *fmt)
|
||||
{
|
||||
struct vsp1_histogram *histo = subdev_to_histo(subdev);
|
||||
|
||||
if (fmt->pad != HISTO_PAD_SINK)
|
||||
return histo_get_format(subdev, sd_state, fmt);
|
||||
if (fmt->pad == HISTO_PAD_SOURCE) {
|
||||
fmt->format.code = MEDIA_BUS_FMT_FIXED;
|
||||
fmt->format.width = 0;
|
||||
fmt->format.height = 0;
|
||||
fmt->format.field = V4L2_FIELD_NONE;
|
||||
fmt->format.colorspace = V4L2_COLORSPACE_RAW;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return vsp1_subdev_set_pad_format(subdev, sd_state, fmt,
|
||||
histo->formats, histo->num_formats,
|
||||
@ -405,7 +377,7 @@ static int histo_set_format(struct v4l2_subdev *subdev,
|
||||
static const struct v4l2_subdev_pad_ops histo_pad_ops = {
|
||||
.enum_mbus_code = histo_enum_mbus_code,
|
||||
.enum_frame_size = histo_enum_frame_size,
|
||||
.get_fmt = histo_get_format,
|
||||
.get_fmt = vsp1_subdev_get_pad_format,
|
||||
.set_fmt = histo_set_format,
|
||||
.get_selection = histo_get_selection,
|
||||
.set_selection = histo_set_selection,
|
||||
|
@ -78,7 +78,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
|
||||
goto done;
|
||||
}
|
||||
|
||||
format = vsp1_entity_get_pad_format(&hsit->entity, state, fmt->pad);
|
||||
format = v4l2_subdev_state_get_format(state, fmt->pad);
|
||||
|
||||
if (fmt->pad == HSIT_PAD_SOURCE) {
|
||||
/*
|
||||
@ -101,8 +101,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
|
||||
fmt->format = *format;
|
||||
|
||||
/* Propagate the format to the source pad. */
|
||||
format = vsp1_entity_get_pad_format(&hsit->entity, state,
|
||||
HSIT_PAD_SOURCE);
|
||||
format = v4l2_subdev_state_get_format(state, HSIT_PAD_SOURCE);
|
||||
*format = fmt->format;
|
||||
format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32
|
||||
: MEDIA_BUS_FMT_AHSV8888_1X32;
|
||||
@ -128,6 +127,7 @@ static const struct v4l2_subdev_ops hsit_ops = {
|
||||
*/
|
||||
|
||||
static void hsit_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
|
@ -83,6 +83,7 @@ static const struct v4l2_subdev_ops lif_ops = {
|
||||
*/
|
||||
|
||||
static void lif_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
@ -93,8 +94,7 @@ static void lif_configure_stream(struct vsp1_entity *entity,
|
||||
unsigned int obth;
|
||||
unsigned int lbth;
|
||||
|
||||
format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.state,
|
||||
LIF_PAD_SOURCE);
|
||||
format = v4l2_subdev_state_get_format(state, LIF_PAD_SOURCE);
|
||||
|
||||
switch (entity->vsp1->version & VI6_IP_VERSION_MODEL_MASK) {
|
||||
case VI6_IP_VERSION_MODEL_VSPD_GEN2:
|
||||
|
@ -146,6 +146,7 @@ static const struct v4l2_subdev_ops lut_ops = {
|
||||
*/
|
||||
|
||||
static void lut_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
|
@ -301,6 +301,28 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe)
|
||||
pipe->state = VSP1_PIPELINE_STOPPED;
|
||||
}
|
||||
|
||||
void __vsp1_pipeline_dump(struct _ddebug *dbg, struct vsp1_pipeline *pipe,
|
||||
const char *msg)
|
||||
{
|
||||
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
|
||||
struct vsp1_entity *entity;
|
||||
bool first = true;
|
||||
|
||||
printk(KERN_DEBUG "%s: %s: pipe: ", dev_name(vsp1->dev), msg);
|
||||
|
||||
list_for_each_entry(entity, &pipe->entities, list_pipe) {
|
||||
const char *name;
|
||||
|
||||
name = strchrnul(entity->subdev.name, ' ');
|
||||
name = name ? name + 1 : entity->subdev.name;
|
||||
|
||||
pr_cont("%s%s", first ? "" : ", ", name);
|
||||
first = false;
|
||||
}
|
||||
|
||||
pr_cont("\n");
|
||||
}
|
||||
|
||||
/* Must be called with the pipe irqlock held. */
|
||||
void vsp1_pipeline_run(struct vsp1_pipeline *pipe)
|
||||
{
|
||||
@ -444,6 +466,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
|
||||
vsp1_uds_set_alpha(pipe->uds, dlb, alpha);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* VSP1 Partition Algorithm support
|
||||
*/
|
||||
|
||||
/*
|
||||
* Propagate the partition calculations through the pipeline
|
||||
*
|
||||
@ -452,17 +478,82 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
|
||||
* source. Each entity must produce the partition required for the previous
|
||||
* entity in the pipeline.
|
||||
*/
|
||||
void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe,
|
||||
struct vsp1_partition *partition,
|
||||
unsigned int index,
|
||||
struct vsp1_partition_window *window)
|
||||
static void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe,
|
||||
struct vsp1_partition *partition,
|
||||
unsigned int index,
|
||||
struct v4l2_rect *window)
|
||||
{
|
||||
struct vsp1_entity *entity;
|
||||
|
||||
list_for_each_entry_reverse(entity, &pipe->entities, list_pipe) {
|
||||
if (entity->ops->partition)
|
||||
entity->ops->partition(entity, pipe, partition, index,
|
||||
window);
|
||||
entity->ops->partition(entity, entity->state, pipe,
|
||||
partition, index, window);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* vsp1_pipeline_calculate_partition - Calculate pipeline configuration for a
|
||||
* partition
|
||||
*
|
||||
* @pipe: the pipeline
|
||||
* @partition: partition that will hold the calculated values
|
||||
* @div_size: pre-determined maximum partition division size
|
||||
* @index: partition index
|
||||
*/
|
||||
void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe,
|
||||
struct vsp1_partition *partition,
|
||||
unsigned int div_size,
|
||||
unsigned int index)
|
||||
{
|
||||
const struct v4l2_mbus_framefmt *format;
|
||||
struct v4l2_rect window;
|
||||
unsigned int modulus;
|
||||
|
||||
/*
|
||||
* Partitions are computed on the size before rotation, use the format
|
||||
* at the WPF sink.
|
||||
*/
|
||||
format = v4l2_subdev_state_get_format(pipe->output->entity.state,
|
||||
RWPF_PAD_SINK);
|
||||
|
||||
/* Initialise the partition with sane starting conditions. */
|
||||
window.left = index * div_size;
|
||||
window.width = div_size;
|
||||
window.top = 0;
|
||||
window.height = format->height;
|
||||
|
||||
modulus = format->width % div_size;
|
||||
|
||||
/*
|
||||
* We need to prevent the last partition from being smaller than the
|
||||
* *minimum* width of the hardware capabilities.
|
||||
*
|
||||
* If the modulus is less than half of the partition size,
|
||||
* the penultimate partition is reduced to half, which is added
|
||||
* to the final partition: |1234|1234|1234|12|341|
|
||||
* to prevent this: |1234|1234|1234|1234|1|.
|
||||
*/
|
||||
if (modulus) {
|
||||
/*
|
||||
* pipe->partitions is 1 based, whilst index is a 0 based index.
|
||||
* Normalise this locally.
|
||||
*/
|
||||
unsigned int partitions = pipe->partitions - 1;
|
||||
|
||||
if (modulus < div_size / 2) {
|
||||
if (index == partitions - 1) {
|
||||
/* Halve the penultimate partition. */
|
||||
window.width = div_size / 2;
|
||||
} else if (index == partitions) {
|
||||
/* Increase the final partition. */
|
||||
window.width = (div_size / 2) + modulus;
|
||||
window.left -= div_size / 2;
|
||||
}
|
||||
} else if (index == partitions) {
|
||||
window.width = modulus;
|
||||
}
|
||||
}
|
||||
|
||||
vsp1_pipeline_propagate_partition(pipe, partition, index, &window);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#ifndef __VSP1_PIPE_H__
|
||||
#define __VSP1_PIPE_H__
|
||||
|
||||
#include <linux/dynamic_debug.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
@ -53,17 +54,6 @@ enum vsp1_pipeline_state {
|
||||
VSP1_PIPELINE_STOPPING,
|
||||
};
|
||||
|
||||
/*
|
||||
* struct vsp1_partition_window - Partition window coordinates
|
||||
* @left: horizontal coordinate of the partition start in pixels relative to the
|
||||
* left edge of the image
|
||||
* @width: partition width in pixels
|
||||
*/
|
||||
struct vsp1_partition_window {
|
||||
unsigned int left;
|
||||
unsigned int width;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct vsp1_partition - A description of a slice for the partition algorithm
|
||||
* @rpf: The RPF partition window configuration
|
||||
@ -73,11 +63,11 @@ struct vsp1_partition_window {
|
||||
* @wpf: The WPF partition window configuration
|
||||
*/
|
||||
struct vsp1_partition {
|
||||
struct vsp1_partition_window rpf;
|
||||
struct vsp1_partition_window uds_sink;
|
||||
struct vsp1_partition_window uds_source;
|
||||
struct vsp1_partition_window sru;
|
||||
struct vsp1_partition_window wpf;
|
||||
struct v4l2_rect rpf[VSP1_MAX_RPF];
|
||||
struct v4l2_rect uds_sink;
|
||||
struct v4l2_rect uds_source;
|
||||
struct v4l2_rect sru;
|
||||
struct v4l2_rect wpf;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -106,7 +96,6 @@ struct vsp1_partition {
|
||||
* @configured: when false the @stream_config shall be written to the hardware
|
||||
* @interlaced: True when the pipeline is configured in interlaced mode
|
||||
* @partitions: The number of partitions used to process one frame
|
||||
* @partition: The current partition for configuration to process
|
||||
* @part_table: The pre-calculated partitions used by the pipeline
|
||||
*/
|
||||
struct vsp1_pipeline {
|
||||
@ -146,7 +135,6 @@ struct vsp1_pipeline {
|
||||
bool interlaced;
|
||||
|
||||
unsigned int partitions;
|
||||
struct vsp1_partition *partition;
|
||||
struct vsp1_partition *part_table;
|
||||
|
||||
u32 underrun_count;
|
||||
@ -155,6 +143,24 @@ struct vsp1_pipeline {
|
||||
void vsp1_pipeline_reset(struct vsp1_pipeline *pipe);
|
||||
void vsp1_pipeline_init(struct vsp1_pipeline *pipe);
|
||||
|
||||
void __vsp1_pipeline_dump(struct _ddebug *, struct vsp1_pipeline *pipe,
|
||||
const char *msg);
|
||||
|
||||
#if defined(CONFIG_DYNAMIC_DEBUG) || \
|
||||
(defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
|
||||
#define vsp1_pipeline_dump(pipe, msg) \
|
||||
_dynamic_func_call("vsp1_pipeline_dump()", __vsp1_pipeline_dump, pipe, msg)
|
||||
#elif defined(DEBUG)
|
||||
#define vsp1_pipeline_dump(pipe, msg) \
|
||||
__vsp1_pipeline_dump(NULL, pipe, msg)
|
||||
#else
|
||||
#define vsp1_pipeline_dump(pipe, msg) \
|
||||
({ \
|
||||
if (0) \
|
||||
__vsp1_pipeline_dump(NULL, pipe, msg); \
|
||||
})
|
||||
#endif
|
||||
|
||||
void vsp1_pipeline_run(struct vsp1_pipeline *pipe);
|
||||
bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe);
|
||||
int vsp1_pipeline_stop(struct vsp1_pipeline *pipe);
|
||||
@ -166,10 +172,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_body *dlb,
|
||||
unsigned int alpha);
|
||||
|
||||
void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe,
|
||||
void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe,
|
||||
struct vsp1_partition *partition,
|
||||
unsigned int index,
|
||||
struct vsp1_partition_window *window);
|
||||
unsigned int div_size,
|
||||
unsigned int index);
|
||||
|
||||
const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
|
||||
u32 fourcc);
|
||||
|
@ -48,6 +48,7 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf,
|
||||
*/
|
||||
|
||||
static void rpf_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
@ -80,12 +81,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity,
|
||||
vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride);
|
||||
|
||||
/* Format */
|
||||
sink_format = vsp1_entity_get_pad_format(&rpf->entity,
|
||||
rpf->entity.state,
|
||||
RWPF_PAD_SINK);
|
||||
source_format = vsp1_entity_get_pad_format(&rpf->entity,
|
||||
rpf->entity.state,
|
||||
RWPF_PAD_SOURCE);
|
||||
sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
|
||||
source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
|
||||
|
||||
infmt = VI6_RPF_INFMT_CIPM
|
||||
| (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);
|
||||
@ -157,10 +154,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity,
|
||||
if (pipe->brx) {
|
||||
const struct v4l2_rect *compose;
|
||||
|
||||
compose = vsp1_entity_get_pad_selection(pipe->brx,
|
||||
pipe->brx->state,
|
||||
rpf->brx_input,
|
||||
V4L2_SEL_TGT_COMPOSE);
|
||||
compose = v4l2_subdev_state_get_compose(pipe->brx->state,
|
||||
rpf->brx_input);
|
||||
left = compose->left;
|
||||
top = compose->top;
|
||||
}
|
||||
@ -284,6 +279,7 @@ static void rpf_configure_frame(struct vsp1_entity *entity,
|
||||
|
||||
static void rpf_configure_partition(struct vsp1_entity *entity,
|
||||
struct vsp1_pipeline *pipe,
|
||||
const struct vsp1_partition *partition,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
{
|
||||
@ -292,7 +288,7 @@ static void rpf_configure_partition(struct vsp1_entity *entity,
|
||||
struct vsp1_device *vsp1 = rpf->entity.vsp1;
|
||||
const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
|
||||
const struct v4l2_pix_format_mplane *format = &rpf->format;
|
||||
struct v4l2_rect crop;
|
||||
struct v4l2_rect crop = partition->rpf[rpf->entity.index];
|
||||
|
||||
/*
|
||||
* Source size and crop offsets.
|
||||
@ -302,22 +298,6 @@ static void rpf_configure_partition(struct vsp1_entity *entity,
|
||||
* offsets are needed, as planes 2 and 3 always have identical
|
||||
* strides.
|
||||
*/
|
||||
crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.state);
|
||||
|
||||
/*
|
||||
* Partition Algorithm Control
|
||||
*
|
||||
* The partition algorithm can split this frame into multiple
|
||||
* slices. We must scale our partition window based on the pipe
|
||||
* configuration to match the destination partition window.
|
||||
* To achieve this, we adjust our crop to provide a 'sub-crop'
|
||||
* matching the expected partition window. Only 'left' and
|
||||
* 'width' need to be adjusted.
|
||||
*/
|
||||
if (pipe->partitions > 1) {
|
||||
crop.width = pipe->partition->rpf.width;
|
||||
crop.left += pipe->partition->rpf.left;
|
||||
}
|
||||
|
||||
if (pipe->interlaced) {
|
||||
crop.height = round_down(crop.height / 2, fmtinfo->vsub);
|
||||
@ -366,12 +346,30 @@ static void rpf_configure_partition(struct vsp1_entity *entity,
|
||||
}
|
||||
|
||||
static void rpf_partition(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_partition *partition,
|
||||
unsigned int partition_idx,
|
||||
struct vsp1_partition_window *window)
|
||||
struct v4l2_rect *window)
|
||||
{
|
||||
partition->rpf = *window;
|
||||
struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
|
||||
struct v4l2_rect *rpf_rect = &partition->rpf[rpf->entity.index];
|
||||
|
||||
/*
|
||||
* Partition Algorithm Control
|
||||
*
|
||||
* The partition algorithm can split this frame into multiple slices. We
|
||||
* must adjust our partition window based on the pipe configuration to
|
||||
* match the destination partition window. To achieve this, we adjust
|
||||
* our crop to provide a 'sub-crop' matching the expected partition
|
||||
* window.
|
||||
*/
|
||||
*rpf_rect = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
|
||||
|
||||
if (pipe->partitions > 1) {
|
||||
rpf_rect->width = window->width;
|
||||
rpf_rect->left += window->left;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct vsp1_entity_operations rpf_entity_ops = {
|
||||
|
@ -16,12 +16,6 @@
|
||||
#define RWPF_MIN_WIDTH 1
|
||||
#define RWPF_MIN_HEIGHT 1
|
||||
|
||||
struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf,
|
||||
struct v4l2_subdev_state *sd_state)
|
||||
{
|
||||
return v4l2_subdev_state_get_crop(sd_state, RWPF_PAD_SINK);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* V4L2 Subdevice Operations
|
||||
*/
|
||||
@ -79,7 +73,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
|
||||
fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
|
||||
fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
|
||||
|
||||
format = vsp1_entity_get_pad_format(&rwpf->entity, state, fmt->pad);
|
||||
format = v4l2_subdev_state_get_format(state, fmt->pad);
|
||||
|
||||
if (fmt->pad == RWPF_PAD_SOURCE) {
|
||||
/*
|
||||
@ -105,7 +99,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
|
||||
struct v4l2_rect *crop;
|
||||
|
||||
/* Update the sink crop rectangle. */
|
||||
crop = vsp1_rwpf_get_crop(rwpf, state);
|
||||
crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
|
||||
crop->left = 0;
|
||||
crop->top = 0;
|
||||
crop->width = fmt->format.width;
|
||||
@ -113,8 +107,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
|
||||
}
|
||||
|
||||
/* Propagate the format to the source pad. */
|
||||
format = vsp1_entity_get_pad_format(&rwpf->entity, state,
|
||||
RWPF_PAD_SOURCE);
|
||||
format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
|
||||
*format = fmt->format;
|
||||
|
||||
if (rwpf->flip.rotate) {
|
||||
@ -153,12 +146,11 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
|
||||
|
||||
switch (sel->target) {
|
||||
case V4L2_SEL_TGT_CROP:
|
||||
sel->r = *vsp1_rwpf_get_crop(rwpf, state);
|
||||
sel->r = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
|
||||
break;
|
||||
|
||||
case V4L2_SEL_TGT_CROP_BOUNDS:
|
||||
format = vsp1_entity_get_pad_format(&rwpf->entity, state,
|
||||
RWPF_PAD_SINK);
|
||||
format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
|
||||
sel->r.left = 0;
|
||||
sel->r.top = 0;
|
||||
sel->r.width = format->width;
|
||||
@ -204,8 +196,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
|
||||
}
|
||||
|
||||
/* Make sure the crop rectangle is entirely contained in the image. */
|
||||
format = vsp1_entity_get_pad_format(&rwpf->entity, state,
|
||||
RWPF_PAD_SINK);
|
||||
format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
|
||||
|
||||
/*
|
||||
* Restrict the crop rectangle coordinates to multiples of 2 to avoid
|
||||
@ -225,12 +216,11 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
|
||||
sel->r.height = min_t(unsigned int, sel->r.height,
|
||||
format->height - sel->r.top);
|
||||
|
||||
crop = vsp1_rwpf_get_crop(rwpf, state);
|
||||
crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
|
||||
*crop = sel->r;
|
||||
|
||||
/* Propagate the format to the source pad. */
|
||||
format = vsp1_entity_get_pad_format(&rwpf->entity, state,
|
||||
RWPF_PAD_SOURCE);
|
||||
format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
|
||||
format->width = crop->width;
|
||||
format->height = crop->height;
|
||||
|
||||
|
@ -85,7 +85,4 @@ int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols);
|
||||
|
||||
extern const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops;
|
||||
|
||||
struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf,
|
||||
struct v4l2_subdev_state *sd_state);
|
||||
|
||||
#endif /* __VSP1_RWPF_H__ */
|
||||
|
@ -131,7 +131,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev,
|
||||
if (!state)
|
||||
return -EINVAL;
|
||||
|
||||
format = vsp1_entity_get_pad_format(&sru->entity, state, SRU_PAD_SINK);
|
||||
format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
|
||||
|
||||
mutex_lock(&sru->entity.lock);
|
||||
|
||||
@ -184,8 +184,7 @@ static void sru_try_format(struct vsp1_sru *sru,
|
||||
|
||||
case SRU_PAD_SOURCE:
|
||||
/* The SRU can't perform format conversion. */
|
||||
format = vsp1_entity_get_pad_format(&sru->entity, sd_state,
|
||||
SRU_PAD_SINK);
|
||||
format = v4l2_subdev_state_get_format(sd_state, SRU_PAD_SINK);
|
||||
fmt->code = format->code;
|
||||
|
||||
/*
|
||||
@ -234,13 +233,12 @@ static int sru_set_format(struct v4l2_subdev *subdev,
|
||||
|
||||
sru_try_format(sru, state, fmt->pad, &fmt->format);
|
||||
|
||||
format = vsp1_entity_get_pad_format(&sru->entity, state, fmt->pad);
|
||||
format = v4l2_subdev_state_get_format(state, fmt->pad);
|
||||
*format = fmt->format;
|
||||
|
||||
if (fmt->pad == SRU_PAD_SINK) {
|
||||
/* Propagate the format to the source pad. */
|
||||
format = vsp1_entity_get_pad_format(&sru->entity, state,
|
||||
SRU_PAD_SOURCE);
|
||||
format = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
|
||||
*format = fmt->format;
|
||||
|
||||
sru_try_format(sru, state, SRU_PAD_SOURCE, format);
|
||||
@ -267,6 +265,7 @@ static const struct v4l2_subdev_ops sru_ops = {
|
||||
*/
|
||||
|
||||
static void sru_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
@ -277,10 +276,8 @@ static void sru_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_mbus_framefmt *output;
|
||||
u32 ctrl0;
|
||||
|
||||
input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
|
||||
SRU_PAD_SINK);
|
||||
output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
|
||||
SRU_PAD_SOURCE);
|
||||
input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
|
||||
output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
|
||||
|
||||
if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32)
|
||||
ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
|
||||
@ -301,16 +298,14 @@ static void sru_configure_stream(struct vsp1_entity *entity,
|
||||
}
|
||||
|
||||
static unsigned int sru_max_width(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe)
|
||||
{
|
||||
struct vsp1_sru *sru = to_sru(&entity->subdev);
|
||||
struct v4l2_mbus_framefmt *input;
|
||||
struct v4l2_mbus_framefmt *output;
|
||||
|
||||
input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
|
||||
SRU_PAD_SINK);
|
||||
output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
|
||||
SRU_PAD_SOURCE);
|
||||
input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
|
||||
output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
|
||||
|
||||
/*
|
||||
* The maximum input width of the SRU is 288 input pixels, but 32
|
||||
@ -324,24 +319,24 @@ static unsigned int sru_max_width(struct vsp1_entity *entity,
|
||||
}
|
||||
|
||||
static void sru_partition(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_partition *partition,
|
||||
unsigned int partition_idx,
|
||||
struct vsp1_partition_window *window)
|
||||
struct v4l2_rect *window)
|
||||
{
|
||||
struct vsp1_sru *sru = to_sru(&entity->subdev);
|
||||
struct v4l2_mbus_framefmt *input;
|
||||
struct v4l2_mbus_framefmt *output;
|
||||
|
||||
input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
|
||||
SRU_PAD_SINK);
|
||||
output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
|
||||
SRU_PAD_SOURCE);
|
||||
input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
|
||||
output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
|
||||
|
||||
/* Adapt if SRUx2 is enabled. */
|
||||
if (input->width != output->width) {
|
||||
window->width /= 2;
|
||||
window->left /= 2;
|
||||
window->height /= 2;
|
||||
window->top /= 2;
|
||||
}
|
||||
|
||||
partition->sru = *window;
|
||||
|
@ -136,7 +136,7 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev,
|
||||
if (!state)
|
||||
return -EINVAL;
|
||||
|
||||
format = vsp1_entity_get_pad_format(&uds->entity, state, UDS_PAD_SINK);
|
||||
format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);
|
||||
|
||||
mutex_lock(&uds->entity.lock);
|
||||
|
||||
@ -183,8 +183,7 @@ static void uds_try_format(struct vsp1_uds *uds,
|
||||
|
||||
case UDS_PAD_SOURCE:
|
||||
/* The UDS scales but can't perform format conversion. */
|
||||
format = vsp1_entity_get_pad_format(&uds->entity, sd_state,
|
||||
UDS_PAD_SINK);
|
||||
format = v4l2_subdev_state_get_format(sd_state, UDS_PAD_SINK);
|
||||
fmt->code = format->code;
|
||||
|
||||
uds_output_limits(format->width, &minimum, &maximum);
|
||||
@ -217,13 +216,12 @@ static int uds_set_format(struct v4l2_subdev *subdev,
|
||||
|
||||
uds_try_format(uds, state, fmt->pad, &fmt->format);
|
||||
|
||||
format = vsp1_entity_get_pad_format(&uds->entity, state, fmt->pad);
|
||||
format = v4l2_subdev_state_get_format(state, fmt->pad);
|
||||
*format = fmt->format;
|
||||
|
||||
if (fmt->pad == UDS_PAD_SINK) {
|
||||
/* Propagate the format to the source pad. */
|
||||
format = vsp1_entity_get_pad_format(&uds->entity, state,
|
||||
UDS_PAD_SOURCE);
|
||||
format = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);
|
||||
*format = fmt->format;
|
||||
|
||||
uds_try_format(uds, state, UDS_PAD_SOURCE, format);
|
||||
@ -254,6 +252,7 @@ static const struct v4l2_subdev_ops uds_ops = {
|
||||
*/
|
||||
|
||||
static void uds_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
@ -265,10 +264,8 @@ static void uds_configure_stream(struct vsp1_entity *entity,
|
||||
unsigned int vscale;
|
||||
bool multitap;
|
||||
|
||||
input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
|
||||
UDS_PAD_SINK);
|
||||
output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
|
||||
UDS_PAD_SOURCE);
|
||||
input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);
|
||||
output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);
|
||||
|
||||
hscale = uds_compute_ratio(input->width, output->width);
|
||||
vscale = uds_compute_ratio(input->height, output->height);
|
||||
@ -303,15 +300,11 @@ static void uds_configure_stream(struct vsp1_entity *entity,
|
||||
|
||||
static void uds_configure_partition(struct vsp1_entity *entity,
|
||||
struct vsp1_pipeline *pipe,
|
||||
const struct vsp1_partition *partition,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
{
|
||||
struct vsp1_uds *uds = to_uds(&entity->subdev);
|
||||
struct vsp1_partition *partition = pipe->partition;
|
||||
const struct v4l2_mbus_framefmt *output;
|
||||
|
||||
output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
|
||||
UDS_PAD_SOURCE);
|
||||
|
||||
/* Input size clipping. */
|
||||
vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN |
|
||||
@ -323,22 +316,20 @@ static void uds_configure_partition(struct vsp1_entity *entity,
|
||||
vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE,
|
||||
(partition->uds_source.width
|
||||
<< VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
|
||||
(output->height
|
||||
(partition->uds_source.height
|
||||
<< VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
|
||||
}
|
||||
|
||||
static unsigned int uds_max_width(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe)
|
||||
{
|
||||
struct vsp1_uds *uds = to_uds(&entity->subdev);
|
||||
const struct v4l2_mbus_framefmt *output;
|
||||
const struct v4l2_mbus_framefmt *input;
|
||||
unsigned int hscale;
|
||||
|
||||
input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
|
||||
UDS_PAD_SINK);
|
||||
output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
|
||||
UDS_PAD_SOURCE);
|
||||
input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);
|
||||
output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);
|
||||
hscale = output->width / input->width;
|
||||
|
||||
/*
|
||||
@ -364,28 +355,26 @@ static unsigned int uds_max_width(struct vsp1_entity *entity,
|
||||
*/
|
||||
|
||||
static void uds_partition(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_partition *partition,
|
||||
unsigned int partition_idx,
|
||||
struct vsp1_partition_window *window)
|
||||
struct v4l2_rect *window)
|
||||
{
|
||||
struct vsp1_uds *uds = to_uds(&entity->subdev);
|
||||
const struct v4l2_mbus_framefmt *output;
|
||||
const struct v4l2_mbus_framefmt *input;
|
||||
|
||||
/* Initialise the partition state. */
|
||||
partition->uds_sink = *window;
|
||||
partition->uds_source = *window;
|
||||
|
||||
input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
|
||||
UDS_PAD_SINK);
|
||||
output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
|
||||
UDS_PAD_SOURCE);
|
||||
input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);
|
||||
output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);
|
||||
|
||||
partition->uds_sink.width = window->width * input->width
|
||||
/ output->width;
|
||||
partition->uds_sink.left = window->left * input->width
|
||||
/ output->width;
|
||||
partition->uds_sink.height = input->height;
|
||||
partition->uds_sink.top = 0;
|
||||
|
||||
partition->uds_source = *window;
|
||||
|
||||
*window = partition->uds_sink;
|
||||
}
|
||||
|
@ -104,8 +104,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev,
|
||||
switch (sel->target) {
|
||||
case V4L2_SEL_TGT_CROP_BOUNDS:
|
||||
case V4L2_SEL_TGT_CROP_DEFAULT:
|
||||
format = vsp1_entity_get_pad_format(&uif->entity, state,
|
||||
UIF_PAD_SINK);
|
||||
format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK);
|
||||
sel->r.left = 0;
|
||||
sel->r.top = 0;
|
||||
sel->r.width = format->width;
|
||||
@ -113,8 +112,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev,
|
||||
break;
|
||||
|
||||
case V4L2_SEL_TGT_CROP:
|
||||
sel->r = *vsp1_entity_get_pad_selection(&uif->entity, state,
|
||||
sel->pad, sel->target);
|
||||
sel->r = *v4l2_subdev_state_get_crop(state, sel->pad);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -150,7 +148,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev,
|
||||
}
|
||||
|
||||
/* The crop rectangle must be inside the input frame. */
|
||||
format = vsp1_entity_get_pad_format(&uif->entity, state, UIF_PAD_SINK);
|
||||
format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK);
|
||||
|
||||
sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
|
||||
sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
|
||||
@ -160,8 +158,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev,
|
||||
format->height - sel->r.top);
|
||||
|
||||
/* Store the crop rectangle. */
|
||||
selection = vsp1_entity_get_pad_selection(&uif->entity, state,
|
||||
sel->pad, V4L2_SEL_TGT_CROP);
|
||||
selection = v4l2_subdev_state_get_crop(state, sel->pad);
|
||||
*selection = sel->r;
|
||||
|
||||
done:
|
||||
@ -191,6 +188,7 @@ static const struct v4l2_subdev_ops uif_ops = {
|
||||
*/
|
||||
|
||||
static void uif_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
@ -203,8 +201,7 @@ static void uif_configure_stream(struct vsp1_entity *entity,
|
||||
vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR,
|
||||
VI6_UIF_DISCOM_DOCMPMR_SEL(9));
|
||||
|
||||
crop = vsp1_entity_get_pad_selection(entity, entity->state,
|
||||
UIF_PAD_SINK, V4L2_SEL_TGT_CROP);
|
||||
crop = v4l2_subdev_state_get_crop(state, UIF_PAD_SINK);
|
||||
|
||||
left = crop->left;
|
||||
width = crop->width;
|
||||
|
@ -78,8 +78,14 @@ static int vsp1_video_verify_format(struct vsp1_video *video)
|
||||
|
||||
if (video->rwpf->fmtinfo->mbus != fmt.format.code ||
|
||||
video->rwpf->format.height != fmt.format.height ||
|
||||
video->rwpf->format.width != fmt.format.width)
|
||||
return -EINVAL;
|
||||
video->rwpf->format.width != fmt.format.width) {
|
||||
dev_dbg(video->vsp1->dev,
|
||||
"Format mismatch: 0x%04x/%ux%u != 0x%04x/%ux%u\n",
|
||||
video->rwpf->fmtinfo->mbus, video->rwpf->format.width,
|
||||
video->rwpf->format.height, fmt.format.code,
|
||||
fmt.format.width, fmt.format.height);
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -172,131 +178,6 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* VSP1 Partition Algorithm support
|
||||
*/
|
||||
|
||||
/**
|
||||
* vsp1_video_calculate_partition - Calculate the active partition output window
|
||||
*
|
||||
* @pipe: the pipeline
|
||||
* @partition: partition that will hold the calculated values
|
||||
* @div_size: pre-determined maximum partition division size
|
||||
* @index: partition index
|
||||
*/
|
||||
static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe,
|
||||
struct vsp1_partition *partition,
|
||||
unsigned int div_size,
|
||||
unsigned int index)
|
||||
{
|
||||
const struct v4l2_mbus_framefmt *format;
|
||||
struct vsp1_partition_window window;
|
||||
unsigned int modulus;
|
||||
|
||||
/*
|
||||
* Partitions are computed on the size before rotation, use the format
|
||||
* at the WPF sink.
|
||||
*/
|
||||
format = vsp1_entity_get_pad_format(&pipe->output->entity,
|
||||
pipe->output->entity.state,
|
||||
RWPF_PAD_SINK);
|
||||
|
||||
/* A single partition simply processes the output size in full. */
|
||||
if (pipe->partitions <= 1) {
|
||||
window.left = 0;
|
||||
window.width = format->width;
|
||||
|
||||
vsp1_pipeline_propagate_partition(pipe, partition, index,
|
||||
&window);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialise the partition with sane starting conditions. */
|
||||
window.left = index * div_size;
|
||||
window.width = div_size;
|
||||
|
||||
modulus = format->width % div_size;
|
||||
|
||||
/*
|
||||
* We need to prevent the last partition from being smaller than the
|
||||
* *minimum* width of the hardware capabilities.
|
||||
*
|
||||
* If the modulus is less than half of the partition size,
|
||||
* the penultimate partition is reduced to half, which is added
|
||||
* to the final partition: |1234|1234|1234|12|341|
|
||||
* to prevent this: |1234|1234|1234|1234|1|.
|
||||
*/
|
||||
if (modulus) {
|
||||
/*
|
||||
* pipe->partitions is 1 based, whilst index is a 0 based index.
|
||||
* Normalise this locally.
|
||||
*/
|
||||
unsigned int partitions = pipe->partitions - 1;
|
||||
|
||||
if (modulus < div_size / 2) {
|
||||
if (index == partitions - 1) {
|
||||
/* Halve the penultimate partition. */
|
||||
window.width = div_size / 2;
|
||||
} else if (index == partitions) {
|
||||
/* Increase the final partition. */
|
||||
window.width = (div_size / 2) + modulus;
|
||||
window.left -= div_size / 2;
|
||||
}
|
||||
} else if (index == partitions) {
|
||||
window.width = modulus;
|
||||
}
|
||||
}
|
||||
|
||||
vsp1_pipeline_propagate_partition(pipe, partition, index, &window);
|
||||
}
|
||||
|
||||
static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
|
||||
{
|
||||
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
|
||||
const struct v4l2_mbus_framefmt *format;
|
||||
struct vsp1_entity *entity;
|
||||
unsigned int div_size;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* Partitions are computed on the size before rotation, use the format
|
||||
* at the WPF sink.
|
||||
*/
|
||||
format = vsp1_entity_get_pad_format(&pipe->output->entity,
|
||||
pipe->output->entity.state,
|
||||
RWPF_PAD_SINK);
|
||||
div_size = format->width;
|
||||
|
||||
/*
|
||||
* Only Gen3+ hardware requires image partitioning, Gen2 will operate
|
||||
* with a single partition that covers the whole output.
|
||||
*/
|
||||
if (vsp1->info->gen >= 3) {
|
||||
list_for_each_entry(entity, &pipe->entities, list_pipe) {
|
||||
unsigned int entity_max;
|
||||
|
||||
if (!entity->ops->max_width)
|
||||
continue;
|
||||
|
||||
entity_max = entity->ops->max_width(entity, pipe);
|
||||
if (entity_max)
|
||||
div_size = min(div_size, entity_max);
|
||||
}
|
||||
}
|
||||
|
||||
pipe->partitions = DIV_ROUND_UP(format->width, div_size);
|
||||
pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table),
|
||||
GFP_KERNEL);
|
||||
if (!pipe->part_table)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < pipe->partitions; ++i)
|
||||
vsp1_video_calculate_partition(pipe, &pipe->part_table[i],
|
||||
div_size, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Pipeline Management
|
||||
*/
|
||||
@ -365,13 +246,12 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
unsigned int partition)
|
||||
{
|
||||
struct vsp1_partition *part = &pipe->part_table[partition];
|
||||
struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl);
|
||||
struct vsp1_entity *entity;
|
||||
|
||||
pipe->partition = &pipe->part_table[partition];
|
||||
|
||||
list_for_each_entry(entity, &pipe->entities, list_pipe)
|
||||
vsp1_entity_configure_partition(entity, pipe, dl, dlb);
|
||||
vsp1_entity_configure_partition(entity, pipe, part, dl, dlb);
|
||||
}
|
||||
|
||||
static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
|
||||
@ -646,11 +526,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
|
||||
static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe,
|
||||
struct vsp1_video *video)
|
||||
{
|
||||
int ret;
|
||||
|
||||
vsp1_pipeline_init(pipe);
|
||||
|
||||
pipe->frame_end = vsp1_video_pipeline_frame_end;
|
||||
|
||||
return vsp1_video_pipeline_build(pipe, video);
|
||||
ret = vsp1_video_pipeline_build(pipe, video);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vsp1_pipeline_dump(pipe, "video");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video)
|
||||
@ -784,6 +672,54 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb)
|
||||
spin_unlock_irqrestore(&pipe->irqlock, flags);
|
||||
}
|
||||
|
||||
static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
|
||||
{
|
||||
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
|
||||
const struct v4l2_mbus_framefmt *format;
|
||||
struct vsp1_entity *entity;
|
||||
unsigned int div_size;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* Partitions are computed on the size before rotation, use the format
|
||||
* at the WPF sink.
|
||||
*/
|
||||
format = v4l2_subdev_state_get_format(pipe->output->entity.state,
|
||||
RWPF_PAD_SINK);
|
||||
div_size = format->width;
|
||||
|
||||
/*
|
||||
* Only Gen3+ hardware requires image partitioning, Gen2 will operate
|
||||
* with a single partition that covers the whole output.
|
||||
*/
|
||||
if (vsp1->info->gen >= 3) {
|
||||
list_for_each_entry(entity, &pipe->entities, list_pipe) {
|
||||
unsigned int entity_max;
|
||||
|
||||
if (!entity->ops->max_width)
|
||||
continue;
|
||||
|
||||
entity_max = entity->ops->max_width(entity,
|
||||
entity->state,
|
||||
pipe);
|
||||
if (entity_max)
|
||||
div_size = min(div_size, entity_max);
|
||||
}
|
||||
}
|
||||
|
||||
pipe->partitions = DIV_ROUND_UP(format->width, div_size);
|
||||
pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table),
|
||||
GFP_KERNEL);
|
||||
if (!pipe->part_table)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < pipe->partitions; ++i)
|
||||
vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[i],
|
||||
div_size, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
|
||||
{
|
||||
struct vsp1_entity *entity;
|
||||
@ -826,7 +762,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
|
||||
|
||||
list_for_each_entry(entity, &pipe->entities, list_pipe) {
|
||||
vsp1_entity_route_setup(entity, pipe, pipe->stream_config);
|
||||
vsp1_entity_configure_stream(entity, pipe, NULL,
|
||||
vsp1_entity_configure_stream(entity, entity->state, pipe, NULL,
|
||||
pipe->stream_config);
|
||||
}
|
||||
|
||||
|
@ -65,12 +65,10 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation)
|
||||
goto done;
|
||||
}
|
||||
|
||||
sink_format = vsp1_entity_get_pad_format(&wpf->entity,
|
||||
wpf->entity.state,
|
||||
RWPF_PAD_SINK);
|
||||
source_format = vsp1_entity_get_pad_format(&wpf->entity,
|
||||
wpf->entity.state,
|
||||
RWPF_PAD_SOURCE);
|
||||
sink_format = v4l2_subdev_state_get_format(wpf->entity.state,
|
||||
RWPF_PAD_SINK);
|
||||
source_format = v4l2_subdev_state_get_format(wpf->entity.state,
|
||||
RWPF_PAD_SOURCE);
|
||||
|
||||
mutex_lock(&wpf->entity.lock);
|
||||
|
||||
@ -231,6 +229,7 @@ static int wpf_configure_writeback_chain(struct vsp1_rwpf *wpf,
|
||||
}
|
||||
|
||||
static void wpf_configure_stream(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
@ -245,12 +244,8 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
|
||||
u32 srcrpf = 0;
|
||||
int ret;
|
||||
|
||||
sink_format = vsp1_entity_get_pad_format(&wpf->entity,
|
||||
wpf->entity.state,
|
||||
RWPF_PAD_SINK);
|
||||
source_format = vsp1_entity_get_pad_format(&wpf->entity,
|
||||
wpf->entity.state,
|
||||
RWPF_PAD_SOURCE);
|
||||
sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
|
||||
source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
|
||||
|
||||
/* Format */
|
||||
if (!pipe->lif || wpf->writeback) {
|
||||
@ -367,13 +362,13 @@ static void wpf_configure_frame(struct vsp1_entity *entity,
|
||||
|
||||
static void wpf_configure_partition(struct vsp1_entity *entity,
|
||||
struct vsp1_pipeline *pipe,
|
||||
const struct vsp1_partition *partition,
|
||||
struct vsp1_dl_list *dl,
|
||||
struct vsp1_dl_body *dlb)
|
||||
{
|
||||
struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
|
||||
struct vsp1_device *vsp1 = wpf->entity.vsp1;
|
||||
struct vsp1_rwpf_memory mem = wpf->mem;
|
||||
const struct v4l2_mbus_framefmt *sink_format;
|
||||
const struct v4l2_pix_format_mplane *format = &wpf->format;
|
||||
const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
|
||||
unsigned int width;
|
||||
@ -383,21 +378,13 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
|
||||
unsigned int flip;
|
||||
unsigned int i;
|
||||
|
||||
sink_format = vsp1_entity_get_pad_format(&wpf->entity,
|
||||
wpf->entity.state,
|
||||
RWPF_PAD_SINK);
|
||||
width = sink_format->width;
|
||||
height = sink_format->height;
|
||||
left = 0;
|
||||
|
||||
/*
|
||||
* Cropping. The partition algorithm can split the image into
|
||||
* multiple slices.
|
||||
* Cropping. The partition algorithm can split the image into multiple
|
||||
* slices.
|
||||
*/
|
||||
if (pipe->partitions > 1) {
|
||||
width = pipe->partition->wpf.width;
|
||||
left = pipe->partition->wpf.left;
|
||||
}
|
||||
width = partition->wpf.width;
|
||||
left = partition->wpf.left;
|
||||
height = partition->wpf.height;
|
||||
|
||||
vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
|
||||
(0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
|
||||
@ -508,6 +495,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
|
||||
}
|
||||
|
||||
static unsigned int wpf_max_width(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe)
|
||||
{
|
||||
struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
|
||||
@ -516,10 +504,11 @@ static unsigned int wpf_max_width(struct vsp1_entity *entity,
|
||||
}
|
||||
|
||||
static void wpf_partition(struct vsp1_entity *entity,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct vsp1_pipeline *pipe,
|
||||
struct vsp1_partition *partition,
|
||||
unsigned int partition_idx,
|
||||
struct vsp1_partition_window *window)
|
||||
struct v4l2_rect *window)
|
||||
{
|
||||
partition->wpf = *window;
|
||||
}
|
||||
|
@ -1269,14 +1269,6 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream,
|
||||
struct v4l2_subdev *sd;
|
||||
int ret;
|
||||
|
||||
if (!is_media_entity_v4l2_subdev(pad->entity)) {
|
||||
WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L,
|
||||
"Driver bug! Wrong media entity type 0x%08x, entity %s\n",
|
||||
pad->entity->function, pad->entity->name);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sd = media_entity_to_v4l2_subdev(pad->entity);
|
||||
|
||||
fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
||||
|
Loading…
Reference in New Issue
Block a user