From 5acc3e22c25359f41ae18d3f8c63e20b6e4f74a8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:30 +0100 Subject: media: ti-vpe: cal: Create subdev for CAMERARX Create and register V4L2 subdevs for the CAMERARX instances, and link them in the media graph to the sensors and video nodes. The subdev API is not exposed to userspace at this point, and no subdev operation is implemented, but the media controller graph is visible to applications. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-camerarx.c | 42 ++++++++++++++++++++++++++-- drivers/media/platform/ti-vpe/cal-video.c | 12 ++++++++ drivers/media/platform/ti-vpe/cal.c | 35 ++++++++++++++++++----- drivers/media/platform/ti-vpe/cal.h | 9 +++++- 4 files changed, 88 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index 806cbf175d39..1c9ce30eb7af 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -533,8 +533,8 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, static int cal_camerarx_parse_dt(struct cal_camerarx *phy) { struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; - struct device_node *ep_node; char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; + struct device_node *ep_node; unsigned int i; int ret; @@ -582,9 +582,11 @@ static int cal_camerarx_parse_dt(struct cal_camerarx *phy) endpoint->bus.mipi_csi2.flags); /* Retrieve the connected device and store it for later use. */ - phy->sensor_node = of_graph_get_remote_port_parent(ep_node); + phy->sensor_ep_node = of_graph_get_remote_endpoint(ep_node); + phy->sensor_node = of_graph_get_port_parent(phy->sensor_ep_node); if (!phy->sensor_node) { phy_dbg(3, phy, "Can't get remote parent\n"); + of_node_put(phy->sensor_ep_node); ret = -EINVAL; goto done; } @@ -596,11 +598,25 @@ done: return ret; } +/* ------------------------------------------------------------------ + * V4L2 Subdev Operations + * ------------------------------------------------------------------ + */ + +static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { +}; + +/* ------------------------------------------------------------------ + * Create and Destroy + * ------------------------------------------------------------------ + */ + struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, unsigned int instance) { struct platform_device *pdev = to_platform_device(cal->dev); struct cal_camerarx *phy; + struct v4l2_subdev *sd; int ret; phy = kzalloc(sizeof(*phy), GFP_KERNEL); @@ -632,9 +648,28 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, if (ret) goto error; + /* Initialize the V4L2 subdev and media entity. */ + sd = &phy->subdev; + v4l2_subdev_init(sd, &cal_camerarx_subdev_ops); + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance); + sd->dev = cal->dev; + + phy->pads[CAL_CAMERARX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + phy->pads[CAL_CAMERARX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads), + phy->pads); + if (ret) + goto error; + + ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd); + if (ret) + goto error; + return phy; error: + media_entity_cleanup(&phy->subdev.entity); kfree(phy); return ERR_PTR(ret); } @@ -644,6 +679,9 @@ void cal_camerarx_destroy(struct cal_camerarx *phy) if (!phy) return; + v4l2_device_unregister_subdev(&phy->subdev); + media_entity_cleanup(&phy->subdev.entity); + of_node_put(phy->sensor_ep_node); of_node_put(phy->sensor_node); kfree(phy); } diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index df472a175e83..0a1a11692208 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -809,6 +809,18 @@ int cal_ctx_v4l2_register(struct cal_ctx *ctx) return ret; } + ret = media_create_pad_link(&ctx->phy->subdev.entity, + CAL_CAMERARX_PAD_SOURCE, + &vfd->entity, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) { + ctx_err(ctx, "Failed to create media link for context %u\n", + ctx->index); + video_unregister_device(vfd); + return ret; + } + ctx_info(ctx, "V4L2 device registered as %s\n", video_device_node_name(vfd)); diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 59a0266b1f39..f6e42b2c022a 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -421,6 +421,8 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { struct cal_camerarx *phy = to_cal_asd(asd)->phy; + int pad; + int ret; if (phy->sensor) { phy_info(phy, "Rejecting subdev %s (Already set!!)", @@ -431,6 +433,25 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, phy->sensor = subdev; phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name); + pad = media_entity_get_fwnode_pad(&subdev->entity, + of_fwnode_handle(phy->sensor_ep_node), + MEDIA_PAD_FL_SOURCE); + if (pad < 0) { + phy_err(phy, "Sensor %s has no connected source pad\n", + subdev->name); + return pad; + } + + ret = media_create_pad_link(&subdev->entity, pad, + &phy->subdev.entity, CAL_CAMERARX_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) { + phy_err(phy, "Failed to create media link for sensor %s\n", + subdev->name); + return ret; + } + return 0; } @@ -797,6 +818,11 @@ static int cal_probe(struct platform_device *pdev) cal_get_hwinfo(cal); pm_runtime_put_sync(&pdev->dev); + /* Initialize the media device. */ + ret = cal_media_init(cal); + if (ret < 0) + goto error_pm_runtime; + /* Create CAMERARX PHYs. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { cal->phy[i] = cal_camerarx_create(cal, i); @@ -816,11 +842,6 @@ static int cal_probe(struct platform_device *pdev) goto error_camerarx; } - /* Initialize the media device. */ - ret = cal_media_init(cal); - if (ret < 0) - goto error_camerarx; - /* Create contexts. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { if (!cal->phy[i]->sensor_node) @@ -848,12 +869,12 @@ error_context: cal_ctx_v4l2_cleanup(ctx); } - cal_media_cleanup(cal); - error_camerarx: for (i = 0; i < ARRAY_SIZE(cal->phy); i++) cal_camerarx_destroy(cal->phy[i]); + cal_media_cleanup(cal); + error_pm_runtime: pm_runtime_disable(&pdev->dev); diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 4123405ee0cf..9bb6cc1bdbcc 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #define CAL_MODULE_NAME "cal" @@ -33,12 +34,14 @@ #define MAX_WIDTH_BYTES (8192 * 8) #define MAX_HEIGHT_LINES 16383 +#define CAL_CAMERARX_PAD_SINK 0 +#define CAL_CAMERARX_PAD_SOURCE 1 + struct device; struct device_node; struct resource; struct regmap; struct regmap_fied; -struct v4l2_subdev; /* CTRL_CORE_CAMERRX_CONTROL register field id */ enum cal_camerarx_field { @@ -108,8 +111,12 @@ struct cal_camerarx { unsigned int instance; struct v4l2_fwnode_endpoint endpoint; + struct device_node *sensor_ep_node; struct device_node *sensor_node; struct v4l2_subdev *sensor; + + struct v4l2_subdev subdev; + struct media_pad pads[2]; }; struct cal_dev { -- cgit v1.2.3 From 9ccd0021d0264874f26aeeb315a94149869c81cd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:31 +0100 Subject: media: ti-vpe: cal: Drop cal_ctx m_fmt field The struct cal_ctx m_fmt field stores the media bus format for the context input. Only the format 'field' field is used, store it in the video format instead, and drop m_fmt. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 10 +++++----- drivers/media/platform/ti-vpe/cal.c | 2 +- drivers/media/platform/ti-vpe/cal.h | 4 +--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 0a1a11692208..f57767e79ca5 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -387,10 +387,10 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv, v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + ctx->v_fmt.fmt.pix.field = mbus_fmt.field; cal_calc_format_size(ctx, fmt, &ctx->v_fmt); ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; *f = ctx->v_fmt; return 0; @@ -775,13 +775,13 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) return -EINVAL; } - /* Save current subdev format */ + /* Save current format */ v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + ctx->v_fmt.fmt.pix.field = mbus_fmt.field; cal_calc_format_size(ctx, fmt, &ctx->v_fmt); ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; return 0; } diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index f6e42b2c022a..34a344b7f08a 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -314,7 +314,7 @@ static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) static inline void cal_process_buffer_complete(struct cal_ctx *ctx) { ctx->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns(); - ctx->cur_frm->vb.field = ctx->m_fmt.field; + ctx->cur_frm->vb.field = ctx->v_fmt.fmt.pix.field; ctx->cur_frm->vb.sequence = ctx->sequence++; vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 9bb6cc1bdbcc..955dacd878e7 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -164,9 +164,7 @@ struct cal_ctx { /* video capture */ const struct cal_fmt *fmt; /* Used to store current pixel format */ - struct v4l2_format v_fmt; - /* Used to store current mbus frame format */ - struct v4l2_mbus_framefmt m_fmt; + struct v4l2_format v_fmt; /* Current subdev enumerated format */ const struct cal_fmt **active_fmt; -- cgit v1.2.3 From 7168155002cf7aadbfaa14a28f037c880a214764 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:32 +0100 Subject: media: ti-vpe: cal: Move format handling to cal.c and expose helpers The cal_formats array contain the description of all formats supported by the hardware. It's currently used by the V4L2 video device operations only, but will be needed by the CAMERARX subdev code too. Move it from cal-video.c to cal.c and add helper functions to access it. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 105 +----------------------- drivers/media/platform/ti-vpe/cal.c | 127 ++++++++++++++++++++++++++++++ drivers/media/platform/ti-vpe/cal.h | 5 ++ 3 files changed, 134 insertions(+), 103 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index f57767e79ca5..355bb365daf0 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -26,107 +26,6 @@ #include "cal.h" -/* ------------------------------------------------------------------ - * Format Handling - * ------------------------------------------------------------------ - */ - -static const struct cal_fmt cal_formats[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .bpp = 32, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .bpp = 12, - }, -}; - /* Print Four-character-code (FOURCC) */ static char *fourcc_to_str(u32 fmt) { @@ -726,7 +625,7 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) int ret = 0; /* Enumerate sub device formats and enable all matching local formats */ - ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), + ctx->active_fmt = devm_kcalloc(ctx->cal->dev, cal_num_formats, sizeof(*ctx->active_fmt), GFP_KERNEL); ctx->num_active_fmt = 0; @@ -744,7 +643,7 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) "subdev %s: code: %04x idx: %u\n", ctx->phy->sensor->name, mbus_code.code, j); - for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { + for (k = 0; k < cal_num_formats; k++) { const struct cal_fmt *fmt = &cal_formats[k]; if (mbus_code.code == fmt->code) { diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 34a344b7f08a..b9ee535513e8 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -43,6 +43,133 @@ unsigned int cal_debug; module_param_named(debug, cal_debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); +/* ------------------------------------------------------------------ + * Format Handling + * ------------------------------------------------------------------ + */ + +const struct cal_fmt cal_formats[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_2X12_LE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_RGB888_2X12_BE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .bpp = 32, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .bpp = 12, + }, +}; + +const unsigned int cal_num_formats = ARRAY_SIZE(cal_formats); + +const struct cal_fmt *cal_format_by_fourcc(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) { + if (cal_formats[i].fourcc == fourcc) + return &cal_formats[i]; + } + + return NULL; +} + +const struct cal_fmt *cal_format_by_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) { + if (cal_formats[i].code == code) + return &cal_formats[i]; + } + + return NULL; +} + /* ------------------------------------------------------------------ * Platform Data * ------------------------------------------------------------------ diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 955dacd878e7..6e6bdf8af3d0 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -244,6 +244,11 @@ static inline void cal_set_field(u32 *valp, u32 field, u32 mask) *valp = val; } +extern const struct cal_fmt cal_formats[]; +extern const unsigned int cal_num_formats; +const struct cal_fmt *cal_format_by_fourcc(u32 fourcc); +const struct cal_fmt *cal_format_by_code(u32 code); + void cal_quickdump_regs(struct cal_dev *cal); void cal_camerarx_disable(struct cal_camerarx *phy); -- cgit v1.2.3 From 811cb526f314cd023e37b906d45cbe90ad34271a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:33 +0100 Subject: media: ti-vpe: cal: Rename MAX_(WIDTH|HEIGHT)_* macros with CAL_ prefix The MAX_WIDTH_BYTES and MAX_HEIGHT_LINES macros have a generic name that is prone to namespace clashes. Rename them with a CAL_ prefix. While at it, expand their documentation and add CAL_MIN_(WIDTH|HEIGHT)_* macros that will be used to implement CAMERARX subdev operations. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 5 +++-- drivers/media/platform/ti-vpe/cal.h | 12 ++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 355bb365daf0..42e750925e8b 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -163,9 +163,10 @@ static int cal_calc_format_size(struct cal_ctx *ctx, * We need to recalculate the actual maxi width depending on the * number of bytes per pixels required. */ - max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); + max_width = CAL_MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, - &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); + &f->fmt.pix.height, 32, CAL_MAX_HEIGHT_LINES, + 0, 0); bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; f->fmt.pix.bytesperline = ALIGN(bpl, 16); diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 6e6bdf8af3d0..d42d381d928f 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -31,8 +31,16 @@ #define CAL_NUM_CONTEXT 2 #define CAL_NUM_CSI2_PORTS 2 -#define MAX_WIDTH_BYTES (8192 * 8) -#define MAX_HEIGHT_LINES 16383 +/* + * The width is limited by the size of the CAL_WR_DMA_XSIZE_j.XSIZE field, + * expressed in multiples of 64 bits. The height is limited by the size of the + * CAL_CSI2_CTXi_j.CTXi_LINES and CAL_WR_DMA_CTRL_j.YSIZE fields, expressed in + * lines. + */ +#define CAL_MIN_WIDTH_BYTES 16 +#define CAL_MAX_WIDTH_BYTES (8192 * 8) +#define CAL_MIN_HEIGHT_LINES 1 +#define CAL_MAX_HEIGHT_LINES 16383 #define CAL_CAMERARX_PAD_SINK 0 #define CAL_CAMERARX_PAD_SOURCE 1 -- cgit v1.2.3 From 695baaa373abf407214b117c0f24e43307388ac4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:34 +0100 Subject: media: ti-vpe: cal: Replace hardcoded BIT() value with macro Replace the hardcoded BIT(3) value with CAL_CSI2_PPI_CTRL_FRAME_MASK to increase readability. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-camerarx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index 1c9ce30eb7af..6af09d4c0049 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -485,7 +485,8 @@ void cal_camerarx_disable_irqs(struct cal_camerarx *phy) void cal_camerarx_ppi_enable(struct cal_camerarx *phy) { - cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); + cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + CAL_CSI2_PPI_CTRL_FRAME_MASK); cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } -- cgit v1.2.3 From 71c1f16ddd528db3955414316ba2ce2730ff7455 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:35 +0100 Subject: media: ti-vpe: cal: Iterate over correct number of CAMERARX instances When performing operations on all CAMERARX instances, code usually iterates over all cal->phy[] entries and skips the NULL pointers. The number of available CAMERARX instances is however available through cal->data->num_csi2_phy. Use it instead. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index b9ee535513e8..5d073d9cd8b5 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -263,12 +263,9 @@ void cal_quickdump_regs(struct cal_dev *cal) (__force const void *)cal->base, resource_size(cal->res), false); - for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + for (i = 0; i < cal->data->num_csi2_phy; ++i) { struct cal_camerarx *phy = cal->phy[i]; - if (!phy) - continue; - cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i, &phy->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, @@ -464,7 +461,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) if (status & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); - for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { + for (i = 0; i < cal->data->num_csi2_phy; ++i) { if (status & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = cal_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); @@ -608,13 +605,13 @@ static int cal_async_notifier_register(struct cal_dev *cal) v4l2_async_notifier_init(&cal->notifier); cal->notifier.ops = &cal_async_notifier_ops; - for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + for (i = 0; i < cal->data->num_csi2_phy; ++i) { struct cal_camerarx *phy = cal->phy[i]; struct cal_v4l2_async_subdev *casd; struct v4l2_async_subdev *asd; struct fwnode_handle *fwnode; - if (!phy || !phy->sensor_node) + if (!phy->sensor_node) continue; fwnode = of_fwnode_handle(phy->sensor_node); @@ -997,7 +994,7 @@ error_context: } error_camerarx: - for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + for (i = 0; i < cal->data->num_csi2_phy; i++) cal_camerarx_destroy(cal->phy[i]); cal_media_cleanup(cal); @@ -1026,7 +1023,7 @@ static int cal_remove(struct platform_device *pdev) cal_media_cleanup(cal); - for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + for (i = 0; i < cal->data->num_csi2_phy; i++) cal_camerarx_destroy(cal->phy[i]); pm_runtime_put_sync(&pdev->dev); @@ -1038,14 +1035,15 @@ static int cal_remove(struct platform_device *pdev) static int cal_runtime_resume(struct device *dev) { struct cal_dev *cal = dev_get_drvdata(dev); + unsigned int i; if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { /* * Apply errata on both port everytime we (re-)enable * the clock */ - cal_camerarx_i913_errata(cal->phy[0]); - cal_camerarx_i913_errata(cal->phy[1]); + for (i = 0; i < cal->data->num_csi2_phy; i++) + cal_camerarx_i913_errata(cal->phy[i]); } return 0; -- cgit v1.2.3 From 8d6637f1087611a1408ae1c183c93e18b7fd9534 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:36 +0100 Subject: media: ti-vpe: cal: Implement subdev ops for CAMERARX Implement subdev operations for the CAMERARX. They will be used to replace calls to custom CAMERARX functions in the V4L2 video device code, and will be exposed to userspace. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-camerarx.c | 205 +++++++++++++++++++++++++++ drivers/media/platform/ti-vpe/cal.h | 2 + 2 files changed, 207 insertions(+) diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index 6af09d4c0049..ca941e3e72e9 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -604,7 +604,209 @@ done: * ------------------------------------------------------------------ */ +static inline struct cal_camerarx *to_cal_camerarx(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cal_camerarx, subdev); +} + +static struct v4l2_mbus_framefmt * +cal_camerarx_get_pad_format(struct cal_camerarx *phy, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&phy->subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &phy->formats[pad]; + default: + return NULL; + } +} + +static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + + if (enable) + return cal_camerarx_start(phy, NULL); + + cal_camerarx_stop(phy); + return 0; +} + +static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + + /* No transcoding, source and sink codes must match. */ + if (code->pad == CAL_CAMERARX_PAD_SOURCE) { + struct v4l2_mbus_framefmt *fmt; + + if (code->index > 0) + return -EINVAL; + + fmt = cal_camerarx_get_pad_format(phy, cfg, + CAL_CAMERARX_PAD_SINK, + code->which); + code->code = fmt->code; + return 0; + } + + if (code->index >= cal_num_formats) + return -EINVAL; + + code->code = cal_formats[code->index].code; + + return 0; +} + +static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + const struct cal_fmt *fmtinfo; + + if (fse->index > 0) + return -EINVAL; + + /* No transcoding, source and sink formats must match. */ + if (fse->pad == CAL_CAMERARX_PAD_SOURCE) { + struct v4l2_mbus_framefmt *fmt; + + fmt = cal_camerarx_get_pad_format(phy, cfg, + CAL_CAMERARX_PAD_SINK, + fse->which); + if (fse->code != fmt->code) + return -EINVAL; + + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = fmt->height; + fse->max_height = fmt->height; + + return 0; + } + + fmtinfo = cal_format_by_code(fse->code); + if (!fmtinfo) + return -EINVAL; + + fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->min_height = CAL_MIN_HEIGHT_LINES; + fse->max_height = CAL_MAX_HEIGHT_LINES; + + return 0; +} + +static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + struct v4l2_mbus_framefmt *fmt; + + fmt = cal_camerarx_get_pad_format(phy, cfg, format->pad, format->which); + format->format = *fmt; + + return 0; +} + +static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + const struct cal_fmt *fmtinfo; + struct v4l2_mbus_framefmt *fmt; + unsigned int bpp; + + /* No transcoding, source and sink formats must match. */ + if (format->pad == CAL_CAMERARX_PAD_SOURCE) + return cal_camerarx_sd_get_fmt(sd, cfg, format); + + /* + * Default to the first format is the requested media bus code isn't + * supported. + */ + fmtinfo = cal_format_by_code(format->format.code); + if (!fmtinfo) + fmtinfo = &cal_formats[0]; + + /* + * Clamp the size, update the code. The field and colorspace are + * accepted as-is. + */ + bpp = ALIGN(fmtinfo->bpp, 8); + + format->format.width = clamp_t(unsigned int, format->format.width, + CAL_MIN_WIDTH_BYTES * 8 / bpp, + CAL_MAX_WIDTH_BYTES * 8 / bpp); + format->format.height = clamp_t(unsigned int, format->format.height, + CAL_MIN_HEIGHT_LINES, + CAL_MAX_HEIGHT_LINES); + format->format.code = fmtinfo->code; + + /* Store the format and propagate it to the source pad. */ + fmt = cal_camerarx_get_pad_format(phy, cfg, CAL_CAMERARX_PAD_SINK, + format->which); + *fmt = format->format; + + fmt = cal_camerarx_get_pad_format(phy, cfg, CAL_CAMERARX_PAD_SOURCE, + format->which); + *fmt = format->format; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + phy->fmtinfo = fmtinfo; + + return 0; +} + +static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format format = { + .which = cfg ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = CAL_CAMERARX_PAD_SINK, + .format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }, + }; + + return cal_camerarx_sd_set_fmt(sd, cfg, &format); +} + +static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = { + .s_stream = cal_camerarx_sd_s_stream, +}; + +static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = { + .init_cfg = cal_camerarx_sd_init_cfg, + .enum_mbus_code = cal_camerarx_sd_enum_mbus_code, + .enum_frame_size = cal_camerarx_sd_enum_frame_size, + .get_fmt = cal_camerarx_sd_get_fmt, + .set_fmt = cal_camerarx_sd_set_fmt, +}; + static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { + .video = &cal_camerarx_video_ops, + .pad = &cal_camerarx_pad_ops, +}; + +struct media_entity_operations cal_camerarx_media_ops = { + .link_validate = v4l2_subdev_link_validate, }; /* ------------------------------------------------------------------ @@ -658,11 +860,14 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, phy->pads[CAL_CAMERARX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; phy->pads[CAL_CAMERARX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + sd->entity.ops = &cal_camerarx_media_ops; ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads), phy->pads); if (ret) goto error; + cal_camerarx_sd_init_cfg(sd, NULL); + ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd); if (ret) goto error; diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index d42d381d928f..241edd8b7536 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -125,6 +125,8 @@ struct cal_camerarx { struct v4l2_subdev subdev; struct media_pad pads[2]; + struct v4l2_mbus_framefmt formats[2]; + const struct cal_fmt *fmtinfo; }; struct cal_dev { -- cgit v1.2.3 From cc548febd2c9b3571e28d906d2355f67547e2805 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:37 +0100 Subject: media: ti-vpe: cal: Use CAMERARX subdev s_stream op in video device code Replace calls to cal_camerarx_start() and cal_camerarx_stop() with usage of the .s_stream() subdev operation. This requires calling the .set_fmt() operation as the CAMERARX now relies on the format information set through the subdev API instead of receiving it in the cal_camerarx_start() function. This change prepare for exposing the CAMERARX subdev operations to userspace by using the same API within the kernel. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-camerarx.c | 15 +++++++-------- drivers/media/platform/ti-vpe/cal-video.c | 25 ++++++++++++++----------- drivers/media/platform/ti-vpe/cal.h | 2 -- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index ca941e3e72e9..584dc548504d 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -116,8 +116,7 @@ void cal_camerarx_disable(struct cal_camerarx *phy) #define TCLK_MISS 1 #define TCLK_SETTLE 14 -static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, - const struct cal_fmt *fmt) +static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate) { unsigned int reg0, reg1; unsigned int ths_term, ths_settle; @@ -132,9 +131,9 @@ static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, * CSI-2 is DDR and we only count used lanes. * * csi2_ddrclk_khz = external_rate / 1000 - * / (2 * num_lanes) * fmt->bpp; + * / (2 * num_lanes) * phy->fmtinfo->bpp; */ - csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, + csi2_ddrclk_khz = div_s64(external_rate * phy->fmtinfo->bpp, 2 * num_lanes * 1000); phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); @@ -234,7 +233,7 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) phy_err(phy, "Timeout waiting for stop state\n"); } -int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) +static int cal_camerarx_start(struct cal_camerarx *phy) { s64 external_rate; u32 sscounter; @@ -289,7 +288,7 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) camerarx_read(phy, CAL_CSI2_PHY_REG0); /* Program the PHY timing parameters. */ - cal_camerarx_config(phy, external_rate, fmt); + cal_camerarx_config(phy, external_rate); /* * b. Assert the FORCERXMODE signal. @@ -362,7 +361,7 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) return 0; } -void cal_camerarx_stop(struct cal_camerarx *phy) +static void cal_camerarx_stop(struct cal_camerarx *phy) { unsigned int i; int ret; @@ -629,7 +628,7 @@ static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable) struct cal_camerarx *phy = to_cal_camerarx(sd); if (enable) - return cal_camerarx_start(phy, NULL); + return cal_camerarx_start(phy); cal_camerarx_stop(phy); return 0; diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 42e750925e8b..1ada27d42da1 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -256,8 +256,11 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv, { struct cal_ctx *ctx = video_drvdata(file); struct vb2_queue *q = &ctx->vb_vidq; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = CAL_CAMERARX_PAD_SINK, + }; const struct cal_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; int ret; if (vb2_is_busy(q)) { @@ -271,25 +274,28 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv, fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); + v4l2_fill_mbus_format(&sd_fmt.format, &f->fmt.pix, fmt->code); - ret = __subdev_set_format(ctx, &mbus_fmt); + ret = __subdev_set_format(ctx, &sd_fmt.format); if (ret) return ret; /* Just double check nothing has gone wrong */ - if (mbus_fmt.code != fmt->code) { + if (sd_fmt.format.code != fmt->code) { ctx_dbg(3, ctx, "%s subdev changed format on us, this should not happen\n", __func__); return -EINVAL; } - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &sd_fmt.format); ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - ctx->v_fmt.fmt.pix.field = mbus_fmt.field; + ctx->v_fmt.fmt.pix.field = sd_fmt.format.field; cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + + v4l2_subdev_call(&ctx->phy->subdev, pad, set_fmt, NULL, &sd_fmt); + ctx->fmt = fmt; *f = ctx->v_fmt; @@ -455,9 +461,6 @@ static int cal_buffer_prepare(struct vb2_buffer *vb) vb.vb2_buf); unsigned long size; - if (WARN_ON(!ctx->fmt)) - return -EINVAL; - size = ctx->v_fmt.fmt.pix.sizeimage; if (vb2_plane_size(vb, 0) < size) { ctx_err(ctx, @@ -518,7 +521,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) cal_camerarx_enable_irqs(ctx->phy); - ret = cal_camerarx_start(ctx->phy, ctx->fmt); + ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1); if (ret) goto err; @@ -569,7 +572,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) ctx_err(ctx, "failed to disable dma cleanly\n"); cal_camerarx_disable_irqs(ctx->phy); - cal_camerarx_stop(ctx->phy); + v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0); /* Release all active buffers */ spin_lock_irqsave(&ctx->slock, flags); diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 241edd8b7536..45f65ebf7aa9 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -262,8 +262,6 @@ const struct cal_fmt *cal_format_by_code(u32 code); void cal_quickdump_regs(struct cal_dev *cal); void cal_camerarx_disable(struct cal_camerarx *phy); -int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt); -void cal_camerarx_stop(struct cal_camerarx *phy); void cal_camerarx_enable_irqs(struct cal_camerarx *phy); void cal_camerarx_disable_irqs(struct cal_camerarx *phy); void cal_camerarx_ppi_enable(struct cal_camerarx *phy); -- cgit v1.2.3 From b496dc9071a0d798657cf3c018725fc099f22d5d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:38 +0100 Subject: media: ti-vpe: cal: Don't pass format to cal_ctx_wr_dma_config() The cal_ctx_wr_dma_config() function has access to the context, there's no need to give it format-related values retrieved from the context. Access the values internally, and reword internal comments. The comment regarding the CAL_WR_DMA_OFST register not being well understood is dropped, as the datasheet explicitly documents it as "Offset between two consecutive line starts". Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 3 +-- drivers/media/platform/ti-vpe/cal.c | 25 ++++++++++--------------- drivers/media/platform/ti-vpe/cal.h | 3 +-- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 1ada27d42da1..083389635269 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -516,8 +516,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) cal_ctx_csi2_config(ctx); cal_ctx_pix_proc_config(ctx); - cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, - ctx->v_fmt.fmt.pix.height); + cal_ctx_wr_dma_config(ctx); cal_camerarx_enable_irqs(ctx->phy); diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 5d073d9cd8b5..4c938ca1f1a9 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -356,14 +356,15 @@ void cal_ctx_pix_proc_config(struct cal_ctx *ctx) cal_read(ctx->cal, CAL_PIX_PROC(ctx->index))); } -void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, - unsigned int height) +void cal_ctx_wr_dma_config(struct cal_ctx *ctx) { + unsigned int stride = ctx->v_fmt.fmt.pix.bytesperline; u32 val; val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); - cal_set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); + cal_set_field(&val, ctx->v_fmt.fmt.pix.height, + CAL_WR_DMA_CTRL_YSIZE_MASK); cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, CAL_WR_DMA_CTRL_DTAG_MASK); cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, @@ -375,14 +376,8 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index, cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); - /* - * width/16 not sure but giving it a whirl. - * zero does not work right - */ - cal_write_field(ctx->cal, - CAL_WR_DMA_OFST(ctx->index), - (width / 16), - CAL_WR_DMA_OFST_MASK); + cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->index), + stride / 16, CAL_WR_DMA_OFST_MASK); ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index, cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); @@ -390,11 +385,11 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, /* 64 bit word means no skipping */ cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); /* - * (width*8)/64 this should be size of an entire line - * in 64bit word but 0 means all data until the end - * is detected automagically + * The XSIZE field is expressed in 64-bit units and prevents overflows + * in case of synchronization issues by limiting the number of bytes + * written per line. */ - cal_set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); + cal_set_field(&val, stride / 8, CAL_WR_DMA_XSIZE_MASK); cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index, cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 45f65ebf7aa9..d8e7f0c586f6 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -273,8 +273,7 @@ void cal_camerarx_destroy(struct cal_camerarx *phy); void cal_ctx_csi2_config(struct cal_ctx *ctx); void cal_ctx_pix_proc_config(struct cal_ctx *ctx); -void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, - unsigned int height); +void cal_ctx_wr_dma_config(struct cal_ctx *ctx); void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr); int cal_ctx_v4l2_register(struct cal_ctx *ctx); -- cgit v1.2.3 From 51e8c97df58fcfbf9725506939c00652bf63d813 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:39 +0100 Subject: media: ti-vpe: cal: Rename struct cal_fmt to cal_format_info The cal_fmt structure stores information about a format. Its name is ambiguous, as it could be understood as storing a format. Furthermore, instances of the structure are called 'fmt' through the code, which leads to confusion with the 'format' variables. Rename the structure to cal_format_info, and the corresponding 'fmt' variables to 'fmtinfo'. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-camerarx.c | 4 +- drivers/media/platform/ti-vpe/cal-video.c | 104 +++++++++++++-------------- drivers/media/platform/ti-vpe/cal.c | 10 +-- drivers/media/platform/ti-vpe/cal.h | 14 ++-- 4 files changed, 66 insertions(+), 66 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index 584dc548504d..fd37ce209461 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -667,7 +667,7 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_frame_size_enum *fse) { struct cal_camerarx *phy = to_cal_camerarx(sd); - const struct cal_fmt *fmtinfo; + const struct cal_format_info *fmtinfo; if (fse->index > 0) return -EINVAL; @@ -720,7 +720,7 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct cal_camerarx *phy = to_cal_camerarx(sd); - const struct cal_fmt *fmtinfo; + const struct cal_format_info *fmtinfo; struct v4l2_mbus_framefmt *fmt; unsigned int bpp; diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 083389635269..e7ad0b93fc59 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -45,31 +45,31 @@ static char *fourcc_to_str(u32 fmt) * ------------------------------------------------------------------ */ -static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, - u32 pixelformat) +static const struct cal_format_info *find_format_by_pix(struct cal_ctx *ctx, + u32 pixelformat) { - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; unsigned int k; for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->fourcc == pixelformat) - return fmt; + fmtinfo = ctx->active_fmt[k]; + if (fmtinfo->fourcc == pixelformat) + return fmtinfo; } return NULL; } -static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, - u32 code) +static const struct cal_format_info *find_format_by_code(struct cal_ctx *ctx, + u32 code) { - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; unsigned int k; for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->code == code) - return fmt; + fmtinfo = ctx->active_fmt[k]; + if (fmtinfo->code == code) + return fmtinfo; } return NULL; @@ -92,14 +92,14 @@ static int cal_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; if (f->index >= ctx->num_active_fmt) return -EINVAL; - fmt = ctx->active_fmt[f->index]; + fmtinfo = ctx->active_fmt[f->index]; - f->pixelformat = fmt->fourcc; + f->pixelformat = fmtinfo->fourcc; f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; return 0; } @@ -148,12 +148,12 @@ static int __subdev_set_format(struct cal_ctx *ctx, } static int cal_calc_format_size(struct cal_ctx *ctx, - const struct cal_fmt *fmt, + const struct cal_format_info *fmtinfo, struct v4l2_format *f) { u32 bpl, max_width; - if (!fmt) { + if (!fmtinfo) { ctx_dbg(3, ctx, "No cal_fmt provided!\n"); return -EINVAL; } @@ -163,12 +163,12 @@ static int cal_calc_format_size(struct cal_ctx *ctx, * We need to recalculate the actual maxi width depending on the * number of bytes per pixels required. */ - max_width = CAL_MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); + max_width = CAL_MAX_WIDTH_BYTES / (ALIGN(fmtinfo->bpp, 8) >> 3); v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, &f->fmt.pix.height, 32, CAL_MAX_HEIGHT_LINES, 0, 0); - bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; + bpl = (f->fmt.pix.width * ALIGN(fmtinfo->bpp, 8)) >> 3; f->fmt.pix.bytesperline = ALIGN(bpl, 16); f->fmt.pix.sizeimage = f->fmt.pix.height * @@ -196,18 +196,18 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_size_enum fse; int ret, found; - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - if (!fmt) { + fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + if (!fmtinfo) { ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", f->fmt.pix.pixelformat); /* Just get the first one enumerated */ - fmt = ctx->active_fmt[0]; - f->fmt.pix.pixelformat = fmt->fourcc; + fmtinfo = ctx->active_fmt[0]; + f->fmt.pix.pixelformat = fmtinfo->fourcc; } f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; @@ -216,7 +216,7 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv, ret = 0; found = false; fse.pad = 0; - fse.code = fmt->code; + fse.code = fmtinfo->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; for (fse.index = 0; ; fse.index++) { ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, @@ -248,7 +248,7 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv, * updated properly during s_fmt */ f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; - return cal_calc_format_size(ctx, fmt, f); + return cal_calc_format_size(ctx, fmtinfo, f); } static int cal_s_fmt_vid_cap(struct file *file, void *priv, @@ -260,7 +260,7 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv, .which = V4L2_SUBDEV_FORMAT_ACTIVE, .pad = CAL_CAMERARX_PAD_SINK, }; - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; int ret; if (vb2_is_busy(q)) { @@ -272,16 +272,16 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv, if (ret < 0) return ret; - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - v4l2_fill_mbus_format(&sd_fmt.format, &f->fmt.pix, fmt->code); + v4l2_fill_mbus_format(&sd_fmt.format, &f->fmt.pix, fmtinfo->code); ret = __subdev_set_format(ctx, &sd_fmt.format); if (ret) return ret; /* Just double check nothing has gone wrong */ - if (sd_fmt.format.code != fmt->code) { + if (sd_fmt.format.code != fmtinfo->code) { ctx_dbg(3, ctx, "%s subdev changed format on us, this should not happen\n", __func__); @@ -290,13 +290,13 @@ static int cal_s_fmt_vid_cap(struct file *file, void *priv, v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &sd_fmt.format); ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + ctx->v_fmt.fmt.pix.pixelformat = fmtinfo->fourcc; ctx->v_fmt.fmt.pix.field = sd_fmt.format.field; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt); v4l2_subdev_call(&ctx->phy->subdev, pad, set_fmt, NULL, &sd_fmt); - ctx->fmt = fmt; + ctx->fmtinfo = fmtinfo; *f = ctx->v_fmt; return 0; @@ -306,13 +306,13 @@ static int cal_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_size_enum fse; int ret; /* check for valid format */ - fmt = find_format_by_pix(ctx, fsize->pixel_format); - if (!fmt) { + fmtinfo = find_format_by_pix(ctx, fsize->pixel_format); + if (!fmtinfo) { ctx_dbg(3, ctx, "Invalid pixel code: %x\n", fsize->pixel_format); return -EINVAL; @@ -320,7 +320,7 @@ static int cal_enum_framesizes(struct file *file, void *fh, fse.index = fsize->index; fse.pad = 0; - fse.code = fmt->code; + fse.code = fmtinfo->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, @@ -366,7 +366,7 @@ static int cal_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fival) { struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_interval_enum fie = { .index = fival->index, .width = fival->width, @@ -375,11 +375,11 @@ static int cal_enum_frameintervals(struct file *file, void *priv, }; int ret; - fmt = find_format_by_pix(ctx, fival->pixel_format); - if (!fmt) + fmtinfo = find_format_by_pix(ctx, fival->pixel_format); + if (!fmtinfo) return -EINVAL; - fie.code = fmt->code; + fie.code = fmtinfo->code; ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, NULL, &fie); if (ret) @@ -623,7 +623,7 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) { struct v4l2_subdev_mbus_code_enum mbus_code; struct v4l2_mbus_framefmt mbus_fmt; - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; unsigned int i, j, k; int ret = 0; @@ -647,14 +647,14 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) ctx->phy->sensor->name, mbus_code.code, j); for (k = 0; k < cal_num_formats; k++) { - const struct cal_fmt *fmt = &cal_formats[k]; + fmtinfo = &cal_formats[k]; - if (mbus_code.code == fmt->code) { - ctx->active_fmt[i] = fmt; + if (mbus_code.code == fmtinfo->code) { + ctx->active_fmt[i] = fmtinfo; ctx_dbg(2, ctx, "matched fourcc: %s: code: %04x idx: %u\n", - fourcc_to_str(fmt->fourcc), - fmt->code, i); + fourcc_to_str(fmtinfo->fourcc), + fmtinfo->code, i); ctx->num_active_fmt = ++i; } } @@ -670,8 +670,8 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) if (ret) return ret; - fmt = find_format_by_code(ctx, mbus_fmt.code); - if (!fmt) { + fmtinfo = find_format_by_code(ctx, mbus_fmt.code); + if (!fmtinfo) { ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", mbus_fmt.code); return -EINVAL; @@ -680,10 +680,10 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) /* Save current format */ v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + ctx->v_fmt.fmt.pix.pixelformat = fmtinfo->fourcc; ctx->v_fmt.fmt.pix.field = mbus_fmt.field; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; + cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt); + ctx->fmtinfo = fmtinfo; return 0; } diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 4c938ca1f1a9..213381b8ddfa 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -48,7 +48,7 @@ MODULE_PARM_DESC(debug, "activates debug info"); * ------------------------------------------------------------------ */ -const struct cal_fmt cal_formats[] = { +const struct cal_format_info cal_formats[] = { { .fourcc = V4L2_PIX_FMT_YUYV, .code = MEDIA_BUS_FMT_YUYV8_2X8, @@ -146,7 +146,7 @@ const struct cal_fmt cal_formats[] = { const unsigned int cal_num_formats = ARRAY_SIZE(cal_formats); -const struct cal_fmt *cal_format_by_fourcc(u32 fourcc) +const struct cal_format_info *cal_format_by_fourcc(u32 fourcc) { unsigned int i; @@ -158,7 +158,7 @@ const struct cal_fmt *cal_format_by_fourcc(u32 fourcc) return NULL; } -const struct cal_fmt *cal_format_by_code(u32 code) +const struct cal_format_info *cal_format_by_code(u32 code) { unsigned int i; @@ -309,7 +309,7 @@ void cal_ctx_pix_proc_config(struct cal_ctx *ctx) { u32 val, extract, pack; - switch (ctx->fmt->bpp) { + switch (ctx->fmtinfo->bpp) { case 8: extract = CAL_PIX_PROC_EXTRACT_B8; pack = CAL_PIX_PROC_PACK_B8; @@ -338,7 +338,7 @@ void cal_ctx_pix_proc_config(struct cal_ctx *ctx) */ dev_warn_once(ctx->cal->dev, "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", - __FILE__, __LINE__, __func__, ctx->fmt->bpp); + __FILE__, __LINE__, __func__, ctx->fmtinfo->bpp); extract = CAL_PIX_PROC_EXTRACT_B8; pack = CAL_PIX_PROC_PACK_B8; break; diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index d8e7f0c586f6..be8224942120 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -60,7 +60,7 @@ enum cal_camerarx_field { F_MAX_FIELDS, }; -struct cal_fmt { +struct cal_format_info { u32 fourcc; u32 code; /* Bits per pixel */ @@ -126,7 +126,7 @@ struct cal_camerarx { struct v4l2_subdev subdev; struct media_pad pads[2]; struct v4l2_mbus_framefmt formats[2]; - const struct cal_fmt *fmtinfo; + const struct cal_format_info *fmtinfo; }; struct cal_dev { @@ -172,12 +172,12 @@ struct cal_ctx { struct cal_dmaqueue vidq; /* video capture */ - const struct cal_fmt *fmt; + const struct cal_format_info *fmtinfo; /* Used to store current pixel format */ struct v4l2_format v_fmt; /* Current subdev enumerated format */ - const struct cal_fmt **active_fmt; + const struct cal_format_info **active_fmt; unsigned int num_active_fmt; unsigned int sequence; @@ -254,10 +254,10 @@ static inline void cal_set_field(u32 *valp, u32 field, u32 mask) *valp = val; } -extern const struct cal_fmt cal_formats[]; +extern const struct cal_format_info cal_formats[]; extern const unsigned int cal_num_formats; -const struct cal_fmt *cal_format_by_fourcc(u32 fourcc); -const struct cal_fmt *cal_format_by_code(u32 code); +const struct cal_format_info *cal_format_by_fourcc(u32 fourcc); +const struct cal_format_info *cal_format_by_code(u32 code); void cal_quickdump_regs(struct cal_dev *cal); -- cgit v1.2.3 From 75c80311eda70e2bbd592b04d5836abe7a73c103 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:40 +0100 Subject: media: ti-vpe: cal: Refactor interrupt enable/disable Interrupts are enabled and disabled by the cal_camerarx_enable_irqs() and cal_camerarx_disable_irqs(). Despite their name, they deal with all interrupts, not just the CAMERARX interrupts, and they hardcode the assumption that the context index is identical to the CAMERARX index. Split the context-related interrupt management to two new functions, cal_ctx_enable_irqs() and cal_ctx_disable_irqs(), called from the cal_start_streaming() and cal_stop_streaming() functions. The explicit calls to cal_camerarx_enable_irqs() and cal_camerarx_disable_irqs() are folded with the CAMERARX .s_stream() operation to simplify the CAMERARX API. Enabling the OCPO error interrupt is moved to the PM runtime resume operation, as it's global to the device, not related to a CAMERARX or context. The VC IRQ enable and disable are removed as they're not used, the parent interrupt bit (CAL_HL_IRQ_VC_MASK) never being set. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-camerarx.c | 83 ++++++++++------------------ drivers/media/platform/ti-vpe/cal-video.c | 6 +- drivers/media/platform/ti-vpe/cal.c | 24 ++++++++ drivers/media/platform/ti-vpe/cal.h | 4 +- 4 files changed, 57 insertions(+), 60 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index fd37ce209461..ce46046c9ba4 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -233,6 +233,29 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) phy_err(phy, "Timeout waiting for stop state\n"); } +static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +{ + const u32 cio_err_mask = + CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | + CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | + CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | + CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; + + /* Enable CIO error IRQs. */ + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cio_err_mask); +} + +static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) +{ + /* Disable CIO error irqs */ + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); +} + static int cal_camerarx_start(struct cal_camerarx *phy) { s64 external_rate; @@ -250,6 +273,8 @@ static int cal_camerarx_start(struct cal_camerarx *phy) return ret; } + cal_camerarx_enable_irqs(phy); + /* * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / @@ -339,6 +364,7 @@ static int cal_camerarx_start(struct cal_camerarx *phy) ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); if (ret) { v4l2_subdev_call(phy->sensor, core, s_power, 0); + cal_camerarx_disable_irqs(phy); phy_err(phy, "stream on failed in subdev\n"); return ret; } @@ -366,6 +392,8 @@ static void cal_camerarx_stop(struct cal_camerarx *phy) unsigned int i; int ret; + cal_camerarx_disable_irqs(phy); + cal_camerarx_power(phy, false); /* Assert Complex IO Reset */ @@ -427,61 +455,6 @@ void cal_camerarx_i913_errata(struct cal_camerarx *phy) camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); } -/* - * Enable the expected IRQ sources - */ -void cal_camerarx_enable_irqs(struct cal_camerarx *phy) -{ - u32 val; - - const u32 cio_err_mask = - CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | - CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | - CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | - CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; - - /* Enable CIO error irqs */ - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), - cio_err_mask); - - /* Always enable OCPO error */ - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); - - /* Enable IRQ_WDMA_END 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); - /* Enable IRQ_WDMA_START 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); -} - -void cal_camerarx_disable_irqs(struct cal_camerarx *phy) -{ - u32 val; - - /* Disable CIO error irqs */ - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); - - /* Disable IRQ_WDMA_END 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); - /* Disable IRQ_WDMA_START 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); -} - void cal_camerarx_ppi_enable(struct cal_camerarx *phy) { cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index e7ad0b93fc59..3807d91f0392 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -517,8 +517,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) cal_ctx_csi2_config(ctx); cal_ctx_pix_proc_config(ctx); cal_ctx_wr_dma_config(ctx); - - cal_camerarx_enable_irqs(ctx->phy); + cal_ctx_enable_irqs(ctx); ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1); if (ret) @@ -570,7 +569,8 @@ static void cal_stop_streaming(struct vb2_queue *vq) if (dma_act) ctx_err(ctx, "failed to disable dma cleanly\n"); - cal_camerarx_disable_irqs(ctx->phy); + cal_ctx_disable_irqs(ctx); + v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0); /* Release all active buffers */ diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 213381b8ddfa..785ce4171d40 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -411,6 +411,24 @@ void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); } +void cal_ctx_enable_irqs(struct cal_ctx *ctx) +{ + /* Enable IRQ_WDMA_END and IRQ_WDMA_START. */ + cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(1), + CAL_HL_IRQ_MASK(ctx->index)); + cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(2), + CAL_HL_IRQ_MASK(ctx->index)); +} + +void cal_ctx_disable_irqs(struct cal_ctx *ctx) +{ + /* Disable IRQ_WDMA_END and IRQ_WDMA_START. */ + cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1), + CAL_HL_IRQ_MASK(ctx->index)); + cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2), + CAL_HL_IRQ_MASK(ctx->index)); +} + /* ------------------------------------------------------------------ * IRQ Handling * ------------------------------------------------------------------ @@ -1041,6 +1059,12 @@ static int cal_runtime_resume(struct device *dev) cal_camerarx_i913_errata(cal->phy[i]); } + /* + * Enable global interrupts that are not related to a particular + * CAMERARAX or context. + */ + cal_write(cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + return 0; } diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index be8224942120..1c6fbee211d2 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -262,8 +262,6 @@ const struct cal_format_info *cal_format_by_code(u32 code); void cal_quickdump_regs(struct cal_dev *cal); void cal_camerarx_disable(struct cal_camerarx *phy); -void cal_camerarx_enable_irqs(struct cal_camerarx *phy); -void cal_camerarx_disable_irqs(struct cal_camerarx *phy); void cal_camerarx_ppi_enable(struct cal_camerarx *phy); void cal_camerarx_ppi_disable(struct cal_camerarx *phy); void cal_camerarx_i913_errata(struct cal_camerarx *phy); @@ -275,6 +273,8 @@ void cal_ctx_csi2_config(struct cal_ctx *ctx); void cal_ctx_pix_proc_config(struct cal_ctx *ctx); void cal_ctx_wr_dma_config(struct cal_ctx *ctx); void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr); +void cal_ctx_enable_irqs(struct cal_ctx *ctx); +void cal_ctx_disable_irqs(struct cal_ctx *ctx); int cal_ctx_v4l2_register(struct cal_ctx *ctx); void cal_ctx_v4l2_unregister(struct cal_ctx *ctx); -- cgit v1.2.3 From 172ba79dafa0fab92b646671ddae54ae46e9d354 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:41 +0100 Subject: media: ti-vpe: cal: Fold PPI enable in CAMERARX .s_stream() To further decouple the context and CAMERARX components, move the call to cal_camerarx_ppi_enable() from cal_start_streaming() to the CAMERARX .s_stream() operation. The DMA destination address has to be set before starting the CAMERARX, which is desirable anyway. cal_camerarx_ppi_disable() will be addressed separately. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-camerarx.c | 31 +++++++++++++++------------- drivers/media/platform/ti-vpe/cal-video.c | 4 +--- drivers/media/platform/ti-vpe/cal.h | 1 - 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index ce46046c9ba4..941efa99e3b5 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -256,6 +256,20 @@ static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); } +static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) +{ + cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + CAL_CSI2_PPI_CTRL_FRAME_MASK); + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +void cal_camerarx_ppi_disable(struct cal_camerarx *phy) +{ + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + static int cal_camerarx_start(struct cal_camerarx *phy) { s64 external_rate; @@ -384,6 +398,9 @@ static int cal_camerarx_start(struct cal_camerarx *phy) * implemented. */ + /* Finally, enable the PHY Protocol Interface (PPI). */ + cal_camerarx_ppi_enable(phy); + return 0; } @@ -455,20 +472,6 @@ void cal_camerarx_i913_errata(struct cal_camerarx *phy) camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); } -void cal_camerarx_ppi_enable(struct cal_camerarx *phy) -{ - cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - CAL_CSI2_PPI_CTRL_FRAME_MASK); - cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - -void cal_camerarx_ppi_disable(struct cal_camerarx *phy) -{ - cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - static int cal_camerarx_regmap_init(struct cal_dev *cal, struct cal_camerarx *phy) { diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 3807d91f0392..627d816548b8 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -517,15 +517,13 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) cal_ctx_csi2_config(ctx); cal_ctx_pix_proc_config(ctx); cal_ctx_wr_dma_config(ctx); + cal_ctx_wr_dma_addr(ctx, addr); cal_ctx_enable_irqs(ctx); ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1); if (ret) goto err; - cal_ctx_wr_dma_addr(ctx, addr); - cal_camerarx_ppi_enable(ctx->phy); - if (cal_debug >= 4) cal_quickdump_regs(ctx->cal); diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 1c6fbee211d2..501700fc4955 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -262,7 +262,6 @@ const struct cal_format_info *cal_format_by_code(u32 code); void cal_quickdump_regs(struct cal_dev *cal); void cal_camerarx_disable(struct cal_camerarx *phy); -void cal_camerarx_ppi_enable(struct cal_camerarx *phy); void cal_camerarx_ppi_disable(struct cal_camerarx *phy); void cal_camerarx_i913_errata(struct cal_camerarx *phy); struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, -- cgit v1.2.3 From cbb8cd7cc129f2920a3edf686bf2d7961cd1b00c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:42 +0100 Subject: media: ti-vpe: cal: Stop write DMA without disabling PPI When stopping the stream, the driver needs to ensure that ongoing DMA completes and that no new DMA is started. It does so using a feature of the PPI that can be stopped on a frame boundary. The downside of this mechanism is that the DMA can't be stopped independently of the source, which prevents usage of multiple contexts for the same source (to handle CSI-2 virtual channels or data types). Rework the stream stop mechanism to stop the write DMA without disabling the PPI first. The new mechanism relies on the combination of a state machine in the driver and shadowing of the CAL_WR_DMA_CTRL_j.MODE field in the hardware. When a stop is requested, the DMA start interrupt handler will request the hardware to stop at the end of the current frame by disabling the write DMA context in the shadowed register, and flag that a stop is in progress. The next DMA end interrupt will then report that the stop is complete. This makes it possible to stop the PPI after stopping the DMA, and fold the cal_camerarx_ppi_disable() call into cal_camerarx_stop(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-camerarx.c | 6 +- drivers/media/platform/ti-vpe/cal-video.c | 26 ++---- drivers/media/platform/ti-vpe/cal.c | 133 +++++++++++++++++++-------- drivers/media/platform/ti-vpe/cal.h | 14 ++- 4 files changed, 117 insertions(+), 62 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index 941efa99e3b5..1920f36137b8 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -258,13 +258,11 @@ static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) { - cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - CAL_CSI2_PPI_CTRL_FRAME_MASK); cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } -void cal_camerarx_ppi_disable(struct cal_camerarx *phy) +static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) { cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); @@ -409,6 +407,8 @@ static void cal_camerarx_stop(struct cal_camerarx *phy) unsigned int i; int ret; + cal_camerarx_ppi_disable(phy); + cal_camerarx_disable_irqs(phy); cal_camerarx_power(phy, false); diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 627d816548b8..d3f805a512c0 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -9,7 +9,6 @@ * Laurent Pinchart */ -#include #include #include #include @@ -511,6 +510,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); ctx->sequence = 0; + ctx->dma_state = CAL_DMA_RUNNING; pm_runtime_get_sync(ctx->cal->dev); @@ -530,6 +530,10 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; err: + cal_ctx_wr_dma_disable(ctx); + cal_ctx_disable_irqs(ctx); + ctx->dma_state = CAL_DMA_STOPPED; + spin_lock_irqsave(&ctx->slock, flags); vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); ctx->cur_frm = NULL; @@ -547,26 +551,9 @@ static void cal_stop_streaming(struct vb2_queue *vq) struct cal_ctx *ctx = vb2_get_drv_priv(vq); struct cal_dmaqueue *dma_q = &ctx->vidq; struct cal_buffer *buf, *tmp; - unsigned long timeout; unsigned long flags; - bool dma_act; - - cal_camerarx_ppi_disable(ctx->phy); - - /* wait for stream and dma to finish */ - dma_act = true; - timeout = jiffies + msecs_to_jiffies(500); - while (dma_act && time_before(jiffies, timeout)) { - msleep(50); - - spin_lock_irqsave(&ctx->slock, flags); - dma_act = ctx->dma_act; - spin_unlock_irqrestore(&ctx->slock, flags); - } - - if (dma_act) - ctx_err(ctx, "failed to disable dma cleanly\n"); + cal_ctx_wr_dma_stop(ctx); cal_ctx_disable_irqs(ctx); v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0); @@ -745,6 +732,7 @@ int cal_ctx_v4l2_init(struct cal_ctx *ctx) INIT_LIST_HEAD(&ctx->vidq.active); spin_lock_init(&ctx->slock); mutex_init(&ctx->mutex); + init_waitqueue_head(&ctx->dma_wait); /* Initialize the vb2 queue. */ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 785ce4171d40..f1c2b8bc28f4 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -411,6 +411,45 @@ void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); } +void cal_ctx_wr_dma_disable(struct cal_ctx *ctx) +{ + u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); + + cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_DIS, + CAL_WR_DMA_CTRL_MODE_MASK); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); +} + +static bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx) +{ + bool stopped; + + spin_lock_irq(&ctx->slock); + stopped = ctx->dma_state == CAL_DMA_STOPPED; + spin_unlock_irq(&ctx->slock); + + return stopped; +} + +int cal_ctx_wr_dma_stop(struct cal_ctx *ctx) +{ + long timeout; + + /* Request DMA stop and wait until it completes. */ + spin_lock_irq(&ctx->slock); + ctx->dma_state = CAL_DMA_STOP_REQUESTED; + spin_unlock_irq(&ctx->slock); + + timeout = wait_event_timeout(ctx->dma_wait, cal_ctx_wr_dma_stopped(ctx), + msecs_to_jiffies(500)); + if (!timeout) { + ctx_err(ctx, "failed to disable dma cleanly\n"); + return -ETIMEDOUT; + } + + return 0; +} + void cal_ctx_enable_irqs(struct cal_ctx *ctx) { /* Enable IRQ_WDMA_END and IRQ_WDMA_START. */ @@ -434,35 +473,71 @@ void cal_ctx_disable_irqs(struct cal_ctx *ctx) * ------------------------------------------------------------------ */ -static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) +static inline void cal_irq_wdma_start(struct cal_ctx *ctx) { struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf; - unsigned long addr; - buf = list_entry(dma_q->active.next, struct cal_buffer, list); - ctx->next_frm = buf; - list_del(&buf->list); + spin_lock(&ctx->slock); + + if (ctx->dma_state == CAL_DMA_STOP_REQUESTED) { + /* + * If a stop is requested, disable the write DMA context + * immediately. The CAL_WR_DMA_CTRL_j.MODE field is shadowed, + * the current frame will complete and the DMA will then stop. + */ + cal_ctx_wr_dma_disable(ctx); + ctx->dma_state = CAL_DMA_STOP_PENDING; + } else if (!list_empty(&dma_q->active) && + ctx->cur_frm == ctx->next_frm) { + /* + * Otherwise, if a new buffer is available, queue it to the + * hardware. + */ + struct cal_buffer *buf; + unsigned long addr; + + buf = list_entry(dma_q->active.next, struct cal_buffer, list); + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + cal_ctx_wr_dma_addr(ctx, addr); + + ctx->next_frm = buf; + list_del(&buf->list); + } - addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - cal_ctx_wr_dma_addr(ctx, addr); + spin_unlock(&ctx->slock); } -static inline void cal_process_buffer_complete(struct cal_ctx *ctx) +static inline void cal_irq_wdma_end(struct cal_ctx *ctx) { - ctx->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns(); - ctx->cur_frm->vb.field = ctx->v_fmt.fmt.pix.field; - ctx->cur_frm->vb.sequence = ctx->sequence++; + struct cal_buffer *buf = NULL; + + spin_lock(&ctx->slock); - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); - ctx->cur_frm = ctx->next_frm; + /* If the DMA context was stopping, it is now stopped. */ + if (ctx->dma_state == CAL_DMA_STOP_PENDING) { + ctx->dma_state = CAL_DMA_STOPPED; + wake_up(&ctx->dma_wait); + } + + /* If a new buffer was queued, complete the current buffer. */ + if (ctx->cur_frm != ctx->next_frm) { + buf = ctx->cur_frm; + ctx->cur_frm = ctx->next_frm; + } + + spin_unlock(&ctx->slock); + + if (buf) { + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + buf->vb.field = ctx->v_fmt.fmt.pix.field; + buf->vb.sequence = ctx->sequence++; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + } } static irqreturn_t cal_irq(int irq_cal, void *data) { struct cal_dev *cal = data; - struct cal_ctx *ctx; - struct cal_dmaqueue *dma_q; u32 status; status = cal_read(cal, CAL_HL_IRQSTATUS(0)); @@ -497,17 +572,8 @@ static irqreturn_t cal_irq(int irq_cal, void *data) cal_write(cal, CAL_HL_IRQSTATUS(1), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (status & CAL_HL_IRQ_MASK(i)) { - ctx = cal->ctx[i]; - - spin_lock(&ctx->slock); - ctx->dma_act = false; - - if (ctx->cur_frm != ctx->next_frm) - cal_process_buffer_complete(ctx); - - spin_unlock(&ctx->slock); - } + if (status & CAL_HL_IRQ_MASK(i)) + cal_irq_wdma_end(cal->ctx[i]); } } @@ -520,17 +586,8 @@ static irqreturn_t cal_irq(int irq_cal, void *data) cal_write(cal, CAL_HL_IRQSTATUS(2), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (status & CAL_HL_IRQ_MASK(i)) { - ctx = cal->ctx[i]; - dma_q = &ctx->vidq; - - spin_lock(&ctx->slock); - ctx->dma_act = true; - if (!list_empty(&dma_q->active) && - ctx->cur_frm == ctx->next_frm) - cal_schedule_next_buffer(ctx); - spin_unlock(&ctx->slock); - } + if (status & CAL_HL_IRQ_MASK(i)) + cal_irq_wdma_start(cal->ctx[i]); } } diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 501700fc4955..5ee0e05c3305 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -60,6 +61,13 @@ enum cal_camerarx_field { F_MAX_FIELDS, }; +enum cal_dma_state { + CAL_DMA_RUNNING, + CAL_DMA_STOP_REQUESTED, + CAL_DMA_STOP_PENDING, + CAL_DMA_STOPPED, +}; + struct cal_format_info { u32 fourcc; u32 code; @@ -190,7 +198,8 @@ struct cal_ctx { /* Pointer pointing to next v4l2_buffer */ struct cal_buffer *next_frm; - bool dma_act; + enum cal_dma_state dma_state; + struct wait_queue_head dma_wait; }; extern unsigned int cal_debug; @@ -262,7 +271,6 @@ const struct cal_format_info *cal_format_by_code(u32 code); void cal_quickdump_regs(struct cal_dev *cal); void cal_camerarx_disable(struct cal_camerarx *phy); -void cal_camerarx_ppi_disable(struct cal_camerarx *phy); void cal_camerarx_i913_errata(struct cal_camerarx *phy); struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, unsigned int instance); @@ -272,6 +280,8 @@ void cal_ctx_csi2_config(struct cal_ctx *ctx); void cal_ctx_pix_proc_config(struct cal_ctx *ctx); void cal_ctx_wr_dma_config(struct cal_ctx *ctx); void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr); +void cal_ctx_wr_dma_disable(struct cal_ctx *ctx); +int cal_ctx_wr_dma_stop(struct cal_ctx *ctx); void cal_ctx_enable_irqs(struct cal_ctx *ctx); void cal_ctx_disable_irqs(struct cal_ctx *ctx); -- cgit v1.2.3 From cff615dba5c76d8c806d95e81ee6b8c66e3d7031 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:43 +0100 Subject: media: ti-vpe: cal: Use spin_lock_irq() when starting or stopping stream The cal_start_streaming() and cal_stop_streaming() functions are called with interrupts enabled. spin_lock_irq() can thus be used instead of spin_lock_irqsave(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index d3f805a512c0..de0ba6128715 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -492,12 +492,11 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) struct cal_dmaqueue *dma_q = &ctx->vidq; struct cal_buffer *buf, *tmp; unsigned long addr; - unsigned long flags; int ret; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irq(&ctx->slock); if (list_empty(&dma_q->active)) { - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irq(&ctx->slock); ctx_dbg(3, ctx, "buffer queue is empty\n"); return -EIO; } @@ -506,7 +505,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) ctx->cur_frm = buf; ctx->next_frm = buf; list_del(&buf->list); - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irq(&ctx->slock); addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); ctx->sequence = 0; @@ -534,7 +533,7 @@ err: cal_ctx_disable_irqs(ctx); ctx->dma_state = CAL_DMA_STOPPED; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irq(&ctx->slock); vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); ctx->cur_frm = NULL; ctx->next_frm = NULL; @@ -542,7 +541,7 @@ err: list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); } - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irq(&ctx->slock); return ret; } @@ -551,7 +550,6 @@ static void cal_stop_streaming(struct vb2_queue *vq) struct cal_ctx *ctx = vb2_get_drv_priv(vq); struct cal_dmaqueue *dma_q = &ctx->vidq; struct cal_buffer *buf, *tmp; - unsigned long flags; cal_ctx_wr_dma_stop(ctx); cal_ctx_disable_irqs(ctx); @@ -559,7 +557,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0); /* Release all active buffers */ - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irq(&ctx->slock); list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); @@ -574,7 +572,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) } ctx->cur_frm = NULL; ctx->next_frm = NULL; - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irq(&ctx->slock); pm_runtime_put_sync(ctx->cal->dev); } -- cgit v1.2.3 From 159172f07c9106fa8e11f6dff58fe2e50c2f92cf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:44 +0100 Subject: media: ti-vpe: cal: Share buffer release code between start and stop The cal_start_streaming() and cal_stop_streaming() functions duplicate the same buffer release logic. split it to a separate function to share the code. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 55 +++++++++++++++---------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index de0ba6128715..7eec0a57b141 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -486,11 +486,34 @@ static void cal_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&ctx->slock, flags); } +static void cal_release_buffers(struct cal_ctx *ctx, + enum vb2_buffer_state state) +{ + struct cal_buffer *buf, *tmp; + + /* Release all active buffers. */ + spin_lock_irq(&ctx->slock); + + list_for_each_entry_safe(buf, tmp, &ctx->vidq.active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + if (ctx->next_frm != ctx->cur_frm) + vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, state); + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, state); + + ctx->cur_frm = NULL; + ctx->next_frm = NULL; + + spin_unlock_irq(&ctx->slock); +} + static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) { struct cal_ctx *ctx = vb2_get_drv_priv(vq); struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; + struct cal_buffer *buf; unsigned long addr; int ret; @@ -533,46 +556,20 @@ err: cal_ctx_disable_irqs(ctx); ctx->dma_state = CAL_DMA_STOPPED; - spin_lock_irq(&ctx->slock); - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - } - spin_unlock_irq(&ctx->slock); + cal_release_buffers(ctx, VB2_BUF_STATE_QUEUED); return ret; } static void cal_stop_streaming(struct vb2_queue *vq) { struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; cal_ctx_wr_dma_stop(ctx); cal_ctx_disable_irqs(ctx); v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0); - /* Release all active buffers */ - spin_lock_irq(&ctx->slock); - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - if (ctx->cur_frm == ctx->next_frm) { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } else { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - } - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - spin_unlock_irq(&ctx->slock); + cal_release_buffers(ctx, VB2_BUF_STATE_ERROR); pm_runtime_put_sync(ctx->cal->dev); } -- cgit v1.2.3 From 5dda1b346d183a7c3b0a0d13c90546ff03bf63a9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:45 +0100 Subject: media: ti-vpe: cal: Drop V4L2_CAP_READWRITE The V4L2 read/write API is inefficient and makes little sense for devices that handle frame-based formats. Applications shouldn't use it, drop its support from the driver. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 7eec0a57b141..32dd4d9ca212 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -393,7 +393,6 @@ static const struct v4l2_file_operations cal_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, .release = vb2_fop_release, - .read = vb2_fop_read, .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ .mmap = vb2_fop_mmap, @@ -595,8 +594,7 @@ static const struct video_device cal_videodev = { .ioctl_ops = &cal_ioctl_ops, .minor = -1, .release = video_device_release_empty, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING, }; static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) @@ -731,7 +729,7 @@ int cal_ctx_v4l2_init(struct cal_ctx *ctx) /* Initialize the vb2 queue. */ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->io_modes = VB2_MMAP | VB2_DMABUF; q->drv_priv = ctx; q->buf_struct_size = sizeof(struct cal_buffer); q->ops = &cal_video_qops; -- cgit v1.2.3 From 455466400c4164c14756113405fa5e00dea399a4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:46 +0100 Subject: media: ti-vpe: cal: Drop unneeded check in cal_calc_format_size() The cal_calc_format_size() function checks that the passed fmtinfo argument is not NULL. All callers ensure that the pointer is valid. Drop the check. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 32dd4d9ca212..ad1e85189831 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -146,17 +146,12 @@ static int __subdev_set_format(struct cal_ctx *ctx, return 0; } -static int cal_calc_format_size(struct cal_ctx *ctx, - const struct cal_format_info *fmtinfo, - struct v4l2_format *f) +static void cal_calc_format_size(struct cal_ctx *ctx, + const struct cal_format_info *fmtinfo, + struct v4l2_format *f) { u32 bpl, max_width; - if (!fmtinfo) { - ctx_dbg(3, ctx, "No cal_fmt provided!\n"); - return -EINVAL; - } - /* * Maximum width is bound by the DMA max width in bytes. * We need to recalculate the actual maxi width depending on the @@ -177,8 +172,6 @@ static int cal_calc_format_size(struct cal_ctx *ctx, __func__, fourcc_to_str(f->fmt.pix.pixelformat), f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); - - return 0; } static int cal_g_fmt_vid_cap(struct file *file, void *priv, @@ -247,7 +240,8 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv, * updated properly during s_fmt */ f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; - return cal_calc_format_size(ctx, fmtinfo, f); + cal_calc_format_size(ctx, fmtinfo, f); + return 0; } static int cal_s_fmt_vid_cap(struct file *file, void *priv, -- cgit v1.2.3 From d7d24d772f2c56e32ce8beb1a05a04fe28e4d888 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:47 +0100 Subject: media: ti-vpe: cal: Remove DMA queue empty check at start streaming time The vb2 queue ensures that the start streaming operation will only be called with a minimal number of buffers queued to the driver. There's thus no need to manually check if the DMA queue is empty. Remove the check. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index ad1e85189831..bfc3ba4a96af 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -511,12 +511,6 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) int ret; spin_lock_irq(&ctx->slock); - if (list_empty(&dma_q->active)) { - spin_unlock_irq(&ctx->slock); - ctx_dbg(3, ctx, "buffer queue is empty\n"); - return -EIO; - } - buf = list_entry(dma_q->active.next, struct cal_buffer, list); ctx->cur_frm = buf; ctx->next_frm = buf; -- cgit v1.2.3 From 9ae6b925848eed49f08451c6ee90d9a21ce0a3c9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:48 +0100 Subject: media: ti-vpe: cal: Use list_first_entry() Use the list_first_entry() macro where appropriate to replace manual usage of list_entry(head.next). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 2 +- drivers/media/platform/ti-vpe/cal.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index bfc3ba4a96af..c80c4643d8ed 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -511,7 +511,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) int ret; spin_lock_irq(&ctx->slock); - buf = list_entry(dma_q->active.next, struct cal_buffer, list); + buf = list_first_entry(&dma_q->active, struct cal_buffer, list); ctx->cur_frm = buf; ctx->next_frm = buf; list_del(&buf->list); diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index f1c2b8bc28f4..8f25e7a6f5e8 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -496,7 +496,7 @@ static inline void cal_irq_wdma_start(struct cal_ctx *ctx) struct cal_buffer *buf; unsigned long addr; - buf = list_entry(dma_q->active.next, struct cal_buffer, list); + buf = list_first_entry(&dma_q->active, struct cal_buffer, list); addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); cal_ctx_wr_dma_addr(ctx, addr); -- cgit v1.2.3 From ca4fec54f6ba0825532ac21698cf2b7324d02737 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:49 +0100 Subject: media: ti-vpe: cal: Group all DMA queue fields in struct cal_dmaqueue The cal_dmaqueue structure only contains the list of queued buffers. Move the other fields that are logically related to the DMA queue (current and next buffer points, state, wait queue and lock) from cal_ctx to cal_dmaqueue. Take this as an opportunity to document the fields usage and to give them more appropriate names. The 'active' field stored the list of all queued buffers, not the active buffers, so rename it to 'queue'. The 'cur_frm' and 'next_frm' are respectively renamed to 'active' and 'pending' to better explain their purpose. The 'dma_state' and 'dma_wait' fields are stripped of their 'dma_' prefix as they're now part of cal_dmaqueue. Finally, 'slock' is renamed to 'lock'. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 48 +++++++++++++++--------------- drivers/media/platform/ti-vpe/cal.c | 49 +++++++++++++++---------------- drivers/media/platform/ti-vpe/cal.h | 44 +++++++++++++++++++-------- 3 files changed, 79 insertions(+), 62 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index c80c4643d8ed..34dfe38dc960 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -470,13 +470,12 @@ static void cal_buffer_queue(struct vb2_buffer *vb) struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct cal_buffer *buf = container_of(vb, struct cal_buffer, vb.vb2_buf); - struct cal_dmaqueue *vidq = &ctx->vidq; unsigned long flags; /* recheck locking */ - spin_lock_irqsave(&ctx->slock, flags); - list_add_tail(&buf->list, &vidq->active); - spin_unlock_irqrestore(&ctx->slock, flags); + spin_lock_irqsave(&ctx->dma.lock, flags); + list_add_tail(&buf->list, &ctx->dma.queue); + spin_unlock_irqrestore(&ctx->dma.lock, flags); } static void cal_release_buffers(struct cal_ctx *ctx, @@ -484,42 +483,41 @@ static void cal_release_buffers(struct cal_ctx *ctx, { struct cal_buffer *buf, *tmp; - /* Release all active buffers. */ - spin_lock_irq(&ctx->slock); + /* Release all queued buffers. */ + spin_lock_irq(&ctx->dma.lock); - list_for_each_entry_safe(buf, tmp, &ctx->vidq.active, list) { + list_for_each_entry_safe(buf, tmp, &ctx->dma.queue, list) { list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, state); } - if (ctx->next_frm != ctx->cur_frm) - vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, state); - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, state); + if (ctx->dma.pending != ctx->dma.active) + vb2_buffer_done(&ctx->dma.pending->vb.vb2_buf, state); + vb2_buffer_done(&ctx->dma.active->vb.vb2_buf, state); - ctx->cur_frm = NULL; - ctx->next_frm = NULL; + ctx->dma.active = NULL; + ctx->dma.pending = NULL; - spin_unlock_irq(&ctx->slock); + spin_unlock_irq(&ctx->dma.lock); } static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) { struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; struct cal_buffer *buf; unsigned long addr; int ret; - spin_lock_irq(&ctx->slock); - buf = list_first_entry(&dma_q->active, struct cal_buffer, list); - ctx->cur_frm = buf; - ctx->next_frm = buf; + spin_lock_irq(&ctx->dma.lock); + buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, list); + ctx->dma.active = buf; + ctx->dma.pending = buf; list_del(&buf->list); - spin_unlock_irq(&ctx->slock); + spin_unlock_irq(&ctx->dma.lock); - addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); + addr = vb2_dma_contig_plane_dma_addr(&ctx->dma.active->vb.vb2_buf, 0); ctx->sequence = 0; - ctx->dma_state = CAL_DMA_RUNNING; + ctx->dma.state = CAL_DMA_RUNNING; pm_runtime_get_sync(ctx->cal->dev); @@ -541,7 +539,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) err: cal_ctx_wr_dma_disable(ctx); cal_ctx_disable_irqs(ctx); - ctx->dma_state = CAL_DMA_STOPPED; + ctx->dma.state = CAL_DMA_STOPPED; cal_release_buffers(ctx, VB2_BUF_STATE_QUEUED); return ret; @@ -710,10 +708,10 @@ int cal_ctx_v4l2_init(struct cal_ctx *ctx) struct vb2_queue *q = &ctx->vb_vidq; int ret; - INIT_LIST_HEAD(&ctx->vidq.active); - spin_lock_init(&ctx->slock); + INIT_LIST_HEAD(&ctx->dma.queue); + spin_lock_init(&ctx->dma.lock); mutex_init(&ctx->mutex); - init_waitqueue_head(&ctx->dma_wait); + init_waitqueue_head(&ctx->dma.wait); /* Initialize the vb2 queue. */ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 8f25e7a6f5e8..3e0a69bb7fe5 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -424,9 +424,9 @@ static bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx) { bool stopped; - spin_lock_irq(&ctx->slock); - stopped = ctx->dma_state == CAL_DMA_STOPPED; - spin_unlock_irq(&ctx->slock); + spin_lock_irq(&ctx->dma.lock); + stopped = ctx->dma.state == CAL_DMA_STOPPED; + spin_unlock_irq(&ctx->dma.lock); return stopped; } @@ -436,11 +436,11 @@ int cal_ctx_wr_dma_stop(struct cal_ctx *ctx) long timeout; /* Request DMA stop and wait until it completes. */ - spin_lock_irq(&ctx->slock); - ctx->dma_state = CAL_DMA_STOP_REQUESTED; - spin_unlock_irq(&ctx->slock); + spin_lock_irq(&ctx->dma.lock); + ctx->dma.state = CAL_DMA_STOP_REQUESTED; + spin_unlock_irq(&ctx->dma.lock); - timeout = wait_event_timeout(ctx->dma_wait, cal_ctx_wr_dma_stopped(ctx), + timeout = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx), msecs_to_jiffies(500)); if (!timeout) { ctx_err(ctx, "failed to disable dma cleanly\n"); @@ -475,20 +475,18 @@ void cal_ctx_disable_irqs(struct cal_ctx *ctx) static inline void cal_irq_wdma_start(struct cal_ctx *ctx) { - struct cal_dmaqueue *dma_q = &ctx->vidq; + spin_lock(&ctx->dma.lock); - spin_lock(&ctx->slock); - - if (ctx->dma_state == CAL_DMA_STOP_REQUESTED) { + if (ctx->dma.state == CAL_DMA_STOP_REQUESTED) { /* * If a stop is requested, disable the write DMA context * immediately. The CAL_WR_DMA_CTRL_j.MODE field is shadowed, * the current frame will complete and the DMA will then stop. */ cal_ctx_wr_dma_disable(ctx); - ctx->dma_state = CAL_DMA_STOP_PENDING; - } else if (!list_empty(&dma_q->active) && - ctx->cur_frm == ctx->next_frm) { + ctx->dma.state = CAL_DMA_STOP_PENDING; + } else if (!list_empty(&ctx->dma.queue) && + ctx->dma.active == ctx->dma.pending) { /* * Otherwise, if a new buffer is available, queue it to the * hardware. @@ -496,36 +494,37 @@ static inline void cal_irq_wdma_start(struct cal_ctx *ctx) struct cal_buffer *buf; unsigned long addr; - buf = list_first_entry(&dma_q->active, struct cal_buffer, list); + buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, + list); addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); cal_ctx_wr_dma_addr(ctx, addr); - ctx->next_frm = buf; + ctx->dma.pending = buf; list_del(&buf->list); } - spin_unlock(&ctx->slock); + spin_unlock(&ctx->dma.lock); } static inline void cal_irq_wdma_end(struct cal_ctx *ctx) { struct cal_buffer *buf = NULL; - spin_lock(&ctx->slock); + spin_lock(&ctx->dma.lock); /* If the DMA context was stopping, it is now stopped. */ - if (ctx->dma_state == CAL_DMA_STOP_PENDING) { - ctx->dma_state = CAL_DMA_STOPPED; - wake_up(&ctx->dma_wait); + if (ctx->dma.state == CAL_DMA_STOP_PENDING) { + ctx->dma.state = CAL_DMA_STOPPED; + wake_up(&ctx->dma.wait); } /* If a new buffer was queued, complete the current buffer. */ - if (ctx->cur_frm != ctx->next_frm) { - buf = ctx->cur_frm; - ctx->cur_frm = ctx->next_frm; + if (ctx->dma.active != ctx->dma.pending) { + buf = ctx->dma.active; + ctx->dma.active = ctx->dma.pending; } - spin_unlock(&ctx->slock); + spin_unlock(&ctx->dma.lock); if (buf) { buf->vb.vb2_buf.timestamp = ktime_get_ns(); diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 5ee0e05c3305..9d3cbc13f915 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -82,8 +82,38 @@ struct cal_buffer { struct list_head list; }; +/** + * struct cal_dmaqueue - Queue of DMA buffers + * @active: Buffer being DMA'ed to for the current frame + */ struct cal_dmaqueue { - struct list_head active; + /** + * Protects all fields in the cal_dmaqueue. + */ + spinlock_t lock; + + /** + * Buffers queued to the driver and waiting for DMA processing. + * Buffers are added to the list by the vb2 .buffer_queue() operation, + * and move to @pending when they are scheduled for the next frame. + */ + struct list_head queue; + /** + * Buffer provided to the hardware to DMA the next frame. Will move to + * @active at the end of the current frame. + */ + struct cal_buffer *pending; + /** + * Buffer being DMA'ed to for the current frame. Will be retired and + * given back to vb2 at the end of the current frame if a @pending + * buffer has been scheduled to replace it. + */ + struct cal_buffer *active; + + /** State of the DMA engine. */ + enum cal_dma_state state; + /** Wait queue to signal a @state transition to CAL_DMA_STOPPED. */ + struct wait_queue_head wait; }; struct cal_camerarx_data { @@ -174,10 +204,8 @@ struct cal_ctx { /* v4l2_ioctl mutex */ struct mutex mutex; - /* v4l2 buffers lock */ - spinlock_t slock; - struct cal_dmaqueue vidq; + struct cal_dmaqueue dma; /* video capture */ const struct cal_format_info *fmtinfo; @@ -192,14 +220,6 @@ struct cal_ctx { struct vb2_queue vb_vidq; unsigned int index; unsigned int cport; - - /* Pointer pointing to current v4l2_buffer */ - struct cal_buffer *cur_frm; - /* Pointer pointing to next v4l2_buffer */ - struct cal_buffer *next_frm; - - enum cal_dma_state dma_state; - struct wait_queue_head dma_wait; }; extern unsigned int cal_debug; -- cgit v1.2.3 From 2e2279b53a9f48b6bab045a41096dbd781020cdd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:50 +0100 Subject: media: ti-vpe: cal: Set cal_dmaqueue.pending to NULL when no pending buffer When a pending buffer becomes active, the cal_dmaqueue.active field is updated, but the pending field keeps the same value until a new buffer becomes pending. This requires handling the special case of pending == active in different places. Simplify the code by setting the pending field to NULL when the pending buffer becomes active. Buffers are now simply moved from queue to pending and from pending to active. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 14 ++++++++------ drivers/media/platform/ti-vpe/cal.c | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 34dfe38dc960..438447728b46 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -491,12 +491,15 @@ static void cal_release_buffers(struct cal_ctx *ctx, vb2_buffer_done(&buf->vb.vb2_buf, state); } - if (ctx->dma.pending != ctx->dma.active) + if (ctx->dma.pending) { vb2_buffer_done(&ctx->dma.pending->vb.vb2_buf, state); - vb2_buffer_done(&ctx->dma.active->vb.vb2_buf, state); + ctx->dma.pending = NULL; + } - ctx->dma.active = NULL; - ctx->dma.pending = NULL; + if (ctx->dma.active) { + vb2_buffer_done(&ctx->dma.active->vb.vb2_buf, state); + ctx->dma.active = NULL; + } spin_unlock_irq(&ctx->dma.lock); } @@ -510,12 +513,11 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) spin_lock_irq(&ctx->dma.lock); buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, list); - ctx->dma.active = buf; ctx->dma.pending = buf; list_del(&buf->list); spin_unlock_irq(&ctx->dma.lock); - addr = vb2_dma_contig_plane_dma_addr(&ctx->dma.active->vb.vb2_buf, 0); + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); ctx->sequence = 0; ctx->dma.state = CAL_DMA_RUNNING; diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 3e0a69bb7fe5..547dffcfe68f 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -485,8 +485,7 @@ static inline void cal_irq_wdma_start(struct cal_ctx *ctx) */ cal_ctx_wr_dma_disable(ctx); ctx->dma.state = CAL_DMA_STOP_PENDING; - } else if (!list_empty(&ctx->dma.queue) && - ctx->dma.active == ctx->dma.pending) { + } else if (!list_empty(&ctx->dma.queue) && !ctx->dma.pending) { /* * Otherwise, if a new buffer is available, queue it to the * hardware. @@ -519,9 +518,10 @@ static inline void cal_irq_wdma_end(struct cal_ctx *ctx) } /* If a new buffer was queued, complete the current buffer. */ - if (ctx->dma.active != ctx->dma.pending) { + if (ctx->dma.pending) { buf = ctx->dma.active; ctx->dma.active = ctx->dma.pending; + ctx->dma.pending = NULL; } spin_unlock(&ctx->dma.lock); -- cgit v1.2.3 From 67252cf0ea44a66fb8c0ade59a67b69a6098dfa4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:51 +0100 Subject: media: ti-vpe: cal: Store buffer DMA address in dma_addr_t dma_addr_t is the correct type to store DMA addresses. Replace incorrect usage of unsigned long and unsigned int. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 2 +- drivers/media/platform/ti-vpe/cal.c | 6 +++--- drivers/media/platform/ti-vpe/cal.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 438447728b46..511767dd69bd 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -508,7 +508,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) { struct cal_ctx *ctx = vb2_get_drv_priv(vq); struct cal_buffer *buf; - unsigned long addr; + dma_addr_t addr; int ret; spin_lock_irq(&ctx->dma.lock); diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 547dffcfe68f..3cf625262d32 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -406,9 +406,9 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx) ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL)); } -void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) +void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, dma_addr_t addr) { - cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); + cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), addr); } void cal_ctx_wr_dma_disable(struct cal_ctx *ctx) @@ -491,7 +491,7 @@ static inline void cal_irq_wdma_start(struct cal_ctx *ctx) * hardware. */ struct cal_buffer *buf; - unsigned long addr; + dma_addr_t addr; buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, list); diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 9d3cbc13f915..26916f72fd60 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -299,7 +299,7 @@ void cal_camerarx_destroy(struct cal_camerarx *phy); void cal_ctx_csi2_config(struct cal_ctx *ctx); void cal_ctx_pix_proc_config(struct cal_ctx *ctx); void cal_ctx_wr_dma_config(struct cal_ctx *ctx); -void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr); +void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, dma_addr_t addr); void cal_ctx_wr_dma_disable(struct cal_ctx *ctx); int cal_ctx_wr_dma_stop(struct cal_ctx *ctx); void cal_ctx_enable_irqs(struct cal_ctx *ctx); -- cgit v1.2.3 From 2ad100f359c28bed1b3bbca607a7c3e7ab684708 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Dec 2020 00:53:52 +0100 Subject: media: ti-vpe: cal: Simplify the context API Rework the context API exposed to cal-video.c to simplify it. The configuration and enable steps are all grouped in a single cal_ctx_start() function, and the DMA stop and IRQ disable are similarly groupd in cal_ctx_stop(). The cal_ctx_wr_dma_addr() function is renamed to cal_ctx_set_dma_addr() for consistency with the cal_ctx_ prefix of the start and stop functions. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 21 ++++-------- drivers/media/platform/ti-vpe/cal.c | 54 ++++++++++++++++++------------- drivers/media/platform/ti-vpe/cal.h | 11 ++----- 3 files changed, 41 insertions(+), 45 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 511767dd69bd..779f1e1bc529 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -518,16 +518,11 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) spin_unlock_irq(&ctx->dma.lock); addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - ctx->sequence = 0; - ctx->dma.state = CAL_DMA_RUNNING; pm_runtime_get_sync(ctx->cal->dev); - cal_ctx_csi2_config(ctx); - cal_ctx_pix_proc_config(ctx); - cal_ctx_wr_dma_config(ctx); - cal_ctx_wr_dma_addr(ctx, addr); - cal_ctx_enable_irqs(ctx); + cal_ctx_set_dma_addr(ctx, addr); + cal_ctx_start(ctx); ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1); if (ret) @@ -539,9 +534,8 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; err: - cal_ctx_wr_dma_disable(ctx); - cal_ctx_disable_irqs(ctx); - ctx->dma.state = CAL_DMA_STOPPED; + cal_ctx_stop(ctx); + pm_runtime_put_sync(ctx->cal->dev); cal_release_buffers(ctx, VB2_BUF_STATE_QUEUED); return ret; @@ -551,14 +545,13 @@ static void cal_stop_streaming(struct vb2_queue *vq) { struct cal_ctx *ctx = vb2_get_drv_priv(vq); - cal_ctx_wr_dma_stop(ctx); - cal_ctx_disable_irqs(ctx); + cal_ctx_stop(ctx); v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0); - cal_release_buffers(ctx, VB2_BUF_STATE_ERROR); - pm_runtime_put_sync(ctx->cal->dev); + + cal_release_buffers(ctx, VB2_BUF_STATE_ERROR); } static const struct vb2_ops cal_video_qops = { diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 3cf625262d32..293cbac905b3 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -280,7 +280,7 @@ void cal_quickdump_regs(struct cal_dev *cal) * ------------------------------------------------------------------ */ -void cal_ctx_csi2_config(struct cal_ctx *ctx) +static void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; @@ -305,7 +305,7 @@ void cal_ctx_csi2_config(struct cal_ctx *ctx) cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); } -void cal_ctx_pix_proc_config(struct cal_ctx *ctx) +static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) { u32 val, extract, pack; @@ -356,7 +356,7 @@ void cal_ctx_pix_proc_config(struct cal_ctx *ctx) cal_read(ctx->cal, CAL_PIX_PROC(ctx->index))); } -void cal_ctx_wr_dma_config(struct cal_ctx *ctx) +static void cal_ctx_wr_dma_config(struct cal_ctx *ctx) { unsigned int stride = ctx->v_fmt.fmt.pix.bytesperline; u32 val; @@ -406,12 +406,12 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx) ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL)); } -void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, dma_addr_t addr) +void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr) { cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), addr); } -void cal_ctx_wr_dma_disable(struct cal_ctx *ctx) +static void cal_ctx_wr_dma_disable(struct cal_ctx *ctx) { u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); @@ -431,11 +431,31 @@ static bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx) return stopped; } -int cal_ctx_wr_dma_stop(struct cal_ctx *ctx) +void cal_ctx_start(struct cal_ctx *ctx) +{ + ctx->sequence = 0; + ctx->dma.state = CAL_DMA_RUNNING; + + /* Configure the CSI-2, pixel processing and write DMA contexts. */ + cal_ctx_csi2_config(ctx); + cal_ctx_pix_proc_config(ctx); + cal_ctx_wr_dma_config(ctx); + + /* Enable IRQ_WDMA_END and IRQ_WDMA_START. */ + cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(1), + CAL_HL_IRQ_MASK(ctx->index)); + cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(2), + CAL_HL_IRQ_MASK(ctx->index)); +} + +void cal_ctx_stop(struct cal_ctx *ctx) { long timeout; - /* Request DMA stop and wait until it completes. */ + /* + * Request DMA stop and wait until it completes. If completion times + * out, forcefully disable the DMA. + */ spin_lock_irq(&ctx->dma.lock); ctx->dma.state = CAL_DMA_STOP_REQUESTED; spin_unlock_irq(&ctx->dma.lock); @@ -444,28 +464,16 @@ int cal_ctx_wr_dma_stop(struct cal_ctx *ctx) msecs_to_jiffies(500)); if (!timeout) { ctx_err(ctx, "failed to disable dma cleanly\n"); - return -ETIMEDOUT; + cal_ctx_wr_dma_disable(ctx); } - return 0; -} - -void cal_ctx_enable_irqs(struct cal_ctx *ctx) -{ - /* Enable IRQ_WDMA_END and IRQ_WDMA_START. */ - cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(1), - CAL_HL_IRQ_MASK(ctx->index)); - cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(2), - CAL_HL_IRQ_MASK(ctx->index)); -} - -void cal_ctx_disable_irqs(struct cal_ctx *ctx) -{ /* Disable IRQ_WDMA_END and IRQ_WDMA_START. */ cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1), CAL_HL_IRQ_MASK(ctx->index)); cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2), CAL_HL_IRQ_MASK(ctx->index)); + + ctx->dma.state = CAL_DMA_STOPPED; } /* ------------------------------------------------------------------ @@ -496,7 +504,7 @@ static inline void cal_irq_wdma_start(struct cal_ctx *ctx) buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, list); addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - cal_ctx_wr_dma_addr(ctx, addr); + cal_ctx_set_dma_addr(ctx, addr); ctx->dma.pending = buf; list_del(&buf->list); diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 26916f72fd60..60f5f7480b17 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -296,14 +296,9 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, unsigned int instance); void cal_camerarx_destroy(struct cal_camerarx *phy); -void cal_ctx_csi2_config(struct cal_ctx *ctx); -void cal_ctx_pix_proc_config(struct cal_ctx *ctx); -void cal_ctx_wr_dma_config(struct cal_ctx *ctx); -void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, dma_addr_t addr); -void cal_ctx_wr_dma_disable(struct cal_ctx *ctx); -int cal_ctx_wr_dma_stop(struct cal_ctx *ctx); -void cal_ctx_enable_irqs(struct cal_ctx *ctx); -void cal_ctx_disable_irqs(struct cal_ctx *ctx); +void cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr); +void cal_ctx_start(struct cal_ctx *ctx); +void cal_ctx_stop(struct cal_ctx *ctx); int cal_ctx_v4l2_register(struct cal_ctx *ctx); void cal_ctx_v4l2_unregister(struct cal_ctx *ctx); -- cgit v1.2.3 From 56f64b82356b7494ca58d31652317c2f009d8ca1 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:30 +0100 Subject: media: tegra-video: Use zero crop settings if subdev has no get_selection Currently try format implementation doesn't check if subdevice has get_selection ops implemented and returns -EINVAL on error from direct v4l2_subdev_call of get_selection. Selection API's are not mandatory for all V4L2 subdevices. This patch fixes it by adding v4l2_subdev_has_ops check prior to calling get_selection ops and continues with try or set format with zero crop settings for subdevices that don't have get_selection and returns -EINVAL only for subdevices that has get_selection ops. Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-video/vi.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 560d8b368124..7edd35c07a11 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -533,11 +533,18 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, fse.code = fmtinfo->code; ret = v4l2_subdev_call(subdev, pad, enum_frame_size, pad_cfg, &fse); if (ret) { - ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); - if (ret) - return -EINVAL; - pad_cfg->try_crop.width = sdsel.r.width; - pad_cfg->try_crop.height = sdsel.r.height; + if (!v4l2_subdev_has_op(subdev, pad, get_selection)) { + pad_cfg->try_crop.width = 0; + pad_cfg->try_crop.height = 0; + } else { + ret = v4l2_subdev_call(subdev, pad, get_selection, + NULL, &sdsel); + if (ret) + return -EINVAL; + + pad_cfg->try_crop.width = sdsel.r.width; + pad_cfg->try_crop.height = sdsel.r.height; + } } else { pad_cfg->try_crop.width = fse.max_width; pad_cfg->try_crop.height = fse.max_height; -- cgit v1.2.3 From c1bcc54728253a83dd61b27b9da553116ae433be Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:31 +0100 Subject: media: tegra-video: Enable VI pixel transform for YUV and RGB formats VI Pixel transforms converts source pixel data to selected destination pixel formats in memory and aligns properly. YUV and RGB formats need this pixel transform to be enabled. RAW formats use T_R16_I destination pixel format in memory and does not need pixel transform as they support direct write to memory. So, this patch enables pixel transform for YUV and RGB and keeps it bypass for RAW formats. Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-video/tegra210.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c index ac066c030a4f..6b23aa799fbe 100644 --- a/drivers/staging/media/tegra-video/tegra210.c +++ b/drivers/staging/media/tegra-video/tegra210.c @@ -178,10 +178,23 @@ static int tegra_channel_capture_setup(struct tegra_vi_channel *chan) u32 format = chan->fmtinfo->img_fmt; u32 data_type = chan->fmtinfo->img_dt; u32 word_count = (width * chan->fmtinfo->bit_width) / 8; + u32 bypass_pixel_transform = BIT(BYPASS_PXL_TRANSFORM_OFFSET); + + /* + * VI Pixel transformation unit converts source pixels data format + * into selected destination pixel format and aligns properly while + * interfacing with memory packer. + * This pixel transformation should be enabled for YUV and RGB + * formats and should be bypassed for RAW formats as RAW formats + * only support direct to memory. + */ + if (chan->pg_mode || data_type == TEGRA_IMAGE_DT_YUV422_8 || + data_type == TEGRA_IMAGE_DT_RGB888) + bypass_pixel_transform = 0; vi_csi_write(chan, TEGRA_VI_CSI_ERROR_STATUS, 0xffffffff); vi_csi_write(chan, TEGRA_VI_CSI_IMAGE_DEF, - ((chan->pg_mode ? 0 : 1) << BYPASS_PXL_TRANSFORM_OFFSET) | + bypass_pixel_transform | (format << IMAGE_DEF_FORMAT_OFFSET) | IMAGE_DEF_DEST_MEM); vi_csi_write(chan, TEGRA_VI_CSI_IMAGE_DT, data_type); -- cgit v1.2.3 From 689bfcac95d5ab17ba27fc9a423e7f676ed38fab Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:32 +0100 Subject: media: tegra-video: Fix V4L2 pixel format RGB and YUV V4L2 pixel format is incorrect for RGB and YUV formats. This patch fixes it. Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-video/tegra210.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c index 6b23aa799fbe..68f09e41bc77 100644 --- a/drivers/staging/media/tegra-video/tegra210.c +++ b/drivers/staging/media/tegra-video/tegra210.c @@ -619,19 +619,19 @@ static const struct tegra_video_format tegra210_video_formats[] = { TEGRA210_VIDEO_FMT(RAW12, 12, SGBRG12_1X12, 2, T_R16_I, SGBRG12), TEGRA210_VIDEO_FMT(RAW12, 12, SBGGR12_1X12, 2, T_R16_I, SBGGR12), /* RGB888 */ - TEGRA210_VIDEO_FMT(RGB888, 24, RGB888_1X24, 4, T_A8R8G8B8, RGB24), + TEGRA210_VIDEO_FMT(RGB888, 24, RGB888_1X24, 4, T_A8R8G8B8, XBGR32), TEGRA210_VIDEO_FMT(RGB888, 24, RGB888_1X32_PADHI, 4, T_A8B8G8R8, - XBGR32), + RGBX32), /* YUV422 */ - TEGRA210_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 2, T_U8_Y8__V8_Y8, UYVY), - TEGRA210_VIDEO_FMT(YUV422_8, 16, VYUY8_1X16, 2, T_V8_Y8__U8_Y8, VYUY), - TEGRA210_VIDEO_FMT(YUV422_8, 16, YUYV8_1X16, 2, T_Y8_U8__Y8_V8, YUYV), - TEGRA210_VIDEO_FMT(YUV422_8, 16, YVYU8_1X16, 2, T_Y8_V8__Y8_U8, YVYU), + TEGRA210_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 2, T_U8_Y8__V8_Y8, YVYU), + TEGRA210_VIDEO_FMT(YUV422_8, 16, VYUY8_1X16, 2, T_V8_Y8__U8_Y8, YUYV), + TEGRA210_VIDEO_FMT(YUV422_8, 16, YUYV8_1X16, 2, T_Y8_U8__Y8_V8, VYUY), + TEGRA210_VIDEO_FMT(YUV422_8, 16, YVYU8_1X16, 2, T_Y8_V8__Y8_U8, UYVY), TEGRA210_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 1, T_Y8__V8U8_N422, NV16), - TEGRA210_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 2, T_U8_Y8__V8_Y8, UYVY), - TEGRA210_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 2, T_V8_Y8__U8_Y8, VYUY), - TEGRA210_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 2, T_Y8_U8__Y8_V8, YUYV), - TEGRA210_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 2, T_Y8_V8__Y8_U8, YVYU), + TEGRA210_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 2, T_U8_Y8__V8_Y8, YVYU), + TEGRA210_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 2, T_V8_Y8__U8_Y8, YUYV), + TEGRA210_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 2, T_Y8_U8__Y8_V8, VYUY), + TEGRA210_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 2, T_Y8_V8__Y8_U8, UYVY), }; /* Tegra210 VI operations */ -- cgit v1.2.3 From fbef4d6bb92e99e309cae1d251821ef22979a7f1 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:33 +0100 Subject: media: tegra-video: Add support for V4L2_PIX_FMT_NV16 NV16 are two-plane versions of YUV422 format. VI/CSI surface0 registers corresponds to first Y plane and surface1 registers corresponds to seconds UV plane. This patch updates image size for NV16 format to include both planes and programs VI/CSI surface1 registers for UV plane capture. Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-video/tegra210.c | 13 +++++++++++++ drivers/staging/media/tegra-video/vi.c | 2 ++ 2 files changed, 15 insertions(+) diff --git a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c index 68f09e41bc77..b731aa54dda1 100644 --- a/drivers/staging/media/tegra-video/tegra210.c +++ b/drivers/staging/media/tegra-video/tegra210.c @@ -287,6 +287,7 @@ static int tegra_channel_capture_frame(struct tegra_vi_channel *chan, { u32 thresh, value, frame_start, mw_ack_done; int bytes_per_line = chan->format.bytesperline; + u32 sizeimage = chan->format.sizeimage; int err; /* program buffer address by using surface 0 */ @@ -295,6 +296,18 @@ static int tegra_channel_capture_frame(struct tegra_vi_channel *chan, vi_csi_write(chan, TEGRA_VI_CSI_SURFACE0_OFFSET_LSB, buf->addr); vi_csi_write(chan, TEGRA_VI_CSI_SURFACE0_STRIDE, bytes_per_line); + /* + * Program surface 1 for UV plane with offset sizeimage from Y plane. + */ + if (chan->fmtinfo->fourcc == V4L2_PIX_FMT_NV16) { + vi_csi_write(chan, TEGRA_VI_CSI_SURFACE1_OFFSET_MSB, + ((u64)buf->addr + sizeimage / 2) >> 32); + vi_csi_write(chan, TEGRA_VI_CSI_SURFACE1_OFFSET_LSB, + buf->addr + sizeimage / 2); + vi_csi_write(chan, TEGRA_VI_CSI_SURFACE1_STRIDE, + bytes_per_line); + } + /* * Tegra VI block interacts with host1x syncpt for synchronizing * programmed condition of capture state and hardware operation. diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 7edd35c07a11..525c08739d99 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -484,6 +484,8 @@ static void tegra_channel_fmt_align(struct tegra_vi_channel *chan, pix->bytesperline = clamp(bpl, min_bpl, max_bpl); pix->sizeimage = pix->bytesperline * pix->height; + if (pix->pixelformat == V4L2_PIX_FMT_NV16) + pix->sizeimage *= 2; } static int __tegra_channel_try_format(struct tegra_vi_channel *chan, -- cgit v1.2.3 From 4281d115a4eba3b7767a214ed09613c2e1f0c4ea Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:34 +0100 Subject: media: tegra-video: Add DV timing support This patch adds below v4l2 DV timing ioctls to support HDMI-to-CSI bridges. Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-video/vi.c | 99 ++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 525c08739d99..d01e88de6b31 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -720,6 +721,97 @@ static int tegra_channel_s_selection(struct file *file, void *fh, return ret; } +static int tegra_channel_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct tegra_vi_channel *chan = video_drvdata(file); + struct v4l2_subdev *subdev; + + subdev = tegra_channel_get_remote_source_subdev(chan); + if (!v4l2_subdev_has_op(subdev, video, g_dv_timings)) + return -ENOTTY; + + return v4l2_device_call_until_err(chan->video.v4l2_dev, 0, + video, g_dv_timings, timings); +} + +static int tegra_channel_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct tegra_vi_channel *chan = video_drvdata(file); + struct v4l2_subdev *subdev; + struct v4l2_bt_timings *bt = &timings->bt; + struct v4l2_dv_timings curr_timings; + int ret; + + subdev = tegra_channel_get_remote_source_subdev(chan); + if (!v4l2_subdev_has_op(subdev, video, s_dv_timings)) + return -ENOTTY; + + ret = tegra_channel_g_dv_timings(file, fh, &curr_timings); + if (ret) + return ret; + + if (v4l2_match_dv_timings(timings, &curr_timings, 0, false)) + return 0; + + if (vb2_is_busy(&chan->queue)) + return -EBUSY; + + ret = v4l2_device_call_until_err(chan->video.v4l2_dev, 0, + video, s_dv_timings, timings); + if (ret) + return ret; + + chan->format.width = bt->width; + chan->format.height = bt->height; + chan->format.bytesperline = bt->width * chan->fmtinfo->bpp; + chan->format.sizeimage = chan->format.bytesperline * bt->height; + tegra_channel_fmt_align(chan, &chan->format, chan->fmtinfo->bpp); + + return 0; +} + +static int tegra_channel_query_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct tegra_vi_channel *chan = video_drvdata(file); + struct v4l2_subdev *subdev; + + subdev = tegra_channel_get_remote_source_subdev(chan); + if (!v4l2_subdev_has_op(subdev, video, query_dv_timings)) + return -ENOTTY; + + return v4l2_device_call_until_err(chan->video.v4l2_dev, 0, + video, query_dv_timings, timings); +} + +static int tegra_channel_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *timings) +{ + struct tegra_vi_channel *chan = video_drvdata(file); + struct v4l2_subdev *subdev; + + subdev = tegra_channel_get_remote_source_subdev(chan); + if (!v4l2_subdev_has_op(subdev, pad, enum_dv_timings)) + return -ENOTTY; + + return v4l2_subdev_call(subdev, pad, enum_dv_timings, timings); +} + +static int tegra_channel_dv_timings_cap(struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap) +{ + struct tegra_vi_channel *chan = video_drvdata(file); + struct v4l2_subdev *subdev; + + subdev = tegra_channel_get_remote_source_subdev(chan); + if (!v4l2_subdev_has_op(subdev, pad, dv_timings_cap)) + return -ENOTTY; + + return v4l2_subdev_call(subdev, pad, dv_timings_cap, cap); +} + static int tegra_channel_enum_input(struct file *file, void *fh, struct v4l2_input *inp) { @@ -732,6 +824,8 @@ static int tegra_channel_enum_input(struct file *file, void *fh, inp->type = V4L2_INPUT_TYPE_CAMERA; subdev = tegra_channel_get_remote_source_subdev(chan); strscpy(inp->name, subdev->name, sizeof(inp->name)); + if (v4l2_subdev_has_op(subdev, pad, dv_timings_cap)) + inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; return 0; } @@ -779,6 +873,11 @@ static const struct v4l2_ioctl_ops tegra_channel_ioctl_ops = { .vidioc_unsubscribe_event = v4l2_event_unsubscribe, .vidioc_g_selection = tegra_channel_g_selection, .vidioc_s_selection = tegra_channel_s_selection, + .vidioc_g_dv_timings = tegra_channel_g_dv_timings, + .vidioc_s_dv_timings = tegra_channel_s_dv_timings, + .vidioc_query_dv_timings = tegra_channel_query_dv_timings, + .vidioc_enum_dv_timings = tegra_channel_enum_dv_timings, + .vidioc_dv_timings_cap = tegra_channel_dv_timings_cap, }; /* -- cgit v1.2.3 From 52b21a0aed900b99b4fc5ee3d71d55d36e7f4621 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:35 +0100 Subject: media: tegra-video: Add support for EDID ioctl ops This patch adds support for EDID get and set v4l2 ioctl ops to use with HDMI to CSI bridges. Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-video/vi.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index d01e88de6b31..bc38136eae35 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -721,6 +721,32 @@ static int tegra_channel_s_selection(struct file *file, void *fh, return ret; } +static int tegra_channel_g_edid(struct file *file, void *fh, + struct v4l2_edid *edid) +{ + struct tegra_vi_channel *chan = video_drvdata(file); + struct v4l2_subdev *subdev; + + subdev = tegra_channel_get_remote_source_subdev(chan); + if (!v4l2_subdev_has_op(subdev, pad, get_edid)) + return -ENOTTY; + + return v4l2_subdev_call(subdev, pad, get_edid, edid); +} + +static int tegra_channel_s_edid(struct file *file, void *fh, + struct v4l2_edid *edid) +{ + struct tegra_vi_channel *chan = video_drvdata(file); + struct v4l2_subdev *subdev; + + subdev = tegra_channel_get_remote_source_subdev(chan); + if (!v4l2_subdev_has_op(subdev, pad, set_edid)) + return -ENOTTY; + + return v4l2_subdev_call(subdev, pad, set_edid, edid); +} + static int tegra_channel_g_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings) { @@ -873,6 +899,8 @@ static const struct v4l2_ioctl_ops tegra_channel_ioctl_ops = { .vidioc_unsubscribe_event = v4l2_event_unsubscribe, .vidioc_g_selection = tegra_channel_g_selection, .vidioc_s_selection = tegra_channel_s_selection, + .vidioc_g_edid = tegra_channel_g_edid, + .vidioc_s_edid = tegra_channel_s_edid, .vidioc_g_dv_timings = tegra_channel_g_dv_timings, .vidioc_s_dv_timings = tegra_channel_s_dv_timings, .vidioc_query_dv_timings = tegra_channel_query_dv_timings, -- cgit v1.2.3 From 6a4d30ce09ba13ee49cf928dee53c51209a678dc Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:36 +0100 Subject: media: tegra-video: Add support for VIDIOC_LOG_STATUS ioctl This patch adds support for log_status ioctl. Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-video/vi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index bc38136eae35..c280117a523c 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -838,6 +838,15 @@ static int tegra_channel_dv_timings_cap(struct file *file, void *fh, return v4l2_subdev_call(subdev, pad, dv_timings_cap, cap); } +static int tegra_channel_log_status(struct file *file, void *fh) +{ + struct tegra_vi_channel *chan = video_drvdata(file); + + v4l2_device_call_all(chan->video.v4l2_dev, 0, core, log_status); + + return 0; +} + static int tegra_channel_enum_input(struct file *file, void *fh, struct v4l2_input *inp) { @@ -906,6 +915,7 @@ static const struct v4l2_ioctl_ops tegra_channel_ioctl_ops = { .vidioc_query_dv_timings = tegra_channel_query_dv_timings, .vidioc_enum_dv_timings = tegra_channel_enum_dv_timings, .vidioc_dv_timings_cap = tegra_channel_dv_timings_cap, + .vidioc_log_status = tegra_channel_log_status, }; /* -- cgit v1.2.3 From 2be21e68345b5cfb908ba47aa38c3a49583a0760 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:37 +0100 Subject: media: tegra-video: Add support for V4L2_EVENT_SOURCE_CHANGE Current implementation uses v4l2_ctrl_subscribe_event() and this does not handle V4L2_EVENT_SOURCE_CHANGE. So, update driver to handle V4L2_EVENT_SOURCE_CHANGE. Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-video/vi.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index c280117a523c..0a85e527ec40 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -642,6 +642,18 @@ static int tegra_channel_set_subdev_active_fmt(struct tegra_vi_channel *chan) return 0; } +static int +tegra_channel_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_event_subscribe(fh, sub, 4, NULL); + } + + return v4l2_ctrl_subscribe_event(fh, sub); +} + static int tegra_channel_g_selection(struct file *file, void *priv, struct v4l2_selection *sel) { @@ -904,7 +916,7 @@ static const struct v4l2_ioctl_ops tegra_channel_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_subscribe_event = tegra_channel_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, .vidioc_g_selection = tegra_channel_g_selection, .vidioc_s_selection = tegra_channel_s_selection, -- cgit v1.2.3 From 4fe27eb68caca9db1324ee958140cf4a83ecdc9a Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:38 +0100 Subject: media: tegra-video: Implement V4L2 device notify callback Implement V4L2 device notify callback to handle V4L2_EVENT_SOURCE_CHANGE event from subdevices. HDMI-to-CSI bridge drivers trigger V4L2_EVENT_SOURCE_CHANGE when source DV timing changes are detected or when HDMI hotplug happens. Runtime source parameter changes during active streaming is not allowed and Tegra video driver calls vb2_queue_error to signal a fatal error if a notification of this event happens during an active streaming. Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-video/vi.c | 3 +++ drivers/staging/media/tegra-video/video.c | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 0a85e527ec40..d42a218d3ee9 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -1636,6 +1636,9 @@ static int tegra_vi_graph_notify_complete(struct v4l2_async_notifier *notifier) v4l2_set_subdev_hostdata(subdev, chan); + subdev = tegra_channel_get_remote_source_subdev(chan); + v4l2_set_subdev_hostdata(subdev, chan); + return 0; unregister_video: diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c index e50bd70575f3..d966b319553f 100644 --- a/drivers/staging/media/tegra-video/video.c +++ b/drivers/staging/media/tegra-video/video.c @@ -7,6 +7,8 @@ #include #include +#include + #include "video.h" static void tegra_v4l2_dev_release(struct v4l2_device *v4l2_dev) @@ -24,6 +26,21 @@ static void tegra_v4l2_dev_release(struct v4l2_device *v4l2_dev) kfree(vid); } +static void tegra_v4l2_dev_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct tegra_vi_channel *chan; + const struct v4l2_event *ev = arg; + + if (notification != V4L2_DEVICE_NOTIFY_EVENT) + return; + + chan = v4l2_get_subdev_hostdata(sd); + v4l2_event_queue(&chan->video, arg); + if (ev->type == V4L2_EVENT_SOURCE_CHANGE && vb2_is_streaming(&chan->queue)) + vb2_queue_error(&chan->queue); +} + static int host1x_video_probe(struct host1x_device *dev) { struct tegra_video_device *vid; @@ -49,6 +66,7 @@ static int host1x_video_probe(struct host1x_device *dev) vid->v4l2_dev.mdev = &vid->media_dev; vid->v4l2_dev.release = tegra_v4l2_dev_release; + vid->v4l2_dev.notify = tegra_v4l2_dev_notify; ret = v4l2_device_register(&dev->dev, &vid->v4l2_dev); if (ret < 0) { dev_err(&dev->dev, -- cgit v1.2.3 From 8f81888bec5c0a5f27083ca58024fb80fe902393 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:39 +0100 Subject: media: v4l2-fwnode: Update V4L2_FWNODE_CSI2_MAX_DATA_LANES to 8 Some CSI2 receivers support 8 data lanes. So, this patch updates CSI2 maximum data lanes to be 8. Acked-by: Sakari Ailus Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-fwnode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index 4365430eea6f..6d026dadbf98 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -25,7 +25,7 @@ struct fwnode_handle; struct v4l2_async_notifier; struct v4l2_async_subdev; -#define V4L2_FWNODE_CSI2_MAX_DATA_LANES 4 +#define V4L2_FWNODE_CSI2_MAX_DATA_LANES 8 /** * struct v4l2_fwnode_bus_mipi_csi2 - MIPI CSI-2 bus data structure -- cgit v1.2.3 From f8c9dd2b826d8f1c23b8e86d5e4135a668a7bdd4 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:40 +0100 Subject: media: dt-bindings: tegra: Update csi data-lanes to maximum 8 lanes Tegra VI/CSI hardware don't have native 8 lane CSI RX port. But x8 capture can be supported by using consecutive x4 ports simultaneously with HDMI-to-CSI bridges where source image is split on to two x4 ports. This patch updates dt-bindings for csi endpoint data-lane property with maximum of 8 lanes. Acked-by: Rob Herring Acked-by: Sakari Ailus Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt index 34d993338453..8a6d3e1ee306 100644 --- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt @@ -111,8 +111,8 @@ of the following host1x client modules: endpoint (required node) Required properties: - - data-lanes: an array of data lane from 1 to 4. Valid array - lengths are 1/2/4. + - data-lanes: an array of data lane from 1 to 8. Valid array + lengths are 1/2/4/8. - remote-endpoint: phandle to sensor 'endpoint' node. port@1 (required node) -- cgit v1.2.3 From 2ac4035a78c93330025d005009c132f5552ce4bc Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:41 +0100 Subject: media: tegra-video: Add support for x8 captures with gang ports Tegra VI/CSI hardware don't have native 8 lane capture support. Each CSI port has max 4 lanes only. So for x8 captures, consecutive ports are ganged up for left half and right half captures on to each x4 ports with buffer offsets based on source image split width to align side-by-side. All ports in gang are configured together during the corresponding video device node streaming for x8 captures. x8 capture with gang ports are supported with HDMI-to-CSI bridges where they split 4K image into left half onto one x4 port and right half onto second x4 port. Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-video/csi.c | 35 ++- drivers/staging/media/tegra-video/csi.h | 14 +- drivers/staging/media/tegra-video/tegra210.c | 304 ++++++++++++++++++--------- drivers/staging/media/tegra-video/vi.c | 139 +++++++++--- drivers/staging/media/tegra-video/vi.h | 19 +- 5 files changed, 362 insertions(+), 149 deletions(-) diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c index a19c85c57fca..033a6935c26d 100644 --- a/drivers/staging/media/tegra-video/csi.c +++ b/drivers/staging/media/tegra-video/csi.c @@ -253,13 +253,14 @@ static unsigned int csi_get_pixel_rate(struct tegra_csi_channel *csi_chan) } void tegra_csi_calc_settle_time(struct tegra_csi_channel *csi_chan, + u8 csi_port_num, u8 *clk_settle_time, u8 *ths_settle_time) { struct tegra_csi *csi = csi_chan->csi; unsigned int cil_clk_mhz; unsigned int pix_clk_mhz; - int clk_idx = (csi_chan->csi_port_num >> 1) + 1; + int clk_idx = (csi_port_num >> 1) + 1; cil_clk_mhz = clk_get_rate(csi->clks[clk_idx].clk) / MHZ; pix_clk_mhz = csi_get_pixel_rate(csi_chan) / MHZ; @@ -410,7 +411,7 @@ static int tegra_csi_channel_alloc(struct tegra_csi *csi, unsigned int num_pads) { struct tegra_csi_channel *chan; - int ret = 0; + int ret = 0, i; chan = kzalloc(sizeof(*chan), GFP_KERNEL); if (!chan) @@ -418,8 +419,21 @@ static int tegra_csi_channel_alloc(struct tegra_csi *csi, list_add_tail(&chan->list, &csi->csi_chans); chan->csi = csi; - chan->csi_port_num = port_num; - chan->numlanes = lanes; + /* + * Each CSI brick has maximum of 4 lanes. + * For lanes more than 4, use multiple of immediate CSI bricks as gang. + */ + if (lanes <= CSI_LANES_PER_BRICK) { + chan->numlanes = lanes; + chan->numgangports = 1; + } else { + chan->numlanes = CSI_LANES_PER_BRICK; + chan->numgangports = lanes / CSI_LANES_PER_BRICK; + } + + for (i = 0; i < chan->numgangports; i++) + chan->csi_port_nums[i] = port_num + i * CSI_PORTS_PER_BRICK; + chan->of_node = node; chan->numpads = num_pads; if (num_pads & 0x2) { @@ -500,7 +514,14 @@ static int tegra_csi_channels_alloc(struct tegra_csi *csi) } lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; - if (!lanes || ((lanes & (lanes - 1)) != 0)) { + /* + * Each CSI brick has maximum 4 data lanes. + * For lanes more than 4, validate lanes to be multiple of 4 + * so multiple of consecutive CSI bricks can be ganged up for + * streaming. + */ + if (!lanes || ((lanes & (lanes - 1)) != 0) || + (lanes > CSI_LANES_PER_BRICK && ((portno & 1) != 0))) { dev_err(csi->dev, "invalid data-lanes %d for %pOF\n", lanes, channel); ret = -EINVAL; @@ -544,7 +565,7 @@ static int tegra_csi_channel_init(struct tegra_csi_channel *chan) subdev->dev = csi->dev; if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "%s-%d", "tpg", - chan->csi_port_num); + chan->csi_port_nums[0]); else snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "%s", kbasename(chan->of_node->full_name)); @@ -596,7 +617,7 @@ static int tegra_csi_channels_init(struct tegra_csi *csi) if (ret) { dev_err(csi->dev, "failed to initialize channel-%d: %d\n", - chan->csi_port_num, ret); + chan->csi_port_nums[0], ret); return ret; } } diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h index c65ff73b1cdc..386f7c664259 100644 --- a/drivers/staging/media/tegra-video/csi.h +++ b/drivers/staging/media/tegra-video/csi.h @@ -17,6 +17,10 @@ * CILB. */ #define CSI_PORTS_PER_BRICK 2 +#define CSI_LANES_PER_BRICK 4 + +/* Maximum 2 CSI x4 ports can be ganged up for streaming */ +#define GANG_PORTS_MAX 2 /* each CSI channel can have one sink and one source pads */ #define TEGRA_CSI_PADS_NUM 2 @@ -43,8 +47,10 @@ struct tegra_csi; * @numpads: number of pads. * @csi: Tegra CSI device structure * @of_node: csi device tree node - * @numlanes: number of lanes used per port/channel - * @csi_port_num: CSI channel port number + * @numgangports: number of immediate ports ganged up to meet the + * channel bus-width + * @numlanes: number of lanes used per port + * @csi_port_nums: CSI channel port numbers * @pg_mode: test pattern generator mode for channel * @format: active format of the channel * @framerate: active framerate for TPG @@ -60,8 +66,9 @@ struct tegra_csi_channel { unsigned int numpads; struct tegra_csi *csi; struct device_node *of_node; + u8 numgangports; unsigned int numlanes; - u8 csi_port_num; + u8 csi_port_nums[GANG_PORTS_MAX]; u8 pg_mode; struct v4l2_mbus_framefmt format; unsigned int framerate; @@ -150,6 +157,7 @@ extern const struct tegra_csi_soc tegra210_csi_soc; void tegra_csi_error_recover(struct v4l2_subdev *subdev); void tegra_csi_calc_settle_time(struct tegra_csi_channel *csi_chan, + u8 csi_port_num, u8 *clk_settle_time, u8 *ths_settle_time); #endif diff --git a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c index b731aa54dda1..063d0a33bf71 100644 --- a/drivers/staging/media/tegra-video/tegra210.c +++ b/drivers/staging/media/tegra-video/tegra210.c @@ -149,21 +149,22 @@ static u32 tegra_vi_read(struct tegra_vi_channel *chan, unsigned int addr) } /* Tegra210 VI_CSI registers accessors */ -static void vi_csi_write(struct tegra_vi_channel *chan, unsigned int addr, - u32 val) +static void vi_csi_write(struct tegra_vi_channel *chan, u8 portno, + unsigned int addr, u32 val) { void __iomem *vi_csi_base; - vi_csi_base = chan->vi->iomem + TEGRA210_VI_CSI_BASE(chan->portno); + vi_csi_base = chan->vi->iomem + TEGRA210_VI_CSI_BASE(portno); writel_relaxed(val, vi_csi_base + addr); } -static u32 vi_csi_read(struct tegra_vi_channel *chan, unsigned int addr) +static u32 vi_csi_read(struct tegra_vi_channel *chan, u8 portno, + unsigned int addr) { void __iomem *vi_csi_base; - vi_csi_base = chan->vi->iomem + TEGRA210_VI_CSI_BASE(chan->portno); + vi_csi_base = chan->vi->iomem + TEGRA210_VI_CSI_BASE(portno); return readl_relaxed(vi_csi_base + addr); } @@ -171,7 +172,8 @@ static u32 vi_csi_read(struct tegra_vi_channel *chan, unsigned int addr) /* * Tegra210 VI channel capture operations */ -static int tegra_channel_capture_setup(struct tegra_vi_channel *chan) +static int tegra_channel_capture_setup(struct tegra_vi_channel *chan, + u8 portno) { u32 height = chan->format.height; u32 width = chan->format.width; @@ -192,19 +194,30 @@ static int tegra_channel_capture_setup(struct tegra_vi_channel *chan) data_type == TEGRA_IMAGE_DT_RGB888) bypass_pixel_transform = 0; - vi_csi_write(chan, TEGRA_VI_CSI_ERROR_STATUS, 0xffffffff); - vi_csi_write(chan, TEGRA_VI_CSI_IMAGE_DEF, + /* + * For x8 source streaming, the source image is split onto two x4 ports + * with left half to first x4 port and right half to second x4 port. + * So, use split width and corresponding word count for each x4 port. + */ + if (chan->numgangports > 1) { + width = width >> 1; + word_count = (width * chan->fmtinfo->bit_width) / 8; + } + + vi_csi_write(chan, portno, TEGRA_VI_CSI_ERROR_STATUS, 0xffffffff); + vi_csi_write(chan, portno, TEGRA_VI_CSI_IMAGE_DEF, bypass_pixel_transform | (format << IMAGE_DEF_FORMAT_OFFSET) | IMAGE_DEF_DEST_MEM); - vi_csi_write(chan, TEGRA_VI_CSI_IMAGE_DT, data_type); - vi_csi_write(chan, TEGRA_VI_CSI_IMAGE_SIZE_WC, word_count); - vi_csi_write(chan, TEGRA_VI_CSI_IMAGE_SIZE, + vi_csi_write(chan, portno, TEGRA_VI_CSI_IMAGE_DT, data_type); + vi_csi_write(chan, portno, TEGRA_VI_CSI_IMAGE_SIZE_WC, word_count); + vi_csi_write(chan, portno, TEGRA_VI_CSI_IMAGE_SIZE, (height << IMAGE_SIZE_HEIGHT_OFFSET) | width); return 0; } -static void tegra_channel_vi_soft_reset(struct tegra_vi_channel *chan) +static void tegra_channel_vi_soft_reset(struct tegra_vi_channel *chan, + u8 portno) { /* disable clock gating to enable continuous clock */ tegra_vi_write(chan, TEGRA_VI_CFG_CG_CTRL, 0); @@ -212,15 +225,16 @@ static void tegra_channel_vi_soft_reset(struct tegra_vi_channel *chan) * Soft reset memory client interface, pixel format logic, sensor * control logic, and a shadow copy logic to bring VI to clean state. */ - vi_csi_write(chan, TEGRA_VI_CSI_SW_RESET, 0xf); + vi_csi_write(chan, portno, TEGRA_VI_CSI_SW_RESET, 0xf); usleep_range(100, 200); - vi_csi_write(chan, TEGRA_VI_CSI_SW_RESET, 0x0); + vi_csi_write(chan, portno, TEGRA_VI_CSI_SW_RESET, 0x0); /* enable back VI clock gating */ tegra_vi_write(chan, TEGRA_VI_CFG_CG_CTRL, VI_CG_2ND_LEVEL_EN); } -static void tegra_channel_capture_error_recover(struct tegra_vi_channel *chan) +static void tegra_channel_capture_error_recover(struct tegra_vi_channel *chan, + u8 portno) { struct v4l2_subdev *subdev; u32 val; @@ -232,9 +246,9 @@ static void tegra_channel_capture_error_recover(struct tegra_vi_channel *chan) * events which can cause CSI and VI hardware hang. * This helps to have a clean capture for next frame. */ - val = vi_csi_read(chan, TEGRA_VI_CSI_ERROR_STATUS); + val = vi_csi_read(chan, portno, TEGRA_VI_CSI_ERROR_STATUS); dev_dbg(&chan->video.dev, "TEGRA_VI_CSI_ERROR_STATUS 0x%08x\n", val); - vi_csi_write(chan, TEGRA_VI_CSI_ERROR_STATUS, val); + vi_csi_write(chan, portno, TEGRA_VI_CSI_ERROR_STATUS, val); val = tegra_vi_read(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR); dev_dbg(&chan->video.dev, @@ -242,8 +256,8 @@ static void tegra_channel_capture_error_recover(struct tegra_vi_channel *chan) tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR, val); /* recover VI by issuing software reset and re-setup for capture */ - tegra_channel_vi_soft_reset(chan); - tegra_channel_capture_setup(chan); + tegra_channel_vi_soft_reset(chan, portno); + tegra_channel_capture_setup(chan, portno); /* recover CSI block */ subdev = tegra_channel_get_remote_csi_subdev(chan); @@ -282,80 +296,114 @@ static void release_buffer(struct tegra_vi_channel *chan, vb2_buffer_done(&vb->vb2_buf, state); } -static int tegra_channel_capture_frame(struct tegra_vi_channel *chan, - struct tegra_channel_buffer *buf) +static void tegra_channel_vi_buffer_setup(struct tegra_vi_channel *chan, + u8 portno, u32 buf_offset, + struct tegra_channel_buffer *buf) { - u32 thresh, value, frame_start, mw_ack_done; - int bytes_per_line = chan->format.bytesperline; + int bytesperline = chan->format.bytesperline; u32 sizeimage = chan->format.sizeimage; - int err; /* program buffer address by using surface 0 */ - vi_csi_write(chan, TEGRA_VI_CSI_SURFACE0_OFFSET_MSB, - (u64)buf->addr >> 32); - vi_csi_write(chan, TEGRA_VI_CSI_SURFACE0_OFFSET_LSB, buf->addr); - vi_csi_write(chan, TEGRA_VI_CSI_SURFACE0_STRIDE, bytes_per_line); + vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE0_OFFSET_MSB, + ((u64)buf->addr + buf_offset) >> 32); + vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE0_OFFSET_LSB, + buf->addr + buf_offset); + vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE0_STRIDE, bytesperline); + if (chan->fmtinfo->fourcc != V4L2_PIX_FMT_NV16) + return; /* * Program surface 1 for UV plane with offset sizeimage from Y plane. */ - if (chan->fmtinfo->fourcc == V4L2_PIX_FMT_NV16) { - vi_csi_write(chan, TEGRA_VI_CSI_SURFACE1_OFFSET_MSB, - ((u64)buf->addr + sizeimage / 2) >> 32); - vi_csi_write(chan, TEGRA_VI_CSI_SURFACE1_OFFSET_LSB, - buf->addr + sizeimage / 2); - vi_csi_write(chan, TEGRA_VI_CSI_SURFACE1_STRIDE, - bytes_per_line); - } - - /* - * Tegra VI block interacts with host1x syncpt for synchronizing - * programmed condition of capture state and hardware operation. - * Frame start and Memory write acknowledge syncpts has their own - * FIFO of depth 2. - * - * Syncpoint trigger conditions set through VI_INCR_SYNCPT register - * are added to HW syncpt FIFO and when the HW triggers, syncpt - * condition is removed from the FIFO and counter at syncpoint index - * will be incremented by the hardware and software can wait for - * counter to reach threshold to synchronize capturing frame with the - * hardware capture events. - */ + vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE1_OFFSET_MSB, + (((u64)buf->addr + sizeimage / 2) + buf_offset) >> 32); + vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE1_OFFSET_LSB, + buf->addr + sizeimage / 2 + buf_offset); + vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE1_STRIDE, bytesperline); +} - /* increase channel syncpoint threshold for FRAME_START */ - thresh = host1x_syncpt_incr_max(chan->frame_start_sp, 1); +static int tegra_channel_capture_frame(struct tegra_vi_channel *chan, + struct tegra_channel_buffer *buf) +{ + u32 thresh, value, frame_start, mw_ack_done; + u32 fs_thresh[GANG_PORTS_MAX]; + u8 *portnos = chan->portnos; + int gang_bpl = (chan->format.width >> 1) * chan->fmtinfo->bpp; + u32 buf_offset; + bool capture_timedout = false; + int err, i; + + for (i = 0; i < chan->numgangports; i++) { + /* + * Align buffers side-by-side for all consecutive x4 ports + * in gang ports using bytes per line based on source split + * width. + */ + buf_offset = i * roundup(gang_bpl, SURFACE_ALIGN_BYTES); + tegra_channel_vi_buffer_setup(chan, portnos[i], buf_offset, + buf); - /* Program FRAME_START trigger condition syncpt request */ - frame_start = VI_CSI_PP_FRAME_START(chan->portno); - value = VI_CFG_VI_INCR_SYNCPT_COND(frame_start) | - host1x_syncpt_id(chan->frame_start_sp); - tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT, value); + /* + * Tegra VI block interacts with host1x syncpt to synchronize + * programmed condition and hardware operation for capture. + * Frame start and Memory write acknowledge syncpts has their + * own FIFO of depth 2. + * + * Syncpoint trigger conditions set through VI_INCR_SYNCPT + * register are added to HW syncpt FIFO and when HW triggers, + * syncpt condition is removed from the FIFO and counter at + * syncpoint index will be incremented by the hardware and + * software can wait for counter to reach threshold to + * synchronize capturing frame with hardware capture events. + */ - /* increase channel syncpoint threshold for MW_ACK_DONE */ - buf->mw_ack_sp_thresh = host1x_syncpt_incr_max(chan->mw_ack_sp, 1); + /* increase channel syncpoint threshold for FRAME_START */ + thresh = host1x_syncpt_incr_max(chan->frame_start_sp[i], 1); + fs_thresh[i] = thresh; + + /* Program FRAME_START trigger condition syncpt request */ + frame_start = VI_CSI_PP_FRAME_START(portnos[i]); + value = VI_CFG_VI_INCR_SYNCPT_COND(frame_start) | + host1x_syncpt_id(chan->frame_start_sp[i]); + tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT, value); + + /* increase channel syncpoint threshold for MW_ACK_DONE */ + thresh = host1x_syncpt_incr_max(chan->mw_ack_sp[i], 1); + buf->mw_ack_sp_thresh[i] = thresh; + + /* Program MW_ACK_DONE trigger condition syncpt request */ + mw_ack_done = VI_CSI_MW_ACK_DONE(portnos[i]); + value = VI_CFG_VI_INCR_SYNCPT_COND(mw_ack_done) | + host1x_syncpt_id(chan->mw_ack_sp[i]); + tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT, value); + } - /* Program MW_ACK_DONE trigger condition syncpt request */ - mw_ack_done = VI_CSI_MW_ACK_DONE(chan->portno); - value = VI_CFG_VI_INCR_SYNCPT_COND(mw_ack_done) | - host1x_syncpt_id(chan->mw_ack_sp); - tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT, value); + /* enable single shot capture after all ganged ports are ready */ + for (i = 0; i < chan->numgangports; i++) + vi_csi_write(chan, portnos[i], TEGRA_VI_CSI_SINGLE_SHOT, + SINGLE_SHOT_CAPTURE); - /* enable single shot capture */ - vi_csi_write(chan, TEGRA_VI_CSI_SINGLE_SHOT, SINGLE_SHOT_CAPTURE); + for (i = 0; i < chan->numgangports; i++) { + /* + * Wait for syncpt counter to reach frame start event threshold + */ + err = host1x_syncpt_wait(chan->frame_start_sp[i], fs_thresh[i], + TEGRA_VI_SYNCPT_WAIT_TIMEOUT, &value); + if (err) { + capture_timedout = true; + /* increment syncpoint counter for timedout events */ + host1x_syncpt_incr(chan->frame_start_sp[i]); + spin_lock(&chan->sp_incr_lock[i]); + host1x_syncpt_incr(chan->mw_ack_sp[i]); + spin_unlock(&chan->sp_incr_lock[i]); + /* clear errors and recover */ + tegra_channel_capture_error_recover(chan, portnos[i]); + } + } - /* wait for syncpt counter to reach frame start event threshold */ - err = host1x_syncpt_wait(chan->frame_start_sp, thresh, - TEGRA_VI_SYNCPT_WAIT_TIMEOUT, &value); - if (err) { + if (capture_timedout) { dev_err_ratelimited(&chan->video.dev, "frame start syncpt timeout: %d\n", err); - /* increment syncpoint counter for timedout events */ - host1x_syncpt_incr(chan->frame_start_sp); - spin_lock(&chan->sp_incr_lock); - host1x_syncpt_incr(chan->mw_ack_sp); - spin_unlock(&chan->sp_incr_lock); - /* clear errors and recover */ - tegra_channel_capture_error_recover(chan); release_buffer(chan, buf, VB2_BUF_STATE_ERROR); return err; } @@ -376,21 +424,29 @@ static void tegra_channel_capture_done(struct tegra_vi_channel *chan, { enum vb2_buffer_state state = VB2_BUF_STATE_DONE; u32 value; - int ret; + bool capture_timedout = false; + int ret, i; - /* wait for syncpt counter to reach MW_ACK_DONE event threshold */ - ret = host1x_syncpt_wait(chan->mw_ack_sp, buf->mw_ack_sp_thresh, - TEGRA_VI_SYNCPT_WAIT_TIMEOUT, &value); - if (ret) { - dev_err_ratelimited(&chan->video.dev, - "MW_ACK_DONE syncpt timeout: %d\n", ret); - state = VB2_BUF_STATE_ERROR; - /* increment syncpoint counter for timedout event */ - spin_lock(&chan->sp_incr_lock); - host1x_syncpt_incr(chan->mw_ack_sp); - spin_unlock(&chan->sp_incr_lock); + for (i = 0; i < chan->numgangports; i++) { + /* + * Wait for syncpt counter to reach MW_ACK_DONE event threshold + */ + ret = host1x_syncpt_wait(chan->mw_ack_sp[i], + buf->mw_ack_sp_thresh[i], + TEGRA_VI_SYNCPT_WAIT_TIMEOUT, &value); + if (ret) { + capture_timedout = true; + state = VB2_BUF_STATE_ERROR; + /* increment syncpoint counter for timedout event */ + spin_lock(&chan->sp_incr_lock[i]); + host1x_syncpt_incr(chan->mw_ack_sp[i]); + spin_unlock(&chan->sp_incr_lock[i]); + } } + if (capture_timedout) + dev_err_ratelimited(&chan->video.dev, + "MW_ACK_DONE syncpt timeout: %d\n", ret); release_buffer(chan, buf, state); } @@ -463,14 +519,12 @@ static int tegra210_vi_start_streaming(struct vb2_queue *vq, u32 count) struct tegra_vi_channel *chan = vb2_get_drv_priv(vq); struct media_pipeline *pipe = &chan->video.pipe; u32 val; - int ret; + u8 *portnos = chan->portnos; + int ret, i; tegra_vi_write(chan, TEGRA_VI_CFG_CG_CTRL, VI_CG_2ND_LEVEL_EN); - /* clear errors */ - val = vi_csi_read(chan, TEGRA_VI_CSI_ERROR_STATUS); - vi_csi_write(chan, TEGRA_VI_CSI_ERROR_STATUS, val); - + /* clear syncpt errors */ val = tegra_vi_read(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR); tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR, val); @@ -489,7 +543,14 @@ static int tegra210_vi_start_streaming(struct vb2_queue *vq, u32 count) if (ret < 0) goto error_pipeline_start; - tegra_channel_capture_setup(chan); + /* clear csi errors and do capture setup for all ports in gang mode */ + for (i = 0; i < chan->numgangports; i++) { + val = vi_csi_read(chan, portnos[i], TEGRA_VI_CSI_ERROR_STATUS); + vi_csi_write(chan, portnos[i], TEGRA_VI_CSI_ERROR_STATUS, val); + + tegra_channel_capture_setup(chan, portnos[i]); + } + ret = tegra_channel_set_stream(chan, true); if (ret < 0) goto error_set_stream; @@ -743,10 +804,10 @@ static void tpg_write(struct tegra_csi *csi, u8 portno, unsigned int addr, /* * Tegra210 CSI operations */ -static void tegra210_csi_error_recover(struct tegra_csi_channel *csi_chan) +static void tegra210_csi_port_recover(struct tegra_csi_channel *csi_chan, + u8 portno) { struct tegra_csi *csi = csi_chan->csi; - unsigned int portno = csi_chan->csi_port_num; u32 val; /* @@ -795,16 +856,26 @@ static void tegra210_csi_error_recover(struct tegra_csi_channel *csi_chan) } } -static int tegra210_csi_start_streaming(struct tegra_csi_channel *csi_chan) +static void tegra210_csi_error_recover(struct tegra_csi_channel *csi_chan) +{ + u8 *portnos = csi_chan->csi_port_nums; + int i; + + for (i = 0; i < csi_chan->numgangports; i++) + tegra210_csi_port_recover(csi_chan, portnos[i]); +} + +static int +tegra210_csi_port_start_streaming(struct tegra_csi_channel *csi_chan, + u8 portno) { struct tegra_csi *csi = csi_chan->csi; - unsigned int portno = csi_chan->csi_port_num; u8 clk_settle_time = 0; u8 ths_settle_time = 10; u32 val; if (!csi_chan->pg_mode) - tegra_csi_calc_settle_time(csi_chan, &clk_settle_time, + tegra_csi_calc_settle_time(csi_chan, portno, &clk_settle_time, &ths_settle_time); csi_write(csi, portno, TEGRA_CSI_CLKEN_OVERRIDE, 0); @@ -903,10 +974,10 @@ static int tegra210_csi_start_streaming(struct tegra_csi_channel *csi_chan) return 0; } -static void tegra210_csi_stop_streaming(struct tegra_csi_channel *csi_chan) +static void +tegra210_csi_port_stop_streaming(struct tegra_csi_channel *csi_chan, u8 portno) { struct tegra_csi *csi = csi_chan->csi; - unsigned int portno = csi_chan->csi_port_num; u32 val; val = pp_read(csi, portno, TEGRA_CSI_PIXEL_PARSER_STATUS); @@ -944,6 +1015,35 @@ static void tegra210_csi_stop_streaming(struct tegra_csi_channel *csi_chan) } } +static int tegra210_csi_start_streaming(struct tegra_csi_channel *csi_chan) +{ + u8 *portnos = csi_chan->csi_port_nums; + int ret, i; + + for (i = 0; i < csi_chan->numgangports; i++) { + ret = tegra210_csi_port_start_streaming(csi_chan, portnos[i]); + if (ret) + goto stream_start_fail; + } + + return 0; + +stream_start_fail: + for (i = i - 1; i >= 0; i--) + tegra210_csi_port_stop_streaming(csi_chan, portnos[i]); + + return ret; +} + +static void tegra210_csi_stop_streaming(struct tegra_csi_channel *csi_chan) +{ + u8 *portnos = csi_chan->csi_port_nums; + int i; + + for (i = 0; i < csi_chan->numgangports; i++) + tegra210_csi_port_stop_streaming(csi_chan, portnos[i]); +} + /* * Tegra210 CSI TPG frame rate table with horizontal and vertical * blanking intervals for corresponding format and resolution. diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index d42a218d3ee9..4773281e440f 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -30,7 +30,6 @@ #include "vi.h" #include "video.h" -#define SURFACE_ALIGN_BYTES 64 #define MAX_CID_CONTROLS 1 static const struct tegra_video_format tegra_default_format = { @@ -573,6 +572,14 @@ static int tegra_channel_try_format(struct file *file, void *fh, return __tegra_channel_try_format(chan, &format->fmt.pix); } +static void tegra_channel_update_gangports(struct tegra_vi_channel *chan) +{ + if (chan->format.width <= 1920) + chan->numgangports = 1; + else + chan->numgangports = chan->totalports; +} + static int tegra_channel_set_format(struct file *file, void *fh, struct v4l2_format *format) { @@ -606,6 +613,7 @@ static int tegra_channel_set_format(struct file *file, void *fh, chan->format = *pix; chan->fmtinfo = fmtinfo; + tegra_channel_update_gangports(chan); return 0; } @@ -638,6 +646,7 @@ static int tegra_channel_set_subdev_active_fmt(struct tegra_vi_channel *chan) chan->format.sizeimage = chan->format.bytesperline * chan->format.height; tegra_channel_fmt_align(chan, &chan->format, chan->fmtinfo->bpp); + tegra_channel_update_gangports(chan); return 0; } @@ -806,6 +815,7 @@ static int tegra_channel_s_dv_timings(struct file *file, void *fh, chan->format.bytesperline = bt->width * chan->fmtinfo->bpp; chan->format.sizeimage = chan->format.bytesperline * bt->height; tegra_channel_fmt_align(chan, &chan->format, chan->fmtinfo->bpp); + tegra_channel_update_gangports(chan); return 0; } @@ -1092,12 +1102,21 @@ static int vi_fmts_bitmap_init(struct tegra_vi_channel *chan) return 0; } +static void tegra_channel_host1x_syncpts_free(struct tegra_vi_channel *chan) +{ + int i; + + for (i = 0; i < chan->numgangports; i++) { + host1x_syncpt_free(chan->mw_ack_sp[i]); + host1x_syncpt_free(chan->frame_start_sp[i]); + } +} + static void tegra_channel_cleanup(struct tegra_vi_channel *chan) { v4l2_ctrl_handler_free(&chan->ctrl_handler); media_entity_cleanup(&chan->video.entity); - host1x_syncpt_free(chan->mw_ack_sp); - host1x_syncpt_free(chan->frame_start_sp); + tegra_channel_host1x_syncpts_free(chan); mutex_destroy(&chan->video_lock); } @@ -1115,11 +1134,46 @@ void tegra_channels_cleanup(struct tegra_vi *vi) } } +static int tegra_channel_host1x_syncpt_init(struct tegra_vi_channel *chan) +{ + struct tegra_vi *vi = chan->vi; + unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; + struct host1x_syncpt *fs_sp; + struct host1x_syncpt *mw_sp; + int ret, i; + + for (i = 0; i < chan->numgangports; i++) { + fs_sp = host1x_syncpt_request(&vi->client, flags); + if (!fs_sp) { + dev_err(vi->dev, "failed to request frame start syncpoint\n"); + ret = -ENOMEM; + goto free_syncpts; + } + + mw_sp = host1x_syncpt_request(&vi->client, flags); + if (!mw_sp) { + dev_err(vi->dev, "failed to request memory ack syncpoint\n"); + host1x_syncpt_free(fs_sp); + ret = -ENOMEM; + goto free_syncpts; + } + + chan->frame_start_sp[i] = fs_sp; + chan->mw_ack_sp[i] = mw_sp; + spin_lock_init(&chan->sp_incr_lock[i]); + } + + return 0; + +free_syncpts: + tegra_channel_host1x_syncpts_free(chan); + return ret; +} + static int tegra_channel_init(struct tegra_vi_channel *chan) { struct tegra_vi *vi = chan->vi; struct tegra_video_device *vid = dev_get_drvdata(vi->client.host); - unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; int ret; mutex_init(&chan->video_lock); @@ -1127,7 +1181,6 @@ static int tegra_channel_init(struct tegra_vi_channel *chan) INIT_LIST_HEAD(&chan->done); spin_lock_init(&chan->start_lock); spin_lock_init(&chan->done_lock); - spin_lock_init(&chan->sp_incr_lock); init_waitqueue_head(&chan->start_wait); init_waitqueue_head(&chan->done_wait); @@ -1142,18 +1195,9 @@ static int tegra_channel_init(struct tegra_vi_channel *chan) chan->format.sizeimage = chan->format.bytesperline * TEGRA_DEF_HEIGHT; tegra_channel_fmt_align(chan, &chan->format, chan->fmtinfo->bpp); - chan->frame_start_sp = host1x_syncpt_request(&vi->client, flags); - if (!chan->frame_start_sp) { - dev_err(vi->dev, "failed to request frame start syncpoint\n"); - return -ENOMEM; - } - - chan->mw_ack_sp = host1x_syncpt_request(&vi->client, flags); - if (!chan->mw_ack_sp) { - dev_err(vi->dev, "failed to request memory ack syncpoint\n"); - ret = -ENOMEM; - goto free_fs_syncpt; - } + ret = tegra_channel_host1x_syncpt_init(chan); + if (ret) + return ret; /* initialize the media entity */ chan->pad.flags = MEDIA_PAD_FL_SINK; @@ -1161,7 +1205,7 @@ static int tegra_channel_init(struct tegra_vi_channel *chan) if (ret < 0) { dev_err(vi->dev, "failed to initialize media entity: %d\n", ret); - goto free_mw_ack_syncpt; + goto free_syncpts; } ret = v4l2_ctrl_handler_init(&chan->ctrl_handler, MAX_CID_CONTROLS); @@ -1177,7 +1221,7 @@ static int tegra_channel_init(struct tegra_vi_channel *chan) chan->video.release = video_device_release_empty; chan->video.queue = &chan->queue; snprintf(chan->video.name, sizeof(chan->video.name), "%s-%s-%u", - dev_name(vi->dev), "output", chan->portno); + dev_name(vi->dev), "output", chan->portnos[0]); chan->video.vfl_type = VFL_TYPE_VIDEO; chan->video.vfl_dir = VFL_DIR_RX; chan->video.ioctl_ops = &tegra_channel_ioctl_ops; @@ -1213,17 +1257,16 @@ free_v4l2_ctrl_hdl: v4l2_ctrl_handler_free(&chan->ctrl_handler); cleanup_media: media_entity_cleanup(&chan->video.entity); -free_mw_ack_syncpt: - host1x_syncpt_free(chan->mw_ack_sp); -free_fs_syncpt: - host1x_syncpt_free(chan->frame_start_sp); +free_syncpts: + tegra_channel_host1x_syncpts_free(chan); return ret; } static int tegra_vi_channel_alloc(struct tegra_vi *vi, unsigned int port_num, - struct device_node *node) + struct device_node *node, unsigned int lanes) { struct tegra_vi_channel *chan; + unsigned int i; /* * Do not use devm_kzalloc as memory is freed immediately @@ -1236,7 +1279,20 @@ static int tegra_vi_channel_alloc(struct tegra_vi *vi, unsigned int port_num, return -ENOMEM; chan->vi = vi; - chan->portno = port_num; + chan->portnos[0] = port_num; + /* + * For data lanes more than maximum csi lanes per brick, multiple of + * x4 ports are used simultaneously for capture. + */ + if (lanes <= CSI_LANES_PER_BRICK) + chan->totalports = 1; + else + chan->totalports = lanes / CSI_LANES_PER_BRICK; + chan->numgangports = chan->totalports; + + for (i = 1; i < chan->totalports; i++) + chan->portnos[i] = chan->portnos[0] + i * CSI_PORTS_PER_BRICK; + chan->of_node = node; list_add_tail(&chan->list, &vi->vi_chans); @@ -1250,7 +1306,8 @@ static int tegra_vi_tpg_channels_alloc(struct tegra_vi *vi) int ret; for (port_num = 0; port_num < nchannels; port_num++) { - ret = tegra_vi_channel_alloc(vi, port_num, vi->dev->of_node); + ret = tegra_vi_channel_alloc(vi, port_num, + vi->dev->of_node, 2); if (ret < 0) return ret; } @@ -1265,6 +1322,9 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) struct device_node *ports; struct device_node *port; unsigned int port_num; + struct device_node *parent; + struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; + unsigned int lanes; int ret = 0; ports = of_get_child_by_name(node, "ports"); @@ -1291,8 +1351,21 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) if (!ep) continue; + parent = of_graph_get_remote_port_parent(ep); + of_node_put(ep); + if (!parent) + continue; + + ep = of_graph_get_endpoint_by_regs(parent, 0, 0); + of_node_put(parent); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), + &v4l2_ep); of_node_put(ep); - ret = tegra_vi_channel_alloc(vi, port_num, port); + if (ret) + continue; + + lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; + ret = tegra_vi_channel_alloc(vi, port_num, port, lanes); if (ret < 0) { of_node_put(port); goto cleanup; @@ -1314,7 +1387,7 @@ static int tegra_vi_channels_init(struct tegra_vi *vi) if (ret < 0) { dev_err(vi->dev, "failed to initialize channel-%d: %d\n", - chan->portno, ret); + chan->portnos[0], ret); goto cleanup; } } @@ -1761,7 +1834,8 @@ static int tegra_vi_graph_init(struct tegra_vi *vi) * next channels. */ list_for_each_entry(chan, &vi->vi_chans, list) { - remote = fwnode_graph_get_remote_node(fwnode, chan->portno, 0); + remote = fwnode_graph_get_remote_node(fwnode, chan->portnos[0], + 0); if (!remote) continue; @@ -1776,7 +1850,7 @@ static int tegra_vi_graph_init(struct tegra_vi *vi) if (ret < 0) { dev_err(vi->dev, "failed to register channel %d notifier: %d\n", - chan->portno, ret); + chan->portnos[0], ret); v4l2_async_notifier_cleanup(&chan->notifier); } } @@ -1827,11 +1901,14 @@ static int tegra_vi_init(struct host1x_client *client) if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) { ret = tegra_vi_graph_init(vi); if (ret < 0) - goto free_chans; + goto cleanup_chans; } return 0; +cleanup_chans: + list_for_each_entry(chan, &vi->vi_chans, list) + tegra_channel_cleanup(chan); free_chans: list_for_each_entry_safe(chan, tmp, &vi->vi_chans, list) { list_del(&chan->list); diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h index 7d6b7a6d0a45..27061a500ef2 100644 --- a/drivers/staging/media/tegra-video/vi.h +++ b/drivers/staging/media/tegra-video/vi.h @@ -21,6 +21,8 @@ #include #include +#include "csi.h" + #define TEGRA_MIN_WIDTH 32U #define TEGRA_MAX_WIDTH 32768U #define TEGRA_MIN_HEIGHT 32U @@ -31,6 +33,7 @@ #define TEGRA_IMAGE_FORMAT_DEF 32 #define MAX_FORMAT_NUM 64 +#define SURFACE_ALIGN_BYTES 64 enum tegra_vi_pg_mode { TEGRA_VI_PG_DISABLED = 0, @@ -151,7 +154,9 @@ struct tegra_vi_graph_entity { * @done: list of capture done queued buffers * @done_lock: protects the capture done queue list * - * @portno: VI channel port number + * @portnos: VI channel port numbers + * @totalports: total number of ports used for this channel + * @numgangports: number of ports combined together as a gang for capture * @of_node: device node of VI channel * * @ctrl_handler: V4L2 control handler of this video channel @@ -168,10 +173,10 @@ struct tegra_vi_channel { struct media_pad pad; struct tegra_vi *vi; - struct host1x_syncpt *frame_start_sp; - struct host1x_syncpt *mw_ack_sp; + struct host1x_syncpt *frame_start_sp[GANG_PORTS_MAX]; + struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX]; /* protects the cpu syncpoint increment */ - spinlock_t sp_incr_lock; + spinlock_t sp_incr_lock[GANG_PORTS_MAX]; struct task_struct *kthread_start_capture; wait_queue_head_t start_wait; @@ -190,7 +195,9 @@ struct tegra_vi_channel { /* protects the capture done queue list */ spinlock_t done_lock; - unsigned char portno; + unsigned char portnos[GANG_PORTS_MAX]; + u8 totalports; + u8 numgangports; struct device_node *of_node; struct v4l2_ctrl_handler ctrl_handler; @@ -216,7 +223,7 @@ struct tegra_channel_buffer { struct list_head queue; struct tegra_vi_channel *chan; dma_addr_t addr; - u32 mw_ack_sp_thresh; + u32 mw_ack_sp_thresh[GANG_PORTS_MAX]; }; /* -- cgit v1.2.3 From a45c39b8295f39930095f5a3693762f3ea454205 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Fri, 11 Dec 2020 18:02:42 +0100 Subject: media: tegra-video: Add custom V4L2 control V4L2_CID_TEGRA_SYNCPT_TIMEOUT_RETRY This patch adds custom V4L2 control for syncpt timeout retry to continue capture on error for specified retries count through this control. This is useful for HDMI-to-CSI bridge debug purposes like for hotplug scenarios or for ignoring captures till HDMI input is stabilized. Signed-off-by: Sowjanya Komatineni Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-video/tegra210.c | 10 +++++++++- drivers/staging/media/tegra-video/vi.c | 26 +++++++++++++++++++++++++- drivers/staging/media/tegra-video/vi.h | 4 ++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c index 063d0a33bf71..f10a041e3e6c 100644 --- a/drivers/staging/media/tegra-video/tegra210.c +++ b/drivers/staging/media/tegra-video/tegra210.c @@ -454,6 +454,7 @@ static int chan_capture_kthread_start(void *data) { struct tegra_vi_channel *chan = data; struct tegra_channel_buffer *buf; + unsigned int retries = 0; int err = 0; while (1) { @@ -483,8 +484,15 @@ static int chan_capture_kthread_start(void *data) spin_unlock(&chan->start_lock); err = tegra_channel_capture_frame(chan, buf); - if (err) + if (!err) { + retries = 0; + continue; + } + + if (retries++ > chan->syncpt_timeout_retry) vb2_queue_error(&chan->queue); + else + err = 0; } return 0; diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 4773281e440f..70e1e18644b2 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -956,7 +956,6 @@ static const struct v4l2_file_operations tegra_channel_fops = { /* * V4L2 control operations */ -#if IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG) static int vi_s_ctrl(struct v4l2_ctrl *ctrl) { struct tegra_vi_channel *chan = container_of(ctrl->handler, @@ -968,6 +967,9 @@ static int vi_s_ctrl(struct v4l2_ctrl *ctrl) /* pattern change takes effect on next stream */ chan->pg_mode = ctrl->val + 1; break; + case V4L2_CID_TEGRA_SYNCPT_TIMEOUT_RETRY: + chan->syncpt_timeout_retry = ctrl->val; + break; default: return -EINVAL; } @@ -979,10 +981,22 @@ static const struct v4l2_ctrl_ops vi_ctrl_ops = { .s_ctrl = vi_s_ctrl, }; +#if IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG) static const char *const vi_pattern_strings[] = { "Black/White Direct Mode", "Color Patch Mode", }; +#else +static const struct v4l2_ctrl_config syncpt_timeout_ctrl = { + .ops = &vi_ctrl_ops, + .id = V4L2_CID_TEGRA_SYNCPT_TIMEOUT_RETRY, + .name = "Syncpt timeout retry", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 10000, + .step = 1, + .def = 5, +}; #endif static int tegra_channel_setup_ctrl_handler(struct tegra_vi_channel *chan) @@ -1004,6 +1018,16 @@ static int tegra_channel_setup_ctrl_handler(struct tegra_vi_channel *chan) #else struct v4l2_subdev *subdev; + /* custom control */ + v4l2_ctrl_new_custom(&chan->ctrl_handler, &syncpt_timeout_ctrl, NULL); + if (chan->ctrl_handler.error) { + dev_err(chan->vi->dev, "failed to add %s ctrl handler: %d\n", + syncpt_timeout_ctrl.name, + chan->ctrl_handler.error); + v4l2_ctrl_handler_free(&chan->ctrl_handler); + return chan->ctrl_handler.error; + } + subdev = tegra_channel_get_remote_source_subdev(chan); if (!subdev) return -ENODEV; diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h index 27061a500ef2..a68e2c02c7b0 100644 --- a/drivers/staging/media/tegra-video/vi.h +++ b/drivers/staging/media/tegra-video/vi.h @@ -23,6 +23,8 @@ #include "csi.h" +#define V4L2_CID_TEGRA_SYNCPT_TIMEOUT_RETRY (V4L2_CTRL_CLASS_CAMERA | 0x1001) + #define TEGRA_MIN_WIDTH 32U #define TEGRA_MAX_WIDTH 32768U #define TEGRA_MIN_HEIGHT 32U @@ -160,6 +162,7 @@ struct tegra_vi_graph_entity { * @of_node: device node of VI channel * * @ctrl_handler: V4L2 control handler of this video channel + * @syncpt_timeout_retry: syncpt timeout retry count for the capture * @fmts_bitmap: a bitmap for supported formats matching v4l2 subdev formats * @tpg_fmts_bitmap: a bitmap for supported TPG formats * @pg_mode: test pattern generator mode (disabled/direct/patch) @@ -201,6 +204,7 @@ struct tegra_vi_channel { struct device_node *of_node; struct v4l2_ctrl_handler ctrl_handler; + unsigned int syncpt_timeout_retry; DECLARE_BITMAP(fmts_bitmap, MAX_FORMAT_NUM); DECLARE_BITMAP(tpg_fmts_bitmap, MAX_FORMAT_NUM); enum tegra_vi_pg_mode pg_mode; -- cgit v1.2.3 From 575c52cc4cae8b35654ea2d934d04e649a95cc6f Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 1 Dec 2020 13:44:41 +0100 Subject: media: videobuf2: always call poll_wait() on queues do_poll()/do_select() seem to set the _qproc member of poll_table to NULL the first time they are called on a given table, making subsequent calls of poll_wait() on that table no-ops. This is a problem for vb2 which calls poll_wait() on the V4L2 queues' waitqueues only when a queue-related event is requested, which may not necessarily be the case during the first poll. Fix this by making the call to poll_wait() happen first thing and unconditionally in vb2_core_poll(). Signed-off-by: Alexandre Courbot Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 89e38392509c..02281d13505f 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -2374,13 +2374,20 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, struct vb2_buffer *vb = NULL; unsigned long flags; + /* + * poll_wait() MUST be called on the first invocation on all the + * potential queues of interest, even if we are not interested in their + * events during this first call. Failure to do so will result in + * queue's events to be ignored because the poll_table won't be capable + * of adding new wait queues thereafter. + */ + poll_wait(file, &q->done_wq, wait); + if (!q->is_output && !(req_events & (EPOLLIN | EPOLLRDNORM))) return 0; if (q->is_output && !(req_events & (EPOLLOUT | EPOLLWRNORM))) return 0; - poll_wait(file, &q->done_wq, wait); - /* * Start file I/O emulator only if streaming API has not been used yet. */ -- cgit v1.2.3 From 726daf6bafe9d1d9e5c36e1e2a4008941fbc28bd Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 1 Dec 2020 13:44:42 +0100 Subject: media: v4l2-mem2mem: always call poll_wait() on queues do_poll()/do_select() seem to set the _qproc member of poll_table to NULL the first time they are called on a given table, making subsequent calls of poll_wait() on that table no-ops. This is a problem for mem2mem which calls poll_wait() on the V4L2 queues' waitqueues only when a queue-related event is requested, which may not necessarily be the case during the first poll. For instance, a stateful decoder is typically only interested in EPOLLPRI events when it starts, and will switch to listening to both EPOLLPRI and EPOLLIN after receiving the initial resolution change event and configuring the CAPTURE queue. However by the time that switch happens and v4l2_m2m_poll_for_data() is called for the first time, poll_wait() has become a no-op and the V4L2 queues waitqueues thus cannot be registered. Fix this by moving the registration to v4l2_m2m_poll() and do it whether or not one of the queue-related events are requested. Signed-off-by: Alexandre Courbot Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index b221b4e438a1..e7f4bf5bc8dd 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -887,9 +887,6 @@ static __poll_t v4l2_m2m_poll_for_data(struct file *file, src_q = v4l2_m2m_get_src_vq(m2m_ctx); dst_q = v4l2_m2m_get_dst_vq(m2m_ctx); - poll_wait(file, &src_q->done_wq, wait); - poll_wait(file, &dst_q->done_wq, wait); - /* * There has to be at least one buffer queued on each queued_list, which * means either in driver already or waiting for driver to claim it @@ -922,9 +919,21 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct poll_table_struct *wait) { struct video_device *vfd = video_devdata(file); + struct vb2_queue *src_q = v4l2_m2m_get_src_vq(m2m_ctx); + struct vb2_queue *dst_q = v4l2_m2m_get_dst_vq(m2m_ctx); __poll_t req_events = poll_requested_events(wait); __poll_t rc = 0; + /* + * poll_wait() MUST be called on the first invocation on all the + * potential queues of interest, even if we are not interested in their + * events during this first call. Failure to do so will result in + * queue's events to be ignored because the poll_table won't be capable + * of adding new wait queues thereafter. + */ + poll_wait(file, &src_q->done_wq, wait); + poll_wait(file, &dst_q->done_wq, wait); + if (req_events & (EPOLLOUT | EPOLLWRNORM | EPOLLIN | EPOLLRDNORM)) rc = v4l2_m2m_poll_for_data(file, m2m_ctx, wait); -- cgit v1.2.3 From 28955a61568ca4ef39ef7fc814b443be24616c64 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 Dec 2020 13:44:43 +0100 Subject: media: v4l2-dev/event: add v4l2_event_wake_all() When unregistering a V4L2 device node, make sure any filehandles that are waiting for an event are woken up. Add v4l2_event_wake_all() to v4l2-event.c and call it from video_unregister_device(). Otherwise userspace might never know that a device node was removed. [hverkuil: checkpatch: replaced 'if (vdev == NULL)' by 'if (!vdev)'] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 3 +++ drivers/media/v4l2-core/v4l2-event.c | 17 +++++++++++++++++ include/media/v4l2-event.h | 13 +++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index a593ea0598b5..0ddc3554f1a4 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -28,6 +28,7 @@ #include #include #include +#include #define VIDEO_NUM_DEVICES 256 #define VIDEO_NAME "video4linux" @@ -1086,6 +1087,8 @@ void video_unregister_device(struct video_device *vdev) */ clear_bit(V4L2_FL_REGISTERED, &vdev->flags); mutex_unlock(&videodev_lock); + if (test_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags)) + v4l2_event_wake_all(vdev); device_unregister(&vdev->dev); } EXPORT_SYMBOL(video_unregister_device); diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c index 290c6b213179..caad58bde326 100644 --- a/drivers/media/v4l2-core/v4l2-event.c +++ b/drivers/media/v4l2-core/v4l2-event.c @@ -187,6 +187,23 @@ int v4l2_event_pending(struct v4l2_fh *fh) } EXPORT_SYMBOL_GPL(v4l2_event_pending); +void v4l2_event_wake_all(struct video_device *vdev) +{ + struct v4l2_fh *fh; + unsigned long flags; + + if (!vdev) + return; + + spin_lock_irqsave(&vdev->fh_lock, flags); + + list_for_each_entry(fh, &vdev->fh_list, list) + wake_up_all(&fh->wait); + + spin_unlock_irqrestore(&vdev->fh_lock, flags); +} +EXPORT_SYMBOL_GPL(v4l2_event_wake_all); + static void __v4l2_event_unsubscribe(struct v4l2_subscribed_event *sev) { struct v4l2_fh *fh = sev->fh; diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h index 3f0281d06ec7..4ffa914ade3a 100644 --- a/include/media/v4l2-event.h +++ b/include/media/v4l2-event.h @@ -101,7 +101,7 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, * * .. note:: * The driver's only responsibility is to fill in the type and the data - * fields.The other fields will be filled in by V4L2. + * fields. The other fields will be filled in by V4L2. */ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev); @@ -116,10 +116,19 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev); * * .. note:: * The driver's only responsibility is to fill in the type and the data - * fields.The other fields will be filled in by V4L2. + * fields. The other fields will be filled in by V4L2. */ void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev); +/** + * v4l2_event_wake_all - Wake all filehandles. + * + * Used when unregistering a video device. + * + * @vdev: pointer to &struct video_device + */ +void v4l2_event_wake_all(struct video_device *vdev); + /** * v4l2_event_pending - Check if an event is available * -- cgit v1.2.3 From b996922bed2c506dce3eef087b87e0eda8bcc170 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 Dec 2020 13:44:44 +0100 Subject: media: vivid: call v4l2_event_wake_all() on disconnect When the disconnect error injection control is set, then besides faking unregistering the device nodes, also call v4l2_event_wake_all() to ensure any userspace applications will wake up as per a 'normal' unregister. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vivid/vivid-ctrls.c | 38 ++++++++++++-------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index 11e3b5617f52..7957eadf3e2b 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -100,6 +100,14 @@ /* General User Controls */ +static void vivid_unregister_dev(bool valid, struct video_device *vdev) +{ + if (!valid) + return; + clear_bit(V4L2_FL_REGISTERED, &vdev->flags); + v4l2_event_wake_all(vdev); +} + static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl) { struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_user_gen); @@ -108,26 +116,16 @@ static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl) case VIVID_CID_DISCONNECT: v4l2_info(&dev->v4l2_dev, "disconnect\n"); dev->disconnect_error = true; - if (dev->has_vid_cap) - clear_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags); - if (dev->has_vid_out) - clear_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags); - if (dev->has_vbi_cap) - clear_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags); - if (dev->has_vbi_out) - clear_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags); - if (dev->has_radio_rx) - clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); - if (dev->has_radio_tx) - clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); - if (dev->has_sdr_cap) - clear_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); - if (dev->has_meta_cap) - clear_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); - if (dev->has_meta_out) - clear_bit(V4L2_FL_REGISTERED, &dev->meta_out_dev.flags); - if (dev->has_touch_cap) - clear_bit(V4L2_FL_REGISTERED, &dev->touch_cap_dev.flags); + vivid_unregister_dev(dev->has_vid_cap, &dev->vid_cap_dev); + vivid_unregister_dev(dev->has_vid_out, &dev->vid_out_dev); + vivid_unregister_dev(dev->has_vbi_cap, &dev->vbi_cap_dev); + vivid_unregister_dev(dev->has_vbi_out, &dev->vbi_out_dev); + vivid_unregister_dev(dev->has_radio_rx, &dev->radio_rx_dev); + vivid_unregister_dev(dev->has_radio_tx, &dev->radio_tx_dev); + vivid_unregister_dev(dev->has_sdr_cap, &dev->sdr_cap_dev); + vivid_unregister_dev(dev->has_meta_cap, &dev->meta_cap_dev); + vivid_unregister_dev(dev->has_meta_out, &dev->meta_out_dev); + vivid_unregister_dev(dev->has_touch_cap, &dev->touch_cap_dev); break; case VIVID_CID_BUTTON: dev->button_pressed = 30; -- cgit v1.2.3 From 5cb0a64effe7608d6fd46f5be3106b1b73300621 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 Dec 2020 13:44:45 +0100 Subject: media: v4l2-dev: add EPOLLPRI in v4l2_poll() when dev is unregistered If the V4L2 device was unregistered, then add EPOLLPRI to the poll mask. Otherwise a select() that only waits for exceptions will not wake up. A select() that waits for read and/or write events *will* wake up on an EPOLLERR, but not (for some reason) if it just waits for exceptions. Strangly the epoll functionality will wakeup on EPOLLERR if you just wait for an exception, so in this respect select() and epoll differ. In the end it doesn't really matter, what matters is that polling file handles are woken up on device unregistration. It also improves the code a bit if vdev->fops->poll is NULL: this didn't check for device unregistration. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 0ddc3554f1a4..f9cff033d0dc 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -339,12 +339,14 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf, static __poll_t v4l2_poll(struct file *filp, struct poll_table_struct *poll) { struct video_device *vdev = video_devdata(filp); - __poll_t res = EPOLLERR | EPOLLHUP; + __poll_t res = EPOLLERR | EPOLLHUP | EPOLLPRI; - if (!vdev->fops->poll) - return DEFAULT_POLLMASK; - if (video_is_registered(vdev)) - res = vdev->fops->poll(filp, poll); + if (video_is_registered(vdev)) { + if (!vdev->fops->poll) + res = DEFAULT_POLLMASK; + else + res = vdev->fops->poll(filp, poll); + } if (vdev->dev_debug & V4L2_DEV_DEBUG_POLL) dprintk("%s: poll: %08x\n", video_device_node_name(vdev), res); -- cgit v1.2.3 From 4f20b7beca5a183139b22ed4fdfc161396452426 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 Dec 2020 13:44:46 +0100 Subject: media: cec: add EPOLLPRI in poll() when dev is unregistered If the CEC device was unregistered, then add EPOLLPRI to the poll() mask. Otherwise a select() that only waits for exceptions will not wake up. A select() that waits for read and/or write events *will* wake up on an EPOLLERR, but not (for some reason) if it just waits for exceptions. Strangly the epoll functionality will wakeup on EPOLLERR if you just wait for an exception, so in this respect select() and epoll differ. In the end it doesn't really matter, what matters is that polling file handles are woken up on device unregistration. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/cec/core/cec-api.c b/drivers/media/cec/core/cec-api.c index f922a2196b2b..769e6b4cddce 100644 --- a/drivers/media/cec/core/cec-api.c +++ b/drivers/media/cec/core/cec-api.c @@ -40,7 +40,7 @@ static __poll_t cec_poll(struct file *filp, poll_wait(filp, &fh->wait, poll); if (!cec_is_registered(adap)) - return EPOLLERR | EPOLLHUP; + return EPOLLERR | EPOLLHUP | EPOLLPRI; mutex_lock(&adap->lock); if (adap->is_configured && adap->transmit_queue_sz < CEC_MAX_MSG_TX_QUEUE_SZ) -- cgit v1.2.3 From ce814ad4bb52bfc7c0472e6da0aa742ab88f4361 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 14 Dec 2020 12:54:47 +0100 Subject: media: allegro: Fix use after free on error The "channel" is added to the "dev->channels" but then if v4l2_m2m_ctx_init() fails then we free "channel" but it's still on the list so it could lead to a use after free. Let's not add it to the list until after v4l2_m2m_ctx_init() succeeds. Fixes: cc62c74749a3 ("media: allegro: add missed checks in allegro_open()") Signed-off-by: Dan Carpenter Reviewed-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 9f718f43282b..640451134072 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -2483,8 +2483,6 @@ static int allegro_open(struct file *file) INIT_LIST_HEAD(&channel->buffers_reference); INIT_LIST_HEAD(&channel->buffers_intermediate); - list_add(&channel->list, &dev->channels); - channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel, allegro_queue_init); @@ -2493,6 +2491,7 @@ static int allegro_open(struct file *file) goto error; } + list_add(&channel->list, &dev->channels); file->private_data = &channel->fh; v4l2_fh_add(&channel->fh); -- cgit v1.2.3 From d74d4e2359ec7985831192f9b5ee22ed5e55b81c Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 2 Dec 2020 14:30:37 +0100 Subject: media: allegro: move driver out of staging The stateful encoder API was finalized. Nothing is blocking the driver from being moved out of staging. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- drivers/media/platform/Kconfig | 15 + drivers/media/platform/Makefile | 1 + drivers/media/platform/allegro-dvt/Makefile | 5 + drivers/media/platform/allegro-dvt/allegro-core.c | 3226 +++++++++++++++++++++ drivers/media/platform/allegro-dvt/allegro-mail.c | 543 ++++ drivers/media/platform/allegro-dvt/allegro-mail.h | 294 ++ drivers/media/platform/allegro-dvt/nal-h264.c | 1001 +++++++ drivers/media/platform/allegro-dvt/nal-h264.h | 208 ++ drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/allegro-dvt/Kconfig | 16 - drivers/staging/media/allegro-dvt/Makefile | 5 - drivers/staging/media/allegro-dvt/TODO | 4 - drivers/staging/media/allegro-dvt/allegro-core.c | 3226 --------------------- drivers/staging/media/allegro-dvt/allegro-mail.c | 543 ---- drivers/staging/media/allegro-dvt/allegro-mail.h | 294 -- drivers/staging/media/allegro-dvt/nal-h264.c | 1001 ------- drivers/staging/media/allegro-dvt/nal-h264.h | 208 -- 19 files changed, 5294 insertions(+), 5301 deletions(-) create mode 100644 drivers/media/platform/allegro-dvt/Makefile create mode 100644 drivers/media/platform/allegro-dvt/allegro-core.c create mode 100644 drivers/media/platform/allegro-dvt/allegro-mail.c create mode 100644 drivers/media/platform/allegro-dvt/allegro-mail.h create mode 100644 drivers/media/platform/allegro-dvt/nal-h264.c create mode 100644 drivers/media/platform/allegro-dvt/nal-h264.h delete mode 100644 drivers/staging/media/allegro-dvt/Kconfig delete mode 100644 drivers/staging/media/allegro-dvt/Makefile delete mode 100644 drivers/staging/media/allegro-dvt/TODO delete mode 100644 drivers/staging/media/allegro-dvt/allegro-core.c delete mode 100644 drivers/staging/media/allegro-dvt/allegro-mail.c delete mode 100644 drivers/staging/media/allegro-dvt/allegro-mail.h delete mode 100644 drivers/staging/media/allegro-dvt/nal-h264.c delete mode 100644 drivers/staging/media/allegro-dvt/nal-h264.h diff --git a/MAINTAINERS b/MAINTAINERS index 546aa66428c9..a40345e0477c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -699,7 +699,7 @@ M: Michael Tretter R: Pengutronix Kernel Team L: linux-media@vger.kernel.org S: Maintained -F: drivers/staging/media/allegro-dvt/ +F: drivers/media/platform/allegro-dvt/ ALLWINNER A10 CSI DRIVER M: Maxime Ripard diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 35a18d388f3f..e419b18613c6 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -199,6 +199,21 @@ menuconfig V4L_MEM2MEM_DRIVERS if V4L_MEM2MEM_DRIVERS +config VIDEO_ALLEGRO_DVT + tristate "Allegro DVT Video IP Core" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_ZYNQMP || COMPILE_TEST + select V4L2_MEM2MEM_DEV + select VIDEOBUF2_DMA_CONTIG + select REGMAP_MMIO + help + Support for the encoder video IP core by Allegro DVT. This core is + found for example on the Xilinx ZynqMP SoC in the EV family and is + called VCU in the reference manual. + + To compile this driver as a module, choose M here: the module + will be called allegro. + config VIDEO_CODA tristate "Chips&Media Coda multi-standard codec IP" depends on VIDEO_DEV && VIDEO_V4L2 && OF && (ARCH_MXC || COMPILE_TEST) diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 1d63aa956bcd..9d4d6370908d 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -3,6 +3,7 @@ # Makefile for the video capture/playback device drivers. # +obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro-dvt/ obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o obj-$(CONFIG_VIDEO_CADENCE) += cadence/ obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o diff --git a/drivers/media/platform/allegro-dvt/Makefile b/drivers/media/platform/allegro-dvt/Makefile new file mode 100644 index 000000000000..8e306dcdc55c --- /dev/null +++ b/drivers/media/platform/allegro-dvt/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +allegro-objs := allegro-core.o nal-h264.o allegro-mail.o + +obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro.o diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c new file mode 100644 index 000000000000..640451134072 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -0,0 +1,3226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + * + * Allegro DVT video encoder driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "allegro-mail.h" +#include "nal-h264.h" + +/* + * Support up to 4k video streams. The hardware actually supports higher + * resolutions, which are specified in PG252 June 6, 2018 (H.264/H.265 Video + * Codec Unit v1.1) Chapter 3. + */ +#define ALLEGRO_WIDTH_MIN 128 +#define ALLEGRO_WIDTH_DEFAULT 1920 +#define ALLEGRO_WIDTH_MAX 3840 +#define ALLEGRO_HEIGHT_MIN 64 +#define ALLEGRO_HEIGHT_DEFAULT 1080 +#define ALLEGRO_HEIGHT_MAX 2160 + +#define ALLEGRO_FRAMERATE_DEFAULT ((struct v4l2_fract) { 30, 1 }) + +#define ALLEGRO_GOP_SIZE_DEFAULT 25 +#define ALLEGRO_GOP_SIZE_MAX 1000 + +/* + * MCU Control Registers + * + * The Zynq UltraScale+ Devices Register Reference documents the registers + * with an offset of 0x9000, which equals the size of the SRAM and one page + * gap. The driver handles SRAM and registers separately and, therefore, is + * oblivious of the offset. + */ +#define AL5_MCU_RESET 0x0000 +#define AL5_MCU_RESET_SOFT BIT(0) +#define AL5_MCU_RESET_REGS BIT(1) +#define AL5_MCU_RESET_MODE 0x0004 +#define AL5_MCU_RESET_MODE_SLEEP BIT(0) +#define AL5_MCU_RESET_MODE_HALT BIT(1) +#define AL5_MCU_STA 0x0008 +#define AL5_MCU_STA_SLEEP BIT(0) +#define AL5_MCU_WAKEUP 0x000c + +#define AL5_ICACHE_ADDR_OFFSET_MSB 0x0010 +#define AL5_ICACHE_ADDR_OFFSET_LSB 0x0014 +#define AL5_DCACHE_ADDR_OFFSET_MSB 0x0018 +#define AL5_DCACHE_ADDR_OFFSET_LSB 0x001c + +#define AL5_MCU_INTERRUPT 0x0100 +#define AL5_ITC_CPU_IRQ_MSK 0x0104 +#define AL5_ITC_CPU_IRQ_CLR 0x0108 +#define AL5_ITC_CPU_IRQ_STA 0x010C +#define AL5_ITC_CPU_IRQ_STA_TRIGGERED BIT(0) + +#define AXI_ADDR_OFFSET_IP 0x0208 + +/* + * The MCU accesses the system memory with a 2G offset compared to CPU + * physical addresses. + */ +#define MCU_CACHE_OFFSET SZ_2G + +/* + * The driver needs to reserve some space at the beginning of capture buffers, + * because it needs to write SPS/PPS NAL units. The encoder writes the actual + * frame data after the offset. + */ +#define ENCODER_STREAM_OFFSET SZ_64 + +#define SIZE_MACROBLOCK 16 + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +struct allegro_buffer { + void *vaddr; + dma_addr_t paddr; + size_t size; + struct list_head head; +}; + +struct allegro_dev; +struct allegro_channel; + +struct allegro_mbox { + struct allegro_dev *dev; + unsigned int head; + unsigned int tail; + unsigned int data; + size_t size; + /* protect mailbox from simultaneous accesses */ + struct mutex lock; +}; + +struct allegro_dev { + struct v4l2_device v4l2_dev; + struct video_device video_dev; + struct v4l2_m2m_dev *m2m_dev; + struct platform_device *plat_dev; + + /* mutex protecting vb2_queue structure */ + struct mutex lock; + + struct regmap *regmap; + struct regmap *sram; + + const struct fw_info *fw_info; + struct allegro_buffer firmware; + struct allegro_buffer suballocator; + + struct completion init_complete; + + /* The mailbox interface */ + struct allegro_mbox *mbox_command; + struct allegro_mbox *mbox_status; + + /* + * The downstream driver limits the users to 64 users, thus I can use + * a bitfield for the user_ids that are in use. See also user_id in + * struct allegro_channel. + */ + unsigned long channel_user_ids; + struct list_head channels; +}; + +static struct regmap_config allegro_regmap_config = { + .name = "regmap", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0xfff, + .cache_type = REGCACHE_NONE, +}; + +static struct regmap_config allegro_sram_config = { + .name = "sram", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x7fff, + .cache_type = REGCACHE_NONE, +}; + +enum allegro_state { + ALLEGRO_STATE_ENCODING, + ALLEGRO_STATE_DRAIN, + ALLEGRO_STATE_WAIT_FOR_BUFFER, + ALLEGRO_STATE_STOPPED, +}; + +#define fh_to_channel(__fh) container_of(__fh, struct allegro_channel, fh) + +struct allegro_channel { + struct allegro_dev *dev; + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; + + unsigned int width; + unsigned int height; + unsigned int stride; + struct v4l2_fract framerate; + + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + enum v4l2_xfer_func xfer_func; + + u32 pixelformat; + unsigned int sizeimage_raw; + unsigned int osequence; + + u32 codec; + enum v4l2_mpeg_video_h264_profile profile; + enum v4l2_mpeg_video_h264_level level; + unsigned int sizeimage_encoded; + unsigned int csequence; + + bool frame_rc_enable; + unsigned int bitrate; + unsigned int bitrate_peak; + unsigned int cpb_size; + unsigned int gop_size; + + struct allegro_buffer config_blob; + + unsigned int num_ref_idx_l0; + unsigned int num_ref_idx_l1; + + struct v4l2_ctrl *mpeg_video_h264_profile; + struct v4l2_ctrl *mpeg_video_h264_level; + struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; + struct v4l2_ctrl *mpeg_video_h264_max_qp; + struct v4l2_ctrl *mpeg_video_h264_min_qp; + struct v4l2_ctrl *mpeg_video_h264_p_frame_qp; + struct v4l2_ctrl *mpeg_video_h264_b_frame_qp; + struct v4l2_ctrl *mpeg_video_frame_rc_enable; + struct { /* video bitrate mode control cluster */ + struct v4l2_ctrl *mpeg_video_bitrate_mode; + struct v4l2_ctrl *mpeg_video_bitrate; + struct v4l2_ctrl *mpeg_video_bitrate_peak; + }; + struct v4l2_ctrl *mpeg_video_cpb_size; + struct v4l2_ctrl *mpeg_video_gop_size; + + /* user_id is used to identify the channel during CREATE_CHANNEL */ + /* not sure, what to set here and if this is actually required */ + int user_id; + /* channel_id is set by the mcu and used by all later commands */ + int mcu_channel_id; + + struct list_head buffers_reference; + struct list_head buffers_intermediate; + + struct list_head source_shadow_list; + struct list_head stream_shadow_list; + /* protect shadow lists of buffers passed to firmware */ + struct mutex shadow_list_lock; + + struct list_head list; + struct completion completion; + + unsigned int error; + enum allegro_state state; +}; + +static inline int +allegro_set_state(struct allegro_channel *channel, enum allegro_state state) +{ + channel->state = state; + + return 0; +} + +static inline enum allegro_state +allegro_get_state(struct allegro_channel *channel) +{ + return channel->state; +} + +struct allegro_m2m_buffer { + struct v4l2_m2m_buffer buf; + struct list_head head; +}; + +#define to_allegro_m2m_buffer(__buf) \ + container_of(__buf, struct allegro_m2m_buffer, buf) + +struct fw_info { + unsigned int id; + unsigned int id_codec; + char *version; + unsigned int mailbox_cmd; + unsigned int mailbox_status; + size_t mailbox_size; + enum mcu_msg_version mailbox_version; + size_t suballocator_size; +}; + +static const struct fw_info supported_firmware[] = { + { + .id = 18296, + .id_codec = 96272, + .version = "v2018.2", + .mailbox_cmd = 0x7800, + .mailbox_status = 0x7c00, + .mailbox_size = 0x400 - 0x8, + .mailbox_version = MCU_MSG_VERSION_2018_2, + .suballocator_size = SZ_16M, + }, { + .id = 14680, + .id_codec = 126572, + .version = "v2019.2", + .mailbox_cmd = 0x7000, + .mailbox_status = 0x7800, + .mailbox_size = 0x800 - 0x8, + .mailbox_version = MCU_MSG_VERSION_2019_2, + .suballocator_size = SZ_32M, + }, +}; + +static inline u32 to_mcu_addr(struct allegro_dev *dev, dma_addr_t phys) +{ + if (upper_32_bits(phys) || (lower_32_bits(phys) & MCU_CACHE_OFFSET)) + v4l2_warn(&dev->v4l2_dev, + "address %pad is outside mcu window\n", &phys); + + return lower_32_bits(phys) | MCU_CACHE_OFFSET; +} + +static inline u32 to_mcu_size(struct allegro_dev *dev, size_t size) +{ + return lower_32_bits(size); +} + +static inline u32 to_codec_addr(struct allegro_dev *dev, dma_addr_t phys) +{ + if (upper_32_bits(phys)) + v4l2_warn(&dev->v4l2_dev, + "address %pad cannot be used by codec\n", &phys); + + return lower_32_bits(phys); +} + +static inline u64 ptr_to_u64(const void *ptr) +{ + return (uintptr_t)ptr; +} + +/* Helper functions for channel and user operations */ + +static unsigned long allegro_next_user_id(struct allegro_dev *dev) +{ + if (dev->channel_user_ids == ~0UL) + return -EBUSY; + + return ffz(dev->channel_user_ids); +} + +static struct allegro_channel * +allegro_find_channel_by_user_id(struct allegro_dev *dev, + unsigned int user_id) +{ + struct allegro_channel *channel; + + list_for_each_entry(channel, &dev->channels, list) { + if (channel->user_id == user_id) + return channel; + } + + return ERR_PTR(-EINVAL); +} + +static struct allegro_channel * +allegro_find_channel_by_channel_id(struct allegro_dev *dev, + unsigned int channel_id) +{ + struct allegro_channel *channel; + + list_for_each_entry(channel, &dev->channels, list) { + if (channel->mcu_channel_id == channel_id) + return channel; + } + + return ERR_PTR(-EINVAL); +} + +static inline bool channel_exists(struct allegro_channel *channel) +{ + return channel->mcu_channel_id != -1; +} + +#define AL_ERROR 0x80 +#define AL_ERR_INIT_FAILED 0x81 +#define AL_ERR_NO_FRAME_DECODED 0x82 +#define AL_ERR_RESOLUTION_CHANGE 0x85 +#define AL_ERR_NO_MEMORY 0x87 +#define AL_ERR_STREAM_OVERFLOW 0x88 +#define AL_ERR_TOO_MANY_SLICES 0x89 +#define AL_ERR_BUF_NOT_READY 0x8c +#define AL_ERR_NO_CHANNEL_AVAILABLE 0x8d +#define AL_ERR_RESOURCE_UNAVAILABLE 0x8e +#define AL_ERR_NOT_ENOUGH_CORES 0x8f +#define AL_ERR_REQUEST_MALFORMED 0x90 +#define AL_ERR_CMD_NOT_ALLOWED 0x91 +#define AL_ERR_INVALID_CMD_VALUE 0x92 + +static inline const char *allegro_err_to_string(unsigned int err) +{ + switch (err) { + case AL_ERR_INIT_FAILED: + return "initialization failed"; + case AL_ERR_NO_FRAME_DECODED: + return "no frame decoded"; + case AL_ERR_RESOLUTION_CHANGE: + return "resolution change"; + case AL_ERR_NO_MEMORY: + return "out of memory"; + case AL_ERR_STREAM_OVERFLOW: + return "stream buffer overflow"; + case AL_ERR_TOO_MANY_SLICES: + return "too many slices"; + case AL_ERR_BUF_NOT_READY: + return "buffer not ready"; + case AL_ERR_NO_CHANNEL_AVAILABLE: + return "no channel available"; + case AL_ERR_RESOURCE_UNAVAILABLE: + return "resource unavailable"; + case AL_ERR_NOT_ENOUGH_CORES: + return "not enough cores"; + case AL_ERR_REQUEST_MALFORMED: + return "request malformed"; + case AL_ERR_CMD_NOT_ALLOWED: + return "command not allowed"; + case AL_ERR_INVALID_CMD_VALUE: + return "invalid command value"; + case AL_ERROR: + default: + return "unknown error"; + } +} + +static unsigned int estimate_stream_size(unsigned int width, + unsigned int height) +{ + unsigned int offset = ENCODER_STREAM_OFFSET; + unsigned int num_blocks = DIV_ROUND_UP(width, SIZE_MACROBLOCK) * + DIV_ROUND_UP(height, SIZE_MACROBLOCK); + unsigned int pcm_size = SZ_256; + unsigned int partition_table = SZ_256; + + return round_up(offset + num_blocks * pcm_size + partition_table, 32); +} + +static enum v4l2_mpeg_video_h264_level +select_minimum_h264_level(unsigned int width, unsigned int height) +{ + unsigned int pic_width_in_mb = DIV_ROUND_UP(width, SIZE_MACROBLOCK); + unsigned int frame_height_in_mb = DIV_ROUND_UP(height, SIZE_MACROBLOCK); + unsigned int frame_size_in_mb = pic_width_in_mb * frame_height_in_mb; + enum v4l2_mpeg_video_h264_level level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + + /* + * The level limits are specified in Rec. ITU-T H.264 Annex A.3.1 and + * also specify limits regarding bit rate and CBP size. Only approximate + * the levels using the frame size. + * + * Level 5.1 allows up to 4k video resolution. + */ + if (frame_size_in_mb <= 99) + level = V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + else if (frame_size_in_mb <= 396) + level = V4L2_MPEG_VIDEO_H264_LEVEL_1_1; + else if (frame_size_in_mb <= 792) + level = V4L2_MPEG_VIDEO_H264_LEVEL_2_1; + else if (frame_size_in_mb <= 1620) + level = V4L2_MPEG_VIDEO_H264_LEVEL_2_2; + else if (frame_size_in_mb <= 3600) + level = V4L2_MPEG_VIDEO_H264_LEVEL_3_1; + else if (frame_size_in_mb <= 5120) + level = V4L2_MPEG_VIDEO_H264_LEVEL_3_2; + else if (frame_size_in_mb <= 8192) + level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + else if (frame_size_in_mb <= 8704) + level = V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + else if (frame_size_in_mb <= 22080) + level = V4L2_MPEG_VIDEO_H264_LEVEL_5_0; + else + level = V4L2_MPEG_VIDEO_H264_LEVEL_5_1; + + return level; +} + +static unsigned int maximum_bitrate(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 64000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return 128000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 192000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 384000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 768000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 2000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 4000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 4000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 10000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 14000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 20000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 20000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 50000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 50000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 135000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + default: + return 240000000; + } +} + +static unsigned int maximum_cpb_size(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 175; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return 350; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 500; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 1000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 2000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 2000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 4000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 4000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 10000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 14000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 20000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 25000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 62500; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 62500; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 135000; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + default: + return 240000; + } +} + +static const struct fw_info * +allegro_get_firmware_info(struct allegro_dev *dev, + const struct firmware *fw, + const struct firmware *fw_codec) +{ + int i; + unsigned int id = fw->size; + unsigned int id_codec = fw_codec->size; + + for (i = 0; i < ARRAY_SIZE(supported_firmware); i++) + if (supported_firmware[i].id == id && + supported_firmware[i].id_codec == id_codec) + return &supported_firmware[i]; + + return NULL; +} + +/* + * Buffers that are used internally by the MCU. + */ + +static int allegro_alloc_buffer(struct allegro_dev *dev, + struct allegro_buffer *buffer, size_t size) +{ + buffer->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, + &buffer->paddr, GFP_KERNEL); + if (!buffer->vaddr) + return -ENOMEM; + buffer->size = size; + + return 0; +} + +static void allegro_free_buffer(struct allegro_dev *dev, + struct allegro_buffer *buffer) +{ + if (buffer->vaddr) { + dma_free_coherent(&dev->plat_dev->dev, buffer->size, + buffer->vaddr, buffer->paddr); + buffer->vaddr = NULL; + buffer->size = 0; + } +} + +/* + * Mailbox interface to send messages to the MCU. + */ + +static void allegro_mcu_interrupt(struct allegro_dev *dev); +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg); + +static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, + unsigned int base, size_t size) +{ + struct allegro_mbox *mbox; + + mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return ERR_PTR(-ENOMEM); + + mbox->dev = dev; + + mbox->head = base; + mbox->tail = base + 0x4; + mbox->data = base + 0x8; + mbox->size = size; + mutex_init(&mbox->lock); + + regmap_write(dev->sram, mbox->head, 0); + regmap_write(dev->sram, mbox->tail, 0); + + return mbox; +} + +static int allegro_mbox_write(struct allegro_mbox *mbox, + const u32 *src, size_t size) +{ + struct regmap *sram = mbox->dev->sram; + unsigned int tail; + size_t size_no_wrap; + int err = 0; + int stride = regmap_get_reg_stride(sram); + + if (!src) + return -EINVAL; + + if (size > mbox->size) + return -EINVAL; + + mutex_lock(&mbox->lock); + regmap_read(sram, mbox->tail, &tail); + if (tail > mbox->size) { + err = -EIO; + goto out; + } + size_no_wrap = min(size, mbox->size - (size_t)tail); + regmap_bulk_write(sram, mbox->data + tail, + src, size_no_wrap / stride); + regmap_bulk_write(sram, mbox->data, + src + (size_no_wrap / sizeof(*src)), + (size - size_no_wrap) / stride); + regmap_write(sram, mbox->tail, (tail + size) % mbox->size); + +out: + mutex_unlock(&mbox->lock); + + return err; +} + +static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, + u32 *dst, size_t nbyte) +{ + struct { + u16 length; + u16 type; + } __attribute__ ((__packed__)) *header; + struct regmap *sram = mbox->dev->sram; + unsigned int head; + ssize_t size; + size_t body_no_wrap; + int stride = regmap_get_reg_stride(sram); + + regmap_read(sram, mbox->head, &head); + if (head > mbox->size) + return -EIO; + + /* Assume that the header does not wrap. */ + regmap_bulk_read(sram, mbox->data + head, + dst, sizeof(*header) / stride); + header = (void *)dst; + size = header->length + sizeof(*header); + if (size > mbox->size || size & 0x3) + return -EIO; + if (size > nbyte) + return -EINVAL; + + /* + * The message might wrap within the mailbox. If the message does not + * wrap, the first read will read the entire message, otherwise the + * first read will read message until the end of the mailbox and the + * second read will read the remaining bytes from the beginning of the + * mailbox. + * + * Skip the header, as was already read to get the size of the body. + */ + body_no_wrap = min((size_t)header->length, + (size_t)(mbox->size - (head + sizeof(*header)))); + regmap_bulk_read(sram, mbox->data + head + sizeof(*header), + dst + (sizeof(*header) / sizeof(*dst)), + body_no_wrap / stride); + regmap_bulk_read(sram, mbox->data, + dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst), + (header->length - body_no_wrap) / stride); + + regmap_write(sram, mbox->head, (head + size) % mbox->size); + + return size; +} + +/** + * allegro_mbox_send() - Send a message via the mailbox + * @mbox: the mailbox which is used to send the message + * @msg: the message to send + */ +static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) +{ + struct allegro_dev *dev = mbox->dev; + ssize_t size; + int err; + u32 *tmp; + + tmp = kzalloc(mbox->size, GFP_KERNEL); + if (!tmp) { + err = -ENOMEM; + goto out; + } + + size = allegro_encode_mail(tmp, msg); + + err = allegro_mbox_write(mbox, tmp, size); + kfree(tmp); + if (err) + goto out; + + allegro_mcu_interrupt(dev); + +out: + return err; +} + +/** + * allegro_mbox_notify() - Notify the mailbox about a new message + * @mbox: The allegro_mbox to notify + */ +static void allegro_mbox_notify(struct allegro_mbox *mbox) +{ + struct allegro_dev *dev = mbox->dev; + union mcu_msg_response *msg; + ssize_t size; + u32 *tmp; + int err; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return; + + msg->header.version = dev->fw_info->mailbox_version; + + tmp = kmalloc(mbox->size, GFP_KERNEL); + if (!tmp) + goto out; + + size = allegro_mbox_read(mbox, tmp, mbox->size); + if (size < 0) + goto out; + + err = allegro_decode_mail(msg, tmp); + if (err) + goto out; + + allegro_handle_message(dev, msg); + +out: + kfree(tmp); + kfree(msg); +} + +static void allegro_mcu_send_init(struct allegro_dev *dev, + dma_addr_t suballoc_dma, size_t suballoc_size) +{ + struct mcu_msg_init_request msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_INIT; + msg.header.version = dev->fw_info->mailbox_version; + + msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); + msg.suballoc_size = to_mcu_size(dev, suballoc_size); + + /* disable L2 cache */ + msg.l2_cache[0] = -1; + msg.l2_cache[1] = -1; + msg.l2_cache[2] = -1; + + allegro_mbox_send(dev->mbox_command, &msg); +} + +static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat) +{ + switch (pixelformat) { + case V4L2_PIX_FMT_NV12: + /* AL_420_8BITS: 0x100 -> NV12, 0x88 -> 8 bit */ + return 0x100 | 0x88; + default: + return -EINVAL; + } +} + +static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace) +{ + switch (colorspace) { + case V4L2_COLORSPACE_REC709: + return 2; + case V4L2_COLORSPACE_SMPTE170M: + return 3; + case V4L2_COLORSPACE_SMPTE240M: + return 4; + case V4L2_COLORSPACE_SRGB: + return 7; + default: + /* UNKNOWN */ + return 0; + } +} + +static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + default: + return 66; + } +} + +static u16 v4l2_level_to_mcu_level(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 10; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 20; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 30; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 40; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 50; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + default: + return 51; + } +} + +static u32 +v4l2_bitrate_mode_to_mcu_mode(enum v4l2_mpeg_video_bitrate_mode mode) +{ + switch (mode) { + case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: + return 2; + case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: + default: + return 1; + } +} + +static u32 v4l2_cpb_size_to_mcu(unsigned int cpb_size, unsigned int bitrate) +{ + unsigned int cpb_size_kbit; + unsigned int bitrate_kbps; + + /* + * The mcu expects the CPB size in units of a 90 kHz clock, but the + * channel follows the V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE and stores + * the CPB size in kilobytes. + */ + cpb_size_kbit = cpb_size * BITS_PER_BYTE; + bitrate_kbps = bitrate / 1000; + + return (cpb_size_kbit * 90000) / bitrate_kbps; +} + +static s16 get_qp_delta(int minuend, int subtrahend) +{ + if (minuend == subtrahend) + return -1; + else + return minuend - subtrahend; +} + +static int fill_create_channel_param(struct allegro_channel *channel, + struct create_channel_param *param) +{ + int i_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); + int p_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); + int b_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); + int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode); + + param->width = channel->width; + param->height = channel->height; + param->format = v4l2_pixelformat_to_mcu_format(channel->pixelformat); + param->colorspace = + v4l2_colorspace_to_mcu_colorspace(channel->colorspace); + param->src_mode = 0x0; + param->profile = v4l2_profile_to_mcu_profile(channel->profile); + param->constraint_set_flags = BIT(1); + param->codec = channel->codec; + param->level = v4l2_level_to_mcu_level(channel->level); + param->tier = 0; + + param->log2_max_poc = 10; + param->log2_max_frame_num = 4; + param->temporal_mvp_enable = 1; + + param->dbf_ovr_en = 1; + param->rdo_cost_mode = 1; + param->custom_lda = 1; + param->lf = 1; + param->lf_x_tile = 1; + param->lf_x_slice = 1; + + param->src_bit_depth = 8; + + param->beta_offset = -1; + param->tc_offset = -1; + param->num_slices = 1; + param->me_range[0] = 8; + param->me_range[1] = 8; + param->me_range[2] = 16; + param->me_range[3] = 16; + param->max_cu_size = ilog2(SIZE_MACROBLOCK); + param->min_cu_size = ilog2(8); + param->max_tu_size = 2; + param->min_tu_size = 2; + param->max_transfo_depth_intra = 1; + param->max_transfo_depth_inter = 1; + + param->prefetch_auto = 0; + param->prefetch_mem_offset = 0; + param->prefetch_mem_size = 0; + + param->rate_control_mode = channel->frame_rc_enable ? + v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0; + + param->cpb_size = v4l2_cpb_size_to_mcu(channel->cpb_size, + channel->bitrate_peak); + /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */ + param->initial_rem_delay = param->cpb_size; + param->framerate = DIV_ROUND_UP(channel->framerate.numerator, + channel->framerate.denominator); + param->clk_ratio = channel->framerate.denominator == 1001 ? 1001 : 1000; + param->target_bitrate = channel->bitrate; + param->max_bitrate = channel->bitrate_peak; + param->initial_qp = i_frame_qp; + param->min_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); + param->max_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); + param->ip_delta = get_qp_delta(i_frame_qp, p_frame_qp); + param->pb_delta = get_qp_delta(p_frame_qp, b_frame_qp); + param->golden_ref = 0; + param->golden_delta = 2; + param->golden_ref_frequency = 10; + param->rate_control_option = 0x00000000; + + param->num_pixel = channel->width + channel->height; + param->max_psnr = 4200; + param->max_pixel_value = 255; + + param->gop_ctrl_mode = 0x00000002; + param->freq_idr = channel->gop_size; + param->freq_lt = 0; + param->gdr_mode = 0x00000000; + param->gop_length = channel->gop_size; + param->subframe_latency = 0x00000000; + + param->lda_factors[0] = 51; + param->lda_factors[1] = 90; + param->lda_factors[2] = 151; + param->lda_factors[3] = 151; + param->lda_factors[4] = 151; + param->lda_factors[5] = 151; + + param->max_num_merge_cand = 5; + + return 0; +} + +static int allegro_mcu_send_create_channel(struct allegro_dev *dev, + struct allegro_channel *channel) +{ + struct mcu_msg_create_channel msg; + struct allegro_buffer *blob = &channel->config_blob; + struct create_channel_param param; + size_t size; + + memset(¶m, 0, sizeof(param)); + fill_create_channel_param(channel, ¶m); + allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param)); + param.version = dev->fw_info->mailbox_version; + size = allegro_encode_config_blob(blob->vaddr, ¶m); + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL; + msg.header.version = dev->fw_info->mailbox_version; + + msg.user_id = channel->user_id; + + msg.blob = blob->vaddr; + msg.blob_size = size; + msg.blob_mcu_addr = to_mcu_addr(dev, blob->paddr); + + allegro_mbox_send(dev->mbox_command, &msg); + + return 0; +} + +static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, + struct allegro_channel *channel) +{ + struct mcu_msg_destroy_channel msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL; + msg.header.version = dev->fw_info->mailbox_version; + + msg.channel_id = channel->mcu_channel_id; + + allegro_mbox_send(dev->mbox_command, &msg); + + return 0; +} + +static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, + struct allegro_channel *channel, + dma_addr_t paddr, + unsigned long size, + u64 stream_id) +{ + struct mcu_msg_put_stream_buffer msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER; + msg.header.version = dev->fw_info->mailbox_version; + + msg.channel_id = channel->mcu_channel_id; + msg.dma_addr = to_codec_addr(dev, paddr); + msg.mcu_addr = to_mcu_addr(dev, paddr); + msg.size = size; + msg.offset = ENCODER_STREAM_OFFSET; + /* copied to mcu_msg_encode_frame_response */ + msg.stream_id = stream_id; + + allegro_mbox_send(dev->mbox_command, &msg); + + return 0; +} + +static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, + struct allegro_channel *channel, + dma_addr_t src_y, dma_addr_t src_uv, + u64 src_handle) +{ + struct mcu_msg_encode_frame msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME; + msg.header.version = dev->fw_info->mailbox_version; + + msg.channel_id = channel->mcu_channel_id; + msg.encoding_options = AL_OPT_FORCE_LOAD; + msg.pps_qp = 26; /* qp are relative to 26 */ + msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */ + /* src_handle is copied to mcu_msg_encode_frame_response */ + msg.src_handle = src_handle; + msg.src_y = to_codec_addr(dev, src_y); + msg.src_uv = to_codec_addr(dev, src_uv); + msg.stride = channel->stride; + msg.ep2 = 0x0; + msg.ep2_v = to_mcu_addr(dev, msg.ep2); + + allegro_mbox_send(dev->mbox_command, &msg); + + return 0; +} + +static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev, + unsigned long timeout_ms) +{ + unsigned long tmo; + + tmo = wait_for_completion_timeout(&dev->init_complete, + msecs_to_jiffies(timeout_ms)); + if (tmo == 0) + return -ETIMEDOUT; + + reinit_completion(&dev->init_complete); + return 0; +} + +static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, + enum mcu_msg_type type) +{ + struct allegro_dev *dev = channel->dev; + struct mcu_msg_push_buffers_internal *msg; + struct mcu_msg_push_buffers_internal_buffer *buffer; + unsigned int num_buffers = 0; + size_t size; + struct allegro_buffer *al_buffer; + struct list_head *list; + int err; + + switch (type) { + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + list = &channel->buffers_reference; + break; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + list = &channel->buffers_intermediate; + break; + default: + return -EINVAL; + } + + list_for_each_entry(al_buffer, list, head) + num_buffers++; + size = struct_size(msg, buffer, num_buffers); + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->header.type = type; + msg->header.version = dev->fw_info->mailbox_version; + + msg->channel_id = channel->mcu_channel_id; + msg->num_buffers = num_buffers; + + buffer = msg->buffer; + list_for_each_entry(al_buffer, list, head) { + buffer->dma_addr = to_codec_addr(dev, al_buffer->paddr); + buffer->mcu_addr = to_mcu_addr(dev, al_buffer->paddr); + buffer->size = to_mcu_size(dev, al_buffer->size); + buffer++; + } + + err = allegro_mbox_send(dev->mbox_command, msg); + + kfree(msg); + return err; +} + +static int allegro_mcu_push_buffer_intermediate(struct allegro_channel *channel) +{ + enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE; + + return allegro_mcu_push_buffer_internal(channel, type); +} + +static int allegro_mcu_push_buffer_reference(struct allegro_channel *channel) +{ + enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE; + + return allegro_mcu_push_buffer_internal(channel, type); +} + +static int allocate_buffers_internal(struct allegro_channel *channel, + struct list_head *list, + size_t n, size_t size) +{ + struct allegro_dev *dev = channel->dev; + unsigned int i; + int err; + struct allegro_buffer *buffer, *tmp; + + for (i = 0; i < n; i++) { + buffer = kmalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) { + err = -ENOMEM; + goto err; + } + INIT_LIST_HEAD(&buffer->head); + + err = allegro_alloc_buffer(dev, buffer, size); + if (err) + goto err; + list_add(&buffer->head, list); + } + + return 0; + +err: + list_for_each_entry_safe(buffer, tmp, list, head) { + list_del(&buffer->head); + allegro_free_buffer(dev, buffer); + kfree(buffer); + } + return err; +} + +static void destroy_buffers_internal(struct allegro_channel *channel, + struct list_head *list) +{ + struct allegro_dev *dev = channel->dev; + struct allegro_buffer *buffer, *tmp; + + list_for_each_entry_safe(buffer, tmp, list, head) { + list_del(&buffer->head); + allegro_free_buffer(dev, buffer); + kfree(buffer); + } +} + +static void destroy_reference_buffers(struct allegro_channel *channel) +{ + return destroy_buffers_internal(channel, &channel->buffers_reference); +} + +static void destroy_intermediate_buffers(struct allegro_channel *channel) +{ + return destroy_buffers_internal(channel, + &channel->buffers_intermediate); +} + +static int allocate_intermediate_buffers(struct allegro_channel *channel, + size_t n, size_t size) +{ + return allocate_buffers_internal(channel, + &channel->buffers_intermediate, + n, size); +} + +static int allocate_reference_buffers(struct allegro_channel *channel, + size_t n, size_t size) +{ + return allocate_buffers_internal(channel, + &channel->buffers_reference, + n, PAGE_ALIGN(size)); +} + +static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, + void *dest, size_t n) +{ + struct allegro_dev *dev = channel->dev; + struct nal_h264_sps *sps; + ssize_t size; + unsigned int size_mb = SIZE_MACROBLOCK; + /* Calculation of crop units in Rec. ITU-T H.264 (04/2017) p. 76 */ + unsigned int crop_unit_x = 2; + unsigned int crop_unit_y = 2; + + sps = kzalloc(sizeof(*sps), GFP_KERNEL); + if (!sps) + return -ENOMEM; + + sps->profile_idc = nal_h264_profile_from_v4l2(channel->profile); + sps->constraint_set0_flag = 0; + sps->constraint_set1_flag = 1; + sps->constraint_set2_flag = 0; + sps->constraint_set3_flag = 0; + sps->constraint_set4_flag = 0; + sps->constraint_set5_flag = 0; + sps->level_idc = nal_h264_level_from_v4l2(channel->level); + sps->seq_parameter_set_id = 0; + sps->log2_max_frame_num_minus4 = 0; + sps->pic_order_cnt_type = 0; + sps->log2_max_pic_order_cnt_lsb_minus4 = 6; + sps->max_num_ref_frames = 3; + sps->gaps_in_frame_num_value_allowed_flag = 0; + sps->pic_width_in_mbs_minus1 = + DIV_ROUND_UP(channel->width, size_mb) - 1; + sps->pic_height_in_map_units_minus1 = + DIV_ROUND_UP(channel->height, size_mb) - 1; + sps->frame_mbs_only_flag = 1; + sps->mb_adaptive_frame_field_flag = 0; + sps->direct_8x8_inference_flag = 1; + sps->frame_cropping_flag = + (channel->width % size_mb) || (channel->height % size_mb); + if (sps->frame_cropping_flag) { + sps->crop_left = 0; + sps->crop_right = (round_up(channel->width, size_mb) - channel->width) / crop_unit_x; + sps->crop_top = 0; + sps->crop_bottom = (round_up(channel->height, size_mb) - channel->height) / crop_unit_y; + } + sps->vui_parameters_present_flag = 1; + sps->vui.aspect_ratio_info_present_flag = 0; + sps->vui.overscan_info_present_flag = 0; + sps->vui.video_signal_type_present_flag = 1; + sps->vui.video_format = 1; + sps->vui.video_full_range_flag = 0; + sps->vui.colour_description_present_flag = 1; + sps->vui.colour_primaries = 5; + sps->vui.transfer_characteristics = 5; + sps->vui.matrix_coefficients = 5; + sps->vui.chroma_loc_info_present_flag = 1; + sps->vui.chroma_sample_loc_type_top_field = 0; + sps->vui.chroma_sample_loc_type_bottom_field = 0; + + sps->vui.timing_info_present_flag = 1; + sps->vui.num_units_in_tick = channel->framerate.denominator; + sps->vui.time_scale = 2 * channel->framerate.numerator; + + sps->vui.fixed_frame_rate_flag = 1; + sps->vui.nal_hrd_parameters_present_flag = 0; + sps->vui.vcl_hrd_parameters_present_flag = 1; + sps->vui.vcl_hrd_parameters.cpb_cnt_minus1 = 0; + sps->vui.vcl_hrd_parameters.bit_rate_scale = 0; + sps->vui.vcl_hrd_parameters.cpb_size_scale = 1; + /* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */ + sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] = + channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1; + /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */ + sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] = + (channel->cpb_size * 1000) / (1 << (4 + sps->vui.vcl_hrd_parameters.cpb_size_scale)) - 1; + sps->vui.vcl_hrd_parameters.cbr_flag[0] = + !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable); + sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 = 31; + sps->vui.vcl_hrd_parameters.cpb_removal_delay_length_minus1 = 31; + sps->vui.vcl_hrd_parameters.dpb_output_delay_length_minus1 = 31; + sps->vui.vcl_hrd_parameters.time_offset_length = 0; + sps->vui.low_delay_hrd_flag = 0; + sps->vui.pic_struct_present_flag = 1; + sps->vui.bitstream_restriction_flag = 0; + + size = nal_h264_write_sps(&dev->plat_dev->dev, dest, n, sps); + + kfree(sps); + + return size; +} + +static ssize_t allegro_h264_write_pps(struct allegro_channel *channel, + void *dest, size_t n) +{ + struct allegro_dev *dev = channel->dev; + struct nal_h264_pps *pps; + ssize_t size; + + pps = kzalloc(sizeof(*pps), GFP_KERNEL); + if (!pps) + return -ENOMEM; + + pps->pic_parameter_set_id = 0; + pps->seq_parameter_set_id = 0; + pps->entropy_coding_mode_flag = 0; + pps->bottom_field_pic_order_in_frame_present_flag = 0; + pps->num_slice_groups_minus1 = 0; + pps->num_ref_idx_l0_default_active_minus1 = channel->num_ref_idx_l0 - 1; + pps->num_ref_idx_l1_default_active_minus1 = channel->num_ref_idx_l1 - 1; + pps->weighted_pred_flag = 0; + pps->weighted_bipred_idc = 0; + pps->pic_init_qp_minus26 = 0; + pps->pic_init_qs_minus26 = 0; + pps->chroma_qp_index_offset = 0; + pps->deblocking_filter_control_present_flag = 1; + pps->constrained_intra_pred_flag = 0; + pps->redundant_pic_cnt_present_flag = 0; + pps->transform_8x8_mode_flag = 0; + pps->pic_scaling_matrix_present_flag = 0; + pps->second_chroma_qp_index_offset = 0; + + size = nal_h264_write_pps(&dev->plat_dev->dev, dest, n, pps); + + kfree(pps); + + return size; +} + +static bool allegro_channel_is_at_eos(struct allegro_channel *channel) +{ + bool is_at_eos = false; + + switch (allegro_get_state(channel)) { + case ALLEGRO_STATE_STOPPED: + is_at_eos = true; + break; + case ALLEGRO_STATE_DRAIN: + case ALLEGRO_STATE_WAIT_FOR_BUFFER: + mutex_lock(&channel->shadow_list_lock); + if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0 && + list_empty(&channel->source_shadow_list)) + is_at_eos = true; + mutex_unlock(&channel->shadow_list_lock); + break; + default: + break; + } + + return is_at_eos; +} + +static void allegro_channel_buf_done(struct allegro_channel *channel, + struct vb2_v4l2_buffer *buf, + enum vb2_buffer_state state) +{ + const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + + if (allegro_channel_is_at_eos(channel)) { + buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_event_queue_fh(&channel->fh, &eos_event); + + allegro_set_state(channel, ALLEGRO_STATE_STOPPED); + } + + v4l2_m2m_buf_done(buf, state); +} + +static u64 allegro_put_buffer(struct allegro_channel *channel, + struct list_head *list, + struct vb2_v4l2_buffer *buffer) +{ + struct v4l2_m2m_buffer *b = container_of(buffer, + struct v4l2_m2m_buffer, vb); + struct allegro_m2m_buffer *shadow = to_allegro_m2m_buffer(b); + + mutex_lock(&channel->shadow_list_lock); + list_add_tail(&shadow->head, list); + mutex_unlock(&channel->shadow_list_lock); + + return ptr_to_u64(buffer); +} + +static struct vb2_v4l2_buffer * +allegro_get_buffer(struct allegro_channel *channel, + struct list_head *list, u64 handle) +{ + struct allegro_m2m_buffer *shadow, *tmp; + struct vb2_v4l2_buffer *buffer = NULL; + + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, list, head) { + if (handle == ptr_to_u64(&shadow->buf.vb)) { + buffer = &shadow->buf.vb; + list_del_init(&shadow->head); + break; + } + } + mutex_unlock(&channel->shadow_list_lock); + + return buffer; +} + +static void allegro_channel_finish_frame(struct allegro_channel *channel, + struct mcu_msg_encode_frame_response *msg) +{ + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *src_buf; + struct vb2_v4l2_buffer *dst_buf; + struct { + u32 offset; + u32 size; + } *partition; + enum vb2_buffer_state state = VB2_BUF_STATE_ERROR; + char *curr; + ssize_t len; + ssize_t free; + + src_buf = allegro_get_buffer(channel, &channel->source_shadow_list, + msg->src_handle); + if (!src_buf) + v4l2_warn(&dev->v4l2_dev, + "channel %d: invalid source buffer\n", + channel->mcu_channel_id); + + dst_buf = allegro_get_buffer(channel, &channel->stream_shadow_list, + msg->stream_id); + if (!dst_buf) + v4l2_warn(&dev->v4l2_dev, + "channel %d: invalid stream buffer\n", + channel->mcu_channel_id); + + if (!src_buf || !dst_buf) + goto err; + + dst_buf->sequence = channel->csequence++; + + if (msg->error_code & AL_ERROR) { + v4l2_err(&dev->v4l2_dev, + "channel %d: failed to encode frame: %s (%x)\n", + channel->mcu_channel_id, + allegro_err_to_string(msg->error_code), + msg->error_code); + goto err; + } + + if (msg->partition_table_size != 1) { + v4l2_warn(&dev->v4l2_dev, + "channel %d: only handling first partition table entry (%d entries)\n", + channel->mcu_channel_id, msg->partition_table_size); + } + + if (msg->partition_table_offset + + msg->partition_table_size * sizeof(*partition) > + vb2_plane_size(&dst_buf->vb2_buf, 0)) { + v4l2_err(&dev->v4l2_dev, + "channel %d: partition table outside of dst_buf\n", + channel->mcu_channel_id); + goto err; + } + + partition = + vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + msg->partition_table_offset; + if (partition->offset + partition->size > + vb2_plane_size(&dst_buf->vb2_buf, 0)) { + v4l2_err(&dev->v4l2_dev, + "channel %d: encoded frame is outside of dst_buf (offset 0x%x, size 0x%x)\n", + channel->mcu_channel_id, partition->offset, + partition->size); + goto err; + } + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "channel %d: encoded frame of size %d is at offset 0x%x\n", + channel->mcu_channel_id, partition->size, partition->offset); + + /* + * The payload must include the data before the partition offset, + * because we will put the sps and pps data there. + */ + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + partition->offset + partition->size); + + curr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + free = partition->offset; + if (msg->is_idr) { + len = allegro_h264_write_sps(channel, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "not enough space for sequence parameter set: %zd left\n", + free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: wrote %zd byte SPS nal unit\n", + channel->mcu_channel_id, len); + } + + if (msg->slice_type == AL_ENC_SLICE_TYPE_I) { + len = allegro_h264_write_pps(channel, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "not enough space for picture parameter set: %zd left\n", + free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: wrote %zd byte PPS nal unit\n", + channel->mcu_channel_id, len); + } + + if (msg->slice_type != AL_ENC_SLICE_TYPE_I && !msg->is_idr) { + dst_buf->vb2_buf.planes[0].data_offset = free; + free = 0; + } else { + len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to write %zd filler data\n", free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "channel %d: wrote %zd bytes filler nal unit\n", + channel->mcu_channel_id, len); + } + + if (free != 0) { + v4l2_err(&dev->v4l2_dev, + "non-VCL NAL units do not fill space until VCL NAL unit: %zd bytes left\n", + free); + goto err; + } + + state = VB2_BUF_STATE_DONE; + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + if (msg->is_idr) + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + else + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: encoded frame #%03d (%s%s, QP %d, %d bytes)\n", + channel->mcu_channel_id, + dst_buf->sequence, + msg->is_idr ? "IDR, " : "", + msg->slice_type == AL_ENC_SLICE_TYPE_I ? "I slice" : + msg->slice_type == AL_ENC_SLICE_TYPE_P ? "P slice" : "unknown", + msg->qp, partition->size); + +err: + if (src_buf) + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + + if (dst_buf) + allegro_channel_buf_done(channel, dst_buf, state); +} + +static int allegro_handle_init(struct allegro_dev *dev, + struct mcu_msg_init_response *msg) +{ + complete(&dev->init_complete); + + return 0; +} + +static int +allegro_handle_create_channel(struct allegro_dev *dev, + struct mcu_msg_create_channel_response *msg) +{ + struct allegro_channel *channel; + int err = 0; + struct create_channel_param param; + + channel = allegro_find_channel_by_user_id(dev, msg->user_id); + if (IS_ERR(channel)) { + v4l2_warn(&dev->v4l2_dev, + "received %s for unknown user %d\n", + msg_type_name(msg->header.type), + msg->user_id); + return -EINVAL; + } + + if (msg->error_code) { + v4l2_err(&dev->v4l2_dev, + "user %d: mcu failed to create channel: %s (%x)\n", + channel->user_id, + allegro_err_to_string(msg->error_code), + msg->error_code); + err = -EIO; + goto out; + } + + channel->mcu_channel_id = msg->channel_id; + v4l2_dbg(1, debug, &dev->v4l2_dev, + "user %d: channel has channel id %d\n", + channel->user_id, channel->mcu_channel_id); + + err = allegro_decode_config_blob(¶m, msg, channel->config_blob.vaddr); + allegro_free_buffer(channel->dev, &channel->config_blob); + if (err) + goto out; + + channel->num_ref_idx_l0 = param.num_ref_idx_l0; + channel->num_ref_idx_l1 = param.num_ref_idx_l1; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: intermediate buffers: %d x %d bytes\n", + channel->mcu_channel_id, + msg->int_buffers_count, msg->int_buffers_size); + err = allocate_intermediate_buffers(channel, msg->int_buffers_count, + msg->int_buffers_size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "channel %d: failed to allocate intermediate buffers\n", + channel->mcu_channel_id); + goto out; + } + err = allegro_mcu_push_buffer_intermediate(channel); + if (err) + goto out; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: reference buffers: %d x %d bytes\n", + channel->mcu_channel_id, + msg->rec_buffers_count, msg->rec_buffers_size); + err = allocate_reference_buffers(channel, msg->rec_buffers_count, + msg->rec_buffers_size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "channel %d: failed to allocate reference buffers\n", + channel->mcu_channel_id); + goto out; + } + err = allegro_mcu_push_buffer_reference(channel); + if (err) + goto out; + +out: + channel->error = err; + complete(&channel->completion); + + /* Handled successfully, error is passed via channel->error */ + return 0; +} + +static int +allegro_handle_destroy_channel(struct allegro_dev *dev, + struct mcu_msg_destroy_channel_response *msg) +{ + struct allegro_channel *channel; + + channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); + if (IS_ERR(channel)) { + v4l2_err(&dev->v4l2_dev, + "received %s for unknown channel %d\n", + msg_type_name(msg->header.type), + msg->channel_id); + return -EINVAL; + } + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "user %d: vcu destroyed channel %d\n", + channel->user_id, channel->mcu_channel_id); + complete(&channel->completion); + + return 0; +} + +static int +allegro_handle_encode_frame(struct allegro_dev *dev, + struct mcu_msg_encode_frame_response *msg) +{ + struct allegro_channel *channel; + + channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); + if (IS_ERR(channel)) { + v4l2_err(&dev->v4l2_dev, + "received %s for unknown channel %d\n", + msg_type_name(msg->header.type), + msg->channel_id); + return -EINVAL; + } + + allegro_channel_finish_frame(channel, msg); + + return 0; +} + +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg) +{ + switch (msg->header.type) { + case MCU_MSG_TYPE_INIT: + allegro_handle_init(dev, &msg->init); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + allegro_handle_create_channel(dev, &msg->create_channel); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + allegro_handle_destroy_channel(dev, &msg->destroy_channel); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + allegro_handle_encode_frame(dev, &msg->encode_frame); + break; + default: + v4l2_warn(&dev->v4l2_dev, + "%s: unknown message %s\n", + __func__, msg_type_name(msg->header.type)); + break; + } +} + +static irqreturn_t allegro_hardirq(int irq, void *data) +{ + struct allegro_dev *dev = data; + unsigned int status; + + regmap_read(dev->regmap, AL5_ITC_CPU_IRQ_STA, &status); + if (!(status & AL5_ITC_CPU_IRQ_STA_TRIGGERED)) + return IRQ_NONE; + + regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_CLR, status); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t allegro_irq_thread(int irq, void *data) +{ + struct allegro_dev *dev = data; + + allegro_mbox_notify(dev->mbox_status); + + return IRQ_HANDLED; +} + +static void allegro_copy_firmware(struct allegro_dev *dev, + const u8 * const buf, size_t size) +{ + int err = 0; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "copy mcu firmware (%zu B) to SRAM\n", size); + err = regmap_bulk_write(dev->sram, 0x0, buf, size / 4); + if (err) + v4l2_err(&dev->v4l2_dev, + "failed to copy firmware: %d\n", err); +} + +static void allegro_copy_fw_codec(struct allegro_dev *dev, + const u8 * const buf, size_t size) +{ + int err; + dma_addr_t icache_offset, dcache_offset; + + /* + * The downstream allocates 600 KB for the codec firmware to have some + * extra space for "possible extensions." My tests were fine with + * allocating just enough memory for the actual firmware, but I am not + * sure that the firmware really does not use the remaining space. + */ + err = allegro_alloc_buffer(dev, &dev->firmware, size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate %zu bytes for firmware\n", size); + return; + } + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "copy codec firmware (%zd B) to phys %pad\n", + size, &dev->firmware.paddr); + memcpy(dev->firmware.vaddr, buf, size); + + regmap_write(dev->regmap, AXI_ADDR_OFFSET_IP, + upper_32_bits(dev->firmware.paddr)); + + icache_offset = dev->firmware.paddr - MCU_CACHE_OFFSET; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "icache_offset: msb = 0x%x, lsb = 0x%x\n", + upper_32_bits(icache_offset), lower_32_bits(icache_offset)); + regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_MSB, + upper_32_bits(icache_offset)); + regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_LSB, + lower_32_bits(icache_offset)); + + dcache_offset = + (dev->firmware.paddr & 0xffffffff00000000ULL) - MCU_CACHE_OFFSET; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "dcache_offset: msb = 0x%x, lsb = 0x%x\n", + upper_32_bits(dcache_offset), lower_32_bits(dcache_offset)); + regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_MSB, + upper_32_bits(dcache_offset)); + regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_LSB, + lower_32_bits(dcache_offset)); +} + +static void allegro_free_fw_codec(struct allegro_dev *dev) +{ + allegro_free_buffer(dev, &dev->firmware); +} + +/* + * Control functions for the MCU + */ + +static int allegro_mcu_enable_interrupts(struct allegro_dev *dev) +{ + return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, BIT(0)); +} + +static int allegro_mcu_disable_interrupts(struct allegro_dev *dev) +{ + return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, 0); +} + +static int allegro_mcu_wait_for_sleep(struct allegro_dev *dev) +{ + unsigned long timeout; + unsigned int status; + + timeout = jiffies + msecs_to_jiffies(100); + while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && + status != AL5_MCU_STA_SLEEP) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + return 0; +} + +static int allegro_mcu_start(struct allegro_dev *dev) +{ + unsigned long timeout; + unsigned int status; + int err; + + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, BIT(0)); + if (err) + return err; + + timeout = jiffies + msecs_to_jiffies(100); + while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && + status == AL5_MCU_STA_SLEEP) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); + if (err) + return err; + + return 0; +} + +static int allegro_mcu_reset(struct allegro_dev *dev) +{ + int err; + + /* + * Ensure that the AL5_MCU_WAKEUP bit is set to 0 otherwise the mcu + * does not go to sleep after the reset. + */ + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); + if (err) + return err; + + err = regmap_write(dev->regmap, + AL5_MCU_RESET_MODE, AL5_MCU_RESET_MODE_SLEEP); + if (err < 0) + return err; + + err = regmap_write(dev->regmap, AL5_MCU_RESET, AL5_MCU_RESET_SOFT); + if (err < 0) + return err; + + return allegro_mcu_wait_for_sleep(dev); +} + +static void allegro_mcu_interrupt(struct allegro_dev *dev) +{ + regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); +} + +static void allegro_destroy_channel(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + unsigned long timeout; + + if (channel_exists(channel)) { + reinit_completion(&channel->completion); + allegro_mcu_send_destroy_channel(dev, channel); + timeout = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (timeout == 0) + v4l2_warn(&dev->v4l2_dev, + "channel %d: timeout while destroying\n", + channel->mcu_channel_id); + + channel->mcu_channel_id = -1; + } + + destroy_intermediate_buffers(channel); + destroy_reference_buffers(channel); + + v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_level, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false); + v4l2_ctrl_grab(channel->mpeg_video_cpb_size, false); + v4l2_ctrl_grab(channel->mpeg_video_gop_size, false); + + if (channel->user_id != -1) { + clear_bit(channel->user_id, &dev->channel_user_ids); + channel->user_id = -1; + } +} + +/* + * Create the MCU channel + * + * After the channel has been created, the picture size, format, colorspace + * and framerate are fixed. Also the codec, profile, bitrate, etc. cannot be + * changed anymore. + * + * The channel can be created only once. The MCU will accept source buffers + * and stream buffers only after a channel has been created. + */ +static int allegro_create_channel(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + unsigned long timeout; + enum v4l2_mpeg_video_h264_level min_level; + + if (channel_exists(channel)) { + v4l2_warn(&dev->v4l2_dev, + "channel already exists\n"); + return 0; + } + + channel->user_id = allegro_next_user_id(dev); + if (channel->user_id < 0) { + v4l2_err(&dev->v4l2_dev, + "no free channels available\n"); + return -EBUSY; + } + set_bit(channel->user_id, &dev->channel_user_ids); + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "user %d: creating channel (%4.4s, %dx%d@%d)\n", + channel->user_id, + (char *)&channel->codec, channel->width, channel->height, + DIV_ROUND_UP(channel->framerate.numerator, + channel->framerate.denominator)); + + min_level = select_minimum_h264_level(channel->width, channel->height); + if (channel->level < min_level) { + v4l2_warn(&dev->v4l2_dev, + "user %d: selected Level %s too low: increasing to Level %s\n", + channel->user_id, + v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[channel->level], + v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[min_level]); + channel->level = min_level; + } + + v4l2_ctrl_grab(channel->mpeg_video_h264_profile, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_level, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true); + v4l2_ctrl_grab(channel->mpeg_video_cpb_size, true); + v4l2_ctrl_grab(channel->mpeg_video_gop_size, true); + + reinit_completion(&channel->completion); + allegro_mcu_send_create_channel(dev, channel); + timeout = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (timeout == 0) + channel->error = -ETIMEDOUT; + if (channel->error) + goto err; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: accepting buffers\n", + channel->mcu_channel_id); + + return 0; + +err: + allegro_destroy_channel(channel); + + return channel->error; +} + +static void allegro_set_default_params(struct allegro_channel *channel) +{ + channel->width = ALLEGRO_WIDTH_DEFAULT; + channel->height = ALLEGRO_HEIGHT_DEFAULT; + channel->stride = round_up(channel->width, 32); + channel->framerate = ALLEGRO_FRAMERATE_DEFAULT; + + channel->colorspace = V4L2_COLORSPACE_REC709; + channel->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + channel->quantization = V4L2_QUANTIZATION_DEFAULT; + channel->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + channel->pixelformat = V4L2_PIX_FMT_NV12; + channel->sizeimage_raw = channel->stride * channel->height * 3 / 2; + + channel->codec = V4L2_PIX_FMT_H264; + channel->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + channel->level = + select_minimum_h264_level(channel->width, channel->height); + channel->sizeimage_encoded = + estimate_stream_size(channel->width, channel->height); + + channel->bitrate = maximum_bitrate(channel->level); + channel->bitrate_peak = maximum_bitrate(channel->level); + channel->cpb_size = maximum_cpb_size(channel->level); + channel->gop_size = ALLEGRO_GOP_SIZE_DEFAULT; +} + +static int allegro_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct allegro_channel *channel = vb2_get_drv_priv(vq); + struct allegro_dev *dev = channel->dev; + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "%s: queue setup[%s]: nplanes = %d\n", + V4L2_TYPE_IS_OUTPUT(vq->type) ? "output" : "capture", + *nplanes == 0 ? "REQBUFS" : "CREATE_BUFS", *nplanes); + + if (*nplanes != 0) { + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + if (sizes[0] < channel->sizeimage_raw) + return -EINVAL; + } else { + if (sizes[0] < channel->sizeimage_encoded) + return -EINVAL; + } + } else { + *nplanes = 1; + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + sizes[0] = channel->sizeimage_raw; + else + sizes[0] = channel->sizeimage_encoded; + } + + return 0; +} + +static int allegro_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); + struct allegro_dev *dev = channel->dev; + + if (allegro_get_state(channel) == ALLEGRO_STATE_DRAIN && + V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + return -EBUSY; + + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + v4l2_err(&dev->v4l2_dev, + "channel %d: unsupported field\n", + channel->mcu_channel_id); + return -EINVAL; + } + } + + return 0; +} + +static void allegro_buf_queue(struct vb2_buffer *vb) +{ + struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (allegro_get_state(channel) == ALLEGRO_STATE_WAIT_FOR_BUFFER && + vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + allegro_channel_buf_done(channel, vbuf, VB2_BUF_STATE_DONE); + return; + } + + v4l2_m2m_buf_queue(channel->fh.m2m_ctx, vbuf); +} + +static int allegro_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct allegro_channel *channel = vb2_get_drv_priv(q); + struct allegro_dev *dev = channel->dev; + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "%s: start streaming\n", + V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + channel->osequence = 0; + allegro_set_state(channel, ALLEGRO_STATE_ENCODING); + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + channel->csequence = 0; + } + + return 0; +} + +static void allegro_stop_streaming(struct vb2_queue *q) +{ + struct allegro_channel *channel = vb2_get_drv_priv(q); + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *buffer; + struct allegro_m2m_buffer *shadow, *tmp; + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "%s: stop streaming\n", + V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, + &channel->source_shadow_list, head) { + list_del(&shadow->head); + v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); + } + mutex_unlock(&channel->shadow_list_lock); + + allegro_set_state(channel, ALLEGRO_STATE_STOPPED); + while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx))) + v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, + &channel->stream_shadow_list, head) { + list_del(&shadow->head); + v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); + } + mutex_unlock(&channel->shadow_list_lock); + + allegro_destroy_channel(channel); + while ((buffer = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx))) + v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); + } +} + +static const struct vb2_ops allegro_queue_ops = { + .queue_setup = allegro_queue_setup, + .buf_prepare = allegro_buf_prepare, + .buf_queue = allegro_buf_queue, + .start_streaming = allegro_start_streaming, + .stop_streaming = allegro_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int allegro_queue_init(void *priv, + struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int err; + struct allegro_channel *channel = priv; + + src_vq->dev = &channel->dev->plat_dev->dev; + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->drv_priv = channel; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->ops = &allegro_queue_ops; + src_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); + src_vq->lock = &channel->dev->lock; + err = vb2_queue_init(src_vq); + if (err) + return err; + + dst_vq->dev = &channel->dev->plat_dev->dev; + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->drv_priv = channel; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->ops = &allegro_queue_ops; + dst_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); + dst_vq->lock = &channel->dev->lock; + err = vb2_queue_init(dst_vq); + if (err) + return err; + + return 0; +} + +static int allegro_clamp_qp(struct allegro_channel *channel, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl *next_ctrl; + + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP) + next_ctrl = channel->mpeg_video_h264_p_frame_qp; + else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP) + next_ctrl = channel->mpeg_video_h264_b_frame_qp; + else + return 0; + + /* Modify range automatically updates the value */ + __v4l2_ctrl_modify_range(next_ctrl, ctrl->val, 51, 1, ctrl->val); + + return allegro_clamp_qp(channel, next_ctrl); +} + +static int allegro_clamp_bitrate(struct allegro_channel *channel, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl *ctrl_bitrate = channel->mpeg_video_bitrate; + struct v4l2_ctrl *ctrl_bitrate_peak = channel->mpeg_video_bitrate_peak; + + if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + ctrl_bitrate_peak->val < ctrl_bitrate->val) + ctrl_bitrate_peak->val = ctrl_bitrate->val; + + return 0; +} + +static int allegro_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct allegro_channel *channel = container_of(ctrl->handler, + struct allegro_channel, + ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + allegro_clamp_bitrate(channel, ctrl); + break; + } + + return 0; +} + +static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct allegro_channel *channel = container_of(ctrl->handler, + struct allegro_channel, + ctrl_handler); + struct allegro_dev *dev = channel->dev; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "s_ctrl: %s = %d\n", v4l2_ctrl_get_name(ctrl->id), ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + channel->level = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + channel->frame_rc_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + channel->bitrate = channel->mpeg_video_bitrate->val; + channel->bitrate_peak = channel->mpeg_video_bitrate_peak->val; + v4l2_ctrl_activate(channel->mpeg_video_bitrate_peak, + ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + break; + case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: + channel->cpb_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + channel->gop_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: + allegro_clamp_qp(channel, ctrl); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops allegro_ctrl_ops = { + .try_ctrl = allegro_try_ctrl, + .s_ctrl = allegro_s_ctrl, +}; + +static int allegro_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct allegro_dev *dev = video_get_drvdata(vdev); + struct allegro_channel *channel = NULL; + struct v4l2_ctrl_handler *handler; + u64 mask; + int ret; + + channel = kzalloc(sizeof(*channel), GFP_KERNEL); + if (!channel) + return -ENOMEM; + + v4l2_fh_init(&channel->fh, vdev); + + init_completion(&channel->completion); + INIT_LIST_HEAD(&channel->source_shadow_list); + INIT_LIST_HEAD(&channel->stream_shadow_list); + mutex_init(&channel->shadow_list_lock); + + channel->dev = dev; + + allegro_set_default_params(channel); + + handler = &channel->ctrl_handler; + v4l2_ctrl_handler_init(handler, 0); + channel->mpeg_video_h264_profile = v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); + mask = 1 << V4L2_MPEG_VIDEO_H264_LEVEL_1B; + channel->mpeg_video_h264_level = v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1, mask, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + channel->mpeg_video_h264_i_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_h264_max_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 51, 1, 51); + channel->mpeg_video_h264_min_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + 0, 51, 1, 0); + channel->mpeg_video_h264_p_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_h264_b_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_frame_rc_enable = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + false, 0x1, + true, false); + channel->mpeg_video_bitrate_mode = v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), + 1, channel->bitrate); + channel->mpeg_video_bitrate_peak = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), + 1, channel->bitrate_peak); + channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, + 0, maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), + 1, channel->cpb_size); + channel->mpeg_video_gop_size = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 0, ALLEGRO_GOP_SIZE_MAX, + 1, channel->gop_size); + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, + 1, 32, + 1, 1); + if (handler->error != 0) { + ret = handler->error; + goto error; + } + + channel->fh.ctrl_handler = handler; + + v4l2_ctrl_cluster(3, &channel->mpeg_video_bitrate_mode); + + channel->mcu_channel_id = -1; + channel->user_id = -1; + + INIT_LIST_HEAD(&channel->buffers_reference); + INIT_LIST_HEAD(&channel->buffers_intermediate); + + channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel, + allegro_queue_init); + + if (IS_ERR(channel->fh.m2m_ctx)) { + ret = PTR_ERR(channel->fh.m2m_ctx); + goto error; + } + + list_add(&channel->list, &dev->channels); + file->private_data = &channel->fh; + v4l2_fh_add(&channel->fh); + + return 0; + +error: + v4l2_ctrl_handler_free(handler); + kfree(channel); + return ret; +} + +static int allegro_release(struct file *file) +{ + struct allegro_channel *channel = fh_to_channel(file->private_data); + + v4l2_m2m_ctx_release(channel->fh.m2m_ctx); + + list_del(&channel->list); + + v4l2_ctrl_handler_free(&channel->ctrl_handler); + + v4l2_fh_del(&channel->fh); + v4l2_fh_exit(&channel->fh); + + kfree(channel); + + return 0; +} + +static int allegro_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct video_device *vdev = video_devdata(file); + struct allegro_dev *dev = video_get_drvdata(vdev); + + strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strscpy(cap->card, "Allegro DVT Video Encoder", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(&dev->plat_dev->dev)); + + return 0; +} + +static int allegro_enum_fmt_vid(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + f->pixelformat = V4L2_PIX_FMT_NV12; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + f->pixelformat = V4L2_PIX_FMT_H264; + break; + default: + return -EINVAL; + } + return 0; +} + +static int allegro_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct allegro_channel *channel = fh_to_channel(fh); + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = channel->width; + f->fmt.pix.height = channel->height; + + f->fmt.pix.colorspace = channel->colorspace; + f->fmt.pix.ycbcr_enc = channel->ycbcr_enc; + f->fmt.pix.quantization = channel->quantization; + f->fmt.pix.xfer_func = channel->xfer_func; + + f->fmt.pix.pixelformat = channel->codec; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = channel->sizeimage_encoded; + + return 0; +} + +static int allegro_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + f->fmt.pix.field = V4L2_FIELD_NONE; + + f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width, + ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX); + f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height, + ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX); + + f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + estimate_stream_size(f->fmt.pix.width, f->fmt.pix.height); + + return 0; +} + +static int allegro_g_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct allegro_channel *channel = fh_to_channel(fh); + + f->fmt.pix.field = V4L2_FIELD_NONE; + + f->fmt.pix.width = channel->width; + f->fmt.pix.height = channel->height; + + f->fmt.pix.colorspace = channel->colorspace; + f->fmt.pix.ycbcr_enc = channel->ycbcr_enc; + f->fmt.pix.quantization = channel->quantization; + f->fmt.pix.xfer_func = channel->xfer_func; + + f->fmt.pix.pixelformat = channel->pixelformat; + f->fmt.pix.bytesperline = channel->stride; + f->fmt.pix.sizeimage = channel->sizeimage_raw; + + return 0; +} + +static int allegro_try_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + f->fmt.pix.field = V4L2_FIELD_NONE; + + /* + * The firmware of the Allegro codec handles the padding internally + * and expects the visual frame size when configuring a channel. + * Therefore, unlike other encoder drivers, this driver does not round + * up the width and height to macroblock alignment and does not + * implement the selection api. + */ + f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width, + ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX); + f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height, + ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX); + + f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 32); + f->fmt.pix.sizeimage = + f->fmt.pix.bytesperline * f->fmt.pix.height * 3 / 2; + + return 0; +} + +static int allegro_s_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct allegro_channel *channel = fh_to_channel(fh); + int err; + + err = allegro_try_fmt_vid_out(file, fh, f); + if (err) + return err; + + channel->width = f->fmt.pix.width; + channel->height = f->fmt.pix.height; + channel->stride = f->fmt.pix.bytesperline; + channel->sizeimage_raw = f->fmt.pix.sizeimage; + + channel->colorspace = f->fmt.pix.colorspace; + channel->ycbcr_enc = f->fmt.pix.ycbcr_enc; + channel->quantization = f->fmt.pix.quantization; + channel->xfer_func = f->fmt.pix.xfer_func; + + channel->level = + select_minimum_h264_level(channel->width, channel->height); + channel->sizeimage_encoded = + estimate_stream_size(channel->width, channel->height); + + return 0; +} + +static int allegro_channel_cmd_stop(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *dst_buf; + + switch (allegro_get_state(channel)) { + case ALLEGRO_STATE_DRAIN: + case ALLEGRO_STATE_WAIT_FOR_BUFFER: + return -EBUSY; + case ALLEGRO_STATE_ENCODING: + allegro_set_state(channel, ALLEGRO_STATE_DRAIN); + break; + default: + return 0; + } + + /* If there are output buffers, they must be encoded */ + if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) != 0) { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: CMD_STOP: continue encoding src buffers\n", + channel->mcu_channel_id); + return 0; + } + + /* If there are capture buffers, use it to signal EOS */ + dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); + if (dst_buf) { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: CMD_STOP: signaling EOS\n", + channel->mcu_channel_id); + allegro_channel_buf_done(channel, dst_buf, VB2_BUF_STATE_DONE); + return 0; + } + + /* + * If there are no capture buffers, we need to wait for the next + * buffer to signal EOS. + */ + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: CMD_STOP: wait for CAPTURE buffer to signal EOS\n", + channel->mcu_channel_id); + allegro_set_state(channel, ALLEGRO_STATE_WAIT_FOR_BUFFER); + + return 0; +} + +static int allegro_channel_cmd_start(struct allegro_channel *channel) +{ + switch (allegro_get_state(channel)) { + case ALLEGRO_STATE_DRAIN: + case ALLEGRO_STATE_WAIT_FOR_BUFFER: + return -EBUSY; + case ALLEGRO_STATE_STOPPED: + allegro_set_state(channel, ALLEGRO_STATE_ENCODING); + break; + default: + return 0; + } + + return 0; +} + +static int allegro_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *cmd) +{ + struct allegro_channel *channel = fh_to_channel(fh); + int err; + + err = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd); + if (err) + return err; + + switch (cmd->cmd) { + case V4L2_ENC_CMD_STOP: + err = allegro_channel_cmd_stop(channel); + break; + case V4L2_ENC_CMD_START: + err = allegro_channel_cmd_start(channel); + break; + default: + err = -EINVAL; + break; + } + + return err; +} + +static int allegro_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + switch (fsize->pixel_format) { + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_NV12: + break; + default: + return -EINVAL; + } + + if (fsize->index) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = ALLEGRO_WIDTH_MIN; + fsize->stepwise.max_width = ALLEGRO_WIDTH_MAX; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = ALLEGRO_HEIGHT_MIN; + fsize->stepwise.max_height = ALLEGRO_HEIGHT_MAX; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int allegro_ioctl_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct v4l2_fh *fh = file->private_data; + struct allegro_channel *channel = fh_to_channel(fh); + int err; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + err = allegro_create_channel(channel); + if (err) + return err; + } + + return v4l2_m2m_streamon(file, fh->m2m_ctx, type); +} + +static int allegro_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct allegro_channel *channel = fh_to_channel(fh); + struct v4l2_fract *timeperframe; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + timeperframe = &a->parm.output.timeperframe; + timeperframe->numerator = channel->framerate.denominator; + timeperframe->denominator = channel->framerate.numerator; + + return 0; +} + +static int allegro_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct allegro_channel *channel = fh_to_channel(fh); + struct v4l2_fract *timeperframe; + int div; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + timeperframe = &a->parm.output.timeperframe; + + if (timeperframe->numerator == 0 || timeperframe->denominator == 0) + return allegro_g_parm(file, fh, a); + + div = gcd(timeperframe->denominator, timeperframe->numerator); + channel->framerate.numerator = timeperframe->denominator / div; + channel->framerate.denominator = timeperframe->numerator / div; + + return 0; +} + +static int allegro_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + default: + return v4l2_ctrl_subscribe_event(fh, sub); + } +} + +static const struct v4l2_ioctl_ops allegro_ioctl_ops = { + .vidioc_querycap = allegro_querycap, + .vidioc_enum_fmt_vid_cap = allegro_enum_fmt_vid, + .vidioc_enum_fmt_vid_out = allegro_enum_fmt_vid, + .vidioc_g_fmt_vid_cap = allegro_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = allegro_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = allegro_try_fmt_vid_cap, + .vidioc_g_fmt_vid_out = allegro_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = allegro_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = allegro_s_fmt_vid_out, + + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + + .vidioc_streamon = allegro_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = allegro_encoder_cmd, + .vidioc_enum_framesizes = allegro_enum_framesizes, + + .vidioc_g_parm = allegro_g_parm, + .vidioc_s_parm = allegro_s_parm, + + .vidioc_subscribe_event = allegro_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations allegro_fops = { + .owner = THIS_MODULE, + .open = allegro_open, + .release = allegro_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int allegro_register_device(struct allegro_dev *dev) +{ + struct video_device *video_dev = &dev->video_dev; + + strscpy(video_dev->name, "allegro", sizeof(video_dev->name)); + video_dev->fops = &allegro_fops; + video_dev->ioctl_ops = &allegro_ioctl_ops; + video_dev->release = video_device_release_empty; + video_dev->lock = &dev->lock; + video_dev->v4l2_dev = &dev->v4l2_dev; + video_dev->vfl_dir = VFL_DIR_M2M; + video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + video_set_drvdata(video_dev, dev); + + return video_register_device(video_dev, VFL_TYPE_VIDEO, 0); +} + +static void allegro_device_run(void *priv) +{ + struct allegro_channel *channel = priv; + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *src_buf; + struct vb2_v4l2_buffer *dst_buf; + dma_addr_t src_y; + dma_addr_t src_uv; + dma_addr_t dst_addr; + unsigned long dst_size; + u64 src_handle; + u64 dst_handle; + + dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0); + dst_handle = allegro_put_buffer(channel, &channel->stream_shadow_list, + dst_buf); + allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size, + dst_handle); + + src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx); + src_buf->sequence = channel->osequence++; + src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + src_uv = src_y + (channel->stride * channel->height); + src_handle = allegro_put_buffer(channel, &channel->source_shadow_list, + src_buf); + allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv, src_handle); + + v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx); +} + +static const struct v4l2_m2m_ops allegro_m2m_ops = { + .device_run = allegro_device_run, +}; + +static int allegro_mcu_hw_init(struct allegro_dev *dev, + const struct fw_info *info) +{ + int err; + + dev->mbox_command = allegro_mbox_init(dev, info->mailbox_cmd, + info->mailbox_size); + dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status, + info->mailbox_size); + if (IS_ERR(dev->mbox_command) || IS_ERR(dev->mbox_status)) { + v4l2_err(&dev->v4l2_dev, + "failed to initialize mailboxes\n"); + return -EIO; + } + + allegro_mcu_enable_interrupts(dev); + + /* The mcu sends INIT after reset. */ + allegro_mcu_start(dev); + err = allegro_mcu_wait_for_init_timeout(dev, 5000); + if (err < 0) { + v4l2_err(&dev->v4l2_dev, + "mcu did not send INIT after reset\n"); + err = -EIO; + goto err_disable_interrupts; + } + + err = allegro_alloc_buffer(dev, &dev->suballocator, + info->suballocator_size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate %zu bytes for suballocator\n", + info->suballocator_size); + goto err_reset_mcu; + } + + allegro_mcu_send_init(dev, dev->suballocator.paddr, + dev->suballocator.size); + err = allegro_mcu_wait_for_init_timeout(dev, 5000); + if (err < 0) { + v4l2_err(&dev->v4l2_dev, + "mcu failed to configure sub-allocator\n"); + err = -EIO; + goto err_free_suballocator; + } + + return 0; + +err_free_suballocator: + allegro_free_buffer(dev, &dev->suballocator); +err_reset_mcu: + allegro_mcu_reset(dev); +err_disable_interrupts: + allegro_mcu_disable_interrupts(dev); + + return err; +} + +static int allegro_mcu_hw_deinit(struct allegro_dev *dev) +{ + int err; + + err = allegro_mcu_reset(dev); + if (err) + v4l2_warn(&dev->v4l2_dev, + "mcu failed to enter sleep state\n"); + + err = allegro_mcu_disable_interrupts(dev); + if (err) + v4l2_warn(&dev->v4l2_dev, + "failed to disable interrupts\n"); + + allegro_free_buffer(dev, &dev->suballocator); + + return 0; +} + +static void allegro_fw_callback(const struct firmware *fw, void *context) +{ + struct allegro_dev *dev = context; + const char *fw_codec_name = "al5e.fw"; + const struct firmware *fw_codec; + int err; + + if (!fw) + return; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "requesting codec firmware '%s'\n", fw_codec_name); + err = request_firmware(&fw_codec, fw_codec_name, &dev->plat_dev->dev); + if (err) + goto err_release_firmware; + + dev->fw_info = allegro_get_firmware_info(dev, fw, fw_codec); + if (!dev->fw_info) { + v4l2_err(&dev->v4l2_dev, "firmware is not supported\n"); + goto err_release_firmware_codec; + } + + v4l2_info(&dev->v4l2_dev, + "using mcu firmware version '%s'\n", dev->fw_info->version); + + /* Ensure that the mcu is sleeping at the reset vector */ + err = allegro_mcu_reset(dev); + if (err) { + v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n"); + goto err_release_firmware_codec; + } + + allegro_copy_firmware(dev, fw->data, fw->size); + allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size); + + err = allegro_mcu_hw_init(dev, dev->fw_info); + if (err) { + v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n"); + goto err_free_fw_codec; + } + + dev->m2m_dev = v4l2_m2m_init(&allegro_m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, "failed to init mem2mem device\n"); + goto err_mcu_hw_deinit; + } + + err = allegro_register_device(dev); + if (err) { + v4l2_err(&dev->v4l2_dev, "failed to register video device\n"); + goto err_m2m_release; + } + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "allegro codec registered as /dev/video%d\n", + dev->video_dev.num); + + release_firmware(fw_codec); + release_firmware(fw); + + return; + +err_m2m_release: + v4l2_m2m_release(dev->m2m_dev); + dev->m2m_dev = NULL; +err_mcu_hw_deinit: + allegro_mcu_hw_deinit(dev); +err_free_fw_codec: + allegro_free_fw_codec(dev); +err_release_firmware_codec: + release_firmware(fw_codec); +err_release_firmware: + release_firmware(fw); +} + +static int allegro_firmware_request_nowait(struct allegro_dev *dev) +{ + const char *fw = "al5e_b.fw"; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "requesting firmware '%s'\n", fw); + return request_firmware_nowait(THIS_MODULE, true, fw, + &dev->plat_dev->dev, GFP_KERNEL, dev, + allegro_fw_callback); +} + +static int allegro_probe(struct platform_device *pdev) +{ + struct allegro_dev *dev; + struct resource *res, *sram_res; + int ret; + int irq; + void __iomem *regs, *sram_regs; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->plat_dev = pdev; + init_completion(&dev->init_complete); + INIT_LIST_HEAD(&dev->channels); + + mutex_init(&dev->lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!res) { + dev_err(&pdev->dev, + "regs resource missing from device tree\n"); + return -EINVAL; + } + regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!regs) { + dev_err(&pdev->dev, "failed to map registers\n"); + return -ENOMEM; + } + dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &allegro_regmap_config); + if (IS_ERR(dev->regmap)) { + dev_err(&pdev->dev, "failed to init regmap\n"); + return PTR_ERR(dev->regmap); + } + + sram_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + if (!sram_res) { + dev_err(&pdev->dev, + "sram resource missing from device tree\n"); + return -EINVAL; + } + sram_regs = devm_ioremap(&pdev->dev, + sram_res->start, + resource_size(sram_res)); + if (!sram_regs) { + dev_err(&pdev->dev, "failed to map sram\n"); + return -ENOMEM; + } + dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs, + &allegro_sram_config); + if (IS_ERR(dev->sram)) { + dev_err(&pdev->dev, "failed to init sram\n"); + return PTR_ERR(dev->sram); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + ret = devm_request_threaded_irq(&pdev->dev, irq, + allegro_hardirq, + allegro_irq_thread, + IRQF_SHARED, dev_name(&pdev->dev), dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq: %d\n", ret); + return ret; + } + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, dev); + + ret = allegro_firmware_request_nowait(dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to request firmware: %d\n", ret); + return ret; + } + + return 0; +} + +static int allegro_remove(struct platform_device *pdev) +{ + struct allegro_dev *dev = platform_get_drvdata(pdev); + + video_unregister_device(&dev->video_dev); + if (dev->m2m_dev) + v4l2_m2m_release(dev->m2m_dev); + allegro_mcu_hw_deinit(dev); + allegro_free_fw_codec(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + + return 0; +} + +static const struct of_device_id allegro_dt_ids[] = { + { .compatible = "allegro,al5e-1.1" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, allegro_dt_ids); + +static struct platform_driver allegro_driver = { + .probe = allegro_probe, + .remove = allegro_remove, + .driver = { + .name = "allegro", + .of_match_table = of_match_ptr(allegro_dt_ids), + }, +}; + +module_platform_driver(allegro_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Tretter "); +MODULE_DESCRIPTION("Allegro DVT encoder driver"); diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.c b/drivers/media/platform/allegro-dvt/allegro-mail.c new file mode 100644 index 000000000000..9286d2162377 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/allegro-mail.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + * + * Helper functions for handling messages that are send via mailbox to the + * Allegro VCU firmware. + */ + +#include +#include +#include +#include +#include + +#include "allegro-mail.h" + +const char *msg_type_name(enum mcu_msg_type type) +{ + static char buf[9]; + + switch (type) { + case MCU_MSG_TYPE_INIT: + return "INIT"; + case MCU_MSG_TYPE_CREATE_CHANNEL: + return "CREATE_CHANNEL"; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + return "DESTROY_CHANNEL"; + case MCU_MSG_TYPE_ENCODE_FRAME: + return "ENCODE_FRAME"; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + return "PUT_STREAM_BUFFER"; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + return "PUSH_BUFFER_INTERMEDIATE"; + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + return "PUSH_BUFFER_REFERENCE"; + default: + snprintf(buf, sizeof(buf), "(0x%04x)", type); + return buf; + } +} +EXPORT_SYMBOL(msg_type_name); + +static ssize_t +allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) +{ + unsigned int i = 0; + enum mcu_msg_version version = msg->header.version; + + dst[i++] = msg->reserved0; + dst[i++] = msg->suballoc_dma; + dst[i++] = msg->suballoc_size; + dst[i++] = msg->l2_cache[0]; + dst[i++] = msg->l2_cache[1]; + dst[i++] = msg->l2_cache[2]; + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = -1; + dst[i++] = 0; + } + + return i * sizeof(*dst); +} + +static inline u32 settings_get_mcu_codec(struct create_channel_param *param) +{ + enum mcu_msg_version version = param->version; + u32 pixelformat = param->codec; + + if (version < MCU_MSG_VERSION_2019_2) { + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 1; + } + } else { + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 0; + } + } +} + +ssize_t +allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) +{ + enum mcu_msg_version version = param->version; + unsigned int i = 0; + unsigned int j = 0; + u32 val; + unsigned int codec = settings_get_mcu_codec(param); + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->layer_id; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | + FIELD_PREP(GENMASK(15, 0), param->width); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->videomode; + dst[i++] = param->format; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = param->colorspace; + dst[i++] = param->src_mode; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->src_bit_depth; + dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) | + FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | + FIELD_PREP(GENMASK(7, 0), param->profile); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) | + FIELD_PREP(GENMASK(15, 0), param->level); + + val = 0; + val |= param->temporal_mvp_enable ? BIT(20) : 0; + val |= FIELD_PREP(GENMASK(7, 4), param->log2_max_frame_num) | + FIELD_PREP(GENMASK(3, 0), param->log2_max_poc); + dst[i++] = val; + + val = 0; + val |= param->dbf_ovr_en ? BIT(2) : 0; + dst[i++] = val; + + if (version >= MCU_MSG_VERSION_2019_2) { + val = 0; + val |= param->custom_lda ? BIT(2) : 0; + val |= param->rdo_cost_mode ? BIT(20) : 0; + dst[i++] = val; + + val = 0; + val |= param->lf ? BIT(2) : 0; + val |= param->lf_x_tile ? BIT(3) : 0; + val |= param->lf_x_slice ? BIT(4) : 0; + dst[i++] = val; + } else { + val = 0; + dst[i++] = val; + } + + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | + FIELD_PREP(GENMASK(7, 0), param->tc_offset); + dst[i++] = param->unknown11; + dst[i++] = param->unknown12; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->num_slices; + else + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | + FIELD_PREP(GENMASK(15, 0), param->num_slices); + dst[i++] = param->prefetch_mem_offset; + dst[i++] = param->prefetch_mem_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | + FIELD_PREP(GENMASK(15, 0), param->clip_hrz_range); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[1]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[0]); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[3]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[2]); + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->min_tu_size) | + FIELD_PREP(GENMASK(23, 16), param->max_tu_size) | + FIELD_PREP(GENMASK(15, 8), param->min_cu_size) | + FIELD_PREP(GENMASK(8, 0), param->max_cu_size); + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->max_transfo_depth_intra) | + FIELD_PREP(GENMASK(7, 0), param->max_transfo_depth_inter); + dst[i++] = param->entropy_mode; + dst[i++] = param->wp_mode; + + dst[i++] = param->rate_control_mode; + dst[i++] = param->initial_rem_delay; + dst[i++] = param->cpb_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clk_ratio) | + FIELD_PREP(GENMASK(15, 0), param->framerate); + dst[i++] = param->target_bitrate; + dst[i++] = param->max_bitrate; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->min_qp) | + FIELD_PREP(GENMASK(15, 0), param->initial_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->ip_delta) | + FIELD_PREP(GENMASK(15, 0), param->max_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref) | + FIELD_PREP(GENMASK(15, 0), param->pb_delta); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | + FIELD_PREP(GENMASK(15, 0), param->golden_delta); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->rate_control_option; + else + dst[i++] = 0; + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = param->num_pixel; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->max_pixel_value) | + FIELD_PREP(GENMASK(15, 0), param->max_psnr); + for (j = 0; j < 3; j++) + dst[i++] = param->maxpicturesize[j]; + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->gop_ctrl_mode; + else + dst[i++] = 0; + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + dst[i++] = param->freq_idr; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->enable_lt; + dst[i++] = param->freq_lt; + dst[i++] = param->gdr_mode; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->tmpdqp; + + dst[i++] = param->subframe_latency; + dst[i++] = param->lda_control_mode; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = param->unknown41; + + if (version >= MCU_MSG_VERSION_2019_2) { + for (j = 0; j < 6; j++) + dst[i++] = param->lda_factors[j]; + dst[i++] = param->max_num_merge_cand; + } + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + + dst[i++] = msg->user_id; + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = msg->blob_mcu_addr; + } else { + memcpy(&dst[i], msg->blob, msg->blob_size); + i += msg->blob_size / sizeof(*dst); + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->ep1_addr; + + return i * sizeof(*dst); +} + +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src) +{ + enum mcu_msg_version version = msg->header.version; + + if (version >= MCU_MSG_VERSION_2019_2) { + param->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[9]); + param->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[9]); + } else { + param->num_ref_idx_l0 = msg->num_ref_idx_l0; + param->num_ref_idx_l1 = msg->num_ref_idx_l1; + } + + return 0; +} + +static ssize_t +allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg) +{ + unsigned int i = 0; + struct mcu_msg_push_buffers_internal_buffer *buffer; + unsigned int num_buffers = msg->num_buffers; + unsigned int j; + + dst[i++] = msg->channel_id; + + for (j = 0; j < num_buffers; j++) { + buffer = &msg->buffer[j]; + dst[i++] = buffer->dma_addr; + dst[i++] = buffer->mcu_addr; + dst[i++] = buffer->size; + } + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_put_stream_buffer(u32 *dst, + struct mcu_msg_put_stream_buffer *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + dst[i++] = msg->dma_addr; + dst[i++] = msg->mcu_addr; + dst[i++] = msg->size; + dst[i++] = msg->offset; + dst[i++] = lower_32_bits(msg->stream_id); + dst[i++] = upper_32_bits(msg->stream_id); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + + dst[i++] = msg->channel_id; + + dst[i++] = msg->reserved; + dst[i++] = msg->encoding_options; + dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) | + FIELD_PREP(GENMASK(15, 0), msg->pps_qp); + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = 0; + dst[i++] = 0; + dst[i++] = 0; + dst[i++] = 0; + } + + dst[i++] = lower_32_bits(msg->user_param); + dst[i++] = upper_32_bits(msg->user_param); + dst[i++] = lower_32_bits(msg->src_handle); + dst[i++] = upper_32_bits(msg->src_handle); + dst[i++] = msg->request_options; + dst[i++] = msg->src_y; + dst[i++] = msg->src_uv; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->is_10_bit; + dst[i++] = msg->stride; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->format; + dst[i++] = msg->ep2; + dst[i++] = lower_32_bits(msg->ep2_v); + dst[i++] = upper_32_bits(msg->ep2_v); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_dec_init(struct mcu_msg_init_response *msg, u32 *src) +{ + unsigned int i = 0; + + msg->reserved0 = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, + u32 *src) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + + msg->channel_id = src[i++]; + msg->user_id = src[i++]; + /* + * Version >= MCU_MSG_VERSION_2019_2 is handled in + * allegro_decode_config_blob(). + */ + if (version < MCU_MSG_VERSION_2019_2) { + msg->options = src[i++]; + msg->num_core = src[i++]; + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); + } + msg->int_buffers_count = src[i++]; + msg->int_buffers_size = src[i++]; + msg->rec_buffers_count = src[i++]; + msg->rec_buffers_size = src[i++]; + msg->reserved = src[i++]; + msg->error_code = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, + u32 *src) +{ + unsigned int i = 0; + + msg->channel_id = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) +{ + enum mcu_msg_version version = msg->header.version; + unsigned int i = 0; + unsigned int j; + + msg->channel_id = src[i++]; + + msg->stream_id = src[i++]; + msg->stream_id |= (((u64)src[i++]) << 32); + msg->user_param = src[i++]; + msg->user_param |= (((u64)src[i++]) << 32); + msg->src_handle = src[i++]; + msg->src_handle |= (((u64)src[i++]) << 32); + msg->skip = FIELD_GET(GENMASK(31, 16), src[i]); + msg->is_ref = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->initial_removal_delay = src[i++]; + msg->dpb_output_delay = src[i++]; + msg->size = src[i++]; + msg->frame_tag_size = src[i++]; + msg->stuffing = src[i++]; + msg->filler = src[i++]; + msg->num_column = FIELD_GET(GENMASK(31, 16), src[i]); + msg->num_row = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(31, 24), src[i]); + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(23, 16), src[i]); + msg->qp = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->partition_table_offset = src[i++]; + msg->partition_table_size = src[i++]; + msg->sum_complex = src[i++]; + for (j = 0; j < 4; j++) + msg->tile_width[j] = src[i++]; + for (j = 0; j < 22; j++) + msg->tile_height[j] = src[i++]; + msg->error_code = src[i++]; + msg->slice_type = src[i++]; + msg->pic_struct = src[i++]; + msg->reserved = FIELD_GET(GENMASK(31, 24), src[i]); + msg->is_last_slice = FIELD_GET(GENMASK(23, 16), src[i]); + msg->is_first_slice = FIELD_GET(GENMASK(15, 8), src[i]); + msg->is_idr = FIELD_GET(GENMASK(7, 0), src[i++]); + + msg->reserved1 = FIELD_GET(GENMASK(31, 16), src[i]); + msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); + + msg->reserved2 = src[i++]; + if (version >= MCU_MSG_VERSION_2019_2) { + msg->reserved3 = src[i++]; + msg->reserved4 = src[i++]; + msg->reserved5 = src[i++]; + msg->reserved6 = src[i++]; + } + + return i * sizeof(*src); +} + +/** + * allegro_encode_mail() - Encode allegro messages to firmware format + * @dst: Pointer to the memory that will be filled with data + * @msg: The allegro message that will be encoded + */ +ssize_t allegro_encode_mail(u32 *dst, void *msg) +{ + const struct mcu_msg_header *header = msg; + ssize_t size; + + if (!msg || !dst) + return -EINVAL; + + switch (header->type) { + case MCU_MSG_TYPE_INIT: + size = allegro_enc_init(&dst[1], msg); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + size = allegro_enc_create_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + size = allegro_enc_destroy_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + size = allegro_enc_encode_frame(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + size = allegro_enc_put_stream_buffer(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + size = allegro_enc_push_buffers(&dst[1], msg); + break; + default: + return -EINVAL; + } + + /* + * The encoded messages might have different length depending on + * the firmware version or certain fields. Therefore, we have to + * set the body length after encoding the message. + */ + dst[0] = FIELD_PREP(GENMASK(31, 16), header->type) | + FIELD_PREP(GENMASK(15, 0), size); + + return size + sizeof(*dst); +} + +/** + * allegro_decode_mail() - Parse allegro messages from the firmware. + * @msg: The mcu_msg_response that will be filled with parsed values. + * @src: Pointer to the memory that will be parsed + * + * The message format in the mailbox depends on the firmware. Parse the + * different formats into a uniform message format that can be used without + * taking care of the firmware version. + */ +int allegro_decode_mail(void *msg, u32 *src) +{ + struct mcu_msg_header *header; + + if (!src || !msg) + return -EINVAL; + + header = msg; + header->type = FIELD_GET(GENMASK(31, 16), src[0]); + + src++; + switch (header->type) { + case MCU_MSG_TYPE_INIT: + allegro_dec_init(msg, src); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + allegro_dec_create_channel(msg, src); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + allegro_dec_destroy_channel(msg, src); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + allegro_dec_encode_frame(msg, src); + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.h b/drivers/media/platform/allegro-dvt/allegro-mail.h new file mode 100644 index 000000000000..486ecb12b098 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/allegro-mail.h @@ -0,0 +1,294 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + * + * Allegro VCU firmware mailbox mail definitions + */ + +#ifndef ALLEGRO_MAIL_H +#define ALLEGRO_MAIL_H + +#include + +enum mcu_msg_type { + MCU_MSG_TYPE_INIT = 0x0000, + MCU_MSG_TYPE_CREATE_CHANNEL = 0x0005, + MCU_MSG_TYPE_DESTROY_CHANNEL = 0x0006, + MCU_MSG_TYPE_ENCODE_FRAME = 0x0007, + MCU_MSG_TYPE_PUT_STREAM_BUFFER = 0x0012, + MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE = 0x000e, + MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, +}; + +enum mcu_msg_version { + MCU_MSG_VERSION_2018_2, + MCU_MSG_VERSION_2019_2, +}; + +const char *msg_type_name(enum mcu_msg_type type); + +struct mcu_msg_header { + enum mcu_msg_type type; + enum mcu_msg_version version; +}; + +struct mcu_msg_init_request { + struct mcu_msg_header header; + u32 reserved0; /* maybe a unused channel id */ + u32 suballoc_dma; + u32 suballoc_size; + s32 l2_cache[3]; +}; + +struct mcu_msg_init_response { + struct mcu_msg_header header; + u32 reserved0; +}; + +struct create_channel_param { + enum mcu_msg_version version; + u32 layer_id; + u16 width; + u16 height; + u32 videomode; + u32 format; + u32 colorspace; + u32 src_mode; + u32 src_bit_depth; + u8 profile; + u16 constraint_set_flags; + u32 codec; + u16 level; + u16 tier; + u32 log2_max_poc; + u32 log2_max_frame_num; + u32 temporal_mvp_enable; + u32 enable_reordering; + u32 dbf_ovr_en; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; + u32 custom_lda; + u32 rdo_cost_mode; + u32 lf; + u32 lf_x_tile; + u32 lf_x_slice; + s8 beta_offset; + s8 tc_offset; + u16 reserved10; + u32 unknown11; + u32 unknown12; + u16 num_slices; + u16 prefetch_auto; + u32 prefetch_mem_offset; + u32 prefetch_mem_size; + u16 clip_hrz_range; + u16 clip_vrt_range; + u16 me_range[4]; + u8 max_cu_size; + u8 min_cu_size; + u8 max_tu_size; + u8 min_tu_size; + u8 max_transfo_depth_inter; + u8 max_transfo_depth_intra; + u16 reserved20; + u32 entropy_mode; + u32 wp_mode; + + /* rate control param */ + u32 rate_control_mode; + u32 initial_rem_delay; + u32 cpb_size; + u16 framerate; + u16 clk_ratio; + u32 target_bitrate; + u32 max_bitrate; + u16 initial_qp; + u16 min_qp; + u16 max_qp; + s16 ip_delta; + s16 pb_delta; + u16 golden_ref; + u16 golden_delta; + u16 golden_ref_frequency; + u32 rate_control_option; + u32 num_pixel; + u16 max_psnr; + u16 max_pixel_value; + u32 maxpicturesize[3]; + + /* gop param */ + u32 gop_ctrl_mode; + u32 freq_idr; + u32 freq_lt; + u32 gdr_mode; + u16 gop_length; + u8 num_b; + u8 freq_golden_ref; + u32 enable_lt; + u32 tmpdqp; + + u32 subframe_latency; + u32 lda_control_mode; + u32 unknown41; + + u32 lda_factors[6]; + + u32 max_num_merge_cand; +}; + +struct mcu_msg_create_channel { + struct mcu_msg_header header; + u32 user_id; + u32 *blob; + size_t blob_size; + u32 blob_mcu_addr; + u32 ep1_addr; +}; + +struct mcu_msg_create_channel_response { + struct mcu_msg_header header; + u32 channel_id; + u32 user_id; + u32 options; + u32 num_core; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; + u32 int_buffers_count; + u32 int_buffers_size; + u32 rec_buffers_count; + u32 rec_buffers_size; + u32 reserved; + u32 error_code; +}; + +struct mcu_msg_destroy_channel { + struct mcu_msg_header header; + u32 channel_id; +}; + +struct mcu_msg_destroy_channel_response { + struct mcu_msg_header header; + u32 channel_id; +}; + +struct mcu_msg_push_buffers_internal_buffer { + u32 dma_addr; + u32 mcu_addr; + u32 size; +}; + +struct mcu_msg_push_buffers_internal { + struct mcu_msg_header header; + u32 channel_id; + size_t num_buffers; + struct mcu_msg_push_buffers_internal_buffer buffer[]; +}; + +struct mcu_msg_put_stream_buffer { + struct mcu_msg_header header; + u32 channel_id; + u32 dma_addr; + u32 mcu_addr; + u32 size; + u32 offset; + u64 stream_id; +}; + +struct mcu_msg_encode_frame { + struct mcu_msg_header header; + u32 channel_id; + u32 reserved; + + u32 encoding_options; +#define AL_OPT_USE_QP_TABLE BIT(0) +#define AL_OPT_FORCE_LOAD BIT(1) +#define AL_OPT_USE_L2 BIT(2) +#define AL_OPT_DISABLE_INTRA BIT(3) +#define AL_OPT_DEPENDENT_SLICES BIT(4) + + s16 pps_qp; + u16 padding; + u64 user_param; + u64 src_handle; + + u32 request_options; +#define AL_OPT_SCENE_CHANGE BIT(0) +#define AL_OPT_RESTART_GOP BIT(1) +#define AL_OPT_USE_LONG_TERM BIT(2) +#define AL_OPT_UPDATE_PARAMS BIT(3) + + /* u32 scene_change_delay (optional) */ + /* rate control param (optional) */ + /* gop param (optional) */ + /* dynamic resolution params (optional) */ + u32 src_y; + u32 src_uv; + u32 is_10_bit; + u32 stride; + u32 format; + u32 ep2; + u64 ep2_v; +}; + +struct mcu_msg_encode_frame_response { + struct mcu_msg_header header; + u32 channel_id; + u64 stream_id; /* see mcu_msg_put_stream_buffer */ + u64 user_param; /* see mcu_msg_encode_frame */ + u64 src_handle; /* see mcu_msg_encode_frame */ + u16 skip; + u16 is_ref; + u32 initial_removal_delay; + u32 dpb_output_delay; + u32 size; + u32 frame_tag_size; + s32 stuffing; + s32 filler; + u16 num_column; + u16 num_row; + u16 qp; + u8 num_ref_idx_l0; + u8 num_ref_idx_l1; + u32 partition_table_offset; + s32 partition_table_size; + u32 sum_complex; + s32 tile_width[4]; + s32 tile_height[22]; + u32 error_code; + + u32 slice_type; +#define AL_ENC_SLICE_TYPE_B 0 +#define AL_ENC_SLICE_TYPE_P 1 +#define AL_ENC_SLICE_TYPE_I 2 + + u32 pic_struct; + u8 is_idr; + u8 is_first_slice; + u8 is_last_slice; + u8 reserved; + u16 pps_qp; + u16 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4; + u32 reserved5; + u32 reserved6; +}; + +union mcu_msg_response { + struct mcu_msg_header header; + struct mcu_msg_init_response init; + struct mcu_msg_create_channel_response create_channel; + struct mcu_msg_destroy_channel_response destroy_channel; + struct mcu_msg_encode_frame_response encode_frame; +}; + +ssize_t allegro_encode_config_blob(u32 *dst, struct create_channel_param *param); +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src); + +int allegro_decode_mail(void *msg, u32 *src); +ssize_t allegro_encode_mail(u32 *dst, void *msg); + +#endif diff --git a/drivers/media/platform/allegro-dvt/nal-h264.c b/drivers/media/platform/allegro-dvt/nal-h264.c new file mode 100644 index 000000000000..bd48b8883572 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-h264.c @@ -0,0 +1,1001 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + * + * Convert NAL units between raw byte sequence payloads (RBSP) and C structs + * + * The conversion is defined in "ITU-T Rec. H.264 (04/2017) Advanced video + * coding for generic audiovisual services". Decoder drivers may use the + * parser to parse RBSP from encoded streams and configure the hardware, if + * the hardware is not able to parse RBSP itself. Encoder drivers may use the + * generator to generate the RBSP for SPS/PPS nal units and add them to the + * encoded stream if the hardware does not generate the units. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "nal-h264.h" + +/* + * See Rec. ITU-T H.264 (04/2017) Table 7-1 – NAL unit type codes, syntax + * element categories, and NAL unit type classes + */ +enum nal_unit_type { + SEQUENCE_PARAMETER_SET = 7, + PICTURE_PARAMETER_SET = 8, + FILLER_DATA = 12, +}; + +struct rbsp; + +struct nal_h264_ops { + int (*rbsp_bit)(struct rbsp *rbsp, int *val); + int (*rbsp_bits)(struct rbsp *rbsp, int n, unsigned int *val); + int (*rbsp_uev)(struct rbsp *rbsp, unsigned int *val); + int (*rbsp_sev)(struct rbsp *rbsp, int *val); +}; + +/** + * struct rbsp - State object for handling a raw byte sequence payload + * @data: pointer to the data of the rbsp + * @size: maximum size of the data of the rbsp + * @pos: current bit position inside the rbsp + * @num_consecutive_zeros: number of zeros before @pos + * @ops: per datatype functions for interacting with the rbsp + * @error: an error occurred while handling the rbsp + * + * This struct is passed around the various parsing functions and tracks the + * current position within the raw byte sequence payload. + * + * The @ops field allows to separate the operation, i.e., reading/writing a + * value from/to that rbsp, from the structure of the NAL unit. This allows to + * have a single function for iterating the NAL unit, while @ops has function + * pointers for handling each type in the rbsp. + */ +struct rbsp { + u8 *data; + size_t size; + unsigned int pos; + unsigned int num_consecutive_zeros; + struct nal_h264_ops *ops; + int error; +}; + +static void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, + struct nal_h264_ops *ops) +{ + if (!rbsp) + return; + + rbsp->data = addr; + rbsp->size = size; + rbsp->pos = 0; + rbsp->ops = ops; + rbsp->error = 0; +} + +/** + * nal_h264_profile_from_v4l2() - Get profile_idc for v4l2 h264 profile + * @profile: the profile as &enum v4l2_mpeg_video_h264_profile + * + * Convert the &enum v4l2_mpeg_video_h264_profile to profile_idc as specified + * in Rec. ITU-T H.264 (04/2017) A.2. + * + * Return: the profile_idc for the passed level + */ +int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + return 66; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + return 77; + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: + return 88; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + return 100; + default: + return -EINVAL; + } +} + +/** + * nal_h264_level_from_v4l2() - Get level_idc for v4l2 h264 level + * @level: the level as &enum v4l2_mpeg_video_h264_level + * + * Convert the &enum v4l2_mpeg_video_h264_level to level_idc as specified in + * Rec. ITU-T H.264 (04/2017) A.3.2. + * + * Return: the level_idc for the passed level + */ +int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 10; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return 9; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 20; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 30; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 40; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 50; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + return 51; + default: + return -EINVAL; + } +} + +static int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value); +static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value); + +/* + * When reading or writing, the emulation_prevention_three_byte is detected + * only when the 2 one bits need to be inserted. Therefore, we are not + * actually adding the 0x3 byte, but the 2 one bits and the six 0 bits of the + * next byte. + */ +#define EMULATION_PREVENTION_THREE_BYTE (0x3 << 6) + +static int add_emulation_prevention_three_byte(struct rbsp *rbsp) +{ + rbsp->num_consecutive_zeros = 0; + rbsp_write_bits(rbsp, 8, EMULATION_PREVENTION_THREE_BYTE); + + return 0; +} + +static int discard_emulation_prevention_three_byte(struct rbsp *rbsp) +{ + unsigned int tmp = 0; + + rbsp->num_consecutive_zeros = 0; + rbsp_read_bits(rbsp, 8, &tmp); + if (tmp != EMULATION_PREVENTION_THREE_BYTE) + return -EINVAL; + + return 0; +} + +static inline int rbsp_read_bit(struct rbsp *rbsp) +{ + int shift; + int ofs; + int bit; + int err; + + if (rbsp->num_consecutive_zeros == 22) { + err = discard_emulation_prevention_three_byte(rbsp); + if (err) + return err; + } + + shift = 7 - (rbsp->pos % 8); + ofs = rbsp->pos / 8; + if (ofs >= rbsp->size) + return -EINVAL; + + bit = (rbsp->data[ofs] >> shift) & 1; + + rbsp->pos++; + + if (bit == 1 || + (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) + rbsp->num_consecutive_zeros = 0; + else + rbsp->num_consecutive_zeros++; + + return bit; +} + +static inline int rbsp_write_bit(struct rbsp *rbsp, bool value) +{ + int shift; + int ofs; + + if (rbsp->num_consecutive_zeros == 22) + add_emulation_prevention_three_byte(rbsp); + + shift = 7 - (rbsp->pos % 8); + ofs = rbsp->pos / 8; + if (ofs >= rbsp->size) + return -EINVAL; + + rbsp->data[ofs] &= ~(1 << shift); + rbsp->data[ofs] |= value << shift; + + rbsp->pos++; + + if (value || + (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) { + rbsp->num_consecutive_zeros = 0; + } else { + rbsp->num_consecutive_zeros++; + } + + return 0; +} + +static inline int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value) +{ + int i; + int bit; + unsigned int tmp = 0; + + if (n > 8 * sizeof(*value)) + return -EINVAL; + + for (i = n; i > 0; i--) { + bit = rbsp_read_bit(rbsp); + if (bit < 0) + return bit; + tmp |= bit << (i - 1); + } + + if (value) + *value = tmp; + + return 0; +} + +static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value) +{ + int ret; + + if (n > 8 * sizeof(value)) + return -EINVAL; + + while (n--) { + ret = rbsp_write_bit(rbsp, (value >> n) & 1); + if (ret) + return ret; + } + + return 0; +} + +static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *value) +{ + int leading_zero_bits = 0; + unsigned int tmp = 0; + int ret; + + while ((ret = rbsp_read_bit(rbsp)) == 0) + leading_zero_bits++; + if (ret < 0) + return ret; + + if (leading_zero_bits > 0) { + ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); + if (ret) + return ret; + } + + if (value) + *value = (1 << leading_zero_bits) - 1 + tmp; + + return 0; +} + +static int rbsp_write_uev(struct rbsp *rbsp, unsigned int *value) +{ + int ret; + int leading_zero_bits; + + if (!value) + return -EINVAL; + + leading_zero_bits = ilog2(*value + 1); + + ret = rbsp_write_bits(rbsp, leading_zero_bits, 0); + if (ret) + return ret; + + return rbsp_write_bits(rbsp, leading_zero_bits + 1, *value + 1); +} + +static int rbsp_read_sev(struct rbsp *rbsp, int *value) +{ + int ret; + unsigned int tmp; + + ret = rbsp_read_uev(rbsp, &tmp); + if (ret) + return ret; + + if (value) { + if (tmp & 1) + *value = (tmp + 1) / 2; + else + *value = -(tmp / 2); + } + + return 0; +} + +static int rbsp_write_sev(struct rbsp *rbsp, int *value) +{ + unsigned int tmp; + + if (!value) + return -EINVAL; + + if (*value > 0) + tmp = (2 * (*value)) | 1; + else + tmp = -2 * (*value); + + return rbsp_write_uev(rbsp, &tmp); +} + +static int __rbsp_write_bit(struct rbsp *rbsp, int *value) +{ + return rbsp_write_bit(rbsp, *value); +} + +static int __rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int *value) +{ + return rbsp_write_bits(rbsp, n, *value); +} + +static struct nal_h264_ops write = { + .rbsp_bit = __rbsp_write_bit, + .rbsp_bits = __rbsp_write_bits, + .rbsp_uev = rbsp_write_uev, + .rbsp_sev = rbsp_write_sev, +}; + +static int __rbsp_read_bit(struct rbsp *rbsp, int *value) +{ + int tmp = rbsp_read_bit(rbsp); + + if (tmp < 0) + return tmp; + *value = tmp; + + return 0; +} + +static struct nal_h264_ops read = { + .rbsp_bit = __rbsp_read_bit, + .rbsp_bits = rbsp_read_bits, + .rbsp_uev = rbsp_read_uev, + .rbsp_sev = rbsp_read_sev, +}; + +static inline void rbsp_bit(struct rbsp *rbsp, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_bit(rbsp, value); +} + +static inline void rbsp_bits(struct rbsp *rbsp, int n, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_bits(rbsp, n, value); +} + +static inline void rbsp_uev(struct rbsp *rbsp, unsigned int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_uev(rbsp, value); +} + +static inline void rbsp_sev(struct rbsp *rbsp, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_sev(rbsp, value); +} + +static void nal_h264_rbsp_trailing_bits(struct rbsp *rbsp) +{ + unsigned int rbsp_stop_one_bit = 1; + unsigned int rbsp_alignment_zero_bit = 0; + + rbsp_bit(rbsp, &rbsp_stop_one_bit); + rbsp_bits(rbsp, round_up(rbsp->pos, 8) - rbsp->pos, + &rbsp_alignment_zero_bit); +} + +static void nal_h264_write_start_code_prefix(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i = 4; + + if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + p[0] = 0x00; + p[1] = 0x00; + p[2] = 0x00; + p[3] = 0x01; + + rbsp->pos += i * 8; +} + +static void nal_h264_read_start_code_prefix(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i = 4; + + if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x00 || p[3] != 0x01) { + rbsp->error = -EINVAL; + return; + } + + rbsp->pos += i * 8; +} + +static void nal_h264_write_filler_data(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i; + + /* Keep 1 byte extra for terminating the NAL unit */ + i = rbsp->size - DIV_ROUND_UP(rbsp->pos, 8) - 1; + memset(p, 0xff, i); + rbsp->pos += i * 8; +} + +static void nal_h264_read_filler_data(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + + while (*p == 0xff) { + if (DIV_ROUND_UP(rbsp->pos, 8) > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + p++; + rbsp->pos += 8; + } +} + +static void nal_h264_rbsp_hrd_parameters(struct rbsp *rbsp, + struct nal_h264_hrd_parameters *hrd) +{ + unsigned int i; + + if (!hrd) { + rbsp->error = -EINVAL; + return; + } + + rbsp_uev(rbsp, &hrd->cpb_cnt_minus1); + rbsp_bits(rbsp, 4, &hrd->bit_rate_scale); + rbsp_bits(rbsp, 4, &hrd->cpb_size_scale); + + for (i = 0; i <= hrd->cpb_cnt_minus1; i++) { + rbsp_uev(rbsp, &hrd->bit_rate_value_minus1[i]); + rbsp_uev(rbsp, &hrd->cpb_size_value_minus1[i]); + rbsp_bit(rbsp, &hrd->cbr_flag[i]); + } + + rbsp_bits(rbsp, 5, &hrd->initial_cpb_removal_delay_length_minus1); + rbsp_bits(rbsp, 5, &hrd->cpb_removal_delay_length_minus1); + rbsp_bits(rbsp, 5, &hrd->dpb_output_delay_length_minus1); + rbsp_bits(rbsp, 5, &hrd->time_offset_length); +} + +static void nal_h264_rbsp_vui_parameters(struct rbsp *rbsp, + struct nal_h264_vui_parameters *vui) +{ + if (!vui) { + rbsp->error = -EINVAL; + return; + } + + rbsp_bit(rbsp, &vui->aspect_ratio_info_present_flag); + if (vui->aspect_ratio_info_present_flag) { + rbsp_bits(rbsp, 8, &vui->aspect_ratio_idc); + if (vui->aspect_ratio_idc == 255) { + rbsp_bits(rbsp, 16, &vui->sar_width); + rbsp_bits(rbsp, 16, &vui->sar_height); + } + } + + rbsp_bit(rbsp, &vui->overscan_info_present_flag); + if (vui->overscan_info_present_flag) + rbsp_bit(rbsp, &vui->overscan_appropriate_flag); + + rbsp_bit(rbsp, &vui->video_signal_type_present_flag); + if (vui->video_signal_type_present_flag) { + rbsp_bits(rbsp, 3, &vui->video_format); + rbsp_bit(rbsp, &vui->video_full_range_flag); + + rbsp_bit(rbsp, &vui->colour_description_present_flag); + if (vui->colour_description_present_flag) { + rbsp_bits(rbsp, 8, &vui->colour_primaries); + rbsp_bits(rbsp, 8, &vui->transfer_characteristics); + rbsp_bits(rbsp, 8, &vui->matrix_coefficients); + } + } + + rbsp_bit(rbsp, &vui->chroma_loc_info_present_flag); + if (vui->chroma_loc_info_present_flag) { + rbsp_uev(rbsp, &vui->chroma_sample_loc_type_top_field); + rbsp_uev(rbsp, &vui->chroma_sample_loc_type_bottom_field); + } + + rbsp_bit(rbsp, &vui->timing_info_present_flag); + if (vui->timing_info_present_flag) { + rbsp_bits(rbsp, 32, &vui->num_units_in_tick); + rbsp_bits(rbsp, 32, &vui->time_scale); + rbsp_bit(rbsp, &vui->fixed_frame_rate_flag); + } + + rbsp_bit(rbsp, &vui->nal_hrd_parameters_present_flag); + if (vui->nal_hrd_parameters_present_flag) + nal_h264_rbsp_hrd_parameters(rbsp, &vui->nal_hrd_parameters); + + rbsp_bit(rbsp, &vui->vcl_hrd_parameters_present_flag); + if (vui->vcl_hrd_parameters_present_flag) + nal_h264_rbsp_hrd_parameters(rbsp, &vui->vcl_hrd_parameters); + + if (vui->nal_hrd_parameters_present_flag || + vui->vcl_hrd_parameters_present_flag) + rbsp_bit(rbsp, &vui->low_delay_hrd_flag); + + rbsp_bit(rbsp, &vui->pic_struct_present_flag); + + rbsp_bit(rbsp, &vui->bitstream_restriction_flag); + if (vui->bitstream_restriction_flag) { + rbsp_bit(rbsp, &vui->motion_vectors_over_pic_boundaries_flag); + rbsp_uev(rbsp, &vui->max_bytes_per_pic_denom); + rbsp_uev(rbsp, &vui->max_bits_per_mb_denom); + rbsp_uev(rbsp, &vui->log2_max_mv_length_horizontal); + rbsp_uev(rbsp, &vui->log21_max_mv_length_vertical); + rbsp_uev(rbsp, &vui->max_num_reorder_frames); + rbsp_uev(rbsp, &vui->max_dec_frame_buffering); + } +} + +static void nal_h264_rbsp_sps(struct rbsp *rbsp, struct nal_h264_sps *sps) +{ + unsigned int i; + + if (!sps) { + rbsp->error = -EINVAL; + return; + } + + rbsp_bits(rbsp, 8, &sps->profile_idc); + rbsp_bit(rbsp, &sps->constraint_set0_flag); + rbsp_bit(rbsp, &sps->constraint_set1_flag); + rbsp_bit(rbsp, &sps->constraint_set2_flag); + rbsp_bit(rbsp, &sps->constraint_set3_flag); + rbsp_bit(rbsp, &sps->constraint_set4_flag); + rbsp_bit(rbsp, &sps->constraint_set5_flag); + rbsp_bits(rbsp, 2, &sps->reserved_zero_2bits); + rbsp_bits(rbsp, 8, &sps->level_idc); + + rbsp_uev(rbsp, &sps->seq_parameter_set_id); + + if (sps->profile_idc == 100 || sps->profile_idc == 110 || + sps->profile_idc == 122 || sps->profile_idc == 244 || + sps->profile_idc == 44 || sps->profile_idc == 83 || + sps->profile_idc == 86 || sps->profile_idc == 118 || + sps->profile_idc == 128 || sps->profile_idc == 138 || + sps->profile_idc == 139 || sps->profile_idc == 134 || + sps->profile_idc == 135) { + rbsp_uev(rbsp, &sps->chroma_format_idc); + + if (sps->chroma_format_idc == 3) + rbsp_bit(rbsp, &sps->separate_colour_plane_flag); + rbsp_uev(rbsp, &sps->bit_depth_luma_minus8); + rbsp_uev(rbsp, &sps->bit_depth_chroma_minus8); + rbsp_bit(rbsp, &sps->qpprime_y_zero_transform_bypass_flag); + rbsp_bit(rbsp, &sps->seq_scaling_matrix_present_flag); + if (sps->seq_scaling_matrix_present_flag) + rbsp->error = -EINVAL; + } + + rbsp_uev(rbsp, &sps->log2_max_frame_num_minus4); + + rbsp_uev(rbsp, &sps->pic_order_cnt_type); + switch (sps->pic_order_cnt_type) { + case 0: + rbsp_uev(rbsp, &sps->log2_max_pic_order_cnt_lsb_minus4); + break; + case 1: + rbsp_bit(rbsp, &sps->delta_pic_order_always_zero_flag); + rbsp_sev(rbsp, &sps->offset_for_non_ref_pic); + rbsp_sev(rbsp, &sps->offset_for_top_to_bottom_field); + + rbsp_uev(rbsp, &sps->num_ref_frames_in_pic_order_cnt_cycle); + for (i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++) + rbsp_sev(rbsp, &sps->offset_for_ref_frame[i]); + break; + default: + rbsp->error = -EINVAL; + break; + } + + rbsp_uev(rbsp, &sps->max_num_ref_frames); + rbsp_bit(rbsp, &sps->gaps_in_frame_num_value_allowed_flag); + rbsp_uev(rbsp, &sps->pic_width_in_mbs_minus1); + rbsp_uev(rbsp, &sps->pic_height_in_map_units_minus1); + + rbsp_bit(rbsp, &sps->frame_mbs_only_flag); + if (!sps->frame_mbs_only_flag) + rbsp_bit(rbsp, &sps->mb_adaptive_frame_field_flag); + + rbsp_bit(rbsp, &sps->direct_8x8_inference_flag); + + rbsp_bit(rbsp, &sps->frame_cropping_flag); + if (sps->frame_cropping_flag) { + rbsp_uev(rbsp, &sps->crop_left); + rbsp_uev(rbsp, &sps->crop_right); + rbsp_uev(rbsp, &sps->crop_top); + rbsp_uev(rbsp, &sps->crop_bottom); + } + + rbsp_bit(rbsp, &sps->vui_parameters_present_flag); + if (sps->vui_parameters_present_flag) + nal_h264_rbsp_vui_parameters(rbsp, &sps->vui); +} + +static void nal_h264_rbsp_pps(struct rbsp *rbsp, struct nal_h264_pps *pps) +{ + int i; + + rbsp_uev(rbsp, &pps->pic_parameter_set_id); + rbsp_uev(rbsp, &pps->seq_parameter_set_id); + rbsp_bit(rbsp, &pps->entropy_coding_mode_flag); + rbsp_bit(rbsp, &pps->bottom_field_pic_order_in_frame_present_flag); + rbsp_uev(rbsp, &pps->num_slice_groups_minus1); + if (pps->num_slice_groups_minus1 > 0) { + rbsp_uev(rbsp, &pps->slice_group_map_type); + switch (pps->slice_group_map_type) { + case 0: + for (i = 0; i < pps->num_slice_groups_minus1; i++) + rbsp_uev(rbsp, &pps->run_length_minus1[i]); + break; + case 2: + for (i = 0; i < pps->num_slice_groups_minus1; i++) { + rbsp_uev(rbsp, &pps->top_left[i]); + rbsp_uev(rbsp, &pps->bottom_right[i]); + } + break; + case 3: case 4: case 5: + rbsp_bit(rbsp, &pps->slice_group_change_direction_flag); + rbsp_uev(rbsp, &pps->slice_group_change_rate_minus1); + break; + case 6: + rbsp_uev(rbsp, &pps->pic_size_in_map_units_minus1); + for (i = 0; i < pps->pic_size_in_map_units_minus1; i++) + rbsp_bits(rbsp, + order_base_2(pps->num_slice_groups_minus1 + 1), + &pps->slice_group_id[i]); + break; + default: + break; + } + } + rbsp_uev(rbsp, &pps->num_ref_idx_l0_default_active_minus1); + rbsp_uev(rbsp, &pps->num_ref_idx_l1_default_active_minus1); + rbsp_bit(rbsp, &pps->weighted_pred_flag); + rbsp_bits(rbsp, 2, &pps->weighted_bipred_idc); + rbsp_sev(rbsp, &pps->pic_init_qp_minus26); + rbsp_sev(rbsp, &pps->pic_init_qs_minus26); + rbsp_sev(rbsp, &pps->chroma_qp_index_offset); + rbsp_bit(rbsp, &pps->deblocking_filter_control_present_flag); + rbsp_bit(rbsp, &pps->constrained_intra_pred_flag); + rbsp_bit(rbsp, &pps->redundant_pic_cnt_present_flag); + if (/* more_rbsp_data() */ false) { + rbsp_bit(rbsp, &pps->transform_8x8_mode_flag); + rbsp_bit(rbsp, &pps->pic_scaling_matrix_present_flag); + if (pps->pic_scaling_matrix_present_flag) + rbsp->error = -EINVAL; + rbsp_sev(rbsp, &pps->second_chroma_qp_index_offset); + } +} + +/** + * nal_h264_write_sps() - Write SPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @sps: &struct nal_h264_sps to convert to RBSP + * + * Convert @sps to RBSP data and write it into @dest. + * + * The size of the SPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the SPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_h264_write_sps(const struct device *dev, + void *dest, size_t n, struct nal_h264_sps *sps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_ref_idc = 0; + unsigned int nal_unit_type = SEQUENCE_PARAMETER_SET; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_h264_write_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + nal_h264_rbsp_sps(&rbsp, sps); + + nal_h264_rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_write_sps); + +/** + * nal_h264_read_sps() - Read SPS NAL unit from RBSP format + * @dev: device pointer + * @sps: the &struct nal_h264_sps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @sps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_h264_read_sps(const struct device *dev, + struct nal_h264_sps *sps, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_ref_idc; + unsigned int nal_unit_type; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_h264_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + if (rbsp.error || + forbidden_zero_bit != 0 || + nal_ref_idc != 0 || + nal_unit_type != SEQUENCE_PARAMETER_SET) + return -EINVAL; + + nal_h264_rbsp_sps(&rbsp, sps); + + nal_h264_rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_read_sps); + +/** + * nal_h264_write_pps() - Write PPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @pps: &struct nal_h264_pps to convert to RBSP + * + * Convert @pps to RBSP data and write it into @dest. + * + * The size of the PPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the PPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_h264_write_pps(const struct device *dev, + void *dest, size_t n, struct nal_h264_pps *pps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_ref_idc = 0; + unsigned int nal_unit_type = PICTURE_PARAMETER_SET; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_h264_write_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + nal_h264_rbsp_pps(&rbsp, pps); + + nal_h264_rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_write_pps); + +/** + * nal_h264_read_pps() - Read PPS NAL unit from RBSP format + * @dev: device pointer + * @pps: the &struct nal_h264_pps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @pps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_h264_read_pps(const struct device *dev, + struct nal_h264_pps *pps, void *src, size_t n) +{ + struct rbsp rbsp; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_h264_read_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp.pos += 8; + + nal_h264_rbsp_pps(&rbsp, pps); + + nal_h264_rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_read_pps); + +/** + * nal_h264_write_filler() - Write filler data RBSP + * @dev: device pointer + * @dest: buffer to fill with filler data + * @n: size of the buffer to fill with filler data + * + * Write a filler data RBSP to @dest with a size of @n bytes and return the + * number of written filler data bytes. + * + * Use this function to generate dummy data in an RBSP data stream that can be + * safely ignored by h264 decoders. + * + * The RBSP format of the filler data is specified in Rec. ITU-T H.264 + * (04/2017) 7.3.2.7 Filler data RBSP syntax. + * + * Return: number of filler data bytes (including marker) or negative error + */ +ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_ref_idc = 0; + unsigned int nal_unit_type = FILLER_DATA; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_h264_write_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + nal_h264_write_filler_data(&rbsp); + + nal_h264_rbsp_trailing_bits(&rbsp); + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_write_filler); + +/** + * nal_h264_read_filler() - Read filler data RBSP + * @dev: device pointer + * @src: buffer with RBSP data that is read + * @n: maximum size of src that shall be read + * + * Read a filler data RBSP from @src up to a maximum size of @n bytes and + * return the size of the filler data in bytes including the marker. + * + * This function is used to parse filler data and skip the respective bytes in + * the RBSP data. + * + * The RBSP format of the filler data is specified in Rec. ITU-T H.264 + * (04/2017) 7.3.2.7 Filler data RBSP syntax. + * + * Return: number of filler data bytes (including marker) or negative error + */ +ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_ref_idc; + unsigned int nal_unit_type; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_h264_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + if (rbsp.error) + return rbsp.error; + if (forbidden_zero_bit != 0 || + nal_ref_idc != 0 || + nal_unit_type != FILLER_DATA) + return -EINVAL; + + nal_h264_read_filler_data(&rbsp); + nal_h264_rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_read_filler); diff --git a/drivers/media/platform/allegro-dvt/nal-h264.h b/drivers/media/platform/allegro-dvt/nal-h264.h new file mode 100644 index 000000000000..2ba7cbced7a5 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-h264.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + * + * Convert NAL units between raw byte sequence payloads (RBSP) and C structs. + */ + +#ifndef __NAL_H264_H__ +#define __NAL_H264_H__ + +#include +#include + +/** + * struct nal_h264_hdr_parameters - HDR parameters + * + * C struct representation of the sequence parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) E.1.2 HRD parameters syntax. + */ +struct nal_h264_hrd_parameters { + unsigned int cpb_cnt_minus1; + unsigned int bit_rate_scale; + unsigned int cpb_size_scale; + struct { + int bit_rate_value_minus1[16]; + int cpb_size_value_minus1[16]; + unsigned int cbr_flag[16]; + }; + unsigned int initial_cpb_removal_delay_length_minus1; + unsigned int cpb_removal_delay_length_minus1; + unsigned int dpb_output_delay_length_minus1; + unsigned int time_offset_length; +}; + +/** + * struct nal_h264_vui_parameters - VUI parameters + * + * C struct representation of the VUI parameters as defined by Rec. ITU-T + * H.264 (04/2017) E.1.1 VUI parameters syntax. + */ +struct nal_h264_vui_parameters { + unsigned int aspect_ratio_info_present_flag; + struct { + unsigned int aspect_ratio_idc; + unsigned int sar_width; + unsigned int sar_height; + }; + unsigned int overscan_info_present_flag; + unsigned int overscan_appropriate_flag; + unsigned int video_signal_type_present_flag; + struct { + unsigned int video_format; + unsigned int video_full_range_flag; + unsigned int colour_description_present_flag; + struct { + unsigned int colour_primaries; + unsigned int transfer_characteristics; + unsigned int matrix_coefficients; + }; + }; + unsigned int chroma_loc_info_present_flag; + struct { + unsigned int chroma_sample_loc_type_top_field; + unsigned int chroma_sample_loc_type_bottom_field; + }; + unsigned int timing_info_present_flag; + struct { + unsigned int num_units_in_tick; + unsigned int time_scale; + unsigned int fixed_frame_rate_flag; + }; + unsigned int nal_hrd_parameters_present_flag; + struct nal_h264_hrd_parameters nal_hrd_parameters; + unsigned int vcl_hrd_parameters_present_flag; + struct nal_h264_hrd_parameters vcl_hrd_parameters; + unsigned int low_delay_hrd_flag; + unsigned int pic_struct_present_flag; + unsigned int bitstream_restriction_flag; + struct { + unsigned int motion_vectors_over_pic_boundaries_flag; + unsigned int max_bytes_per_pic_denom; + unsigned int max_bits_per_mb_denom; + unsigned int log2_max_mv_length_horizontal; + unsigned int log21_max_mv_length_vertical; + unsigned int max_num_reorder_frames; + unsigned int max_dec_frame_buffering; + }; +}; + +/** + * struct nal_h264_sps - Sequence parameter set + * + * C struct representation of the sequence parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) 7.3.2.1.1 Sequence parameter set data syntax. + */ +struct nal_h264_sps { + unsigned int profile_idc; + unsigned int constraint_set0_flag; + unsigned int constraint_set1_flag; + unsigned int constraint_set2_flag; + unsigned int constraint_set3_flag; + unsigned int constraint_set4_flag; + unsigned int constraint_set5_flag; + unsigned int reserved_zero_2bits; + unsigned int level_idc; + unsigned int seq_parameter_set_id; + struct { + unsigned int chroma_format_idc; + unsigned int separate_colour_plane_flag; + unsigned int bit_depth_luma_minus8; + unsigned int bit_depth_chroma_minus8; + unsigned int qpprime_y_zero_transform_bypass_flag; + unsigned int seq_scaling_matrix_present_flag; + }; + unsigned int log2_max_frame_num_minus4; + unsigned int pic_order_cnt_type; + union { + unsigned int log2_max_pic_order_cnt_lsb_minus4; + struct { + unsigned int delta_pic_order_always_zero_flag; + int offset_for_non_ref_pic; + int offset_for_top_to_bottom_field; + unsigned int num_ref_frames_in_pic_order_cnt_cycle; + int offset_for_ref_frame[255]; + }; + }; + unsigned int max_num_ref_frames; + unsigned int gaps_in_frame_num_value_allowed_flag; + unsigned int pic_width_in_mbs_minus1; + unsigned int pic_height_in_map_units_minus1; + unsigned int frame_mbs_only_flag; + unsigned int mb_adaptive_frame_field_flag; + unsigned int direct_8x8_inference_flag; + unsigned int frame_cropping_flag; + struct { + unsigned int crop_left; + unsigned int crop_right; + unsigned int crop_top; + unsigned int crop_bottom; + }; + unsigned int vui_parameters_present_flag; + struct nal_h264_vui_parameters vui; +}; + +/** + * struct nal_h264_pps - Picture parameter set + * + * C struct representation of the picture parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) 7.3.2.2 Picture parameter set RBSP syntax. + */ +struct nal_h264_pps { + unsigned int pic_parameter_set_id; + unsigned int seq_parameter_set_id; + unsigned int entropy_coding_mode_flag; + unsigned int bottom_field_pic_order_in_frame_present_flag; + unsigned int num_slice_groups_minus1; + unsigned int slice_group_map_type; + union { + unsigned int run_length_minus1[8]; + struct { + unsigned int top_left[8]; + unsigned int bottom_right[8]; + }; + struct { + unsigned int slice_group_change_direction_flag; + unsigned int slice_group_change_rate_minus1; + }; + struct { + unsigned int pic_size_in_map_units_minus1; + unsigned int slice_group_id[8]; + }; + }; + unsigned int num_ref_idx_l0_default_active_minus1; + unsigned int num_ref_idx_l1_default_active_minus1; + unsigned int weighted_pred_flag; + unsigned int weighted_bipred_idc; + int pic_init_qp_minus26; + int pic_init_qs_minus26; + int chroma_qp_index_offset; + unsigned int deblocking_filter_control_present_flag; + unsigned int constrained_intra_pred_flag; + unsigned int redundant_pic_cnt_present_flag; + struct { + unsigned int transform_8x8_mode_flag; + unsigned int pic_scaling_matrix_present_flag; + int second_chroma_qp_index_offset; + }; +}; + +int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile); +int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level); + +ssize_t nal_h264_write_sps(const struct device *dev, + void *dest, size_t n, struct nal_h264_sps *sps); +ssize_t nal_h264_read_sps(const struct device *dev, + struct nal_h264_sps *sps, void *src, size_t n); +void nal_h264_print_sps(const struct device *dev, struct nal_h264_sps *sps); + +ssize_t nal_h264_write_pps(const struct device *dev, + void *dest, size_t n, struct nal_h264_pps *pps); +ssize_t nal_h264_read_pps(const struct device *dev, + struct nal_h264_pps *pps, void *src, size_t n); +void nal_h264_print_pps(const struct device *dev, struct nal_h264_pps *pps); + +ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n); +ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n); + +#endif /* __NAL_H264_H__ */ diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index e8996b1c3b35..ca59986b20f8 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -20,8 +20,6 @@ menuconfig STAGING_MEDIA if STAGING_MEDIA && MEDIA_SUPPORT # Please keep them in alphabetic order -source "drivers/staging/media/allegro-dvt/Kconfig" - source "drivers/staging/media/atomisp/Kconfig" source "drivers/staging/media/hantro/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 24b5873ff760..716929a1a313 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro-dvt/ obj-$(CONFIG_INTEL_ATOMISP) += atomisp/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/ diff --git a/drivers/staging/media/allegro-dvt/Kconfig b/drivers/staging/media/allegro-dvt/Kconfig deleted file mode 100644 index 6b7107d9995c..000000000000 --- a/drivers/staging/media/allegro-dvt/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config VIDEO_ALLEGRO_DVT - tristate "Allegro DVT Video IP Core" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_ZYNQMP || COMPILE_TEST - select V4L2_MEM2MEM_DEV - select VIDEOBUF2_DMA_CONTIG - select REGMAP - select REGMAP_MMIO - help - Support for the encoder video IP core by Allegro DVT. This core is - found for example on the Xilinx ZynqMP SoC in the EV family and is - called VCU in the reference manual. - - To compile this driver as a module, choose M here: the module - will be called allegro. diff --git a/drivers/staging/media/allegro-dvt/Makefile b/drivers/staging/media/allegro-dvt/Makefile deleted file mode 100644 index 8e306dcdc55c..000000000000 --- a/drivers/staging/media/allegro-dvt/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -allegro-objs := allegro-core.o nal-h264.o allegro-mail.o - -obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro.o diff --git a/drivers/staging/media/allegro-dvt/TODO b/drivers/staging/media/allegro-dvt/TODO deleted file mode 100644 index 99e19be0e45a..000000000000 --- a/drivers/staging/media/allegro-dvt/TODO +++ /dev/null @@ -1,4 +0,0 @@ -TODO: - -- This driver is waiting for the stateful encoder spec and corresponding - v4l2-compliance tests to be finalized. diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c deleted file mode 100644 index 640451134072..000000000000 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ /dev/null @@ -1,3226 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2019 Pengutronix, Michael Tretter - * - * Allegro DVT video encoder driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "allegro-mail.h" -#include "nal-h264.h" - -/* - * Support up to 4k video streams. The hardware actually supports higher - * resolutions, which are specified in PG252 June 6, 2018 (H.264/H.265 Video - * Codec Unit v1.1) Chapter 3. - */ -#define ALLEGRO_WIDTH_MIN 128 -#define ALLEGRO_WIDTH_DEFAULT 1920 -#define ALLEGRO_WIDTH_MAX 3840 -#define ALLEGRO_HEIGHT_MIN 64 -#define ALLEGRO_HEIGHT_DEFAULT 1080 -#define ALLEGRO_HEIGHT_MAX 2160 - -#define ALLEGRO_FRAMERATE_DEFAULT ((struct v4l2_fract) { 30, 1 }) - -#define ALLEGRO_GOP_SIZE_DEFAULT 25 -#define ALLEGRO_GOP_SIZE_MAX 1000 - -/* - * MCU Control Registers - * - * The Zynq UltraScale+ Devices Register Reference documents the registers - * with an offset of 0x9000, which equals the size of the SRAM and one page - * gap. The driver handles SRAM and registers separately and, therefore, is - * oblivious of the offset. - */ -#define AL5_MCU_RESET 0x0000 -#define AL5_MCU_RESET_SOFT BIT(0) -#define AL5_MCU_RESET_REGS BIT(1) -#define AL5_MCU_RESET_MODE 0x0004 -#define AL5_MCU_RESET_MODE_SLEEP BIT(0) -#define AL5_MCU_RESET_MODE_HALT BIT(1) -#define AL5_MCU_STA 0x0008 -#define AL5_MCU_STA_SLEEP BIT(0) -#define AL5_MCU_WAKEUP 0x000c - -#define AL5_ICACHE_ADDR_OFFSET_MSB 0x0010 -#define AL5_ICACHE_ADDR_OFFSET_LSB 0x0014 -#define AL5_DCACHE_ADDR_OFFSET_MSB 0x0018 -#define AL5_DCACHE_ADDR_OFFSET_LSB 0x001c - -#define AL5_MCU_INTERRUPT 0x0100 -#define AL5_ITC_CPU_IRQ_MSK 0x0104 -#define AL5_ITC_CPU_IRQ_CLR 0x0108 -#define AL5_ITC_CPU_IRQ_STA 0x010C -#define AL5_ITC_CPU_IRQ_STA_TRIGGERED BIT(0) - -#define AXI_ADDR_OFFSET_IP 0x0208 - -/* - * The MCU accesses the system memory with a 2G offset compared to CPU - * physical addresses. - */ -#define MCU_CACHE_OFFSET SZ_2G - -/* - * The driver needs to reserve some space at the beginning of capture buffers, - * because it needs to write SPS/PPS NAL units. The encoder writes the actual - * frame data after the offset. - */ -#define ENCODER_STREAM_OFFSET SZ_64 - -#define SIZE_MACROBLOCK 16 - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-2)"); - -struct allegro_buffer { - void *vaddr; - dma_addr_t paddr; - size_t size; - struct list_head head; -}; - -struct allegro_dev; -struct allegro_channel; - -struct allegro_mbox { - struct allegro_dev *dev; - unsigned int head; - unsigned int tail; - unsigned int data; - size_t size; - /* protect mailbox from simultaneous accesses */ - struct mutex lock; -}; - -struct allegro_dev { - struct v4l2_device v4l2_dev; - struct video_device video_dev; - struct v4l2_m2m_dev *m2m_dev; - struct platform_device *plat_dev; - - /* mutex protecting vb2_queue structure */ - struct mutex lock; - - struct regmap *regmap; - struct regmap *sram; - - const struct fw_info *fw_info; - struct allegro_buffer firmware; - struct allegro_buffer suballocator; - - struct completion init_complete; - - /* The mailbox interface */ - struct allegro_mbox *mbox_command; - struct allegro_mbox *mbox_status; - - /* - * The downstream driver limits the users to 64 users, thus I can use - * a bitfield for the user_ids that are in use. See also user_id in - * struct allegro_channel. - */ - unsigned long channel_user_ids; - struct list_head channels; -}; - -static struct regmap_config allegro_regmap_config = { - .name = "regmap", - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = 0xfff, - .cache_type = REGCACHE_NONE, -}; - -static struct regmap_config allegro_sram_config = { - .name = "sram", - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = 0x7fff, - .cache_type = REGCACHE_NONE, -}; - -enum allegro_state { - ALLEGRO_STATE_ENCODING, - ALLEGRO_STATE_DRAIN, - ALLEGRO_STATE_WAIT_FOR_BUFFER, - ALLEGRO_STATE_STOPPED, -}; - -#define fh_to_channel(__fh) container_of(__fh, struct allegro_channel, fh) - -struct allegro_channel { - struct allegro_dev *dev; - struct v4l2_fh fh; - struct v4l2_ctrl_handler ctrl_handler; - - unsigned int width; - unsigned int height; - unsigned int stride; - struct v4l2_fract framerate; - - enum v4l2_colorspace colorspace; - enum v4l2_ycbcr_encoding ycbcr_enc; - enum v4l2_quantization quantization; - enum v4l2_xfer_func xfer_func; - - u32 pixelformat; - unsigned int sizeimage_raw; - unsigned int osequence; - - u32 codec; - enum v4l2_mpeg_video_h264_profile profile; - enum v4l2_mpeg_video_h264_level level; - unsigned int sizeimage_encoded; - unsigned int csequence; - - bool frame_rc_enable; - unsigned int bitrate; - unsigned int bitrate_peak; - unsigned int cpb_size; - unsigned int gop_size; - - struct allegro_buffer config_blob; - - unsigned int num_ref_idx_l0; - unsigned int num_ref_idx_l1; - - struct v4l2_ctrl *mpeg_video_h264_profile; - struct v4l2_ctrl *mpeg_video_h264_level; - struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; - struct v4l2_ctrl *mpeg_video_h264_max_qp; - struct v4l2_ctrl *mpeg_video_h264_min_qp; - struct v4l2_ctrl *mpeg_video_h264_p_frame_qp; - struct v4l2_ctrl *mpeg_video_h264_b_frame_qp; - struct v4l2_ctrl *mpeg_video_frame_rc_enable; - struct { /* video bitrate mode control cluster */ - struct v4l2_ctrl *mpeg_video_bitrate_mode; - struct v4l2_ctrl *mpeg_video_bitrate; - struct v4l2_ctrl *mpeg_video_bitrate_peak; - }; - struct v4l2_ctrl *mpeg_video_cpb_size; - struct v4l2_ctrl *mpeg_video_gop_size; - - /* user_id is used to identify the channel during CREATE_CHANNEL */ - /* not sure, what to set here and if this is actually required */ - int user_id; - /* channel_id is set by the mcu and used by all later commands */ - int mcu_channel_id; - - struct list_head buffers_reference; - struct list_head buffers_intermediate; - - struct list_head source_shadow_list; - struct list_head stream_shadow_list; - /* protect shadow lists of buffers passed to firmware */ - struct mutex shadow_list_lock; - - struct list_head list; - struct completion completion; - - unsigned int error; - enum allegro_state state; -}; - -static inline int -allegro_set_state(struct allegro_channel *channel, enum allegro_state state) -{ - channel->state = state; - - return 0; -} - -static inline enum allegro_state -allegro_get_state(struct allegro_channel *channel) -{ - return channel->state; -} - -struct allegro_m2m_buffer { - struct v4l2_m2m_buffer buf; - struct list_head head; -}; - -#define to_allegro_m2m_buffer(__buf) \ - container_of(__buf, struct allegro_m2m_buffer, buf) - -struct fw_info { - unsigned int id; - unsigned int id_codec; - char *version; - unsigned int mailbox_cmd; - unsigned int mailbox_status; - size_t mailbox_size; - enum mcu_msg_version mailbox_version; - size_t suballocator_size; -}; - -static const struct fw_info supported_firmware[] = { - { - .id = 18296, - .id_codec = 96272, - .version = "v2018.2", - .mailbox_cmd = 0x7800, - .mailbox_status = 0x7c00, - .mailbox_size = 0x400 - 0x8, - .mailbox_version = MCU_MSG_VERSION_2018_2, - .suballocator_size = SZ_16M, - }, { - .id = 14680, - .id_codec = 126572, - .version = "v2019.2", - .mailbox_cmd = 0x7000, - .mailbox_status = 0x7800, - .mailbox_size = 0x800 - 0x8, - .mailbox_version = MCU_MSG_VERSION_2019_2, - .suballocator_size = SZ_32M, - }, -}; - -static inline u32 to_mcu_addr(struct allegro_dev *dev, dma_addr_t phys) -{ - if (upper_32_bits(phys) || (lower_32_bits(phys) & MCU_CACHE_OFFSET)) - v4l2_warn(&dev->v4l2_dev, - "address %pad is outside mcu window\n", &phys); - - return lower_32_bits(phys) | MCU_CACHE_OFFSET; -} - -static inline u32 to_mcu_size(struct allegro_dev *dev, size_t size) -{ - return lower_32_bits(size); -} - -static inline u32 to_codec_addr(struct allegro_dev *dev, dma_addr_t phys) -{ - if (upper_32_bits(phys)) - v4l2_warn(&dev->v4l2_dev, - "address %pad cannot be used by codec\n", &phys); - - return lower_32_bits(phys); -} - -static inline u64 ptr_to_u64(const void *ptr) -{ - return (uintptr_t)ptr; -} - -/* Helper functions for channel and user operations */ - -static unsigned long allegro_next_user_id(struct allegro_dev *dev) -{ - if (dev->channel_user_ids == ~0UL) - return -EBUSY; - - return ffz(dev->channel_user_ids); -} - -static struct allegro_channel * -allegro_find_channel_by_user_id(struct allegro_dev *dev, - unsigned int user_id) -{ - struct allegro_channel *channel; - - list_for_each_entry(channel, &dev->channels, list) { - if (channel->user_id == user_id) - return channel; - } - - return ERR_PTR(-EINVAL); -} - -static struct allegro_channel * -allegro_find_channel_by_channel_id(struct allegro_dev *dev, - unsigned int channel_id) -{ - struct allegro_channel *channel; - - list_for_each_entry(channel, &dev->channels, list) { - if (channel->mcu_channel_id == channel_id) - return channel; - } - - return ERR_PTR(-EINVAL); -} - -static inline bool channel_exists(struct allegro_channel *channel) -{ - return channel->mcu_channel_id != -1; -} - -#define AL_ERROR 0x80 -#define AL_ERR_INIT_FAILED 0x81 -#define AL_ERR_NO_FRAME_DECODED 0x82 -#define AL_ERR_RESOLUTION_CHANGE 0x85 -#define AL_ERR_NO_MEMORY 0x87 -#define AL_ERR_STREAM_OVERFLOW 0x88 -#define AL_ERR_TOO_MANY_SLICES 0x89 -#define AL_ERR_BUF_NOT_READY 0x8c -#define AL_ERR_NO_CHANNEL_AVAILABLE 0x8d -#define AL_ERR_RESOURCE_UNAVAILABLE 0x8e -#define AL_ERR_NOT_ENOUGH_CORES 0x8f -#define AL_ERR_REQUEST_MALFORMED 0x90 -#define AL_ERR_CMD_NOT_ALLOWED 0x91 -#define AL_ERR_INVALID_CMD_VALUE 0x92 - -static inline const char *allegro_err_to_string(unsigned int err) -{ - switch (err) { - case AL_ERR_INIT_FAILED: - return "initialization failed"; - case AL_ERR_NO_FRAME_DECODED: - return "no frame decoded"; - case AL_ERR_RESOLUTION_CHANGE: - return "resolution change"; - case AL_ERR_NO_MEMORY: - return "out of memory"; - case AL_ERR_STREAM_OVERFLOW: - return "stream buffer overflow"; - case AL_ERR_TOO_MANY_SLICES: - return "too many slices"; - case AL_ERR_BUF_NOT_READY: - return "buffer not ready"; - case AL_ERR_NO_CHANNEL_AVAILABLE: - return "no channel available"; - case AL_ERR_RESOURCE_UNAVAILABLE: - return "resource unavailable"; - case AL_ERR_NOT_ENOUGH_CORES: - return "not enough cores"; - case AL_ERR_REQUEST_MALFORMED: - return "request malformed"; - case AL_ERR_CMD_NOT_ALLOWED: - return "command not allowed"; - case AL_ERR_INVALID_CMD_VALUE: - return "invalid command value"; - case AL_ERROR: - default: - return "unknown error"; - } -} - -static unsigned int estimate_stream_size(unsigned int width, - unsigned int height) -{ - unsigned int offset = ENCODER_STREAM_OFFSET; - unsigned int num_blocks = DIV_ROUND_UP(width, SIZE_MACROBLOCK) * - DIV_ROUND_UP(height, SIZE_MACROBLOCK); - unsigned int pcm_size = SZ_256; - unsigned int partition_table = SZ_256; - - return round_up(offset + num_blocks * pcm_size + partition_table, 32); -} - -static enum v4l2_mpeg_video_h264_level -select_minimum_h264_level(unsigned int width, unsigned int height) -{ - unsigned int pic_width_in_mb = DIV_ROUND_UP(width, SIZE_MACROBLOCK); - unsigned int frame_height_in_mb = DIV_ROUND_UP(height, SIZE_MACROBLOCK); - unsigned int frame_size_in_mb = pic_width_in_mb * frame_height_in_mb; - enum v4l2_mpeg_video_h264_level level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; - - /* - * The level limits are specified in Rec. ITU-T H.264 Annex A.3.1 and - * also specify limits regarding bit rate and CBP size. Only approximate - * the levels using the frame size. - * - * Level 5.1 allows up to 4k video resolution. - */ - if (frame_size_in_mb <= 99) - level = V4L2_MPEG_VIDEO_H264_LEVEL_1_0; - else if (frame_size_in_mb <= 396) - level = V4L2_MPEG_VIDEO_H264_LEVEL_1_1; - else if (frame_size_in_mb <= 792) - level = V4L2_MPEG_VIDEO_H264_LEVEL_2_1; - else if (frame_size_in_mb <= 1620) - level = V4L2_MPEG_VIDEO_H264_LEVEL_2_2; - else if (frame_size_in_mb <= 3600) - level = V4L2_MPEG_VIDEO_H264_LEVEL_3_1; - else if (frame_size_in_mb <= 5120) - level = V4L2_MPEG_VIDEO_H264_LEVEL_3_2; - else if (frame_size_in_mb <= 8192) - level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; - else if (frame_size_in_mb <= 8704) - level = V4L2_MPEG_VIDEO_H264_LEVEL_4_2; - else if (frame_size_in_mb <= 22080) - level = V4L2_MPEG_VIDEO_H264_LEVEL_5_0; - else - level = V4L2_MPEG_VIDEO_H264_LEVEL_5_1; - - return level; -} - -static unsigned int maximum_bitrate(enum v4l2_mpeg_video_h264_level level) -{ - switch (level) { - case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: - return 64000; - case V4L2_MPEG_VIDEO_H264_LEVEL_1B: - return 128000; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: - return 192000; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: - return 384000; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: - return 768000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: - return 2000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: - return 4000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: - return 4000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: - return 10000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: - return 14000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: - return 20000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: - return 20000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: - return 50000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: - return 50000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: - return 135000000; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: - default: - return 240000000; - } -} - -static unsigned int maximum_cpb_size(enum v4l2_mpeg_video_h264_level level) -{ - switch (level) { - case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: - return 175; - case V4L2_MPEG_VIDEO_H264_LEVEL_1B: - return 350; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: - return 500; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: - return 1000; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: - return 2000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: - return 2000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: - return 4000; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: - return 4000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: - return 10000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: - return 14000; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: - return 20000; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: - return 25000; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: - return 62500; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: - return 62500; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: - return 135000; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: - default: - return 240000; - } -} - -static const struct fw_info * -allegro_get_firmware_info(struct allegro_dev *dev, - const struct firmware *fw, - const struct firmware *fw_codec) -{ - int i; - unsigned int id = fw->size; - unsigned int id_codec = fw_codec->size; - - for (i = 0; i < ARRAY_SIZE(supported_firmware); i++) - if (supported_firmware[i].id == id && - supported_firmware[i].id_codec == id_codec) - return &supported_firmware[i]; - - return NULL; -} - -/* - * Buffers that are used internally by the MCU. - */ - -static int allegro_alloc_buffer(struct allegro_dev *dev, - struct allegro_buffer *buffer, size_t size) -{ - buffer->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, - &buffer->paddr, GFP_KERNEL); - if (!buffer->vaddr) - return -ENOMEM; - buffer->size = size; - - return 0; -} - -static void allegro_free_buffer(struct allegro_dev *dev, - struct allegro_buffer *buffer) -{ - if (buffer->vaddr) { - dma_free_coherent(&dev->plat_dev->dev, buffer->size, - buffer->vaddr, buffer->paddr); - buffer->vaddr = NULL; - buffer->size = 0; - } -} - -/* - * Mailbox interface to send messages to the MCU. - */ - -static void allegro_mcu_interrupt(struct allegro_dev *dev); -static void allegro_handle_message(struct allegro_dev *dev, - union mcu_msg_response *msg); - -static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, - unsigned int base, size_t size) -{ - struct allegro_mbox *mbox; - - mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL); - if (!mbox) - return ERR_PTR(-ENOMEM); - - mbox->dev = dev; - - mbox->head = base; - mbox->tail = base + 0x4; - mbox->data = base + 0x8; - mbox->size = size; - mutex_init(&mbox->lock); - - regmap_write(dev->sram, mbox->head, 0); - regmap_write(dev->sram, mbox->tail, 0); - - return mbox; -} - -static int allegro_mbox_write(struct allegro_mbox *mbox, - const u32 *src, size_t size) -{ - struct regmap *sram = mbox->dev->sram; - unsigned int tail; - size_t size_no_wrap; - int err = 0; - int stride = regmap_get_reg_stride(sram); - - if (!src) - return -EINVAL; - - if (size > mbox->size) - return -EINVAL; - - mutex_lock(&mbox->lock); - regmap_read(sram, mbox->tail, &tail); - if (tail > mbox->size) { - err = -EIO; - goto out; - } - size_no_wrap = min(size, mbox->size - (size_t)tail); - regmap_bulk_write(sram, mbox->data + tail, - src, size_no_wrap / stride); - regmap_bulk_write(sram, mbox->data, - src + (size_no_wrap / sizeof(*src)), - (size - size_no_wrap) / stride); - regmap_write(sram, mbox->tail, (tail + size) % mbox->size); - -out: - mutex_unlock(&mbox->lock); - - return err; -} - -static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, - u32 *dst, size_t nbyte) -{ - struct { - u16 length; - u16 type; - } __attribute__ ((__packed__)) *header; - struct regmap *sram = mbox->dev->sram; - unsigned int head; - ssize_t size; - size_t body_no_wrap; - int stride = regmap_get_reg_stride(sram); - - regmap_read(sram, mbox->head, &head); - if (head > mbox->size) - return -EIO; - - /* Assume that the header does not wrap. */ - regmap_bulk_read(sram, mbox->data + head, - dst, sizeof(*header) / stride); - header = (void *)dst; - size = header->length + sizeof(*header); - if (size > mbox->size || size & 0x3) - return -EIO; - if (size > nbyte) - return -EINVAL; - - /* - * The message might wrap within the mailbox. If the message does not - * wrap, the first read will read the entire message, otherwise the - * first read will read message until the end of the mailbox and the - * second read will read the remaining bytes from the beginning of the - * mailbox. - * - * Skip the header, as was already read to get the size of the body. - */ - body_no_wrap = min((size_t)header->length, - (size_t)(mbox->size - (head + sizeof(*header)))); - regmap_bulk_read(sram, mbox->data + head + sizeof(*header), - dst + (sizeof(*header) / sizeof(*dst)), - body_no_wrap / stride); - regmap_bulk_read(sram, mbox->data, - dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst), - (header->length - body_no_wrap) / stride); - - regmap_write(sram, mbox->head, (head + size) % mbox->size); - - return size; -} - -/** - * allegro_mbox_send() - Send a message via the mailbox - * @mbox: the mailbox which is used to send the message - * @msg: the message to send - */ -static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) -{ - struct allegro_dev *dev = mbox->dev; - ssize_t size; - int err; - u32 *tmp; - - tmp = kzalloc(mbox->size, GFP_KERNEL); - if (!tmp) { - err = -ENOMEM; - goto out; - } - - size = allegro_encode_mail(tmp, msg); - - err = allegro_mbox_write(mbox, tmp, size); - kfree(tmp); - if (err) - goto out; - - allegro_mcu_interrupt(dev); - -out: - return err; -} - -/** - * allegro_mbox_notify() - Notify the mailbox about a new message - * @mbox: The allegro_mbox to notify - */ -static void allegro_mbox_notify(struct allegro_mbox *mbox) -{ - struct allegro_dev *dev = mbox->dev; - union mcu_msg_response *msg; - ssize_t size; - u32 *tmp; - int err; - - msg = kmalloc(sizeof(*msg), GFP_KERNEL); - if (!msg) - return; - - msg->header.version = dev->fw_info->mailbox_version; - - tmp = kmalloc(mbox->size, GFP_KERNEL); - if (!tmp) - goto out; - - size = allegro_mbox_read(mbox, tmp, mbox->size); - if (size < 0) - goto out; - - err = allegro_decode_mail(msg, tmp); - if (err) - goto out; - - allegro_handle_message(dev, msg); - -out: - kfree(tmp); - kfree(msg); -} - -static void allegro_mcu_send_init(struct allegro_dev *dev, - dma_addr_t suballoc_dma, size_t suballoc_size) -{ - struct mcu_msg_init_request msg; - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_INIT; - msg.header.version = dev->fw_info->mailbox_version; - - msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); - msg.suballoc_size = to_mcu_size(dev, suballoc_size); - - /* disable L2 cache */ - msg.l2_cache[0] = -1; - msg.l2_cache[1] = -1; - msg.l2_cache[2] = -1; - - allegro_mbox_send(dev->mbox_command, &msg); -} - -static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat) -{ - switch (pixelformat) { - case V4L2_PIX_FMT_NV12: - /* AL_420_8BITS: 0x100 -> NV12, 0x88 -> 8 bit */ - return 0x100 | 0x88; - default: - return -EINVAL; - } -} - -static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace) -{ - switch (colorspace) { - case V4L2_COLORSPACE_REC709: - return 2; - case V4L2_COLORSPACE_SMPTE170M: - return 3; - case V4L2_COLORSPACE_SMPTE240M: - return 4; - case V4L2_COLORSPACE_SRGB: - return 7; - default: - /* UNKNOWN */ - return 0; - } -} - -static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile) -{ - switch (profile) { - case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: - default: - return 66; - } -} - -static u16 v4l2_level_to_mcu_level(enum v4l2_mpeg_video_h264_level level) -{ - switch (level) { - case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: - return 10; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: - return 11; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: - return 12; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: - return 13; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: - return 20; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: - return 21; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: - return 22; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: - return 30; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: - return 31; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: - return 32; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: - return 40; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: - return 41; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: - return 42; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: - return 50; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: - default: - return 51; - } -} - -static u32 -v4l2_bitrate_mode_to_mcu_mode(enum v4l2_mpeg_video_bitrate_mode mode) -{ - switch (mode) { - case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: - return 2; - case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: - default: - return 1; - } -} - -static u32 v4l2_cpb_size_to_mcu(unsigned int cpb_size, unsigned int bitrate) -{ - unsigned int cpb_size_kbit; - unsigned int bitrate_kbps; - - /* - * The mcu expects the CPB size in units of a 90 kHz clock, but the - * channel follows the V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE and stores - * the CPB size in kilobytes. - */ - cpb_size_kbit = cpb_size * BITS_PER_BYTE; - bitrate_kbps = bitrate / 1000; - - return (cpb_size_kbit * 90000) / bitrate_kbps; -} - -static s16 get_qp_delta(int minuend, int subtrahend) -{ - if (minuend == subtrahend) - return -1; - else - return minuend - subtrahend; -} - -static int fill_create_channel_param(struct allegro_channel *channel, - struct create_channel_param *param) -{ - int i_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); - int p_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); - int b_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); - int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode); - - param->width = channel->width; - param->height = channel->height; - param->format = v4l2_pixelformat_to_mcu_format(channel->pixelformat); - param->colorspace = - v4l2_colorspace_to_mcu_colorspace(channel->colorspace); - param->src_mode = 0x0; - param->profile = v4l2_profile_to_mcu_profile(channel->profile); - param->constraint_set_flags = BIT(1); - param->codec = channel->codec; - param->level = v4l2_level_to_mcu_level(channel->level); - param->tier = 0; - - param->log2_max_poc = 10; - param->log2_max_frame_num = 4; - param->temporal_mvp_enable = 1; - - param->dbf_ovr_en = 1; - param->rdo_cost_mode = 1; - param->custom_lda = 1; - param->lf = 1; - param->lf_x_tile = 1; - param->lf_x_slice = 1; - - param->src_bit_depth = 8; - - param->beta_offset = -1; - param->tc_offset = -1; - param->num_slices = 1; - param->me_range[0] = 8; - param->me_range[1] = 8; - param->me_range[2] = 16; - param->me_range[3] = 16; - param->max_cu_size = ilog2(SIZE_MACROBLOCK); - param->min_cu_size = ilog2(8); - param->max_tu_size = 2; - param->min_tu_size = 2; - param->max_transfo_depth_intra = 1; - param->max_transfo_depth_inter = 1; - - param->prefetch_auto = 0; - param->prefetch_mem_offset = 0; - param->prefetch_mem_size = 0; - - param->rate_control_mode = channel->frame_rc_enable ? - v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0; - - param->cpb_size = v4l2_cpb_size_to_mcu(channel->cpb_size, - channel->bitrate_peak); - /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */ - param->initial_rem_delay = param->cpb_size; - param->framerate = DIV_ROUND_UP(channel->framerate.numerator, - channel->framerate.denominator); - param->clk_ratio = channel->framerate.denominator == 1001 ? 1001 : 1000; - param->target_bitrate = channel->bitrate; - param->max_bitrate = channel->bitrate_peak; - param->initial_qp = i_frame_qp; - param->min_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); - param->max_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); - param->ip_delta = get_qp_delta(i_frame_qp, p_frame_qp); - param->pb_delta = get_qp_delta(p_frame_qp, b_frame_qp); - param->golden_ref = 0; - param->golden_delta = 2; - param->golden_ref_frequency = 10; - param->rate_control_option = 0x00000000; - - param->num_pixel = channel->width + channel->height; - param->max_psnr = 4200; - param->max_pixel_value = 255; - - param->gop_ctrl_mode = 0x00000002; - param->freq_idr = channel->gop_size; - param->freq_lt = 0; - param->gdr_mode = 0x00000000; - param->gop_length = channel->gop_size; - param->subframe_latency = 0x00000000; - - param->lda_factors[0] = 51; - param->lda_factors[1] = 90; - param->lda_factors[2] = 151; - param->lda_factors[3] = 151; - param->lda_factors[4] = 151; - param->lda_factors[5] = 151; - - param->max_num_merge_cand = 5; - - return 0; -} - -static int allegro_mcu_send_create_channel(struct allegro_dev *dev, - struct allegro_channel *channel) -{ - struct mcu_msg_create_channel msg; - struct allegro_buffer *blob = &channel->config_blob; - struct create_channel_param param; - size_t size; - - memset(¶m, 0, sizeof(param)); - fill_create_channel_param(channel, ¶m); - allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param)); - param.version = dev->fw_info->mailbox_version; - size = allegro_encode_config_blob(blob->vaddr, ¶m); - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL; - msg.header.version = dev->fw_info->mailbox_version; - - msg.user_id = channel->user_id; - - msg.blob = blob->vaddr; - msg.blob_size = size; - msg.blob_mcu_addr = to_mcu_addr(dev, blob->paddr); - - allegro_mbox_send(dev->mbox_command, &msg); - - return 0; -} - -static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, - struct allegro_channel *channel) -{ - struct mcu_msg_destroy_channel msg; - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL; - msg.header.version = dev->fw_info->mailbox_version; - - msg.channel_id = channel->mcu_channel_id; - - allegro_mbox_send(dev->mbox_command, &msg); - - return 0; -} - -static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, - struct allegro_channel *channel, - dma_addr_t paddr, - unsigned long size, - u64 stream_id) -{ - struct mcu_msg_put_stream_buffer msg; - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER; - msg.header.version = dev->fw_info->mailbox_version; - - msg.channel_id = channel->mcu_channel_id; - msg.dma_addr = to_codec_addr(dev, paddr); - msg.mcu_addr = to_mcu_addr(dev, paddr); - msg.size = size; - msg.offset = ENCODER_STREAM_OFFSET; - /* copied to mcu_msg_encode_frame_response */ - msg.stream_id = stream_id; - - allegro_mbox_send(dev->mbox_command, &msg); - - return 0; -} - -static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, - struct allegro_channel *channel, - dma_addr_t src_y, dma_addr_t src_uv, - u64 src_handle) -{ - struct mcu_msg_encode_frame msg; - - memset(&msg, 0, sizeof(msg)); - - msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME; - msg.header.version = dev->fw_info->mailbox_version; - - msg.channel_id = channel->mcu_channel_id; - msg.encoding_options = AL_OPT_FORCE_LOAD; - msg.pps_qp = 26; /* qp are relative to 26 */ - msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */ - /* src_handle is copied to mcu_msg_encode_frame_response */ - msg.src_handle = src_handle; - msg.src_y = to_codec_addr(dev, src_y); - msg.src_uv = to_codec_addr(dev, src_uv); - msg.stride = channel->stride; - msg.ep2 = 0x0; - msg.ep2_v = to_mcu_addr(dev, msg.ep2); - - allegro_mbox_send(dev->mbox_command, &msg); - - return 0; -} - -static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev, - unsigned long timeout_ms) -{ - unsigned long tmo; - - tmo = wait_for_completion_timeout(&dev->init_complete, - msecs_to_jiffies(timeout_ms)); - if (tmo == 0) - return -ETIMEDOUT; - - reinit_completion(&dev->init_complete); - return 0; -} - -static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, - enum mcu_msg_type type) -{ - struct allegro_dev *dev = channel->dev; - struct mcu_msg_push_buffers_internal *msg; - struct mcu_msg_push_buffers_internal_buffer *buffer; - unsigned int num_buffers = 0; - size_t size; - struct allegro_buffer *al_buffer; - struct list_head *list; - int err; - - switch (type) { - case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: - list = &channel->buffers_reference; - break; - case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: - list = &channel->buffers_intermediate; - break; - default: - return -EINVAL; - } - - list_for_each_entry(al_buffer, list, head) - num_buffers++; - size = struct_size(msg, buffer, num_buffers); - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - msg->header.type = type; - msg->header.version = dev->fw_info->mailbox_version; - - msg->channel_id = channel->mcu_channel_id; - msg->num_buffers = num_buffers; - - buffer = msg->buffer; - list_for_each_entry(al_buffer, list, head) { - buffer->dma_addr = to_codec_addr(dev, al_buffer->paddr); - buffer->mcu_addr = to_mcu_addr(dev, al_buffer->paddr); - buffer->size = to_mcu_size(dev, al_buffer->size); - buffer++; - } - - err = allegro_mbox_send(dev->mbox_command, msg); - - kfree(msg); - return err; -} - -static int allegro_mcu_push_buffer_intermediate(struct allegro_channel *channel) -{ - enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE; - - return allegro_mcu_push_buffer_internal(channel, type); -} - -static int allegro_mcu_push_buffer_reference(struct allegro_channel *channel) -{ - enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE; - - return allegro_mcu_push_buffer_internal(channel, type); -} - -static int allocate_buffers_internal(struct allegro_channel *channel, - struct list_head *list, - size_t n, size_t size) -{ - struct allegro_dev *dev = channel->dev; - unsigned int i; - int err; - struct allegro_buffer *buffer, *tmp; - - for (i = 0; i < n; i++) { - buffer = kmalloc(sizeof(*buffer), GFP_KERNEL); - if (!buffer) { - err = -ENOMEM; - goto err; - } - INIT_LIST_HEAD(&buffer->head); - - err = allegro_alloc_buffer(dev, buffer, size); - if (err) - goto err; - list_add(&buffer->head, list); - } - - return 0; - -err: - list_for_each_entry_safe(buffer, tmp, list, head) { - list_del(&buffer->head); - allegro_free_buffer(dev, buffer); - kfree(buffer); - } - return err; -} - -static void destroy_buffers_internal(struct allegro_channel *channel, - struct list_head *list) -{ - struct allegro_dev *dev = channel->dev; - struct allegro_buffer *buffer, *tmp; - - list_for_each_entry_safe(buffer, tmp, list, head) { - list_del(&buffer->head); - allegro_free_buffer(dev, buffer); - kfree(buffer); - } -} - -static void destroy_reference_buffers(struct allegro_channel *channel) -{ - return destroy_buffers_internal(channel, &channel->buffers_reference); -} - -static void destroy_intermediate_buffers(struct allegro_channel *channel) -{ - return destroy_buffers_internal(channel, - &channel->buffers_intermediate); -} - -static int allocate_intermediate_buffers(struct allegro_channel *channel, - size_t n, size_t size) -{ - return allocate_buffers_internal(channel, - &channel->buffers_intermediate, - n, size); -} - -static int allocate_reference_buffers(struct allegro_channel *channel, - size_t n, size_t size) -{ - return allocate_buffers_internal(channel, - &channel->buffers_reference, - n, PAGE_ALIGN(size)); -} - -static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, - void *dest, size_t n) -{ - struct allegro_dev *dev = channel->dev; - struct nal_h264_sps *sps; - ssize_t size; - unsigned int size_mb = SIZE_MACROBLOCK; - /* Calculation of crop units in Rec. ITU-T H.264 (04/2017) p. 76 */ - unsigned int crop_unit_x = 2; - unsigned int crop_unit_y = 2; - - sps = kzalloc(sizeof(*sps), GFP_KERNEL); - if (!sps) - return -ENOMEM; - - sps->profile_idc = nal_h264_profile_from_v4l2(channel->profile); - sps->constraint_set0_flag = 0; - sps->constraint_set1_flag = 1; - sps->constraint_set2_flag = 0; - sps->constraint_set3_flag = 0; - sps->constraint_set4_flag = 0; - sps->constraint_set5_flag = 0; - sps->level_idc = nal_h264_level_from_v4l2(channel->level); - sps->seq_parameter_set_id = 0; - sps->log2_max_frame_num_minus4 = 0; - sps->pic_order_cnt_type = 0; - sps->log2_max_pic_order_cnt_lsb_minus4 = 6; - sps->max_num_ref_frames = 3; - sps->gaps_in_frame_num_value_allowed_flag = 0; - sps->pic_width_in_mbs_minus1 = - DIV_ROUND_UP(channel->width, size_mb) - 1; - sps->pic_height_in_map_units_minus1 = - DIV_ROUND_UP(channel->height, size_mb) - 1; - sps->frame_mbs_only_flag = 1; - sps->mb_adaptive_frame_field_flag = 0; - sps->direct_8x8_inference_flag = 1; - sps->frame_cropping_flag = - (channel->width % size_mb) || (channel->height % size_mb); - if (sps->frame_cropping_flag) { - sps->crop_left = 0; - sps->crop_right = (round_up(channel->width, size_mb) - channel->width) / crop_unit_x; - sps->crop_top = 0; - sps->crop_bottom = (round_up(channel->height, size_mb) - channel->height) / crop_unit_y; - } - sps->vui_parameters_present_flag = 1; - sps->vui.aspect_ratio_info_present_flag = 0; - sps->vui.overscan_info_present_flag = 0; - sps->vui.video_signal_type_present_flag = 1; - sps->vui.video_format = 1; - sps->vui.video_full_range_flag = 0; - sps->vui.colour_description_present_flag = 1; - sps->vui.colour_primaries = 5; - sps->vui.transfer_characteristics = 5; - sps->vui.matrix_coefficients = 5; - sps->vui.chroma_loc_info_present_flag = 1; - sps->vui.chroma_sample_loc_type_top_field = 0; - sps->vui.chroma_sample_loc_type_bottom_field = 0; - - sps->vui.timing_info_present_flag = 1; - sps->vui.num_units_in_tick = channel->framerate.denominator; - sps->vui.time_scale = 2 * channel->framerate.numerator; - - sps->vui.fixed_frame_rate_flag = 1; - sps->vui.nal_hrd_parameters_present_flag = 0; - sps->vui.vcl_hrd_parameters_present_flag = 1; - sps->vui.vcl_hrd_parameters.cpb_cnt_minus1 = 0; - sps->vui.vcl_hrd_parameters.bit_rate_scale = 0; - sps->vui.vcl_hrd_parameters.cpb_size_scale = 1; - /* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */ - sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] = - channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1; - /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */ - sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] = - (channel->cpb_size * 1000) / (1 << (4 + sps->vui.vcl_hrd_parameters.cpb_size_scale)) - 1; - sps->vui.vcl_hrd_parameters.cbr_flag[0] = - !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable); - sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 = 31; - sps->vui.vcl_hrd_parameters.cpb_removal_delay_length_minus1 = 31; - sps->vui.vcl_hrd_parameters.dpb_output_delay_length_minus1 = 31; - sps->vui.vcl_hrd_parameters.time_offset_length = 0; - sps->vui.low_delay_hrd_flag = 0; - sps->vui.pic_struct_present_flag = 1; - sps->vui.bitstream_restriction_flag = 0; - - size = nal_h264_write_sps(&dev->plat_dev->dev, dest, n, sps); - - kfree(sps); - - return size; -} - -static ssize_t allegro_h264_write_pps(struct allegro_channel *channel, - void *dest, size_t n) -{ - struct allegro_dev *dev = channel->dev; - struct nal_h264_pps *pps; - ssize_t size; - - pps = kzalloc(sizeof(*pps), GFP_KERNEL); - if (!pps) - return -ENOMEM; - - pps->pic_parameter_set_id = 0; - pps->seq_parameter_set_id = 0; - pps->entropy_coding_mode_flag = 0; - pps->bottom_field_pic_order_in_frame_present_flag = 0; - pps->num_slice_groups_minus1 = 0; - pps->num_ref_idx_l0_default_active_minus1 = channel->num_ref_idx_l0 - 1; - pps->num_ref_idx_l1_default_active_minus1 = channel->num_ref_idx_l1 - 1; - pps->weighted_pred_flag = 0; - pps->weighted_bipred_idc = 0; - pps->pic_init_qp_minus26 = 0; - pps->pic_init_qs_minus26 = 0; - pps->chroma_qp_index_offset = 0; - pps->deblocking_filter_control_present_flag = 1; - pps->constrained_intra_pred_flag = 0; - pps->redundant_pic_cnt_present_flag = 0; - pps->transform_8x8_mode_flag = 0; - pps->pic_scaling_matrix_present_flag = 0; - pps->second_chroma_qp_index_offset = 0; - - size = nal_h264_write_pps(&dev->plat_dev->dev, dest, n, pps); - - kfree(pps); - - return size; -} - -static bool allegro_channel_is_at_eos(struct allegro_channel *channel) -{ - bool is_at_eos = false; - - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_STOPPED: - is_at_eos = true; - break; - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - mutex_lock(&channel->shadow_list_lock); - if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0 && - list_empty(&channel->source_shadow_list)) - is_at_eos = true; - mutex_unlock(&channel->shadow_list_lock); - break; - default: - break; - } - - return is_at_eos; -} - -static void allegro_channel_buf_done(struct allegro_channel *channel, - struct vb2_v4l2_buffer *buf, - enum vb2_buffer_state state) -{ - const struct v4l2_event eos_event = { - .type = V4L2_EVENT_EOS - }; - - if (allegro_channel_is_at_eos(channel)) { - buf->flags |= V4L2_BUF_FLAG_LAST; - v4l2_event_queue_fh(&channel->fh, &eos_event); - - allegro_set_state(channel, ALLEGRO_STATE_STOPPED); - } - - v4l2_m2m_buf_done(buf, state); -} - -static u64 allegro_put_buffer(struct allegro_channel *channel, - struct list_head *list, - struct vb2_v4l2_buffer *buffer) -{ - struct v4l2_m2m_buffer *b = container_of(buffer, - struct v4l2_m2m_buffer, vb); - struct allegro_m2m_buffer *shadow = to_allegro_m2m_buffer(b); - - mutex_lock(&channel->shadow_list_lock); - list_add_tail(&shadow->head, list); - mutex_unlock(&channel->shadow_list_lock); - - return ptr_to_u64(buffer); -} - -static struct vb2_v4l2_buffer * -allegro_get_buffer(struct allegro_channel *channel, - struct list_head *list, u64 handle) -{ - struct allegro_m2m_buffer *shadow, *tmp; - struct vb2_v4l2_buffer *buffer = NULL; - - mutex_lock(&channel->shadow_list_lock); - list_for_each_entry_safe(shadow, tmp, list, head) { - if (handle == ptr_to_u64(&shadow->buf.vb)) { - buffer = &shadow->buf.vb; - list_del_init(&shadow->head); - break; - } - } - mutex_unlock(&channel->shadow_list_lock); - - return buffer; -} - -static void allegro_channel_finish_frame(struct allegro_channel *channel, - struct mcu_msg_encode_frame_response *msg) -{ - struct allegro_dev *dev = channel->dev; - struct vb2_v4l2_buffer *src_buf; - struct vb2_v4l2_buffer *dst_buf; - struct { - u32 offset; - u32 size; - } *partition; - enum vb2_buffer_state state = VB2_BUF_STATE_ERROR; - char *curr; - ssize_t len; - ssize_t free; - - src_buf = allegro_get_buffer(channel, &channel->source_shadow_list, - msg->src_handle); - if (!src_buf) - v4l2_warn(&dev->v4l2_dev, - "channel %d: invalid source buffer\n", - channel->mcu_channel_id); - - dst_buf = allegro_get_buffer(channel, &channel->stream_shadow_list, - msg->stream_id); - if (!dst_buf) - v4l2_warn(&dev->v4l2_dev, - "channel %d: invalid stream buffer\n", - channel->mcu_channel_id); - - if (!src_buf || !dst_buf) - goto err; - - dst_buf->sequence = channel->csequence++; - - if (msg->error_code & AL_ERROR) { - v4l2_err(&dev->v4l2_dev, - "channel %d: failed to encode frame: %s (%x)\n", - channel->mcu_channel_id, - allegro_err_to_string(msg->error_code), - msg->error_code); - goto err; - } - - if (msg->partition_table_size != 1) { - v4l2_warn(&dev->v4l2_dev, - "channel %d: only handling first partition table entry (%d entries)\n", - channel->mcu_channel_id, msg->partition_table_size); - } - - if (msg->partition_table_offset + - msg->partition_table_size * sizeof(*partition) > - vb2_plane_size(&dst_buf->vb2_buf, 0)) { - v4l2_err(&dev->v4l2_dev, - "channel %d: partition table outside of dst_buf\n", - channel->mcu_channel_id); - goto err; - } - - partition = - vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + msg->partition_table_offset; - if (partition->offset + partition->size > - vb2_plane_size(&dst_buf->vb2_buf, 0)) { - v4l2_err(&dev->v4l2_dev, - "channel %d: encoded frame is outside of dst_buf (offset 0x%x, size 0x%x)\n", - channel->mcu_channel_id, partition->offset, - partition->size); - goto err; - } - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "channel %d: encoded frame of size %d is at offset 0x%x\n", - channel->mcu_channel_id, partition->size, partition->offset); - - /* - * The payload must include the data before the partition offset, - * because we will put the sps and pps data there. - */ - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, - partition->offset + partition->size); - - curr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); - free = partition->offset; - if (msg->is_idr) { - len = allegro_h264_write_sps(channel, curr, free); - if (len < 0) { - v4l2_err(&dev->v4l2_dev, - "not enough space for sequence parameter set: %zd left\n", - free); - goto err; - } - curr += len; - free -= len; - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: wrote %zd byte SPS nal unit\n", - channel->mcu_channel_id, len); - } - - if (msg->slice_type == AL_ENC_SLICE_TYPE_I) { - len = allegro_h264_write_pps(channel, curr, free); - if (len < 0) { - v4l2_err(&dev->v4l2_dev, - "not enough space for picture parameter set: %zd left\n", - free); - goto err; - } - curr += len; - free -= len; - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: wrote %zd byte PPS nal unit\n", - channel->mcu_channel_id, len); - } - - if (msg->slice_type != AL_ENC_SLICE_TYPE_I && !msg->is_idr) { - dst_buf->vb2_buf.planes[0].data_offset = free; - free = 0; - } else { - len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free); - if (len < 0) { - v4l2_err(&dev->v4l2_dev, - "failed to write %zd filler data\n", free); - goto err; - } - curr += len; - free -= len; - v4l2_dbg(2, debug, &dev->v4l2_dev, - "channel %d: wrote %zd bytes filler nal unit\n", - channel->mcu_channel_id, len); - } - - if (free != 0) { - v4l2_err(&dev->v4l2_dev, - "non-VCL NAL units do not fill space until VCL NAL unit: %zd bytes left\n", - free); - goto err; - } - - state = VB2_BUF_STATE_DONE; - - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); - if (msg->is_idr) - dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - else - dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: encoded frame #%03d (%s%s, QP %d, %d bytes)\n", - channel->mcu_channel_id, - dst_buf->sequence, - msg->is_idr ? "IDR, " : "", - msg->slice_type == AL_ENC_SLICE_TYPE_I ? "I slice" : - msg->slice_type == AL_ENC_SLICE_TYPE_P ? "P slice" : "unknown", - msg->qp, partition->size); - -err: - if (src_buf) - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - - if (dst_buf) - allegro_channel_buf_done(channel, dst_buf, state); -} - -static int allegro_handle_init(struct allegro_dev *dev, - struct mcu_msg_init_response *msg) -{ - complete(&dev->init_complete); - - return 0; -} - -static int -allegro_handle_create_channel(struct allegro_dev *dev, - struct mcu_msg_create_channel_response *msg) -{ - struct allegro_channel *channel; - int err = 0; - struct create_channel_param param; - - channel = allegro_find_channel_by_user_id(dev, msg->user_id); - if (IS_ERR(channel)) { - v4l2_warn(&dev->v4l2_dev, - "received %s for unknown user %d\n", - msg_type_name(msg->header.type), - msg->user_id); - return -EINVAL; - } - - if (msg->error_code) { - v4l2_err(&dev->v4l2_dev, - "user %d: mcu failed to create channel: %s (%x)\n", - channel->user_id, - allegro_err_to_string(msg->error_code), - msg->error_code); - err = -EIO; - goto out; - } - - channel->mcu_channel_id = msg->channel_id; - v4l2_dbg(1, debug, &dev->v4l2_dev, - "user %d: channel has channel id %d\n", - channel->user_id, channel->mcu_channel_id); - - err = allegro_decode_config_blob(¶m, msg, channel->config_blob.vaddr); - allegro_free_buffer(channel->dev, &channel->config_blob); - if (err) - goto out; - - channel->num_ref_idx_l0 = param.num_ref_idx_l0; - channel->num_ref_idx_l1 = param.num_ref_idx_l1; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: intermediate buffers: %d x %d bytes\n", - channel->mcu_channel_id, - msg->int_buffers_count, msg->int_buffers_size); - err = allocate_intermediate_buffers(channel, msg->int_buffers_count, - msg->int_buffers_size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "channel %d: failed to allocate intermediate buffers\n", - channel->mcu_channel_id); - goto out; - } - err = allegro_mcu_push_buffer_intermediate(channel); - if (err) - goto out; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: reference buffers: %d x %d bytes\n", - channel->mcu_channel_id, - msg->rec_buffers_count, msg->rec_buffers_size); - err = allocate_reference_buffers(channel, msg->rec_buffers_count, - msg->rec_buffers_size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "channel %d: failed to allocate reference buffers\n", - channel->mcu_channel_id); - goto out; - } - err = allegro_mcu_push_buffer_reference(channel); - if (err) - goto out; - -out: - channel->error = err; - complete(&channel->completion); - - /* Handled successfully, error is passed via channel->error */ - return 0; -} - -static int -allegro_handle_destroy_channel(struct allegro_dev *dev, - struct mcu_msg_destroy_channel_response *msg) -{ - struct allegro_channel *channel; - - channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); - if (IS_ERR(channel)) { - v4l2_err(&dev->v4l2_dev, - "received %s for unknown channel %d\n", - msg_type_name(msg->header.type), - msg->channel_id); - return -EINVAL; - } - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "user %d: vcu destroyed channel %d\n", - channel->user_id, channel->mcu_channel_id); - complete(&channel->completion); - - return 0; -} - -static int -allegro_handle_encode_frame(struct allegro_dev *dev, - struct mcu_msg_encode_frame_response *msg) -{ - struct allegro_channel *channel; - - channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); - if (IS_ERR(channel)) { - v4l2_err(&dev->v4l2_dev, - "received %s for unknown channel %d\n", - msg_type_name(msg->header.type), - msg->channel_id); - return -EINVAL; - } - - allegro_channel_finish_frame(channel, msg); - - return 0; -} - -static void allegro_handle_message(struct allegro_dev *dev, - union mcu_msg_response *msg) -{ - switch (msg->header.type) { - case MCU_MSG_TYPE_INIT: - allegro_handle_init(dev, &msg->init); - break; - case MCU_MSG_TYPE_CREATE_CHANNEL: - allegro_handle_create_channel(dev, &msg->create_channel); - break; - case MCU_MSG_TYPE_DESTROY_CHANNEL: - allegro_handle_destroy_channel(dev, &msg->destroy_channel); - break; - case MCU_MSG_TYPE_ENCODE_FRAME: - allegro_handle_encode_frame(dev, &msg->encode_frame); - break; - default: - v4l2_warn(&dev->v4l2_dev, - "%s: unknown message %s\n", - __func__, msg_type_name(msg->header.type)); - break; - } -} - -static irqreturn_t allegro_hardirq(int irq, void *data) -{ - struct allegro_dev *dev = data; - unsigned int status; - - regmap_read(dev->regmap, AL5_ITC_CPU_IRQ_STA, &status); - if (!(status & AL5_ITC_CPU_IRQ_STA_TRIGGERED)) - return IRQ_NONE; - - regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_CLR, status); - - return IRQ_WAKE_THREAD; -} - -static irqreturn_t allegro_irq_thread(int irq, void *data) -{ - struct allegro_dev *dev = data; - - allegro_mbox_notify(dev->mbox_status); - - return IRQ_HANDLED; -} - -static void allegro_copy_firmware(struct allegro_dev *dev, - const u8 * const buf, size_t size) -{ - int err = 0; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "copy mcu firmware (%zu B) to SRAM\n", size); - err = regmap_bulk_write(dev->sram, 0x0, buf, size / 4); - if (err) - v4l2_err(&dev->v4l2_dev, - "failed to copy firmware: %d\n", err); -} - -static void allegro_copy_fw_codec(struct allegro_dev *dev, - const u8 * const buf, size_t size) -{ - int err; - dma_addr_t icache_offset, dcache_offset; - - /* - * The downstream allocates 600 KB for the codec firmware to have some - * extra space for "possible extensions." My tests were fine with - * allocating just enough memory for the actual firmware, but I am not - * sure that the firmware really does not use the remaining space. - */ - err = allegro_alloc_buffer(dev, &dev->firmware, size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "failed to allocate %zu bytes for firmware\n", size); - return; - } - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "copy codec firmware (%zd B) to phys %pad\n", - size, &dev->firmware.paddr); - memcpy(dev->firmware.vaddr, buf, size); - - regmap_write(dev->regmap, AXI_ADDR_OFFSET_IP, - upper_32_bits(dev->firmware.paddr)); - - icache_offset = dev->firmware.paddr - MCU_CACHE_OFFSET; - v4l2_dbg(2, debug, &dev->v4l2_dev, - "icache_offset: msb = 0x%x, lsb = 0x%x\n", - upper_32_bits(icache_offset), lower_32_bits(icache_offset)); - regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_MSB, - upper_32_bits(icache_offset)); - regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_LSB, - lower_32_bits(icache_offset)); - - dcache_offset = - (dev->firmware.paddr & 0xffffffff00000000ULL) - MCU_CACHE_OFFSET; - v4l2_dbg(2, debug, &dev->v4l2_dev, - "dcache_offset: msb = 0x%x, lsb = 0x%x\n", - upper_32_bits(dcache_offset), lower_32_bits(dcache_offset)); - regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_MSB, - upper_32_bits(dcache_offset)); - regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_LSB, - lower_32_bits(dcache_offset)); -} - -static void allegro_free_fw_codec(struct allegro_dev *dev) -{ - allegro_free_buffer(dev, &dev->firmware); -} - -/* - * Control functions for the MCU - */ - -static int allegro_mcu_enable_interrupts(struct allegro_dev *dev) -{ - return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, BIT(0)); -} - -static int allegro_mcu_disable_interrupts(struct allegro_dev *dev) -{ - return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, 0); -} - -static int allegro_mcu_wait_for_sleep(struct allegro_dev *dev) -{ - unsigned long timeout; - unsigned int status; - - timeout = jiffies + msecs_to_jiffies(100); - while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && - status != AL5_MCU_STA_SLEEP) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - cpu_relax(); - } - - return 0; -} - -static int allegro_mcu_start(struct allegro_dev *dev) -{ - unsigned long timeout; - unsigned int status; - int err; - - err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, BIT(0)); - if (err) - return err; - - timeout = jiffies + msecs_to_jiffies(100); - while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && - status == AL5_MCU_STA_SLEEP) { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - cpu_relax(); - } - - err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); - if (err) - return err; - - return 0; -} - -static int allegro_mcu_reset(struct allegro_dev *dev) -{ - int err; - - /* - * Ensure that the AL5_MCU_WAKEUP bit is set to 0 otherwise the mcu - * does not go to sleep after the reset. - */ - err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); - if (err) - return err; - - err = regmap_write(dev->regmap, - AL5_MCU_RESET_MODE, AL5_MCU_RESET_MODE_SLEEP); - if (err < 0) - return err; - - err = regmap_write(dev->regmap, AL5_MCU_RESET, AL5_MCU_RESET_SOFT); - if (err < 0) - return err; - - return allegro_mcu_wait_for_sleep(dev); -} - -static void allegro_mcu_interrupt(struct allegro_dev *dev) -{ - regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); -} - -static void allegro_destroy_channel(struct allegro_channel *channel) -{ - struct allegro_dev *dev = channel->dev; - unsigned long timeout; - - if (channel_exists(channel)) { - reinit_completion(&channel->completion); - allegro_mcu_send_destroy_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) - v4l2_warn(&dev->v4l2_dev, - "channel %d: timeout while destroying\n", - channel->mcu_channel_id); - - channel->mcu_channel_id = -1; - } - - destroy_intermediate_buffers(channel); - destroy_reference_buffers(channel); - - v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_level, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false); - v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false); - v4l2_ctrl_grab(channel->mpeg_video_bitrate, false); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false); - v4l2_ctrl_grab(channel->mpeg_video_cpb_size, false); - v4l2_ctrl_grab(channel->mpeg_video_gop_size, false); - - if (channel->user_id != -1) { - clear_bit(channel->user_id, &dev->channel_user_ids); - channel->user_id = -1; - } -} - -/* - * Create the MCU channel - * - * After the channel has been created, the picture size, format, colorspace - * and framerate are fixed. Also the codec, profile, bitrate, etc. cannot be - * changed anymore. - * - * The channel can be created only once. The MCU will accept source buffers - * and stream buffers only after a channel has been created. - */ -static int allegro_create_channel(struct allegro_channel *channel) -{ - struct allegro_dev *dev = channel->dev; - unsigned long timeout; - enum v4l2_mpeg_video_h264_level min_level; - - if (channel_exists(channel)) { - v4l2_warn(&dev->v4l2_dev, - "channel already exists\n"); - return 0; - } - - channel->user_id = allegro_next_user_id(dev); - if (channel->user_id < 0) { - v4l2_err(&dev->v4l2_dev, - "no free channels available\n"); - return -EBUSY; - } - set_bit(channel->user_id, &dev->channel_user_ids); - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "user %d: creating channel (%4.4s, %dx%d@%d)\n", - channel->user_id, - (char *)&channel->codec, channel->width, channel->height, - DIV_ROUND_UP(channel->framerate.numerator, - channel->framerate.denominator)); - - min_level = select_minimum_h264_level(channel->width, channel->height); - if (channel->level < min_level) { - v4l2_warn(&dev->v4l2_dev, - "user %d: selected Level %s too low: increasing to Level %s\n", - channel->user_id, - v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[channel->level], - v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[min_level]); - channel->level = min_level; - } - - v4l2_ctrl_grab(channel->mpeg_video_h264_profile, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_level, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, true); - v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, true); - v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, true); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true); - v4l2_ctrl_grab(channel->mpeg_video_bitrate, true); - v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true); - v4l2_ctrl_grab(channel->mpeg_video_cpb_size, true); - v4l2_ctrl_grab(channel->mpeg_video_gop_size, true); - - reinit_completion(&channel->completion); - allegro_mcu_send_create_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) - channel->error = -ETIMEDOUT; - if (channel->error) - goto err; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: accepting buffers\n", - channel->mcu_channel_id); - - return 0; - -err: - allegro_destroy_channel(channel); - - return channel->error; -} - -static void allegro_set_default_params(struct allegro_channel *channel) -{ - channel->width = ALLEGRO_WIDTH_DEFAULT; - channel->height = ALLEGRO_HEIGHT_DEFAULT; - channel->stride = round_up(channel->width, 32); - channel->framerate = ALLEGRO_FRAMERATE_DEFAULT; - - channel->colorspace = V4L2_COLORSPACE_REC709; - channel->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - channel->quantization = V4L2_QUANTIZATION_DEFAULT; - channel->xfer_func = V4L2_XFER_FUNC_DEFAULT; - - channel->pixelformat = V4L2_PIX_FMT_NV12; - channel->sizeimage_raw = channel->stride * channel->height * 3 / 2; - - channel->codec = V4L2_PIX_FMT_H264; - channel->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; - channel->level = - select_minimum_h264_level(channel->width, channel->height); - channel->sizeimage_encoded = - estimate_stream_size(channel->width, channel->height); - - channel->bitrate = maximum_bitrate(channel->level); - channel->bitrate_peak = maximum_bitrate(channel->level); - channel->cpb_size = maximum_cpb_size(channel->level); - channel->gop_size = ALLEGRO_GOP_SIZE_DEFAULT; -} - -static int allegro_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct allegro_channel *channel = vb2_get_drv_priv(vq); - struct allegro_dev *dev = channel->dev; - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "%s: queue setup[%s]: nplanes = %d\n", - V4L2_TYPE_IS_OUTPUT(vq->type) ? "output" : "capture", - *nplanes == 0 ? "REQBUFS" : "CREATE_BUFS", *nplanes); - - if (*nplanes != 0) { - if (V4L2_TYPE_IS_OUTPUT(vq->type)) { - if (sizes[0] < channel->sizeimage_raw) - return -EINVAL; - } else { - if (sizes[0] < channel->sizeimage_encoded) - return -EINVAL; - } - } else { - *nplanes = 1; - if (V4L2_TYPE_IS_OUTPUT(vq->type)) - sizes[0] = channel->sizeimage_raw; - else - sizes[0] = channel->sizeimage_encoded; - } - - return 0; -} - -static int allegro_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); - struct allegro_dev *dev = channel->dev; - - if (allegro_get_state(channel) == ALLEGRO_STATE_DRAIN && - V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) - return -EBUSY; - - if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { - if (vbuf->field == V4L2_FIELD_ANY) - vbuf->field = V4L2_FIELD_NONE; - if (vbuf->field != V4L2_FIELD_NONE) { - v4l2_err(&dev->v4l2_dev, - "channel %d: unsupported field\n", - channel->mcu_channel_id); - return -EINVAL; - } - } - - return 0; -} - -static void allegro_buf_queue(struct vb2_buffer *vb) -{ - struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - if (allegro_get_state(channel) == ALLEGRO_STATE_WAIT_FOR_BUFFER && - vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - allegro_channel_buf_done(channel, vbuf, VB2_BUF_STATE_DONE); - return; - } - - v4l2_m2m_buf_queue(channel->fh.m2m_ctx, vbuf); -} - -static int allegro_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct allegro_channel *channel = vb2_get_drv_priv(q); - struct allegro_dev *dev = channel->dev; - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "%s: start streaming\n", - V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); - - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - channel->osequence = 0; - allegro_set_state(channel, ALLEGRO_STATE_ENCODING); - } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - channel->csequence = 0; - } - - return 0; -} - -static void allegro_stop_streaming(struct vb2_queue *q) -{ - struct allegro_channel *channel = vb2_get_drv_priv(q); - struct allegro_dev *dev = channel->dev; - struct vb2_v4l2_buffer *buffer; - struct allegro_m2m_buffer *shadow, *tmp; - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "%s: stop streaming\n", - V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); - - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - mutex_lock(&channel->shadow_list_lock); - list_for_each_entry_safe(shadow, tmp, - &channel->source_shadow_list, head) { - list_del(&shadow->head); - v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); - } - mutex_unlock(&channel->shadow_list_lock); - - allegro_set_state(channel, ALLEGRO_STATE_STOPPED); - while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx))) - v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); - } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_lock(&channel->shadow_list_lock); - list_for_each_entry_safe(shadow, tmp, - &channel->stream_shadow_list, head) { - list_del(&shadow->head); - v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); - } - mutex_unlock(&channel->shadow_list_lock); - - allegro_destroy_channel(channel); - while ((buffer = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx))) - v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); - } -} - -static const struct vb2_ops allegro_queue_ops = { - .queue_setup = allegro_queue_setup, - .buf_prepare = allegro_buf_prepare, - .buf_queue = allegro_buf_queue, - .start_streaming = allegro_start_streaming, - .stop_streaming = allegro_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int allegro_queue_init(void *priv, - struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - int err; - struct allegro_channel *channel = priv; - - src_vq->dev = &channel->dev->plat_dev->dev; - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_DMABUF | VB2_MMAP; - src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->drv_priv = channel; - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->ops = &allegro_queue_ops; - src_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); - src_vq->lock = &channel->dev->lock; - err = vb2_queue_init(src_vq); - if (err) - return err; - - dst_vq->dev = &channel->dev->plat_dev->dev; - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->drv_priv = channel; - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->ops = &allegro_queue_ops; - dst_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); - dst_vq->lock = &channel->dev->lock; - err = vb2_queue_init(dst_vq); - if (err) - return err; - - return 0; -} - -static int allegro_clamp_qp(struct allegro_channel *channel, - struct v4l2_ctrl *ctrl) -{ - struct v4l2_ctrl *next_ctrl; - - if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP) - next_ctrl = channel->mpeg_video_h264_p_frame_qp; - else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP) - next_ctrl = channel->mpeg_video_h264_b_frame_qp; - else - return 0; - - /* Modify range automatically updates the value */ - __v4l2_ctrl_modify_range(next_ctrl, ctrl->val, 51, 1, ctrl->val); - - return allegro_clamp_qp(channel, next_ctrl); -} - -static int allegro_clamp_bitrate(struct allegro_channel *channel, - struct v4l2_ctrl *ctrl) -{ - struct v4l2_ctrl *ctrl_bitrate = channel->mpeg_video_bitrate; - struct v4l2_ctrl *ctrl_bitrate_peak = channel->mpeg_video_bitrate_peak; - - if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && - ctrl_bitrate_peak->val < ctrl_bitrate->val) - ctrl_bitrate_peak->val = ctrl_bitrate->val; - - return 0; -} - -static int allegro_try_ctrl(struct v4l2_ctrl *ctrl) -{ - struct allegro_channel *channel = container_of(ctrl->handler, - struct allegro_channel, - ctrl_handler); - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - allegro_clamp_bitrate(channel, ctrl); - break; - } - - return 0; -} - -static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct allegro_channel *channel = container_of(ctrl->handler, - struct allegro_channel, - ctrl_handler); - struct allegro_dev *dev = channel->dev; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "s_ctrl: %s = %d\n", v4l2_ctrl_get_name(ctrl->id), ctrl->val); - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - channel->level = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: - channel->frame_rc_enable = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - channel->bitrate = channel->mpeg_video_bitrate->val; - channel->bitrate_peak = channel->mpeg_video_bitrate_peak->val; - v4l2_ctrl_activate(channel->mpeg_video_bitrate_peak, - ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - break; - case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: - channel->cpb_size = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - channel->gop_size = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: - case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: - case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: - allegro_clamp_qp(channel, ctrl); - break; - } - - return 0; -} - -static const struct v4l2_ctrl_ops allegro_ctrl_ops = { - .try_ctrl = allegro_try_ctrl, - .s_ctrl = allegro_s_ctrl, -}; - -static int allegro_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct allegro_dev *dev = video_get_drvdata(vdev); - struct allegro_channel *channel = NULL; - struct v4l2_ctrl_handler *handler; - u64 mask; - int ret; - - channel = kzalloc(sizeof(*channel), GFP_KERNEL); - if (!channel) - return -ENOMEM; - - v4l2_fh_init(&channel->fh, vdev); - - init_completion(&channel->completion); - INIT_LIST_HEAD(&channel->source_shadow_list); - INIT_LIST_HEAD(&channel->stream_shadow_list); - mutex_init(&channel->shadow_list_lock); - - channel->dev = dev; - - allegro_set_default_params(channel); - - handler = &channel->ctrl_handler; - v4l2_ctrl_handler_init(handler, 0); - channel->mpeg_video_h264_profile = v4l2_ctrl_new_std_menu(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_PROFILE, - V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, - V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); - mask = 1 << V4L2_MPEG_VIDEO_H264_LEVEL_1B; - channel->mpeg_video_h264_level = v4l2_ctrl_new_std_menu(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_LEVEL, - V4L2_MPEG_VIDEO_H264_LEVEL_5_1, mask, - V4L2_MPEG_VIDEO_H264_LEVEL_5_1); - channel->mpeg_video_h264_i_frame_qp = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, - 0, 51, 1, 30); - channel->mpeg_video_h264_max_qp = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MAX_QP, - 0, 51, 1, 51); - channel->mpeg_video_h264_min_qp = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MIN_QP, - 0, 51, 1, 0); - channel->mpeg_video_h264_p_frame_qp = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, - 0, 51, 1, 30); - channel->mpeg_video_h264_b_frame_qp = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, - 0, 51, 1, 30); - channel->mpeg_video_frame_rc_enable = - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, - false, 0x1, - true, false); - channel->mpeg_video_bitrate_mode = v4l2_ctrl_new_std_menu(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); - channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE, - 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->bitrate); - channel->mpeg_video_bitrate_peak = v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->bitrate_peak); - channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, - 0, maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->cpb_size); - channel->mpeg_video_gop_size = v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - 0, ALLEGRO_GOP_SIZE_MAX, - 1, channel->gop_size); - v4l2_ctrl_new_std(handler, - &allegro_ctrl_ops, - V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, - 1, 32, - 1, 1); - if (handler->error != 0) { - ret = handler->error; - goto error; - } - - channel->fh.ctrl_handler = handler; - - v4l2_ctrl_cluster(3, &channel->mpeg_video_bitrate_mode); - - channel->mcu_channel_id = -1; - channel->user_id = -1; - - INIT_LIST_HEAD(&channel->buffers_reference); - INIT_LIST_HEAD(&channel->buffers_intermediate); - - channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel, - allegro_queue_init); - - if (IS_ERR(channel->fh.m2m_ctx)) { - ret = PTR_ERR(channel->fh.m2m_ctx); - goto error; - } - - list_add(&channel->list, &dev->channels); - file->private_data = &channel->fh; - v4l2_fh_add(&channel->fh); - - return 0; - -error: - v4l2_ctrl_handler_free(handler); - kfree(channel); - return ret; -} - -static int allegro_release(struct file *file) -{ - struct allegro_channel *channel = fh_to_channel(file->private_data); - - v4l2_m2m_ctx_release(channel->fh.m2m_ctx); - - list_del(&channel->list); - - v4l2_ctrl_handler_free(&channel->ctrl_handler); - - v4l2_fh_del(&channel->fh); - v4l2_fh_exit(&channel->fh); - - kfree(channel); - - return 0; -} - -static int allegro_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct video_device *vdev = video_devdata(file); - struct allegro_dev *dev = video_get_drvdata(vdev); - - strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); - strscpy(cap->card, "Allegro DVT Video Encoder", sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", - dev_name(&dev->plat_dev->dev)); - - return 0; -} - -static int allegro_enum_fmt_vid(struct file *file, void *fh, - struct v4l2_fmtdesc *f) -{ - if (f->index) - return -EINVAL; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - f->pixelformat = V4L2_PIX_FMT_NV12; - break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - f->pixelformat = V4L2_PIX_FMT_H264; - break; - default: - return -EINVAL; - } - return 0; -} - -static int allegro_g_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct allegro_channel *channel = fh_to_channel(fh); - - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = channel->width; - f->fmt.pix.height = channel->height; - - f->fmt.pix.colorspace = channel->colorspace; - f->fmt.pix.ycbcr_enc = channel->ycbcr_enc; - f->fmt.pix.quantization = channel->quantization; - f->fmt.pix.xfer_func = channel->xfer_func; - - f->fmt.pix.pixelformat = channel->codec; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = channel->sizeimage_encoded; - - return 0; -} - -static int allegro_try_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - f->fmt.pix.field = V4L2_FIELD_NONE; - - f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width, - ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX); - f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height, - ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX); - - f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - estimate_stream_size(f->fmt.pix.width, f->fmt.pix.height); - - return 0; -} - -static int allegro_g_fmt_vid_out(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct allegro_channel *channel = fh_to_channel(fh); - - f->fmt.pix.field = V4L2_FIELD_NONE; - - f->fmt.pix.width = channel->width; - f->fmt.pix.height = channel->height; - - f->fmt.pix.colorspace = channel->colorspace; - f->fmt.pix.ycbcr_enc = channel->ycbcr_enc; - f->fmt.pix.quantization = channel->quantization; - f->fmt.pix.xfer_func = channel->xfer_func; - - f->fmt.pix.pixelformat = channel->pixelformat; - f->fmt.pix.bytesperline = channel->stride; - f->fmt.pix.sizeimage = channel->sizeimage_raw; - - return 0; -} - -static int allegro_try_fmt_vid_out(struct file *file, void *fh, - struct v4l2_format *f) -{ - f->fmt.pix.field = V4L2_FIELD_NONE; - - /* - * The firmware of the Allegro codec handles the padding internally - * and expects the visual frame size when configuring a channel. - * Therefore, unlike other encoder drivers, this driver does not round - * up the width and height to macroblock alignment and does not - * implement the selection api. - */ - f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width, - ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX); - f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height, - ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX); - - f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 32); - f->fmt.pix.sizeimage = - f->fmt.pix.bytesperline * f->fmt.pix.height * 3 / 2; - - return 0; -} - -static int allegro_s_fmt_vid_out(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct allegro_channel *channel = fh_to_channel(fh); - int err; - - err = allegro_try_fmt_vid_out(file, fh, f); - if (err) - return err; - - channel->width = f->fmt.pix.width; - channel->height = f->fmt.pix.height; - channel->stride = f->fmt.pix.bytesperline; - channel->sizeimage_raw = f->fmt.pix.sizeimage; - - channel->colorspace = f->fmt.pix.colorspace; - channel->ycbcr_enc = f->fmt.pix.ycbcr_enc; - channel->quantization = f->fmt.pix.quantization; - channel->xfer_func = f->fmt.pix.xfer_func; - - channel->level = - select_minimum_h264_level(channel->width, channel->height); - channel->sizeimage_encoded = - estimate_stream_size(channel->width, channel->height); - - return 0; -} - -static int allegro_channel_cmd_stop(struct allegro_channel *channel) -{ - struct allegro_dev *dev = channel->dev; - struct vb2_v4l2_buffer *dst_buf; - - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - return -EBUSY; - case ALLEGRO_STATE_ENCODING: - allegro_set_state(channel, ALLEGRO_STATE_DRAIN); - break; - default: - return 0; - } - - /* If there are output buffers, they must be encoded */ - if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) != 0) { - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: continue encoding src buffers\n", - channel->mcu_channel_id); - return 0; - } - - /* If there are capture buffers, use it to signal EOS */ - dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); - if (dst_buf) { - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: signaling EOS\n", - channel->mcu_channel_id); - allegro_channel_buf_done(channel, dst_buf, VB2_BUF_STATE_DONE); - return 0; - } - - /* - * If there are no capture buffers, we need to wait for the next - * buffer to signal EOS. - */ - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: wait for CAPTURE buffer to signal EOS\n", - channel->mcu_channel_id); - allegro_set_state(channel, ALLEGRO_STATE_WAIT_FOR_BUFFER); - - return 0; -} - -static int allegro_channel_cmd_start(struct allegro_channel *channel) -{ - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - return -EBUSY; - case ALLEGRO_STATE_STOPPED: - allegro_set_state(channel, ALLEGRO_STATE_ENCODING); - break; - default: - return 0; - } - - return 0; -} - -static int allegro_encoder_cmd(struct file *file, void *fh, - struct v4l2_encoder_cmd *cmd) -{ - struct allegro_channel *channel = fh_to_channel(fh); - int err; - - err = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd); - if (err) - return err; - - switch (cmd->cmd) { - case V4L2_ENC_CMD_STOP: - err = allegro_channel_cmd_stop(channel); - break; - case V4L2_ENC_CMD_START: - err = allegro_channel_cmd_start(channel); - break; - default: - err = -EINVAL; - break; - } - - return err; -} - -static int allegro_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - switch (fsize->pixel_format) { - case V4L2_PIX_FMT_H264: - case V4L2_PIX_FMT_NV12: - break; - default: - return -EINVAL; - } - - if (fsize->index) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = ALLEGRO_WIDTH_MIN; - fsize->stepwise.max_width = ALLEGRO_WIDTH_MAX; - fsize->stepwise.step_width = 1; - fsize->stepwise.min_height = ALLEGRO_HEIGHT_MIN; - fsize->stepwise.max_height = ALLEGRO_HEIGHT_MAX; - fsize->stepwise.step_height = 1; - - return 0; -} - -static int allegro_ioctl_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct v4l2_fh *fh = file->private_data; - struct allegro_channel *channel = fh_to_channel(fh); - int err; - - if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - err = allegro_create_channel(channel); - if (err) - return err; - } - - return v4l2_m2m_streamon(file, fh->m2m_ctx, type); -} - -static int allegro_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct allegro_channel *channel = fh_to_channel(fh); - struct v4l2_fract *timeperframe; - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; - timeperframe = &a->parm.output.timeperframe; - timeperframe->numerator = channel->framerate.denominator; - timeperframe->denominator = channel->framerate.numerator; - - return 0; -} - -static int allegro_s_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct allegro_channel *channel = fh_to_channel(fh); - struct v4l2_fract *timeperframe; - int div; - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; - timeperframe = &a->parm.output.timeperframe; - - if (timeperframe->numerator == 0 || timeperframe->denominator == 0) - return allegro_g_parm(file, fh, a); - - div = gcd(timeperframe->denominator, timeperframe->numerator); - channel->framerate.numerator = timeperframe->denominator / div; - channel->framerate.denominator = timeperframe->numerator / div; - - return 0; -} - -static int allegro_subscribe_event(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - switch (sub->type) { - case V4L2_EVENT_EOS: - return v4l2_event_subscribe(fh, sub, 0, NULL); - default: - return v4l2_ctrl_subscribe_event(fh, sub); - } -} - -static const struct v4l2_ioctl_ops allegro_ioctl_ops = { - .vidioc_querycap = allegro_querycap, - .vidioc_enum_fmt_vid_cap = allegro_enum_fmt_vid, - .vidioc_enum_fmt_vid_out = allegro_enum_fmt_vid, - .vidioc_g_fmt_vid_cap = allegro_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = allegro_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = allegro_try_fmt_vid_cap, - .vidioc_g_fmt_vid_out = allegro_g_fmt_vid_out, - .vidioc_try_fmt_vid_out = allegro_try_fmt_vid_out, - .vidioc_s_fmt_vid_out = allegro_s_fmt_vid_out, - - .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, - .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, - - .vidioc_streamon = allegro_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, - .vidioc_encoder_cmd = allegro_encoder_cmd, - .vidioc_enum_framesizes = allegro_enum_framesizes, - - .vidioc_g_parm = allegro_g_parm, - .vidioc_s_parm = allegro_s_parm, - - .vidioc_subscribe_event = allegro_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct v4l2_file_operations allegro_fops = { - .owner = THIS_MODULE, - .open = allegro_open, - .release = allegro_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static int allegro_register_device(struct allegro_dev *dev) -{ - struct video_device *video_dev = &dev->video_dev; - - strscpy(video_dev->name, "allegro", sizeof(video_dev->name)); - video_dev->fops = &allegro_fops; - video_dev->ioctl_ops = &allegro_ioctl_ops; - video_dev->release = video_device_release_empty; - video_dev->lock = &dev->lock; - video_dev->v4l2_dev = &dev->v4l2_dev; - video_dev->vfl_dir = VFL_DIR_M2M; - video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - video_set_drvdata(video_dev, dev); - - return video_register_device(video_dev, VFL_TYPE_VIDEO, 0); -} - -static void allegro_device_run(void *priv) -{ - struct allegro_channel *channel = priv; - struct allegro_dev *dev = channel->dev; - struct vb2_v4l2_buffer *src_buf; - struct vb2_v4l2_buffer *dst_buf; - dma_addr_t src_y; - dma_addr_t src_uv; - dma_addr_t dst_addr; - unsigned long dst_size; - u64 src_handle; - u64 dst_handle; - - dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); - dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0); - dst_handle = allegro_put_buffer(channel, &channel->stream_shadow_list, - dst_buf); - allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size, - dst_handle); - - src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx); - src_buf->sequence = channel->osequence++; - src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - src_uv = src_y + (channel->stride * channel->height); - src_handle = allegro_put_buffer(channel, &channel->source_shadow_list, - src_buf); - allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv, src_handle); - - v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx); -} - -static const struct v4l2_m2m_ops allegro_m2m_ops = { - .device_run = allegro_device_run, -}; - -static int allegro_mcu_hw_init(struct allegro_dev *dev, - const struct fw_info *info) -{ - int err; - - dev->mbox_command = allegro_mbox_init(dev, info->mailbox_cmd, - info->mailbox_size); - dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status, - info->mailbox_size); - if (IS_ERR(dev->mbox_command) || IS_ERR(dev->mbox_status)) { - v4l2_err(&dev->v4l2_dev, - "failed to initialize mailboxes\n"); - return -EIO; - } - - allegro_mcu_enable_interrupts(dev); - - /* The mcu sends INIT after reset. */ - allegro_mcu_start(dev); - err = allegro_mcu_wait_for_init_timeout(dev, 5000); - if (err < 0) { - v4l2_err(&dev->v4l2_dev, - "mcu did not send INIT after reset\n"); - err = -EIO; - goto err_disable_interrupts; - } - - err = allegro_alloc_buffer(dev, &dev->suballocator, - info->suballocator_size); - if (err) { - v4l2_err(&dev->v4l2_dev, - "failed to allocate %zu bytes for suballocator\n", - info->suballocator_size); - goto err_reset_mcu; - } - - allegro_mcu_send_init(dev, dev->suballocator.paddr, - dev->suballocator.size); - err = allegro_mcu_wait_for_init_timeout(dev, 5000); - if (err < 0) { - v4l2_err(&dev->v4l2_dev, - "mcu failed to configure sub-allocator\n"); - err = -EIO; - goto err_free_suballocator; - } - - return 0; - -err_free_suballocator: - allegro_free_buffer(dev, &dev->suballocator); -err_reset_mcu: - allegro_mcu_reset(dev); -err_disable_interrupts: - allegro_mcu_disable_interrupts(dev); - - return err; -} - -static int allegro_mcu_hw_deinit(struct allegro_dev *dev) -{ - int err; - - err = allegro_mcu_reset(dev); - if (err) - v4l2_warn(&dev->v4l2_dev, - "mcu failed to enter sleep state\n"); - - err = allegro_mcu_disable_interrupts(dev); - if (err) - v4l2_warn(&dev->v4l2_dev, - "failed to disable interrupts\n"); - - allegro_free_buffer(dev, &dev->suballocator); - - return 0; -} - -static void allegro_fw_callback(const struct firmware *fw, void *context) -{ - struct allegro_dev *dev = context; - const char *fw_codec_name = "al5e.fw"; - const struct firmware *fw_codec; - int err; - - if (!fw) - return; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "requesting codec firmware '%s'\n", fw_codec_name); - err = request_firmware(&fw_codec, fw_codec_name, &dev->plat_dev->dev); - if (err) - goto err_release_firmware; - - dev->fw_info = allegro_get_firmware_info(dev, fw, fw_codec); - if (!dev->fw_info) { - v4l2_err(&dev->v4l2_dev, "firmware is not supported\n"); - goto err_release_firmware_codec; - } - - v4l2_info(&dev->v4l2_dev, - "using mcu firmware version '%s'\n", dev->fw_info->version); - - /* Ensure that the mcu is sleeping at the reset vector */ - err = allegro_mcu_reset(dev); - if (err) { - v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n"); - goto err_release_firmware_codec; - } - - allegro_copy_firmware(dev, fw->data, fw->size); - allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size); - - err = allegro_mcu_hw_init(dev, dev->fw_info); - if (err) { - v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n"); - goto err_free_fw_codec; - } - - dev->m2m_dev = v4l2_m2m_init(&allegro_m2m_ops); - if (IS_ERR(dev->m2m_dev)) { - v4l2_err(&dev->v4l2_dev, "failed to init mem2mem device\n"); - goto err_mcu_hw_deinit; - } - - err = allegro_register_device(dev); - if (err) { - v4l2_err(&dev->v4l2_dev, "failed to register video device\n"); - goto err_m2m_release; - } - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "allegro codec registered as /dev/video%d\n", - dev->video_dev.num); - - release_firmware(fw_codec); - release_firmware(fw); - - return; - -err_m2m_release: - v4l2_m2m_release(dev->m2m_dev); - dev->m2m_dev = NULL; -err_mcu_hw_deinit: - allegro_mcu_hw_deinit(dev); -err_free_fw_codec: - allegro_free_fw_codec(dev); -err_release_firmware_codec: - release_firmware(fw_codec); -err_release_firmware: - release_firmware(fw); -} - -static int allegro_firmware_request_nowait(struct allegro_dev *dev) -{ - const char *fw = "al5e_b.fw"; - - v4l2_dbg(1, debug, &dev->v4l2_dev, - "requesting firmware '%s'\n", fw); - return request_firmware_nowait(THIS_MODULE, true, fw, - &dev->plat_dev->dev, GFP_KERNEL, dev, - allegro_fw_callback); -} - -static int allegro_probe(struct platform_device *pdev) -{ - struct allegro_dev *dev; - struct resource *res, *sram_res; - int ret; - int irq; - void __iomem *regs, *sram_regs; - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - dev->plat_dev = pdev; - init_completion(&dev->init_complete); - INIT_LIST_HEAD(&dev->channels); - - mutex_init(&dev->lock); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - if (!res) { - dev_err(&pdev->dev, - "regs resource missing from device tree\n"); - return -EINVAL; - } - regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!regs) { - dev_err(&pdev->dev, "failed to map registers\n"); - return -ENOMEM; - } - dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - &allegro_regmap_config); - if (IS_ERR(dev->regmap)) { - dev_err(&pdev->dev, "failed to init regmap\n"); - return PTR_ERR(dev->regmap); - } - - sram_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); - if (!sram_res) { - dev_err(&pdev->dev, - "sram resource missing from device tree\n"); - return -EINVAL; - } - sram_regs = devm_ioremap(&pdev->dev, - sram_res->start, - resource_size(sram_res)); - if (!sram_regs) { - dev_err(&pdev->dev, "failed to map sram\n"); - return -ENOMEM; - } - dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs, - &allegro_sram_config); - if (IS_ERR(dev->sram)) { - dev_err(&pdev->dev, "failed to init sram\n"); - return PTR_ERR(dev->sram); - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - ret = devm_request_threaded_irq(&pdev->dev, irq, - allegro_hardirq, - allegro_irq_thread, - IRQF_SHARED, dev_name(&pdev->dev), dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request irq: %d\n", ret); - return ret; - } - - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); - if (ret) - return ret; - - platform_set_drvdata(pdev, dev); - - ret = allegro_firmware_request_nowait(dev); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, - "failed to request firmware: %d\n", ret); - return ret; - } - - return 0; -} - -static int allegro_remove(struct platform_device *pdev) -{ - struct allegro_dev *dev = platform_get_drvdata(pdev); - - video_unregister_device(&dev->video_dev); - if (dev->m2m_dev) - v4l2_m2m_release(dev->m2m_dev); - allegro_mcu_hw_deinit(dev); - allegro_free_fw_codec(dev); - - v4l2_device_unregister(&dev->v4l2_dev); - - return 0; -} - -static const struct of_device_id allegro_dt_ids[] = { - { .compatible = "allegro,al5e-1.1" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, allegro_dt_ids); - -static struct platform_driver allegro_driver = { - .probe = allegro_probe, - .remove = allegro_remove, - .driver = { - .name = "allegro", - .of_match_table = of_match_ptr(allegro_dt_ids), - }, -}; - -module_platform_driver(allegro_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Michael Tretter "); -MODULE_DESCRIPTION("Allegro DVT encoder driver"); diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c deleted file mode 100644 index 9286d2162377..000000000000 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ /dev/null @@ -1,543 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2019 Pengutronix, Michael Tretter - * - * Helper functions for handling messages that are send via mailbox to the - * Allegro VCU firmware. - */ - -#include -#include -#include -#include -#include - -#include "allegro-mail.h" - -const char *msg_type_name(enum mcu_msg_type type) -{ - static char buf[9]; - - switch (type) { - case MCU_MSG_TYPE_INIT: - return "INIT"; - case MCU_MSG_TYPE_CREATE_CHANNEL: - return "CREATE_CHANNEL"; - case MCU_MSG_TYPE_DESTROY_CHANNEL: - return "DESTROY_CHANNEL"; - case MCU_MSG_TYPE_ENCODE_FRAME: - return "ENCODE_FRAME"; - case MCU_MSG_TYPE_PUT_STREAM_BUFFER: - return "PUT_STREAM_BUFFER"; - case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: - return "PUSH_BUFFER_INTERMEDIATE"; - case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: - return "PUSH_BUFFER_REFERENCE"; - default: - snprintf(buf, sizeof(buf), "(0x%04x)", type); - return buf; - } -} -EXPORT_SYMBOL(msg_type_name); - -static ssize_t -allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) -{ - unsigned int i = 0; - enum mcu_msg_version version = msg->header.version; - - dst[i++] = msg->reserved0; - dst[i++] = msg->suballoc_dma; - dst[i++] = msg->suballoc_size; - dst[i++] = msg->l2_cache[0]; - dst[i++] = msg->l2_cache[1]; - dst[i++] = msg->l2_cache[2]; - if (version >= MCU_MSG_VERSION_2019_2) { - dst[i++] = -1; - dst[i++] = 0; - } - - return i * sizeof(*dst); -} - -static inline u32 settings_get_mcu_codec(struct create_channel_param *param) -{ - enum mcu_msg_version version = param->version; - u32 pixelformat = param->codec; - - if (version < MCU_MSG_VERSION_2019_2) { - switch (pixelformat) { - case V4L2_PIX_FMT_H264: - default: - return 1; - } - } else { - switch (pixelformat) { - case V4L2_PIX_FMT_H264: - default: - return 0; - } - } -} - -ssize_t -allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) -{ - enum mcu_msg_version version = param->version; - unsigned int i = 0; - unsigned int j = 0; - u32 val; - unsigned int codec = settings_get_mcu_codec(param); - - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->layer_id; - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | - FIELD_PREP(GENMASK(15, 0), param->width); - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->videomode; - dst[i++] = param->format; - if (version < MCU_MSG_VERSION_2019_2) - dst[i++] = param->colorspace; - dst[i++] = param->src_mode; - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->src_bit_depth; - dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) | - FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | - FIELD_PREP(GENMASK(7, 0), param->profile); - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) | - FIELD_PREP(GENMASK(15, 0), param->level); - - val = 0; - val |= param->temporal_mvp_enable ? BIT(20) : 0; - val |= FIELD_PREP(GENMASK(7, 4), param->log2_max_frame_num) | - FIELD_PREP(GENMASK(3, 0), param->log2_max_poc); - dst[i++] = val; - - val = 0; - val |= param->dbf_ovr_en ? BIT(2) : 0; - dst[i++] = val; - - if (version >= MCU_MSG_VERSION_2019_2) { - val = 0; - val |= param->custom_lda ? BIT(2) : 0; - val |= param->rdo_cost_mode ? BIT(20) : 0; - dst[i++] = val; - - val = 0; - val |= param->lf ? BIT(2) : 0; - val |= param->lf_x_tile ? BIT(3) : 0; - val |= param->lf_x_slice ? BIT(4) : 0; - dst[i++] = val; - } else { - val = 0; - dst[i++] = val; - } - - dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | - FIELD_PREP(GENMASK(7, 0), param->tc_offset); - dst[i++] = param->unknown11; - dst[i++] = param->unknown12; - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->num_slices; - else - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | - FIELD_PREP(GENMASK(15, 0), param->num_slices); - dst[i++] = param->prefetch_mem_offset; - dst[i++] = param->prefetch_mem_size; - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | - FIELD_PREP(GENMASK(15, 0), param->clip_hrz_range); - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[1]) | - FIELD_PREP(GENMASK(15, 0), param->me_range[0]); - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[3]) | - FIELD_PREP(GENMASK(15, 0), param->me_range[2]); - dst[i++] = FIELD_PREP(GENMASK(31, 24), param->min_tu_size) | - FIELD_PREP(GENMASK(23, 16), param->max_tu_size) | - FIELD_PREP(GENMASK(15, 8), param->min_cu_size) | - FIELD_PREP(GENMASK(8, 0), param->max_cu_size); - dst[i++] = FIELD_PREP(GENMASK(15, 8), param->max_transfo_depth_intra) | - FIELD_PREP(GENMASK(7, 0), param->max_transfo_depth_inter); - dst[i++] = param->entropy_mode; - dst[i++] = param->wp_mode; - - dst[i++] = param->rate_control_mode; - dst[i++] = param->initial_rem_delay; - dst[i++] = param->cpb_size; - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clk_ratio) | - FIELD_PREP(GENMASK(15, 0), param->framerate); - dst[i++] = param->target_bitrate; - dst[i++] = param->max_bitrate; - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->min_qp) | - FIELD_PREP(GENMASK(15, 0), param->initial_qp); - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->ip_delta) | - FIELD_PREP(GENMASK(15, 0), param->max_qp); - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref) | - FIELD_PREP(GENMASK(15, 0), param->pb_delta); - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | - FIELD_PREP(GENMASK(15, 0), param->golden_delta); - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->rate_control_option; - else - dst[i++] = 0; - - if (version >= MCU_MSG_VERSION_2019_2) { - dst[i++] = param->num_pixel; - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->max_pixel_value) | - FIELD_PREP(GENMASK(15, 0), param->max_psnr); - for (j = 0; j < 3; j++) - dst[i++] = param->maxpicturesize[j]; - } - - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->gop_ctrl_mode; - else - dst[i++] = 0; - - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | - FIELD_PREP(GENMASK(23, 16), param->num_b) | - FIELD_PREP(GENMASK(15, 0), param->gop_length); - dst[i++] = param->freq_idr; - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->enable_lt; - dst[i++] = param->freq_lt; - dst[i++] = param->gdr_mode; - if (version < MCU_MSG_VERSION_2019_2) - dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | - FIELD_PREP(GENMASK(23, 16), param->num_b) | - FIELD_PREP(GENMASK(15, 0), param->gop_length); - - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = param->tmpdqp; - - dst[i++] = param->subframe_latency; - dst[i++] = param->lda_control_mode; - if (version < MCU_MSG_VERSION_2019_2) - dst[i++] = param->unknown41; - - if (version >= MCU_MSG_VERSION_2019_2) { - for (j = 0; j < 6; j++) - dst[i++] = param->lda_factors[j]; - dst[i++] = param->max_num_merge_cand; - } - - return i * sizeof(*dst); -} - -static ssize_t -allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) -{ - enum mcu_msg_version version = msg->header.version; - unsigned int i = 0; - - dst[i++] = msg->user_id; - - if (version >= MCU_MSG_VERSION_2019_2) { - dst[i++] = msg->blob_mcu_addr; - } else { - memcpy(&dst[i], msg->blob, msg->blob_size); - i += msg->blob_size / sizeof(*dst); - } - - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = msg->ep1_addr; - - return i * sizeof(*dst); -} - -ssize_t allegro_decode_config_blob(struct create_channel_param *param, - struct mcu_msg_create_channel_response *msg, - u32 *src) -{ - enum mcu_msg_version version = msg->header.version; - - if (version >= MCU_MSG_VERSION_2019_2) { - param->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[9]); - param->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[9]); - } else { - param->num_ref_idx_l0 = msg->num_ref_idx_l0; - param->num_ref_idx_l1 = msg->num_ref_idx_l1; - } - - return 0; -} - -static ssize_t -allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg) -{ - unsigned int i = 0; - - dst[i++] = msg->channel_id; - - return i * sizeof(*dst); -} - -static ssize_t -allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg) -{ - unsigned int i = 0; - struct mcu_msg_push_buffers_internal_buffer *buffer; - unsigned int num_buffers = msg->num_buffers; - unsigned int j; - - dst[i++] = msg->channel_id; - - for (j = 0; j < num_buffers; j++) { - buffer = &msg->buffer[j]; - dst[i++] = buffer->dma_addr; - dst[i++] = buffer->mcu_addr; - dst[i++] = buffer->size; - } - - return i * sizeof(*dst); -} - -static ssize_t -allegro_enc_put_stream_buffer(u32 *dst, - struct mcu_msg_put_stream_buffer *msg) -{ - unsigned int i = 0; - - dst[i++] = msg->channel_id; - dst[i++] = msg->dma_addr; - dst[i++] = msg->mcu_addr; - dst[i++] = msg->size; - dst[i++] = msg->offset; - dst[i++] = lower_32_bits(msg->stream_id); - dst[i++] = upper_32_bits(msg->stream_id); - - return i * sizeof(*dst); -} - -static ssize_t -allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) -{ - enum mcu_msg_version version = msg->header.version; - unsigned int i = 0; - - dst[i++] = msg->channel_id; - - dst[i++] = msg->reserved; - dst[i++] = msg->encoding_options; - dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) | - FIELD_PREP(GENMASK(15, 0), msg->pps_qp); - - if (version >= MCU_MSG_VERSION_2019_2) { - dst[i++] = 0; - dst[i++] = 0; - dst[i++] = 0; - dst[i++] = 0; - } - - dst[i++] = lower_32_bits(msg->user_param); - dst[i++] = upper_32_bits(msg->user_param); - dst[i++] = lower_32_bits(msg->src_handle); - dst[i++] = upper_32_bits(msg->src_handle); - dst[i++] = msg->request_options; - dst[i++] = msg->src_y; - dst[i++] = msg->src_uv; - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = msg->is_10_bit; - dst[i++] = msg->stride; - if (version >= MCU_MSG_VERSION_2019_2) - dst[i++] = msg->format; - dst[i++] = msg->ep2; - dst[i++] = lower_32_bits(msg->ep2_v); - dst[i++] = upper_32_bits(msg->ep2_v); - - return i * sizeof(*dst); -} - -static ssize_t -allegro_dec_init(struct mcu_msg_init_response *msg, u32 *src) -{ - unsigned int i = 0; - - msg->reserved0 = src[i++]; - - return i * sizeof(*src); -} - -static ssize_t -allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, - u32 *src) -{ - enum mcu_msg_version version = msg->header.version; - unsigned int i = 0; - - msg->channel_id = src[i++]; - msg->user_id = src[i++]; - /* - * Version >= MCU_MSG_VERSION_2019_2 is handled in - * allegro_decode_config_blob(). - */ - if (version < MCU_MSG_VERSION_2019_2) { - msg->options = src[i++]; - msg->num_core = src[i++]; - msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); - msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); - } - msg->int_buffers_count = src[i++]; - msg->int_buffers_size = src[i++]; - msg->rec_buffers_count = src[i++]; - msg->rec_buffers_size = src[i++]; - msg->reserved = src[i++]; - msg->error_code = src[i++]; - - return i * sizeof(*src); -} - -static ssize_t -allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, - u32 *src) -{ - unsigned int i = 0; - - msg->channel_id = src[i++]; - - return i * sizeof(*src); -} - -static ssize_t -allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) -{ - enum mcu_msg_version version = msg->header.version; - unsigned int i = 0; - unsigned int j; - - msg->channel_id = src[i++]; - - msg->stream_id = src[i++]; - msg->stream_id |= (((u64)src[i++]) << 32); - msg->user_param = src[i++]; - msg->user_param |= (((u64)src[i++]) << 32); - msg->src_handle = src[i++]; - msg->src_handle |= (((u64)src[i++]) << 32); - msg->skip = FIELD_GET(GENMASK(31, 16), src[i]); - msg->is_ref = FIELD_GET(GENMASK(15, 0), src[i++]); - msg->initial_removal_delay = src[i++]; - msg->dpb_output_delay = src[i++]; - msg->size = src[i++]; - msg->frame_tag_size = src[i++]; - msg->stuffing = src[i++]; - msg->filler = src[i++]; - msg->num_column = FIELD_GET(GENMASK(31, 16), src[i]); - msg->num_row = FIELD_GET(GENMASK(15, 0), src[i++]); - msg->num_ref_idx_l1 = FIELD_GET(GENMASK(31, 24), src[i]); - msg->num_ref_idx_l0 = FIELD_GET(GENMASK(23, 16), src[i]); - msg->qp = FIELD_GET(GENMASK(15, 0), src[i++]); - msg->partition_table_offset = src[i++]; - msg->partition_table_size = src[i++]; - msg->sum_complex = src[i++]; - for (j = 0; j < 4; j++) - msg->tile_width[j] = src[i++]; - for (j = 0; j < 22; j++) - msg->tile_height[j] = src[i++]; - msg->error_code = src[i++]; - msg->slice_type = src[i++]; - msg->pic_struct = src[i++]; - msg->reserved = FIELD_GET(GENMASK(31, 24), src[i]); - msg->is_last_slice = FIELD_GET(GENMASK(23, 16), src[i]); - msg->is_first_slice = FIELD_GET(GENMASK(15, 8), src[i]); - msg->is_idr = FIELD_GET(GENMASK(7, 0), src[i++]); - - msg->reserved1 = FIELD_GET(GENMASK(31, 16), src[i]); - msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); - - msg->reserved2 = src[i++]; - if (version >= MCU_MSG_VERSION_2019_2) { - msg->reserved3 = src[i++]; - msg->reserved4 = src[i++]; - msg->reserved5 = src[i++]; - msg->reserved6 = src[i++]; - } - - return i * sizeof(*src); -} - -/** - * allegro_encode_mail() - Encode allegro messages to firmware format - * @dst: Pointer to the memory that will be filled with data - * @msg: The allegro message that will be encoded - */ -ssize_t allegro_encode_mail(u32 *dst, void *msg) -{ - const struct mcu_msg_header *header = msg; - ssize_t size; - - if (!msg || !dst) - return -EINVAL; - - switch (header->type) { - case MCU_MSG_TYPE_INIT: - size = allegro_enc_init(&dst[1], msg); - break; - case MCU_MSG_TYPE_CREATE_CHANNEL: - size = allegro_enc_create_channel(&dst[1], msg); - break; - case MCU_MSG_TYPE_DESTROY_CHANNEL: - size = allegro_enc_destroy_channel(&dst[1], msg); - break; - case MCU_MSG_TYPE_ENCODE_FRAME: - size = allegro_enc_encode_frame(&dst[1], msg); - break; - case MCU_MSG_TYPE_PUT_STREAM_BUFFER: - size = allegro_enc_put_stream_buffer(&dst[1], msg); - break; - case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: - case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: - size = allegro_enc_push_buffers(&dst[1], msg); - break; - default: - return -EINVAL; - } - - /* - * The encoded messages might have different length depending on - * the firmware version or certain fields. Therefore, we have to - * set the body length after encoding the message. - */ - dst[0] = FIELD_PREP(GENMASK(31, 16), header->type) | - FIELD_PREP(GENMASK(15, 0), size); - - return size + sizeof(*dst); -} - -/** - * allegro_decode_mail() - Parse allegro messages from the firmware. - * @msg: The mcu_msg_response that will be filled with parsed values. - * @src: Pointer to the memory that will be parsed - * - * The message format in the mailbox depends on the firmware. Parse the - * different formats into a uniform message format that can be used without - * taking care of the firmware version. - */ -int allegro_decode_mail(void *msg, u32 *src) -{ - struct mcu_msg_header *header; - - if (!src || !msg) - return -EINVAL; - - header = msg; - header->type = FIELD_GET(GENMASK(31, 16), src[0]); - - src++; - switch (header->type) { - case MCU_MSG_TYPE_INIT: - allegro_dec_init(msg, src); - break; - case MCU_MSG_TYPE_CREATE_CHANNEL: - allegro_dec_create_channel(msg, src); - break; - case MCU_MSG_TYPE_DESTROY_CHANNEL: - allegro_dec_destroy_channel(msg, src); - break; - case MCU_MSG_TYPE_ENCODE_FRAME: - allegro_dec_encode_frame(msg, src); - break; - default: - return -EINVAL; - } - - return 0; -} diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h deleted file mode 100644 index 486ecb12b098..000000000000 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ /dev/null @@ -1,294 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2019 Pengutronix, Michael Tretter - * - * Allegro VCU firmware mailbox mail definitions - */ - -#ifndef ALLEGRO_MAIL_H -#define ALLEGRO_MAIL_H - -#include - -enum mcu_msg_type { - MCU_MSG_TYPE_INIT = 0x0000, - MCU_MSG_TYPE_CREATE_CHANNEL = 0x0005, - MCU_MSG_TYPE_DESTROY_CHANNEL = 0x0006, - MCU_MSG_TYPE_ENCODE_FRAME = 0x0007, - MCU_MSG_TYPE_PUT_STREAM_BUFFER = 0x0012, - MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE = 0x000e, - MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, -}; - -enum mcu_msg_version { - MCU_MSG_VERSION_2018_2, - MCU_MSG_VERSION_2019_2, -}; - -const char *msg_type_name(enum mcu_msg_type type); - -struct mcu_msg_header { - enum mcu_msg_type type; - enum mcu_msg_version version; -}; - -struct mcu_msg_init_request { - struct mcu_msg_header header; - u32 reserved0; /* maybe a unused channel id */ - u32 suballoc_dma; - u32 suballoc_size; - s32 l2_cache[3]; -}; - -struct mcu_msg_init_response { - struct mcu_msg_header header; - u32 reserved0; -}; - -struct create_channel_param { - enum mcu_msg_version version; - u32 layer_id; - u16 width; - u16 height; - u32 videomode; - u32 format; - u32 colorspace; - u32 src_mode; - u32 src_bit_depth; - u8 profile; - u16 constraint_set_flags; - u32 codec; - u16 level; - u16 tier; - u32 log2_max_poc; - u32 log2_max_frame_num; - u32 temporal_mvp_enable; - u32 enable_reordering; - u32 dbf_ovr_en; - u32 num_ref_idx_l0; - u32 num_ref_idx_l1; - u32 custom_lda; - u32 rdo_cost_mode; - u32 lf; - u32 lf_x_tile; - u32 lf_x_slice; - s8 beta_offset; - s8 tc_offset; - u16 reserved10; - u32 unknown11; - u32 unknown12; - u16 num_slices; - u16 prefetch_auto; - u32 prefetch_mem_offset; - u32 prefetch_mem_size; - u16 clip_hrz_range; - u16 clip_vrt_range; - u16 me_range[4]; - u8 max_cu_size; - u8 min_cu_size; - u8 max_tu_size; - u8 min_tu_size; - u8 max_transfo_depth_inter; - u8 max_transfo_depth_intra; - u16 reserved20; - u32 entropy_mode; - u32 wp_mode; - - /* rate control param */ - u32 rate_control_mode; - u32 initial_rem_delay; - u32 cpb_size; - u16 framerate; - u16 clk_ratio; - u32 target_bitrate; - u32 max_bitrate; - u16 initial_qp; - u16 min_qp; - u16 max_qp; - s16 ip_delta; - s16 pb_delta; - u16 golden_ref; - u16 golden_delta; - u16 golden_ref_frequency; - u32 rate_control_option; - u32 num_pixel; - u16 max_psnr; - u16 max_pixel_value; - u32 maxpicturesize[3]; - - /* gop param */ - u32 gop_ctrl_mode; - u32 freq_idr; - u32 freq_lt; - u32 gdr_mode; - u16 gop_length; - u8 num_b; - u8 freq_golden_ref; - u32 enable_lt; - u32 tmpdqp; - - u32 subframe_latency; - u32 lda_control_mode; - u32 unknown41; - - u32 lda_factors[6]; - - u32 max_num_merge_cand; -}; - -struct mcu_msg_create_channel { - struct mcu_msg_header header; - u32 user_id; - u32 *blob; - size_t blob_size; - u32 blob_mcu_addr; - u32 ep1_addr; -}; - -struct mcu_msg_create_channel_response { - struct mcu_msg_header header; - u32 channel_id; - u32 user_id; - u32 options; - u32 num_core; - u32 num_ref_idx_l0; - u32 num_ref_idx_l1; - u32 int_buffers_count; - u32 int_buffers_size; - u32 rec_buffers_count; - u32 rec_buffers_size; - u32 reserved; - u32 error_code; -}; - -struct mcu_msg_destroy_channel { - struct mcu_msg_header header; - u32 channel_id; -}; - -struct mcu_msg_destroy_channel_response { - struct mcu_msg_header header; - u32 channel_id; -}; - -struct mcu_msg_push_buffers_internal_buffer { - u32 dma_addr; - u32 mcu_addr; - u32 size; -}; - -struct mcu_msg_push_buffers_internal { - struct mcu_msg_header header; - u32 channel_id; - size_t num_buffers; - struct mcu_msg_push_buffers_internal_buffer buffer[]; -}; - -struct mcu_msg_put_stream_buffer { - struct mcu_msg_header header; - u32 channel_id; - u32 dma_addr; - u32 mcu_addr; - u32 size; - u32 offset; - u64 stream_id; -}; - -struct mcu_msg_encode_frame { - struct mcu_msg_header header; - u32 channel_id; - u32 reserved; - - u32 encoding_options; -#define AL_OPT_USE_QP_TABLE BIT(0) -#define AL_OPT_FORCE_LOAD BIT(1) -#define AL_OPT_USE_L2 BIT(2) -#define AL_OPT_DISABLE_INTRA BIT(3) -#define AL_OPT_DEPENDENT_SLICES BIT(4) - - s16 pps_qp; - u16 padding; - u64 user_param; - u64 src_handle; - - u32 request_options; -#define AL_OPT_SCENE_CHANGE BIT(0) -#define AL_OPT_RESTART_GOP BIT(1) -#define AL_OPT_USE_LONG_TERM BIT(2) -#define AL_OPT_UPDATE_PARAMS BIT(3) - - /* u32 scene_change_delay (optional) */ - /* rate control param (optional) */ - /* gop param (optional) */ - /* dynamic resolution params (optional) */ - u32 src_y; - u32 src_uv; - u32 is_10_bit; - u32 stride; - u32 format; - u32 ep2; - u64 ep2_v; -}; - -struct mcu_msg_encode_frame_response { - struct mcu_msg_header header; - u32 channel_id; - u64 stream_id; /* see mcu_msg_put_stream_buffer */ - u64 user_param; /* see mcu_msg_encode_frame */ - u64 src_handle; /* see mcu_msg_encode_frame */ - u16 skip; - u16 is_ref; - u32 initial_removal_delay; - u32 dpb_output_delay; - u32 size; - u32 frame_tag_size; - s32 stuffing; - s32 filler; - u16 num_column; - u16 num_row; - u16 qp; - u8 num_ref_idx_l0; - u8 num_ref_idx_l1; - u32 partition_table_offset; - s32 partition_table_size; - u32 sum_complex; - s32 tile_width[4]; - s32 tile_height[22]; - u32 error_code; - - u32 slice_type; -#define AL_ENC_SLICE_TYPE_B 0 -#define AL_ENC_SLICE_TYPE_P 1 -#define AL_ENC_SLICE_TYPE_I 2 - - u32 pic_struct; - u8 is_idr; - u8 is_first_slice; - u8 is_last_slice; - u8 reserved; - u16 pps_qp; - u16 reserved1; - u32 reserved2; - u32 reserved3; - u32 reserved4; - u32 reserved5; - u32 reserved6; -}; - -union mcu_msg_response { - struct mcu_msg_header header; - struct mcu_msg_init_response init; - struct mcu_msg_create_channel_response create_channel; - struct mcu_msg_destroy_channel_response destroy_channel; - struct mcu_msg_encode_frame_response encode_frame; -}; - -ssize_t allegro_encode_config_blob(u32 *dst, struct create_channel_param *param); -ssize_t allegro_decode_config_blob(struct create_channel_param *param, - struct mcu_msg_create_channel_response *msg, - u32 *src); - -int allegro_decode_mail(void *msg, u32 *src); -ssize_t allegro_encode_mail(u32 *dst, void *msg); - -#endif diff --git a/drivers/staging/media/allegro-dvt/nal-h264.c b/drivers/staging/media/allegro-dvt/nal-h264.c deleted file mode 100644 index bd48b8883572..000000000000 --- a/drivers/staging/media/allegro-dvt/nal-h264.c +++ /dev/null @@ -1,1001 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2019 Pengutronix, Michael Tretter - * - * Convert NAL units between raw byte sequence payloads (RBSP) and C structs - * - * The conversion is defined in "ITU-T Rec. H.264 (04/2017) Advanced video - * coding for generic audiovisual services". Decoder drivers may use the - * parser to parse RBSP from encoded streams and configure the hardware, if - * the hardware is not able to parse RBSP itself. Encoder drivers may use the - * generator to generate the RBSP for SPS/PPS nal units and add them to the - * encoded stream if the hardware does not generate the units. - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include "nal-h264.h" - -/* - * See Rec. ITU-T H.264 (04/2017) Table 7-1 – NAL unit type codes, syntax - * element categories, and NAL unit type classes - */ -enum nal_unit_type { - SEQUENCE_PARAMETER_SET = 7, - PICTURE_PARAMETER_SET = 8, - FILLER_DATA = 12, -}; - -struct rbsp; - -struct nal_h264_ops { - int (*rbsp_bit)(struct rbsp *rbsp, int *val); - int (*rbsp_bits)(struct rbsp *rbsp, int n, unsigned int *val); - int (*rbsp_uev)(struct rbsp *rbsp, unsigned int *val); - int (*rbsp_sev)(struct rbsp *rbsp, int *val); -}; - -/** - * struct rbsp - State object for handling a raw byte sequence payload - * @data: pointer to the data of the rbsp - * @size: maximum size of the data of the rbsp - * @pos: current bit position inside the rbsp - * @num_consecutive_zeros: number of zeros before @pos - * @ops: per datatype functions for interacting with the rbsp - * @error: an error occurred while handling the rbsp - * - * This struct is passed around the various parsing functions and tracks the - * current position within the raw byte sequence payload. - * - * The @ops field allows to separate the operation, i.e., reading/writing a - * value from/to that rbsp, from the structure of the NAL unit. This allows to - * have a single function for iterating the NAL unit, while @ops has function - * pointers for handling each type in the rbsp. - */ -struct rbsp { - u8 *data; - size_t size; - unsigned int pos; - unsigned int num_consecutive_zeros; - struct nal_h264_ops *ops; - int error; -}; - -static void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, - struct nal_h264_ops *ops) -{ - if (!rbsp) - return; - - rbsp->data = addr; - rbsp->size = size; - rbsp->pos = 0; - rbsp->ops = ops; - rbsp->error = 0; -} - -/** - * nal_h264_profile_from_v4l2() - Get profile_idc for v4l2 h264 profile - * @profile: the profile as &enum v4l2_mpeg_video_h264_profile - * - * Convert the &enum v4l2_mpeg_video_h264_profile to profile_idc as specified - * in Rec. ITU-T H.264 (04/2017) A.2. - * - * Return: the profile_idc for the passed level - */ -int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile) -{ - switch (profile) { - case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: - return 66; - case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: - return 77; - case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: - return 88; - case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: - return 100; - default: - return -EINVAL; - } -} - -/** - * nal_h264_level_from_v4l2() - Get level_idc for v4l2 h264 level - * @level: the level as &enum v4l2_mpeg_video_h264_level - * - * Convert the &enum v4l2_mpeg_video_h264_level to level_idc as specified in - * Rec. ITU-T H.264 (04/2017) A.3.2. - * - * Return: the level_idc for the passed level - */ -int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level) -{ - switch (level) { - case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: - return 10; - case V4L2_MPEG_VIDEO_H264_LEVEL_1B: - return 9; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: - return 11; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: - return 12; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: - return 13; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: - return 20; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: - return 21; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: - return 22; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: - return 30; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: - return 31; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: - return 32; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: - return 40; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: - return 41; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: - return 42; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: - return 50; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: - return 51; - default: - return -EINVAL; - } -} - -static int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value); -static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value); - -/* - * When reading or writing, the emulation_prevention_three_byte is detected - * only when the 2 one bits need to be inserted. Therefore, we are not - * actually adding the 0x3 byte, but the 2 one bits and the six 0 bits of the - * next byte. - */ -#define EMULATION_PREVENTION_THREE_BYTE (0x3 << 6) - -static int add_emulation_prevention_three_byte(struct rbsp *rbsp) -{ - rbsp->num_consecutive_zeros = 0; - rbsp_write_bits(rbsp, 8, EMULATION_PREVENTION_THREE_BYTE); - - return 0; -} - -static int discard_emulation_prevention_three_byte(struct rbsp *rbsp) -{ - unsigned int tmp = 0; - - rbsp->num_consecutive_zeros = 0; - rbsp_read_bits(rbsp, 8, &tmp); - if (tmp != EMULATION_PREVENTION_THREE_BYTE) - return -EINVAL; - - return 0; -} - -static inline int rbsp_read_bit(struct rbsp *rbsp) -{ - int shift; - int ofs; - int bit; - int err; - - if (rbsp->num_consecutive_zeros == 22) { - err = discard_emulation_prevention_three_byte(rbsp); - if (err) - return err; - } - - shift = 7 - (rbsp->pos % 8); - ofs = rbsp->pos / 8; - if (ofs >= rbsp->size) - return -EINVAL; - - bit = (rbsp->data[ofs] >> shift) & 1; - - rbsp->pos++; - - if (bit == 1 || - (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) - rbsp->num_consecutive_zeros = 0; - else - rbsp->num_consecutive_zeros++; - - return bit; -} - -static inline int rbsp_write_bit(struct rbsp *rbsp, bool value) -{ - int shift; - int ofs; - - if (rbsp->num_consecutive_zeros == 22) - add_emulation_prevention_three_byte(rbsp); - - shift = 7 - (rbsp->pos % 8); - ofs = rbsp->pos / 8; - if (ofs >= rbsp->size) - return -EINVAL; - - rbsp->data[ofs] &= ~(1 << shift); - rbsp->data[ofs] |= value << shift; - - rbsp->pos++; - - if (value || - (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) { - rbsp->num_consecutive_zeros = 0; - } else { - rbsp->num_consecutive_zeros++; - } - - return 0; -} - -static inline int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value) -{ - int i; - int bit; - unsigned int tmp = 0; - - if (n > 8 * sizeof(*value)) - return -EINVAL; - - for (i = n; i > 0; i--) { - bit = rbsp_read_bit(rbsp); - if (bit < 0) - return bit; - tmp |= bit << (i - 1); - } - - if (value) - *value = tmp; - - return 0; -} - -static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value) -{ - int ret; - - if (n > 8 * sizeof(value)) - return -EINVAL; - - while (n--) { - ret = rbsp_write_bit(rbsp, (value >> n) & 1); - if (ret) - return ret; - } - - return 0; -} - -static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *value) -{ - int leading_zero_bits = 0; - unsigned int tmp = 0; - int ret; - - while ((ret = rbsp_read_bit(rbsp)) == 0) - leading_zero_bits++; - if (ret < 0) - return ret; - - if (leading_zero_bits > 0) { - ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); - if (ret) - return ret; - } - - if (value) - *value = (1 << leading_zero_bits) - 1 + tmp; - - return 0; -} - -static int rbsp_write_uev(struct rbsp *rbsp, unsigned int *value) -{ - int ret; - int leading_zero_bits; - - if (!value) - return -EINVAL; - - leading_zero_bits = ilog2(*value + 1); - - ret = rbsp_write_bits(rbsp, leading_zero_bits, 0); - if (ret) - return ret; - - return rbsp_write_bits(rbsp, leading_zero_bits + 1, *value + 1); -} - -static int rbsp_read_sev(struct rbsp *rbsp, int *value) -{ - int ret; - unsigned int tmp; - - ret = rbsp_read_uev(rbsp, &tmp); - if (ret) - return ret; - - if (value) { - if (tmp & 1) - *value = (tmp + 1) / 2; - else - *value = -(tmp / 2); - } - - return 0; -} - -static int rbsp_write_sev(struct rbsp *rbsp, int *value) -{ - unsigned int tmp; - - if (!value) - return -EINVAL; - - if (*value > 0) - tmp = (2 * (*value)) | 1; - else - tmp = -2 * (*value); - - return rbsp_write_uev(rbsp, &tmp); -} - -static int __rbsp_write_bit(struct rbsp *rbsp, int *value) -{ - return rbsp_write_bit(rbsp, *value); -} - -static int __rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int *value) -{ - return rbsp_write_bits(rbsp, n, *value); -} - -static struct nal_h264_ops write = { - .rbsp_bit = __rbsp_write_bit, - .rbsp_bits = __rbsp_write_bits, - .rbsp_uev = rbsp_write_uev, - .rbsp_sev = rbsp_write_sev, -}; - -static int __rbsp_read_bit(struct rbsp *rbsp, int *value) -{ - int tmp = rbsp_read_bit(rbsp); - - if (tmp < 0) - return tmp; - *value = tmp; - - return 0; -} - -static struct nal_h264_ops read = { - .rbsp_bit = __rbsp_read_bit, - .rbsp_bits = rbsp_read_bits, - .rbsp_uev = rbsp_read_uev, - .rbsp_sev = rbsp_read_sev, -}; - -static inline void rbsp_bit(struct rbsp *rbsp, int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_bit(rbsp, value); -} - -static inline void rbsp_bits(struct rbsp *rbsp, int n, int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_bits(rbsp, n, value); -} - -static inline void rbsp_uev(struct rbsp *rbsp, unsigned int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_uev(rbsp, value); -} - -static inline void rbsp_sev(struct rbsp *rbsp, int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_sev(rbsp, value); -} - -static void nal_h264_rbsp_trailing_bits(struct rbsp *rbsp) -{ - unsigned int rbsp_stop_one_bit = 1; - unsigned int rbsp_alignment_zero_bit = 0; - - rbsp_bit(rbsp, &rbsp_stop_one_bit); - rbsp_bits(rbsp, round_up(rbsp->pos, 8) - rbsp->pos, - &rbsp_alignment_zero_bit); -} - -static void nal_h264_write_start_code_prefix(struct rbsp *rbsp) -{ - u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); - int i = 4; - - if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { - rbsp->error = -EINVAL; - return; - } - - p[0] = 0x00; - p[1] = 0x00; - p[2] = 0x00; - p[3] = 0x01; - - rbsp->pos += i * 8; -} - -static void nal_h264_read_start_code_prefix(struct rbsp *rbsp) -{ - u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); - int i = 4; - - if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { - rbsp->error = -EINVAL; - return; - } - - if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x00 || p[3] != 0x01) { - rbsp->error = -EINVAL; - return; - } - - rbsp->pos += i * 8; -} - -static void nal_h264_write_filler_data(struct rbsp *rbsp) -{ - u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); - int i; - - /* Keep 1 byte extra for terminating the NAL unit */ - i = rbsp->size - DIV_ROUND_UP(rbsp->pos, 8) - 1; - memset(p, 0xff, i); - rbsp->pos += i * 8; -} - -static void nal_h264_read_filler_data(struct rbsp *rbsp) -{ - u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); - - while (*p == 0xff) { - if (DIV_ROUND_UP(rbsp->pos, 8) > rbsp->size) { - rbsp->error = -EINVAL; - return; - } - - p++; - rbsp->pos += 8; - } -} - -static void nal_h264_rbsp_hrd_parameters(struct rbsp *rbsp, - struct nal_h264_hrd_parameters *hrd) -{ - unsigned int i; - - if (!hrd) { - rbsp->error = -EINVAL; - return; - } - - rbsp_uev(rbsp, &hrd->cpb_cnt_minus1); - rbsp_bits(rbsp, 4, &hrd->bit_rate_scale); - rbsp_bits(rbsp, 4, &hrd->cpb_size_scale); - - for (i = 0; i <= hrd->cpb_cnt_minus1; i++) { - rbsp_uev(rbsp, &hrd->bit_rate_value_minus1[i]); - rbsp_uev(rbsp, &hrd->cpb_size_value_minus1[i]); - rbsp_bit(rbsp, &hrd->cbr_flag[i]); - } - - rbsp_bits(rbsp, 5, &hrd->initial_cpb_removal_delay_length_minus1); - rbsp_bits(rbsp, 5, &hrd->cpb_removal_delay_length_minus1); - rbsp_bits(rbsp, 5, &hrd->dpb_output_delay_length_minus1); - rbsp_bits(rbsp, 5, &hrd->time_offset_length); -} - -static void nal_h264_rbsp_vui_parameters(struct rbsp *rbsp, - struct nal_h264_vui_parameters *vui) -{ - if (!vui) { - rbsp->error = -EINVAL; - return; - } - - rbsp_bit(rbsp, &vui->aspect_ratio_info_present_flag); - if (vui->aspect_ratio_info_present_flag) { - rbsp_bits(rbsp, 8, &vui->aspect_ratio_idc); - if (vui->aspect_ratio_idc == 255) { - rbsp_bits(rbsp, 16, &vui->sar_width); - rbsp_bits(rbsp, 16, &vui->sar_height); - } - } - - rbsp_bit(rbsp, &vui->overscan_info_present_flag); - if (vui->overscan_info_present_flag) - rbsp_bit(rbsp, &vui->overscan_appropriate_flag); - - rbsp_bit(rbsp, &vui->video_signal_type_present_flag); - if (vui->video_signal_type_present_flag) { - rbsp_bits(rbsp, 3, &vui->video_format); - rbsp_bit(rbsp, &vui->video_full_range_flag); - - rbsp_bit(rbsp, &vui->colour_description_present_flag); - if (vui->colour_description_present_flag) { - rbsp_bits(rbsp, 8, &vui->colour_primaries); - rbsp_bits(rbsp, 8, &vui->transfer_characteristics); - rbsp_bits(rbsp, 8, &vui->matrix_coefficients); - } - } - - rbsp_bit(rbsp, &vui->chroma_loc_info_present_flag); - if (vui->chroma_loc_info_present_flag) { - rbsp_uev(rbsp, &vui->chroma_sample_loc_type_top_field); - rbsp_uev(rbsp, &vui->chroma_sample_loc_type_bottom_field); - } - - rbsp_bit(rbsp, &vui->timing_info_present_flag); - if (vui->timing_info_present_flag) { - rbsp_bits(rbsp, 32, &vui->num_units_in_tick); - rbsp_bits(rbsp, 32, &vui->time_scale); - rbsp_bit(rbsp, &vui->fixed_frame_rate_flag); - } - - rbsp_bit(rbsp, &vui->nal_hrd_parameters_present_flag); - if (vui->nal_hrd_parameters_present_flag) - nal_h264_rbsp_hrd_parameters(rbsp, &vui->nal_hrd_parameters); - - rbsp_bit(rbsp, &vui->vcl_hrd_parameters_present_flag); - if (vui->vcl_hrd_parameters_present_flag) - nal_h264_rbsp_hrd_parameters(rbsp, &vui->vcl_hrd_parameters); - - if (vui->nal_hrd_parameters_present_flag || - vui->vcl_hrd_parameters_present_flag) - rbsp_bit(rbsp, &vui->low_delay_hrd_flag); - - rbsp_bit(rbsp, &vui->pic_struct_present_flag); - - rbsp_bit(rbsp, &vui->bitstream_restriction_flag); - if (vui->bitstream_restriction_flag) { - rbsp_bit(rbsp, &vui->motion_vectors_over_pic_boundaries_flag); - rbsp_uev(rbsp, &vui->max_bytes_per_pic_denom); - rbsp_uev(rbsp, &vui->max_bits_per_mb_denom); - rbsp_uev(rbsp, &vui->log2_max_mv_length_horizontal); - rbsp_uev(rbsp, &vui->log21_max_mv_length_vertical); - rbsp_uev(rbsp, &vui->max_num_reorder_frames); - rbsp_uev(rbsp, &vui->max_dec_frame_buffering); - } -} - -static void nal_h264_rbsp_sps(struct rbsp *rbsp, struct nal_h264_sps *sps) -{ - unsigned int i; - - if (!sps) { - rbsp->error = -EINVAL; - return; - } - - rbsp_bits(rbsp, 8, &sps->profile_idc); - rbsp_bit(rbsp, &sps->constraint_set0_flag); - rbsp_bit(rbsp, &sps->constraint_set1_flag); - rbsp_bit(rbsp, &sps->constraint_set2_flag); - rbsp_bit(rbsp, &sps->constraint_set3_flag); - rbsp_bit(rbsp, &sps->constraint_set4_flag); - rbsp_bit(rbsp, &sps->constraint_set5_flag); - rbsp_bits(rbsp, 2, &sps->reserved_zero_2bits); - rbsp_bits(rbsp, 8, &sps->level_idc); - - rbsp_uev(rbsp, &sps->seq_parameter_set_id); - - if (sps->profile_idc == 100 || sps->profile_idc == 110 || - sps->profile_idc == 122 || sps->profile_idc == 244 || - sps->profile_idc == 44 || sps->profile_idc == 83 || - sps->profile_idc == 86 || sps->profile_idc == 118 || - sps->profile_idc == 128 || sps->profile_idc == 138 || - sps->profile_idc == 139 || sps->profile_idc == 134 || - sps->profile_idc == 135) { - rbsp_uev(rbsp, &sps->chroma_format_idc); - - if (sps->chroma_format_idc == 3) - rbsp_bit(rbsp, &sps->separate_colour_plane_flag); - rbsp_uev(rbsp, &sps->bit_depth_luma_minus8); - rbsp_uev(rbsp, &sps->bit_depth_chroma_minus8); - rbsp_bit(rbsp, &sps->qpprime_y_zero_transform_bypass_flag); - rbsp_bit(rbsp, &sps->seq_scaling_matrix_present_flag); - if (sps->seq_scaling_matrix_present_flag) - rbsp->error = -EINVAL; - } - - rbsp_uev(rbsp, &sps->log2_max_frame_num_minus4); - - rbsp_uev(rbsp, &sps->pic_order_cnt_type); - switch (sps->pic_order_cnt_type) { - case 0: - rbsp_uev(rbsp, &sps->log2_max_pic_order_cnt_lsb_minus4); - break; - case 1: - rbsp_bit(rbsp, &sps->delta_pic_order_always_zero_flag); - rbsp_sev(rbsp, &sps->offset_for_non_ref_pic); - rbsp_sev(rbsp, &sps->offset_for_top_to_bottom_field); - - rbsp_uev(rbsp, &sps->num_ref_frames_in_pic_order_cnt_cycle); - for (i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++) - rbsp_sev(rbsp, &sps->offset_for_ref_frame[i]); - break; - default: - rbsp->error = -EINVAL; - break; - } - - rbsp_uev(rbsp, &sps->max_num_ref_frames); - rbsp_bit(rbsp, &sps->gaps_in_frame_num_value_allowed_flag); - rbsp_uev(rbsp, &sps->pic_width_in_mbs_minus1); - rbsp_uev(rbsp, &sps->pic_height_in_map_units_minus1); - - rbsp_bit(rbsp, &sps->frame_mbs_only_flag); - if (!sps->frame_mbs_only_flag) - rbsp_bit(rbsp, &sps->mb_adaptive_frame_field_flag); - - rbsp_bit(rbsp, &sps->direct_8x8_inference_flag); - - rbsp_bit(rbsp, &sps->frame_cropping_flag); - if (sps->frame_cropping_flag) { - rbsp_uev(rbsp, &sps->crop_left); - rbsp_uev(rbsp, &sps->crop_right); - rbsp_uev(rbsp, &sps->crop_top); - rbsp_uev(rbsp, &sps->crop_bottom); - } - - rbsp_bit(rbsp, &sps->vui_parameters_present_flag); - if (sps->vui_parameters_present_flag) - nal_h264_rbsp_vui_parameters(rbsp, &sps->vui); -} - -static void nal_h264_rbsp_pps(struct rbsp *rbsp, struct nal_h264_pps *pps) -{ - int i; - - rbsp_uev(rbsp, &pps->pic_parameter_set_id); - rbsp_uev(rbsp, &pps->seq_parameter_set_id); - rbsp_bit(rbsp, &pps->entropy_coding_mode_flag); - rbsp_bit(rbsp, &pps->bottom_field_pic_order_in_frame_present_flag); - rbsp_uev(rbsp, &pps->num_slice_groups_minus1); - if (pps->num_slice_groups_minus1 > 0) { - rbsp_uev(rbsp, &pps->slice_group_map_type); - switch (pps->slice_group_map_type) { - case 0: - for (i = 0; i < pps->num_slice_groups_minus1; i++) - rbsp_uev(rbsp, &pps->run_length_minus1[i]); - break; - case 2: - for (i = 0; i < pps->num_slice_groups_minus1; i++) { - rbsp_uev(rbsp, &pps->top_left[i]); - rbsp_uev(rbsp, &pps->bottom_right[i]); - } - break; - case 3: case 4: case 5: - rbsp_bit(rbsp, &pps->slice_group_change_direction_flag); - rbsp_uev(rbsp, &pps->slice_group_change_rate_minus1); - break; - case 6: - rbsp_uev(rbsp, &pps->pic_size_in_map_units_minus1); - for (i = 0; i < pps->pic_size_in_map_units_minus1; i++) - rbsp_bits(rbsp, - order_base_2(pps->num_slice_groups_minus1 + 1), - &pps->slice_group_id[i]); - break; - default: - break; - } - } - rbsp_uev(rbsp, &pps->num_ref_idx_l0_default_active_minus1); - rbsp_uev(rbsp, &pps->num_ref_idx_l1_default_active_minus1); - rbsp_bit(rbsp, &pps->weighted_pred_flag); - rbsp_bits(rbsp, 2, &pps->weighted_bipred_idc); - rbsp_sev(rbsp, &pps->pic_init_qp_minus26); - rbsp_sev(rbsp, &pps->pic_init_qs_minus26); - rbsp_sev(rbsp, &pps->chroma_qp_index_offset); - rbsp_bit(rbsp, &pps->deblocking_filter_control_present_flag); - rbsp_bit(rbsp, &pps->constrained_intra_pred_flag); - rbsp_bit(rbsp, &pps->redundant_pic_cnt_present_flag); - if (/* more_rbsp_data() */ false) { - rbsp_bit(rbsp, &pps->transform_8x8_mode_flag); - rbsp_bit(rbsp, &pps->pic_scaling_matrix_present_flag); - if (pps->pic_scaling_matrix_present_flag) - rbsp->error = -EINVAL; - rbsp_sev(rbsp, &pps->second_chroma_qp_index_offset); - } -} - -/** - * nal_h264_write_sps() - Write SPS NAL unit into RBSP format - * @dev: device pointer - * @dest: the buffer that is filled with RBSP data - * @n: maximum size of @dest in bytes - * @sps: &struct nal_h264_sps to convert to RBSP - * - * Convert @sps to RBSP data and write it into @dest. - * - * The size of the SPS NAL unit is not known in advance and this function will - * fail, if @dest does not hold sufficient space for the SPS NAL unit. - * - * Return: number of bytes written to @dest or negative error code - */ -ssize_t nal_h264_write_sps(const struct device *dev, - void *dest, size_t n, struct nal_h264_sps *sps) -{ - struct rbsp rbsp; - unsigned int forbidden_zero_bit = 0; - unsigned int nal_ref_idc = 0; - unsigned int nal_unit_type = SEQUENCE_PARAMETER_SET; - - if (!dest) - return -EINVAL; - - rbsp_init(&rbsp, dest, n, &write); - - nal_h264_write_start_code_prefix(&rbsp); - - rbsp_bit(&rbsp, &forbidden_zero_bit); - rbsp_bits(&rbsp, 2, &nal_ref_idc); - rbsp_bits(&rbsp, 5, &nal_unit_type); - - nal_h264_rbsp_sps(&rbsp, sps); - - nal_h264_rbsp_trailing_bits(&rbsp); - - if (rbsp.error) - return rbsp.error; - - return DIV_ROUND_UP(rbsp.pos, 8); -} -EXPORT_SYMBOL_GPL(nal_h264_write_sps); - -/** - * nal_h264_read_sps() - Read SPS NAL unit from RBSP format - * @dev: device pointer - * @sps: the &struct nal_h264_sps to fill from the RBSP data - * @src: the buffer that contains the RBSP data - * @n: size of @src in bytes - * - * Read RBSP data from @src and use it to fill @sps. - * - * Return: number of bytes read from @src or negative error code - */ -ssize_t nal_h264_read_sps(const struct device *dev, - struct nal_h264_sps *sps, void *src, size_t n) -{ - struct rbsp rbsp; - unsigned int forbidden_zero_bit; - unsigned int nal_ref_idc; - unsigned int nal_unit_type; - - if (!src) - return -EINVAL; - - rbsp_init(&rbsp, src, n, &read); - - nal_h264_read_start_code_prefix(&rbsp); - - rbsp_bit(&rbsp, &forbidden_zero_bit); - rbsp_bits(&rbsp, 2, &nal_ref_idc); - rbsp_bits(&rbsp, 5, &nal_unit_type); - - if (rbsp.error || - forbidden_zero_bit != 0 || - nal_ref_idc != 0 || - nal_unit_type != SEQUENCE_PARAMETER_SET) - return -EINVAL; - - nal_h264_rbsp_sps(&rbsp, sps); - - nal_h264_rbsp_trailing_bits(&rbsp); - - if (rbsp.error) - return rbsp.error; - - return DIV_ROUND_UP(rbsp.pos, 8); -} -EXPORT_SYMBOL_GPL(nal_h264_read_sps); - -/** - * nal_h264_write_pps() - Write PPS NAL unit into RBSP format - * @dev: device pointer - * @dest: the buffer that is filled with RBSP data - * @n: maximum size of @dest in bytes - * @pps: &struct nal_h264_pps to convert to RBSP - * - * Convert @pps to RBSP data and write it into @dest. - * - * The size of the PPS NAL unit is not known in advance and this function will - * fail, if @dest does not hold sufficient space for the PPS NAL unit. - * - * Return: number of bytes written to @dest or negative error code - */ -ssize_t nal_h264_write_pps(const struct device *dev, - void *dest, size_t n, struct nal_h264_pps *pps) -{ - struct rbsp rbsp; - unsigned int forbidden_zero_bit = 0; - unsigned int nal_ref_idc = 0; - unsigned int nal_unit_type = PICTURE_PARAMETER_SET; - - if (!dest) - return -EINVAL; - - rbsp_init(&rbsp, dest, n, &write); - - nal_h264_write_start_code_prefix(&rbsp); - - /* NAL unit header */ - rbsp_bit(&rbsp, &forbidden_zero_bit); - rbsp_bits(&rbsp, 2, &nal_ref_idc); - rbsp_bits(&rbsp, 5, &nal_unit_type); - - nal_h264_rbsp_pps(&rbsp, pps); - - nal_h264_rbsp_trailing_bits(&rbsp); - - if (rbsp.error) - return rbsp.error; - - return DIV_ROUND_UP(rbsp.pos, 8); -} -EXPORT_SYMBOL_GPL(nal_h264_write_pps); - -/** - * nal_h264_read_pps() - Read PPS NAL unit from RBSP format - * @dev: device pointer - * @pps: the &struct nal_h264_pps to fill from the RBSP data - * @src: the buffer that contains the RBSP data - * @n: size of @src in bytes - * - * Read RBSP data from @src and use it to fill @pps. - * - * Return: number of bytes read from @src or negative error code - */ -ssize_t nal_h264_read_pps(const struct device *dev, - struct nal_h264_pps *pps, void *src, size_t n) -{ - struct rbsp rbsp; - - if (!src) - return -EINVAL; - - rbsp_init(&rbsp, src, n, &read); - - nal_h264_read_start_code_prefix(&rbsp); - - /* NAL unit header */ - rbsp.pos += 8; - - nal_h264_rbsp_pps(&rbsp, pps); - - nal_h264_rbsp_trailing_bits(&rbsp); - - if (rbsp.error) - return rbsp.error; - - return DIV_ROUND_UP(rbsp.pos, 8); -} -EXPORT_SYMBOL_GPL(nal_h264_read_pps); - -/** - * nal_h264_write_filler() - Write filler data RBSP - * @dev: device pointer - * @dest: buffer to fill with filler data - * @n: size of the buffer to fill with filler data - * - * Write a filler data RBSP to @dest with a size of @n bytes and return the - * number of written filler data bytes. - * - * Use this function to generate dummy data in an RBSP data stream that can be - * safely ignored by h264 decoders. - * - * The RBSP format of the filler data is specified in Rec. ITU-T H.264 - * (04/2017) 7.3.2.7 Filler data RBSP syntax. - * - * Return: number of filler data bytes (including marker) or negative error - */ -ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n) -{ - struct rbsp rbsp; - unsigned int forbidden_zero_bit = 0; - unsigned int nal_ref_idc = 0; - unsigned int nal_unit_type = FILLER_DATA; - - if (!dest) - return -EINVAL; - - rbsp_init(&rbsp, dest, n, &write); - - nal_h264_write_start_code_prefix(&rbsp); - - rbsp_bit(&rbsp, &forbidden_zero_bit); - rbsp_bits(&rbsp, 2, &nal_ref_idc); - rbsp_bits(&rbsp, 5, &nal_unit_type); - - nal_h264_write_filler_data(&rbsp); - - nal_h264_rbsp_trailing_bits(&rbsp); - - return DIV_ROUND_UP(rbsp.pos, 8); -} -EXPORT_SYMBOL_GPL(nal_h264_write_filler); - -/** - * nal_h264_read_filler() - Read filler data RBSP - * @dev: device pointer - * @src: buffer with RBSP data that is read - * @n: maximum size of src that shall be read - * - * Read a filler data RBSP from @src up to a maximum size of @n bytes and - * return the size of the filler data in bytes including the marker. - * - * This function is used to parse filler data and skip the respective bytes in - * the RBSP data. - * - * The RBSP format of the filler data is specified in Rec. ITU-T H.264 - * (04/2017) 7.3.2.7 Filler data RBSP syntax. - * - * Return: number of filler data bytes (including marker) or negative error - */ -ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n) -{ - struct rbsp rbsp; - unsigned int forbidden_zero_bit; - unsigned int nal_ref_idc; - unsigned int nal_unit_type; - - if (!src) - return -EINVAL; - - rbsp_init(&rbsp, src, n, &read); - - nal_h264_read_start_code_prefix(&rbsp); - - rbsp_bit(&rbsp, &forbidden_zero_bit); - rbsp_bits(&rbsp, 2, &nal_ref_idc); - rbsp_bits(&rbsp, 5, &nal_unit_type); - - if (rbsp.error) - return rbsp.error; - if (forbidden_zero_bit != 0 || - nal_ref_idc != 0 || - nal_unit_type != FILLER_DATA) - return -EINVAL; - - nal_h264_read_filler_data(&rbsp); - nal_h264_rbsp_trailing_bits(&rbsp); - - if (rbsp.error) - return rbsp.error; - - return DIV_ROUND_UP(rbsp.pos, 8); -} -EXPORT_SYMBOL_GPL(nal_h264_read_filler); diff --git a/drivers/staging/media/allegro-dvt/nal-h264.h b/drivers/staging/media/allegro-dvt/nal-h264.h deleted file mode 100644 index 2ba7cbced7a5..000000000000 --- a/drivers/staging/media/allegro-dvt/nal-h264.h +++ /dev/null @@ -1,208 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2019 Pengutronix, Michael Tretter - * - * Convert NAL units between raw byte sequence payloads (RBSP) and C structs. - */ - -#ifndef __NAL_H264_H__ -#define __NAL_H264_H__ - -#include -#include - -/** - * struct nal_h264_hdr_parameters - HDR parameters - * - * C struct representation of the sequence parameter set NAL unit as defined by - * Rec. ITU-T H.264 (04/2017) E.1.2 HRD parameters syntax. - */ -struct nal_h264_hrd_parameters { - unsigned int cpb_cnt_minus1; - unsigned int bit_rate_scale; - unsigned int cpb_size_scale; - struct { - int bit_rate_value_minus1[16]; - int cpb_size_value_minus1[16]; - unsigned int cbr_flag[16]; - }; - unsigned int initial_cpb_removal_delay_length_minus1; - unsigned int cpb_removal_delay_length_minus1; - unsigned int dpb_output_delay_length_minus1; - unsigned int time_offset_length; -}; - -/** - * struct nal_h264_vui_parameters - VUI parameters - * - * C struct representation of the VUI parameters as defined by Rec. ITU-T - * H.264 (04/2017) E.1.1 VUI parameters syntax. - */ -struct nal_h264_vui_parameters { - unsigned int aspect_ratio_info_present_flag; - struct { - unsigned int aspect_ratio_idc; - unsigned int sar_width; - unsigned int sar_height; - }; - unsigned int overscan_info_present_flag; - unsigned int overscan_appropriate_flag; - unsigned int video_signal_type_present_flag; - struct { - unsigned int video_format; - unsigned int video_full_range_flag; - unsigned int colour_description_present_flag; - struct { - unsigned int colour_primaries; - unsigned int transfer_characteristics; - unsigned int matrix_coefficients; - }; - }; - unsigned int chroma_loc_info_present_flag; - struct { - unsigned int chroma_sample_loc_type_top_field; - unsigned int chroma_sample_loc_type_bottom_field; - }; - unsigned int timing_info_present_flag; - struct { - unsigned int num_units_in_tick; - unsigned int time_scale; - unsigned int fixed_frame_rate_flag; - }; - unsigned int nal_hrd_parameters_present_flag; - struct nal_h264_hrd_parameters nal_hrd_parameters; - unsigned int vcl_hrd_parameters_present_flag; - struct nal_h264_hrd_parameters vcl_hrd_parameters; - unsigned int low_delay_hrd_flag; - unsigned int pic_struct_present_flag; - unsigned int bitstream_restriction_flag; - struct { - unsigned int motion_vectors_over_pic_boundaries_flag; - unsigned int max_bytes_per_pic_denom; - unsigned int max_bits_per_mb_denom; - unsigned int log2_max_mv_length_horizontal; - unsigned int log21_max_mv_length_vertical; - unsigned int max_num_reorder_frames; - unsigned int max_dec_frame_buffering; - }; -}; - -/** - * struct nal_h264_sps - Sequence parameter set - * - * C struct representation of the sequence parameter set NAL unit as defined by - * Rec. ITU-T H.264 (04/2017) 7.3.2.1.1 Sequence parameter set data syntax. - */ -struct nal_h264_sps { - unsigned int profile_idc; - unsigned int constraint_set0_flag; - unsigned int constraint_set1_flag; - unsigned int constraint_set2_flag; - unsigned int constraint_set3_flag; - unsigned int constraint_set4_flag; - unsigned int constraint_set5_flag; - unsigned int reserved_zero_2bits; - unsigned int level_idc; - unsigned int seq_parameter_set_id; - struct { - unsigned int chroma_format_idc; - unsigned int separate_colour_plane_flag; - unsigned int bit_depth_luma_minus8; - unsigned int bit_depth_chroma_minus8; - unsigned int qpprime_y_zero_transform_bypass_flag; - unsigned int seq_scaling_matrix_present_flag; - }; - unsigned int log2_max_frame_num_minus4; - unsigned int pic_order_cnt_type; - union { - unsigned int log2_max_pic_order_cnt_lsb_minus4; - struct { - unsigned int delta_pic_order_always_zero_flag; - int offset_for_non_ref_pic; - int offset_for_top_to_bottom_field; - unsigned int num_ref_frames_in_pic_order_cnt_cycle; - int offset_for_ref_frame[255]; - }; - }; - unsigned int max_num_ref_frames; - unsigned int gaps_in_frame_num_value_allowed_flag; - unsigned int pic_width_in_mbs_minus1; - unsigned int pic_height_in_map_units_minus1; - unsigned int frame_mbs_only_flag; - unsigned int mb_adaptive_frame_field_flag; - unsigned int direct_8x8_inference_flag; - unsigned int frame_cropping_flag; - struct { - unsigned int crop_left; - unsigned int crop_right; - unsigned int crop_top; - unsigned int crop_bottom; - }; - unsigned int vui_parameters_present_flag; - struct nal_h264_vui_parameters vui; -}; - -/** - * struct nal_h264_pps - Picture parameter set - * - * C struct representation of the picture parameter set NAL unit as defined by - * Rec. ITU-T H.264 (04/2017) 7.3.2.2 Picture parameter set RBSP syntax. - */ -struct nal_h264_pps { - unsigned int pic_parameter_set_id; - unsigned int seq_parameter_set_id; - unsigned int entropy_coding_mode_flag; - unsigned int bottom_field_pic_order_in_frame_present_flag; - unsigned int num_slice_groups_minus1; - unsigned int slice_group_map_type; - union { - unsigned int run_length_minus1[8]; - struct { - unsigned int top_left[8]; - unsigned int bottom_right[8]; - }; - struct { - unsigned int slice_group_change_direction_flag; - unsigned int slice_group_change_rate_minus1; - }; - struct { - unsigned int pic_size_in_map_units_minus1; - unsigned int slice_group_id[8]; - }; - }; - unsigned int num_ref_idx_l0_default_active_minus1; - unsigned int num_ref_idx_l1_default_active_minus1; - unsigned int weighted_pred_flag; - unsigned int weighted_bipred_idc; - int pic_init_qp_minus26; - int pic_init_qs_minus26; - int chroma_qp_index_offset; - unsigned int deblocking_filter_control_present_flag; - unsigned int constrained_intra_pred_flag; - unsigned int redundant_pic_cnt_present_flag; - struct { - unsigned int transform_8x8_mode_flag; - unsigned int pic_scaling_matrix_present_flag; - int second_chroma_qp_index_offset; - }; -}; - -int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile); -int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level); - -ssize_t nal_h264_write_sps(const struct device *dev, - void *dest, size_t n, struct nal_h264_sps *sps); -ssize_t nal_h264_read_sps(const struct device *dev, - struct nal_h264_sps *sps, void *src, size_t n); -void nal_h264_print_sps(const struct device *dev, struct nal_h264_sps *sps); - -ssize_t nal_h264_write_pps(const struct device *dev, - void *dest, size_t n, struct nal_h264_pps *pps); -ssize_t nal_h264_read_pps(const struct device *dev, - struct nal_h264_pps *pps, void *src, size_t n); -void nal_h264_print_pps(const struct device *dev, struct nal_h264_pps *pps); - -ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n); -ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n); - -#endif /* __NAL_H264_H__ */ -- cgit v1.2.3 From 0f3cc7cac0e8b58a9ec1d1d76abaa9d489112c03 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 2 Dec 2020 14:30:38 +0100 Subject: media: dt-bindings: media: allegro,al5e: Convert to YAML Convert the Allegro DVT video IP codec text binding to Yaml. Add the converted binding to the MAINTAINERS file. Signed-off-by: Michael Tretter Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/allegro,al5e.yaml | 105 +++++++++++++++++++++ .../devicetree/bindings/media/allegro.txt | 43 --------- MAINTAINERS | 1 + 3 files changed, 106 insertions(+), 43 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/allegro,al5e.yaml delete mode 100644 Documentation/devicetree/bindings/media/allegro.txt diff --git a/Documentation/devicetree/bindings/media/allegro,al5e.yaml b/Documentation/devicetree/bindings/media/allegro,al5e.yaml new file mode 100644 index 000000000000..135bea94b587 --- /dev/null +++ b/Documentation/devicetree/bindings/media/allegro,al5e.yaml @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/allegro,al5e.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allegro DVT Video IP Codecs Device Tree Bindings + +maintainers: + - Michael Tretter + +description: |- + Allegro DVT video IP codecs present in the Xilinx ZynqMP SoC. The IP core may + either be a H.264/H.265 encoder or H.264/H.265 decoder ip core. + + Each actual codec engine is controlled by a microcontroller (MCU). Host + software uses a provided mailbox interface to communicate with the MCU. The + MCUs share an interrupt. + +properties: + compatible: + oneOf: + - items: + - const: allegro,al5e-1.1 + - const: allegro,al5e + - items: + - const: allegro,al5d-1.1 + - const: allegro,al5d + + reg: + items: + - description: The registers + - description: The SRAM + + reg-names: + items: + - const: regs + - const: sram + + interrupts: + maxItems: 1 + + clocks: + items: + - description: Core clock + - description: MCU clock + - description: Core AXI master port clock + - description: MCU AXI master port clock + - description: AXI4-Lite slave port clock + + clock-names: + items: + - const: core_clk + - const: mcu_clk + - const: m_axi_core_aclk + - const: m_axi_mcu_aclk + - const: s_axi_lite_aclk + +required: + - compatible + - reg + - reg-names + - interrupts + - clocks + - clock-names + +additionalProperties: False + +examples: + - | + fpga { + #address-cells = <2>; + #size-cells = <2>; + + al5e: video-codec@a0009000 { + compatible = "allegro,al5e-1.1", "allegro,al5e"; + reg = <0 0xa0009000 0 0x1000>, + <0 0xa0000000 0 0x8000>; + reg-names = "regs", "sram"; + interrupts = <0 96 4>; + clocks = <&xlnx_vcu 0>, <&xlnx_vcu 1>, + <&clkc 71>, <&clkc 71>, <&clkc 71>; + clock-names = "core_clk", "mcu_clk", "m_axi_core_aclk", + "m_axi_mcu_aclk", "s_axi_lite_aclk"; + }; + }; + - | + fpga { + #address-cells = <2>; + #size-cells = <2>; + + al5d: video-codec@a0029000 { + compatible = "allegro,al5d-1.1", "allegro,al5d"; + reg = <0 0xa0029000 0 0x1000>, + <0 0xa0020000 0 0x8000>; + reg-names = "regs", "sram"; + interrupts = <0 96 4>; + clocks = <&xlnx_vcu 2>, <&xlnx_vcu 3>, + <&clkc 71>, <&clkc 71>, <&clkc 71>; + clock-names = "core_clk", "mcu_clk", "m_axi_core_aclk", + "m_axi_mcu_aclk", "s_axi_lite_aclk"; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/allegro.txt b/Documentation/devicetree/bindings/media/allegro.txt deleted file mode 100644 index a92e2fbf26c9..000000000000 --- a/Documentation/devicetree/bindings/media/allegro.txt +++ /dev/null @@ -1,43 +0,0 @@ -Device-tree bindings for the Allegro DVT video IP codecs present in the Xilinx -ZynqMP SoC. The IP core may either be a H.264/H.265 encoder or H.264/H.265 -decoder ip core. - -Each actual codec engines is controlled by a microcontroller (MCU). Host -software uses a provided mailbox interface to communicate with the MCU. The -MCU share an interrupt. - -Required properties: - - compatible: value should be one of the following - "allegro,al5e-1.1", "allegro,al5e": encoder IP core - "allegro,al5d-1.1", "allegro,al5d": decoder IP core - - reg: base and length of the memory mapped register region and base and - length of the memory mapped sram - - reg-names: must include "regs" and "sram" - - interrupts: shared interrupt from the MCUs to the processing system - - clocks: must contain an entry for each entry in clock-names - - clock-names: must include "core_clk", "mcu_clk", "m_axi_core_aclk", - "m_axi_mcu_aclk", "s_axi_lite_aclk" - -Example: - al5e: video-codec@a0009000 { - compatible = "allegro,al5e-1.1", "allegro,al5e"; - reg = <0 0xa0009000 0 0x1000>, - <0 0xa0000000 0 0x8000>; - reg-names = "regs", "sram"; - interrupts = <0 96 4>; - clocks = <&xlnx_vcu 0>, <&xlnx_vcu 1>, - <&clkc 71>, <&clkc 71>, <&clkc 71>; - clock-names = "core_clk", "mcu_clk", "m_axi_core_aclk", - "m_axi_mcu_aclk", "s_axi_lite_aclk" - }; - al5d: video-codec@a0029000 { - compatible = "allegro,al5d-1.1", "allegro,al5d"; - reg = <0 0xa0029000 0 0x1000>, - <0 0xa0020000 0 0x8000>; - reg-names = "regs", "sram"; - interrupts = <0 96 4>; - clocks = <&xlnx_vcu 2>, <&xlnx_vcu 3>, - <&clkc 71>, <&clkc 71>, <&clkc 71>; - clock-names = "core_clk", "mcu_clk", "m_axi_core_aclk", - "m_axi_mcu_aclk", "s_axi_lite_aclk" - }; diff --git a/MAINTAINERS b/MAINTAINERS index a40345e0477c..471561d9d55f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -699,6 +699,7 @@ M: Michael Tretter R: Pengutronix Kernel Team L: linux-media@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/media/allegro,al5e.yaml F: drivers/media/platform/allegro-dvt/ ALLWINNER A10 CSI DRIVER -- cgit v1.2.3 From 0e13f6f6ff9d065cf0aba26a6f4f9c851c92a9d2 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 2 Dec 2020 14:30:39 +0100 Subject: media: allegro: remove custom drain state handling The v4l2-m2m has various helpers for correctly handle the draining. Drop the driver specific state machine and use the m2m helper functions. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 174 ++++++---------------- 1 file changed, 45 insertions(+), 129 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index 640451134072..ef191a5d6dae 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -167,13 +167,6 @@ static struct regmap_config allegro_sram_config = { .cache_type = REGCACHE_NONE, }; -enum allegro_state { - ALLEGRO_STATE_ENCODING, - ALLEGRO_STATE_DRAIN, - ALLEGRO_STATE_WAIT_FOR_BUFFER, - ALLEGRO_STATE_STOPPED, -}; - #define fh_to_channel(__fh) container_of(__fh, struct allegro_channel, fh) struct allegro_channel { @@ -246,23 +239,8 @@ struct allegro_channel { struct completion completion; unsigned int error; - enum allegro_state state; }; -static inline int -allegro_set_state(struct allegro_channel *channel, enum allegro_state state) -{ - channel->state = state; - - return 0; -} - -static inline enum allegro_state -allegro_get_state(struct allegro_channel *channel) -{ - return channel->state; -} - struct allegro_m2m_buffer { struct v4l2_m2m_buffer buf; struct list_head head; @@ -1392,45 +1370,13 @@ static ssize_t allegro_h264_write_pps(struct allegro_channel *channel, return size; } -static bool allegro_channel_is_at_eos(struct allegro_channel *channel) -{ - bool is_at_eos = false; - - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_STOPPED: - is_at_eos = true; - break; - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - mutex_lock(&channel->shadow_list_lock); - if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0 && - list_empty(&channel->source_shadow_list)) - is_at_eos = true; - mutex_unlock(&channel->shadow_list_lock); - break; - default: - break; - } - - return is_at_eos; -} - -static void allegro_channel_buf_done(struct allegro_channel *channel, - struct vb2_v4l2_buffer *buf, - enum vb2_buffer_state state) +static void allegro_channel_eos_event(struct allegro_channel *channel) { const struct v4l2_event eos_event = { .type = V4L2_EVENT_EOS }; - if (allegro_channel_is_at_eos(channel)) { - buf->flags |= V4L2_BUF_FLAG_LAST; - v4l2_event_queue_fh(&channel->fh, &eos_event); - - allegro_set_state(channel, ALLEGRO_STATE_STOPPED); - } - - v4l2_m2m_buf_done(buf, state); + v4l2_event_queue_fh(&channel->fh, &eos_event); } static u64 allegro_put_buffer(struct allegro_channel *channel, @@ -1500,6 +1446,12 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, if (!src_buf || !dst_buf) goto err; + if (v4l2_m2m_is_last_draining_src_buf(channel->fh.m2m_ctx, src_buf)) { + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + allegro_channel_eos_event(channel); + v4l2_m2m_mark_stopped(channel->fh.m2m_ctx); + } + dst_buf->sequence = channel->csequence++; if (msg->error_code & AL_ERROR) { @@ -1626,7 +1578,7 @@ err: v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); if (dst_buf) - allegro_channel_buf_done(channel, dst_buf, state); + v4l2_m2m_buf_done(dst_buf, state); } static int allegro_handle_init(struct allegro_dev *dev, @@ -2145,10 +2097,6 @@ static int allegro_buf_prepare(struct vb2_buffer *vb) struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); struct allegro_dev *dev = channel->dev; - if (allegro_get_state(channel) == ALLEGRO_STATE_DRAIN && - V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) - return -EBUSY; - if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { if (vbuf->field == V4L2_FIELD_ANY) vbuf->field = V4L2_FIELD_NONE; @@ -2167,10 +2115,21 @@ static void allegro_buf_queue(struct vb2_buffer *vb) { struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vb2_queue *q = vb->vb2_queue; + + if (V4L2_TYPE_IS_CAPTURE(q->type) && + vb2_is_streaming(q) && + v4l2_m2m_dst_buf_is_last(channel->fh.m2m_ctx)) { + unsigned int i; + + for (i = 0; i < vb->num_planes; i++) + vb->planes[i].bytesused = 0; - if (allegro_get_state(channel) == ALLEGRO_STATE_WAIT_FOR_BUFFER && - vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - allegro_channel_buf_done(channel, vbuf, VB2_BUF_STATE_DONE); + vbuf->field = V4L2_FIELD_NONE; + vbuf->sequence = channel->csequence++; + + v4l2_m2m_last_buffer_done(channel->fh.m2m_ctx, vbuf); + allegro_channel_eos_event(channel); return; } @@ -2186,12 +2145,12 @@ static int allegro_start_streaming(struct vb2_queue *q, unsigned int count) "%s: start streaming\n", V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); - if (V4L2_TYPE_IS_OUTPUT(q->type)) { + v4l2_m2m_update_start_streaming_state(channel->fh.m2m_ctx, q); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) channel->osequence = 0; - allegro_set_state(channel, ALLEGRO_STATE_ENCODING); - } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + else channel->csequence = 0; - } return 0; } @@ -2216,10 +2175,9 @@ static void allegro_stop_streaming(struct vb2_queue *q) } mutex_unlock(&channel->shadow_list_lock); - allegro_set_state(channel, ALLEGRO_STATE_STOPPED); while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx))) v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); - } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + } else { mutex_lock(&channel->shadow_list_lock); list_for_each_entry_safe(shadow, tmp, &channel->stream_shadow_list, head) { @@ -2232,6 +2190,12 @@ static void allegro_stop_streaming(struct vb2_queue *q) while ((buffer = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx))) v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); } + + v4l2_m2m_update_stop_streaming_state(channel->fh.m2m_ctx, q); + + if (V4L2_TYPE_IS_OUTPUT(q->type) && + v4l2_m2m_has_stopped(channel->fh.m2m_ctx)) + allegro_channel_eos_event(channel); } static const struct vb2_ops allegro_queue_ops = { @@ -2669,62 +2633,16 @@ static int allegro_s_fmt_vid_out(struct file *file, void *fh, static int allegro_channel_cmd_stop(struct allegro_channel *channel) { - struct allegro_dev *dev = channel->dev; - struct vb2_v4l2_buffer *dst_buf; - - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - return -EBUSY; - case ALLEGRO_STATE_ENCODING: - allegro_set_state(channel, ALLEGRO_STATE_DRAIN); - break; - default: - return 0; - } - - /* If there are output buffers, they must be encoded */ - if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) != 0) { - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: continue encoding src buffers\n", - channel->mcu_channel_id); - return 0; - } - - /* If there are capture buffers, use it to signal EOS */ - dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); - if (dst_buf) { - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: signaling EOS\n", - channel->mcu_channel_id); - allegro_channel_buf_done(channel, dst_buf, VB2_BUF_STATE_DONE); - return 0; - } - - /* - * If there are no capture buffers, we need to wait for the next - * buffer to signal EOS. - */ - v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: CMD_STOP: wait for CAPTURE buffer to signal EOS\n", - channel->mcu_channel_id); - allegro_set_state(channel, ALLEGRO_STATE_WAIT_FOR_BUFFER); + if (v4l2_m2m_has_stopped(channel->fh.m2m_ctx)) + allegro_channel_eos_event(channel); return 0; } static int allegro_channel_cmd_start(struct allegro_channel *channel) { - switch (allegro_get_state(channel)) { - case ALLEGRO_STATE_DRAIN: - case ALLEGRO_STATE_WAIT_FOR_BUFFER: - return -EBUSY; - case ALLEGRO_STATE_STOPPED: - allegro_set_state(channel, ALLEGRO_STATE_ENCODING); - break; - default: - return 0; - } + if (v4l2_m2m_has_stopped(channel->fh.m2m_ctx)) + vb2_clear_last_buffer_dequeued(&channel->fh.m2m_ctx->cap_q_ctx.q); return 0; } @@ -2739,17 +2657,15 @@ static int allegro_encoder_cmd(struct file *file, void *fh, if (err) return err; - switch (cmd->cmd) { - case V4L2_ENC_CMD_STOP: + err = v4l2_m2m_ioctl_encoder_cmd(file, fh, cmd); + if (err) + return err; + + if (cmd->cmd == V4L2_ENC_CMD_STOP) err = allegro_channel_cmd_stop(channel); - break; - case V4L2_ENC_CMD_START: + + if (cmd->cmd == V4L2_ENC_CMD_START) err = allegro_channel_cmd_start(channel); - break; - default: - err = -EINVAL; - break; - } return err; } -- cgit v1.2.3 From ecd07f4b9d2173694be9214a3ab07f9efb5ba206 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 2 Dec 2020 14:30:40 +0100 Subject: media: allegro: rename stream_id to dst_handle The dst_handle field in the PUT_STREAM_BUFFER and ENCODE_FRAME_RESPONSE is used to retrieve the V4L2 CAPTURE buffer. Calling it stream_id is confusing. Call it dst_handle inspired by src_handle for the OUTPUT buffer. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 6 +++--- drivers/media/platform/allegro-dvt/allegro-mail.c | 8 ++++---- drivers/media/platform/allegro-dvt/allegro-mail.h | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index ef191a5d6dae..dd02ca5a584b 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -1038,7 +1038,7 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, struct allegro_channel *channel, dma_addr_t paddr, unsigned long size, - u64 stream_id) + u64 dst_handle) { struct mcu_msg_put_stream_buffer msg; @@ -1053,7 +1053,7 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, msg.size = size; msg.offset = ENCODER_STREAM_OFFSET; /* copied to mcu_msg_encode_frame_response */ - msg.stream_id = stream_id; + msg.dst_handle = dst_handle; allegro_mbox_send(dev->mbox_command, &msg); @@ -1437,7 +1437,7 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, channel->mcu_channel_id); dst_buf = allegro_get_buffer(channel, &channel->stream_shadow_list, - msg->stream_id); + msg->dst_handle); if (!dst_buf) v4l2_warn(&dev->v4l2_dev, "channel %d: invalid stream buffer\n", diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.c b/drivers/media/platform/allegro-dvt/allegro-mail.c index 9286d2162377..993e16f06305 100644 --- a/drivers/media/platform/allegro-dvt/allegro-mail.c +++ b/drivers/media/platform/allegro-dvt/allegro-mail.c @@ -302,8 +302,8 @@ allegro_enc_put_stream_buffer(u32 *dst, dst[i++] = msg->mcu_addr; dst[i++] = msg->size; dst[i++] = msg->offset; - dst[i++] = lower_32_bits(msg->stream_id); - dst[i++] = upper_32_bits(msg->stream_id); + dst[i++] = lower_32_bits(msg->dst_handle); + dst[i++] = upper_32_bits(msg->dst_handle); return i * sizeof(*dst); } @@ -406,8 +406,8 @@ allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) msg->channel_id = src[i++]; - msg->stream_id = src[i++]; - msg->stream_id |= (((u64)src[i++]) << 32); + msg->dst_handle = src[i++]; + msg->dst_handle |= (((u64)src[i++]) << 32); msg->user_param = src[i++]; msg->user_param |= (((u64)src[i++]) << 32); msg->src_handle = src[i++]; diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.h b/drivers/media/platform/allegro-dvt/allegro-mail.h index 486ecb12b098..f7485cf78c4f 100644 --- a/drivers/media/platform/allegro-dvt/allegro-mail.h +++ b/drivers/media/platform/allegro-dvt/allegro-mail.h @@ -191,7 +191,7 @@ struct mcu_msg_put_stream_buffer { u32 mcu_addr; u32 size; u32 offset; - u64 stream_id; + u64 dst_handle; }; struct mcu_msg_encode_frame { @@ -233,7 +233,7 @@ struct mcu_msg_encode_frame { struct mcu_msg_encode_frame_response { struct mcu_msg_header header; u32 channel_id; - u64 stream_id; /* see mcu_msg_put_stream_buffer */ + u64 dst_handle; /* see mcu_msg_put_stream_buffer */ u64 user_param; /* see mcu_msg_encode_frame */ u64 src_handle; /* see mcu_msg_encode_frame */ u16 skip; -- cgit v1.2.3 From 352cf679c73d30c2262aa337d7e2ce93c66dd2b6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 30 Nov 2020 14:44:32 +0100 Subject: media: Documentation: v4l: Remove reference to video ops We no longer have format related callbacks in video ops. Remove the reference to them. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/media/v4l2-subdev.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Documentation/driver-api/media/v4l2-subdev.rst b/Documentation/driver-api/media/v4l2-subdev.rst index bb5b1a7cdfd9..d4cba0d6c4ca 100644 --- a/Documentation/driver-api/media/v4l2-subdev.rst +++ b/Documentation/driver-api/media/v4l2-subdev.rst @@ -122,10 +122,6 @@ Don't forget to cleanup the media entity before the sub-device is destroyed: media_entity_cleanup(&sd->entity); -If the subdev driver intends to process video and integrate with the media -framework, it must implement format related functionality using -:c:type:`v4l2_subdev_pad_ops` instead of :c:type:`v4l2_subdev_video_ops`. - In that case, the subdev driver may set the link_validate field to provide its own link validation function. The link validation function is called for every link in the pipeline where both of the ends of the links are V4L2 -- cgit v1.2.3 From 25c8d9a7689ed2ee092a618fff718d62eed07489 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 30 Nov 2020 14:46:06 +0100 Subject: media: Documentation: v4l: Document that link_validate op is valid for sink only The link_validate pad op will only be called on sink pads. Document this. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/media/v4l2-subdev.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Documentation/driver-api/media/v4l2-subdev.rst b/Documentation/driver-api/media/v4l2-subdev.rst index d4cba0d6c4ca..6d5c799c49fe 100644 --- a/Documentation/driver-api/media/v4l2-subdev.rst +++ b/Documentation/driver-api/media/v4l2-subdev.rst @@ -122,11 +122,12 @@ Don't forget to cleanup the media entity before the sub-device is destroyed: media_entity_cleanup(&sd->entity); -In that case, the subdev driver may set the link_validate field to provide -its own link validation function. The link validation function is called for -every link in the pipeline where both of the ends of the links are V4L2 -sub-devices. The driver is still responsible for validating the correctness -of the format configuration between sub-devices and video nodes. +If a sub-device driver implements sink pads, the subdev driver may set the +link_validate field in :c:type:`v4l2_subdev_pad_ops`to provide its own link +validation function. For every link in the pipeline, the link_validate pad +operation of the sink end of the link is called. In both cases the driver is +still responsible for validating the correctness of the format configuration +between sub-devices and video nodes. If link_validate op is not set, the default function :c:func:`v4l2_subdev_link_validate_default` is used instead. This function -- cgit v1.2.3 From 81015221a269e0d9bf6dbc3fda099b09a876ebb7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 30 Jun 2020 10:44:27 +0200 Subject: media: i2c: imx219: take lock in imx219_enum_mbus_code/frame_size These two functions did not take the imx219->mutex lock, but imx219_get_format_code checks that a lock is taken, so it issues a warning: [ 8.738717] WARNING: CPU: 2 PID: 60 at drivers/media/i2c/imx219.c:653 imx219_get_format_code+0xac/0xc0 [ 8.748113] Modules linked in: [ 8.751214] CPU: 2 PID: 60 Comm: kworker/2:1 Tainted: G W 5.8.0-rc1-arm64 #148 [ 8.759821] Hardware name: NVIDIA Jetson TX1 Developer Kit (DT) [ 8.765806] Workqueue: events deferred_probe_work_func [ 8.771003] pstate: 60000005 (nZCv daif -PAN -UAO BTYPE=--) [ 8.776635] pc : imx219_get_format_code+0xac/0xc0 [ 8.781390] lr : imx219_get_format_code+0xa8/0xc0 [ 8.786143] sp : ffff800012a538f0 [ 8.789495] x29: ffff800012a538f0 x28: ffff800012838e90 [ 8.794867] x27: ffff0000f28c5800 x26: ffff800011161c68 [ 8.800237] x25: ffff0000f2a5a3f8 x24: 0000000000000018 [ 8.805605] x23: ffff0000f284ef18 x22: ffff0000f2a5a080 [ 8.810974] x21: ffff0000f284ff00 x20: ffff0000f2a5a080 [ 8.816343] x19: 000000000000300f x18: 00000000ffffffff [ 8.821712] x17: ffff800011c77268 x16: 00000000000040d7 [ 8.827081] x15: 00000000000040d8 x14: 0000000000000000 [ 8.832451] x13: 00000000000040d4 x12: ffff800011d19300 [ 8.837819] x11: 00000000000208c0 x10: 0000000000000004 [ 8.843188] x9 : 000000003baa2ecd x8 : 000000008b3f9c73 [ 8.848558] x7 : 0000000000000008 x6 : 0000000000000034 [ 8.853929] x5 : 0000000000000000 x4 : 0000000000000001 [ 8.859297] x3 : ffff800010a2a8a8 x2 : ffff0000f84a8000 [ 8.864666] x1 : 0000000000000000 x0 : 0000000000000000 [ 8.870034] Call trace: [ 8.872515] imx219_get_format_code+0xac/0xc0 [ 8.876921] imx219_enum_mbus_code+0x38/0x60 [ 8.881241] call_enum_mbus_code+0x50/0x70 [ 8.885387] tegra_vi_graph_notify_complete+0x290/0x5e8 [ 8.890670] v4l2_async_notifier_try_complete.part.0+0x48/0x68 [ 8.896563] v4l2_async_register_subdev+0x100/0x1c0 [ 8.901497] v4l2_async_register_subdev_sensor_common+0x70/0xf0 [ 8.907477] imx219_probe+0x590/0x728 [ 8.911184] i2c_device_probe+0xe4/0x2b0 [ 8.915151] really_probe+0xd8/0x330 [ 8.918768] driver_probe_device+0x58/0xb8 [ 8.922909] __device_attach_driver+0x84/0xc8 [ 8.927315] bus_for_each_drv+0x78/0xc8 [ 8.931193] __device_attach+0xe4/0x140 [ 8.935072] device_initial_probe+0x14/0x20 [ 8.939301] bus_probe_device+0x9c/0xa8 [ 8.943179] deferred_probe_work_func+0x74/0xb0 [ 8.947759] process_one_work+0x2c4/0x740 [ 8.951813] worker_thread+0x4c/0x430 [ 8.955518] kthread+0x158/0x178 [ 8.958786] ret_from_fork+0x10/0x1c [ 8.962401] irq event stamp: 63536 [ 8.965846] hardirqs last enabled at (63535): [] el1_irq+0xd8/0x180 [ 8.973846] hardirqs last disabled at (63536): [] do_debug_exception+0x16c/0x258 [ 8.982895] softirqs last enabled at (63534): [] _stext+0x54c/0x594 [ 8.990896] softirqs last disabled at (63525): [] irq_exit+0x100/0x138 [ 8.999066] ---[ end trace ebfbcd84b75ef921 ]--- [ 9.004354] ------------[ cut here ]------------ Signed-off-by: Hans Verkuil Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx219.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index e7791a0848b3..92a8d52776b8 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -806,7 +806,9 @@ static int imx219_enum_mbus_code(struct v4l2_subdev *sd, if (code->index >= (ARRAY_SIZE(codes) / 4)) return -EINVAL; + mutex_lock(&imx219->mutex); code->code = imx219_get_format_code(imx219, codes[code->index * 4]); + mutex_unlock(&imx219->mutex); return 0; } @@ -816,11 +818,15 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_frame_size_enum *fse) { struct imx219 *imx219 = to_imx219(sd); + u32 code; if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fse->code != imx219_get_format_code(imx219, fse->code)) + mutex_lock(&imx219->mutex); + code = imx219_get_format_code(imx219, fse->code); + mutex_unlock(&imx219->mutex); + if (fse->code != code) return -EINVAL; fse->min_width = supported_modes[fse->index].width; -- cgit v1.2.3 From 1b5071af824039cb71592d5b2a60773ee1ee94be Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:19:27 +0100 Subject: media: dt-bindings: media: i2c: Rename ov5647.yaml Rename 'ov5647.yaml' as 'ovti,ov5647.yaml' and update the MAINTAINERS file entry accordingly. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/ov5647.yaml | 88 ---------------------- .../devicetree/bindings/media/i2c/ovti,ov5647.yaml | 88 ++++++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 89 insertions(+), 89 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/i2c/ov5647.yaml create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/ov5647.yaml b/Documentation/devicetree/bindings/media/i2c/ov5647.yaml deleted file mode 100644 index 280c62afae13..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov5647.yaml +++ /dev/null @@ -1,88 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/media/i2c/ov5647.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Omnivision OV5647 raw image sensor - -maintainers: - - Dave Stevenson - - Jacopo Mondi - -description: |- - The OV5647 is a raw image sensor with MIPI CSI-2 and CCP2 image data - interfaces and CCI (I2C compatible) control bus. - -properties: - compatible: - const: ovti,ov5647 - - reg: - description: I2C device address. - maxItems: 1 - - clocks: - description: Reference to the xclk clock. - maxItems: 1 - - pwdn-gpios: - description: Reference to the GPIO connected to the pwdn pin. Active high. - maxItems: 1 - - port: - type: object - description: |- - Should contain one endpoint sub-node used to model connection to the - video receiver according to the specification defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. - - properties: - endpoint: - type: object - - properties: - remote-endpoint: - description: |- - phandle to the video receiver input port. - - clock-noncontinuous: - type: boolean - description: |- - Set to true to allow MIPI CSI-2 non-continuous clock operations. - - additionalProperties: false - - additionalProperties: false - -required: - - compatible - - reg - - clocks - - port - -additionalProperties: false - -examples: - - | - #include - - i2c { - #address-cells = <1>; - #size-cells = <0>; - - ov5647: camera@36 { - compatible = "ovti,ov5647"; - reg = <0x36>; - clocks = <&camera_clk>; - pwdn-gpios = <&pioE 29 GPIO_ACTIVE_HIGH>; - - port { - camera_out: endpoint { - remote-endpoint = <&csi1_ep1>; - }; - }; - }; - }; - -... diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml new file mode 100644 index 000000000000..280c62afae13 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ov5647.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Omnivision OV5647 raw image sensor + +maintainers: + - Dave Stevenson + - Jacopo Mondi + +description: |- + The OV5647 is a raw image sensor with MIPI CSI-2 and CCP2 image data + interfaces and CCI (I2C compatible) control bus. + +properties: + compatible: + const: ovti,ov5647 + + reg: + description: I2C device address. + maxItems: 1 + + clocks: + description: Reference to the xclk clock. + maxItems: 1 + + pwdn-gpios: + description: Reference to the GPIO connected to the pwdn pin. Active high. + maxItems: 1 + + port: + type: object + description: |- + Should contain one endpoint sub-node used to model connection to the + video receiver according to the specification defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. + + properties: + endpoint: + type: object + + properties: + remote-endpoint: + description: |- + phandle to the video receiver input port. + + clock-noncontinuous: + type: boolean + description: |- + Set to true to allow MIPI CSI-2 non-continuous clock operations. + + additionalProperties: false + + additionalProperties: false + +required: + - compatible + - reg + - clocks + - port + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + ov5647: camera@36 { + compatible = "ovti,ov5647"; + reg = <0x36>; + clocks = <&camera_clk>; + pwdn-gpios = <&pioE 29 GPIO_ACTIVE_HIGH>; + + port { + camera_out: endpoint { + remote-endpoint = <&csi1_ep1>; + }; + }; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index 471561d9d55f..be80b8142ac8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13115,7 +13115,7 @@ M: Jacopo Mondi L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/i2c/ov5647.yaml +F: Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml F: drivers/media/i2c/ov5647.c OMNIVISION OV5670 SENSOR DRIVER -- cgit v1.2.3 From b050791d287afcaa1114321dd409c77ca90da880 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 19 Nov 2020 17:19:28 +0100 Subject: media: ov5647: Add support for PWDN GPIO. Add support for an optional GPIO connected to PWDN on the sensor. This allows the use of hardware standby mode where internal device clock and circuit activities are halted. Please note that power is off when PWDN is high. Signed-off-by: Dave Stevenson Signed-off-by: Roman Kovalivskyi Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index e7d2e5b4ad4b..5dde138763eb 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -35,6 +36,13 @@ #define SENSOR_NAME "ov5647" +/* + * From the datasheet, "20ms after PWDN goes low or 20ms after RESETB goes + * high if reset is inserted after PWDN goes high, host can access sensor's + * SCCB to initialize sensor." + */ +#define PWDN_ACTIVE_DELAY_MS 20 + #define MIPI_CTRL00_CLOCK_LANE_GATE BIT(5) #define MIPI_CTRL00_BUS_IDLE BIT(2) #define MIPI_CTRL00_CLOCK_LANE_DISABLE BIT(0) @@ -86,6 +94,7 @@ struct ov5647 { unsigned int height; int power_count; struct clk *xclk; + struct gpio_desc *pwdn; }; static inline struct ov5647 *to_state(struct v4l2_subdev *sd) @@ -355,6 +364,11 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) if (on && !ov5647->power_count) { dev_dbg(&client->dev, "OV5647 power on\n"); + if (ov5647->pwdn) { + gpiod_set_value_cansleep(ov5647->pwdn, 0); + msleep(PWDN_ACTIVE_DELAY_MS); + } + ret = clk_prepare_enable(ov5647->xclk); if (ret < 0) { dev_err(&client->dev, "clk prepare enable failed\n"); @@ -392,6 +406,8 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) dev_dbg(&client->dev, "soft stby failed\n"); clk_disable_unprepare(ov5647->xclk); + + gpiod_set_value_cansleep(ov5647->pwdn, 1); } /* Update the power count. */ @@ -581,6 +597,14 @@ static int ov5647_probe(struct i2c_client *client) return -EINVAL; } + /* Request the power down GPIO asserted */ + sensor->pwdn = devm_gpiod_get_optional(&client->dev, "pwdn", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->pwdn)) { + dev_err(dev, "Failed to get 'pwdn' gpio\n"); + return -EINVAL; + } + mutex_init(&sensor->lock); sd = &sensor->sd; @@ -594,7 +618,15 @@ static int ov5647_probe(struct i2c_client *client) if (ret < 0) goto mutex_remove; + if (sensor->pwdn) { + gpiod_set_value_cansleep(sensor->pwdn, 0); + msleep(PWDN_ACTIVE_DELAY_MS); + } + ret = ov5647_detect(sd); + + gpiod_set_value_cansleep(sensor->pwdn, 1); + if (ret < 0) goto error; -- cgit v1.2.3 From dea4fcfe77d25f8069ef6a48dab9b732c4074ee2 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 19 Nov 2020 17:19:29 +0100 Subject: media: ov5647: Add support for non-continuous clock mode Add support for optional non-continuous clock mode to the ov5647 sensor driver. Non-continuous clock saves a small amount of power and on some SoCs is easier to interface with. Signed-off-by: Dave Stevenson Signed-off-by: Roman Kovalivskyi Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 5dde138763eb..ccb56f9b09fd 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -44,6 +44,7 @@ #define PWDN_ACTIVE_DELAY_MS 20 #define MIPI_CTRL00_CLOCK_LANE_GATE BIT(5) +#define MIPI_CTRL00_LINE_SYNC_ENABLE BIT(4) #define MIPI_CTRL00_BUS_IDLE BIT(2) #define MIPI_CTRL00_CLOCK_LANE_DISABLE BIT(0) @@ -95,6 +96,7 @@ struct ov5647 { int power_count; struct clk *xclk; struct gpio_desc *pwdn; + bool clock_ncont; }; static inline struct ov5647 *to_state(struct v4l2_subdev *sd) @@ -269,9 +271,15 @@ static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel) static int ov5647_stream_on(struct v4l2_subdev *sd) { + struct ov5647 *ov5647 = to_state(sd); + u8 val = MIPI_CTRL00_BUS_IDLE; int ret; - ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, MIPI_CTRL00_BUS_IDLE); + if (ov5647->clock_ncont) + val |= MIPI_CTRL00_CLOCK_LANE_GATE | + MIPI_CTRL00_LINE_SYNC_ENABLE; + + ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, val); if (ret < 0) return ret; @@ -546,9 +554,11 @@ static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = { .open = ov5647_open, }; -static int ov5647_parse_dt(struct device_node *np) +static int ov5647_parse_dt(struct ov5647 *sensor, struct device_node *np) { - struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; struct device_node *ep; int ret; @@ -558,7 +568,13 @@ static int ov5647_parse_dt(struct device_node *np) return -EINVAL; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); + if (ret) + goto out; + sensor->clock_ncont = bus_cfg.bus.mipi_csi2.flags & + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + +out: of_node_put(ep); return ret; } @@ -577,7 +593,7 @@ static int ov5647_probe(struct i2c_client *client) return -ENOMEM; if (IS_ENABLED(CONFIG_OF) && np) { - ret = ov5647_parse_dt(np); + ret = ov5647_parse_dt(sensor, np); if (ret) { dev_err(dev, "DT parsing error: %d\n", ret); return ret; -- cgit v1.2.3 From 0f87233a473de6cc22c4785d53aea41c1939f0c9 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 19 Nov 2020 17:19:30 +0100 Subject: media: ov5647: Add set_fmt and get_fmt calls. There's no way to query the subdevice for the supported resolutions. Add set_fmt and get_fmt implementations. Since there's only one format supported set_fmt does nothing and get returns single format. Signed-off-by: Dave Stevenson Signed-off-by: Roman Kovalivskyi Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index ccb56f9b09fd..9093a1ca7bce 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -487,8 +487,27 @@ static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, return 0; } +static int ov5647_set_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + + /* Only one format is supported, so return that */ + memset(fmt, 0, sizeof(*fmt)); + fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->field = V4L2_FIELD_NONE; + fmt->width = 640; + fmt->height = 480; + + return 0; +} + static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { .enum_mbus_code = ov5647_enum_mbus_code, + .set_fmt = ov5647_set_get_fmt, + .get_fmt = ov5647_set_get_fmt, }; static const struct v4l2_subdev_ops ov5647_subdev_ops = { -- cgit v1.2.3 From 7a4826309722ad86679a61e9a3bde80c1ef3c8d5 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:19:31 +0100 Subject: media: ov5647: Fix format initialization The driver currently support a single format. Fix its initialization to use the only supported resolution. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 9093a1ca7bce..04551ed683df 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -560,9 +560,8 @@ static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) crop->height = OV5647_WINDOW_HEIGHT_DEF; format->code = MEDIA_BUS_FMT_SBGGR8_1X8; - - format->width = OV5647_WINDOW_WIDTH_DEF; - format->height = OV5647_WINDOW_HEIGHT_DEF; + format->width = 640; + format->height = 480; format->field = V4L2_FIELD_NONE; format->colorspace = V4L2_COLORSPACE_SRGB; -- cgit v1.2.3 From c9a05cece64c609b68777d6e536f695ac1c8f8d3 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:19:32 +0100 Subject: media: ov5647: Fix style issues The driver has some obvious style issues which are worth fixing before expanding the driver capabilities. Fix: - Variable declaration order - Function parameters alignment - Multi-line comments and spurious line breaks - Use lowercase for hexadecimal values - > 80 cols lines Cosmetic change, no functional changes intended. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 102 +++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 55 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 04551ed683df..f8e982407ed6 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -34,8 +34,6 @@ #include #include -#define SENSOR_NAME "ov5647" - /* * From the datasheet, "20ms after PWDN goes low or 20ms after RESETB goes * high if reset is inserted after PWDN goes high, host can access sensor's @@ -50,9 +48,9 @@ #define OV5647_SW_STANDBY 0x0100 #define OV5647_SW_RESET 0x0103 -#define OV5647_REG_CHIPID_H 0x300A -#define OV5647_REG_CHIPID_L 0x300B -#define OV5640_REG_PAD_OUT 0x300D +#define OV5647_REG_CHIPID_H 0x300a +#define OV5647_REG_CHIPID_L 0x300b +#define OV5640_REG_PAD_OUT 0x300d #define OV5647_REG_FRAME_OFF_NUMBER 0x4202 #define OV5647_REG_MIPI_CTRL00 0x4800 #define OV5647_REG_MIPI_CTRL14 0x4814 @@ -158,7 +156,7 @@ static struct regval_list ov5647_640x480[] = { {0x3808, 0x02}, {0x3809, 0x80}, {0x380a, 0x01}, - {0x380b, 0xE0}, + {0x380b, 0xe0}, {0x3801, 0x00}, {0x3802, 0x00}, {0x3803, 0x00}, @@ -209,9 +207,9 @@ static struct regval_list ov5647_640x480[] = { static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) { - int ret; unsigned char data[3] = { reg >> 8, reg & 0xff, val}; struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; ret = i2c_master_send(client, data, 3); if (ret < 0) @@ -223,9 +221,9 @@ static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) { - int ret; unsigned char data_w[2] = { reg >> 8, reg & 0xff }; struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; ret = i2c_master_send(client, data_w, 2); if (ret < 0) { @@ -243,7 +241,7 @@ static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) } static int ov5647_write_array(struct v4l2_subdev *sd, - struct regval_list *regs, int array_size) + struct regval_list *regs, int array_size) { int i, ret; @@ -266,7 +264,9 @@ static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel) return ret; channel_id &= ~(3 << 6); - return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6)); + + return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, + channel_id | (channel << 6)); } static int ov5647_stream_on(struct v4l2_subdev *sd) @@ -294,8 +294,9 @@ static int ov5647_stream_off(struct v4l2_subdev *sd) { int ret; - ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, MIPI_CTRL00_CLOCK_LANE_GATE - | MIPI_CTRL00_BUS_IDLE | MIPI_CTRL00_CLOCK_LANE_DISABLE); + ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, + MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_BUS_IDLE | + MIPI_CTRL00_CLOCK_LANE_DISABLE); if (ret < 0) return ret; @@ -325,16 +326,16 @@ static int set_sw_standby(struct v4l2_subdev *sd, bool standby) static int __sensor_init(struct v4l2_subdev *sd) { - int ret; - u8 resetval, rdval; struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 resetval, rdval; + int ret; ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); if (ret < 0) return ret; ret = ov5647_write_array(sd, ov5647_640x480, - ARRAY_SIZE(ov5647_640x480)); + ARRAY_SIZE(ov5647_640x480)); if (ret < 0) { dev_err(&client->dev, "write sensor default regs error\n"); return ret; @@ -355,17 +356,15 @@ static int __sensor_init(struct v4l2_subdev *sd) return ret; } - /* - * stream off to make the clock lane into LP-11 state. - */ + /* Stream off to make the clock lane into LP-11 state. */ return ov5647_stream_off(sd); } static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) { - int ret = 0; - struct ov5647 *ov5647 = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5647 *ov5647 = to_state(sd); + int ret = 0; mutex_lock(&ov5647->lock); @@ -384,7 +383,7 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) } ret = ov5647_write_array(sd, sensor_oe_enable_regs, - ARRAY_SIZE(sensor_oe_enable_regs)); + ARRAY_SIZE(sensor_oe_enable_regs)); if (ret < 0) { clk_disable_unprepare(ov5647->xclk); dev_err(&client->dev, @@ -403,18 +402,15 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) dev_dbg(&client->dev, "OV5647 power off\n"); ret = ov5647_write_array(sd, sensor_oe_disable_regs, - ARRAY_SIZE(sensor_oe_disable_regs)); - + ARRAY_SIZE(sensor_oe_disable_regs)); if (ret < 0) dev_dbg(&client->dev, "disable oe failed\n"); ret = set_sw_standby(sd, true); - if (ret < 0) dev_dbg(&client->dev, "soft stby failed\n"); clk_disable_unprepare(ov5647->xclk); - gpiod_set_value_cansleep(ov5647->pwdn, 1); } @@ -430,10 +426,10 @@ out: #ifdef CONFIG_VIDEO_ADV_DEBUG static int ov5647_sensor_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) + struct v4l2_dbg_register *reg) { - u8 val; int ret; + u8 val; ret = ov5647_read(sd, reg->reg & 0xff, &val); if (ret < 0) @@ -446,15 +442,13 @@ static int ov5647_sensor_get_register(struct v4l2_subdev *sd, } static int ov5647_sensor_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { return ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff); } #endif -/* - * Subdev core operations registration - */ +/* Subdev core operations registration */ static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = { .s_power = ov5647_sensor_power, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -476,8 +470,8 @@ static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { }; static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) return -EINVAL; @@ -493,7 +487,7 @@ static int ov5647_set_get_fmt(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *fmt = &format->format; - /* Only one format is supported, so return that */ + /* Only one format is supported, so return that. */ memset(fmt, 0, sizeof(*fmt)); fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8; fmt->colorspace = V4L2_COLORSPACE_SRGB; @@ -518,9 +512,9 @@ static const struct v4l2_subdev_ops ov5647_subdev_ops = { static int ov5647_detect(struct v4l2_subdev *sd) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 read; int ret; - struct i2c_client *client = v4l2_get_subdevdata(sd); ret = ov5647_write(sd, OV5647_SW_RESET, 0x01); if (ret < 0) @@ -551,8 +545,7 @@ static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); - struct v4l2_rect *crop = - v4l2_subdev_get_try_crop(sd, fh->pad, 0); + struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0); crop->left = OV5647_COLUMN_START_DEF; crop->top = OV5647_ROW_START_DEF; @@ -578,7 +571,6 @@ static int ov5647_parse_dt(struct ov5647 *sensor, struct device_node *np) .bus_type = V4L2_MBUS_CSI2_DPHY, }; struct device_node *ep; - int ret; ep = of_graph_get_next_endpoint(np, NULL); @@ -594,17 +586,18 @@ static int ov5647_parse_dt(struct ov5647 *sensor, struct device_node *np) out: of_node_put(ep); + return ret; } static int ov5647_probe(struct i2c_client *client) { + struct device_node *np = client->dev.of_node; struct device *dev = &client->dev; struct ov5647 *sensor; - int ret; struct v4l2_subdev *sd; - struct device_node *np = client->dev.of_node; u32 xclk_freq; + int ret; sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) @@ -618,7 +611,6 @@ static int ov5647_probe(struct i2c_client *client) } } - /* get system clock (xclk) */ sensor->xclk = devm_clk_get(dev, NULL); if (IS_ERR(sensor->xclk)) { dev_err(dev, "could not get xclk"); @@ -631,9 +623,8 @@ static int ov5647_probe(struct i2c_client *client) return -EINVAL; } - /* Request the power down GPIO asserted */ - sensor->pwdn = devm_gpiod_get_optional(&client->dev, "pwdn", - GPIOD_OUT_HIGH); + /* Request the power down GPIO asserted. */ + sensor->pwdn = devm_gpiod_get_optional(dev, "pwdn", GPIOD_OUT_HIGH); if (IS_ERR(sensor->pwdn)) { dev_err(dev, "Failed to get 'pwdn' gpio\n"); return -EINVAL; @@ -643,14 +634,14 @@ static int ov5647_probe(struct i2c_client *client) sd = &sensor->sd; v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); - sensor->sd.internal_ops = &ov5647_subdev_internal_ops; - sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->internal_ops = &ov5647_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); if (ret < 0) - goto mutex_remove; + goto mutex_destroy; if (sensor->pwdn) { gpiod_set_value_cansleep(sensor->pwdn, 0); @@ -658,22 +649,23 @@ static int ov5647_probe(struct i2c_client *client) } ret = ov5647_detect(sd); - gpiod_set_value_cansleep(sensor->pwdn, 1); - if (ret < 0) - goto error; + goto entity_cleanup; ret = v4l2_async_register_subdev(sd); if (ret < 0) - goto error; + goto entity_cleanup; dev_dbg(dev, "OmniVision OV5647 camera driver probed\n"); + return 0; -error: + +entity_cleanup: media_entity_cleanup(&sd->entity); -mutex_remove: +mutex_destroy: mutex_destroy(&sensor->lock); + return ret; } @@ -692,7 +684,7 @@ static int ov5647_remove(struct i2c_client *client) static const struct i2c_device_id ov5647_id[] = { { "ov5647", 0 }, - { } + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov5647_id); @@ -707,7 +699,7 @@ MODULE_DEVICE_TABLE(of, ov5647_of_match); static struct i2c_driver ov5647_driver = { .driver = { .of_match_table = of_match_ptr(ov5647_of_match), - .name = SENSOR_NAME, + .name = "ov5647", }, .probe_new = ov5647_probe, .remove = ov5647_remove, -- cgit v1.2.3 From 24169a5aee250d53c5d588b6df34b7940c3f875a Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:19:33 +0100 Subject: media: ov5647: Replace license with SPDX identifier Replace the boilerplate license text with the SPDX identifier. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index f8e982407ed6..5c5449c42edf 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * A V4L2 driver for OmniVision OV5647 cameras. * @@ -8,15 +9,6 @@ * Copyright (C) 2006-7 Jonathan Corbet * * Copyright (C) 2016, Synopsys, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include -- cgit v1.2.3 From 2b18cbcf53f4b1f24f758cfee328fb27432c4e19 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:19:34 +0100 Subject: media: ov5647: Fix return value from read/write The ov5647_read()/ov5647_write() return in case of success the number of bytes read or written respectively. This requires callers to check if the return value is less than zero to detect an error. Unfortunately, in several places, callers directly return the result of a read/write call, causing issues when the returned valued is checked to be different from zero to detect an error. Fix this by returning zero if i2c_master_send() and i2c_master_read() return a positive value (the number of bytes written or read). Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 5c5449c42edf..79ac8c76baea 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -204,11 +204,13 @@ static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) int ret; ret = i2c_master_send(client, data, 3); - if (ret < 0) + if (ret < 0) { dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", __func__, reg); + return ret; + } - return ret; + return 0; } static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) @@ -225,11 +227,13 @@ static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) } ret = i2c_master_recv(client, val, 1); - if (ret < 0) + if (ret < 0) { dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n", __func__, reg); + return ret; + } - return ret; + return 0; } static int ov5647_write_array(struct v4l2_subdev *sd, -- cgit v1.2.3 From f7a70f9a43a641380c8df40f97561b671ed79926 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:19:35 +0100 Subject: media: ov5647: Program mode at s_stream(1) time Rename __sensor_init() function to ov5647_set_mode() as the function is a regular one and the double underscores prefix shall be removed, and then move it to program the mode at s_stream(1) time, not at sensor power up. Break out from __sensor_init() the stream_off() operation call at sensor power up to coax the lanes in LP-11 state. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 81 +++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 79ac8c76baea..ad3397881c9a 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -265,12 +265,54 @@ static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel) channel_id | (channel << 6)); } +static int ov5647_set_mode(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 resetval, rdval; + int ret; + + ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); + if (ret < 0) + return ret; + + ret = ov5647_write_array(sd, ov5647_640x480, + ARRAY_SIZE(ov5647_640x480)); + if (ret < 0) { + dev_err(&client->dev, "write sensor default regs error\n"); + return ret; + } + + ret = ov5647_set_virtual_channel(sd, 0); + if (ret < 0) + return ret; + + ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval); + if (ret < 0) + return ret; + + if (!(resetval & 0x01)) { + dev_err(&client->dev, "Device was in SW standby"); + ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01); + if (ret < 0) + return ret; + } + + return 0; +} + static int ov5647_stream_on(struct v4l2_subdev *sd) { + struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5647 *ov5647 = to_state(sd); u8 val = MIPI_CTRL00_BUS_IDLE; int ret; + ret = ov5647_set_mode(sd); + if (ret) { + dev_err(&client->dev, "Failed to program sensor mode: %d\n", ret); + return ret; + } + if (ov5647->clock_ncont) val |= MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_LINE_SYNC_ENABLE; @@ -320,42 +362,6 @@ static int set_sw_standby(struct v4l2_subdev *sd, bool standby) return ov5647_write(sd, OV5647_SW_STANDBY, rdval); } -static int __sensor_init(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 resetval, rdval; - int ret; - - ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); - if (ret < 0) - return ret; - - ret = ov5647_write_array(sd, ov5647_640x480, - ARRAY_SIZE(ov5647_640x480)); - if (ret < 0) { - dev_err(&client->dev, "write sensor default regs error\n"); - return ret; - } - - ret = ov5647_set_virtual_channel(sd, 0); - if (ret < 0) - return ret; - - ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval); - if (ret < 0) - return ret; - - if (!(resetval & 0x01)) { - dev_err(&client->dev, "Device was in SW standby"); - ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01); - if (ret < 0) - return ret; - } - - /* Stream off to make the clock lane into LP-11 state. */ - return ov5647_stream_off(sd); -} - static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -387,7 +393,8 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) goto out; } - ret = __sensor_init(sd); + /* Stream off to coax lanes into LP-11 state. */ + ret = ov5647_stream_off(sd); if (ret < 0) { clk_disable_unprepare(ov5647->xclk); dev_err(&client->dev, -- cgit v1.2.3 From 464090c0af3de6f3fe3a6329f524eadb6a8d9744 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:32:29 +0100 Subject: media: ov5647: Implement enum_frame_size() Implement the .enum_frame_size subdev pad operation. As the driver only supports one format and one resolution at the moment the implementation is trivial. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index ad3397881c9a..2e7a6cb39689 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -484,6 +484,24 @@ static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, return 0; } +static int ov5647_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR8_1X8) + return -EINVAL; + + fse->min_width = 640; + fse->max_width = 640; + fse->min_height = 480; + fse->max_height = 480; + + return 0; +} + static int ov5647_set_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) @@ -502,9 +520,10 @@ static int ov5647_set_get_fmt(struct v4l2_subdev *sd, } static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { - .enum_mbus_code = ov5647_enum_mbus_code, - .set_fmt = ov5647_set_get_fmt, - .get_fmt = ov5647_set_get_fmt, + .enum_mbus_code = ov5647_enum_mbus_code, + .enum_frame_size = ov5647_enum_frame_size, + .set_fmt = ov5647_set_get_fmt, + .get_fmt = ov5647_set_get_fmt, }; static const struct v4l2_subdev_ops ov5647_subdev_ops = { -- cgit v1.2.3 From ab614f27569ffab2441cc8f45fc24b6467aafb3c Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:32:30 +0100 Subject: media: ov5647: Protect s_stream() with mutex Use the driver mutex to protect s_stream() operations. This will become more relevant once the sensor will support more formats and set_format() could be issue concurrently to s_stream(). Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 2e7a6cb39689..69a5e25dcd70 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -462,10 +462,17 @@ static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = { static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) { + struct ov5647 *sensor = to_state(sd); + int ret; + + mutex_lock(&sensor->lock); if (enable) - return ov5647_stream_on(sd); + ret = ov5647_stream_on(sd); else - return ov5647_stream_off(sd); + ret = ov5647_stream_off(sd); + mutex_unlock(&sensor->lock); + + return ret; } static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { -- cgit v1.2.3 From 4974c2f19fd810ec9a4e534bfc69e176256b7a03 Mon Sep 17 00:00:00 2001 From: David Plowman Date: Thu, 19 Nov 2020 17:32:31 +0100 Subject: media: ov5647: Support gain, exposure and AWB controls Add controls to support AWB, AEC and AGC. Also add control support to set exposure (in lines) and analogue gain (as a register code). Signed-off-by: David Plowman Signed-off-by: Naushir Patuck Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 172 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 69a5e25dcd70..9ad1e3004ff1 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -43,9 +44,16 @@ #define OV5647_REG_CHIPID_H 0x300a #define OV5647_REG_CHIPID_L 0x300b #define OV5640_REG_PAD_OUT 0x300d +#define OV5647_REG_EXP_HI 0x3500 +#define OV5647_REG_EXP_MID 0x3501 +#define OV5647_REG_EXP_LO 0x3502 +#define OV5647_REG_AEC_AGC 0x3503 +#define OV5647_REG_GAIN_HI 0x350a +#define OV5647_REG_GAIN_LO 0x350b #define OV5647_REG_FRAME_OFF_NUMBER 0x4202 #define OV5647_REG_MIPI_CTRL00 0x4800 #define OV5647_REG_MIPI_CTRL14 0x4814 +#define OV5647_REG_AWB 0x5001 #define REG_TERM 0xfffe #define VAL_TERM 0xfe @@ -87,6 +95,7 @@ struct ov5647 { struct clk *xclk; struct gpio_desc *pwdn; bool clock_ncont; + struct v4l2_ctrl_handler ctrls; }; static inline struct ov5647 *to_state(struct v4l2_subdev *sd) @@ -121,7 +130,6 @@ static struct regval_list ov5647_640x480[] = { {0x3612, 0x59}, {0x3618, 0x00}, {0x5000, 0x06}, - {0x5001, 0x01}, {0x5002, 0x41}, {0x5003, 0x08}, {0x5a00, 0x08}, @@ -313,6 +321,11 @@ static int ov5647_stream_on(struct v4l2_subdev *sd) return ret; } + /* Apply customized values from user when stream starts. */ + ret = __v4l2_ctrl_handler_setup(sd->ctrl_handler); + if (ret) + return ret; + if (ov5647->clock_ncont) val |= MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_LINE_SYNC_ENABLE; @@ -594,6 +607,154 @@ static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = { .open = ov5647_open, }; +static int ov5647_s_auto_white_balance(struct v4l2_subdev *sd, u32 val) +{ + return ov5647_write(sd, OV5647_REG_AWB, val ? 1 : 0); +} + +static int ov5647_s_autogain(struct v4l2_subdev *sd, u32 val) +{ + int ret; + u8 reg; + + /* Non-zero turns on AGC by clearing bit 1.*/ + ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®); + if (ret) + return ret; + + return ov5647_write(sd, OV5647_REG_AEC_AGC, val ? reg & ~BIT(1) + : reg | BIT(1)); +} + +static int ov5647_s_exposure_auto(struct v4l2_subdev *sd, u32 val) +{ + int ret; + u8 reg; + + /* + * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by + * clearing bit 0. + */ + ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®); + if (ret) + return ret; + + return ov5647_write(sd, OV5647_REG_AEC_AGC, + val == V4L2_EXPOSURE_MANUAL ? reg | BIT(0) + : reg & ~BIT(0)); +} + +static int ov5647_s_analogue_gain(struct v4l2_subdev *sd, u32 val) +{ + int ret; + + /* 10 bits of gain, 2 in the high register. */ + ret = ov5647_write(sd, OV5647_REG_GAIN_HI, (val >> 8) & 3); + if (ret) + return ret; + + return ov5647_write(sd, OV5647_REG_GAIN_LO, val & 0xff); +} + +static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val) +{ + int ret; + + /* + * Sensor has 20 bits, but the bottom 4 bits are fractions of a line + * which we leave as zero (and don't receive in "val"). + */ + ret = ov5647_write(sd, OV5647_REG_EXP_HI, (val >> 12) & 0xf); + if (ret) + return ret; + + ret = ov5647_write(sd, OV5647_REG_EXP_MID, (val >> 4) & 0xff); + if (ret) + return ret; + + return ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4); +} + +static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov5647 *sensor = container_of(ctrl->handler, + struct ov5647, ctrls); + struct v4l2_subdev *sd = &sensor->sd; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* v4l2_ctrl_lock() locks our own mutex */ + + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored at s_stream(1) time. + */ + if (!sensor->power_count) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + return ov5647_s_auto_white_balance(sd, ctrl->val); + case V4L2_CID_AUTOGAIN: + return ov5647_s_autogain(sd, ctrl->val); + case V4L2_CID_EXPOSURE_AUTO: + return ov5647_s_exposure_auto(sd, ctrl->val); + case V4L2_CID_ANALOGUE_GAIN: + return ov5647_s_analogue_gain(sd, ctrl->val); + case V4L2_CID_EXPOSURE: + return ov5647_s_exposure(sd, ctrl->val); + default: + dev_info(&client->dev, + "Control (id:0x%x, val:0x%x) not supported\n", + ctrl->id, ctrl->val); + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops ov5647_ctrl_ops = { + .s_ctrl = ov5647_s_ctrl, +}; + +static int ov5647_init_controls(struct ov5647 *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); + + v4l2_ctrl_handler_init(&sensor->ctrls, 5); + + v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 0); + + v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, + 0, V4L2_EXPOSURE_MANUAL); + + /* min: 4 lines; max: 0xffff lines; default: 1000 lines. */ + v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_EXPOSURE, 4, 65535, 1, 1000); + + /* min: 16 = 1.0x; max (10 bits); default: 32 = 2.0x. */ + v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 32); + + if (sensor->ctrls.error) { + dev_err(&client->dev, + "%s Controls initialization failed (%d)\n", + __func__, sensor->ctrls.error); + v4l2_ctrl_handler_free(&sensor->ctrls); + + return sensor->ctrls.error; + } + + sensor->sd.ctrl_handler = &sensor->ctrls; + + return 0; +} + static int ov5647_parse_dt(struct ov5647 *sensor, struct device_node *np) { struct v4l2_fwnode_endpoint bus_cfg = { @@ -661,6 +822,10 @@ static int ov5647_probe(struct i2c_client *client) mutex_init(&sensor->lock); + ret = ov5647_init_controls(sensor); + if (ret) + goto mutex_destroy; + sd = &sensor->sd; v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); sd->internal_ops = &ov5647_subdev_internal_ops; @@ -670,7 +835,7 @@ static int ov5647_probe(struct i2c_client *client) sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); if (ret < 0) - goto mutex_destroy; + goto ctrl_handler_free; if (sensor->pwdn) { gpiod_set_value_cansleep(sensor->pwdn, 0); @@ -692,6 +857,8 @@ static int ov5647_probe(struct i2c_client *client) entity_cleanup: media_entity_cleanup(&sd->entity); +ctrl_handler_free: + v4l2_ctrl_handler_free(&sensor->ctrls); mutex_destroy: mutex_destroy(&sensor->lock); @@ -705,6 +872,7 @@ static int ov5647_remove(struct i2c_client *client) v4l2_async_unregister_subdev(&ov5647->sd); media_entity_cleanup(&ov5647->sd.entity); + v4l2_ctrl_handler_free(&ov5647->ctrls); v4l2_device_unregister_subdev(sd); mutex_destroy(&ov5647->lock); -- cgit v1.2.3 From 5bc5ca7149c00ac7c8f1920b323c27fbc24430c4 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:32:32 +0100 Subject: media: ov5647: Rationalize driver structure name The driver structure name is referred to with different names ('ov5647', 'state', 'sensor') in different functions in the driver. Polish this up by using 'struct ov5647 *sensor' everywhere. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 9ad1e3004ff1..4c134865cd68 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -98,7 +98,7 @@ struct ov5647 { struct v4l2_ctrl_handler ctrls; }; -static inline struct ov5647 *to_state(struct v4l2_subdev *sd) +static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) { return container_of(sd, struct ov5647, sd); } @@ -311,7 +311,7 @@ static int ov5647_set_mode(struct v4l2_subdev *sd) static int ov5647_stream_on(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5647 *ov5647 = to_state(sd); + struct ov5647 *sensor = to_sensor(sd); u8 val = MIPI_CTRL00_BUS_IDLE; int ret; @@ -326,7 +326,7 @@ static int ov5647_stream_on(struct v4l2_subdev *sd) if (ret) return ret; - if (ov5647->clock_ncont) + if (sensor->clock_ncont) val |= MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_LINE_SYNC_ENABLE; @@ -378,20 +378,20 @@ static int set_sw_standby(struct v4l2_subdev *sd, bool standby) static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5647 *ov5647 = to_state(sd); + struct ov5647 *sensor = to_sensor(sd); int ret = 0; - mutex_lock(&ov5647->lock); + mutex_lock(&sensor->lock); - if (on && !ov5647->power_count) { + if (on && !sensor->power_count) { dev_dbg(&client->dev, "OV5647 power on\n"); - if (ov5647->pwdn) { - gpiod_set_value_cansleep(ov5647->pwdn, 0); + if (sensor->pwdn) { + gpiod_set_value_cansleep(sensor->pwdn, 0); msleep(PWDN_ACTIVE_DELAY_MS); } - ret = clk_prepare_enable(ov5647->xclk); + ret = clk_prepare_enable(sensor->xclk); if (ret < 0) { dev_err(&client->dev, "clk prepare enable failed\n"); goto out; @@ -400,7 +400,7 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) ret = ov5647_write_array(sd, sensor_oe_enable_regs, ARRAY_SIZE(sensor_oe_enable_regs)); if (ret < 0) { - clk_disable_unprepare(ov5647->xclk); + clk_disable_unprepare(sensor->xclk); dev_err(&client->dev, "write sensor_oe_enable_regs error\n"); goto out; @@ -409,12 +409,12 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) /* Stream off to coax lanes into LP-11 state. */ ret = ov5647_stream_off(sd); if (ret < 0) { - clk_disable_unprepare(ov5647->xclk); + clk_disable_unprepare(sensor->xclk); dev_err(&client->dev, "Camera not available, check Power\n"); goto out; } - } else if (!on && ov5647->power_count == 1) { + } else if (!on && sensor->power_count == 1) { dev_dbg(&client->dev, "OV5647 power off\n"); ret = ov5647_write_array(sd, sensor_oe_disable_regs, @@ -426,16 +426,16 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) if (ret < 0) dev_dbg(&client->dev, "soft stby failed\n"); - clk_disable_unprepare(ov5647->xclk); - gpiod_set_value_cansleep(ov5647->pwdn, 1); + clk_disable_unprepare(sensor->xclk); + gpiod_set_value_cansleep(sensor->pwdn, 1); } /* Update the power count. */ - ov5647->power_count += on ? 1 : -1; - WARN_ON(ov5647->power_count < 0); + sensor->power_count += on ? 1 : -1; + WARN_ON(sensor->power_count < 0); out: - mutex_unlock(&ov5647->lock); + mutex_unlock(&sensor->lock); return ret; } @@ -475,7 +475,7 @@ static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = { static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) { - struct ov5647 *sensor = to_state(sd); + struct ov5647 *sensor = to_sensor(sd); int ret; mutex_lock(&sensor->lock); @@ -868,13 +868,13 @@ mutex_destroy: static int ov5647_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov5647 *ov5647 = to_state(sd); + struct ov5647 *sensor = to_sensor(sd); - v4l2_async_unregister_subdev(&ov5647->sd); - media_entity_cleanup(&ov5647->sd.entity); - v4l2_ctrl_handler_free(&ov5647->ctrls); + v4l2_async_unregister_subdev(&sensor->sd); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(&sensor->ctrls); v4l2_device_unregister_subdev(sd); - mutex_destroy(&ov5647->lock); + mutex_destroy(&sensor->lock); return 0; } -- cgit v1.2.3 From d7d6074ecd496073fbffd740a9f40f24b25ad956 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:32:33 +0100 Subject: media: ov5647: Break out format handling Break format handling out from the main driver structure. This commit prepares for the introduction of more sensor formats and resolutions by instrumenting the existing operation to work on multiple modes instead of assuming a single supported one. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 91 +++++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 26 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 4c134865cd68..9a6766410e0e 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -84,18 +84,28 @@ struct regval_list { u8 data; }; +struct ov5647_mode { + struct v4l2_mbus_framefmt format; + const struct regval_list *reg_list; + unsigned int num_regs; +}; + +struct ov5647_format_list { + unsigned int mbus_code; + const struct ov5647_mode *modes; + unsigned int num_modes; +}; + struct ov5647 { struct v4l2_subdev sd; struct media_pad pad; struct mutex lock; - struct v4l2_mbus_framefmt format; - unsigned int width; - unsigned int height; int power_count; struct clk *xclk; struct gpio_desc *pwdn; bool clock_ncont; struct v4l2_ctrl_handler ctrls; + const struct ov5647_mode *mode; }; static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) @@ -115,7 +125,7 @@ static struct regval_list sensor_oe_enable_regs[] = { {0x3002, 0xe4}, }; -static struct regval_list ov5647_640x480[] = { +static const struct regval_list ov5647_640x480[] = { {0x0100, 0x00}, {0x0103, 0x01}, {0x3034, 0x08}, @@ -205,6 +215,33 @@ static struct regval_list ov5647_640x480[] = { {0x0100, 0x01}, }; +static const struct ov5647_mode ov5647_8bit_modes[] = { + { + .format = { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .width = 640, + .height = 480 + }, + .reg_list = ov5647_640x480, + .num_regs = ARRAY_SIZE(ov5647_640x480) + }, +}; + +static const struct ov5647_format_list ov5647_formats[] = { + { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .modes = ov5647_8bit_modes, + .num_modes = ARRAY_SIZE(ov5647_8bit_modes), + }, +}; + +#define OV5647_NUM_FORMATS (ARRAY_SIZE(ov5647_formats)) + +#define OV5647_DEFAULT_MODE (&ov5647_formats[0].modes[0]) +#define OV5647_DEFAULT_FORMAT (ov5647_formats[0].modes[0].format) + static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) { unsigned char data[3] = { reg >> 8, reg & 0xff, val}; @@ -245,7 +282,7 @@ static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) } static int ov5647_write_array(struct v4l2_subdev *sd, - struct regval_list *regs, int array_size) + const struct regval_list *regs, int array_size) { int i, ret; @@ -276,6 +313,7 @@ static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel) static int ov5647_set_mode(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5647 *sensor = to_sensor(sd); u8 resetval, rdval; int ret; @@ -283,8 +321,8 @@ static int ov5647_set_mode(struct v4l2_subdev *sd) if (ret < 0) return ret; - ret = ov5647_write_array(sd, ov5647_640x480, - ARRAY_SIZE(ov5647_640x480)); + ret = ov5647_write_array(sd, sensor->mode->reg_list, + sensor->mode->num_regs); if (ret < 0) { dev_err(&client->dev, "write sensor default regs error\n"); return ret; @@ -496,10 +534,10 @@ static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - if (code->index > 0) + if (code->index >= OV5647_NUM_FORMATS) return -EINVAL; - code->code = MEDIA_BUS_FMT_SBGGR8_1X8; + code->code = ov5647_formats[code->index].mbus_code; return 0; } @@ -508,16 +546,24 @@ static int ov5647_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index) + const struct v4l2_mbus_framefmt *fmt; + unsigned int i = 0; + + for (; i < OV5647_NUM_FORMATS; ++i) { + if (ov5647_formats[i].mbus_code == fse->code) + break; + } + if (i == OV5647_NUM_FORMATS) return -EINVAL; - if (fse->code != MEDIA_BUS_FMT_SBGGR8_1X8) + if (fse->index >= ov5647_formats[i].num_modes) return -EINVAL; - fse->min_width = 640; - fse->max_width = 640; - fse->min_height = 480; - fse->max_height = 480; + fmt = &ov5647_formats[i].modes[fse->index].format; + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = fmt->height; + fse->max_height = fmt->height; return 0; } @@ -529,12 +575,7 @@ static int ov5647_set_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt = &format->format; /* Only one format is supported, so return that. */ - memset(fmt, 0, sizeof(*fmt)); - fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - fmt->field = V4L2_FIELD_NONE; - fmt->width = 640; - fmt->height = 480; + *fmt = OV5647_DEFAULT_FORMAT; return 0; } @@ -594,11 +635,7 @@ static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) crop->width = OV5647_WINDOW_WIDTH_DEF; crop->height = OV5647_WINDOW_HEIGHT_DEF; - format->code = MEDIA_BUS_FMT_SBGGR8_1X8; - format->width = 640; - format->height = 480; - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; + *format = OV5647_DEFAULT_FORMAT; return 0; } @@ -822,6 +859,8 @@ static int ov5647_probe(struct i2c_client *client) mutex_init(&sensor->lock); + sensor->mode = OV5647_DEFAULT_MODE; + ret = ov5647_init_controls(sensor); if (ret) goto mutex_destroy; -- cgit v1.2.3 From 14f70a3232aa0a5a613cd0b690c962e1562f2baa Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 19 Nov 2020 17:32:34 +0100 Subject: media: ov5647: Add support for get_selection() Support the get_selection() pad operation to report the device full pixel array size, the currently applied analogue crop rectangle and the active pixel array dimensions. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 94 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 9a6766410e0e..e5a0e6b7e84e 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -59,25 +59,14 @@ #define VAL_TERM 0xfe #define REG_DLY 0xffff -#define OV5647_ROW_START 0x01 -#define OV5647_ROW_START_MIN 0 -#define OV5647_ROW_START_MAX 2004 -#define OV5647_ROW_START_DEF 54 - -#define OV5647_COLUMN_START 0x02 -#define OV5647_COLUMN_START_MIN 0 -#define OV5647_COLUMN_START_MAX 2750 -#define OV5647_COLUMN_START_DEF 16 - -#define OV5647_WINDOW_HEIGHT 0x03 -#define OV5647_WINDOW_HEIGHT_MIN 2 -#define OV5647_WINDOW_HEIGHT_MAX 2006 -#define OV5647_WINDOW_HEIGHT_DEF 1944 - -#define OV5647_WINDOW_WIDTH 0x04 -#define OV5647_WINDOW_WIDTH_MIN 2 -#define OV5647_WINDOW_WIDTH_MAX 2752 -#define OV5647_WINDOW_WIDTH_DEF 2592 +/* OV5647 native and active pixel array size */ +#define OV5647_NATIVE_WIDTH 2624U +#define OV5647_NATIVE_HEIGHT 1956U + +#define OV5647_PIXEL_ARRAY_LEFT 16U +#define OV5647_PIXEL_ARRAY_TOP 16U +#define OV5647_PIXEL_ARRAY_WIDTH 2592U +#define OV5647_PIXEL_ARRAY_HEIGHT 1944U struct regval_list { u16 addr; @@ -86,6 +75,7 @@ struct regval_list { struct ov5647_mode { struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; const struct regval_list *reg_list; unsigned int num_regs; }; @@ -224,6 +214,12 @@ static const struct ov5647_mode ov5647_8bit_modes[] = { .width = 640, .height = 480 }, + .crop = { + .left = OV5647_PIXEL_ARRAY_LEFT, + .top = OV5647_PIXEL_ARRAY_TOP, + .width = 1280, + .height = 960, + }, .reg_list = ov5647_640x480, .num_regs = ARRAY_SIZE(ov5647_640x480) }, @@ -511,6 +507,20 @@ static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = { #endif }; +static const struct v4l2_rect * +__ov5647_get_pad_crop(struct ov5647 *ov5647, struct v4l2_subdev_pad_config *cfg, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&ov5647->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &ov5647->mode->crop; + } + + return NULL; +} + static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) { struct ov5647 *sensor = to_sensor(sd); @@ -580,11 +590,49 @@ static int ov5647_set_get_fmt(struct v4l2_subdev *sd, return 0; } +static int ov5647_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct ov5647 *sensor = to_sensor(sd); + + mutex_lock(&sensor->lock); + sel->r = *__ov5647_get_pad_crop(sensor, cfg, sel->pad, + sel->which); + mutex_unlock(&sensor->lock); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV5647_NATIVE_WIDTH; + sel->r.height = OV5647_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = OV5647_PIXEL_ARRAY_TOP; + sel->r.left = OV5647_PIXEL_ARRAY_LEFT; + sel->r.width = OV5647_PIXEL_ARRAY_WIDTH; + sel->r.height = OV5647_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { .enum_mbus_code = ov5647_enum_mbus_code, .enum_frame_size = ov5647_enum_frame_size, .set_fmt = ov5647_set_get_fmt, .get_fmt = ov5647_set_get_fmt, + .get_selection = ov5647_get_selection, }; static const struct v4l2_subdev_ops ov5647_subdev_ops = { @@ -630,10 +678,10 @@ static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) v4l2_subdev_get_try_format(sd, fh->pad, 0); struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0); - crop->left = OV5647_COLUMN_START_DEF; - crop->top = OV5647_ROW_START_DEF; - crop->width = OV5647_WINDOW_WIDTH_DEF; - crop->height = OV5647_WINDOW_HEIGHT_DEF; + crop->left = OV5647_PIXEL_ARRAY_LEFT; + crop->top = OV5647_PIXEL_ARRAY_TOP; + crop->width = OV5647_PIXEL_ARRAY_WIDTH; + crop->height = OV5647_PIXEL_ARRAY_HEIGHT; *format = OV5647_DEFAULT_FORMAT; -- cgit v1.2.3 From e907bd662ebd6bf9d60b074c3b9f1facf8a12f8b Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:32:35 +0100 Subject: media: ov5647: Rename SBGGR8 VGA mode Before adding new modes, rename the only existing one to report the bit depth to distinguish it from future additions. While at it, briefly describe the mode. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index e5a0e6b7e84e..0234aed43a10 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -115,7 +115,7 @@ static struct regval_list sensor_oe_enable_regs[] = { {0x3002, 0xe4}, }; -static const struct regval_list ov5647_640x480[] = { +static const struct regval_list ov5647_640x480_8bpp[] = { {0x0100, 0x00}, {0x0103, 0x01}, {0x3034, 0x08}, @@ -205,7 +205,8 @@ static const struct regval_list ov5647_640x480[] = { {0x0100, 0x01}, }; -static const struct ov5647_mode ov5647_8bit_modes[] = { +static const struct ov5647_mode ov5647_8bpp_modes[] = { + /* 8-bit VGA mode: Uncentred crop 2x2 binned 1296x972 image. */ { .format = { .code = MEDIA_BUS_FMT_SBGGR8_1X8, @@ -220,16 +221,16 @@ static const struct ov5647_mode ov5647_8bit_modes[] = { .width = 1280, .height = 960, }, - .reg_list = ov5647_640x480, - .num_regs = ARRAY_SIZE(ov5647_640x480) + .reg_list = ov5647_640x480_8bpp, + .num_regs = ARRAY_SIZE(ov5647_640x480_8bpp) }, }; static const struct ov5647_format_list ov5647_formats[] = { { .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, - .modes = ov5647_8bit_modes, - .num_modes = ARRAY_SIZE(ov5647_8bit_modes), + .modes = ov5647_8bpp_modes, + .num_modes = ARRAY_SIZE(ov5647_8bpp_modes), }, }; -- cgit v1.2.3 From a8df5af695a1ecd5077df18bd00375e24947b468 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:32:36 +0100 Subject: media: ov5647: Add SGGBR10_1X10 modes Add 4 additional sensor modes in SBGGR10_1X10 format. Add the following resolutions: - 2592x1944 full resolution - 1920x1080 1080p cropped - 1296x972 2x2 binned - 640x480 2x2 binned, 2x2 subsampled The register lists and modes definition have been upported from the RaspberryPi BSP at revision: commit 581dfda6d0a62 ("media: i2c: ov5647: Advertise the correct exposure range") Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 441 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 0234aed43a10..e206656a5e88 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -205,6 +205,367 @@ static const struct regval_list ov5647_640x480_8bpp[] = { {0x0100, 0x01}, }; +static struct regval_list ov5647_2592x1944_10bpp[] = { + {0x0100, 0x00}, + {0x0103, 0x01}, + {0x3034, 0x1a}, + {0x3035, 0x21}, + {0x3036, 0x69}, + {0x303c, 0x11}, + {0x3106, 0xf5}, + {0x3821, 0x06}, + {0x3820, 0x00}, + {0x3827, 0xec}, + {0x370c, 0x03}, + {0x3612, 0x5b}, + {0x3618, 0x04}, + {0x5000, 0x06}, + {0x5002, 0x41}, + {0x5003, 0x08}, + {0x5a00, 0x08}, + {0x3000, 0x00}, + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3016, 0x08}, + {0x3017, 0xe0}, + {0x3018, 0x44}, + {0x301c, 0xf8}, + {0x301d, 0xf0}, + {0x3a18, 0x00}, + {0x3a19, 0xf8}, + {0x3c01, 0x80}, + {0x3b07, 0x0c}, + {0x380c, 0x0b}, + {0x380d, 0x1c}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3708, 0x64}, + {0x3709, 0x12}, + {0x3808, 0x0a}, + {0x3809, 0x20}, + {0x380a, 0x07}, + {0x380b, 0x98}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xa3}, + {0x3811, 0x10}, + {0x3813, 0x06}, + {0x3630, 0x2e}, + {0x3632, 0xe2}, + {0x3633, 0x23}, + {0x3634, 0x44}, + {0x3636, 0x06}, + {0x3620, 0x64}, + {0x3621, 0xe0}, + {0x3600, 0x37}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x3731, 0x02}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3f05, 0x02}, + {0x3f06, 0x10}, + {0x3f01, 0x0a}, + {0x3a08, 0x01}, + {0x3a09, 0x28}, + {0x3a0a, 0x00}, + {0x3a0b, 0xf6}, + {0x3a0d, 0x08}, + {0x3a0e, 0x06}, + {0x3a0f, 0x58}, + {0x3a10, 0x50}, + {0x3a1b, 0x58}, + {0x3a1e, 0x50}, + {0x3a11, 0x60}, + {0x3a1f, 0x28}, + {0x4001, 0x02}, + {0x4004, 0x04}, + {0x4000, 0x09}, + {0x4837, 0x19}, + {0x4800, 0x24}, + {0x3503, 0x03}, + {0x0100, 0x01}, +}; + +static struct regval_list ov5647_1080p30_10bpp[] = { + {0x0100, 0x00}, + {0x0103, 0x01}, + {0x3034, 0x1a}, + {0x3035, 0x21}, + {0x3036, 0x62}, + {0x303c, 0x11}, + {0x3106, 0xf5}, + {0x3821, 0x06}, + {0x3820, 0x00}, + {0x3827, 0xec}, + {0x370c, 0x03}, + {0x3612, 0x5b}, + {0x3618, 0x04}, + {0x5000, 0x06}, + {0x5002, 0x41}, + {0x5003, 0x08}, + {0x5a00, 0x08}, + {0x3000, 0x00}, + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3016, 0x08}, + {0x3017, 0xe0}, + {0x3018, 0x44}, + {0x301c, 0xf8}, + {0x301d, 0xf0}, + {0x3a18, 0x00}, + {0x3a19, 0xf8}, + {0x3c01, 0x80}, + {0x3b07, 0x0c}, + {0x380c, 0x09}, + {0x380d, 0x70}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3708, 0x64}, + {0x3709, 0x12}, + {0x3808, 0x07}, + {0x3809, 0x80}, + {0x380a, 0x04}, + {0x380b, 0x38}, + {0x3800, 0x01}, + {0x3801, 0x5c}, + {0x3802, 0x01}, + {0x3803, 0xb2}, + {0x3804, 0x08}, + {0x3805, 0xe3}, + {0x3806, 0x05}, + {0x3807, 0xf1}, + {0x3811, 0x04}, + {0x3813, 0x02}, + {0x3630, 0x2e}, + {0x3632, 0xe2}, + {0x3633, 0x23}, + {0x3634, 0x44}, + {0x3636, 0x06}, + {0x3620, 0x64}, + {0x3621, 0xe0}, + {0x3600, 0x37}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x3731, 0x02}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3f05, 0x02}, + {0x3f06, 0x10}, + {0x3f01, 0x0a}, + {0x3a08, 0x01}, + {0x3a09, 0x4b}, + {0x3a0a, 0x01}, + {0x3a0b, 0x13}, + {0x3a0d, 0x04}, + {0x3a0e, 0x03}, + {0x3a0f, 0x58}, + {0x3a10, 0x50}, + {0x3a1b, 0x58}, + {0x3a1e, 0x50}, + {0x3a11, 0x60}, + {0x3a1f, 0x28}, + {0x4001, 0x02}, + {0x4004, 0x04}, + {0x4000, 0x09}, + {0x4837, 0x19}, + {0x4800, 0x34}, + {0x3503, 0x03}, + {0x0100, 0x01}, +}; + +static struct regval_list ov5647_2x2binned_10bpp[] = { + {0x0100, 0x00}, + {0x0103, 0x01}, + {0x3034, 0x1a}, + {0x3035, 0x21}, + {0x3036, 0x62}, + {0x303c, 0x11}, + {0x3106, 0xf5}, + {0x3827, 0xec}, + {0x370c, 0x03}, + {0x3612, 0x59}, + {0x3618, 0x00}, + {0x5000, 0x06}, + {0x5002, 0x41}, + {0x5003, 0x08}, + {0x5a00, 0x08}, + {0x3000, 0x00}, + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3016, 0x08}, + {0x3017, 0xe0}, + {0x3018, 0x44}, + {0x301c, 0xf8}, + {0x301d, 0xf0}, + {0x3a18, 0x00}, + {0x3a19, 0xf8}, + {0x3c01, 0x80}, + {0x3b07, 0x0c}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xa3}, + {0x3808, 0x05}, + {0x3809, 0x10}, + {0x380a, 0x03}, + {0x380b, 0xcc}, + {0x380c, 0x07}, + {0x380d, 0x68}, + {0x3811, 0x0c}, + {0x3813, 0x06}, + {0x3814, 0x31}, + {0x3815, 0x31}, + {0x3630, 0x2e}, + {0x3632, 0xe2}, + {0x3633, 0x23}, + {0x3634, 0x44}, + {0x3636, 0x06}, + {0x3620, 0x64}, + {0x3621, 0xe0}, + {0x3600, 0x37}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x3731, 0x02}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3f05, 0x02}, + {0x3f06, 0x10}, + {0x3f01, 0x0a}, + {0x3a08, 0x01}, + {0x3a09, 0x28}, + {0x3a0a, 0x00}, + {0x3a0b, 0xf6}, + {0x3a0d, 0x08}, + {0x3a0e, 0x06}, + {0x3a0f, 0x58}, + {0x3a10, 0x50}, + {0x3a1b, 0x58}, + {0x3a1e, 0x50}, + {0x3a11, 0x60}, + {0x3a1f, 0x28}, + {0x4001, 0x02}, + {0x4004, 0x04}, + {0x4000, 0x09}, + {0x4837, 0x16}, + {0x4800, 0x24}, + {0x3503, 0x03}, + {0x3820, 0x41}, + {0x3821, 0x07}, + {0x350a, 0x00}, + {0x350b, 0x10}, + {0x3500, 0x00}, + {0x3501, 0x1a}, + {0x3502, 0xf0}, + {0x3212, 0xa0}, + {0x0100, 0x01}, +}; + +static struct regval_list ov5647_640x480_10bpp[] = { + {0x0100, 0x00}, + {0x0103, 0x01}, + {0x3035, 0x11}, + {0x3036, 0x46}, + {0x303c, 0x11}, + {0x3821, 0x07}, + {0x3820, 0x41}, + {0x370c, 0x03}, + {0x3612, 0x59}, + {0x3618, 0x00}, + {0x5000, 0x06}, + {0x5003, 0x08}, + {0x5a00, 0x08}, + {0x3000, 0xff}, + {0x3001, 0xff}, + {0x3002, 0xff}, + {0x301d, 0xf0}, + {0x3a18, 0x00}, + {0x3a19, 0xf8}, + {0x3c01, 0x80}, + {0x3b07, 0x0c}, + {0x380c, 0x07}, + {0x380d, 0x3c}, + {0x3814, 0x35}, + {0x3815, 0x35}, + {0x3708, 0x64}, + {0x3709, 0x52}, + {0x3808, 0x02}, + {0x3809, 0x80}, + {0x380a, 0x01}, + {0x380b, 0xe0}, + {0x3800, 0x00}, + {0x3801, 0x10}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x2f}, + {0x3806, 0x07}, + {0x3807, 0x9f}, + {0x3630, 0x2e}, + {0x3632, 0xe2}, + {0x3633, 0x23}, + {0x3634, 0x44}, + {0x3620, 0x64}, + {0x3621, 0xe0}, + {0x3600, 0x37}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x3731, 0x02}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3f05, 0x02}, + {0x3f06, 0x10}, + {0x3f01, 0x0a}, + {0x3a08, 0x01}, + {0x3a09, 0x2e}, + {0x3a0a, 0x00}, + {0x3a0b, 0xfb}, + {0x3a0d, 0x02}, + {0x3a0e, 0x01}, + {0x3a0f, 0x58}, + {0x3a10, 0x50}, + {0x3a1b, 0x58}, + {0x3a1e, 0x50}, + {0x3a11, 0x60}, + {0x3a1f, 0x28}, + {0x4001, 0x02}, + {0x4004, 0x02}, + {0x4000, 0x09}, + {0x3000, 0x00}, + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3017, 0xe0}, + {0x301c, 0xfc}, + {0x3636, 0x06}, + {0x3016, 0x08}, + {0x3827, 0xec}, + {0x3018, 0x44}, + {0x3035, 0x21}, + {0x3106, 0xf5}, + {0x3034, 0x1a}, + {0x301c, 0xf8}, + {0x4800, 0x34}, + {0x3503, 0x03}, + {0x0100, 0x01}, +}; + static const struct ov5647_mode ov5647_8bpp_modes[] = { /* 8-bit VGA mode: Uncentred crop 2x2 binned 1296x972 image. */ { @@ -226,12 +587,92 @@ static const struct ov5647_mode ov5647_8bpp_modes[] = { }, }; +static const struct ov5647_mode ov5647_10bpp_modes[] = { + /* 2592x1944 full resolution full FOV 10-bit mode. */ + { + .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .width = 2592, + .height = 1944 + }, + .crop = { + .left = OV5647_PIXEL_ARRAY_LEFT, + .top = OV5647_PIXEL_ARRAY_TOP, + .width = 2592, + .height = 1944 + }, + .reg_list = ov5647_2592x1944_10bpp, + .num_regs = ARRAY_SIZE(ov5647_2592x1944_10bpp) + }, + /* 1080p30 10-bit mode. Full resolution centre-cropped down to 1080p. */ + { + .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .width = 1920, + .height = 1080 + }, + .crop = { + .left = 348 + OV5647_PIXEL_ARRAY_LEFT, + .top = 434 + OV5647_PIXEL_ARRAY_TOP, + .width = 1928, + .height = 1080, + }, + .reg_list = ov5647_1080p30_10bpp, + .num_regs = ARRAY_SIZE(ov5647_1080p30_10bpp) + }, + /* 2x2 binned full FOV 10-bit mode. */ + { + .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .width = 1296, + .height = 972 + }, + .crop = { + .left = OV5647_PIXEL_ARRAY_LEFT, + .top = OV5647_PIXEL_ARRAY_TOP, + .width = 2592, + .height = 1944, + }, + .reg_list = ov5647_2x2binned_10bpp, + .num_regs = ARRAY_SIZE(ov5647_2x2binned_10bpp) + }, + /* 10-bit VGA full FOV 60fps. 2x2 binned and subsampled down to VGA. */ + { + .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .width = 640, + .height = 480 + }, + .crop = { + .left = 16 + OV5647_PIXEL_ARRAY_LEFT, + .top = OV5647_PIXEL_ARRAY_TOP, + .width = 2560, + .height = 1920, + }, + .reg_list = ov5647_640x480_10bpp, + .num_regs = ARRAY_SIZE(ov5647_640x480_10bpp) + }, +}; + static const struct ov5647_format_list ov5647_formats[] = { { .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, .modes = ov5647_8bpp_modes, .num_modes = ARRAY_SIZE(ov5647_8bpp_modes), }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .modes = ov5647_10bpp_modes, + .num_modes = ARRAY_SIZE(ov5647_10bpp_modes), + }, }; #define OV5647_NUM_FORMATS (ARRAY_SIZE(ov5647_formats)) -- cgit v1.2.3 From 87576ac69996fa3488424723723f7f8d2cebc3b3 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:32:37 +0100 Subject: media: ov5647: Use SBGGR10_1X10 640x480 as default The SBGGR10_1X10 formats support more resolutions than SBGGR8_1X8. Make it the default sensor format and set 2x2 binned 640x480 resolution as default sensor size as it maximizes the FOV and framerate. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index e206656a5e88..bb570be48717 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -677,8 +677,9 @@ static const struct ov5647_format_list ov5647_formats[] = { #define OV5647_NUM_FORMATS (ARRAY_SIZE(ov5647_formats)) -#define OV5647_DEFAULT_MODE (&ov5647_formats[0].modes[0]) -#define OV5647_DEFAULT_FORMAT (ov5647_formats[0].modes[0].format) +/* Default sensor mode is 2x2 binned 640x480 SBGGR10_1X10. */ +#define OV5647_DEFAULT_MODE (&ov5647_formats[1].modes[3]) +#define OV5647_DEFAULT_FORMAT (ov5647_formats[1].modes[3].format) static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) { @@ -1026,7 +1027,6 @@ static int ov5647_set_get_fmt(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *fmt = &format->format; - /* Only one format is supported, so return that. */ *fmt = OV5647_DEFAULT_FORMAT; return 0; -- cgit v1.2.3 From 6869e971b084ada46c42669822321560256ea085 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:32:38 +0100 Subject: media: ov5647: Implement set_fmt pad operation Now that the driver supports more than a single mode, implement the .set_fmt pad operation and adjust the existing .get_fmt one to report the currently applied format. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 66 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index bb570be48717..bb85e9c53c9c 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -1021,13 +1021,71 @@ static int ov5647_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int ov5647_set_get_fmt(struct v4l2_subdev *sd, +static int ov5647_get_pad_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; + const struct v4l2_mbus_framefmt *sensor_format; + struct ov5647 *sensor = to_sensor(sd); + + mutex_lock(&sensor->lock); + switch (format->which) { + case V4L2_SUBDEV_FORMAT_TRY: + sensor_format = v4l2_subdev_get_try_format(sd, cfg, format->pad); + break; + default: + sensor_format = &sensor->mode->format; + break; + } + + *fmt = *sensor_format; + mutex_unlock(&sensor->lock); + + return 0; +} + +static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + const struct ov5647_mode *ov5647_mode_list; + struct ov5647 *sensor = to_sensor(sd); + const struct ov5647_mode *mode; + unsigned int num_modes; + unsigned int i; + + for (i = 0; i < OV5647_NUM_FORMATS; ++i) { + if (ov5647_formats[i].mbus_code != fmt->code) + continue; + + ov5647_mode_list = ov5647_formats[i].modes; + num_modes = ov5647_formats[i].num_modes; + break; + } + + /* + * Default mbus code MEDIA_BUS_FMT_SBGGR10_1X10 if the requested one is + * not supported. + */ + if (i == OV5647_NUM_FORMATS) { + ov5647_mode_list = ov5647_10bpp_modes; + num_modes = ARRAY_SIZE(ov5647_10bpp_modes); + } + + mode = v4l2_find_nearest_size(ov5647_mode_list, num_modes, + format.width, format.height, + fmt->width, fmt->height); - *fmt = OV5647_DEFAULT_FORMAT; + /* Update the sensor mode and apply at it at streamon time. */ + mutex_lock(&sensor->lock); + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_get_try_format(sd, cfg, format->pad) = mode->format; + else + sensor->mode = mode; + *fmt = mode->format; + mutex_unlock(&sensor->lock); return 0; } @@ -1072,8 +1130,8 @@ static int ov5647_get_selection(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { .enum_mbus_code = ov5647_enum_mbus_code, .enum_frame_size = ov5647_enum_frame_size, - .set_fmt = ov5647_set_get_fmt, - .get_fmt = ov5647_set_get_fmt, + .set_fmt = ov5647_set_pad_fmt, + .get_fmt = ov5647_get_pad_fmt, .get_selection = ov5647_get_selection, }; -- cgit v1.2.3 From 7ef761a0015bf94d543aeb382610e0691e888287 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 19 Nov 2020 17:35:40 +0100 Subject: media: ov5647: Set V4L2_SUBDEV_FL_HAS_EVENTS flag The ov5647 subdev can generate control events, therefore set the V4L2_SUBDEV_FL_HAS_EVENTS flag. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index bb85e9c53c9c..45144a600c53 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -1416,7 +1416,7 @@ static int ov5647_probe(struct i2c_client *client) sd = &sensor->sd; v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); sd->internal_ops = &ov5647_subdev_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; -- cgit v1.2.3 From 911f4516ee2b940d0c27f994f77c0e702a004f86 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 19 Nov 2020 17:35:41 +0100 Subject: media: ov5647: Support V4L2_CID_PIXEL_RATE Clients need to know the pixel rate in order to compute exposure and frame rate values. Advertise it. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 45144a600c53..86612f941e89 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -76,6 +76,7 @@ struct regval_list { struct ov5647_mode { struct v4l2_mbus_framefmt format; struct v4l2_rect crop; + u64 pixel_rate; const struct regval_list *reg_list; unsigned int num_regs; }; @@ -96,6 +97,7 @@ struct ov5647 { bool clock_ncont; struct v4l2_ctrl_handler ctrls; const struct ov5647_mode *mode; + struct v4l2_ctrl *pixel_rate; }; static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) @@ -582,6 +584,7 @@ static const struct ov5647_mode ov5647_8bpp_modes[] = { .width = 1280, .height = 960, }, + .pixel_rate = 77291670, .reg_list = ov5647_640x480_8bpp, .num_regs = ARRAY_SIZE(ov5647_640x480_8bpp) }, @@ -603,6 +606,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { .width = 2592, .height = 1944 }, + .pixel_rate = 87500000, .reg_list = ov5647_2592x1944_10bpp, .num_regs = ARRAY_SIZE(ov5647_2592x1944_10bpp) }, @@ -621,6 +625,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { .width = 1928, .height = 1080, }, + .pixel_rate = 81666700, .reg_list = ov5647_1080p30_10bpp, .num_regs = ARRAY_SIZE(ov5647_1080p30_10bpp) }, @@ -639,6 +644,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { .width = 2592, .height = 1944, }, + .pixel_rate = 81666700, .reg_list = ov5647_2x2binned_10bpp, .num_regs = ARRAY_SIZE(ov5647_2x2binned_10bpp) }, @@ -657,6 +663,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { .width = 2560, .height = 1920, }, + .pixel_rate = 55000000, .reg_list = ov5647_640x480_10bpp, .num_regs = ARRAY_SIZE(ov5647_640x480_10bpp) }, @@ -1080,10 +1087,13 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, /* Update the sensor mode and apply at it at streamon time. */ mutex_lock(&sensor->lock); - if (format->which == V4L2_SUBDEV_FORMAT_TRY) + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { *v4l2_subdev_get_try_format(sd, cfg, format->pad) = mode->format; - else + } else { sensor->mode = mode; + __v4l2_ctrl_modify_range(sensor->pixel_rate, mode->pixel_rate, + mode->pixel_rate, 1, mode->pixel_rate); + } *fmt = mode->format; mutex_unlock(&sensor->lock); @@ -1288,6 +1298,9 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) return ov5647_s_analogue_gain(sd, ctrl->val); case V4L2_CID_EXPOSURE: return ov5647_s_exposure(sd, ctrl->val); + case V4L2_CID_PIXEL_RATE: + /* Read-only, but we adjust it based on mode. */ + return 0; default: dev_info(&client->dev, "Control (id:0x%x, val:0x%x) not supported\n", @@ -1306,7 +1319,7 @@ static int ov5647_init_controls(struct ov5647 *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); - v4l2_ctrl_handler_init(&sensor->ctrls, 5); + v4l2_ctrl_handler_init(&sensor->ctrls, 6); v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 0); @@ -1326,18 +1339,26 @@ static int ov5647_init_controls(struct ov5647 *sensor) v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 32); - if (sensor->ctrls.error) { - dev_err(&client->dev, - "%s Controls initialization failed (%d)\n", - __func__, sensor->ctrls.error); - v4l2_ctrl_handler_free(&sensor->ctrls); - - return sensor->ctrls.error; - } + /* By default, PIXEL_RATE is read only, but it does change per mode */ + sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_PIXEL_RATE, + sensor->mode->pixel_rate, + sensor->mode->pixel_rate, 1, + sensor->mode->pixel_rate); + if (sensor->ctrls.error) + goto handler_free; + sensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; sensor->sd.ctrl_handler = &sensor->ctrls; return 0; + +handler_free: + dev_err(&client->dev, "%s Controls initialization failed (%d)\n", + __func__, sensor->ctrls.error); + v4l2_ctrl_handler_free(&sensor->ctrls); + + return sensor->ctrls.error; } static int ov5647_parse_dt(struct ov5647 *sensor, struct device_node *np) -- cgit v1.2.3 From c6da1ae49edd02704ce4589189d7895eae55f658 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:35:42 +0100 Subject: media: ov5647: Support V4L2_CID_HBLANK control Add support for the V4L2_CID_HBLANK read-only control. The implementation has been upported from RaspberryPi BSP commit: commit d82f202156605 ("media: i2c: ov5647: Set V4L2_SUBDEV_FL_HAS_EVENTS flag") Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 86612f941e89..2ffca9f658bf 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -77,6 +77,7 @@ struct ov5647_mode { struct v4l2_mbus_framefmt format; struct v4l2_rect crop; u64 pixel_rate; + int hts; const struct regval_list *reg_list; unsigned int num_regs; }; @@ -98,6 +99,7 @@ struct ov5647 { struct v4l2_ctrl_handler ctrls; const struct ov5647_mode *mode; struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *hblank; }; static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) @@ -585,6 +587,7 @@ static const struct ov5647_mode ov5647_8bpp_modes[] = { .height = 960, }, .pixel_rate = 77291670, + .hts = 1896, .reg_list = ov5647_640x480_8bpp, .num_regs = ARRAY_SIZE(ov5647_640x480_8bpp) }, @@ -607,6 +610,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { .height = 1944 }, .pixel_rate = 87500000, + .hts = 2844, .reg_list = ov5647_2592x1944_10bpp, .num_regs = ARRAY_SIZE(ov5647_2592x1944_10bpp) }, @@ -626,6 +630,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { .height = 1080, }, .pixel_rate = 81666700, + .hts = 2416, .reg_list = ov5647_1080p30_10bpp, .num_regs = ARRAY_SIZE(ov5647_1080p30_10bpp) }, @@ -645,6 +650,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { .height = 1944, }, .pixel_rate = 81666700, + .hts = 1896, .reg_list = ov5647_2x2binned_10bpp, .num_regs = ARRAY_SIZE(ov5647_2x2binned_10bpp) }, @@ -664,6 +670,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { .height = 1920, }, .pixel_rate = 55000000, + .hts = 1852, .reg_list = ov5647_640x480_10bpp, .num_regs = ARRAY_SIZE(ov5647_640x480_10bpp) }, @@ -1090,9 +1097,15 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { *v4l2_subdev_get_try_format(sd, cfg, format->pad) = mode->format; } else { + int hblank; + sensor->mode = mode; __v4l2_ctrl_modify_range(sensor->pixel_rate, mode->pixel_rate, mode->pixel_rate, 1, mode->pixel_rate); + + hblank = mode->hts - mode->format.width; + __v4l2_ctrl_modify_range(sensor->hblank, hblank, hblank, 1, + hblank); } *fmt = mode->format; mutex_unlock(&sensor->lock); @@ -1301,6 +1314,9 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_PIXEL_RATE: /* Read-only, but we adjust it based on mode. */ return 0; + case V4L2_CID_HBLANK: + /* Read-only, but we adjust it based on mode. */ + return 0; default: dev_info(&client->dev, "Control (id:0x%x, val:0x%x) not supported\n", @@ -1318,8 +1334,9 @@ static const struct v4l2_ctrl_ops ov5647_ctrl_ops = { static int ov5647_init_controls(struct ov5647 *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); + int hblank; - v4l2_ctrl_handler_init(&sensor->ctrls, 6); + v4l2_ctrl_handler_init(&sensor->ctrls, 7); v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 0); @@ -1345,10 +1362,18 @@ static int ov5647_init_controls(struct ov5647 *sensor) sensor->mode->pixel_rate, sensor->mode->pixel_rate, 1, sensor->mode->pixel_rate); + + /* By default, HBLANK is read only, but it does change per mode. */ + hblank = sensor->mode->hts - sensor->mode->format.width; + sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, 1, + hblank); + if (sensor->ctrls.error) goto handler_free; sensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; sensor->sd.ctrl_handler = &sensor->ctrls; return 0; -- cgit v1.2.3 From 2512c06441e3f0dd505c3f00274fff892c111c8f Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 19 Nov 2020 17:35:43 +0100 Subject: media: ov5647: Support V4L2_CID_VBLANK control Adds vblank control to allow for frame rate control. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 50 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 2ffca9f658bf..654009be0d11 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -50,6 +50,8 @@ #define OV5647_REG_AEC_AGC 0x3503 #define OV5647_REG_GAIN_HI 0x350a #define OV5647_REG_GAIN_LO 0x350b +#define OV5647_REG_VTS_HI 0x380e +#define OV5647_REG_VTS_LO 0x380f #define OV5647_REG_FRAME_OFF_NUMBER 0x4202 #define OV5647_REG_MIPI_CTRL00 0x4800 #define OV5647_REG_MIPI_CTRL14 0x4814 @@ -68,6 +70,9 @@ #define OV5647_PIXEL_ARRAY_WIDTH 2592U #define OV5647_PIXEL_ARRAY_HEIGHT 1944U +#define OV5647_VBLANK_MIN 4 +#define OV5647_VTS_MAX 32767 + struct regval_list { u16 addr; u8 data; @@ -78,6 +83,7 @@ struct ov5647_mode { struct v4l2_rect crop; u64 pixel_rate; int hts; + int vts; const struct regval_list *reg_list; unsigned int num_regs; }; @@ -100,6 +106,7 @@ struct ov5647 { const struct ov5647_mode *mode; struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; }; static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) @@ -151,8 +158,6 @@ static const struct regval_list ov5647_640x480_8bpp[] = { {0x3b07, 0x0c}, {0x380c, 0x07}, {0x380d, 0x68}, - {0x380e, 0x03}, - {0x380f, 0xd8}, {0x3814, 0x31}, {0x3815, 0x31}, {0x3708, 0x64}, @@ -588,6 +593,7 @@ static const struct ov5647_mode ov5647_8bpp_modes[] = { }, .pixel_rate = 77291670, .hts = 1896, + .vts = 0x3d8, .reg_list = ov5647_640x480_8bpp, .num_regs = ARRAY_SIZE(ov5647_640x480_8bpp) }, @@ -611,6 +617,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { }, .pixel_rate = 87500000, .hts = 2844, + .vts = 0x7b0, .reg_list = ov5647_2592x1944_10bpp, .num_regs = ARRAY_SIZE(ov5647_2592x1944_10bpp) }, @@ -631,6 +638,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { }, .pixel_rate = 81666700, .hts = 2416, + .vts = 0x450, .reg_list = ov5647_1080p30_10bpp, .num_regs = ARRAY_SIZE(ov5647_1080p30_10bpp) }, @@ -651,6 +659,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { }, .pixel_rate = 81666700, .hts = 1896, + .vts = 0x59b, .reg_list = ov5647_2x2binned_10bpp, .num_regs = ARRAY_SIZE(ov5647_2x2binned_10bpp) }, @@ -671,6 +680,7 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { }, .pixel_rate = 55000000, .hts = 1852, + .vts = 0x1f8, .reg_list = ov5647_640x480_10bpp, .num_regs = ARRAY_SIZE(ov5647_640x480_10bpp) }, @@ -695,6 +705,22 @@ static const struct ov5647_format_list ov5647_formats[] = { #define OV5647_DEFAULT_MODE (&ov5647_formats[1].modes[3]) #define OV5647_DEFAULT_FORMAT (ov5647_formats[1].modes[3].format) +static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val) +{ + unsigned char data[4] = { reg >> 8, reg & 0xff, val >> 8, val & 0xff}; + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + ret = i2c_master_send(client, data, 4); + if (ret < 0) { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + return ret; + } + + return 0; +} + static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) { unsigned char data[3] = { reg >> 8, reg & 0xff, val}; @@ -1097,7 +1123,7 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { *v4l2_subdev_get_try_format(sd, cfg, format->pad) = mode->format; } else { - int hblank; + int hblank, vblank; sensor->mode = mode; __v4l2_ctrl_modify_range(sensor->pixel_rate, mode->pixel_rate, @@ -1106,6 +1132,12 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, hblank = mode->hts - mode->format.width; __v4l2_ctrl_modify_range(sensor->hblank, hblank, hblank, 1, hblank); + + vblank = mode->vts - mode->format.height; + __v4l2_ctrl_modify_range(sensor->vblank, OV5647_VBLANK_MIN, + OV5647_VTS_MAX - mode->format.height, + 1, vblank); + __v4l2_ctrl_s_ctrl(sensor->vblank, vblank); } *fmt = mode->format; mutex_unlock(&sensor->lock); @@ -1317,6 +1349,9 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_HBLANK: /* Read-only, but we adjust it based on mode. */ return 0; + case V4L2_CID_VBLANK: + return ov5647_write16(sd, OV5647_REG_VTS_HI, + sensor->mode->format.height + ctrl->val); default: dev_info(&client->dev, "Control (id:0x%x, val:0x%x) not supported\n", @@ -1336,7 +1371,7 @@ static int ov5647_init_controls(struct ov5647 *sensor) struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); int hblank; - v4l2_ctrl_handler_init(&sensor->ctrls, 7); + v4l2_ctrl_handler_init(&sensor->ctrls, 8); v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 0); @@ -1369,6 +1404,13 @@ static int ov5647_init_controls(struct ov5647 *sensor) V4L2_CID_HBLANK, hblank, hblank, 1, hblank); + sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_VBLANK, OV5647_VBLANK_MIN, + OV5647_VTS_MAX - + sensor->mode->format.height, 1, + sensor->mode->vts - + sensor->mode->format.height); + if (sensor->ctrls.error) goto handler_free; -- cgit v1.2.3 From 646a0249ffd0d8334ec291e3380845b8ade2f1a0 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 19 Nov 2020 17:35:44 +0100 Subject: media: ov5647: Advertise the correct exposure range Exposure is clipped by the VTS of the mode, so it needs to be updated when this is changed. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 654009be0d11..70eafc5390e1 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -73,6 +73,11 @@ #define OV5647_VBLANK_MIN 4 #define OV5647_VTS_MAX 32767 +#define OV5647_EXPOSURE_MIN 4 +#define OV5647_EXPOSURE_STEP 1 +#define OV5647_EXPOSURE_DEFAULT 1000 +#define OV5647_EXPOSURE_MAX 65535 + struct regval_list { u16 addr; u8 data; @@ -107,6 +112,7 @@ struct ov5647 { struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl *hblank; struct v4l2_ctrl *vblank; + struct v4l2_ctrl *exposure; }; static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) @@ -1092,6 +1098,7 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt = &format->format; const struct ov5647_mode *ov5647_mode_list; struct ov5647 *sensor = to_sensor(sd); + const struct ov5647_mode *mode; unsigned int num_modes; unsigned int i; @@ -1123,6 +1130,7 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) { *v4l2_subdev_get_try_format(sd, cfg, format->pad) = mode->format; } else { + int exposure_max, exposure_def; int hblank, vblank; sensor->mode = mode; @@ -1138,6 +1146,13 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, OV5647_VTS_MAX - mode->format.height, 1, vblank); __v4l2_ctrl_s_ctrl(sensor->vblank, vblank); + + exposure_max = mode->vts - 4; + exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT); + __v4l2_ctrl_modify_range(sensor->exposure, + sensor->exposure->minimum, + exposure_max, sensor->exposure->step, + exposure_def); } *fmt = mode->format; mutex_unlock(&sensor->lock); @@ -1324,6 +1339,18 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) /* v4l2_ctrl_lock() locks our own mutex */ + if (ctrl->id == V4L2_CID_VBLANK) { + int exposure_max, exposure_def; + + /* Update max exposure while meeting expected vblanking */ + exposure_max = sensor->mode->format.height + ctrl->val - 4; + exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT); + __v4l2_ctrl_modify_range(sensor->exposure, + sensor->exposure->minimum, + exposure_max, sensor->exposure->step, + exposure_def); + } + /* * If the device is not powered up by the host driver do * not apply any controls to H/W at this time. Instead @@ -1369,7 +1396,7 @@ static const struct v4l2_ctrl_ops ov5647_ctrl_ops = { static int ov5647_init_controls(struct ov5647 *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); - int hblank; + int hblank, exposure_max, exposure_def; v4l2_ctrl_handler_init(&sensor->ctrls, 8); @@ -1383,9 +1410,13 @@ static int ov5647_init_controls(struct ov5647 *sensor) V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_MANUAL); - /* min: 4 lines; max: 0xffff lines; default: 1000 lines. */ - v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, - V4L2_CID_EXPOSURE, 4, 65535, 1, 1000); + exposure_max = sensor->mode->vts - 4; + exposure_def = min(exposure_max, OV5647_EXPOSURE_DEFAULT); + sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_EXPOSURE, + OV5647_EXPOSURE_MIN, + exposure_max, OV5647_EXPOSURE_STEP, + exposure_def); /* min: 16 = 1.0x; max (10 bits); default: 32 = 2.0x. */ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, -- cgit v1.2.3 From 089b7c70f0d80f5169cc87f65b065fc80668a0eb Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:35:45 +0100 Subject: media: ov5647: Use pm_runtime infrastructure Use the pm_runtime framework to replace the legacy s_power() operation. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 142 ++++++++++++++++++++++----------------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 70eafc5390e1..777c7b30bafd 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -881,86 +882,75 @@ static int ov5647_stream_off(struct v4l2_subdev *sd) return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x01); } -static int set_sw_standby(struct v4l2_subdev *sd, bool standby) +static int ov5647_power_on(struct device *dev) { + struct ov5647 *sensor = dev_get_drvdata(dev); int ret; - u8 rdval; - ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); - if (ret < 0) - return ret; + dev_dbg(dev, "OV5647 power on\n"); - if (standby) - rdval &= ~0x01; - else - rdval |= 0x01; - - return ov5647_write(sd, OV5647_SW_STANDBY, rdval); -} + if (sensor->pwdn) { + gpiod_set_value_cansleep(sensor->pwdn, 0); + msleep(PWDN_ACTIVE_DELAY_MS); + } -static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5647 *sensor = to_sensor(sd); - int ret = 0; + ret = clk_prepare_enable(sensor->xclk); + if (ret < 0) { + dev_err(dev, "clk prepare enable failed\n"); + goto error_pwdn; + } - mutex_lock(&sensor->lock); + ret = ov5647_write_array(&sensor->sd, sensor_oe_enable_regs, + ARRAY_SIZE(sensor_oe_enable_regs)); + if (ret < 0) { + dev_err(dev, "write sensor_oe_enable_regs error\n"); + goto error_clk_disable; + } - if (on && !sensor->power_count) { - dev_dbg(&client->dev, "OV5647 power on\n"); + /* Stream off to coax lanes into LP-11 state. */ + ret = ov5647_stream_off(&sensor->sd); + if (ret < 0) { + dev_err(dev, "camera not available, check power\n"); + goto error_clk_disable; + } - if (sensor->pwdn) { - gpiod_set_value_cansleep(sensor->pwdn, 0); - msleep(PWDN_ACTIVE_DELAY_MS); - } + return 0; - ret = clk_prepare_enable(sensor->xclk); - if (ret < 0) { - dev_err(&client->dev, "clk prepare enable failed\n"); - goto out; - } +error_clk_disable: + clk_disable_unprepare(sensor->xclk); +error_pwdn: + gpiod_set_value_cansleep(sensor->pwdn, 1); - ret = ov5647_write_array(sd, sensor_oe_enable_regs, - ARRAY_SIZE(sensor_oe_enable_regs)); - if (ret < 0) { - clk_disable_unprepare(sensor->xclk); - dev_err(&client->dev, - "write sensor_oe_enable_regs error\n"); - goto out; - } + return ret; +} - /* Stream off to coax lanes into LP-11 state. */ - ret = ov5647_stream_off(sd); - if (ret < 0) { - clk_disable_unprepare(sensor->xclk); - dev_err(&client->dev, - "Camera not available, check Power\n"); - goto out; - } - } else if (!on && sensor->power_count == 1) { - dev_dbg(&client->dev, "OV5647 power off\n"); +static int ov5647_power_off(struct device *dev) +{ + struct ov5647 *sensor = dev_get_drvdata(dev); + u8 rdval; + int ret; - ret = ov5647_write_array(sd, sensor_oe_disable_regs, - ARRAY_SIZE(sensor_oe_disable_regs)); - if (ret < 0) - dev_dbg(&client->dev, "disable oe failed\n"); + dev_dbg(dev, "OV5647 power off\n"); - ret = set_sw_standby(sd, true); - if (ret < 0) - dev_dbg(&client->dev, "soft stby failed\n"); + ret = ov5647_write_array(&sensor->sd, sensor_oe_disable_regs, + ARRAY_SIZE(sensor_oe_disable_regs)); + if (ret < 0) + dev_dbg(dev, "disable oe failed\n"); - clk_disable_unprepare(sensor->xclk); - gpiod_set_value_cansleep(sensor->pwdn, 1); - } + /* Enter software standby */ + ret = ov5647_read(&sensor->sd, OV5647_SW_STANDBY, &rdval); + if (ret < 0) + dev_dbg(dev, "software standby failed\n"); - /* Update the power count. */ - sensor->power_count += on ? 1 : -1; - WARN_ON(sensor->power_count < 0); + rdval &= ~0x01; + ret = ov5647_write(&sensor->sd, OV5647_SW_STANDBY, rdval); + if (ret < 0) + dev_dbg(dev, "software standby failed\n"); -out: - mutex_unlock(&sensor->lock); + clk_disable_unprepare(sensor->xclk); + gpiod_set_value_cansleep(sensor->pwdn, 1); - return ret; + return 0; } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -989,7 +979,6 @@ static int ov5647_sensor_set_register(struct v4l2_subdev *sd, /* Subdev core operations registration */ static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = { - .s_power = ov5647_sensor_power, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov5647_sensor_get_register, .s_register = ov5647_sensor_set_register, @@ -1543,24 +1532,29 @@ static int ov5647_probe(struct i2c_client *client) if (ret < 0) goto ctrl_handler_free; - if (sensor->pwdn) { - gpiod_set_value_cansleep(sensor->pwdn, 0); - msleep(PWDN_ACTIVE_DELAY_MS); - } + ret = ov5647_power_on(dev); + if (ret) + goto entity_cleanup; ret = ov5647_detect(sd); - gpiod_set_value_cansleep(sensor->pwdn, 1); if (ret < 0) - goto entity_cleanup; + goto power_off; ret = v4l2_async_register_subdev(sd); if (ret < 0) - goto entity_cleanup; + goto power_off; + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); dev_dbg(dev, "OmniVision OV5647 camera driver probed\n"); return 0; +power_off: + ov5647_power_off(dev); entity_cleanup: media_entity_cleanup(&sd->entity); ctrl_handler_free: @@ -1580,11 +1574,16 @@ static int ov5647_remove(struct i2c_client *client) media_entity_cleanup(&sensor->sd.entity); v4l2_ctrl_handler_free(&sensor->ctrls); v4l2_device_unregister_subdev(sd); + pm_runtime_disable(&client->dev); mutex_destroy(&sensor->lock); return 0; } +static const struct dev_pm_ops ov5647_pm_ops = { + SET_RUNTIME_PM_OPS(ov5647_power_off, ov5647_power_on, NULL) +}; + static const struct i2c_device_id ov5647_id[] = { { "ov5647", 0 }, { /* sentinel */ } @@ -1603,6 +1602,7 @@ static struct i2c_driver ov5647_driver = { .driver = { .of_match_table = of_match_ptr(ov5647_of_match), .name = "ov5647", + .pm = &ov5647_pm_ops, }, .probe_new = ov5647_probe, .remove = ov5647_remove, -- cgit v1.2.3 From 2f038c97fd7baaf9508cf32998a5f8f1ddc09a5e Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:35:46 +0100 Subject: media: ov5647: Rework s_stream() operation Rework the s_stream() operation to turn the sensor on and off at stream enable/disable time using the pm_runtime infrastructure. Protect the stream on/off from being called multiple times in sequence with a 'streaming' flag. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 777c7b30bafd..1ac1538a3951 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -114,6 +114,7 @@ struct ov5647 { struct v4l2_ctrl *hblank; struct v4l2_ctrl *vblank; struct v4l2_ctrl *exposure; + bool streaming; }; static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) @@ -1001,14 +1002,42 @@ __ov5647_get_pad_crop(struct ov5647 *ov5647, struct v4l2_subdev_pad_config *cfg, static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) { + struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5647 *sensor = to_sensor(sd); int ret; mutex_lock(&sensor->lock); - if (enable) + if (sensor->streaming == enable) { + mutex_unlock(&sensor->lock); + return 0; + } + + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) + goto error_unlock; + ret = ov5647_stream_on(sd); - else + if (ret < 0) { + dev_err(&client->dev, "stream start failed: %d\n", ret); + goto error_unlock; + } + } else { ret = ov5647_stream_off(sd); + if (ret < 0) { + dev_err(&client->dev, "stream stop failed: %d\n", ret); + goto error_unlock; + } + pm_runtime_put(&client->dev); + } + + sensor->streaming = enable; + mutex_unlock(&sensor->lock); + + return 0; + +error_unlock: + pm_runtime_put(&client->dev); mutex_unlock(&sensor->lock); return ret; -- cgit v1.2.3 From 4eec1919c9585d12526dced3de64c1d95c7fc451 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:35:47 +0100 Subject: media: ov5647: Apply controls only when powered Use pm_runtime_get_if_in_use() in s_ctrl to apply controls only when the device is powered on. Rework the control set function to balance the pm_runtime_get_if_in_use() call with pm_runtime_put() at the end of the function. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 1ac1538a3951..8061058e6df8 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -104,7 +104,6 @@ struct ov5647 { struct v4l2_subdev sd; struct media_pad pad; struct mutex lock; - int power_count; struct clk *xclk; struct gpio_desc *pwdn; bool clock_ncont; @@ -1354,6 +1353,8 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) struct ov5647, ctrls); struct v4l2_subdev *sd = &sensor->sd; struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + /* v4l2_ctrl_lock() locks our own mutex */ @@ -1370,33 +1371,40 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) } /* - * If the device is not powered up by the host driver do - * not apply any controls to H/W at this time. Instead - * the controls will be restored at s_stream(1) time. + * If the device is not powered up do not apply any controls + * to H/W at this time. Instead the controls will be restored + * at s_stream(1) time. */ - if (!sensor->power_count) + if (pm_runtime_get_if_in_use(&client->dev) == 0) return 0; switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: - return ov5647_s_auto_white_balance(sd, ctrl->val); + ret = ov5647_s_auto_white_balance(sd, ctrl->val); + break; case V4L2_CID_AUTOGAIN: - return ov5647_s_autogain(sd, ctrl->val); + ret = ov5647_s_autogain(sd, ctrl->val); + break; case V4L2_CID_EXPOSURE_AUTO: - return ov5647_s_exposure_auto(sd, ctrl->val); + ret = ov5647_s_exposure_auto(sd, ctrl->val); + break; case V4L2_CID_ANALOGUE_GAIN: - return ov5647_s_analogue_gain(sd, ctrl->val); + ret = ov5647_s_analogue_gain(sd, ctrl->val); + break; case V4L2_CID_EXPOSURE: - return ov5647_s_exposure(sd, ctrl->val); + ret = ov5647_s_exposure(sd, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = ov5647_write16(sd, OV5647_REG_VTS_HI, + sensor->mode->format.height + ctrl->val); + break; + + /* Read-only, but we adjust it based on mode. */ case V4L2_CID_PIXEL_RATE: - /* Read-only, but we adjust it based on mode. */ - return 0; case V4L2_CID_HBLANK: /* Read-only, but we adjust it based on mode. */ - return 0; - case V4L2_CID_VBLANK: - return ov5647_write16(sd, OV5647_REG_VTS_HI, - sensor->mode->format.height + ctrl->val); + break; + default: dev_info(&client->dev, "Control (id:0x%x, val:0x%x) not supported\n", @@ -1404,7 +1412,9 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) return -EINVAL; } - return 0; + pm_runtime_put(&client->dev); + + return ret; } static const struct v4l2_ctrl_ops ov5647_ctrl_ops = { -- cgit v1.2.3 From d07440702da0ff084f2bb97b1396d75fd7acc53f Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:35:48 +0100 Subject: media: ov5647: Constify oe_enable/disable reglist Make the two register-value lists const. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 8061058e6df8..963f51656266 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -121,13 +121,13 @@ static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) return container_of(sd, struct ov5647, sd); } -static struct regval_list sensor_oe_disable_regs[] = { +static const struct regval_list sensor_oe_disable_regs[] = { {0x3000, 0x00}, {0x3001, 0x00}, {0x3002, 0x00}, }; -static struct regval_list sensor_oe_enable_regs[] = { +static const struct regval_list sensor_oe_enable_regs[] = { {0x3000, 0x0f}, {0x3001, 0xff}, {0x3002, 0xe4}, -- cgit v1.2.3 From dc3373081396f5317f8f0b593a2cb644c4e1786e Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:35:49 +0100 Subject: media: ov5647: Support VIDIOC_SUBSCRIBE_EVENT The driver reports the V4L2_SUBDEV_FL_HAS_EVENTS flag but does not support subscribing and unsubscribing to events. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 963f51656266..be40c60ab7ee 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -979,6 +980,8 @@ static int ov5647_sensor_set_register(struct v4l2_subdev *sd, /* Subdev core operations registration */ static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov5647_sensor_get_register, .s_register = ov5647_sensor_set_register, -- cgit v1.2.3 From 38c22308181562d52926b6701327c3fe209f6d84 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 19 Nov 2020 17:37:53 +0100 Subject: media: ov5647: Remove 640x480 SBGGR8 mode Capturing in 640x480 SBGGR8_1X8 hangs the system when capturing with the unicam driver on RaspberryPi 4 platform. Remove it and remove the support for multiple media bus codes in the driver. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5647.c | 180 +++------------------------------------------ 1 file changed, 9 insertions(+), 171 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index be40c60ab7ee..1cefa15729ce 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -95,12 +95,6 @@ struct ov5647_mode { unsigned int num_regs; }; -struct ov5647_format_list { - unsigned int mbus_code; - const struct ov5647_mode *modes; - unsigned int num_modes; -}; - struct ov5647 { struct v4l2_subdev sd; struct media_pad pad; @@ -134,94 +128,6 @@ static const struct regval_list sensor_oe_enable_regs[] = { {0x3002, 0xe4}, }; -static const struct regval_list ov5647_640x480_8bpp[] = { - {0x0100, 0x00}, - {0x0103, 0x01}, - {0x3034, 0x08}, - {0x3035, 0x21}, - {0x3036, 0x46}, - {0x303c, 0x11}, - {0x3106, 0xf5}, - {0x3821, 0x07}, - {0x3820, 0x41}, - {0x3827, 0xec}, - {0x370c, 0x0f}, - {0x3612, 0x59}, - {0x3618, 0x00}, - {0x5000, 0x06}, - {0x5002, 0x41}, - {0x5003, 0x08}, - {0x5a00, 0x08}, - {0x3000, 0x00}, - {0x3001, 0x00}, - {0x3002, 0x00}, - {0x3016, 0x08}, - {0x3017, 0xe0}, - {0x3018, 0x44}, - {0x301c, 0xf8}, - {0x301d, 0xf0}, - {0x3a18, 0x00}, - {0x3a19, 0xf8}, - {0x3c01, 0x80}, - {0x3b07, 0x0c}, - {0x380c, 0x07}, - {0x380d, 0x68}, - {0x3814, 0x31}, - {0x3815, 0x31}, - {0x3708, 0x64}, - {0x3709, 0x52}, - {0x3808, 0x02}, - {0x3809, 0x80}, - {0x380a, 0x01}, - {0x380b, 0xe0}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x0a}, - {0x3805, 0x3f}, - {0x3806, 0x07}, - {0x3807, 0xa1}, - {0x3811, 0x08}, - {0x3813, 0x02}, - {0x3630, 0x2e}, - {0x3632, 0xe2}, - {0x3633, 0x23}, - {0x3634, 0x44}, - {0x3636, 0x06}, - {0x3620, 0x64}, - {0x3621, 0xe0}, - {0x3600, 0x37}, - {0x3704, 0xa0}, - {0x3703, 0x5a}, - {0x3715, 0x78}, - {0x3717, 0x01}, - {0x3731, 0x02}, - {0x370b, 0x60}, - {0x3705, 0x1a}, - {0x3f05, 0x02}, - {0x3f06, 0x10}, - {0x3f01, 0x0a}, - {0x3a08, 0x01}, - {0x3a09, 0x27}, - {0x3a0a, 0x00}, - {0x3a0b, 0xf6}, - {0x3a0d, 0x04}, - {0x3a0e, 0x03}, - {0x3a0f, 0x58}, - {0x3a10, 0x50}, - {0x3a1b, 0x58}, - {0x3a1e, 0x50}, - {0x3a11, 0x60}, - {0x3a1f, 0x28}, - {0x4001, 0x02}, - {0x4004, 0x02}, - {0x4000, 0x09}, - {0x4837, 0x24}, - {0x4050, 0x6e}, - {0x4051, 0x8f}, - {0x0100, 0x01}, -}; - static struct regval_list ov5647_2592x1944_10bpp[] = { {0x0100, 0x00}, {0x0103, 0x01}, @@ -583,31 +489,7 @@ static struct regval_list ov5647_640x480_10bpp[] = { {0x0100, 0x01}, }; -static const struct ov5647_mode ov5647_8bpp_modes[] = { - /* 8-bit VGA mode: Uncentred crop 2x2 binned 1296x972 image. */ - { - .format = { - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .field = V4L2_FIELD_NONE, - .width = 640, - .height = 480 - }, - .crop = { - .left = OV5647_PIXEL_ARRAY_LEFT, - .top = OV5647_PIXEL_ARRAY_TOP, - .width = 1280, - .height = 960, - }, - .pixel_rate = 77291670, - .hts = 1896, - .vts = 0x3d8, - .reg_list = ov5647_640x480_8bpp, - .num_regs = ARRAY_SIZE(ov5647_640x480_8bpp) - }, -}; - -static const struct ov5647_mode ov5647_10bpp_modes[] = { +static const struct ov5647_mode ov5647_modes[] = { /* 2592x1944 full resolution full FOV 10-bit mode. */ { .format = { @@ -694,24 +576,9 @@ static const struct ov5647_mode ov5647_10bpp_modes[] = { }, }; -static const struct ov5647_format_list ov5647_formats[] = { - { - .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, - .modes = ov5647_8bpp_modes, - .num_modes = ARRAY_SIZE(ov5647_8bpp_modes), - }, - { - .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, - .modes = ov5647_10bpp_modes, - .num_modes = ARRAY_SIZE(ov5647_10bpp_modes), - }, -}; - -#define OV5647_NUM_FORMATS (ARRAY_SIZE(ov5647_formats)) - /* Default sensor mode is 2x2 binned 640x480 SBGGR10_1X10. */ -#define OV5647_DEFAULT_MODE (&ov5647_formats[1].modes[3]) -#define OV5647_DEFAULT_FORMAT (ov5647_formats[1].modes[3].format) +#define OV5647_DEFAULT_MODE (&ov5647_modes[3]) +#define OV5647_DEFAULT_FORMAT (ov5647_modes[3].format) static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val) { @@ -1053,10 +920,10 @@ static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - if (code->index >= OV5647_NUM_FORMATS) + if (code->index > 0) return -EINVAL; - code->code = ov5647_formats[code->index].mbus_code; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; return 0; } @@ -1066,19 +933,12 @@ static int ov5647_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_frame_size_enum *fse) { const struct v4l2_mbus_framefmt *fmt; - unsigned int i = 0; - for (; i < OV5647_NUM_FORMATS; ++i) { - if (ov5647_formats[i].mbus_code == fse->code) - break; - } - if (i == OV5647_NUM_FORMATS) + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10 || + fse->index >= ARRAY_SIZE(ov5647_modes)) return -EINVAL; - if (fse->index >= ov5647_formats[i].num_modes) - return -EINVAL; - - fmt = &ov5647_formats[i].modes[fse->index].format; + fmt = &ov5647_modes[fse->index].format; fse->min_width = fmt->width; fse->max_width = fmt->width; fse->min_height = fmt->height; @@ -1116,32 +976,10 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; - const struct ov5647_mode *ov5647_mode_list; struct ov5647 *sensor = to_sensor(sd); - const struct ov5647_mode *mode; - unsigned int num_modes; - unsigned int i; - - for (i = 0; i < OV5647_NUM_FORMATS; ++i) { - if (ov5647_formats[i].mbus_code != fmt->code) - continue; - - ov5647_mode_list = ov5647_formats[i].modes; - num_modes = ov5647_formats[i].num_modes; - break; - } - - /* - * Default mbus code MEDIA_BUS_FMT_SBGGR10_1X10 if the requested one is - * not supported. - */ - if (i == OV5647_NUM_FORMATS) { - ov5647_mode_list = ov5647_10bpp_modes; - num_modes = ARRAY_SIZE(ov5647_10bpp_modes); - } - mode = v4l2_find_nearest_size(ov5647_mode_list, num_modes, + mode = v4l2_find_nearest_size(ov5647_modes, ARRAY_SIZE(ov5647_modes), format.width, format.height, fmt->width, fmt->height); -- cgit v1.2.3 From 44c1febd7e6018b279d83e2c9725333be6b00f83 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 11 Dec 2020 23:15:04 +0100 Subject: media: Documentation: media: Update pixel rate formula for C-PHY Update the formula to calculate the pixel rate on the link for C-PHY. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/media/csi2.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/driver-api/media/csi2.rst b/Documentation/driver-api/media/csi2.rst index e3bbc6baf0f0..11c52b0be8b8 100644 --- a/Documentation/driver-api/media/csi2.rst +++ b/Documentation/driver-api/media/csi2.rst @@ -35,7 +35,7 @@ ability to start and stop the stream. The value of the V4L2_CID_PIXEL_RATE is calculated as follows:: - pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample + pixel_rate = link_freq * 2 * nr_of_lanes * 16 / k / bits_per_sample where @@ -53,6 +53,8 @@ where - Two bits are transferred per clock cycle per lane. * - bits_per_sample - Number of bits per sample. + * - k + - 16 for D-PHY and 7 for C-PHY The transmitter drivers must, if possible, configure the CSI-2 transmitter to *LP-11 mode* whenever the transmitter is powered on but -- cgit v1.2.3 From d8401ed01f546df989d4bd837e941d09d60350ae Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 14 Dec 2020 22:31:21 +0100 Subject: media: v4l: fwnode: v4l2_async_notifier_parse_fwnode_endpoints is deprecated Document that v4l2_async_notifier_parse_fwnode_endpoints() is deprecated. Its functionality has been replaced by other, better functions. Also add a reference to an example if someone ends up wandering here. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-fwnode.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index 6d026dadbf98..a0b55d70b526 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -453,6 +453,10 @@ typedef int (*parse_endpoint_func)(struct device *dev, * @parse_endpoint: Driver's callback function called on each V4L2 fwnode * endpoint. Optional. * + * DEPRECATED! This function is deprecated. Don't use it in new drivers. + * Instead see an example in cio2_parse_firmware() function in + * drivers/media/pci/intel/ipu3/ipu3-cio2.c . + * * Parse the fwnode endpoints of the @dev device and populate the async sub- * devices list in the notifier. The @parse_endpoint callback function is * called for each endpoint with the corresponding async sub-device pointer to -- cgit v1.2.3 From f7c7d6ccc503c0a5f4e5debb570b55bf2eb24c73 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 17 Dec 2020 19:27:08 +0100 Subject: media: uapi: Add an entity type for Image Signal Processors Add and document a media entity type for Image Signal Processor devices. Surprisingly we didn't have one, so add one now. More or less all ISP drivers should use this type instead of what they currently are using (or not using anything). Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/mediactl/media-types.rst | 7 +++++++ include/uapi/linux/media.h | 1 + 2 files changed, 8 insertions(+) diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst index 7b24a213cae7..e1e4043b3b1c 100644 --- a/Documentation/userspace-api/media/mediactl/media-types.rst +++ b/Documentation/userspace-api/media/mediactl/media-types.rst @@ -39,6 +39,7 @@ Types and flags used to represent the media graph elements .. _MEDIA-ENT-F-PROC-VIDEO-STATISTICS: .. _MEDIA-ENT-F-PROC-VIDEO-ENCODER: .. _MEDIA-ENT-F-PROC-VIDEO-DECODER: +.. _MEDIA-ENT-F-PROC-VIDEO-ISP: .. _MEDIA-ENT-F-VID-MUX: .. _MEDIA-ENT-F-VID-IF-BRIDGE: .. _MEDIA-ENT-F-DV-DECODER: @@ -201,6 +202,12 @@ Types and flags used to represent the media graph elements decompressing a compressed video stream into uncompressed video frames. Must have one sink pad and at least one source pad. + * - ``MEDIA_ENT_F_PROC_VIDEO_ISP`` + - An Image Signal Processor (ISP) device. ISPs generally are one of a + kind devices that have their specific control interfaces using a + combination of custom V4L2 controls and IOCTLs, and parameters + supplied in a metadata buffer. + * - ``MEDIA_ENT_F_VID_MUX`` - Video multiplexer. An entity capable of multiplexing must have at least two sink pads and one source pad, and must pass the video diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 383ac7b7d8f0..200fa8462b90 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -127,6 +127,7 @@ struct media_device_info { #define MEDIA_ENT_F_PROC_VIDEO_STATISTICS (MEDIA_ENT_F_BASE + 0x4006) #define MEDIA_ENT_F_PROC_VIDEO_ENCODER (MEDIA_ENT_F_BASE + 0x4007) #define MEDIA_ENT_F_PROC_VIDEO_DECODER (MEDIA_ENT_F_BASE + 0x4008) +#define MEDIA_ENT_F_PROC_VIDEO_ISP (MEDIA_ENT_F_BASE + 0x4009) /* * Switch and bridge entity functions -- cgit v1.2.3 From fb18802a338b36f675a388fc03d2aa504a0d0899 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 19 Dec 2020 23:29:58 +0100 Subject: media: v4l: ioctl: Fix memory leak in video_usercopy When an IOCTL with argument size larger than 128 that also used array arguments were handled, two memory allocations were made but alas, only the latter one of them was released. This happened because there was only a single local variable to hold such a temporary allocation. Fix this by adding separate variables to hold the pointers to the temporary allocations. Reported-by: Arnd Bergmann Reported-by: syzbot+1115e79c8df6472c612b@syzkaller.appspotmail.com Fixes: d14e6d76ebf7 ("[media] v4l: Add multi-planar ioctl handling code") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Acked-by: Arnd Bergmann Acked-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 3198abdd538c..9906b41004e9 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -3283,7 +3283,7 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, v4l2_kioctl func) { char sbuf[128]; - void *mbuf = NULL; + void *mbuf = NULL, *array_buf = NULL; void *parg = (void *)arg; long err = -EINVAL; bool has_array_args; @@ -3318,27 +3318,21 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, has_array_args = err; if (has_array_args) { - /* - * When adding new types of array args, make sure that the - * parent argument to ioctl (which contains the pointer to the - * array) fits into sbuf (so that mbuf will still remain - * unused up to here). - */ - mbuf = kvmalloc(array_size, GFP_KERNEL); + array_buf = kvmalloc(array_size, GFP_KERNEL); err = -ENOMEM; - if (NULL == mbuf) + if (array_buf == NULL) goto out_array_args; err = -EFAULT; if (in_compat_syscall()) - err = v4l2_compat_get_array_args(file, mbuf, user_ptr, - array_size, orig_cmd, - parg); + err = v4l2_compat_get_array_args(file, array_buf, + user_ptr, array_size, + orig_cmd, parg); else - err = copy_from_user(mbuf, user_ptr, array_size) ? + err = copy_from_user(array_buf, user_ptr, array_size) ? -EFAULT : 0; if (err) goto out_array_args; - *kernel_ptr = mbuf; + *kernel_ptr = array_buf; } /* Handles IOCTL */ @@ -3360,12 +3354,13 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, if (in_compat_syscall()) { int put_err; - put_err = v4l2_compat_put_array_args(file, user_ptr, mbuf, - array_size, orig_cmd, - parg); + put_err = v4l2_compat_put_array_args(file, user_ptr, + array_buf, + array_size, + orig_cmd, parg); if (put_err) err = put_err; - } else if (copy_to_user(user_ptr, mbuf, array_size)) { + } else if (copy_to_user(user_ptr, array_buf, array_size)) { err = -EFAULT; } goto out_array_args; @@ -3381,6 +3376,7 @@ out_array_args: if (video_put_user((void __user *)arg, parg, cmd, orig_cmd)) err = -EFAULT; out: + kvfree(array_buf); kvfree(mbuf); return err; } -- cgit v1.2.3 From 62a1255152be2cb4291fb3f0208bd837d33a8e2d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 20 Dec 2020 21:17:17 +0100 Subject: media: v4l: ioctl: Use kmalloc to allocate a small chunk of memory kvmalloc() was used to allocate the temporary memory buffer that was used to contain both the IOCTL argument as well as a possible array argument that could have been large. Now that the two are separated, the IOCTL argument is known to be small in size. Use kmalloc to allocate it instead of kvmalloc. Similarly for releasing it. Suggested-by: Arnd Bergmann Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 9906b41004e9..8d5d9c39c162 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -3300,7 +3300,7 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, parg = sbuf; } else { /* too big to allocate from stack */ - mbuf = kvmalloc(ioc_size, GFP_KERNEL); + mbuf = kmalloc(ioc_size, GFP_KERNEL); if (NULL == mbuf) return -ENOMEM; parg = mbuf; @@ -3377,7 +3377,7 @@ out_array_args: err = -EFAULT; out: kvfree(array_buf); - kvfree(mbuf); + kfree(mbuf); return err; } -- cgit v1.2.3 From 3ef5e42d281ea108f4ccdca186de4ce20a346326 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 14 Dec 2020 16:28:32 +0100 Subject: media: ipu3-cio2: Build only for x86 According to the original code in the driver it was never assumed to work with big page sizes: unsigned short type followed by PAGE_SHIFT and PAGE_MASK which may be different on non-x86 architectures. Recently LKP found an issue on non-x86 architectures due to above mentioned limitations. Since Sakari acknowledges that it's not really useful to be able to compile this elsewhere, mark it x86 only. Fixes: a31d19f88932 ("media: ipu3: allow building it with COMPILE_TEST on non-x86 archs") Reported-by: kernel test robot Suggested-by: Sakari Ailus Signed-off-by: Andy Shevchenko Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/intel/ipu3/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig index 82d7f17e6a02..7a805201034b 100644 --- a/drivers/media/pci/intel/ipu3/Kconfig +++ b/drivers/media/pci/intel/ipu3/Kconfig @@ -2,7 +2,8 @@ config VIDEO_IPU3_CIO2 tristate "Intel ipu3-cio2 driver" depends on VIDEO_V4L2 && PCI - depends on (X86 && ACPI) || COMPILE_TEST + depends on ACPI || COMPILE_TEST + depends on X86 select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE -- cgit v1.2.3 From 2225cf449294ebba4e03a300636e1a8b20953c0b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 11 Dec 2020 18:56:04 +0100 Subject: media: Documentation: media: Document clock handling in camera sensor drivers Document pratices of handling clocks in camera sensor drivers on both DT and ACPI. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/media/camera-sensor.rst | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst index ffb0cad8137a..3fc378b3b269 100644 --- a/Documentation/driver-api/media/camera-sensor.rst +++ b/Documentation/driver-api/media/camera-sensor.rst @@ -15,7 +15,7 @@ Camera sensors have an internal clock tree including a PLL and a number of divisors. The clock tree is generally configured by the driver based on a few input parameters that are specific to the hardware:: the external clock frequency and the link frequency. The two parameters generally are obtained from system -firmware. No other frequencies should be used in any circumstances. +firmware. **No other frequencies should be used in any circumstances.** The reason why the clock frequencies are so important is that the clock signals come out of the SoC, and in many cases a specific frequency is designed to be @@ -23,6 +23,24 @@ used in the system. Using another frequency may cause harmful effects elsewhere. Therefore only the pre-determined frequencies are configurable by the user. +ACPI +~~~~ + +Read the "clock-frequency" _DSD property to denote the frequency. The driver can +rely on this frequency being used. + +Devicetree +~~~~~~~~~~ + +The currently preferred way to achieve this is using "assigned-clock-rates" +property. See Documentation/devicetree/bindings/clock/clock-bindings.txt for +more information. The driver then gets the frequency using clk_get_rate(). + +This approach has the drawback that there's no guarantee that the frequency +hasn't been modified directly or indirectly by another driver, or supported by +the board's clock tree to begin with. Changes to the Common Clock Framework API +are required to ensure reliability. + Frame size ---------- -- cgit v1.2.3 From dc1eb7c9c290cba52937c9a224b22a400bb0ffd7 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 21 Dec 2020 18:52:20 +0100 Subject: media: i2c: ov5670: Fix PIXEL_RATE minimum value The driver currently reports a single supported value for V4L2_CID_PIXEL_RATE and initializes the control's minimum value to 0, which is very risky, as userspace might accidentally use it as divider when calculating the time duration of a line. Fix this by using as minimum the only supported value when registering the control. Fixes: 5de35c9b8dcd1 ("media: i2c: Add Omnivision OV5670 5M sensor support") Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5670.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index 148fd4e05029..866c8c2e8f59 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -2084,7 +2084,8 @@ static int ov5670_init_controls(struct ov5670 *ov5670) /* By default, V4L2_CID_PIXEL_RATE is read only */ ov5670->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, + V4L2_CID_PIXEL_RATE, + link_freq_configs[0].pixel_rate, link_freq_configs[0].pixel_rate, 1, link_freq_configs[0].pixel_rate); -- cgit v1.2.3 From 334de4b45892f7e67074e1b1b2ac36fd3e091118 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Wed, 30 Dec 2020 13:55:50 +0100 Subject: media: ipu3-cio2: Fix mbus_code processing in cio2_subdev_set_fmt() Loop was useless as it would always exit on the first iteration. Fix it with right condition. Signed-off-by: Pavel Machek (CIP) Fixes: a86cf9b29e8b ("media: ipu3-cio2: Validate mbus format in setting subdev format") Tested-by: Laurent Pinchart Reviewed-by: Laurent Pinchart Cc: stable@vger.kernel.org # v4.16 and up Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 36e354ecf71e..e8ea69d30bfd 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1269,7 +1269,7 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd, fmt->format.code = formats[0].mbus_code; for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].mbus_code == fmt->format.code) { + if (formats[i].mbus_code == mbus_code) { fmt->format.code = mbus_code; break; } -- cgit v1.2.3 From 29a202fa7accd6b1ca617b765d02068e77a71731 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 31 Dec 2020 15:23:56 +0100 Subject: media: dt-bindings: media: i2c: Add OV5648 bindings documentation This introduces YAML bindings documentation for the OV5648 image sensor. Signed-off-by: Paul Kocialkowski Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/ovti,ov5648.yaml | 115 +++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov5648.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov5648.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov5648.yaml new file mode 100644 index 000000000000..f8783f77cc54 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov5648.yaml @@ -0,0 +1,115 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ovti,ov5648.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OmniVision OV5648 Image Sensor Device Tree Bindings + +maintainers: + - Paul Kocialkowski + +properties: + compatible: + const: ovti,ov5648 + + reg: + maxItems: 1 + + clocks: + items: + - description: XVCLK Clock + + assigned-clocks: + maxItems: 1 + + assigned-clock-rates: + maxItems: 1 + + dvdd-supply: + description: Digital Domain Power Supply + + avdd-supply: + description: Analog Domain Power Supply (internal AVDD is used if missing) + + dovdd-supply: + description: I/O Domain Power Supply + + powerdown-gpios: + maxItems: 1 + description: Power Down Pin GPIO Control (active low) + + reset-gpios: + maxItems: 1 + description: Reset Pin GPIO Control (active low) + + port: + type: object + description: MIPI CSI-2 transmitter port + + properties: + endpoint: + type: object + + properties: + remote-endpoint: true + + link-frequencies: + $ref: /schemas/types.yaml#/definitions/uint64-array + description: Allowed MIPI CSI-2 link frequencies + + data-lanes: + minItems: 1 + maxItems: 2 + + required: + - data-lanes + - link-frequencies + - remote-endpoint + + required: + - endpoint + +required: + - compatible + - reg + - clocks + - assigned-clocks + - assigned-clock-rates + - dvdd-supply + - dovdd-supply + - port + +additionalProperties: false + +examples: + - | + #include + #include + + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + ov5648: camera@36 { + compatible = "ovti,ov5648"; + reg = <0x36>; + + dvdd-supply = <&ov5648_dvdd>; + avdd-supply = <&ov5648_avdd>; + dovdd-supply = <&ov5648_dovdd>; + clocks = <&ov5648_xvclk 0>; + assigned-clocks = <&ov5648_xvclk 0>; + assigned-clock-rates = <24000000>; + + + ov5648_out: port { + ov5648_out_mipi_csi2: endpoint { + data-lanes = <1 2>; + link-frequencies = /bits/ 64 <210000000 168000000>; + + remote-endpoint = <&mipi_csi2_in_ov5648>; + }; + }; + }; + }; -- cgit v1.2.3 From e43ccb0a045f34838b786e8021dc4838b4af5c38 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 31 Dec 2020 15:23:57 +0100 Subject: media: i2c: Add support for the OV5648 image sensor The OV5648 is a 5 Mpx CMOS image sensor, connected via MIPI CSI-2 in a one or two lane configuration. Most of the features of the hardware are supported, including: - Auto and manual exposition/gain - Auto and manual white balance - Horizontal and vertical flip - Test patterns But the following are still missing: - Debanding, based on power source frequency; - Exposition setting correlated to time units. Signed-off-by: Paul Kocialkowski Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov5648.c | 2623 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2637 insertions(+) create mode 100644 drivers/media/i2c/ov5648.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 2b9d81e4794a..4c1ae687ab10 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -936,6 +936,19 @@ config VIDEO_OV5647 To compile this driver as a module, choose M here: the module will be called ov5647. +config VIDEO_OV5648 + tristate "OmniVision OV5648 sensor support" + depends on I2C && PM && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV5648 camera. + + To compile this driver as a module, choose M here: the + module will be called ov5648. + config VIDEO_OV6650 tristate "OmniVision OV6650 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index a3149dce21bb..65cfc94d25b6 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_VIDEO_OV2740) += ov2740.o obj-$(CONFIG_VIDEO_OV5640) += ov5640.o obj-$(CONFIG_VIDEO_OV5645) += ov5645.o obj-$(CONFIG_VIDEO_OV5647) += ov5647.o +obj-$(CONFIG_VIDEO_OV5648) += ov5648.o obj-$(CONFIG_VIDEO_OV5670) += ov5670.o obj-$(CONFIG_VIDEO_OV5675) += ov5675.o obj-$(CONFIG_VIDEO_OV5695) += ov5695.o diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c new file mode 100644 index 000000000000..609aa67b54ce --- /dev/null +++ b/drivers/media/i2c/ov5648.c @@ -0,0 +1,2623 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Bootlin + * Author: Paul Kocialkowski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Clock rate */ + +#define OV5648_XVCLK_RATE 24000000 + +/* Register definitions */ + +/* System */ + +#define OV5648_SW_STANDBY_REG 0x100 +#define OV5648_SW_STANDBY_STREAM_ON BIT(0) + +#define OV5648_SW_RESET_REG 0x103 +#define OV5648_SW_RESET_RESET BIT(0) + +#define OV5648_PAD_OEN0_REG 0x3000 +#define OV5648_PAD_OEN1_REG 0x3001 +#define OV5648_PAD_OEN2_REG 0x3002 +#define OV5648_PAD_OUT0_REG 0x3008 +#define OV5648_PAD_OUT1_REG 0x3009 + +#define OV5648_CHIP_ID_H_REG 0x300a +#define OV5648_CHIP_ID_H_VALUE 0x56 +#define OV5648_CHIP_ID_L_REG 0x300b +#define OV5648_CHIP_ID_L_VALUE 0x48 + +#define OV5648_PAD_OUT2_REG 0x300d +#define OV5648_PAD_SEL0_REG 0x300e +#define OV5648_PAD_SEL1_REG 0x300f +#define OV5648_PAD_SEL2_REG 0x3010 +#define OV5648_PAD_PK_REG 0x3011 +#define OV5648_PAD_PK_PD_DATO_EN BIT(7) +#define OV5648_PAD_PK_DRIVE_STRENGTH_1X (0 << 5) +#define OV5648_PAD_PK_DRIVE_STRENGTH_2X (2 << 5) +#define OV5648_PAD_PK_FREX_N BIT(1) + +#define OV5648_A_PWC_PK_O0_REG 0x3013 +#define OV5648_A_PWC_PK_O0_BP_REGULATOR_N BIT(3) +#define OV5648_A_PWC_PK_O1_REG 0x3014 + +#define OV5648_MIPI_PHY0_REG 0x3016 +#define OV5648_MIPI_PHY1_REG 0x3017 +#define OV5648_MIPI_SC_CTRL0_REG 0x3018 +#define OV5648_MIPI_SC_CTRL0_MIPI_LANES(v) (((v) << 5) & GENMASK(7, 5)) +#define OV5648_MIPI_SC_CTRL0_PHY_HS_TX_PD BIT(4) +#define OV5648_MIPI_SC_CTRL0_PHY_LP_RX_PD BIT(3) +#define OV5648_MIPI_SC_CTRL0_MIPI_EN BIT(2) +#define OV5648_MIPI_SC_CTRL0_MIPI_SUSP BIT(1) +#define OV5648_MIPI_SC_CTRL0_LANE_DIS_OP BIT(0) +#define OV5648_MIPI_SC_CTRL1_REG 0x3019 +#define OV5648_MISC_CTRL0_REG 0x3021 +#define OV5648_MIPI_SC_CTRL2_REG 0x3022 +#define OV5648_SUB_ID_REG 0x302a + +#define OV5648_PLL_CTRL0_REG 0x3034 +#define OV5648_PLL_CTRL0_PLL_CHARGE_PUMP(v) (((v) << 4) & GENMASK(6, 4)) +#define OV5648_PLL_CTRL0_BITS(v) ((v) & GENMASK(3, 0)) +#define OV5648_PLL_CTRL1_REG 0x3035 +#define OV5648_PLL_CTRL1_SYS_DIV(v) (((v) << 4) & GENMASK(7, 4)) +#define OV5648_PLL_CTRL1_MIPI_DIV(v) ((v) & GENMASK(3, 0)) +#define OV5648_PLL_MUL_REG 0x3036 +#define OV5648_PLL_MUL(v) ((v) & GENMASK(7, 0)) +#define OV5648_PLL_DIV_REG 0x3037 +#define OV5648_PLL_DIV_ROOT_DIV(v) ((((v) - 1) << 4) & BIT(4)) +#define OV5648_PLL_DIV_PLL_PRE_DIV(v) ((v) & GENMASK(3, 0)) +#define OV5648_PLL_DEBUG_REG 0x3038 +#define OV5648_PLL_BYPASS_REG 0x3039 + +#define OV5648_PLLS_BYPASS_REG 0x303a +#define OV5648_PLLS_MUL_REG 0x303b +#define OV5648_PLLS_MUL(v) ((v) & GENMASK(4, 0)) +#define OV5648_PLLS_CTRL_REG 0x303c +#define OV5648_PLLS_CTRL_PLL_CHARGE_PUMP(v) (((v) << 4) & GENMASK(6, 4)) +#define OV5648_PLLS_CTRL_SYS_DIV(v) ((v) & GENMASK(3, 0)) +#define OV5648_PLLS_DIV_REG 0x303d +#define OV5648_PLLS_DIV_PLLS_PRE_DIV(v) (((v) << 4) & GENMASK(5, 4)) +#define OV5648_PLLS_DIV_PLLS_DIV_R(v) ((((v) - 1) << 2) & BIT(2)) +#define OV5648_PLLS_DIV_PLLS_SEL_DIV(v) ((v) & GENMASK(1, 0)) + +#define OV5648_SRB_CTRL_REG 0x3106 +#define OV5648_SRB_CTRL_SCLK_DIV(v) (((v) << 2) & GENMASK(3, 2)) +#define OV5648_SRB_CTRL_RESET_ARBITER_EN BIT(1) +#define OV5648_SRB_CTRL_SCLK_ARBITER_EN BIT(0) + +/* Group Hold */ + +#define OV5648_GROUP_ADR0_REG 0x3200 +#define OV5648_GROUP_ADR1_REG 0x3201 +#define OV5648_GROUP_ADR2_REG 0x3202 +#define OV5648_GROUP_ADR3_REG 0x3203 +#define OV5648_GROUP_LEN0_REG 0x3204 +#define OV5648_GROUP_LEN1_REG 0x3205 +#define OV5648_GROUP_LEN2_REG 0x3206 +#define OV5648_GROUP_LEN3_REG 0x3207 +#define OV5648_GROUP_ACCESS_REG 0x3208 + +/* Exposure/gain/banding */ + +#define OV5648_EXPOSURE_CTRL_HH_REG 0x3500 +#define OV5648_EXPOSURE_CTRL_HH(v) (((v) & GENMASK(19, 16)) >> 16) +#define OV5648_EXPOSURE_CTRL_HH_VALUE(v) (((v) << 16) & GENMASK(19, 16)) +#define OV5648_EXPOSURE_CTRL_H_REG 0x3501 +#define OV5648_EXPOSURE_CTRL_H(v) (((v) & GENMASK(15, 8)) >> 8) +#define OV5648_EXPOSURE_CTRL_H_VALUE(v) (((v) << 8) & GENMASK(15, 8)) +#define OV5648_EXPOSURE_CTRL_L_REG 0x3502 +#define OV5648_EXPOSURE_CTRL_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_EXPOSURE_CTRL_L_VALUE(v) ((v) & GENMASK(7, 0)) +#define OV5648_MANUAL_CTRL_REG 0x3503 +#define OV5648_MANUAL_CTRL_FRAME_DELAY(v) (((v) << 4) & GENMASK(5, 4)) +#define OV5648_MANUAL_CTRL_AGC_MANUAL_EN BIT(1) +#define OV5648_MANUAL_CTRL_AEC_MANUAL_EN BIT(0) +#define OV5648_GAIN_CTRL_H_REG 0x350a +#define OV5648_GAIN_CTRL_H(v) (((v) & GENMASK(9, 8)) >> 8) +#define OV5648_GAIN_CTRL_H_VALUE(v) (((v) << 8) & GENMASK(9, 8)) +#define OV5648_GAIN_CTRL_L_REG 0x350b +#define OV5648_GAIN_CTRL_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_GAIN_CTRL_L_VALUE(v) ((v) & GENMASK(7, 0)) + +#define OV5648_ANALOG_CTRL0_REG_BASE 0x3600 +#define OV5648_ANALOG_CTRL1_REG_BASE 0x3700 + +#define OV5648_AEC_CTRL0_REG 0x3a00 +#define OV5648_AEC_CTRL0_DEBUG BIT(6) +#define OV5648_AEC_CTRL0_DEBAND_EN BIT(5) +#define OV5648_AEC_CTRL0_DEBAND_LOW_LIMIT_EN BIT(4) +#define OV5648_AEC_CTRL0_START_SEL_EN BIT(3) +#define OV5648_AEC_CTRL0_NIGHT_MODE_EN BIT(2) +#define OV5648_AEC_CTRL0_FREEZE_EN BIT(0) +#define OV5648_EXPOSURE_MIN_REG 0x3a01 +#define OV5648_EXPOSURE_MAX_60_H_REG 0x3a02 +#define OV5648_EXPOSURE_MAX_60_L_REG 0x3a03 +#define OV5648_AEC_CTRL5_REG 0x3a05 +#define OV5648_AEC_CTRL6_REG 0x3a06 +#define OV5648_AEC_CTRL7_REG 0x3a07 +#define OV5648_BANDING_STEP_50_H_REG 0x3a08 +#define OV5648_BANDING_STEP_50_L_REG 0x3a09 +#define OV5648_BANDING_STEP_60_H_REG 0x3a0a +#define OV5648_BANDING_STEP_60_L_REG 0x3a0b +#define OV5648_AEC_CTRLC_REG 0x3a0c +#define OV5648_BANDING_MAX_60_REG 0x3a0d +#define OV5648_BANDING_MAX_50_REG 0x3a0e +#define OV5648_WPT_REG 0x3a0f +#define OV5648_BPT_REG 0x3a10 +#define OV5648_VPT_HIGH_REG 0x3a11 +#define OV5648_AVG_MANUAL_REG 0x3a12 +#define OV5648_PRE_GAIN_REG 0x3a13 +#define OV5648_EXPOSURE_MAX_50_H_REG 0x3a14 +#define OV5648_EXPOSURE_MAX_50_L_REG 0x3a15 +#define OV5648_GAIN_BASE_NIGHT_REG 0x3a17 +#define OV5648_AEC_GAIN_CEILING_H_REG 0x3a18 +#define OV5648_AEC_GAIN_CEILING_L_REG 0x3a19 +#define OV5648_DIFF_MAX_REG 0x3a1a +#define OV5648_WPT2_REG 0x3a1b +#define OV5648_LED_ADD_ROW_H_REG 0x3a1c +#define OV5648_LED_ADD_ROW_L_REG 0x3a1d +#define OV5648_BPT2_REG 0x3a1e +#define OV5648_VPT_LOW_REG 0x3a1f +#define OV5648_AEC_CTRL20_REG 0x3a20 +#define OV5648_AEC_CTRL21_REG 0x3a21 + +#define OV5648_AVG_START_X_H_REG 0x5680 +#define OV5648_AVG_START_X_L_REG 0x5681 +#define OV5648_AVG_START_Y_H_REG 0x5682 +#define OV5648_AVG_START_Y_L_REG 0x5683 +#define OV5648_AVG_WINDOW_X_H_REG 0x5684 +#define OV5648_AVG_WINDOW_X_L_REG 0x5685 +#define OV5648_AVG_WINDOW_Y_H_REG 0x5686 +#define OV5648_AVG_WINDOW_Y_L_REG 0x5687 +#define OV5648_AVG_WEIGHT00_REG 0x5688 +#define OV5648_AVG_WEIGHT01_REG 0x5689 +#define OV5648_AVG_WEIGHT02_REG 0x568a +#define OV5648_AVG_WEIGHT03_REG 0x568b +#define OV5648_AVG_WEIGHT04_REG 0x568c +#define OV5648_AVG_WEIGHT05_REG 0x568d +#define OV5648_AVG_WEIGHT06_REG 0x568e +#define OV5648_AVG_WEIGHT07_REG 0x568f +#define OV5648_AVG_CTRL10_REG 0x5690 +#define OV5648_AVG_WEIGHT_SUM_REG 0x5691 +#define OV5648_AVG_READOUT_REG 0x5693 + +#define OV5648_DIG_CTRL0_REG 0x5a00 +#define OV5648_DIG_COMP_MAN_H_REG 0x5a02 +#define OV5648_DIG_COMP_MAN_L_REG 0x5a03 + +#define OV5648_GAINC_MAN_H_REG 0x5a20 +#define OV5648_GAINC_MAN_L_REG 0x5a21 +#define OV5648_GAINC_DGC_MAN_H_REG 0x5a22 +#define OV5648_GAINC_DGC_MAN_L_REG 0x5a23 +#define OV5648_GAINC_CTRL0_REG 0x5a24 + +#define OV5648_GAINF_ANA_NUM_REG 0x5a40 +#define OV5648_GAINF_DIG_GAIN_REG 0x5a41 + +/* Timing */ + +#define OV5648_CROP_START_X_H_REG 0x3800 +#define OV5648_CROP_START_X_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV5648_CROP_START_X_L_REG 0x3801 +#define OV5648_CROP_START_X_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_CROP_START_Y_H_REG 0x3802 +#define OV5648_CROP_START_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV5648_CROP_START_Y_L_REG 0x3803 +#define OV5648_CROP_START_Y_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_CROP_END_X_H_REG 0x3804 +#define OV5648_CROP_END_X_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV5648_CROP_END_X_L_REG 0x3805 +#define OV5648_CROP_END_X_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_CROP_END_Y_H_REG 0x3806 +#define OV5648_CROP_END_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV5648_CROP_END_Y_L_REG 0x3807 +#define OV5648_CROP_END_Y_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_OUTPUT_SIZE_X_H_REG 0x3808 +#define OV5648_OUTPUT_SIZE_X_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV5648_OUTPUT_SIZE_X_L_REG 0x3809 +#define OV5648_OUTPUT_SIZE_X_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_OUTPUT_SIZE_Y_H_REG 0x380a +#define OV5648_OUTPUT_SIZE_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV5648_OUTPUT_SIZE_Y_L_REG 0x380b +#define OV5648_OUTPUT_SIZE_Y_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_HTS_H_REG 0x380c +#define OV5648_HTS_H(v) (((v) & GENMASK(12, 8)) >> 8) +#define OV5648_HTS_L_REG 0x380d +#define OV5648_HTS_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_VTS_H_REG 0x380e +#define OV5648_VTS_H(v) (((v) & GENMASK(15, 8)) >> 8) +#define OV5648_VTS_L_REG 0x380f +#define OV5648_VTS_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_OFFSET_X_H_REG 0x3810 +#define OV5648_OFFSET_X_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV5648_OFFSET_X_L_REG 0x3811 +#define OV5648_OFFSET_X_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_OFFSET_Y_H_REG 0x3812 +#define OV5648_OFFSET_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV5648_OFFSET_Y_L_REG 0x3813 +#define OV5648_OFFSET_Y_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_SUB_INC_X_REG 0x3814 +#define OV5648_SUB_INC_X_ODD(v) (((v) << 4) & GENMASK(7, 4)) +#define OV5648_SUB_INC_X_EVEN(v) ((v) & GENMASK(3, 0)) +#define OV5648_SUB_INC_Y_REG 0x3815 +#define OV5648_SUB_INC_Y_ODD(v) (((v) << 4) & GENMASK(7, 4)) +#define OV5648_SUB_INC_Y_EVEN(v) ((v) & GENMASK(3, 0)) +#define OV5648_HSYNCST_H_REG 0x3816 +#define OV5648_HSYNCST_H(v) (((v) >> 8) & 0xf) +#define OV5648_HSYNCST_L_REG 0x3817 +#define OV5648_HSYNCST_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_HSYNCW_H_REG 0x3818 +#define OV5648_HSYNCW_H(v) (((v) >> 8) & 0xf) +#define OV5648_HSYNCW_L_REG 0x3819 +#define OV5648_HSYNCW_L(v) ((v) & GENMASK(7, 0)) + +#define OV5648_TC20_REG 0x3820 +#define OV5648_TC20_DEBUG BIT(6) +#define OV5648_TC20_FLIP_VERT_ISP_EN BIT(2) +#define OV5648_TC20_FLIP_VERT_SENSOR_EN BIT(1) +#define OV5648_TC20_BINNING_VERT_EN BIT(0) +#define OV5648_TC21_REG 0x3821 +#define OV5648_TC21_FLIP_HORZ_ISP_EN BIT(2) +#define OV5648_TC21_FLIP_HORZ_SENSOR_EN BIT(1) +#define OV5648_TC21_BINNING_HORZ_EN BIT(0) + +/* Strobe/exposure */ + +#define OV5648_STROBE_REG 0x3b00 +#define OV5648_FREX_EXP_HH_REG 0x3b01 +#define OV5648_SHUTTER_DLY_H_REG 0x3b02 +#define OV5648_SHUTTER_DLY_L_REG 0x3b03 +#define OV5648_FREX_EXP_H_REG 0x3b04 +#define OV5648_FREX_EXP_L_REG 0x3b05 +#define OV5648_FREX_CTRL_REG 0x3b06 +#define OV5648_FREX_MODE_SEL_REG 0x3b07 +#define OV5648_FREX_MODE_SEL_FREX_SA1 BIT(4) +#define OV5648_FREX_MODE_SEL_FX1_FM_EN BIT(3) +#define OV5648_FREX_MODE_SEL_FREX_INV BIT(2) +#define OV5648_FREX_MODE_SEL_MODE1 0x0 +#define OV5648_FREX_MODE_SEL_MODE2 0x1 +#define OV5648_FREX_MODE_SEL_ROLLING 0x2 +#define OV5648_FREX_EXP_REQ_REG 0x3b08 +#define OV5648_FREX_SHUTTER_DLY_REG 0x3b09 +#define OV5648_FREX_RST_LEN_REG 0x3b0a +#define OV5648_STROBE_WIDTH_HH_REG 0x3b0b +#define OV5648_STROBE_WIDTH_H_REG 0x3b0c + +/* OTP */ + +#define OV5648_OTP_DATA_REG_BASE 0x3d00 +#define OV5648_OTP_PROGRAM_CTRL_REG 0x3d80 +#define OV5648_OTP_LOAD_CTRL_REG 0x3d81 + +/* PSRAM */ + +#define OV5648_PSRAM_CTRL1_REG 0x3f01 +#define OV5648_PSRAM_CTRLF_REG 0x3f0f + +/* Black Level */ + +#define OV5648_BLC_CTRL0_REG 0x4000 +#define OV5648_BLC_CTRL1_REG 0x4001 +#define OV5648_BLC_CTRL1_START_LINE(v) ((v) & GENMASK(5, 0)) +#define OV5648_BLC_CTRL2_REG 0x4002 +#define OV5648_BLC_CTRL2_AUTO_EN BIT(6) +#define OV5648_BLC_CTRL2_RESET_FRAME_NUM(v) ((v) & GENMASK(5, 0)) +#define OV5648_BLC_CTRL3_REG 0x4003 +#define OV5648_BLC_LINE_NUM_REG 0x4004 +#define OV5648_BLC_LINE_NUM(v) ((v) & GENMASK(7, 0)) +#define OV5648_BLC_CTRL5_REG 0x4005 +#define OV5648_BLC_CTRL5_UPDATE_EN BIT(1) +#define OV5648_BLC_LEVEL_REG 0x4009 + +/* Frame */ + +#define OV5648_FRAME_CTRL_REG 0x4200 +#define OV5648_FRAME_ON_NUM_REG 0x4201 +#define OV5648_FRAME_OFF_NUM_REG 0x4202 + +/* MIPI CSI-2 */ + +#define OV5648_MIPI_CTRL0_REG 0x4800 +#define OV5648_MIPI_CTRL0_CLK_LANE_AUTOGATE BIT(5) +#define OV5648_MIPI_CTRL0_LANE_SYNC_EN BIT(4) +#define OV5648_MIPI_CTRL0_LANE_SELECT_LANE1 0 +#define OV5648_MIPI_CTRL0_LANE_SELECT_LANE2 BIT(3) +#define OV5648_MIPI_CTRL0_IDLE_LP00 0 +#define OV5648_MIPI_CTRL0_IDLE_LP11 BIT(2) + +#define OV5648_MIPI_CTRL1_REG 0x4801 +#define OV5648_MIPI_CTRL2_REG 0x4802 +#define OV5648_MIPI_CTRL3_REG 0x4803 +#define OV5648_MIPI_CTRL4_REG 0x4804 +#define OV5648_MIPI_CTRL5_REG 0x4805 +#define OV5648_MIPI_MAX_FRAME_COUNT_H_REG 0x4810 +#define OV5648_MIPI_MAX_FRAME_COUNT_L_REG 0x4811 +#define OV5648_MIPI_CTRL14_REG 0x4814 +#define OV5648_MIPI_DT_SPKT_REG 0x4815 +#define OV5648_MIPI_HS_ZERO_MIN_H_REG 0x4818 +#define OV5648_MIPI_HS_ZERO_MIN_L_REG 0x4819 +#define OV5648_MIPI_HS_TRAIN_MIN_H_REG 0x481a +#define OV5648_MIPI_HS_TRAIN_MIN_L_REG 0x481b +#define OV5648_MIPI_CLK_ZERO_MIN_H_REG 0x481c +#define OV5648_MIPI_CLK_ZERO_MIN_L_REG 0x481d +#define OV5648_MIPI_CLK_PREPARE_MIN_H_REG 0x481e +#define OV5648_MIPI_CLK_PREPARE_MIN_L_REG 0x481f +#define OV5648_MIPI_CLK_POST_MIN_H_REG 0x4820 +#define OV5648_MIPI_CLK_POST_MIN_L_REG 0x4821 +#define OV5648_MIPI_CLK_TRAIL_MIN_H_REG 0x4822 +#define OV5648_MIPI_CLK_TRAIL_MIN_L_REG 0x4823 +#define OV5648_MIPI_LPX_P_MIN_H_REG 0x4824 +#define OV5648_MIPI_LPX_P_MIN_L_REG 0x4825 +#define OV5648_MIPI_HS_PREPARE_MIN_H_REG 0x4826 +#define OV5648_MIPI_HS_PREPARE_MIN_L_REG 0x4827 +#define OV5648_MIPI_HS_EXIT_MIN_H_REG 0x4828 +#define OV5648_MIPI_HS_EXIT_MIN_L_REG 0x4829 +#define OV5648_MIPI_HS_ZERO_MIN_UI_REG 0x482a +#define OV5648_MIPI_HS_TRAIL_MIN_UI_REG 0x482b +#define OV5648_MIPI_CLK_ZERO_MIN_UI_REG 0x482c +#define OV5648_MIPI_CLK_PREPARE_MIN_UI_REG 0x482d +#define OV5648_MIPI_CLK_POST_MIN_UI_REG 0x482e +#define OV5648_MIPI_CLK_TRAIL_MIN_UI_REG 0x482f +#define OV5648_MIPI_LPX_P_MIN_UI_REG 0x4830 +#define OV5648_MIPI_HS_PREPARE_MIN_UI_REG 0x4831 +#define OV5648_MIPI_HS_EXIT_MIN_UI_REG 0x4832 +#define OV5648_MIPI_REG_MIN_H_REG 0x4833 +#define OV5648_MIPI_REG_MIN_L_REG 0x4834 +#define OV5648_MIPI_REG_MAX_H_REG 0x4835 +#define OV5648_MIPI_REG_MAX_L_REG 0x4836 +#define OV5648_MIPI_PCLK_PERIOD_REG 0x4837 +#define OV5648_MIPI_WKUP_DLY_REG 0x4838 +#define OV5648_MIPI_LP_GPIO_REG 0x483b +#define OV5648_MIPI_SNR_PCLK_DIV_REG 0x4843 + +/* ISP */ + +#define OV5648_ISP_CTRL0_REG 0x5000 +#define OV5648_ISP_CTRL0_BLACK_CORRECT_EN BIT(2) +#define OV5648_ISP_CTRL0_WHITE_CORRECT_EN BIT(1) +#define OV5648_ISP_CTRL1_REG 0x5001 +#define OV5648_ISP_CTRL1_AWB_EN BIT(0) +#define OV5648_ISP_CTRL2_REG 0x5002 +#define OV5648_ISP_CTRL2_WIN_EN BIT(6) +#define OV5648_ISP_CTRL2_OTP_EN BIT(1) +#define OV5648_ISP_CTRL2_AWB_GAIN_EN BIT(0) +#define OV5648_ISP_CTRL3_REG 0x5003 +#define OV5648_ISP_CTRL3_BUF_EN BIT(3) +#define OV5648_ISP_CTRL3_BIN_MAN_SET BIT(2) +#define OV5648_ISP_CTRL3_BIN_AUTO_EN BIT(1) +#define OV5648_ISP_CTRL4_REG 0x5004 +#define OV5648_ISP_CTRL5_REG 0x5005 +#define OV5648_ISP_CTRL6_REG 0x5006 +#define OV5648_ISP_CTRL7_REG 0x5007 +#define OV5648_ISP_MAN_OFFSET_X_H_REG 0x5008 +#define OV5648_ISP_MAN_OFFSET_X_L_REG 0x5009 +#define OV5648_ISP_MAN_OFFSET_Y_H_REG 0x500a +#define OV5648_ISP_MAN_OFFSET_Y_L_REG 0x500b +#define OV5648_ISP_MAN_WIN_OFFSET_X_H_REG 0x500c +#define OV5648_ISP_MAN_WIN_OFFSET_X_L_REG 0x500d +#define OV5648_ISP_MAN_WIN_OFFSET_Y_H_REG 0x500e +#define OV5648_ISP_MAN_WIN_OFFSET_Y_L_REG 0x500f +#define OV5648_ISP_MAN_WIN_OUTPUT_X_H_REG 0x5010 +#define OV5648_ISP_MAN_WIN_OUTPUT_X_L_REG 0x5011 +#define OV5648_ISP_MAN_WIN_OUTPUT_Y_H_REG 0x5012 +#define OV5648_ISP_MAN_WIN_OUTPUT_Y_L_REG 0x5013 +#define OV5648_ISP_MAN_INPUT_X_H_REG 0x5014 +#define OV5648_ISP_MAN_INPUT_X_L_REG 0x5015 +#define OV5648_ISP_MAN_INPUT_Y_H_REG 0x5016 +#define OV5648_ISP_MAN_INPUT_Y_L_REG 0x5017 +#define OV5648_ISP_CTRL18_REG 0x5018 +#define OV5648_ISP_CTRL19_REG 0x5019 +#define OV5648_ISP_CTRL1A_REG 0x501a +#define OV5648_ISP_CTRL1D_REG 0x501d +#define OV5648_ISP_CTRL1F_REG 0x501f +#define OV5648_ISP_CTRL1F_OUTPUT_EN 3 +#define OV5648_ISP_CTRL25_REG 0x5025 + +#define OV5648_ISP_CTRL3D_REG 0x503d +#define OV5648_ISP_CTRL3D_PATTERN_EN BIT(7) +#define OV5648_ISP_CTRL3D_ROLLING_BAR_EN BIT(6) +#define OV5648_ISP_CTRL3D_TRANSPARENT_MODE BIT(5) +#define OV5648_ISP_CTRL3D_SQUARES_BW_MODE BIT(4) +#define OV5648_ISP_CTRL3D_PATTERN_COLOR_BARS 0 +#define OV5648_ISP_CTRL3D_PATTERN_RANDOM_DATA 1 +#define OV5648_ISP_CTRL3D_PATTERN_COLOR_SQUARES 2 +#define OV5648_ISP_CTRL3D_PATTERN_INPUT 3 + +#define OV5648_ISP_CTRL3E_REG 0x503e +#define OV5648_ISP_CTRL4B_REG 0x504b +#define OV5648_ISP_CTRL4B_POST_BIN_H_EN BIT(5) +#define OV5648_ISP_CTRL4B_POST_BIN_V_EN BIT(4) +#define OV5648_ISP_CTRL4C_REG 0x504c +#define OV5648_ISP_CTRL57_REG 0x5057 +#define OV5648_ISP_CTRL58_REG 0x5058 +#define OV5648_ISP_CTRL59_REG 0x5059 + +#define OV5648_ISP_WINDOW_START_X_H_REG 0x5980 +#define OV5648_ISP_WINDOW_START_X_L_REG 0x5981 +#define OV5648_ISP_WINDOW_START_Y_H_REG 0x5982 +#define OV5648_ISP_WINDOW_START_Y_L_REG 0x5983 +#define OV5648_ISP_WINDOW_WIN_X_H_REG 0x5984 +#define OV5648_ISP_WINDOW_WIN_X_L_REG 0x5985 +#define OV5648_ISP_WINDOW_WIN_Y_H_REG 0x5986 +#define OV5648_ISP_WINDOW_WIN_Y_L_REG 0x5987 +#define OV5648_ISP_WINDOW_MAN_REG 0x5988 + +/* White Balance */ + +#define OV5648_AWB_CTRL_REG 0x5180 +#define OV5648_AWB_CTRL_FAST_AWB BIT(6) +#define OV5648_AWB_CTRL_GAIN_FREEZE_EN BIT(5) +#define OV5648_AWB_CTRL_SUM_FREEZE_EN BIT(4) +#define OV5648_AWB_CTRL_GAIN_MANUAL_EN BIT(3) + +#define OV5648_AWB_DELTA_REG 0x5181 +#define OV5648_AWB_STABLE_RANGE_REG 0x5182 +#define OV5648_AWB_STABLE_RANGE_WIDE_REG 0x5183 +#define OV5648_HSIZE_MAN_REG 0x5185 + +#define OV5648_GAIN_RED_MAN_H_REG 0x5186 +#define OV5648_GAIN_RED_MAN_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV5648_GAIN_RED_MAN_L_REG 0x5187 +#define OV5648_GAIN_RED_MAN_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_GAIN_GREEN_MAN_H_REG 0x5188 +#define OV5648_GAIN_GREEN_MAN_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV5648_GAIN_GREEN_MAN_L_REG 0x5189 +#define OV5648_GAIN_GREEN_MAN_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_GAIN_BLUE_MAN_H_REG 0x518a +#define OV5648_GAIN_BLUE_MAN_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV5648_GAIN_BLUE_MAN_L_REG 0x518b +#define OV5648_GAIN_BLUE_MAN_L(v) ((v) & GENMASK(7, 0)) +#define OV5648_GAIN_RED_LIMIT_REG 0x518c +#define OV5648_GAIN_GREEN_LIMIT_REG 0x518d +#define OV5648_GAIN_BLUE_LIMIT_REG 0x518e +#define OV5648_AWB_FRAME_COUNT_REG 0x518f +#define OV5648_AWB_BASE_MAN_REG 0x51df + +/* Macros */ + +#define ov5648_subdev_sensor(s) \ + container_of(s, struct ov5648_sensor, subdev) + +#define ov5648_ctrl_subdev(c) \ + (&container_of(c->handler, struct ov5648_sensor, ctrls.handler)->subdev) + +/* Data structures */ + +struct ov5648_register_value { + u16 address; + u8 value; + unsigned int delay_ms; +}; + +/* + * PLL1 Clock Tree: + * + * +-< XVCLK + * | + * +-+ pll_pre_div (0x3037 [3:0], special values: 5: 1.5, 7: 2.5) + * | + * +-+ pll_mul (0x3036 [7:0]) + * | + * +-+ sys_div (0x3035 [7:4]) + * | + * +-+ mipi_div (0x3035 [3:0]) + * | | + * | +-> MIPI_SCLK + * | | + * | +-+ mipi_phy_div (2) + * | | + * | +-> MIPI_CLK + * | + * +-+ root_div (0x3037 [4]) + * | + * +-+ bit_div (0x3034 [3:0], 8 bits: 2, 10 bits: 2.5, other: 1) + * | + * +-+ sclk_div (0x3106 [3:2]) + * | + * +-> SCLK + * | + * +-+ mipi_div (0x3035, 1: PCLK = SCLK) + * | + * +-> PCLK + */ + +struct ov5648_pll1_config { + unsigned int pll_pre_div; + unsigned int pll_mul; + unsigned int sys_div; + unsigned int root_div; + unsigned int sclk_div; + unsigned int mipi_div; +}; + +/* + * PLL2 Clock Tree: + * + * +-< XVCLK + * | + * +-+ plls_pre_div (0x303d [5:4], special values: 0: 1, 1: 1.5) + * | + * +-+ plls_div_r (0x303d [2]) + * | + * +-+ plls_mul (0x303b [4:0]) + * | + * +-+ sys_div (0x303c [3:0]) + * | + * +-+ sel_div (0x303d [1:0], special values: 0: 1, 3: 2.5) + * | + * +-> ADCLK + */ + +struct ov5648_pll2_config { + unsigned int plls_pre_div; + unsigned int plls_div_r; + unsigned int plls_mul; + unsigned int sys_div; + unsigned int sel_div; +}; + +/* + * General formulas for (array-centered) mode calculation: + * - photo_array_width = 2624 + * - crop_start_x = (photo_array_width - output_size_x) / 2 + * - crop_end_x = crop_start_x + offset_x + output_size_x - 1 + * + * - photo_array_height = 1956 + * - crop_start_y = (photo_array_height - output_size_y) / 2 + * - crop_end_y = crop_start_y + offset_y + output_size_y - 1 + */ + +struct ov5648_mode { + unsigned int crop_start_x; + unsigned int offset_x; + unsigned int output_size_x; + unsigned int crop_end_x; + unsigned int hts; + + unsigned int crop_start_y; + unsigned int offset_y; + unsigned int output_size_y; + unsigned int crop_end_y; + unsigned int vts; + + bool binning_x; + bool binning_y; + + unsigned int inc_x_odd; + unsigned int inc_x_even; + unsigned int inc_y_odd; + unsigned int inc_y_even; + + /* 8-bit frame interval followed by 10-bit frame interval. */ + struct v4l2_fract frame_interval[2]; + + /* 8-bit config followed by 10-bit config. */ + const struct ov5648_pll1_config *pll1_config[2]; + const struct ov5648_pll2_config *pll2_config; + + const struct ov5648_register_value *register_values; + unsigned int register_values_count; +}; + +struct ov5648_state { + const struct ov5648_mode *mode; + u32 mbus_code; + + bool streaming; +}; + +struct ov5648_ctrls { + struct v4l2_ctrl *exposure_auto; + struct v4l2_ctrl *exposure; + + struct v4l2_ctrl *gain_auto; + struct v4l2_ctrl *gain; + + struct v4l2_ctrl *white_balance_auto; + struct v4l2_ctrl *red_balance; + struct v4l2_ctrl *blue_balance; + + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + + struct v4l2_ctrl_handler handler; +} __packed; + +struct ov5648_sensor { + struct device *dev; + struct i2c_client *i2c_client; + struct gpio_desc *reset; + struct gpio_desc *powerdown; + struct regulator *avdd; + struct regulator *dvdd; + struct regulator *dovdd; + struct clk *xvclk; + + struct v4l2_fwnode_endpoint endpoint; + struct v4l2_subdev subdev; + struct media_pad pad; + + struct mutex mutex; + + struct ov5648_state state; + struct ov5648_ctrls ctrls; +}; + +/* Static definitions */ + +/* + * XVCLK = 24 MHz + * SCLK = 84 MHz + * PCLK = 84 MHz + */ +static const struct ov5648_pll1_config ov5648_pll1_config_native_8_bits = { + .pll_pre_div = 3, + .pll_mul = 84, + .sys_div = 2, + .root_div = 1, + .sclk_div = 1, + .mipi_div = 1, +}; + +/* + * XVCLK = 24 MHz + * SCLK = 84 MHz + * PCLK = 84 MHz + */ +static const struct ov5648_pll1_config ov5648_pll1_config_native_10_bits = { + .pll_pre_div = 3, + .pll_mul = 105, + .sys_div = 2, + .root_div = 1, + .sclk_div = 1, + .mipi_div = 1, +}; + +/* + * XVCLK = 24 MHz + * ADCLK = 200 MHz + */ +static const struct ov5648_pll2_config ov5648_pll2_config_native = { + .plls_pre_div = 3, + .plls_div_r = 1, + .plls_mul = 25, + .sys_div = 1, + .sel_div = 1, +}; + +static const struct ov5648_mode ov5648_modes[] = { + /* 2592x1944 */ + { + /* Horizontal */ + .crop_start_x = 16, + .offset_x = 0, + .output_size_x = 2592, + .crop_end_x = 2607, + .hts = 2816, + + /* Vertical */ + .crop_start_y = 6, + .offset_y = 0, + .output_size_y = 1944, + .crop_end_y = 1949, + .vts = 1984, + + /* Subsample increase */ + .inc_x_odd = 1, + .inc_x_even = 1, + .inc_y_odd = 1, + .inc_y_even = 1, + + /* Frame Interval */ + .frame_interval = { + { 1, 15 }, + { 1, 15 }, + }, + + /* PLL */ + .pll1_config = { + &ov5648_pll1_config_native_8_bits, + &ov5648_pll1_config_native_10_bits, + }, + .pll2_config = &ov5648_pll2_config_native, + }, + /* 1600x1200 (UXGA) */ + { + /* Horizontal */ + .crop_start_x = 512, + .offset_x = 0, + .output_size_x = 1600, + .crop_end_x = 2111, + .hts = 2816, + + /* Vertical */ + .crop_start_y = 378, + .offset_y = 0, + .output_size_y = 1200, + .crop_end_y = 1577, + .vts = 1984, + + /* Subsample increase */ + .inc_x_odd = 1, + .inc_x_even = 1, + .inc_y_odd = 1, + .inc_y_even = 1, + + /* Frame Interval */ + .frame_interval = { + { 1, 15 }, + { 1, 15 }, + }, + + /* PLL */ + .pll1_config = { + &ov5648_pll1_config_native_8_bits, + &ov5648_pll1_config_native_10_bits, + }, + .pll2_config = &ov5648_pll2_config_native, + }, + /* 1920x1080 (Full HD) */ + { + /* Horizontal */ + .crop_start_x = 352, + .offset_x = 0, + .output_size_x = 1920, + .crop_end_x = 2271, + .hts = 2816, + + /* Vertical */ + .crop_start_y = 438, + .offset_y = 0, + .output_size_y = 1080, + .crop_end_y = 1517, + .vts = 1984, + + /* Subsample increase */ + .inc_x_odd = 1, + .inc_x_even = 1, + .inc_y_odd = 1, + .inc_y_even = 1, + + /* Frame Interval */ + .frame_interval = { + { 1, 15 }, + { 1, 15 }, + }, + + /* PLL */ + .pll1_config = { + &ov5648_pll1_config_native_8_bits, + &ov5648_pll1_config_native_10_bits, + }, + .pll2_config = &ov5648_pll2_config_native, + }, + /* 1280x960 */ + { + /* Horizontal */ + .crop_start_x = 16, + .offset_x = 8, + .output_size_x = 1280, + .crop_end_x = 2607, + .hts = 1912, + + /* Vertical */ + .crop_start_y = 6, + .offset_y = 6, + .output_size_y = 960, + .crop_end_y = 1949, + .vts = 1496, + + /* Binning */ + .binning_x = true, + + /* Subsample increase */ + .inc_x_odd = 3, + .inc_x_even = 1, + .inc_y_odd = 3, + .inc_y_even = 1, + + /* Frame Interval */ + .frame_interval = { + { 1, 30 }, + { 1, 30 }, + }, + + /* PLL */ + .pll1_config = { + &ov5648_pll1_config_native_8_bits, + &ov5648_pll1_config_native_10_bits, + }, + .pll2_config = &ov5648_pll2_config_native, + }, + /* 1280x720 (HD) */ + { + /* Horizontal */ + .crop_start_x = 16, + .offset_x = 8, + .output_size_x = 1280, + .crop_end_x = 2607, + .hts = 1912, + + /* Vertical */ + .crop_start_y = 254, + .offset_y = 2, + .output_size_y = 720, + .crop_end_y = 1701, + .vts = 1496, + + /* Binning */ + .binning_x = true, + + /* Subsample increase */ + .inc_x_odd = 3, + .inc_x_even = 1, + .inc_y_odd = 3, + .inc_y_even = 1, + + /* Frame Interval */ + .frame_interval = { + { 1, 30 }, + { 1, 30 }, + }, + + /* PLL */ + .pll1_config = { + &ov5648_pll1_config_native_8_bits, + &ov5648_pll1_config_native_10_bits, + }, + .pll2_config = &ov5648_pll2_config_native, + }, + /* 640x480 (VGA) */ + { + /* Horizontal */ + .crop_start_x = 0, + .offset_x = 8, + .output_size_x = 640, + .crop_end_x = 2623, + .hts = 1896, + + /* Vertical */ + .crop_start_y = 0, + .offset_y = 2, + .output_size_y = 480, + .crop_end_y = 1953, + .vts = 984, + + /* Binning */ + .binning_x = true, + + /* Subsample increase */ + .inc_x_odd = 7, + .inc_x_even = 1, + .inc_y_odd = 7, + .inc_y_even = 1, + + /* Frame Interval */ + .frame_interval = { + { 1, 30 }, + { 1, 30 }, + }, + + /* PLL */ + .pll1_config = { + &ov5648_pll1_config_native_8_bits, + &ov5648_pll1_config_native_10_bits, + }, + .pll2_config = &ov5648_pll2_config_native, + }, +}; + +static const u32 ov5648_mbus_codes[] = { + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SBGGR10_1X10, +}; + +static const struct ov5648_register_value ov5648_init_sequence[] = { + /* PSRAM */ + { OV5648_PSRAM_CTRL1_REG, 0x0d }, + { OV5648_PSRAM_CTRLF_REG, 0xf5 }, +}; + +static const s64 ov5648_link_freq_menu[] = { + 210000000, + 168000000, +}; + +static const char *const ov5648_test_pattern_menu[] = { + "Disabled", + "Random data", + "Color bars", + "Color bars with rolling bar", + "Color squares", + "Color squares with rolling bar" +}; + +static const u8 ov5648_test_pattern_bits[] = { + 0, + OV5648_ISP_CTRL3D_PATTERN_EN | OV5648_ISP_CTRL3D_PATTERN_RANDOM_DATA, + OV5648_ISP_CTRL3D_PATTERN_EN | OV5648_ISP_CTRL3D_PATTERN_COLOR_BARS, + OV5648_ISP_CTRL3D_PATTERN_EN | OV5648_ISP_CTRL3D_ROLLING_BAR_EN | + OV5648_ISP_CTRL3D_PATTERN_COLOR_BARS, + OV5648_ISP_CTRL3D_PATTERN_EN | OV5648_ISP_CTRL3D_PATTERN_COLOR_SQUARES, + OV5648_ISP_CTRL3D_PATTERN_EN | OV5648_ISP_CTRL3D_ROLLING_BAR_EN | + OV5648_ISP_CTRL3D_PATTERN_COLOR_SQUARES, +}; + +/* Input/Output */ + +static int ov5648_read(struct ov5648_sensor *sensor, u16 address, u8 *value) +{ + unsigned char data[2] = { address >> 8, address & 0xff }; + struct i2c_client *client = sensor->i2c_client; + int ret; + + ret = i2c_master_send(client, data, sizeof(data)); + if (ret < 0) { + dev_dbg(&client->dev, "i2c send error at address %#04x\n", + address); + return ret; + } + + ret = i2c_master_recv(client, value, 1); + if (ret < 0) { + dev_dbg(&client->dev, "i2c recv error at address %#04x\n", + address); + return ret; + } + + return 0; +} + +static int ov5648_write(struct ov5648_sensor *sensor, u16 address, u8 value) +{ + unsigned char data[3] = { address >> 8, address & 0xff, value }; + struct i2c_client *client = sensor->i2c_client; + int ret; + + ret = i2c_master_send(client, data, sizeof(data)); + if (ret < 0) { + dev_dbg(&client->dev, "i2c send error at address %#04x\n", + address); + return ret; + } + + return 0; +} + +static int ov5648_write_sequence(struct ov5648_sensor *sensor, + const struct ov5648_register_value *sequence, + unsigned int sequence_count) +{ + unsigned int i; + int ret = 0; + + for (i = 0; i < sequence_count; i++) { + ret = ov5648_write(sensor, sequence[i].address, + sequence[i].value); + if (ret) + break; + + if (sequence[i].delay_ms) + msleep(sequence[i].delay_ms); + } + + return ret; +} + +static int ov5648_update_bits(struct ov5648_sensor *sensor, u16 address, + u8 mask, u8 bits) +{ + u8 value = 0; + int ret; + + ret = ov5648_read(sensor, address, &value); + if (ret) + return ret; + + value &= ~mask; + value |= bits; + + ret = ov5648_write(sensor, address, value); + if (ret) + return ret; + + return 0; +} + +/* Sensor */ + +static int ov5648_sw_reset(struct ov5648_sensor *sensor) +{ + return ov5648_write(sensor, OV5648_SW_RESET_REG, OV5648_SW_RESET_RESET); +} + +static int ov5648_sw_standby(struct ov5648_sensor *sensor, int standby) +{ + u8 value = 0; + + if (!standby) + value = OV5648_SW_STANDBY_STREAM_ON; + + return ov5648_write(sensor, OV5648_SW_STANDBY_REG, value); +} + +static int ov5648_chip_id_check(struct ov5648_sensor *sensor) +{ + u16 regs[] = { OV5648_CHIP_ID_H_REG, OV5648_CHIP_ID_L_REG }; + u8 values[] = { OV5648_CHIP_ID_H_VALUE, OV5648_CHIP_ID_L_VALUE }; + unsigned int i; + u8 value; + int ret; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + ret = ov5648_read(sensor, regs[i], &value); + if (ret < 0) + return ret; + + if (value != values[i]) { + dev_err(sensor->dev, + "chip id value mismatch: %#x instead of %#x\n", + value, values[i]); + return -EINVAL; + } + } + + return 0; +} + +static int ov5648_avdd_internal_power(struct ov5648_sensor *sensor, int on) +{ + return ov5648_write(sensor, OV5648_A_PWC_PK_O0_REG, + on ? 0 : OV5648_A_PWC_PK_O0_BP_REGULATOR_N); +} + +static int ov5648_pad_configure(struct ov5648_sensor *sensor) +{ + int ret; + + /* Configure pads as input. */ + + ret = ov5648_write(sensor, OV5648_PAD_OEN1_REG, 0); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_PAD_OEN2_REG, 0); + if (ret) + return ret; + + /* Disable FREX pin. */ + + return ov5648_write(sensor, OV5648_PAD_PK_REG, + OV5648_PAD_PK_DRIVE_STRENGTH_1X | + OV5648_PAD_PK_FREX_N); +} + +static int ov5648_mipi_configure(struct ov5648_sensor *sensor) +{ + struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 = + &sensor->endpoint.bus.mipi_csi2; + unsigned int lanes_count = bus_mipi_csi2->num_data_lanes; + int ret; + + ret = ov5648_write(sensor, OV5648_MIPI_CTRL0_REG, + OV5648_MIPI_CTRL0_CLK_LANE_AUTOGATE | + OV5648_MIPI_CTRL0_LANE_SELECT_LANE1 | + OV5648_MIPI_CTRL0_IDLE_LP11); + if (ret) + return ret; + + return ov5648_write(sensor, OV5648_MIPI_SC_CTRL0_REG, + OV5648_MIPI_SC_CTRL0_MIPI_LANES(lanes_count) | + OV5648_MIPI_SC_CTRL0_PHY_LP_RX_PD | + OV5648_MIPI_SC_CTRL0_MIPI_EN); +} + +static int ov5648_black_level_configure(struct ov5648_sensor *sensor) +{ + int ret; + + /* Up to 6 lines are available for black level calibration. */ + + ret = ov5648_write(sensor, OV5648_BLC_CTRL1_REG, + OV5648_BLC_CTRL1_START_LINE(2)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_BLC_CTRL2_REG, + OV5648_BLC_CTRL2_AUTO_EN | + OV5648_BLC_CTRL2_RESET_FRAME_NUM(5)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_BLC_LINE_NUM_REG, + OV5648_BLC_LINE_NUM(4)); + if (ret) + return ret; + + return ov5648_update_bits(sensor, OV5648_BLC_CTRL5_REG, + OV5648_BLC_CTRL5_UPDATE_EN, + OV5648_BLC_CTRL5_UPDATE_EN); +} + +static int ov5648_isp_configure(struct ov5648_sensor *sensor) +{ + u8 bits; + int ret; + + /* Enable black and white level correction. */ + bits = OV5648_ISP_CTRL0_BLACK_CORRECT_EN | + OV5648_ISP_CTRL0_WHITE_CORRECT_EN; + + ret = ov5648_update_bits(sensor, OV5648_ISP_CTRL0_REG, bits, bits); + if (ret) + return ret; + + /* Enable AWB. */ + ret = ov5648_write(sensor, OV5648_ISP_CTRL1_REG, + OV5648_ISP_CTRL1_AWB_EN); + if (ret) + return ret; + + /* Enable AWB gain and windowing. */ + ret = ov5648_write(sensor, OV5648_ISP_CTRL2_REG, + OV5648_ISP_CTRL2_WIN_EN | + OV5648_ISP_CTRL2_AWB_GAIN_EN); + if (ret) + return ret; + + /* Enable buffering and auto-binning. */ + ret = ov5648_write(sensor, OV5648_ISP_CTRL3_REG, + OV5648_ISP_CTRL3_BUF_EN | + OV5648_ISP_CTRL3_BIN_AUTO_EN); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_ISP_CTRL4_REG, 0); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_ISP_CTRL1F_REG, + OV5648_ISP_CTRL1F_OUTPUT_EN); + if (ret) + return ret; + + /* Enable post-binning filters. */ + ret = ov5648_write(sensor, OV5648_ISP_CTRL4B_REG, + OV5648_ISP_CTRL4B_POST_BIN_H_EN | + OV5648_ISP_CTRL4B_POST_BIN_V_EN); + if (ret) + return ret; + + /* Disable debanding and night mode. Debug bit seems necessary. */ + ret = ov5648_write(sensor, OV5648_AEC_CTRL0_REG, + OV5648_AEC_CTRL0_DEBUG | + OV5648_AEC_CTRL0_START_SEL_EN); + if (ret) + return ret; + + return ov5648_write(sensor, OV5648_MANUAL_CTRL_REG, + OV5648_MANUAL_CTRL_FRAME_DELAY(1)); +} + +static unsigned long ov5648_mode_pll1_rate(struct ov5648_sensor *sensor, + const struct ov5648_pll1_config *config) +{ + unsigned long xvclk_rate; + unsigned long pll1_rate; + + xvclk_rate = clk_get_rate(sensor->xvclk); + pll1_rate = xvclk_rate * config->pll_mul; + + switch (config->pll_pre_div) { + case 5: + pll1_rate *= 3; + pll1_rate /= 2; + break; + case 7: + pll1_rate *= 5; + pll1_rate /= 2; + break; + default: + pll1_rate /= config->pll_pre_div; + break; + } + + return pll1_rate; +} + +static int ov5648_mode_pll1_configure(struct ov5648_sensor *sensor, + const struct ov5648_mode *mode, + u32 mbus_code) +{ + const struct ov5648_pll1_config *config; + u8 value; + int ret; + + value = OV5648_PLL_CTRL0_PLL_CHARGE_PUMP(1); + + switch (mbus_code) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + config = mode->pll1_config[0]; + value |= OV5648_PLL_CTRL0_BITS(8); + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + config = mode->pll1_config[1]; + value |= OV5648_PLL_CTRL0_BITS(10); + break; + default: + return -EINVAL; + } + + ret = ov5648_write(sensor, OV5648_PLL_CTRL0_REG, value); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_PLL_DIV_REG, + OV5648_PLL_DIV_ROOT_DIV(config->root_div) | + OV5648_PLL_DIV_PLL_PRE_DIV(config->pll_pre_div)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_PLL_MUL_REG, + OV5648_PLL_MUL(config->pll_mul)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_PLL_CTRL1_REG, + OV5648_PLL_CTRL1_SYS_DIV(config->sys_div) | + OV5648_PLL_CTRL1_MIPI_DIV(config->mipi_div)); + if (ret) + return ret; + + return ov5648_write(sensor, OV5648_SRB_CTRL_REG, + OV5648_SRB_CTRL_SCLK_DIV(config->sclk_div) | + OV5648_SRB_CTRL_SCLK_ARBITER_EN); +} + +static int ov5648_mode_pll2_configure(struct ov5648_sensor *sensor, + const struct ov5648_mode *mode) +{ + const struct ov5648_pll2_config *config = mode->pll2_config; + int ret; + + ret = ov5648_write(sensor, OV5648_PLLS_DIV_REG, + OV5648_PLLS_DIV_PLLS_PRE_DIV(config->plls_pre_div) | + OV5648_PLLS_DIV_PLLS_DIV_R(config->plls_div_r) | + OV5648_PLLS_DIV_PLLS_SEL_DIV(config->sel_div)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_PLLS_MUL_REG, + OV5648_PLLS_MUL(config->plls_mul)); + if (ret) + return ret; + + return ov5648_write(sensor, OV5648_PLLS_CTRL_REG, + OV5648_PLLS_CTRL_PLL_CHARGE_PUMP(1) | + OV5648_PLLS_CTRL_SYS_DIV(config->sys_div)); +} + +static int ov5648_mode_configure(struct ov5648_sensor *sensor, + const struct ov5648_mode *mode, u32 mbus_code) +{ + int ret; + + /* Crop Start X */ + + ret = ov5648_write(sensor, OV5648_CROP_START_X_H_REG, + OV5648_CROP_START_X_H(mode->crop_start_x)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_CROP_START_X_L_REG, + OV5648_CROP_START_X_L(mode->crop_start_x)); + if (ret) + return ret; + + /* Offset X */ + + ret = ov5648_write(sensor, OV5648_OFFSET_X_H_REG, + OV5648_OFFSET_X_H(mode->offset_x)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_OFFSET_X_L_REG, + OV5648_OFFSET_X_L(mode->offset_x)); + if (ret) + return ret; + + /* Output Size X */ + + ret = ov5648_write(sensor, OV5648_OUTPUT_SIZE_X_H_REG, + OV5648_OUTPUT_SIZE_X_H(mode->output_size_x)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_OUTPUT_SIZE_X_L_REG, + OV5648_OUTPUT_SIZE_X_L(mode->output_size_x)); + if (ret) + return ret; + + /* Crop End X */ + + ret = ov5648_write(sensor, OV5648_CROP_END_X_H_REG, + OV5648_CROP_END_X_H(mode->crop_end_x)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_CROP_END_X_L_REG, + OV5648_CROP_END_X_L(mode->crop_end_x)); + if (ret) + return ret; + + /* Horizontal Total Size */ + + ret = ov5648_write(sensor, OV5648_HTS_H_REG, OV5648_HTS_H(mode->hts)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_HTS_L_REG, OV5648_HTS_L(mode->hts)); + if (ret) + return ret; + + /* Crop Start Y */ + + ret = ov5648_write(sensor, OV5648_CROP_START_Y_H_REG, + OV5648_CROP_START_Y_H(mode->crop_start_y)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_CROP_START_Y_L_REG, + OV5648_CROP_START_Y_L(mode->crop_start_y)); + if (ret) + return ret; + + /* Offset Y */ + + ret = ov5648_write(sensor, OV5648_OFFSET_Y_H_REG, + OV5648_OFFSET_Y_H(mode->offset_y)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_OFFSET_Y_L_REG, + OV5648_OFFSET_Y_L(mode->offset_y)); + if (ret) + return ret; + + /* Output Size Y */ + + ret = ov5648_write(sensor, OV5648_OUTPUT_SIZE_Y_H_REG, + OV5648_OUTPUT_SIZE_Y_H(mode->output_size_y)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_OUTPUT_SIZE_Y_L_REG, + OV5648_OUTPUT_SIZE_Y_L(mode->output_size_y)); + if (ret) + return ret; + + /* Crop End Y */ + + ret = ov5648_write(sensor, OV5648_CROP_END_Y_H_REG, + OV5648_CROP_END_Y_H(mode->crop_end_y)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_CROP_END_Y_L_REG, + OV5648_CROP_END_Y_L(mode->crop_end_y)); + if (ret) + return ret; + + /* Vertical Total Size */ + + ret = ov5648_write(sensor, OV5648_VTS_H_REG, OV5648_VTS_H(mode->vts)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_VTS_L_REG, OV5648_VTS_L(mode->vts)); + if (ret) + return ret; + + /* Flip/Mirror/Binning */ + + /* + * A debug bit is enabled by default and needs to be cleared for + * subsampling to work. + */ + ret = ov5648_update_bits(sensor, OV5648_TC20_REG, + OV5648_TC20_DEBUG | + OV5648_TC20_BINNING_VERT_EN, + mode->binning_y ? OV5648_TC20_BINNING_VERT_EN : + 0); + if (ret) + return ret; + + ret = ov5648_update_bits(sensor, OV5648_TC21_REG, + OV5648_TC21_BINNING_HORZ_EN, + mode->binning_x ? OV5648_TC21_BINNING_HORZ_EN : + 0); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_SUB_INC_X_REG, + OV5648_SUB_INC_X_ODD(mode->inc_x_odd) | + OV5648_SUB_INC_X_EVEN(mode->inc_x_even)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_SUB_INC_Y_REG, + OV5648_SUB_INC_Y_ODD(mode->inc_y_odd) | + OV5648_SUB_INC_Y_EVEN(mode->inc_y_even)); + if (ret) + return ret; + + /* PLLs */ + + ret = ov5648_mode_pll1_configure(sensor, mode, mbus_code); + if (ret) + return ret; + + ret = ov5648_mode_pll2_configure(sensor, mode); + if (ret) + return ret; + + /* Extra registers */ + + if (mode->register_values) { + ret = ov5648_write_sequence(sensor, mode->register_values, + mode->register_values_count); + if (ret) + return ret; + } + + return 0; +} + +static unsigned long ov5648_mode_mipi_clk_rate(struct ov5648_sensor *sensor, + const struct ov5648_mode *mode, + u32 mbus_code) +{ + const struct ov5648_pll1_config *config; + unsigned long pll1_rate; + + switch (mbus_code) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + config = mode->pll1_config[0]; + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + config = mode->pll1_config[1]; + break; + default: + return 0; + } + + pll1_rate = ov5648_mode_pll1_rate(sensor, config); + + return pll1_rate / config->sys_div / config->mipi_div / 2; +} + +/* Exposure */ + +static int ov5648_exposure_auto_configure(struct ov5648_sensor *sensor, + bool enable) +{ + return ov5648_update_bits(sensor, OV5648_MANUAL_CTRL_REG, + OV5648_MANUAL_CTRL_AEC_MANUAL_EN, + enable ? 0 : OV5648_MANUAL_CTRL_AEC_MANUAL_EN); +} + +static int ov5648_exposure_configure(struct ov5648_sensor *sensor, u32 exposure) +{ + struct ov5648_ctrls *ctrls = &sensor->ctrls; + int ret; + + if (ctrls->exposure_auto->val != V4L2_EXPOSURE_MANUAL) + return -EINVAL; + + ret = ov5648_write(sensor, OV5648_EXPOSURE_CTRL_HH_REG, + OV5648_EXPOSURE_CTRL_HH(exposure)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_EXPOSURE_CTRL_H_REG, + OV5648_EXPOSURE_CTRL_H(exposure)); + if (ret) + return ret; + + return ov5648_write(sensor, OV5648_EXPOSURE_CTRL_L_REG, + OV5648_EXPOSURE_CTRL_L(exposure)); +} + +static int ov5648_exposure_value(struct ov5648_sensor *sensor, + u32 *exposure) +{ + u8 exposure_hh = 0, exposure_h = 0, exposure_l = 0; + int ret; + + ret = ov5648_read(sensor, OV5648_EXPOSURE_CTRL_HH_REG, &exposure_hh); + if (ret) + return ret; + + ret = ov5648_read(sensor, OV5648_EXPOSURE_CTRL_H_REG, &exposure_h); + if (ret) + return ret; + + ret = ov5648_read(sensor, OV5648_EXPOSURE_CTRL_L_REG, &exposure_l); + if (ret) + return ret; + + *exposure = OV5648_EXPOSURE_CTRL_HH_VALUE((u32)exposure_hh) | + OV5648_EXPOSURE_CTRL_H_VALUE((u32)exposure_h) | + OV5648_EXPOSURE_CTRL_L_VALUE((u32)exposure_l); + + return 0; +} + +/* Gain */ + +static int ov5648_gain_auto_configure(struct ov5648_sensor *sensor, bool enable) +{ + return ov5648_update_bits(sensor, OV5648_MANUAL_CTRL_REG, + OV5648_MANUAL_CTRL_AGC_MANUAL_EN, + enable ? 0 : OV5648_MANUAL_CTRL_AGC_MANUAL_EN); +} + +static int ov5648_gain_configure(struct ov5648_sensor *sensor, u32 gain) +{ + struct ov5648_ctrls *ctrls = &sensor->ctrls; + int ret; + + if (ctrls->gain_auto->val) + return -EINVAL; + + ret = ov5648_write(sensor, OV5648_GAIN_CTRL_H_REG, + OV5648_GAIN_CTRL_H(gain)); + if (ret) + return ret; + + return ov5648_write(sensor, OV5648_GAIN_CTRL_L_REG, + OV5648_GAIN_CTRL_L(gain)); +} + +static int ov5648_gain_value(struct ov5648_sensor *sensor, u32 *gain) +{ + u8 gain_h = 0, gain_l = 0; + int ret; + + ret = ov5648_read(sensor, OV5648_GAIN_CTRL_H_REG, &gain_h); + if (ret) + return ret; + + ret = ov5648_read(sensor, OV5648_GAIN_CTRL_L_REG, &gain_l); + if (ret) + return ret; + + *gain = OV5648_GAIN_CTRL_H_VALUE((u32)gain_h) | + OV5648_GAIN_CTRL_L_VALUE((u32)gain_l); + + return 0; +} + +/* White Balance */ + +static int ov5648_white_balance_auto_configure(struct ov5648_sensor *sensor, + bool enable) +{ + return ov5648_write(sensor, OV5648_AWB_CTRL_REG, + enable ? 0 : OV5648_AWB_CTRL_GAIN_MANUAL_EN); +} + +static int ov5648_white_balance_configure(struct ov5648_sensor *sensor, + u32 red_balance, u32 blue_balance) +{ + struct ov5648_ctrls *ctrls = &sensor->ctrls; + int ret; + + if (ctrls->white_balance_auto->val) + return -EINVAL; + + ret = ov5648_write(sensor, OV5648_GAIN_RED_MAN_H_REG, + OV5648_GAIN_RED_MAN_H(red_balance)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_GAIN_RED_MAN_L_REG, + OV5648_GAIN_RED_MAN_L(red_balance)); + if (ret) + return ret; + + ret = ov5648_write(sensor, OV5648_GAIN_BLUE_MAN_H_REG, + OV5648_GAIN_BLUE_MAN_H(blue_balance)); + if (ret) + return ret; + + return ov5648_write(sensor, OV5648_GAIN_BLUE_MAN_L_REG, + OV5648_GAIN_BLUE_MAN_L(blue_balance)); +} + +/* Flip */ + +static int ov5648_flip_vert_configure(struct ov5648_sensor *sensor, bool enable) +{ + u8 bits = OV5648_TC20_FLIP_VERT_ISP_EN | + OV5648_TC20_FLIP_VERT_SENSOR_EN; + + return ov5648_update_bits(sensor, OV5648_TC20_REG, bits, + enable ? bits : 0); +} + +static int ov5648_flip_horz_configure(struct ov5648_sensor *sensor, bool enable) +{ + u8 bits = OV5648_TC21_FLIP_HORZ_ISP_EN | + OV5648_TC21_FLIP_HORZ_SENSOR_EN; + + return ov5648_update_bits(sensor, OV5648_TC21_REG, bits, + enable ? bits : 0); +} + +/* Test Pattern */ + +static int ov5648_test_pattern_configure(struct ov5648_sensor *sensor, + unsigned int index) +{ + if (index >= ARRAY_SIZE(ov5648_test_pattern_bits)) + return -EINVAL; + + return ov5648_write(sensor, OV5648_ISP_CTRL3D_REG, + ov5648_test_pattern_bits[index]); +} + +/* State */ + +static int ov5648_state_mipi_configure(struct ov5648_sensor *sensor, + const struct ov5648_mode *mode, + u32 mbus_code) +{ + struct ov5648_ctrls *ctrls = &sensor->ctrls; + struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 = + &sensor->endpoint.bus.mipi_csi2; + unsigned long mipi_clk_rate; + unsigned int bits_per_sample; + unsigned int lanes_count; + unsigned int i, j; + s64 mipi_pixel_rate; + + mipi_clk_rate = ov5648_mode_mipi_clk_rate(sensor, mode, mbus_code); + if (!mipi_clk_rate) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(ov5648_link_freq_menu); i++) { + s64 freq = ov5648_link_freq_menu[i]; + + if (freq == mipi_clk_rate) + break; + } + + for (j = 0; j < sensor->endpoint.nr_of_link_frequencies; j++) { + u64 freq = sensor->endpoint.link_frequencies[j]; + + if (freq == mipi_clk_rate) + break; + } + + if (i == ARRAY_SIZE(ov5648_link_freq_menu)) { + dev_err(sensor->dev, + "failed to find %lu clk rate in link freq\n", + mipi_clk_rate); + } else if (j == sensor->endpoint.nr_of_link_frequencies) { + dev_err(sensor->dev, + "failed to find %lu clk rate in endpoint link-frequencies\n", + mipi_clk_rate); + } else { + __v4l2_ctrl_s_ctrl(ctrls->link_freq, i); + } + + switch (mbus_code) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + bits_per_sample = 8; + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + bits_per_sample = 10; + break; + default: + return -EINVAL; + } + + lanes_count = bus_mipi_csi2->num_data_lanes; + mipi_pixel_rate = mipi_clk_rate * 2 * lanes_count / bits_per_sample; + + __v4l2_ctrl_s_ctrl_int64(ctrls->pixel_rate, mipi_pixel_rate); + + return 0; +} + +static int ov5648_state_configure(struct ov5648_sensor *sensor, + const struct ov5648_mode *mode, + u32 mbus_code) +{ + int ret; + + if (sensor->state.streaming) + return -EBUSY; + + /* State will be configured at first power on otherwise. */ + if (pm_runtime_enabled(sensor->dev) && + !pm_runtime_suspended(sensor->dev)) { + ret = ov5648_mode_configure(sensor, mode, mbus_code); + if (ret) + return ret; + } + + ret = ov5648_state_mipi_configure(sensor, mode, mbus_code); + if (ret) + return ret; + + sensor->state.mode = mode; + sensor->state.mbus_code = mbus_code; + + return 0; +} + +static int ov5648_state_init(struct ov5648_sensor *sensor) +{ + return ov5648_state_configure(sensor, &ov5648_modes[0], + ov5648_mbus_codes[0]); +} + +/* Sensor Base */ + +static int ov5648_sensor_init(struct ov5648_sensor *sensor) +{ + int ret; + + ret = ov5648_sw_reset(sensor); + if (ret) { + dev_err(sensor->dev, "failed to perform sw reset\n"); + return ret; + } + + ret = ov5648_sw_standby(sensor, 1); + if (ret) { + dev_err(sensor->dev, "failed to set sensor standby\n"); + return ret; + } + + ret = ov5648_chip_id_check(sensor); + if (ret) { + dev_err(sensor->dev, "failed to check sensor chip id\n"); + return ret; + } + + ret = ov5648_avdd_internal_power(sensor, !sensor->avdd); + if (ret) { + dev_err(sensor->dev, "failed to set internal avdd power\n"); + return ret; + } + + ret = ov5648_write_sequence(sensor, ov5648_init_sequence, + ARRAY_SIZE(ov5648_init_sequence)); + if (ret) { + dev_err(sensor->dev, "failed to write init sequence\n"); + return ret; + } + + ret = ov5648_pad_configure(sensor); + if (ret) { + dev_err(sensor->dev, "failed to configure pad\n"); + return ret; + } + + ret = ov5648_mipi_configure(sensor); + if (ret) { + dev_err(sensor->dev, "failed to configure MIPI\n"); + return ret; + } + + ret = ov5648_isp_configure(sensor); + if (ret) { + dev_err(sensor->dev, "failed to configure ISP\n"); + return ret; + } + + ret = ov5648_black_level_configure(sensor); + if (ret) { + dev_err(sensor->dev, "failed to configure black level\n"); + return ret; + } + + /* Configure current mode. */ + ret = ov5648_state_configure(sensor, sensor->state.mode, + sensor->state.mbus_code); + if (ret) { + dev_err(sensor->dev, "failed to configure state\n"); + return ret; + } + + return 0; +} + +static int ov5648_sensor_power(struct ov5648_sensor *sensor, bool on) +{ + /* Keep initialized to zero for disable label. */ + int ret = 0; + + /* + * General notes about the power sequence: + * - power-down GPIO must be active (low) during power-on; + * - reset GPIO state does not matter during power-on; + * - XVCLK must be provided 1 ms before register access; + * - 10 ms are needed between power-down deassert and register access. + */ + + /* Note that regulator-and-GPIO-based power is untested. */ + if (on) { + gpiod_set_value_cansleep(sensor->reset, 1); + gpiod_set_value_cansleep(sensor->powerdown, 1); + + ret = regulator_enable(sensor->dovdd); + if (ret) { + dev_err(sensor->dev, + "failed to enable DOVDD regulator\n"); + goto disable; + } + + if (sensor->avdd) { + ret = regulator_enable(sensor->avdd); + if (ret) { + dev_err(sensor->dev, + "failed to enable AVDD regulator\n"); + goto disable; + } + } + + ret = regulator_enable(sensor->dvdd); + if (ret) { + dev_err(sensor->dev, + "failed to enable DVDD regulator\n"); + goto disable; + } + + /* According to OV5648 power up diagram. */ + usleep_range(5000, 10000); + + ret = clk_prepare_enable(sensor->xvclk); + if (ret) { + dev_err(sensor->dev, "failed to enable XVCLK clock\n"); + goto disable; + } + + gpiod_set_value_cansleep(sensor->reset, 0); + gpiod_set_value_cansleep(sensor->powerdown, 0); + + usleep_range(20000, 25000); + } else { +disable: + gpiod_set_value_cansleep(sensor->powerdown, 1); + gpiod_set_value_cansleep(sensor->reset, 1); + + clk_disable_unprepare(sensor->xvclk); + + regulator_disable(sensor->dvdd); + + if (sensor->avdd) + regulator_disable(sensor->avdd); + + regulator_disable(sensor->dovdd); + } + + return ret; +} + +/* Controls */ + +static int ov5648_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *subdev = ov5648_ctrl_subdev(ctrl); + struct ov5648_sensor *sensor = ov5648_subdev_sensor(subdev); + struct ov5648_ctrls *ctrls = &sensor->ctrls; + int ret; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_AUTO: + ret = ov5648_exposure_value(sensor, &ctrls->exposure->val); + if (ret) + return ret; + break; + case V4L2_CID_AUTOGAIN: + ret = ov5648_gain_value(sensor, &ctrls->gain->val); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ov5648_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *subdev = ov5648_ctrl_subdev(ctrl); + struct ov5648_sensor *sensor = ov5648_subdev_sensor(subdev); + struct ov5648_ctrls *ctrls = &sensor->ctrls; + unsigned int index; + bool enable; + int ret; + + /* Wait for the sensor to be on before setting controls. */ + if (pm_runtime_suspended(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_AUTO: + enable = ctrl->val == V4L2_EXPOSURE_AUTO; + + ret = ov5648_exposure_auto_configure(sensor, enable); + if (ret) + return ret; + + if (!enable && ctrls->exposure->is_new) { + ret = ov5648_exposure_configure(sensor, + ctrls->exposure->val); + if (ret) + return ret; + } + break; + case V4L2_CID_AUTOGAIN: + enable = !!ctrl->val; + + ret = ov5648_gain_auto_configure(sensor, enable); + if (ret) + return ret; + + if (!enable) { + ret = ov5648_gain_configure(sensor, ctrls->gain->val); + if (ret) + return ret; + } + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + enable = !!ctrl->val; + + ret = ov5648_white_balance_auto_configure(sensor, enable); + if (ret) + return ret; + + if (!enable) { + ret = ov5648_white_balance_configure(sensor, + ctrls->red_balance->val, + ctrls->blue_balance->val); + if (ret) + return ret; + } + break; + case V4L2_CID_HFLIP: + enable = !!ctrl->val; + return ov5648_flip_horz_configure(sensor, enable); + case V4L2_CID_VFLIP: + enable = !!ctrl->val; + return ov5648_flip_vert_configure(sensor, enable); + case V4L2_CID_TEST_PATTERN: + index = (unsigned int)ctrl->val; + return ov5648_test_pattern_configure(sensor, index); + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops ov5648_ctrl_ops = { + .g_volatile_ctrl = ov5648_g_volatile_ctrl, + .s_ctrl = ov5648_s_ctrl, +}; + +static int ov5648_ctrls_init(struct ov5648_sensor *sensor) +{ + struct ov5648_ctrls *ctrls = &sensor->ctrls; + struct v4l2_ctrl_handler *handler = &ctrls->handler; + const struct v4l2_ctrl_ops *ops = &ov5648_ctrl_ops; + int ret; + + v4l2_ctrl_handler_init(handler, 32); + + /* Use our mutex for ctrl locking. */ + handler->lock = &sensor->mutex; + + /* Exposure */ + + ctrls->exposure_auto = v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + + ctrls->exposure = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, + 16, 1048575, 16, 512); + + v4l2_ctrl_auto_cluster(2, &ctrls->exposure_auto, 1, true); + + /* Gain */ + + ctrls->gain_auto = + v4l2_ctrl_new_std(handler, ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + + ctrls->gain = v4l2_ctrl_new_std(handler, ops, V4L2_CID_GAIN, 16, 1023, + 16, 16); + + v4l2_ctrl_auto_cluster(2, &ctrls->gain_auto, 0, true); + + /* White Balance */ + + ctrls->white_balance_auto = + v4l2_ctrl_new_std(handler, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, + 1, 1, 1); + + ctrls->red_balance = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_RED_BALANCE, 0, 4095, + 1, 1024); + + ctrls->blue_balance = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_BLUE_BALANCE, 0, 4095, + 1, 1024); + + v4l2_ctrl_auto_cluster(3, &ctrls->white_balance_auto, 0, false); + + /* Flip */ + + v4l2_ctrl_new_std(handler, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + + /* Test Pattern */ + + v4l2_ctrl_new_std_menu_items(handler, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov5648_test_pattern_menu) - 1, + 0, 0, ov5648_test_pattern_menu); + + /* MIPI CSI-2 */ + + ctrls->link_freq = + v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(ov5648_link_freq_menu) - 1, + 0, ov5648_link_freq_menu); + + ctrls->pixel_rate = + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 1, + INT_MAX, 1, 1); + + if (handler->error) { + ret = handler->error; + goto error_ctrls; + } + + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + sensor->subdev.ctrl_handler = handler; + + return 0; + +error_ctrls: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +/* Subdev Video Operations */ + +static int ov5648_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct ov5648_sensor *sensor = ov5648_subdev_sensor(subdev); + struct ov5648_state *state = &sensor->state; + int ret; + + if (enable) { + ret = pm_runtime_get_sync(sensor->dev); + if (ret < 0) { + pm_runtime_put_noidle(sensor->dev); + return ret; + } + } + + mutex_lock(&sensor->mutex); + ret = ov5648_sw_standby(sensor, !enable); + mutex_unlock(&sensor->mutex); + + if (ret) + return ret; + + state->streaming = !!enable; + + if (!enable) + pm_runtime_put(sensor->dev); + + return 0; +} + +static int ov5648_g_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *interval) +{ + struct ov5648_sensor *sensor = ov5648_subdev_sensor(subdev); + const struct ov5648_mode *mode; + int ret = 0; + + mutex_lock(&sensor->mutex); + + mode = sensor->state.mode; + + switch (sensor->state.mbus_code) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + interval->interval = mode->frame_interval[0]; + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + interval->interval = mode->frame_interval[1]; + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&sensor->mutex); + + return ret; +} + +static const struct v4l2_subdev_video_ops ov5648_subdev_video_ops = { + .s_stream = ov5648_s_stream, + .g_frame_interval = ov5648_g_frame_interval, + .s_frame_interval = ov5648_g_frame_interval, +}; + +/* Subdev Pad Operations */ + +static int ov5648_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_mbus_code_enum *code_enum) +{ + if (code_enum->index >= ARRAY_SIZE(ov5648_mbus_codes)) + return -EINVAL; + + code_enum->code = ov5648_mbus_codes[code_enum->index]; + + return 0; +} + +static void ov5648_mbus_format_fill(struct v4l2_mbus_framefmt *mbus_format, + u32 mbus_code, + const struct ov5648_mode *mode) +{ + mbus_format->width = mode->output_size_x; + mbus_format->height = mode->output_size_y; + mbus_format->code = mbus_code; + + mbus_format->field = V4L2_FIELD_NONE; + mbus_format->colorspace = V4L2_COLORSPACE_RAW; + mbus_format->ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(mbus_format->colorspace); + mbus_format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + mbus_format->xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(mbus_format->colorspace); +} + +static int ov5648_get_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_format *format) +{ + struct ov5648_sensor *sensor = ov5648_subdev_sensor(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + + mutex_lock(&sensor->mutex); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *mbus_format = *v4l2_subdev_get_try_format(subdev, config, + format->pad); + else + ov5648_mbus_format_fill(mbus_format, sensor->state.mbus_code, + sensor->state.mode); + + mutex_unlock(&sensor->mutex); + + return 0; +} + +static int ov5648_set_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_format *format) +{ + struct ov5648_sensor *sensor = ov5648_subdev_sensor(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + const struct ov5648_mode *mode; + u32 mbus_code = 0; + unsigned int index; + int ret = 0; + + mutex_lock(&sensor->mutex); + + if (sensor->state.streaming) { + ret = -EBUSY; + goto complete; + } + + /* Try to find requested mbus code. */ + for (index = 0; index < ARRAY_SIZE(ov5648_mbus_codes); index++) { + if (ov5648_mbus_codes[index] == mbus_format->code) { + mbus_code = mbus_format->code; + break; + } + } + + /* Fallback to default. */ + if (!mbus_code) + mbus_code = ov5648_mbus_codes[0]; + + /* Find the mode with nearest dimensions. */ + mode = v4l2_find_nearest_size(ov5648_modes, ARRAY_SIZE(ov5648_modes), + output_size_x, output_size_y, + mbus_format->width, mbus_format->height); + if (!mode) { + ret = -EINVAL; + goto complete; + } + + ov5648_mbus_format_fill(mbus_format, mbus_code, mode); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_get_try_format(subdev, config, format->pad) = + *mbus_format; + else if (sensor->state.mode != mode || + sensor->state.mbus_code != mbus_code) + ret = ov5648_state_configure(sensor, mode, mbus_code); + +complete: + mutex_unlock(&sensor->mutex); + + return ret; +} + +static int ov5648_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_frame_size_enum *size_enum) +{ + const struct ov5648_mode *mode; + + if (size_enum->index >= ARRAY_SIZE(ov5648_modes)) + return -EINVAL; + + mode = &ov5648_modes[size_enum->index]; + + size_enum->min_width = size_enum->max_width = mode->output_size_x; + size_enum->min_height = size_enum->max_height = mode->output_size_y; + + return 0; +} + +static int ov5648_enum_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_frame_interval_enum *interval_enum) +{ + const struct ov5648_mode *mode = NULL; + unsigned int mode_index; + unsigned int interval_index; + + if (interval_enum->index > 0) + return -EINVAL; + + /* + * Multiple modes with the same dimensions may have different frame + * intervals, so look up each relevant mode. + */ + for (mode_index = 0, interval_index = 0; + mode_index < ARRAY_SIZE(ov5648_modes); mode_index++) { + mode = &ov5648_modes[mode_index]; + + if (mode->output_size_x == interval_enum->width && + mode->output_size_y == interval_enum->height) { + if (interval_index == interval_enum->index) + break; + + interval_index++; + } + } + + if (mode_index == ARRAY_SIZE(ov5648_modes) || !mode) + return -EINVAL; + + switch (interval_enum->code) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + interval_enum->interval = mode->frame_interval[0]; + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + interval_enum->interval = mode->frame_interval[1]; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_subdev_pad_ops ov5648_subdev_pad_ops = { + .enum_mbus_code = ov5648_enum_mbus_code, + .get_fmt = ov5648_get_fmt, + .set_fmt = ov5648_set_fmt, + .enum_frame_size = ov5648_enum_frame_size, + .enum_frame_interval = ov5648_enum_frame_interval, +}; + +static const struct v4l2_subdev_ops ov5648_subdev_ops = { + .video = &ov5648_subdev_video_ops, + .pad = &ov5648_subdev_pad_ops, +}; + +static int ov5648_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ov5648_sensor *sensor = ov5648_subdev_sensor(subdev); + struct ov5648_state *state = &sensor->state; + int ret = 0; + + mutex_lock(&sensor->mutex); + + if (state->streaming) { + ret = ov5648_sw_standby(sensor, true); + if (ret) + goto complete; + } + + ret = ov5648_sensor_power(sensor, false); + if (ret) + ov5648_sw_standby(sensor, false); + +complete: + mutex_unlock(&sensor->mutex); + + return ret; +} + +static int ov5648_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ov5648_sensor *sensor = ov5648_subdev_sensor(subdev); + struct ov5648_state *state = &sensor->state; + int ret = 0; + + mutex_lock(&sensor->mutex); + + ret = ov5648_sensor_power(sensor, true); + if (ret) + goto complete; + + ret = ov5648_sensor_init(sensor); + if (ret) + goto error_power; + + ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); + if (ret) + goto error_power; + + if (state->streaming) { + ret = ov5648_sw_standby(sensor, false); + if (ret) + goto error_power; + } + + goto complete; + +error_power: + ov5648_sensor_power(sensor, false); + +complete: + mutex_unlock(&sensor->mutex); + + return ret; +} + +static int ov5648_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct fwnode_handle *handle; + struct ov5648_sensor *sensor; + struct v4l2_subdev *subdev; + struct media_pad *pad; + unsigned long rate; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->dev = dev; + sensor->i2c_client = client; + + /* Graph Endpoint */ + + handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!handle) { + dev_err(dev, "unable to find enpoint node\n"); + return -EINVAL; + } + + sensor->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY; + + ret = v4l2_fwnode_endpoint_alloc_parse(handle, &sensor->endpoint); + fwnode_handle_put(handle); + if (ret) { + dev_err(dev, "failed to parse endpoint node\n"); + return ret; + } + + /* GPIOs */ + + sensor->powerdown = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->powerdown)) { + ret = PTR_ERR(sensor->powerdown); + goto error_endpoint; + } + + sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset)) { + ret = PTR_ERR(sensor->reset); + goto error_endpoint; + } + + /* Regulators */ + + /* DVDD: digital core */ + sensor->dvdd = devm_regulator_get(dev, "dvdd"); + if (IS_ERR(sensor->dvdd)) { + dev_err(dev, "cannot get DVDD (digital core) regulator\n"); + ret = PTR_ERR(sensor->dvdd); + goto error_endpoint; + } + + /* DOVDD: digital I/O */ + sensor->dovdd = devm_regulator_get(dev, "dovdd"); + if (IS_ERR(sensor->dvdd)) { + dev_err(dev, "cannot get DOVDD (digital I/O) regulator\n"); + ret = PTR_ERR(sensor->dvdd); + goto error_endpoint; + } + + /* AVDD: analog */ + sensor->avdd = devm_regulator_get_optional(dev, "avdd"); + if (IS_ERR(sensor->avdd)) { + dev_info(dev, "no AVDD regulator provided, using internal\n"); + sensor->avdd = NULL; + } + + /* External Clock */ + + sensor->xvclk = devm_clk_get(dev, NULL); + if (IS_ERR(sensor->xvclk)) { + dev_err(dev, "failed to get external clock\n"); + ret = PTR_ERR(sensor->xvclk); + goto error_endpoint; + } + + rate = clk_get_rate(sensor->xvclk); + if (rate != OV5648_XVCLK_RATE) { + dev_err(dev, "clock rate %lu Hz is unsupported\n", rate); + ret = -EINVAL; + goto error_endpoint; + } + + /* Subdev, entity and pad */ + + subdev = &sensor->subdev; + v4l2_i2c_subdev_init(subdev, client, &ov5648_subdev_ops); + + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->entity.function = MEDIA_ENT_F_CAM_SENSOR; + + pad = &sensor->pad; + pad->flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&subdev->entity, 1, pad); + if (ret) + goto error_entity; + + /* Mutex */ + + mutex_init(&sensor->mutex); + + /* Sensor */ + + ret = ov5648_ctrls_init(sensor); + if (ret) + goto error_mutex; + + ret = ov5648_state_init(sensor); + if (ret) + goto error_ctrls; + + /* Runtime PM */ + + pm_runtime_enable(sensor->dev); + pm_runtime_set_suspended(sensor->dev); + + /* V4L2 subdev register */ + + ret = v4l2_async_register_subdev_sensor_common(subdev); + if (ret) + goto error_pm; + + return 0; + +error_pm: + pm_runtime_disable(sensor->dev); + +error_ctrls: + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + +error_mutex: + mutex_destroy(&sensor->mutex); + +error_entity: + media_entity_cleanup(&sensor->subdev.entity); + +error_endpoint: + v4l2_fwnode_endpoint_free(&sensor->endpoint); + + return ret; +} + +static int ov5648_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ov5648_sensor *sensor = ov5648_subdev_sensor(subdev); + + v4l2_async_unregister_subdev(subdev); + pm_runtime_disable(sensor->dev); + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + mutex_destroy(&sensor->mutex); + media_entity_cleanup(&subdev->entity); + + return 0; +} + +static const struct dev_pm_ops ov5648_pm_ops = { + SET_RUNTIME_PM_OPS(ov5648_suspend, ov5648_resume, NULL) +}; + +static const struct of_device_id ov5648_of_match[] = { + { .compatible = "ovti,ov5648" }, + { } +}; +MODULE_DEVICE_TABLE(of, ov5648_of_match); + +static struct i2c_driver ov5648_driver = { + .driver = { + .name = "ov5648", + .of_match_table = ov5648_of_match, + .pm = &ov5648_pm_ops, + }, + .probe_new = ov5648_probe, + .remove = ov5648_remove, +}; + +module_i2c_driver(ov5648_driver); + +MODULE_AUTHOR("Paul Kocialkowski "); +MODULE_DESCRIPTION("V4L2 driver for the OmniVision OV5648 image sensor"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 22f2b47517a64c405422db380a93b0829b282b87 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 31 Dec 2020 15:27:00 +0100 Subject: media: dt-bindings: media: i2c: Add OV8865 bindings documentation This introduces YAML bindings documentation for the OV8865 image sensor. Co-developed-by: Kévin L'hôpital Signed-off-by: Kévin L'hôpital Signed-off-by: Paul Kocialkowski Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/ovti,ov8865.yaml | 124 +++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov8865.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov8865.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov8865.yaml new file mode 100644 index 000000000000..c0ba28aa30c4 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov8865.yaml @@ -0,0 +1,124 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ovti,ov8865.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OmniVision OV8865 Image Sensor Device Tree Bindings + +maintainers: + - Paul Kocialkowski + +properties: + compatible: + const: ovti,ov8865 + + reg: + maxItems: 1 + + clocks: + items: + - description: EXTCLK Clock + + assigned-clocks: + maxItems: 1 + + assigned-clock-rates: + maxItems: 1 + + dvdd-supply: + description: Digital Domain Power Supply + + avdd-supply: + description: Analog Domain Power Supply + + dovdd-supply: + description: I/O Domain Power Supply + + powerdown-gpios: + maxItems: 1 + description: Power Down Pin GPIO Control (active low) + + reset-gpios: + maxItems: 1 + description: Reset Pin GPIO Control (active low) + + port: + type: object + description: MIPI CSI-2 transmitter port + + properties: + endpoint: + type: object + + properties: + remote-endpoint: true + + link-frequencies: + $ref: /schemas/types.yaml#/definitions/uint64-array + description: Allowed MIPI CSI-2 link frequencies + + data-lanes: + minItems: 1 + maxItems: 4 + + required: + - data-lanes + - link-frequencies + - remote-endpoint + + required: + - endpoint + +required: + - compatible + - reg + - clocks + - assigned-clocks + - assigned-clock-rates + - dvdd-supply + - avdd-supply + - dovdd-supply + - port + +additionalProperties: false + +examples: + - | + #include + #include + + i2c2 { + #address-cells = <1>; + #size-cells = <0>; + + ov8865: camera@36 { + compatible = "ovti,ov8865"; + reg = <0x36>; + + pinctrl-names = "default"; + pinctrl-0 = <&csi_mclk_pin>; + + clocks = <&ccu CLK_CSI_MCLK>; + assigned-clocks = <&ccu CLK_CSI_MCLK>; + assigned-clock-rates = <24000000>; + + avdd-supply = <®_ov8865_avdd>; + dovdd-supply = <®_ov8865_dovdd>; + dvdd-supply = <®_ov8865_dvdd>; + + powerdown-gpios = <&pio 4 17 GPIO_ACTIVE_LOW>; /* PE17 */ + reset-gpios = <&pio 4 16 GPIO_ACTIVE_LOW>; /* PE16 */ + + port { + ov8865_out_mipi_csi2: endpoint { + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <360000000>; + + remote-endpoint = <&mipi_csi2_in_ov8865>; + }; + }; + }; + }; + +... -- cgit v1.2.3 From 11c0d8fdccc56fa15cb15906480b4737c31dd085 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 31 Dec 2020 15:27:01 +0100 Subject: media: i2c: Add support for the OV8865 image sensor The OV8865 is a 8 Mpx CMOS image sensor producing 3264x2448 at 30 fps. Other modes (including some with sub-sampling) are available too. It outputs 10-bit bayer CFA data through a MIPI CSI-2 interface with up to 4 lanes supported. Some register initialisation sequences are still needed for this driver, as they cover registers for which no documentation is available. This work is based on the first version of the driver submitted by Kévin L'hôpital, which was adapted to mainline from the Allwinner BSP. This version is a rewrite of the first version that matches the structure of the OV5648 driver, with explicit PLL configuration, all the necessary mode-specific fields, associatied registers and reduced static sequences. It was tested with the Banana Pi Camera Board v3 and the Banana Pi M3. Co-developed-by: Kévin L'hôpital Signed-off-by: Kévin L'hôpital Signed-off-by: Paul Kocialkowski Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov8865.c | 2972 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2986 insertions(+) create mode 100644 drivers/media/i2c/ov8865.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 4c1ae687ab10..eddb10220953 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1060,6 +1060,19 @@ config VIDEO_OV8856 To compile this driver as a module, choose M here: the module will be called ov8856. +config VIDEO_OV8865 + tristate "OmniVision OV8865 sensor support" + depends on I2C && PM && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for OmniVision + OV8865 camera sensor. + + To compile this driver as a module, choose M here: the + module will be called ov8865. + config VIDEO_OV9640 tristate "OmniVision OV9640 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 65cfc94d25b6..5b1dcfa3ce76 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV772X) += ov772x.o obj-$(CONFIG_VIDEO_OV7740) += ov7740.o obj-$(CONFIG_VIDEO_OV8856) += ov8856.o +obj-$(CONFIG_VIDEO_OV8865) += ov8865.o obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV9734) += ov9734.o diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c new file mode 100644 index 000000000000..fda5a55979aa --- /dev/null +++ b/drivers/media/i2c/ov8865.c @@ -0,0 +1,2972 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2020 Kévin L'hôpital + * Copyright 2020 Bootlin + * Author: Paul Kocialkowski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Clock rate */ + +#define OV8865_EXTCLK_RATE 24000000 + +/* Register definitions */ + +/* System */ + +#define OV8865_SW_STANDBY_REG 0x100 +#define OV8865_SW_STANDBY_STREAM_ON BIT(0) + +#define OV8865_SW_RESET_REG 0x103 +#define OV8865_SW_RESET_RESET BIT(0) + +#define OV8865_PLL_CTRL0_REG 0x300 +#define OV8865_PLL_CTRL0_PRE_DIV(v) ((v) & GENMASK(2, 0)) +#define OV8865_PLL_CTRL1_REG 0x301 +#define OV8865_PLL_CTRL1_MUL_H(v) (((v) & GENMASK(9, 8)) >> 8) +#define OV8865_PLL_CTRL2_REG 0x302 +#define OV8865_PLL_CTRL2_MUL_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_PLL_CTRL3_REG 0x303 +#define OV8865_PLL_CTRL3_M_DIV(v) (((v) - 1) & GENMASK(3, 0)) +#define OV8865_PLL_CTRL4_REG 0x304 +#define OV8865_PLL_CTRL4_MIPI_DIV(v) ((v) & GENMASK(1, 0)) +#define OV8865_PLL_CTRL5_REG 0x305 +#define OV8865_PLL_CTRL5_SYS_PRE_DIV(v) ((v) & GENMASK(1, 0)) +#define OV8865_PLL_CTRL6_REG 0x306 +#define OV8865_PLL_CTRL6_SYS_DIV(v) (((v) - 1) & BIT(0)) + +#define OV8865_PLL_CTRL8_REG 0x308 +#define OV8865_PLL_CTRL9_REG 0x309 +#define OV8865_PLL_CTRLA_REG 0x30a +#define OV8865_PLL_CTRLA_PRE_DIV_HALF(v) (((v) - 1) & BIT(0)) +#define OV8865_PLL_CTRLB_REG 0x30b +#define OV8865_PLL_CTRLB_PRE_DIV(v) ((v) & GENMASK(2, 0)) +#define OV8865_PLL_CTRLC_REG 0x30c +#define OV8865_PLL_CTRLC_MUL_H(v) (((v) & GENMASK(9, 8)) >> 8) +#define OV8865_PLL_CTRLD_REG 0x30d +#define OV8865_PLL_CTRLD_MUL_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_PLL_CTRLE_REG 0x30e +#define OV8865_PLL_CTRLE_SYS_DIV(v) ((v) & GENMASK(2, 0)) +#define OV8865_PLL_CTRLF_REG 0x30f +#define OV8865_PLL_CTRLF_SYS_PRE_DIV(v) (((v) - 1) & GENMASK(3, 0)) +#define OV8865_PLL_CTRL10_REG 0x310 +#define OV8865_PLL_CTRL11_REG 0x311 +#define OV8865_PLL_CTRL12_REG 0x312 +#define OV8865_PLL_CTRL12_PRE_DIV_HALF(v) ((((v) - 1) << 4) & BIT(4)) +#define OV8865_PLL_CTRL12_DAC_DIV(v) (((v) - 1) & GENMASK(3, 0)) + +#define OV8865_PLL_CTRL1B_REG 0x31b +#define OV8865_PLL_CTRL1C_REG 0x31c + +#define OV8865_PLL_CTRL1E_REG 0x31e +#define OV8865_PLL_CTRL1E_PLL1_NO_LAT BIT(3) + +#define OV8865_PAD_OEN0_REG 0x3000 + +#define OV8865_PAD_OEN2_REG 0x3002 + +#define OV8865_CLK_RST5_REG 0x3005 + +#define OV8865_CHIP_ID_HH_REG 0x300a +#define OV8865_CHIP_ID_HH_VALUE 0x00 +#define OV8865_CHIP_ID_H_REG 0x300b +#define OV8865_CHIP_ID_H_VALUE 0x88 +#define OV8865_CHIP_ID_L_REG 0x300c +#define OV8865_CHIP_ID_L_VALUE 0x65 +#define OV8865_PAD_OUT2_REG 0x300d + +#define OV8865_PAD_SEL2_REG 0x3010 +#define OV8865_PAD_PK_REG 0x3011 +#define OV8865_PAD_PK_DRIVE_STRENGTH_1X (0 << 5) +#define OV8865_PAD_PK_DRIVE_STRENGTH_2X (1 << 5) +#define OV8865_PAD_PK_DRIVE_STRENGTH_3X (2 << 5) +#define OV8865_PAD_PK_DRIVE_STRENGTH_4X (3 << 5) + +#define OV8865_PUMP_CLK_DIV_REG 0x3015 +#define OV8865_PUMP_CLK_DIV_PUMP_N(v) (((v) << 4) & GENMASK(6, 4)) +#define OV8865_PUMP_CLK_DIV_PUMP_P(v) ((v) & GENMASK(2, 0)) + +#define OV8865_MIPI_SC_CTRL0_REG 0x3018 +#define OV8865_MIPI_SC_CTRL0_LANES(v) ((((v) - 1) << 5) & \ + GENMASK(7, 5)) +#define OV8865_MIPI_SC_CTRL0_MIPI_EN BIT(4) +#define OV8865_MIPI_SC_CTRL0_UNKNOWN BIT(1) +#define OV8865_MIPI_SC_CTRL0_LANES_PD_MIPI BIT(0) +#define OV8865_MIPI_SC_CTRL1_REG 0x3019 +#define OV8865_CLK_RST0_REG 0x301a +#define OV8865_CLK_RST1_REG 0x301b +#define OV8865_CLK_RST2_REG 0x301c +#define OV8865_CLK_RST3_REG 0x301d +#define OV8865_CLK_RST4_REG 0x301e + +#define OV8865_PCLK_SEL_REG 0x3020 +#define OV8865_PCLK_SEL_PCLK_DIV_MASK BIT(3) +#define OV8865_PCLK_SEL_PCLK_DIV(v) ((((v) - 1) << 3) & BIT(3)) + +#define OV8865_MISC_CTRL_REG 0x3021 +#define OV8865_MIPI_SC_CTRL2_REG 0x3022 +#define OV8865_MIPI_SC_CTRL2_CLK_LANES_PD_MIPI BIT(1) +#define OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC BIT(0) + +#define OV8865_MIPI_BIT_SEL_REG 0x3031 +#define OV8865_MIPI_BIT_SEL(v) (((v) << 0) & GENMASK(4, 0)) +#define OV8865_CLK_SEL0_REG 0x3032 +#define OV8865_CLK_SEL0_PLL1_SYS_SEL(v) (((v) << 7) & BIT(7)) +#define OV8865_CLK_SEL1_REG 0x3033 +#define OV8865_CLK_SEL1_MIPI_EOF BIT(5) +#define OV8865_CLK_SEL1_UNKNOWN BIT(2) +#define OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK BIT(1) +#define OV8865_CLK_SEL1_PLL_SCLK_SEL(v) (((v) << 1) & BIT(1)) + +#define OV8865_SCLK_CTRL_REG 0x3106 +#define OV8865_SCLK_CTRL_SCLK_DIV(v) (((v) << 4) & GENMASK(7, 4)) +#define OV8865_SCLK_CTRL_SCLK_PRE_DIV(v) (((v) << 2) & GENMASK(3, 2)) +#define OV8865_SCLK_CTRL_UNKNOWN BIT(0) + +/* Exposure/gain */ + +#define OV8865_EXPOSURE_CTRL_HH_REG 0x3500 +#define OV8865_EXPOSURE_CTRL_HH(v) (((v) & GENMASK(19, 16)) >> 16) +#define OV8865_EXPOSURE_CTRL_H_REG 0x3501 +#define OV8865_EXPOSURE_CTRL_H(v) (((v) & GENMASK(15, 8)) >> 8) +#define OV8865_EXPOSURE_CTRL_L_REG 0x3502 +#define OV8865_EXPOSURE_CTRL_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_EXPOSURE_GAIN_MANUAL_REG 0x3503 + +#define OV8865_GAIN_CTRL_H_REG 0x3508 +#define OV8865_GAIN_CTRL_H(v) (((v) & GENMASK(12, 8)) >> 8) +#define OV8865_GAIN_CTRL_L_REG 0x3509 +#define OV8865_GAIN_CTRL_L(v) ((v) & GENMASK(7, 0)) + +/* Timing */ + +#define OV8865_CROP_START_X_H_REG 0x3800 +#define OV8865_CROP_START_X_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_CROP_START_X_L_REG 0x3801 +#define OV8865_CROP_START_X_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_CROP_START_Y_H_REG 0x3802 +#define OV8865_CROP_START_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_CROP_START_Y_L_REG 0x3803 +#define OV8865_CROP_START_Y_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_CROP_END_X_H_REG 0x3804 +#define OV8865_CROP_END_X_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_CROP_END_X_L_REG 0x3805 +#define OV8865_CROP_END_X_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_CROP_END_Y_H_REG 0x3806 +#define OV8865_CROP_END_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_CROP_END_Y_L_REG 0x3807 +#define OV8865_CROP_END_Y_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_OUTPUT_SIZE_X_H_REG 0x3808 +#define OV8865_OUTPUT_SIZE_X_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_OUTPUT_SIZE_X_L_REG 0x3809 +#define OV8865_OUTPUT_SIZE_X_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_OUTPUT_SIZE_Y_H_REG 0x380a +#define OV8865_OUTPUT_SIZE_Y_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_OUTPUT_SIZE_Y_L_REG 0x380b +#define OV8865_OUTPUT_SIZE_Y_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_HTS_H_REG 0x380c +#define OV8865_HTS_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_HTS_L_REG 0x380d +#define OV8865_HTS_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_VTS_H_REG 0x380e +#define OV8865_VTS_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_VTS_L_REG 0x380f +#define OV8865_VTS_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_OFFSET_X_H_REG 0x3810 +#define OV8865_OFFSET_X_H(v) (((v) & GENMASK(15, 8)) >> 8) +#define OV8865_OFFSET_X_L_REG 0x3811 +#define OV8865_OFFSET_X_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_OFFSET_Y_H_REG 0x3812 +#define OV8865_OFFSET_Y_H(v) (((v) & GENMASK(14, 8)) >> 8) +#define OV8865_OFFSET_Y_L_REG 0x3813 +#define OV8865_OFFSET_Y_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_INC_X_ODD_REG 0x3814 +#define OV8865_INC_X_ODD(v) ((v) & GENMASK(4, 0)) +#define OV8865_INC_X_EVEN_REG 0x3815 +#define OV8865_INC_X_EVEN(v) ((v) & GENMASK(4, 0)) +#define OV8865_VSYNC_START_H_REG 0x3816 +#define OV8865_VSYNC_START_H(v) (((v) & GENMASK(15, 8)) >> 8) +#define OV8865_VSYNC_START_L_REG 0x3817 +#define OV8865_VSYNC_START_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_VSYNC_END_H_REG 0x3818 +#define OV8865_VSYNC_END_H(v) (((v) & GENMASK(15, 8)) >> 8) +#define OV8865_VSYNC_END_L_REG 0x3819 +#define OV8865_VSYNC_END_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_HSYNC_FIRST_H_REG 0x381a +#define OV8865_HSYNC_FIRST_H(v) (((v) & GENMASK(15, 8)) >> 8) +#define OV8865_HSYNC_FIRST_L_REG 0x381b +#define OV8865_HSYNC_FIRST_L(v) ((v) & GENMASK(7, 0)) + +#define OV8865_FORMAT1_REG 0x3820 +#define OV8865_FORMAT1_FLIP_VERT_ISP_EN BIT(2) +#define OV8865_FORMAT1_FLIP_VERT_SENSOR_EN BIT(1) +#define OV8865_FORMAT2_REG 0x3821 +#define OV8865_FORMAT2_HSYNC_EN BIT(6) +#define OV8865_FORMAT2_FST_VBIN_EN BIT(5) +#define OV8865_FORMAT2_FST_HBIN_EN BIT(4) +#define OV8865_FORMAT2_ISP_HORZ_VAR2_EN BIT(3) +#define OV8865_FORMAT2_FLIP_HORZ_ISP_EN BIT(2) +#define OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN BIT(1) +#define OV8865_FORMAT2_SYNC_HBIN_EN BIT(0) + + +#define OV8865_INC_Y_ODD_REG 0x382a +#define OV8865_INC_Y_ODD(v) ((v) & GENMASK(4, 0)) +#define OV8865_INC_Y_EVEN_REG 0x382b +#define OV8865_INC_Y_EVEN(v) ((v) & GENMASK(4, 0)) + +#define OV8865_ABLC_NUM_REG 0x3830 +#define OV8865_ABLC_NUM(v) ((v) & GENMASK(4, 0)) + +#define OV8865_ZLINE_NUM_REG 0x3836 +#define OV8865_ZLINE_NUM(v) ((v) & GENMASK(4, 0)) + +#define OV8865_AUTO_SIZE_CTRL_REG 0x3841 +#define OV8865_AUTO_SIZE_CTRL_OFFSET_Y_REG BIT(5) +#define OV8865_AUTO_SIZE_CTRL_OFFSET_X_REG BIT(4) +#define OV8865_AUTO_SIZE_CTRL_CROP_END_Y_REG BIT(3) +#define OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG BIT(2) +#define OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG BIT(1) +#define OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG BIT(0) +#define OV8865_AUTO_SIZE_X_OFFSET_H_REG 0x3842 +#define OV8865_AUTO_SIZE_X_OFFSET_L_REG 0x3843 +#define OV8865_AUTO_SIZE_Y_OFFSET_H_REG 0x3844 +#define OV8865_AUTO_SIZE_Y_OFFSET_L_REG 0x3845 +#define OV8865_AUTO_SIZE_BOUNDARIES_REG 0x3846 +#define OV8865_AUTO_SIZE_BOUNDARIES_Y(v) (((v) << 4) & GENMASK(7, 4)) +#define OV8865_AUTO_SIZE_BOUNDARIES_X(v) ((v) & GENMASK(3, 0)) + +/* PSRAM */ + +#define OV8865_PSRAM_CTRL8_REG 0x3f08 + +/* Black Level */ + +#define OV8865_BLC_CTRL0_REG 0x4000 +#define OV8865_BLC_CTRL0_TRIG_RANGE_EN BIT(7) +#define OV8865_BLC_CTRL0_TRIG_FORMAT_EN BIT(6) +#define OV8865_BLC_CTRL0_TRIG_GAIN_EN BIT(5) +#define OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN BIT(4) +#define OV8865_BLC_CTRL0_TRIG_MANUAL_EN BIT(3) +#define OV8865_BLC_CTRL0_FREEZE_EN BIT(2) +#define OV8865_BLC_CTRL0_ALWAYS_EN BIT(1) +#define OV8865_BLC_CTRL0_FILTER_EN BIT(0) +#define OV8865_BLC_CTRL1_REG 0x4001 +#define OV8865_BLC_CTRL1_DITHER_EN BIT(7) +#define OV8865_BLC_CTRL1_ZERO_LINE_DIFF_EN BIT(6) +#define OV8865_BLC_CTRL1_COL_SHIFT_256 (0 << 4) +#define OV8865_BLC_CTRL1_COL_SHIFT_128 (1 << 4) +#define OV8865_BLC_CTRL1_COL_SHIFT_64 (2 << 4) +#define OV8865_BLC_CTRL1_COL_SHIFT_32 (3 << 4) +#define OV8865_BLC_CTRL1_OFFSET_LIMIT_EN BIT(2) +#define OV8865_BLC_CTRL1_COLUMN_CANCEL_EN BIT(1) +#define OV8865_BLC_CTRL2_REG 0x4002 +#define OV8865_BLC_CTRL3_REG 0x4003 +#define OV8865_BLC_CTRL4_REG 0x4004 +#define OV8865_BLC_CTRL5_REG 0x4005 +#define OV8865_BLC_CTRL6_REG 0x4006 +#define OV8865_BLC_CTRL7_REG 0x4007 +#define OV8865_BLC_CTRL8_REG 0x4008 +#define OV8865_BLC_CTRL9_REG 0x4009 +#define OV8865_BLC_CTRLA_REG 0x400a +#define OV8865_BLC_CTRLB_REG 0x400b +#define OV8865_BLC_CTRLC_REG 0x400c +#define OV8865_BLC_CTRLD_REG 0x400d +#define OV8865_BLC_CTRLD_OFFSET_TRIGGER(v) ((v) & GENMASK(7, 0)) + +#define OV8865_BLC_CTRL1F_REG 0x401f +#define OV8865_BLC_CTRL1F_RB_REVERSE BIT(3) +#define OV8865_BLC_CTRL1F_INTERPOL_X_EN BIT(2) +#define OV8865_BLC_CTRL1F_INTERPOL_Y_EN BIT(1) + +#define OV8865_BLC_ANCHOR_LEFT_START_H_REG 0x4020 +#define OV8865_BLC_ANCHOR_LEFT_START_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_BLC_ANCHOR_LEFT_START_L_REG 0x4021 +#define OV8865_BLC_ANCHOR_LEFT_START_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_BLC_ANCHOR_LEFT_END_H_REG 0x4022 +#define OV8865_BLC_ANCHOR_LEFT_END_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_BLC_ANCHOR_LEFT_END_L_REG 0x4023 +#define OV8865_BLC_ANCHOR_LEFT_END_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_BLC_ANCHOR_RIGHT_START_H_REG 0x4024 +#define OV8865_BLC_ANCHOR_RIGHT_START_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_BLC_ANCHOR_RIGHT_START_L_REG 0x4025 +#define OV8865_BLC_ANCHOR_RIGHT_START_L(v) ((v) & GENMASK(7, 0)) +#define OV8865_BLC_ANCHOR_RIGHT_END_H_REG 0x4026 +#define OV8865_BLC_ANCHOR_RIGHT_END_H(v) (((v) & GENMASK(11, 8)) >> 8) +#define OV8865_BLC_ANCHOR_RIGHT_END_L_REG 0x4027 +#define OV8865_BLC_ANCHOR_RIGHT_END_L(v) ((v) & GENMASK(7, 0)) + +#define OV8865_BLC_TOP_ZLINE_START_REG 0x4028 +#define OV8865_BLC_TOP_ZLINE_START(v) ((v) & GENMASK(5, 0)) +#define OV8865_BLC_TOP_ZLINE_NUM_REG 0x4029 +#define OV8865_BLC_TOP_ZLINE_NUM(v) ((v) & GENMASK(4, 0)) +#define OV8865_BLC_TOP_BLKLINE_START_REG 0x402a +#define OV8865_BLC_TOP_BLKLINE_START(v) ((v) & GENMASK(5, 0)) +#define OV8865_BLC_TOP_BLKLINE_NUM_REG 0x402b +#define OV8865_BLC_TOP_BLKLINE_NUM(v) ((v) & GENMASK(4, 0)) +#define OV8865_BLC_BOT_ZLINE_START_REG 0x402c +#define OV8865_BLC_BOT_ZLINE_START(v) ((v) & GENMASK(5, 0)) +#define OV8865_BLC_BOT_ZLINE_NUM_REG 0x402d +#define OV8865_BLC_BOT_ZLINE_NUM(v) ((v) & GENMASK(4, 0)) +#define OV8865_BLC_BOT_BLKLINE_START_REG 0x402e +#define OV8865_BLC_BOT_BLKLINE_START(v) ((v) & GENMASK(5, 0)) +#define OV8865_BLC_BOT_BLKLINE_NUM_REG 0x402f +#define OV8865_BLC_BOT_BLKLINE_NUM(v) ((v) & GENMASK(4, 0)) + +#define OV8865_BLC_OFFSET_LIMIT_REG 0x4034 +#define OV8865_BLC_OFFSET_LIMIT(v) ((v) & GENMASK(7, 0)) + +/* VFIFO */ + +#define OV8865_VFIFO_READ_START_H_REG 0x4600 +#define OV8865_VFIFO_READ_START_H(v) (((v) & GENMASK(15, 8)) >> 8) +#define OV8865_VFIFO_READ_START_L_REG 0x4601 +#define OV8865_VFIFO_READ_START_L(v) ((v) & GENMASK(7, 0)) + +/* MIPI */ + +#define OV8865_MIPI_CTRL0_REG 0x4800 +#define OV8865_MIPI_CTRL1_REG 0x4801 +#define OV8865_MIPI_CTRL2_REG 0x4802 +#define OV8865_MIPI_CTRL3_REG 0x4803 +#define OV8865_MIPI_CTRL4_REG 0x4804 +#define OV8865_MIPI_CTRL5_REG 0x4805 +#define OV8865_MIPI_CTRL6_REG 0x4806 +#define OV8865_MIPI_CTRL7_REG 0x4807 +#define OV8865_MIPI_CTRL8_REG 0x4808 + +#define OV8865_MIPI_FCNT_MAX_H_REG 0x4810 +#define OV8865_MIPI_FCNT_MAX_L_REG 0x4811 + +#define OV8865_MIPI_CTRL13_REG 0x4813 +#define OV8865_MIPI_CTRL14_REG 0x4814 +#define OV8865_MIPI_CTRL15_REG 0x4815 +#define OV8865_MIPI_EMBEDDED_DT_REG 0x4816 + +#define OV8865_MIPI_HS_ZERO_MIN_H_REG 0x4818 +#define OV8865_MIPI_HS_ZERO_MIN_L_REG 0x4819 +#define OV8865_MIPI_HS_TRAIL_MIN_H_REG 0x481a +#define OV8865_MIPI_HS_TRAIL_MIN_L_REG 0x481b +#define OV8865_MIPI_CLK_ZERO_MIN_H_REG 0x481c +#define OV8865_MIPI_CLK_ZERO_MIN_L_REG 0x481d +#define OV8865_MIPI_CLK_PREPARE_MAX_REG 0x481e +#define OV8865_MIPI_CLK_PREPARE_MIN_REG 0x481f +#define OV8865_MIPI_CLK_POST_MIN_H_REG 0x4820 +#define OV8865_MIPI_CLK_POST_MIN_L_REG 0x4821 +#define OV8865_MIPI_CLK_TRAIL_MIN_H_REG 0x4822 +#define OV8865_MIPI_CLK_TRAIL_MIN_L_REG 0x4823 +#define OV8865_MIPI_LPX_P_MIN_H_REG 0x4824 +#define OV8865_MIPI_LPX_P_MIN_L_REG 0x4825 +#define OV8865_MIPI_HS_PREPARE_MIN_REG 0x4826 +#define OV8865_MIPI_HS_PREPARE_MAX_REG 0x4827 +#define OV8865_MIPI_HS_EXIT_MIN_H_REG 0x4828 +#define OV8865_MIPI_HS_EXIT_MIN_L_REG 0x4829 +#define OV8865_MIPI_UI_HS_ZERO_MIN_REG 0x482a +#define OV8865_MIPI_UI_HS_TRAIL_MIN_REG 0x482b +#define OV8865_MIPI_UI_CLK_ZERO_MIN_REG 0x482c +#define OV8865_MIPI_UI_CLK_PREPARE_REG 0x482d +#define OV8865_MIPI_UI_CLK_POST_MIN_REG 0x482e +#define OV8865_MIPI_UI_CLK_TRAIL_MIN_REG 0x482f +#define OV8865_MIPI_UI_LPX_P_MIN_REG 0x4830 +#define OV8865_MIPI_UI_HS_PREPARE_REG 0x4831 +#define OV8865_MIPI_UI_HS_EXIT_MIN_REG 0x4832 +#define OV8865_MIPI_PKT_START_SIZE_REG 0x4833 + +#define OV8865_MIPI_PCLK_PERIOD_REG 0x4837 +#define OV8865_MIPI_LP_GPIO0_REG 0x4838 +#define OV8865_MIPI_LP_GPIO1_REG 0x4839 + +#define OV8865_MIPI_CTRL3C_REG 0x483c +#define OV8865_MIPI_LP_GPIO4_REG 0x483d + +#define OV8865_MIPI_CTRL4A_REG 0x484a +#define OV8865_MIPI_CTRL4B_REG 0x484b +#define OV8865_MIPI_CTRL4C_REG 0x484c +#define OV8865_MIPI_LANE_TEST_PATTERN_REG 0x484d +#define OV8865_MIPI_FRAME_END_DELAY_REG 0x484e +#define OV8865_MIPI_CLOCK_TEST_PATTERN_REG 0x484f +#define OV8865_MIPI_LANE_SEL01_REG 0x4850 +#define OV8865_MIPI_LANE_SEL01_LANE0(v) (((v) << 0) & GENMASK(2, 0)) +#define OV8865_MIPI_LANE_SEL01_LANE1(v) (((v) << 4) & GENMASK(6, 4)) +#define OV8865_MIPI_LANE_SEL23_REG 0x4851 +#define OV8865_MIPI_LANE_SEL23_LANE2(v) (((v) << 0) & GENMASK(2, 0)) +#define OV8865_MIPI_LANE_SEL23_LANE3(v) (((v) << 4) & GENMASK(6, 4)) + +/* ISP */ + +#define OV8865_ISP_CTRL0_REG 0x5000 +#define OV8865_ISP_CTRL0_LENC_EN BIT(7) +#define OV8865_ISP_CTRL0_WHITE_BALANCE_EN BIT(4) +#define OV8865_ISP_CTRL0_DPC_BLACK_EN BIT(2) +#define OV8865_ISP_CTRL0_DPC_WHITE_EN BIT(1) +#define OV8865_ISP_CTRL1_REG 0x5001 +#define OV8865_ISP_CTRL1_BLC_EN BIT(0) +#define OV8865_ISP_CTRL2_REG 0x5002 +#define OV8865_ISP_CTRL2_DEBUG BIT(3) +#define OV8865_ISP_CTRL2_VARIOPIXEL_EN BIT(2) +#define OV8865_ISP_CTRL2_VSYNC_LATCH_EN BIT(0) +#define OV8865_ISP_CTRL3_REG 0x5003 + +#define OV8865_ISP_GAIN_RED_H_REG 0x5018 +#define OV8865_ISP_GAIN_RED_H(v) (((v) & GENMASK(13, 6)) >> 6) +#define OV8865_ISP_GAIN_RED_L_REG 0x5019 +#define OV8865_ISP_GAIN_RED_L(v) ((v) & GENMASK(5, 0)) +#define OV8865_ISP_GAIN_GREEN_H_REG 0x501a +#define OV8865_ISP_GAIN_GREEN_H(v) (((v) & GENMASK(13, 6)) >> 6) +#define OV8865_ISP_GAIN_GREEN_L_REG 0x501b +#define OV8865_ISP_GAIN_GREEN_L(v) ((v) & GENMASK(5, 0)) +#define OV8865_ISP_GAIN_BLUE_H_REG 0x501c +#define OV8865_ISP_GAIN_BLUE_H(v) (((v) & GENMASK(13, 6)) >> 6) +#define OV8865_ISP_GAIN_BLUE_L_REG 0x501d +#define OV8865_ISP_GAIN_BLUE_L(v) ((v) & GENMASK(5, 0)) + +/* VarioPixel */ + +#define OV8865_VAP_CTRL0_REG 0x5900 +#define OV8865_VAP_CTRL1_REG 0x5901 +#define OV8865_VAP_CTRL1_HSUB_COEF(v) ((((v) - 1) << 2) & \ + GENMASK(3, 2)) +#define OV8865_VAP_CTRL1_VSUB_COEF(v) (((v) - 1) & GENMASK(1, 0)) + +/* Pre-DSP */ + +#define OV8865_PRE_CTRL0_REG 0x5e00 +#define OV8865_PRE_CTRL0_PATTERN_EN BIT(7) +#define OV8865_PRE_CTRL0_ROLLING_BAR_EN BIT(6) +#define OV8865_PRE_CTRL0_TRANSPARENT_MODE BIT(5) +#define OV8865_PRE_CTRL0_SQUARES_BW_MODE BIT(4) +#define OV8865_PRE_CTRL0_PATTERN_COLOR_BARS 0 +#define OV8865_PRE_CTRL0_PATTERN_RANDOM_DATA 1 +#define OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES 2 +#define OV8865_PRE_CTRL0_PATTERN_BLACK 3 + +/* Macros */ + +#define ov8865_subdev_sensor(s) \ + container_of(s, struct ov8865_sensor, subdev) + +#define ov8865_ctrl_subdev(c) \ + (&container_of(c->handler, struct ov8865_sensor, ctrls.handler)->subdev) + +/* Data structures */ + +struct ov8865_register_value { + u16 address; + u8 value; + unsigned int delay_ms; +}; + +/* + * PLL1 Clock Tree: + * + * +-< EXTCLK + * | + * +-+ pll_pre_div_half (0x30a [0]) + * | + * +-+ pll_pre_div (0x300 [2:0], special values: + * | 0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 4, 7: 8) + * +-+ pll_mul (0x301 [1:0], 0x302 [7:0]) + * | + * +-+ m_div (0x303 [3:0]) + * | | + * | +-> PHY_SCLK + * | | + * | +-+ mipi_div (0x304 [1:0], special values: 0: 4, 1: 5, 2: 6, 3: 8) + * | | + * | +-+ pclk_div (0x3020 [3]) + * | | + * | +-> PCLK + * | + * +-+ sys_pre_div (0x305 [1:0], special values: 0: 3, 1: 4, 2: 5, 3: 6) + * | + * +-+ sys_div (0x306 [0]) + * | + * +-+ sys_sel (0x3032 [7], 0: PLL1, 1: PLL2) + * | + * +-+ sclk_sel (0x3033 [1], 0: sys_sel, 1: PLL2 DAC_CLK) + * | + * +-+ sclk_pre_div (0x3106 [3:2], special values: + * | 0: 1, 1: 2, 2: 4, 3: 1) + * | + * +-+ sclk_div (0x3106 [7:4], special values: 0: 1) + * | + * +-> SCLK + */ + +struct ov8865_pll1_config { + unsigned int pll_pre_div_half; + unsigned int pll_pre_div; + unsigned int pll_mul; + unsigned int m_div; + unsigned int mipi_div; + unsigned int pclk_div; + unsigned int sys_pre_div; + unsigned int sys_div; +}; + +/* + * PLL2 Clock Tree: + * + * +-< EXTCLK + * | + * +-+ pll_pre_div_half (0x312 [4]) + * | + * +-+ pll_pre_div (0x30b [2:0], special values: + * | 0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 4, 7: 8) + * +-+ pll_mul (0x30c [1:0], 0x30d [7:0]) + * | + * +-+ dac_div (0x312 [3:0]) + * | | + * | +-> DAC_CLK + * | + * +-+ sys_pre_div (0x30f [3:0]) + * | + * +-+ sys_div (0x30e [2:0], special values: + * | 0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 3.5, 6: 4, 7:5) + * | + * +-+ sys_sel (0x3032 [7], 0: PLL1, 1: PLL2) + * | + * +-+ sclk_sel (0x3033 [1], 0: sys_sel, 1: PLL2 DAC_CLK) + * | + * +-+ sclk_pre_div (0x3106 [3:2], special values: + * | 0: 1, 1: 2, 2: 4, 3: 1) + * | + * +-+ sclk_div (0x3106 [7:4], special values: 0: 1) + * | + * +-> SCLK + */ + +struct ov8865_pll2_config { + unsigned int pll_pre_div_half; + unsigned int pll_pre_div; + unsigned int pll_mul; + unsigned int dac_div; + unsigned int sys_pre_div; + unsigned int sys_div; +}; + +struct ov8865_sclk_config { + unsigned int sys_sel; + unsigned int sclk_sel; + unsigned int sclk_pre_div; + unsigned int sclk_div; +}; + +/* + * General formulas for (array-centered) mode calculation: + * - photo_array_width = 3296 + * - crop_start_x = (photo_array_width - output_size_x) / 2 + * - crop_end_x = crop_start_x + offset_x + output_size_x - 1 + * + * - photo_array_height = 2480 + * - crop_start_y = (photo_array_height - output_size_y) / 2 + * - crop_end_y = crop_start_y + offset_y + output_size_y - 1 + */ + +struct ov8865_mode { + unsigned int crop_start_x; + unsigned int offset_x; + unsigned int output_size_x; + unsigned int crop_end_x; + unsigned int hts; + + unsigned int crop_start_y; + unsigned int offset_y; + unsigned int output_size_y; + unsigned int crop_end_y; + unsigned int vts; + + /* With auto size, only output and total sizes need to be set. */ + bool size_auto; + unsigned int size_auto_boundary_x; + unsigned int size_auto_boundary_y; + + bool binning_x; + bool binning_y; + bool variopixel; + unsigned int variopixel_hsub_coef; + unsigned int variopixel_vsub_coef; + + /* Bits for the format register, used for binning. */ + bool sync_hbin; + bool horz_var2; + + unsigned int inc_x_odd; + unsigned int inc_x_even; + unsigned int inc_y_odd; + unsigned int inc_y_even; + + unsigned int vfifo_read_start; + + unsigned int ablc_num; + unsigned int zline_num; + + unsigned int blc_top_zero_line_start; + unsigned int blc_top_zero_line_num; + unsigned int blc_top_black_line_start; + unsigned int blc_top_black_line_num; + + unsigned int blc_bottom_zero_line_start; + unsigned int blc_bottom_zero_line_num; + unsigned int blc_bottom_black_line_start; + unsigned int blc_bottom_black_line_num; + + u8 blc_col_shift_mask; + + unsigned int blc_anchor_left_start; + unsigned int blc_anchor_left_end; + unsigned int blc_anchor_right_start; + unsigned int blc_anchor_right_end; + + struct v4l2_fract frame_interval; + + const struct ov8865_pll1_config *pll1_config; + const struct ov8865_pll2_config *pll2_config; + const struct ov8865_sclk_config *sclk_config; + + const struct ov8865_register_value *register_values; + unsigned int register_values_count; +}; + +struct ov8865_state { + const struct ov8865_mode *mode; + u32 mbus_code; + + bool streaming; +}; + +struct ov8865_ctrls { + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + + struct v4l2_ctrl_handler handler; +}; + +struct ov8865_sensor { + struct device *dev; + struct i2c_client *i2c_client; + struct gpio_desc *reset; + struct gpio_desc *powerdown; + struct regulator *avdd; + struct regulator *dvdd; + struct regulator *dovdd; + struct clk *extclk; + + struct v4l2_fwnode_endpoint endpoint; + struct v4l2_subdev subdev; + struct media_pad pad; + + struct mutex mutex; + + struct ov8865_state state; + struct ov8865_ctrls ctrls; +}; + +/* Static definitions */ + +/* + * EXTCLK = 24 MHz + * PHY_SCLK = 720 MHz + * MIPI_PCLK = 90 MHz + */ +static const struct ov8865_pll1_config ov8865_pll1_config_native = { + .pll_pre_div_half = 1, + .pll_pre_div = 0, + .pll_mul = 30, + .m_div = 1, + .mipi_div = 3, + .pclk_div = 1, + .sys_pre_div = 1, + .sys_div = 2, +}; + +/* + * EXTCLK = 24 MHz + * DAC_CLK = 360 MHz + * SCLK = 144 MHz + */ + +static const struct ov8865_pll2_config ov8865_pll2_config_native = { + .pll_pre_div_half = 1, + .pll_pre_div = 0, + .pll_mul = 30, + .dac_div = 2, + .sys_pre_div = 5, + .sys_div = 0, +}; + +/* + * EXTCLK = 24 MHz + * DAC_CLK = 360 MHz + * SCLK = 80 MHz + */ + +static const struct ov8865_pll2_config ov8865_pll2_config_binning = { + .pll_pre_div_half = 1, + .pll_pre_div = 0, + .pll_mul = 30, + .dac_div = 2, + .sys_pre_div = 10, + .sys_div = 0, +}; + +static const struct ov8865_sclk_config ov8865_sclk_config_native = { + .sys_sel = 1, + .sclk_sel = 0, + .sclk_pre_div = 0, + .sclk_div = 0, +}; + +static const struct ov8865_register_value ov8865_register_values_native[] = { + /* Sensor */ + + { 0x3700, 0x48 }, + { 0x3701, 0x18 }, + { 0x3702, 0x50 }, + { 0x3703, 0x32 }, + { 0x3704, 0x28 }, + { 0x3706, 0x70 }, + { 0x3707, 0x08 }, + { 0x3708, 0x48 }, + { 0x3709, 0x80 }, + { 0x370a, 0x01 }, + { 0x370b, 0x70 }, + { 0x370c, 0x07 }, + { 0x3718, 0x14 }, + { 0x3712, 0x44 }, + { 0x371e, 0x31 }, + { 0x371f, 0x7f }, + { 0x3720, 0x0a }, + { 0x3721, 0x0a }, + { 0x3724, 0x04 }, + { 0x3725, 0x04 }, + { 0x3726, 0x0c }, + { 0x3728, 0x0a }, + { 0x3729, 0x03 }, + { 0x372a, 0x06 }, + { 0x372b, 0xa6 }, + { 0x372c, 0xa6 }, + { 0x372d, 0xa6 }, + { 0x372e, 0x0c }, + { 0x372f, 0x20 }, + { 0x3730, 0x02 }, + { 0x3731, 0x0c }, + { 0x3732, 0x28 }, + { 0x3736, 0x30 }, + { 0x373a, 0x04 }, + { 0x373b, 0x18 }, + { 0x373c, 0x14 }, + { 0x373e, 0x06 }, + { 0x375a, 0x0c }, + { 0x375b, 0x26 }, + { 0x375d, 0x04 }, + { 0x375f, 0x28 }, + { 0x3767, 0x1e }, + { 0x3772, 0x46 }, + { 0x3773, 0x04 }, + { 0x3774, 0x2c }, + { 0x3775, 0x13 }, + { 0x3776, 0x10 }, + { 0x37a0, 0x88 }, + { 0x37a1, 0x7a }, + { 0x37a2, 0x7a }, + { 0x37a3, 0x02 }, + { 0x37a5, 0x09 }, + { 0x37a7, 0x88 }, + { 0x37a8, 0xb0 }, + { 0x37a9, 0xb0 }, + { 0x37aa, 0x88 }, + { 0x37ab, 0x5c }, + { 0x37ac, 0x5c }, + { 0x37ad, 0x55 }, + { 0x37ae, 0x19 }, + { 0x37af, 0x19 }, + { 0x37b3, 0x84 }, + { 0x37b4, 0x84 }, + { 0x37b5, 0x66 }, + + /* PSRAM */ + + { OV8865_PSRAM_CTRL8_REG, 0x16 }, + + /* ADC Sync */ + + { 0x4500, 0x68 }, +}; + +static const struct ov8865_register_value ov8865_register_values_binning[] = { + /* Sensor */ + + { 0x3700, 0x24 }, + { 0x3701, 0x0c }, + { 0x3702, 0x28 }, + { 0x3703, 0x19 }, + { 0x3704, 0x14 }, + { 0x3706, 0x38 }, + { 0x3707, 0x04 }, + { 0x3708, 0x24 }, + { 0x3709, 0x40 }, + { 0x370a, 0x00 }, + { 0x370b, 0xb8 }, + { 0x370c, 0x04 }, + { 0x3718, 0x12 }, + { 0x3712, 0x42 }, + { 0x371e, 0x19 }, + { 0x371f, 0x40 }, + { 0x3720, 0x05 }, + { 0x3721, 0x05 }, + { 0x3724, 0x02 }, + { 0x3725, 0x02 }, + { 0x3726, 0x06 }, + { 0x3728, 0x05 }, + { 0x3729, 0x02 }, + { 0x372a, 0x03 }, + { 0x372b, 0x53 }, + { 0x372c, 0xa3 }, + { 0x372d, 0x53 }, + { 0x372e, 0x06 }, + { 0x372f, 0x10 }, + { 0x3730, 0x01 }, + { 0x3731, 0x06 }, + { 0x3732, 0x14 }, + { 0x3736, 0x20 }, + { 0x373a, 0x02 }, + { 0x373b, 0x0c }, + { 0x373c, 0x0a }, + { 0x373e, 0x03 }, + { 0x375a, 0x06 }, + { 0x375b, 0x13 }, + { 0x375d, 0x02 }, + { 0x375f, 0x14 }, + { 0x3767, 0x1c }, + { 0x3772, 0x23 }, + { 0x3773, 0x02 }, + { 0x3774, 0x16 }, + { 0x3775, 0x12 }, + { 0x3776, 0x08 }, + { 0x37a0, 0x44 }, + { 0x37a1, 0x3d }, + { 0x37a2, 0x3d }, + { 0x37a3, 0x01 }, + { 0x37a5, 0x08 }, + { 0x37a7, 0x44 }, + { 0x37a8, 0x58 }, + { 0x37a9, 0x58 }, + { 0x37aa, 0x44 }, + { 0x37ab, 0x2e }, + { 0x37ac, 0x2e }, + { 0x37ad, 0x33 }, + { 0x37ae, 0x0d }, + { 0x37af, 0x0d }, + { 0x37b3, 0x42 }, + { 0x37b4, 0x42 }, + { 0x37b5, 0x33 }, + + /* PSRAM */ + + { OV8865_PSRAM_CTRL8_REG, 0x0b }, + + /* ADC Sync */ + + { 0x4500, 0x40 }, +}; + +static const struct ov8865_mode ov8865_modes[] = { + /* 3264x2448 */ + { + /* Horizontal */ + .output_size_x = 3264, + .hts = 1944, + + /* Vertical */ + .output_size_y = 2448, + .vts = 2470, + + .size_auto = true, + .size_auto_boundary_x = 8, + .size_auto_boundary_y = 4, + + /* Subsample increase */ + .inc_x_odd = 1, + .inc_x_even = 1, + .inc_y_odd = 1, + .inc_y_even = 1, + + /* VFIFO */ + .vfifo_read_start = 16, + + .ablc_num = 4, + .zline_num = 1, + + /* Black Level */ + + .blc_top_zero_line_start = 0, + .blc_top_zero_line_num = 2, + .blc_top_black_line_start = 4, + .blc_top_black_line_num = 4, + + .blc_bottom_zero_line_start = 2, + .blc_bottom_zero_line_num = 2, + .blc_bottom_black_line_start = 8, + .blc_bottom_black_line_num = 2, + + .blc_anchor_left_start = 576, + .blc_anchor_left_end = 831, + .blc_anchor_right_start = 1984, + .blc_anchor_right_end = 2239, + + /* Frame Interval */ + .frame_interval = { 1, 30 }, + + /* PLL */ + .pll1_config = &ov8865_pll1_config_native, + .pll2_config = &ov8865_pll2_config_native, + .sclk_config = &ov8865_sclk_config_native, + + /* Registers */ + .register_values = ov8865_register_values_native, + .register_values_count = + ARRAY_SIZE(ov8865_register_values_native), + }, + /* 3264x1836 */ + { + /* Horizontal */ + .output_size_x = 3264, + .hts = 2582, + + /* Vertical */ + .output_size_y = 1836, + .vts = 2002, + + .size_auto = true, + .size_auto_boundary_x = 8, + .size_auto_boundary_y = 4, + + /* Subsample increase */ + .inc_x_odd = 1, + .inc_x_even = 1, + .inc_y_odd = 1, + .inc_y_even = 1, + + /* VFIFO */ + .vfifo_read_start = 16, + + .ablc_num = 4, + .zline_num = 1, + + /* Black Level */ + + .blc_top_zero_line_start = 0, + .blc_top_zero_line_num = 2, + .blc_top_black_line_start = 4, + .blc_top_black_line_num = 4, + + .blc_bottom_zero_line_start = 2, + .blc_bottom_zero_line_num = 2, + .blc_bottom_black_line_start = 8, + .blc_bottom_black_line_num = 2, + + .blc_anchor_left_start = 576, + .blc_anchor_left_end = 831, + .blc_anchor_right_start = 1984, + .blc_anchor_right_end = 2239, + + /* Frame Interval */ + .frame_interval = { 1, 30 }, + + /* PLL */ + .pll1_config = &ov8865_pll1_config_native, + .pll2_config = &ov8865_pll2_config_native, + .sclk_config = &ov8865_sclk_config_native, + + /* Registers */ + .register_values = ov8865_register_values_native, + .register_values_count = + ARRAY_SIZE(ov8865_register_values_native), + }, + /* 1632x1224 */ + { + /* Horizontal */ + .output_size_x = 1632, + .hts = 1923, + + /* Vertical */ + .output_size_y = 1224, + .vts = 1248, + + .size_auto = true, + .size_auto_boundary_x = 8, + .size_auto_boundary_y = 8, + + /* Subsample increase */ + .inc_x_odd = 3, + .inc_x_even = 1, + .inc_y_odd = 3, + .inc_y_even = 1, + + /* Binning */ + .binning_y = true, + .sync_hbin = true, + + /* VFIFO */ + .vfifo_read_start = 116, + + .ablc_num = 8, + .zline_num = 2, + + /* Black Level */ + + .blc_top_zero_line_start = 0, + .blc_top_zero_line_num = 2, + .blc_top_black_line_start = 4, + .blc_top_black_line_num = 4, + + .blc_bottom_zero_line_start = 2, + .blc_bottom_zero_line_num = 2, + .blc_bottom_black_line_start = 8, + .blc_bottom_black_line_num = 2, + + .blc_anchor_left_start = 288, + .blc_anchor_left_end = 415, + .blc_anchor_right_start = 992, + .blc_anchor_right_end = 1119, + + /* Frame Interval */ + .frame_interval = { 1, 30 }, + + /* PLL */ + .pll1_config = &ov8865_pll1_config_native, + .pll2_config = &ov8865_pll2_config_binning, + .sclk_config = &ov8865_sclk_config_native, + + /* Registers */ + .register_values = ov8865_register_values_binning, + .register_values_count = + ARRAY_SIZE(ov8865_register_values_binning), + }, + /* 800x600 (SVGA) */ + { + /* Horizontal */ + .output_size_x = 800, + .hts = 1250, + + /* Vertical */ + .output_size_y = 600, + .vts = 640, + + .size_auto = true, + .size_auto_boundary_x = 8, + .size_auto_boundary_y = 8, + + /* Subsample increase */ + .inc_x_odd = 3, + .inc_x_even = 1, + .inc_y_odd = 5, + .inc_y_even = 3, + + /* Binning */ + .binning_y = true, + .variopixel = true, + .variopixel_hsub_coef = 2, + .variopixel_vsub_coef = 1, + .sync_hbin = true, + .horz_var2 = true, + + /* VFIFO */ + .vfifo_read_start = 80, + + .ablc_num = 8, + .zline_num = 2, + + /* Black Level */ + + .blc_top_zero_line_start = 0, + .blc_top_zero_line_num = 2, + .blc_top_black_line_start = 2, + .blc_top_black_line_num = 2, + + .blc_bottom_zero_line_start = 0, + .blc_bottom_zero_line_num = 0, + .blc_bottom_black_line_start = 4, + .blc_bottom_black_line_num = 2, + + .blc_col_shift_mask = OV8865_BLC_CTRL1_COL_SHIFT_128, + + .blc_anchor_left_start = 288, + .blc_anchor_left_end = 415, + .blc_anchor_right_start = 992, + .blc_anchor_right_end = 1119, + + /* Frame Interval */ + .frame_interval = { 1, 90 }, + + /* PLL */ + .pll1_config = &ov8865_pll1_config_native, + .pll2_config = &ov8865_pll2_config_binning, + .sclk_config = &ov8865_sclk_config_native, + + /* Registers */ + .register_values = ov8865_register_values_binning, + .register_values_count = + ARRAY_SIZE(ov8865_register_values_binning), + }, +}; + +static const u32 ov8865_mbus_codes[] = { + MEDIA_BUS_FMT_SBGGR10_1X10, +}; + +static const struct ov8865_register_value ov8865_init_sequence[] = { + /* Analog */ + + { 0x3604, 0x04 }, + { 0x3602, 0x30 }, + { 0x3605, 0x00 }, + { 0x3607, 0x20 }, + { 0x3608, 0x11 }, + { 0x3609, 0x68 }, + { 0x360a, 0x40 }, + { 0x360c, 0xdd }, + { 0x360e, 0x0c }, + { 0x3610, 0x07 }, + { 0x3612, 0x86 }, + { 0x3613, 0x58 }, + { 0x3614, 0x28 }, + { 0x3617, 0x40 }, + { 0x3618, 0x5a }, + { 0x3619, 0x9b }, + { 0x361c, 0x00 }, + { 0x361d, 0x60 }, + { 0x3631, 0x60 }, + { 0x3633, 0x10 }, + { 0x3634, 0x10 }, + { 0x3635, 0x10 }, + { 0x3636, 0x10 }, + { 0x3638, 0xff }, + { 0x3641, 0x55 }, + { 0x3646, 0x86 }, + { 0x3647, 0x27 }, + { 0x364a, 0x1b }, + + /* Sensor */ + + { 0x3700, 0x24 }, + { 0x3701, 0x0c }, + { 0x3702, 0x28 }, + { 0x3703, 0x19 }, + { 0x3704, 0x14 }, + { 0x3705, 0x00 }, + { 0x3706, 0x38 }, + { 0x3707, 0x04 }, + { 0x3708, 0x24 }, + { 0x3709, 0x40 }, + { 0x370a, 0x00 }, + { 0x370b, 0xb8 }, + { 0x370c, 0x04 }, + { 0x3718, 0x12 }, + { 0x3719, 0x31 }, + { 0x3712, 0x42 }, + { 0x3714, 0x12 }, + { 0x371e, 0x19 }, + { 0x371f, 0x40 }, + { 0x3720, 0x05 }, + { 0x3721, 0x05 }, + { 0x3724, 0x02 }, + { 0x3725, 0x02 }, + { 0x3726, 0x06 }, + { 0x3728, 0x05 }, + { 0x3729, 0x02 }, + { 0x372a, 0x03 }, + { 0x372b, 0x53 }, + { 0x372c, 0xa3 }, + { 0x372d, 0x53 }, + { 0x372e, 0x06 }, + { 0x372f, 0x10 }, + { 0x3730, 0x01 }, + { 0x3731, 0x06 }, + { 0x3732, 0x14 }, + { 0x3733, 0x10 }, + { 0x3734, 0x40 }, + { 0x3736, 0x20 }, + { 0x373a, 0x02 }, + { 0x373b, 0x0c }, + { 0x373c, 0x0a }, + { 0x373e, 0x03 }, + { 0x3755, 0x40 }, + { 0x3758, 0x00 }, + { 0x3759, 0x4c }, + { 0x375a, 0x06 }, + { 0x375b, 0x13 }, + { 0x375c, 0x40 }, + { 0x375d, 0x02 }, + { 0x375e, 0x00 }, + { 0x375f, 0x14 }, + { 0x3767, 0x1c }, + { 0x3768, 0x04 }, + { 0x3769, 0x20 }, + { 0x376c, 0xc0 }, + { 0x376d, 0xc0 }, + { 0x376a, 0x08 }, + { 0x3761, 0x00 }, + { 0x3762, 0x00 }, + { 0x3763, 0x00 }, + { 0x3766, 0xff }, + { 0x376b, 0x42 }, + { 0x3772, 0x23 }, + { 0x3773, 0x02 }, + { 0x3774, 0x16 }, + { 0x3775, 0x12 }, + { 0x3776, 0x08 }, + { 0x37a0, 0x44 }, + { 0x37a1, 0x3d }, + { 0x37a2, 0x3d }, + { 0x37a3, 0x01 }, + { 0x37a4, 0x00 }, + { 0x37a5, 0x08 }, + { 0x37a6, 0x00 }, + { 0x37a7, 0x44 }, + { 0x37a8, 0x58 }, + { 0x37a9, 0x58 }, + { 0x3760, 0x00 }, + { 0x376f, 0x01 }, + { 0x37aa, 0x44 }, + { 0x37ab, 0x2e }, + { 0x37ac, 0x2e }, + { 0x37ad, 0x33 }, + { 0x37ae, 0x0d }, + { 0x37af, 0x0d }, + { 0x37b0, 0x00 }, + { 0x37b1, 0x00 }, + { 0x37b2, 0x00 }, + { 0x37b3, 0x42 }, + { 0x37b4, 0x42 }, + { 0x37b5, 0x33 }, + { 0x37b6, 0x00 }, + { 0x37b7, 0x00 }, + { 0x37b8, 0x00 }, + { 0x37b9, 0xff }, + + /* ADC Sync */ + + { 0x4503, 0x10 }, +}; + +static const s64 ov8865_link_freq_menu[] = { + 360000000, +}; + +static const char *const ov8865_test_pattern_menu[] = { + "Disabled", + "Random data", + "Color bars", + "Color bars with rolling bar", + "Color squares", + "Color squares with rolling bar" +}; + +static const u8 ov8865_test_pattern_bits[] = { + 0, + OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_RANDOM_DATA, + OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_BARS, + OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN | + OV8865_PRE_CTRL0_PATTERN_COLOR_BARS, + OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES, + OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN | + OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES, +}; + +/* Input/Output */ + +static int ov8865_read(struct ov8865_sensor *sensor, u16 address, u8 *value) +{ + unsigned char data[2] = { address >> 8, address & 0xff }; + struct i2c_client *client = sensor->i2c_client; + int ret; + + ret = i2c_master_send(client, data, sizeof(data)); + if (ret < 0) { + dev_dbg(&client->dev, "i2c send error at address %#04x\n", + address); + return ret; + } + + ret = i2c_master_recv(client, value, 1); + if (ret < 0) { + dev_dbg(&client->dev, "i2c recv error at address %#04x\n", + address); + return ret; + } + + return 0; +} + +static int ov8865_write(struct ov8865_sensor *sensor, u16 address, u8 value) +{ + unsigned char data[3] = { address >> 8, address & 0xff, value }; + struct i2c_client *client = sensor->i2c_client; + int ret; + + ret = i2c_master_send(client, data, sizeof(data)); + if (ret < 0) { + dev_dbg(&client->dev, "i2c send error at address %#04x\n", + address); + return ret; + } + + return 0; +} + +static int ov8865_write_sequence(struct ov8865_sensor *sensor, + const struct ov8865_register_value *sequence, + unsigned int sequence_count) +{ + unsigned int i; + int ret = 0; + + for (i = 0; i < sequence_count; i++) { + ret = ov8865_write(sensor, sequence[i].address, + sequence[i].value); + if (ret) + break; + + if (sequence[i].delay_ms) + msleep(sequence[i].delay_ms); + } + + return ret; +} + +static int ov8865_update_bits(struct ov8865_sensor *sensor, u16 address, + u8 mask, u8 bits) +{ + u8 value = 0; + int ret; + + ret = ov8865_read(sensor, address, &value); + if (ret) + return ret; + + value &= ~mask; + value |= bits; + + return ov8865_write(sensor, address, value); +} + +/* Sensor */ + +static int ov8865_sw_reset(struct ov8865_sensor *sensor) +{ + return ov8865_write(sensor, OV8865_SW_RESET_REG, OV8865_SW_RESET_RESET); +} + +static int ov8865_sw_standby(struct ov8865_sensor *sensor, int standby) +{ + u8 value = 0; + + if (!standby) + value = OV8865_SW_STANDBY_STREAM_ON; + + return ov8865_write(sensor, OV8865_SW_STANDBY_REG, value); +} + +static int ov8865_chip_id_check(struct ov8865_sensor *sensor) +{ + u16 regs[] = { OV8865_CHIP_ID_HH_REG, OV8865_CHIP_ID_H_REG, + OV8865_CHIP_ID_L_REG }; + u8 values[] = { OV8865_CHIP_ID_HH_VALUE, OV8865_CHIP_ID_H_VALUE, + OV8865_CHIP_ID_L_VALUE }; + unsigned int i; + u8 value; + int ret; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + ret = ov8865_read(sensor, regs[i], &value); + if (ret < 0) + return ret; + + if (value != values[i]) { + dev_err(sensor->dev, + "chip id value mismatch: %#x instead of %#x\n", + value, values[i]); + return -EINVAL; + } + } + + return 0; +} + +static int ov8865_charge_pump_configure(struct ov8865_sensor *sensor) +{ + return ov8865_write(sensor, OV8865_PUMP_CLK_DIV_REG, + OV8865_PUMP_CLK_DIV_PUMP_P(1)); +} + +static int ov8865_mipi_configure(struct ov8865_sensor *sensor) +{ + struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 = + &sensor->endpoint.bus.mipi_csi2; + unsigned int lanes_count = bus_mipi_csi2->num_data_lanes; + int ret; + + ret = ov8865_write(sensor, OV8865_MIPI_SC_CTRL0_REG, + OV8865_MIPI_SC_CTRL0_LANES(lanes_count) | + OV8865_MIPI_SC_CTRL0_MIPI_EN | + OV8865_MIPI_SC_CTRL0_UNKNOWN); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_MIPI_SC_CTRL2_REG, + OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC); + if (ret) + return ret; + + if (lanes_count >= 2) { + ret = ov8865_write(sensor, OV8865_MIPI_LANE_SEL01_REG, + OV8865_MIPI_LANE_SEL01_LANE0(0) | + OV8865_MIPI_LANE_SEL01_LANE1(1)); + if (ret) + return ret; + } + + if (lanes_count >= 4) { + ret = ov8865_write(sensor, OV8865_MIPI_LANE_SEL23_REG, + OV8865_MIPI_LANE_SEL23_LANE2(2) | + OV8865_MIPI_LANE_SEL23_LANE3(3)); + if (ret) + return ret; + } + + ret = ov8865_update_bits(sensor, OV8865_CLK_SEL1_REG, + OV8865_CLK_SEL1_MIPI_EOF, + OV8865_CLK_SEL1_MIPI_EOF); + if (ret) + return ret; + + /* + * This value might need to change depending on PCLK rate, + * but it's unclear how. This value seems to generally work + * while the default value was found to cause transmission errors. + */ + return ov8865_write(sensor, OV8865_MIPI_PCLK_PERIOD_REG, 0x16); +} + +static int ov8865_black_level_configure(struct ov8865_sensor *sensor) +{ + int ret; + + /* Trigger BLC on relevant events and enable filter. */ + ret = ov8865_write(sensor, OV8865_BLC_CTRL0_REG, + OV8865_BLC_CTRL0_TRIG_RANGE_EN | + OV8865_BLC_CTRL0_TRIG_FORMAT_EN | + OV8865_BLC_CTRL0_TRIG_GAIN_EN | + OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN | + OV8865_BLC_CTRL0_FILTER_EN); + if (ret) + return ret; + + /* Lower BLC offset trigger threshold. */ + ret = ov8865_write(sensor, OV8865_BLC_CTRLD_REG, + OV8865_BLC_CTRLD_OFFSET_TRIGGER(16)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_BLC_CTRL1F_REG, 0); + if (ret) + return ret; + + /* Increase BLC offset maximum limit. */ + return ov8865_write(sensor, OV8865_BLC_OFFSET_LIMIT_REG, + OV8865_BLC_OFFSET_LIMIT(63)); +} + +static int ov8865_isp_configure(struct ov8865_sensor *sensor) +{ + int ret; + + /* Disable lens correction. */ + ret = ov8865_write(sensor, OV8865_ISP_CTRL0_REG, + OV8865_ISP_CTRL0_WHITE_BALANCE_EN | + OV8865_ISP_CTRL0_DPC_BLACK_EN | + OV8865_ISP_CTRL0_DPC_WHITE_EN); + if (ret) + return ret; + + return ov8865_write(sensor, OV8865_ISP_CTRL1_REG, + OV8865_ISP_CTRL1_BLC_EN); +} + +static unsigned long ov8865_mode_pll1_rate(struct ov8865_sensor *sensor, + const struct ov8865_mode *mode) +{ + const struct ov8865_pll1_config *config = mode->pll1_config; + unsigned long extclk_rate; + unsigned long pll1_rate; + + extclk_rate = clk_get_rate(sensor->extclk); + pll1_rate = extclk_rate * config->pll_mul / config->pll_pre_div_half; + + switch (config->pll_pre_div) { + case 0: + break; + case 1: + pll1_rate *= 3; + pll1_rate /= 2; + break; + case 3: + pll1_rate *= 5; + pll1_rate /= 2; + break; + case 4: + pll1_rate /= 3; + break; + case 5: + pll1_rate /= 4; + break; + case 7: + pll1_rate /= 8; + break; + default: + pll1_rate /= config->pll_pre_div; + break; + } + + return pll1_rate; +} + +static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor, + const struct ov8865_mode *mode, + u32 mbus_code) +{ + const struct ov8865_pll1_config *config = mode->pll1_config; + u8 value; + int ret; + + switch (mbus_code) { + case MEDIA_BUS_FMT_SBGGR10_1X10: + value = OV8865_MIPI_BIT_SEL(10); + break; + default: + return -EINVAL; + } + + ret = ov8865_write(sensor, OV8865_MIPI_BIT_SEL_REG, value); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRLA_REG, + OV8865_PLL_CTRLA_PRE_DIV_HALF(config->pll_pre_div_half)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRL0_REG, + OV8865_PLL_CTRL0_PRE_DIV(config->pll_pre_div)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRL1_REG, + OV8865_PLL_CTRL1_MUL_H(config->pll_mul)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRL2_REG, + OV8865_PLL_CTRL2_MUL_L(config->pll_mul)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRL3_REG, + OV8865_PLL_CTRL3_M_DIV(config->m_div)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRL4_REG, + OV8865_PLL_CTRL4_MIPI_DIV(config->mipi_div)); + if (ret) + return ret; + + ret = ov8865_update_bits(sensor, OV8865_PCLK_SEL_REG, + OV8865_PCLK_SEL_PCLK_DIV_MASK, + OV8865_PCLK_SEL_PCLK_DIV(config->pclk_div)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRL5_REG, + OV8865_PLL_CTRL5_SYS_PRE_DIV(config->sys_pre_div)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRL6_REG, + OV8865_PLL_CTRL6_SYS_DIV(config->sys_div)); + if (ret) + return ret; + + return ov8865_update_bits(sensor, OV8865_PLL_CTRL1E_REG, + OV8865_PLL_CTRL1E_PLL1_NO_LAT, + OV8865_PLL_CTRL1E_PLL1_NO_LAT); +} + +static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor, + const struct ov8865_mode *mode) +{ + const struct ov8865_pll2_config *config = mode->pll2_config; + int ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRL12_REG, + OV8865_PLL_CTRL12_PRE_DIV_HALF(config->pll_pre_div_half) | + OV8865_PLL_CTRL12_DAC_DIV(config->dac_div)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRLB_REG, + OV8865_PLL_CTRLB_PRE_DIV(config->pll_pre_div)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRLC_REG, + OV8865_PLL_CTRLC_MUL_H(config->pll_mul)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRLD_REG, + OV8865_PLL_CTRLD_MUL_L(config->pll_mul)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_PLL_CTRLF_REG, + OV8865_PLL_CTRLF_SYS_PRE_DIV(config->sys_pre_div)); + if (ret) + return ret; + + return ov8865_write(sensor, OV8865_PLL_CTRLE_REG, + OV8865_PLL_CTRLE_SYS_DIV(config->sys_div)); +} + +static int ov8865_mode_sclk_configure(struct ov8865_sensor *sensor, + const struct ov8865_mode *mode) +{ + const struct ov8865_sclk_config *config = mode->sclk_config; + int ret; + + ret = ov8865_write(sensor, OV8865_CLK_SEL0_REG, + OV8865_CLK_SEL0_PLL1_SYS_SEL(config->sys_sel)); + if (ret) + return ret; + + ret = ov8865_update_bits(sensor, OV8865_CLK_SEL1_REG, + OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK, + OV8865_CLK_SEL1_PLL_SCLK_SEL(config->sclk_sel)); + if (ret) + return ret; + + return ov8865_write(sensor, OV8865_SCLK_CTRL_REG, + OV8865_SCLK_CTRL_UNKNOWN | + OV8865_SCLK_CTRL_SCLK_DIV(config->sclk_div) | + OV8865_SCLK_CTRL_SCLK_PRE_DIV(config->sclk_pre_div)); +} + +static int ov8865_mode_binning_configure(struct ov8865_sensor *sensor, + const struct ov8865_mode *mode) +{ + unsigned int variopixel_hsub_coef, variopixel_vsub_coef; + u8 value; + int ret; + + ret = ov8865_write(sensor, OV8865_FORMAT1_REG, 0); + if (ret) + return ret; + + value = OV8865_FORMAT2_HSYNC_EN; + + if (mode->binning_x) + value |= OV8865_FORMAT2_FST_HBIN_EN; + + if (mode->binning_y) + value |= OV8865_FORMAT2_FST_VBIN_EN; + + if (mode->sync_hbin) + value |= OV8865_FORMAT2_SYNC_HBIN_EN; + + if (mode->horz_var2) + value |= OV8865_FORMAT2_ISP_HORZ_VAR2_EN; + + ret = ov8865_write(sensor, OV8865_FORMAT2_REG, value); + if (ret) + return ret; + + ret = ov8865_update_bits(sensor, OV8865_ISP_CTRL2_REG, + OV8865_ISP_CTRL2_VARIOPIXEL_EN, + mode->variopixel ? + OV8865_ISP_CTRL2_VARIOPIXEL_EN : 0); + if (ret) + return ret; + + if (mode->variopixel) { + /* VarioPixel coefs needs to be > 1. */ + variopixel_hsub_coef = mode->variopixel_hsub_coef; + variopixel_vsub_coef = mode->variopixel_vsub_coef; + } else { + variopixel_hsub_coef = 1; + variopixel_vsub_coef = 1; + } + + ret = ov8865_write(sensor, OV8865_VAP_CTRL1_REG, + OV8865_VAP_CTRL1_HSUB_COEF(variopixel_hsub_coef) | + OV8865_VAP_CTRL1_VSUB_COEF(variopixel_vsub_coef)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_INC_X_ODD_REG, + OV8865_INC_X_ODD(mode->inc_x_odd)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_INC_X_EVEN_REG, + OV8865_INC_X_EVEN(mode->inc_x_even)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_INC_Y_ODD_REG, + OV8865_INC_Y_ODD(mode->inc_y_odd)); + if (ret) + return ret; + + return ov8865_write(sensor, OV8865_INC_Y_EVEN_REG, + OV8865_INC_Y_EVEN(mode->inc_y_even)); +} + +static int ov8865_mode_black_level_configure(struct ov8865_sensor *sensor, + const struct ov8865_mode *mode) +{ + int ret; + + /* Note that a zero value for blc_col_shift_mask is the default 256. */ + ret = ov8865_write(sensor, OV8865_BLC_CTRL1_REG, + mode->blc_col_shift_mask | + OV8865_BLC_CTRL1_OFFSET_LIMIT_EN); + if (ret) + return ret; + + /* BLC top zero line */ + + ret = ov8865_write(sensor, OV8865_BLC_TOP_ZLINE_START_REG, + OV8865_BLC_TOP_ZLINE_START(mode->blc_top_zero_line_start)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_BLC_TOP_ZLINE_NUM_REG, + OV8865_BLC_TOP_ZLINE_NUM(mode->blc_top_zero_line_num)); + if (ret) + return ret; + + /* BLC top black line */ + + ret = ov8865_write(sensor, OV8865_BLC_TOP_BLKLINE_START_REG, + OV8865_BLC_TOP_BLKLINE_START(mode->blc_top_black_line_start)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_BLC_TOP_BLKLINE_NUM_REG, + OV8865_BLC_TOP_BLKLINE_NUM(mode->blc_top_black_line_num)); + if (ret) + return ret; + + /* BLC bottom zero line */ + + ret = ov8865_write(sensor, OV8865_BLC_BOT_ZLINE_START_REG, + OV8865_BLC_BOT_ZLINE_START(mode->blc_bottom_zero_line_start)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_BLC_BOT_ZLINE_NUM_REG, + OV8865_BLC_BOT_ZLINE_NUM(mode->blc_bottom_zero_line_num)); + if (ret) + return ret; + + /* BLC bottom black line */ + + ret = ov8865_write(sensor, OV8865_BLC_BOT_BLKLINE_START_REG, + OV8865_BLC_BOT_BLKLINE_START(mode->blc_bottom_black_line_start)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_BLC_BOT_BLKLINE_NUM_REG, + OV8865_BLC_BOT_BLKLINE_NUM(mode->blc_bottom_black_line_num)); + if (ret) + return ret; + + /* BLC anchor */ + + ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_START_H_REG, + OV8865_BLC_ANCHOR_LEFT_START_H(mode->blc_anchor_left_start)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_START_L_REG, + OV8865_BLC_ANCHOR_LEFT_START_L(mode->blc_anchor_left_start)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_END_H_REG, + OV8865_BLC_ANCHOR_LEFT_END_H(mode->blc_anchor_left_end)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_END_L_REG, + OV8865_BLC_ANCHOR_LEFT_END_L(mode->blc_anchor_left_end)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_START_H_REG, + OV8865_BLC_ANCHOR_RIGHT_START_H(mode->blc_anchor_right_start)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_START_L_REG, + OV8865_BLC_ANCHOR_RIGHT_START_L(mode->blc_anchor_right_start)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_END_H_REG, + OV8865_BLC_ANCHOR_RIGHT_END_H(mode->blc_anchor_right_end)); + if (ret) + return ret; + + return ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_END_L_REG, + OV8865_BLC_ANCHOR_RIGHT_END_L(mode->blc_anchor_right_end)); +} + +static int ov8865_mode_configure(struct ov8865_sensor *sensor, + const struct ov8865_mode *mode, u32 mbus_code) +{ + int ret; + + /* Output Size X */ + + ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_X_H_REG, + OV8865_OUTPUT_SIZE_X_H(mode->output_size_x)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_X_L_REG, + OV8865_OUTPUT_SIZE_X_L(mode->output_size_x)); + if (ret) + return ret; + + /* Horizontal Total Size */ + + ret = ov8865_write(sensor, OV8865_HTS_H_REG, OV8865_HTS_H(mode->hts)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_HTS_L_REG, OV8865_HTS_L(mode->hts)); + if (ret) + return ret; + + /* Output Size Y */ + + ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_Y_H_REG, + OV8865_OUTPUT_SIZE_Y_H(mode->output_size_y)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_Y_L_REG, + OV8865_OUTPUT_SIZE_Y_L(mode->output_size_y)); + if (ret) + return ret; + + /* Vertical Total Size */ + + ret = ov8865_write(sensor, OV8865_VTS_H_REG, OV8865_VTS_H(mode->vts)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_VTS_L_REG, OV8865_VTS_L(mode->vts)); + if (ret) + return ret; + + if (mode->size_auto) { + /* Auto Size */ + + ret = ov8865_write(sensor, OV8865_AUTO_SIZE_CTRL_REG, + OV8865_AUTO_SIZE_CTRL_OFFSET_Y_REG | + OV8865_AUTO_SIZE_CTRL_OFFSET_X_REG | + OV8865_AUTO_SIZE_CTRL_CROP_END_Y_REG | + OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG | + OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG | + OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_AUTO_SIZE_BOUNDARIES_REG, + OV8865_AUTO_SIZE_BOUNDARIES_Y(mode->size_auto_boundary_y) | + OV8865_AUTO_SIZE_BOUNDARIES_X(mode->size_auto_boundary_x)); + if (ret) + return ret; + } else { + /* Crop Start X */ + + ret = ov8865_write(sensor, OV8865_CROP_START_X_H_REG, + OV8865_CROP_START_X_H(mode->crop_start_x)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_CROP_START_X_L_REG, + OV8865_CROP_START_X_L(mode->crop_start_x)); + if (ret) + return ret; + + /* Offset X */ + + ret = ov8865_write(sensor, OV8865_OFFSET_X_H_REG, + OV8865_OFFSET_X_H(mode->offset_x)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_OFFSET_X_L_REG, + OV8865_OFFSET_X_L(mode->offset_x)); + if (ret) + return ret; + + /* Crop End X */ + + ret = ov8865_write(sensor, OV8865_CROP_END_X_H_REG, + OV8865_CROP_END_X_H(mode->crop_end_x)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_CROP_END_X_L_REG, + OV8865_CROP_END_X_L(mode->crop_end_x)); + if (ret) + return ret; + + /* Crop Start Y */ + + ret = ov8865_write(sensor, OV8865_CROP_START_Y_H_REG, + OV8865_CROP_START_Y_H(mode->crop_start_y)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_CROP_START_Y_L_REG, + OV8865_CROP_START_Y_L(mode->crop_start_y)); + if (ret) + return ret; + + /* Offset Y */ + + ret = ov8865_write(sensor, OV8865_OFFSET_Y_H_REG, + OV8865_OFFSET_Y_H(mode->offset_y)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_OFFSET_Y_L_REG, + OV8865_OFFSET_Y_L(mode->offset_y)); + if (ret) + return ret; + + /* Crop End Y */ + + ret = ov8865_write(sensor, OV8865_CROP_END_Y_H_REG, + OV8865_CROP_END_Y_H(mode->crop_end_y)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_CROP_END_Y_L_REG, + OV8865_CROP_END_Y_L(mode->crop_end_y)); + if (ret) + return ret; + } + + /* VFIFO */ + + ret = ov8865_write(sensor, OV8865_VFIFO_READ_START_H_REG, + OV8865_VFIFO_READ_START_H(mode->vfifo_read_start)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_VFIFO_READ_START_L_REG, + OV8865_VFIFO_READ_START_L(mode->vfifo_read_start)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_ABLC_NUM_REG, + OV8865_ABLC_NUM(mode->ablc_num)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_ZLINE_NUM_REG, + OV8865_ZLINE_NUM(mode->zline_num)); + if (ret) + return ret; + + /* Binning */ + + ret = ov8865_mode_binning_configure(sensor, mode); + if (ret) + return ret; + + /* Black Level */ + + ret = ov8865_mode_black_level_configure(sensor, mode); + if (ret) + return ret; + + /* PLLs */ + + ret = ov8865_mode_pll1_configure(sensor, mode, mbus_code); + if (ret) + return ret; + + ret = ov8865_mode_pll2_configure(sensor, mode); + if (ret) + return ret; + + ret = ov8865_mode_sclk_configure(sensor, mode); + if (ret) + return ret; + + /* Extra registers */ + + if (mode->register_values) { + ret = ov8865_write_sequence(sensor, mode->register_values, + mode->register_values_count); + if (ret) + return ret; + } + + return 0; +} + +static unsigned long ov8865_mode_mipi_clk_rate(struct ov8865_sensor *sensor, + const struct ov8865_mode *mode) +{ + const struct ov8865_pll1_config *config = mode->pll1_config; + unsigned long pll1_rate; + + pll1_rate = ov8865_mode_pll1_rate(sensor, mode); + + return pll1_rate / config->m_div / 2; +} + +/* Exposure */ + +static int ov8865_exposure_configure(struct ov8865_sensor *sensor, u32 exposure) +{ + int ret; + + ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_HH_REG, + OV8865_EXPOSURE_CTRL_HH(exposure)); + if (ret) + return ret; + + ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_H_REG, + OV8865_EXPOSURE_CTRL_H(exposure)); + if (ret) + return ret; + + return ov8865_write(sensor, OV8865_EXPOSURE_CTRL_L_REG, + OV8865_EXPOSURE_CTRL_L(exposure)); +} + +/* Gain */ + +static int ov8865_gain_configure(struct ov8865_sensor *sensor, u32 gain) +{ + int ret; + + ret = ov8865_write(sensor, OV8865_GAIN_CTRL_H_REG, + OV8865_GAIN_CTRL_H(gain)); + if (ret) + return ret; + + return ov8865_write(sensor, OV8865_GAIN_CTRL_L_REG, + OV8865_GAIN_CTRL_L(gain)); +} + +/* White Balance */ + +static int ov8865_red_balance_configure(struct ov8865_sensor *sensor, + u32 red_balance) +{ + int ret; + + ret = ov8865_write(sensor, OV8865_ISP_GAIN_RED_H_REG, + OV8865_ISP_GAIN_RED_H(red_balance)); + if (ret) + return ret; + + return ov8865_write(sensor, OV8865_ISP_GAIN_RED_L_REG, + OV8865_ISP_GAIN_RED_L(red_balance)); +} + +static int ov8865_blue_balance_configure(struct ov8865_sensor *sensor, + u32 blue_balance) +{ + int ret; + + ret = ov8865_write(sensor, OV8865_ISP_GAIN_BLUE_H_REG, + OV8865_ISP_GAIN_BLUE_H(blue_balance)); + if (ret) + return ret; + + return ov8865_write(sensor, OV8865_ISP_GAIN_BLUE_L_REG, + OV8865_ISP_GAIN_BLUE_L(blue_balance)); +} + +/* Flip */ + +static int ov8865_flip_vert_configure(struct ov8865_sensor *sensor, bool enable) +{ + u8 bits = OV8865_FORMAT1_FLIP_VERT_ISP_EN | + OV8865_FORMAT1_FLIP_VERT_SENSOR_EN; + + return ov8865_update_bits(sensor, OV8865_FORMAT1_REG, bits, + enable ? bits : 0); +} + +static int ov8865_flip_horz_configure(struct ov8865_sensor *sensor, bool enable) +{ + u8 bits = OV8865_FORMAT2_FLIP_HORZ_ISP_EN | + OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN; + + return ov8865_update_bits(sensor, OV8865_FORMAT2_REG, bits, + enable ? bits : 0); +} + +/* Test Pattern */ + +static int ov8865_test_pattern_configure(struct ov8865_sensor *sensor, + unsigned int index) +{ + if (index >= ARRAY_SIZE(ov8865_test_pattern_bits)) + return -EINVAL; + + return ov8865_write(sensor, OV8865_PRE_CTRL0_REG, + ov8865_test_pattern_bits[index]); +} + +/* State */ + +static int ov8865_state_mipi_configure(struct ov8865_sensor *sensor, + const struct ov8865_mode *mode, + u32 mbus_code) +{ + struct ov8865_ctrls *ctrls = &sensor->ctrls; + struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 = + &sensor->endpoint.bus.mipi_csi2; + unsigned long mipi_clk_rate; + unsigned int bits_per_sample; + unsigned int lanes_count; + unsigned int i, j; + s64 mipi_pixel_rate; + + mipi_clk_rate = ov8865_mode_mipi_clk_rate(sensor, mode); + if (!mipi_clk_rate) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(ov8865_link_freq_menu); i++) { + s64 freq = ov8865_link_freq_menu[i]; + + if (freq == mipi_clk_rate) + break; + } + + for (j = 0; j < sensor->endpoint.nr_of_link_frequencies; j++) { + u64 freq = sensor->endpoint.link_frequencies[j]; + + if (freq == mipi_clk_rate) + break; + } + + if (i == ARRAY_SIZE(ov8865_link_freq_menu)) { + dev_err(sensor->dev, + "failed to find %lu clk rate in link freq\n", + mipi_clk_rate); + } else if (j == sensor->endpoint.nr_of_link_frequencies) { + dev_err(sensor->dev, + "failed to find %lu clk rate in endpoint link-frequencies\n", + mipi_clk_rate); + } else { + __v4l2_ctrl_s_ctrl(ctrls->link_freq, i); + } + + switch (mbus_code) { + case MEDIA_BUS_FMT_SBGGR10_1X10: + bits_per_sample = 10; + break; + default: + return -EINVAL; + } + + lanes_count = bus_mipi_csi2->num_data_lanes; + mipi_pixel_rate = mipi_clk_rate * 2 * lanes_count / bits_per_sample; + + __v4l2_ctrl_s_ctrl_int64(ctrls->pixel_rate, mipi_pixel_rate); + + return 0; +} + +static int ov8865_state_configure(struct ov8865_sensor *sensor, + const struct ov8865_mode *mode, + u32 mbus_code) +{ + int ret; + + if (sensor->state.streaming) + return -EBUSY; + + /* State will be configured at first power on otherwise. */ + if (pm_runtime_enabled(sensor->dev) && + !pm_runtime_suspended(sensor->dev)) { + ret = ov8865_mode_configure(sensor, mode, mbus_code); + if (ret) + return ret; + } + + ret = ov8865_state_mipi_configure(sensor, mode, mbus_code); + if (ret) + return ret; + + sensor->state.mode = mode; + sensor->state.mbus_code = mbus_code; + + return 0; +} + +static int ov8865_state_init(struct ov8865_sensor *sensor) +{ + return ov8865_state_configure(sensor, &ov8865_modes[0], + ov8865_mbus_codes[0]); +} + +/* Sensor Base */ + +static int ov8865_sensor_init(struct ov8865_sensor *sensor) +{ + int ret; + + ret = ov8865_sw_reset(sensor); + if (ret) { + dev_err(sensor->dev, "failed to perform sw reset\n"); + return ret; + } + + ret = ov8865_sw_standby(sensor, 1); + if (ret) { + dev_err(sensor->dev, "failed to set sensor standby\n"); + return ret; + } + + ret = ov8865_chip_id_check(sensor); + if (ret) { + dev_err(sensor->dev, "failed to check sensor chip id\n"); + return ret; + } + + ret = ov8865_write_sequence(sensor, ov8865_init_sequence, + ARRAY_SIZE(ov8865_init_sequence)); + if (ret) { + dev_err(sensor->dev, "failed to write init sequence\n"); + return ret; + } + + ret = ov8865_charge_pump_configure(sensor); + if (ret) { + dev_err(sensor->dev, "failed to configure pad\n"); + return ret; + } + + ret = ov8865_mipi_configure(sensor); + if (ret) { + dev_err(sensor->dev, "failed to configure MIPI\n"); + return ret; + } + + ret = ov8865_isp_configure(sensor); + if (ret) { + dev_err(sensor->dev, "failed to configure ISP\n"); + return ret; + } + + ret = ov8865_black_level_configure(sensor); + if (ret) { + dev_err(sensor->dev, "failed to configure black level\n"); + return ret; + } + + /* Configure current mode. */ + ret = ov8865_state_configure(sensor, sensor->state.mode, + sensor->state.mbus_code); + if (ret) { + dev_err(sensor->dev, "failed to configure state\n"); + return ret; + } + + return 0; +} + +static int ov8865_sensor_power(struct ov8865_sensor *sensor, bool on) +{ + /* Keep initialized to zero for disable label. */ + int ret = 0; + + if (on) { + gpiod_set_value_cansleep(sensor->reset, 1); + gpiod_set_value_cansleep(sensor->powerdown, 1); + + ret = regulator_enable(sensor->dovdd); + if (ret) { + dev_err(sensor->dev, + "failed to enable DOVDD regulator\n"); + goto disable; + } + + ret = regulator_enable(sensor->avdd); + if (ret) { + dev_err(sensor->dev, + "failed to enable AVDD regulator\n"); + goto disable; + } + + ret = regulator_enable(sensor->dvdd); + if (ret) { + dev_err(sensor->dev, + "failed to enable DVDD regulator\n"); + goto disable; + } + + ret = clk_prepare_enable(sensor->extclk); + if (ret) { + dev_err(sensor->dev, "failed to enable EXTCLK clock\n"); + goto disable; + } + + gpiod_set_value_cansleep(sensor->reset, 0); + gpiod_set_value_cansleep(sensor->powerdown, 0); + + /* Time to enter streaming mode according to power timings. */ + usleep_range(10000, 12000); + } else { +disable: + gpiod_set_value_cansleep(sensor->powerdown, 1); + gpiod_set_value_cansleep(sensor->reset, 1); + + clk_disable_unprepare(sensor->extclk); + + regulator_disable(sensor->dvdd); + regulator_disable(sensor->avdd); + regulator_disable(sensor->dovdd); + } + + return ret; +} + +/* Controls */ + +static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *subdev = ov8865_ctrl_subdev(ctrl); + struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); + unsigned int index; + int ret; + + /* Wait for the sensor to be on before setting controls. */ + if (pm_runtime_suspended(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = ov8865_exposure_configure(sensor, ctrl->val); + if (ret) + return ret; + break; + case V4L2_CID_GAIN: + ret = ov8865_gain_configure(sensor, ctrl->val); + if (ret) + return ret; + break; + case V4L2_CID_RED_BALANCE: + return ov8865_red_balance_configure(sensor, ctrl->val); + case V4L2_CID_BLUE_BALANCE: + return ov8865_blue_balance_configure(sensor, ctrl->val); + case V4L2_CID_HFLIP: + return ov8865_flip_horz_configure(sensor, !!ctrl->val); + case V4L2_CID_VFLIP: + return ov8865_flip_vert_configure(sensor, !!ctrl->val); + case V4L2_CID_TEST_PATTERN: + index = (unsigned int)ctrl->val; + return ov8865_test_pattern_configure(sensor, index); + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops ov8865_ctrl_ops = { + .s_ctrl = ov8865_s_ctrl, +}; + +static int ov8865_ctrls_init(struct ov8865_sensor *sensor) +{ + struct ov8865_ctrls *ctrls = &sensor->ctrls; + struct v4l2_ctrl_handler *handler = &ctrls->handler; + const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops; + int ret; + + v4l2_ctrl_handler_init(handler, 32); + + /* Use our mutex for ctrl locking. */ + handler->lock = &sensor->mutex; + + /* Exposure */ + + v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 16, 1048575, 16, + 512); + + /* Gain */ + + v4l2_ctrl_new_std(handler, ops, V4L2_CID_GAIN, 128, 8191, 128, 128); + + /* White Balance */ + + v4l2_ctrl_new_std(handler, ops, V4L2_CID_RED_BALANCE, 1, 32767, 1, + 1024); + + v4l2_ctrl_new_std(handler, ops, V4L2_CID_BLUE_BALANCE, 1, 32767, 1, + 1024); + + /* Flip */ + + v4l2_ctrl_new_std(handler, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + + /* Test Pattern */ + + v4l2_ctrl_new_std_menu_items(handler, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov8865_test_pattern_menu) - 1, + 0, 0, ov8865_test_pattern_menu); + + /* MIPI CSI-2 */ + + ctrls->link_freq = + v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(ov8865_link_freq_menu) - 1, + 0, ov8865_link_freq_menu); + + ctrls->pixel_rate = + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 1, + INT_MAX, 1, 1); + + if (handler->error) { + ret = handler->error; + goto error_ctrls; + } + + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + sensor->subdev.ctrl_handler = handler; + + return 0; + +error_ctrls: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +/* Subdev Video Operations */ + +static int ov8865_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); + struct ov8865_state *state = &sensor->state; + int ret; + + if (enable) { + ret = pm_runtime_get_sync(sensor->dev); + if (ret < 0) { + pm_runtime_put_noidle(sensor->dev); + return ret; + } + } + + mutex_lock(&sensor->mutex); + ret = ov8865_sw_standby(sensor, !enable); + mutex_unlock(&sensor->mutex); + + if (ret) + return ret; + + state->streaming = !!enable; + + if (!enable) + pm_runtime_put(sensor->dev); + + return 0; +} + +static int ov8865_g_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *interval) +{ + struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); + const struct ov8865_mode *mode; + int ret = 0; + + mutex_lock(&sensor->mutex); + + mode = sensor->state.mode; + interval->interval = mode->frame_interval; + + mutex_unlock(&sensor->mutex); + + return ret; +} + +static const struct v4l2_subdev_video_ops ov8865_subdev_video_ops = { + .s_stream = ov8865_s_stream, + .g_frame_interval = ov8865_g_frame_interval, + .s_frame_interval = ov8865_g_frame_interval, +}; + +/* Subdev Pad Operations */ + +static int ov8865_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_mbus_code_enum *code_enum) +{ + if (code_enum->index >= ARRAY_SIZE(ov8865_mbus_codes)) + return -EINVAL; + + code_enum->code = ov8865_mbus_codes[code_enum->index]; + + return 0; +} + +static void ov8865_mbus_format_fill(struct v4l2_mbus_framefmt *mbus_format, + u32 mbus_code, + const struct ov8865_mode *mode) +{ + mbus_format->width = mode->output_size_x; + mbus_format->height = mode->output_size_y; + mbus_format->code = mbus_code; + + mbus_format->field = V4L2_FIELD_NONE; + mbus_format->colorspace = V4L2_COLORSPACE_RAW; + mbus_format->ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(mbus_format->colorspace); + mbus_format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + mbus_format->xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(mbus_format->colorspace); +} + +static int ov8865_get_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_format *format) +{ + struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + + mutex_lock(&sensor->mutex); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *mbus_format = *v4l2_subdev_get_try_format(subdev, config, + format->pad); + else + ov8865_mbus_format_fill(mbus_format, sensor->state.mbus_code, + sensor->state.mode); + + mutex_unlock(&sensor->mutex); + + return 0; +} + +static int ov8865_set_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_format *format) +{ + struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + const struct ov8865_mode *mode; + u32 mbus_code = 0; + unsigned int index; + int ret = 0; + + mutex_lock(&sensor->mutex); + + if (sensor->state.streaming) { + ret = -EBUSY; + goto complete; + } + + /* Try to find requested mbus code. */ + for (index = 0; index < ARRAY_SIZE(ov8865_mbus_codes); index++) { + if (ov8865_mbus_codes[index] == mbus_format->code) { + mbus_code = mbus_format->code; + break; + } + } + + /* Fallback to default. */ + if (!mbus_code) + mbus_code = ov8865_mbus_codes[0]; + + /* Find the mode with nearest dimensions. */ + mode = v4l2_find_nearest_size(ov8865_modes, ARRAY_SIZE(ov8865_modes), + output_size_x, output_size_y, + mbus_format->width, mbus_format->height); + if (!mode) { + ret = -EINVAL; + goto complete; + } + + ov8865_mbus_format_fill(mbus_format, mbus_code, mode); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_get_try_format(subdev, config, format->pad) = + *mbus_format; + else if (sensor->state.mode != mode || + sensor->state.mbus_code != mbus_code) + ret = ov8865_state_configure(sensor, mode, mbus_code); + +complete: + mutex_unlock(&sensor->mutex); + + return ret; +} + +static int ov8865_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_frame_size_enum *size_enum) +{ + const struct ov8865_mode *mode; + + if (size_enum->index >= ARRAY_SIZE(ov8865_modes)) + return -EINVAL; + + mode = &ov8865_modes[size_enum->index]; + + size_enum->min_width = size_enum->max_width = mode->output_size_x; + size_enum->min_height = size_enum->max_height = mode->output_size_y; + + return 0; +} + +static int ov8865_enum_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *config, + struct v4l2_subdev_frame_interval_enum *interval_enum) +{ + const struct ov8865_mode *mode = NULL; + unsigned int mode_index; + unsigned int interval_index; + + if (interval_enum->index > 0) + return -EINVAL; + /* + * Multiple modes with the same dimensions may have different frame + * intervals, so look up each relevant mode. + */ + for (mode_index = 0, interval_index = 0; + mode_index < ARRAY_SIZE(ov8865_modes); mode_index++) { + mode = &ov8865_modes[mode_index]; + + if (mode->output_size_x == interval_enum->width && + mode->output_size_y == interval_enum->height) { + if (interval_index == interval_enum->index) + break; + + interval_index++; + } + } + + if (mode_index == ARRAY_SIZE(ov8865_modes) || !mode) + return -EINVAL; + + interval_enum->interval = mode->frame_interval; + + return 0; +} + +static const struct v4l2_subdev_pad_ops ov8865_subdev_pad_ops = { + .enum_mbus_code = ov8865_enum_mbus_code, + .get_fmt = ov8865_get_fmt, + .set_fmt = ov8865_set_fmt, + .enum_frame_size = ov8865_enum_frame_size, + .enum_frame_interval = ov8865_enum_frame_interval, +}; + +static const struct v4l2_subdev_ops ov8865_subdev_ops = { + .video = &ov8865_subdev_video_ops, + .pad = &ov8865_subdev_pad_ops, +}; + +static int ov8865_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); + struct ov8865_state *state = &sensor->state; + int ret = 0; + + mutex_lock(&sensor->mutex); + + if (state->streaming) { + ret = ov8865_sw_standby(sensor, true); + if (ret) + goto complete; + } + + ret = ov8865_sensor_power(sensor, false); + if (ret) + ov8865_sw_standby(sensor, false); + +complete: + mutex_unlock(&sensor->mutex); + + return ret; +} + +static int ov8865_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); + struct ov8865_state *state = &sensor->state; + int ret = 0; + + mutex_lock(&sensor->mutex); + + ret = ov8865_sensor_power(sensor, true); + if (ret) + goto complete; + + ret = ov8865_sensor_init(sensor); + if (ret) + goto error_power; + + ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); + if (ret) + goto error_power; + + if (state->streaming) { + ret = ov8865_sw_standby(sensor, false); + if (ret) + goto error_power; + } + + goto complete; + +error_power: + ov8865_sensor_power(sensor, false); + +complete: + mutex_unlock(&sensor->mutex); + + return ret; +} + +static int ov8865_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct fwnode_handle *handle; + struct ov8865_sensor *sensor; + struct v4l2_subdev *subdev; + struct media_pad *pad; + unsigned long rate; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->dev = dev; + sensor->i2c_client = client; + + /* Graph Endpoint */ + + handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!handle) { + dev_err(dev, "unable to find enpoint node\n"); + return -EINVAL; + } + + sensor->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY; + + ret = v4l2_fwnode_endpoint_alloc_parse(handle, &sensor->endpoint); + fwnode_handle_put(handle); + if (ret) { + dev_err(dev, "failed to parse endpoint node\n"); + return ret; + } + + /* GPIOs */ + + sensor->powerdown = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->powerdown)) { + ret = PTR_ERR(sensor->powerdown); + goto error_endpoint; + } + + sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset)) { + ret = PTR_ERR(sensor->reset); + goto error_endpoint; + } + + /* Regulators */ + + /* DVDD: digital core */ + sensor->dvdd = devm_regulator_get(dev, "dvdd"); + if (IS_ERR(sensor->dvdd)) { + dev_err(dev, "cannot get DVDD (digital core) regulator\n"); + ret = PTR_ERR(sensor->dvdd); + goto error_endpoint; + } + + /* DOVDD: digital I/O */ + sensor->dovdd = devm_regulator_get(dev, "dovdd"); + if (IS_ERR(sensor->dvdd)) { + dev_err(dev, "cannot get DOVDD (digital I/O) regulator\n"); + ret = PTR_ERR(sensor->dvdd); + goto error_endpoint; + } + + /* AVDD: analog */ + sensor->avdd = devm_regulator_get(dev, "avdd"); + if (IS_ERR(sensor->avdd)) { + dev_err(dev, "cannot get AVDD (analog) regulator\n"); + ret = PTR_ERR(sensor->dvdd); + goto error_endpoint; + } + + /* External Clock */ + + sensor->extclk = devm_clk_get(dev, NULL); + if (IS_ERR(sensor->extclk)) { + dev_err(dev, "failed to get external clock\n"); + ret = PTR_ERR(sensor->extclk); + goto error_endpoint; + } + + rate = clk_get_rate(sensor->extclk); + if (rate != OV8865_EXTCLK_RATE) { + dev_err(dev, "clock rate %lu Hz is unsupported\n", rate); + ret = -EINVAL; + goto error_endpoint; + } + + /* Subdev, entity and pad */ + + subdev = &sensor->subdev; + v4l2_i2c_subdev_init(subdev, client, &ov8865_subdev_ops); + + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->entity.function = MEDIA_ENT_F_CAM_SENSOR; + + pad = &sensor->pad; + pad->flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&subdev->entity, 1, pad); + if (ret) + goto error_entity; + + /* Mutex */ + + mutex_init(&sensor->mutex); + + /* Sensor */ + + ret = ov8865_ctrls_init(sensor); + if (ret) + goto error_mutex; + + ret = ov8865_state_init(sensor); + if (ret) + goto error_ctrls; + + /* Runtime PM */ + + pm_runtime_enable(sensor->dev); + pm_runtime_set_suspended(sensor->dev); + + /* V4L2 subdev register */ + + ret = v4l2_async_register_subdev_sensor_common(subdev); + if (ret) + goto error_pm; + + return 0; + +error_pm: + pm_runtime_disable(sensor->dev); + +error_ctrls: + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + +error_mutex: + mutex_destroy(&sensor->mutex); + +error_entity: + media_entity_cleanup(&sensor->subdev.entity); + +error_endpoint: + v4l2_fwnode_endpoint_free(&sensor->endpoint); + + return ret; +} + +static int ov8865_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); + + v4l2_async_unregister_subdev(subdev); + pm_runtime_disable(sensor->dev); + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + mutex_destroy(&sensor->mutex); + media_entity_cleanup(&subdev->entity); + + v4l2_fwnode_endpoint_free(&sensor->endpoint); + + return 0; +} + +static const struct dev_pm_ops ov8865_pm_ops = { + SET_RUNTIME_PM_OPS(ov8865_suspend, ov8865_resume, NULL) +}; + +static const struct of_device_id ov8865_of_match[] = { + { .compatible = "ovti,ov8865" }, + { } +}; +MODULE_DEVICE_TABLE(of, ov8865_of_match); + +static struct i2c_driver ov8865_driver = { + .driver = { + .name = "ov8865", + .of_match_table = ov8865_of_match, + .pm = &ov8865_pm_ops, + }, + .probe_new = ov8865_probe, + .remove = ov8865_remove, +}; + +module_i2c_driver(ov8865_driver); + +MODULE_AUTHOR("Paul Kocialkowski "); +MODULE_DESCRIPTION("V4L2 driver for the OmniVision OV8865 image sensor"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8fe0267dc9685385325e28e0e9167446c71f7153 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sat, 2 Jan 2021 14:29:39 +0100 Subject: media: mt9v111: Remove unneeded device-managed puts Drivers don't need to explicitly call devm_{}_put on driver removal, as it's automatically called by the device driver resource management code. Fixes: aab7ed1c3927 ("media: i2c: Add driver for Aptina MT9V111") Signed-off-by: Ezequiel Garcia Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9v111.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index 61ae6a0d5679..97c7527b74ed 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -1253,12 +1253,6 @@ static int mt9v111_remove(struct i2c_client *client) mutex_destroy(&mt9v111->pwr_mutex); mutex_destroy(&mt9v111->stream_mutex); - devm_gpiod_put(mt9v111->dev, mt9v111->oe); - devm_gpiod_put(mt9v111->dev, mt9v111->standby); - devm_gpiod_put(mt9v111->dev, mt9v111->reset); - - devm_clk_put(mt9v111->dev, mt9v111->clk); - return 0; } -- cgit v1.2.3 From 7eb5a7e1e7b6cf7153beeba608f5a73d1e912b77 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 23 Sep 2020 16:26:33 +0200 Subject: media: ccs: Add digital gain support CCS supports global (all-component) digital gain. Add support for it. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index b39ae5f8446b..f1fecc72e247 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -670,6 +670,11 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_ANALOGUE_GAIN: rval = ccs_write(sensor, ANALOG_GAIN_CODE_GLOBAL, ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + rval = ccs_write(sensor, DIGITAL_GAIN_GLOBAL, ctrl->val); + break; case V4L2_CID_EXPOSURE: rval = ccs_write(sensor, COARSE_INTEGRATION_TIME, ctrl->val); @@ -739,7 +744,7 @@ static int ccs_init_controls(struct ccs_sensor *sensor) struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; - rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 12); + rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 13); if (rval) return rval; @@ -753,6 +758,16 @@ static int ccs_init_controls(struct ccs_sensor *sensor) max(CCS_LIM(sensor, ANALOG_GAIN_CODE_STEP), 1U), CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN)); + if (CCS_LIM(sensor, DIGITAL_GAIN_CAPABILITY) == + CCS_DIGITAL_GAIN_CAPABILITY_GLOBAL) + v4l2_ctrl_new_std(&sensor->pixel_array->ctrl_handler, + &ccs_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + CCS_LIM(sensor, DIGITAL_GAIN_MIN), + CCS_LIM(sensor, DIGITAL_GAIN_MAX), + max(CCS_LIM(sensor, DIGITAL_GAIN_STEP_SIZE), + 1U), + 0x100); + /* Exposure limits will be updated soon, use just something here. */ sensor->exposure = v4l2_ctrl_new_std( &sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, -- cgit v1.2.3 From 821878578975bae1949dbfcef3e39c2968a969c8 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 23 Sep 2020 16:38:24 +0200 Subject: media: ccs: Add support for old-style SMIA digital gain SMIA only has per-component digital gain. Add support for it. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 33 +++++++++++++++++++++++++++++++-- drivers/media/i2c/ccs/smiapp-reg-defs.h | 2 ++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index f1fecc72e247..4b765ac62c0c 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -673,7 +673,34 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_DIGITAL_GAIN: - rval = ccs_write(sensor, DIGITAL_GAIN_GLOBAL, ctrl->val); + if (CCS_LIM(sensor, DIGITAL_GAIN_CAPABILITY) == + CCS_DIGITAL_GAIN_CAPABILITY_GLOBAL) { + rval = ccs_write(sensor, DIGITAL_GAIN_GLOBAL, + ctrl->val); + break; + } + + rval = ccs_write_addr(sensor, + SMIAPP_REG_U16_DIGITAL_GAIN_GREENR, + ctrl->val); + if (rval) + break; + + rval = ccs_write_addr(sensor, + SMIAPP_REG_U16_DIGITAL_GAIN_RED, + ctrl->val); + if (rval) + break; + + rval = ccs_write_addr(sensor, + SMIAPP_REG_U16_DIGITAL_GAIN_BLUE, + ctrl->val); + if (rval) + break; + + rval = ccs_write_addr(sensor, + SMIAPP_REG_U16_DIGITAL_GAIN_GREENB, + ctrl->val); break; case V4L2_CID_EXPOSURE: @@ -759,7 +786,9 @@ static int ccs_init_controls(struct ccs_sensor *sensor) CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN)); if (CCS_LIM(sensor, DIGITAL_GAIN_CAPABILITY) == - CCS_DIGITAL_GAIN_CAPABILITY_GLOBAL) + CCS_DIGITAL_GAIN_CAPABILITY_GLOBAL || + CCS_LIM(sensor, DIGITAL_GAIN_CAPABILITY) == + SMIAPP_DIGITAL_GAIN_CAPABILITY_PER_CHANNEL) v4l2_ctrl_new_std(&sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, V4L2_CID_DIGITAL_GAIN, CCS_LIM(sensor, DIGITAL_GAIN_MIN), diff --git a/drivers/media/i2c/ccs/smiapp-reg-defs.h b/drivers/media/i2c/ccs/smiapp-reg-defs.h index e80c110ebf3a..177e3e51207a 100644 --- a/drivers/media/i2c/ccs/smiapp-reg-defs.h +++ b/drivers/media/i2c/ccs/smiapp-reg-defs.h @@ -535,6 +535,8 @@ #define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0 #define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1 +#define SMIAPP_DIGITAL_GAIN_CAPABILITY_PER_CHANNEL 1 + #define SMIAPP_BINNING_CAPABILITY_NO 0 #define SMIAPP_BINNING_CAPABILITY_YES 1 -- cgit v1.2.3 From d36eb68a49948a7fb29c43bd5745692ede4b6d52 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 24 Sep 2020 00:40:45 +0200 Subject: media: ccs: Remove analogue gain field The analogue gain control was stored to the device specific struct but was never used. Remove it. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 13 ++++++------- drivers/media/i2c/ccs/ccs.h | 1 - 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 4b765ac62c0c..706fa811d9b5 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -777,13 +777,12 @@ static int ccs_init_controls(struct ccs_sensor *sensor) sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; - sensor->analog_gain = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, - V4L2_CID_ANALOGUE_GAIN, - CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN), - CCS_LIM(sensor, ANALOG_GAIN_CODE_MAX), - max(CCS_LIM(sensor, ANALOG_GAIN_CODE_STEP), 1U), - CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN)); + v4l2_ctrl_new_std(&sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN), + CCS_LIM(sensor, ANALOG_GAIN_CODE_MAX), + max(CCS_LIM(sensor, ANALOG_GAIN_CODE_STEP), 1U), + CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN)); if (CCS_LIM(sensor, DIGITAL_GAIN_CAPABILITY) == CCS_DIGITAL_GAIN_CAPABILITY_GLOBAL || diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index 356b87c33405..9fc3333f6c4e 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -262,7 +262,6 @@ struct ccs_sensor { unsigned long *valid_link_freqs; /* Pixel array controls */ - struct v4l2_ctrl *analog_gain; struct v4l2_ctrl *exposure; struct v4l2_ctrl *hflip; struct v4l2_ctrl *vflip; -- cgit v1.2.3 From 541374837cd911785ae9307b4902896f2e3b8f15 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 24 Sep 2020 00:43:55 +0200 Subject: media: ccs: Only add analogue gain control if the device supports it Some devices do not implement analogue gain this way. Only add the control when a device does have the support. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 706fa811d9b5..47879f9bfe20 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -777,12 +777,16 @@ static int ccs_init_controls(struct ccs_sensor *sensor) sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; - v4l2_ctrl_new_std(&sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, - V4L2_CID_ANALOGUE_GAIN, - CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN), - CCS_LIM(sensor, ANALOG_GAIN_CODE_MAX), - max(CCS_LIM(sensor, ANALOG_GAIN_CODE_STEP), 1U), - CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN)); + switch (CCS_LIM(sensor, ANALOG_GAIN_CAPABILITY)) { + case CCS_ANALOG_GAIN_CAPABILITY_GLOBAL: + v4l2_ctrl_new_std(&sensor->pixel_array->ctrl_handler, + &ccs_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN), + CCS_LIM(sensor, ANALOG_GAIN_CODE_MAX), + max(CCS_LIM(sensor, ANALOG_GAIN_CODE_STEP), + 1U), + CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN)); + } if (CCS_LIM(sensor, DIGITAL_GAIN_CAPABILITY) == CCS_DIGITAL_GAIN_CAPABILITY_GLOBAL || -- cgit v1.2.3 From cd9f145dabafec8ad001756f2abe60c7c2ec3cc1 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 24 Sep 2020 21:44:13 +0200 Subject: media: v4l: uapi: Add user control base for CCS controls Add a control base for CCS controls, and reserve 128 controls. Luckily these numbers are cheap. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/v4l2-controls.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 823b214aac0c..9ead7a8386c8 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -204,6 +204,11 @@ enum v4l2_colorfx { * We reserve 16 controls for this driver. */ #define V4L2_CID_USER_CODA_BASE (V4L2_CID_USER_BASE + 0x10e0) +/* + * The base for MIPI CCS driver controls. + * We reserve 128 controls for this driver. + */ +#define V4L2_CID_USER_CCS_BASE (V4L2_CID_USER_BASE + 0x10f0) /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls -- cgit v1.2.3 From db08f69ef8205dc4bcc2af2a24bcfc8f9da47899 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 13 Nov 2020 17:12:28 +0100 Subject: media: Documentation: ccs: Add user documentation for the CCS driver Add user documentation for the CCS driver. This includes e.g. sub-devices implemented by the driver. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/drivers/ccs.rst | 59 ++++++++++++++++++++++ .../userspace-api/media/drivers/index.rst | 1 + MAINTAINERS | 1 + 3 files changed, 61 insertions(+) create mode 100644 Documentation/userspace-api/media/drivers/ccs.rst diff --git a/Documentation/userspace-api/media/drivers/ccs.rst b/Documentation/userspace-api/media/drivers/ccs.rst new file mode 100644 index 000000000000..00bb3a6288f5 --- /dev/null +++ b/Documentation/userspace-api/media/drivers/ccs.rst @@ -0,0 +1,59 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +.. include:: + +MIPI CCS camera sensor driver +============================= + +The MIPI CCS camera sensor driver is a generic driver for `MIPI CCS +`_ compliant +camera sensors. It exposes three sub-devices representing the pixel array, +the binner and the scaler. + +As the capabilities of individual devices vary, the driver exposes +interfaces based on the capabilities that exist in hardware. + +Pixel Array sub-device +---------------------- + +The pixel array sub-device represents the camera sensor's pixel matrix, as well +as analogue crop functionality present in many compliant devices. The analogue +crop is configured using the ``V4L2_SEL_TGT_CROP`` on the source pad (0) of the +entity. The size of the pixel matrix can be obtained by getting the +``V4L2_SEL_TGT_NATIVE_SIZE`` target. + +Binner +------ + +The binner sub-device represents the binning functionality on the sensor. For +that purpose, selection target ``V4L2_SEL_TGT_COMPOSE`` is supported on the +sink pad (0). + +Additionally, if a device has no scaler or digital crop functionality, the +source pad (1) expses another digital crop selection rectangle that can only +crop at the end of the lines and frames. + +Scaler +------ + +The scaler sub-device represents the digital crop and scaling functionality of +the sensor. The V4L2 selection target ``V4L2_SEL_TGT_CROP`` is used to +configure the digital crop on the sink pad (0) when digital crop is supported. +Scaling is configured using selection target ``V4L2_SEL_TGT_COMPOSE`` on the +sink pad (0) as well. + +Additionally, if the scaler sub-device exists, its source pad (1) exposes +another digital crop selection rectangle that can only crop at the end of the +lines and frames. + +Digital and analogue crop +------------------------- + +Digital crop functionality is referred to as cropping that effectively works by +dropping some data on the floor. Analogue crop, on the other hand, means that +the cropped information is never retrieved. In case of camera sensors, the +analogue data is never read from the pixel matrix that are outside the +configured selection rectangle that designates crop. The difference has an +effect in device timing and likely also in power consumption. + +**Copyright** |copy| 2020 Intel Corporation diff --git a/Documentation/userspace-api/media/drivers/index.rst b/Documentation/userspace-api/media/drivers/index.rst index 05a82f8c0c99..1a9038f5f9fa 100644 --- a/Documentation/userspace-api/media/drivers/index.rst +++ b/Documentation/userspace-api/media/drivers/index.rst @@ -31,6 +31,7 @@ For more details see the file COPYING in the source distribution of Linux. :maxdepth: 5 :numbered: + ccs cx2341x-uapi imx-uapi max2175 diff --git a/MAINTAINERS b/MAINTAINERS index be80b8142ac8..906ef1373285 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11836,6 +11836,7 @@ L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml F: Documentation/driver-api/media/drivers/ccs/ +F: Documentation/userspace-api/media/drivers/ccs.rst F: drivers/media/i2c/ccs-pll.c F: drivers/media/i2c/ccs-pll.h F: drivers/media/i2c/ccs/ -- cgit v1.2.3 From a8a2d75b0897fc359ada5fb9f8b62f985351882b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 24 Sep 2020 00:21:32 +0200 Subject: media: v4l: uapi: ccs: Add controls for analogue gain constants Add V4L2 controls for analogue gain constants required to control analogue gain. The values are device specific and thus need to be obtained from the driver. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/drivers/ccs.rst | 25 +++++++++++++++++++++++ MAINTAINERS | 1 + include/uapi/linux/ccs.h | 14 +++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 include/uapi/linux/ccs.h diff --git a/Documentation/userspace-api/media/drivers/ccs.rst b/Documentation/userspace-api/media/drivers/ccs.rst index 00bb3a6288f5..a95d7941533d 100644 --- a/Documentation/userspace-api/media/drivers/ccs.rst +++ b/Documentation/userspace-api/media/drivers/ccs.rst @@ -56,4 +56,29 @@ analogue data is never read from the pixel matrix that are outside the configured selection rectangle that designates crop. The difference has an effect in device timing and likely also in power consumption. +Private controls +---------------- + +The MIPI CCS driver implements a number of private controls under +``V4L2_CID_USER_BASE_CCS`` to control the MIPI CCS compliant camera sensors. + +Analogue gain model +~~~~~~~~~~~~~~~~~~~ + +The CCS defines an analogue gain model where the gain can be calculated using +the following formula: + + gain = m0 * x + c0 / (m1 * x + c1) + +Either m0 or c0 will be zero. The constants that are device specific, can be +obtained from the following controls: + + V4L2_CID_CCS_ANALOGUE_GAIN_M0 + V4L2_CID_CCS_ANALOGUE_GAIN_M1 + V4L2_CID_CCS_ANALOGUE_GAIN_C0 + V4L2_CID_CCS_ANALOGUE_GAIN_C1 + +The analogue gain (``x`` in the formula) is controlled through +``V4L2_CID_ANALOGUE_GAIN`` in this case. + **Copyright** |copy| 2020 Intel Corporation diff --git a/MAINTAINERS b/MAINTAINERS index 906ef1373285..7faa7ae50716 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11840,6 +11840,7 @@ F: Documentation/userspace-api/media/drivers/ccs.rst F: drivers/media/i2c/ccs-pll.c F: drivers/media/i2c/ccs-pll.h F: drivers/media/i2c/ccs/ +F: include/uapi/linux/ccs.h F: include/uapi/linux/smiapp.h MIPS diff --git a/include/uapi/linux/ccs.h b/include/uapi/linux/ccs.h new file mode 100644 index 000000000000..57515ed7aaab --- /dev/null +++ b/include/uapi/linux/ccs.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* Copyright (C) 2020 Intel Corporation */ + +#ifndef __UAPI_CCS_H__ +#define __UAPI_CCS_H__ + +#include + +#define V4L2_CID_CCS_ANALOGUE_GAIN_M0 (V4L2_CID_USER_CCS_BASE + 1) +#define V4L2_CID_CCS_ANALOGUE_GAIN_C0 (V4L2_CID_USER_CCS_BASE + 2) +#define V4L2_CID_CCS_ANALOGUE_GAIN_M1 (V4L2_CID_USER_CCS_BASE + 3) +#define V4L2_CID_CCS_ANALOGUE_GAIN_C1 (V4L2_CID_USER_CCS_BASE + 4) + +#endif -- cgit v1.2.3 From ee25e211ade18a655953115d642e4c04b17748ed Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 24 Sep 2020 00:38:07 +0200 Subject: media: ccs: Add support for analogue gain coefficient controls Add four controls for reading CCS analogue gain coefficients. The values are constants that are device specific. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 47879f9bfe20..c51197318c3a 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "ccs.h" @@ -771,14 +772,46 @@ static int ccs_init_controls(struct ccs_sensor *sensor) struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; - rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 13); + rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 17); if (rval) return rval; sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; switch (CCS_LIM(sensor, ANALOG_GAIN_CAPABILITY)) { - case CCS_ANALOG_GAIN_CAPABILITY_GLOBAL: + case CCS_ANALOG_GAIN_CAPABILITY_GLOBAL: { + struct { + const char *name; + u32 id; + s32 value; + } const gain_ctrls[] = { + { "Analogue Gain m0", V4L2_CID_CCS_ANALOGUE_GAIN_M0, + CCS_LIM(sensor, ANALOG_GAIN_M0), }, + { "Analogue Gain c0", V4L2_CID_CCS_ANALOGUE_GAIN_C0, + CCS_LIM(sensor, ANALOG_GAIN_C0), }, + { "Analogue Gain m1", V4L2_CID_CCS_ANALOGUE_GAIN_M1, + CCS_LIM(sensor, ANALOG_GAIN_M1), }, + { "Analogue Gain c1", V4L2_CID_CCS_ANALOGUE_GAIN_C1, + CCS_LIM(sensor, ANALOG_GAIN_C1), }, + }; + struct v4l2_ctrl_config ctrl_cfg = { + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &ccs_ctrl_ops, + .flags = V4L2_CTRL_FLAG_READ_ONLY, + .step = 1, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gain_ctrls); i++) { + ctrl_cfg.name = gain_ctrls[i].name; + ctrl_cfg.id = gain_ctrls[i].id; + ctrl_cfg.min = ctrl_cfg.max = ctrl_cfg.def = + gain_ctrls[i].value; + + v4l2_ctrl_new_custom(&sensor->pixel_array->ctrl_handler, + &ctrl_cfg, NULL); + } + v4l2_ctrl_new_std(&sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN), @@ -787,6 +820,7 @@ static int ccs_init_controls(struct ccs_sensor *sensor) 1U), CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN)); } + } if (CCS_LIM(sensor, DIGITAL_GAIN_CAPABILITY) == CCS_DIGITAL_GAIN_CAPABILITY_GLOBAL || -- cgit v1.2.3 From a75210a62b81ce8cfbe9e10e29880d870ce94b8d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 24 Sep 2020 21:57:52 +0200 Subject: media: v4l: uapi: ccs: Add controls for CCS alternative analogue gain Add two new controls for alternative analogue gain some CCS compliant camera sensors support. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/drivers/ccs.rst | 13 +++++++++++++ include/uapi/linux/ccs.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/Documentation/userspace-api/media/drivers/ccs.rst b/Documentation/userspace-api/media/drivers/ccs.rst index a95d7941533d..ea09c8139c23 100644 --- a/Documentation/userspace-api/media/drivers/ccs.rst +++ b/Documentation/userspace-api/media/drivers/ccs.rst @@ -81,4 +81,17 @@ obtained from the following controls: The analogue gain (``x`` in the formula) is controlled through ``V4L2_CID_ANALOGUE_GAIN`` in this case. +Alternate analogue gain model +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The CCS defines another analogue gain model called alternate analogue gain. In +this case, the formula to calculate actual gain consists of linear and +exponential parts: + + gain = linear * 2 ^ exponent + +The ``linear`` and ``exponent`` factors can be set using the +``V4L2_CID_CCS_ANALOGUE_LINEAR_GAIN`` and +``V4L2_CID_CCS_ANALOGUE_EXPONENTIAL_GAIN`` controls, respectively + **Copyright** |copy| 2020 Intel Corporation diff --git a/include/uapi/linux/ccs.h b/include/uapi/linux/ccs.h index 57515ed7aaab..9ddec24c1401 100644 --- a/include/uapi/linux/ccs.h +++ b/include/uapi/linux/ccs.h @@ -10,5 +10,7 @@ #define V4L2_CID_CCS_ANALOGUE_GAIN_C0 (V4L2_CID_USER_CCS_BASE + 2) #define V4L2_CID_CCS_ANALOGUE_GAIN_M1 (V4L2_CID_USER_CCS_BASE + 3) #define V4L2_CID_CCS_ANALOGUE_GAIN_C1 (V4L2_CID_USER_CCS_BASE + 4) +#define V4L2_CID_CCS_ANALOGUE_LINEAR_GAIN (V4L2_CID_USER_CCS_BASE + 5) +#define V4L2_CID_CCS_ANALOGUE_EXPONENTIAL_GAIN (V4L2_CID_USER_CCS_BASE + 6) #endif -- cgit v1.2.3 From 57801b6aa72bee123503fc4630ef4da1b88fca00 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 24 Sep 2020 00:55:00 +0200 Subject: media: ccs: Add support for alternate analogue global gain The CCS spec defines an alternative implementation for global analogue gain. Add support for that in the driver. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index c51197318c3a..7591a52a41a4 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -673,6 +673,17 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) break; + case V4L2_CID_CCS_ANALOGUE_LINEAR_GAIN: + rval = ccs_write(sensor, ANALOG_LINEAR_GAIN_GLOBAL, ctrl->val); + + break; + + case V4L2_CID_CCS_ANALOGUE_EXPONENTIAL_GAIN: + rval = ccs_write(sensor, ANALOG_EXPONENTIAL_GAIN_GLOBAL, + ctrl->val); + + break; + case V4L2_CID_DIGITAL_GAIN: if (CCS_LIM(sensor, DIGITAL_GAIN_CAPABILITY) == CCS_DIGITAL_GAIN_CAPABILITY_GLOBAL) { @@ -819,6 +830,50 @@ static int ccs_init_controls(struct ccs_sensor *sensor) max(CCS_LIM(sensor, ANALOG_GAIN_CODE_STEP), 1U), CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN)); + } + break; + + case CCS_ANALOG_GAIN_CAPABILITY_ALTERNATE_GLOBAL: { + struct { + const char *name; + u32 id; + u16 min, max, step; + } const gain_ctrls[] = { + { + "Analogue Linear Gain", + V4L2_CID_CCS_ANALOGUE_LINEAR_GAIN, + CCS_LIM(sensor, ANALOG_LINEAR_GAIN_MIN), + CCS_LIM(sensor, ANALOG_LINEAR_GAIN_MAX), + max(CCS_LIM(sensor, + ANALOG_LINEAR_GAIN_STEP_SIZE), + 1U), + }, + { + "Analogue Exponential Gain", + V4L2_CID_CCS_ANALOGUE_EXPONENTIAL_GAIN, + CCS_LIM(sensor, ANALOG_EXPONENTIAL_GAIN_MIN), + CCS_LIM(sensor, ANALOG_EXPONENTIAL_GAIN_MAX), + max(CCS_LIM(sensor, + ANALOG_EXPONENTIAL_GAIN_STEP_SIZE), + 1U), + }, + }; + struct v4l2_ctrl_config ctrl_cfg = { + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &ccs_ctrl_ops, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gain_ctrls); i++) { + ctrl_cfg.name = gain_ctrls[i].name; + ctrl_cfg.min = ctrl_cfg.def = gain_ctrls[i].min; + ctrl_cfg.max = gain_ctrls[i].max; + ctrl_cfg.step = gain_ctrls[i].step; + ctrl_cfg.id = gain_ctrls[i].id; + + v4l2_ctrl_new_custom(&sensor->pixel_array->ctrl_handler, + &ctrl_cfg, NULL); + } } } -- cgit v1.2.3 From 7a42609843d2f09de842b825bfefa9fed40e3e7a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 24 Sep 2020 23:34:27 +0200 Subject: media: ccs: Add debug prints for MSR registers Also print out MSR registers written to the sensor. This isn't entirely optimal as the debug strings are produced even if they're not used but that isn't really a grave issue --- the I²C bus is very slow anyway. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-reg-access.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c index b776af2a3c33..5f0705952198 100644 --- a/drivers/media/i2c/ccs/ccs-reg-access.c +++ b/drivers/media/i2c/ccs/ccs-reg-access.c @@ -387,12 +387,20 @@ int ccs_write_data_regs(struct ccs_sensor *sensor, struct ccs_reg *regs, for (j = 0; j < regs->len; j += msg.len - 2, regdata += msg.len - 2) { + char printbuf[(MAX_WRITE_LEN << 1) + + 1 /* \0 */] = { 0 }; int rval; msg.len = min(regs->len - j, MAX_WRITE_LEN); + bin2hex(printbuf, regdata, msg.len); + dev_dbg(&client->dev, + "writing msr reg 0x%4.4x value 0x%s\n", + regs->addr + j, printbuf); + put_unaligned_be16(regs->addr + j, buf); memcpy(buf + 2, regdata, msg.len); + msg.len += 2; rval = ccs_write_retry(client, &msg); -- cgit v1.2.3 From 7c0ed600f04d247cbb4c4030d87a8d36b12d44a4 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 25 Sep 2020 10:28:56 +0200 Subject: media: v4l: uapi: ccs: Add CCS controls for shading correction Add V4L2 controls for controlling CCS lens shading correction as well as conveying its capabilities. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/drivers/ccs.rst | 13 +++++++++++++ include/uapi/linux/ccs.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/Documentation/userspace-api/media/drivers/ccs.rst b/Documentation/userspace-api/media/drivers/ccs.rst index ea09c8139c23..161cb65f4d98 100644 --- a/Documentation/userspace-api/media/drivers/ccs.rst +++ b/Documentation/userspace-api/media/drivers/ccs.rst @@ -94,4 +94,17 @@ The ``linear`` and ``exponent`` factors can be set using the ``V4L2_CID_CCS_ANALOGUE_LINEAR_GAIN`` and ``V4L2_CID_CCS_ANALOGUE_EXPONENTIAL_GAIN`` controls, respectively +Shading correction +~~~~~~~~~~~~~~~~~~ + +The CCS standard supports lens shading correction. The feature can be controlled +using ``V4L2_CID_CCS_SHADING_CORRECTION``. Additionally, the luminance +correction level may be changed using +``V4L2_CID_CCS_LUMINANCE_CORRECTION_LEVEL``, where value 0 indicates no +correction and 128 indicates correcting the luminance in corners to 10 % less +than in the centre. + +Shading correction needs to be enabled for luminance correction level to have an +effect. + **Copyright** |copy| 2020 Intel Corporation diff --git a/include/uapi/linux/ccs.h b/include/uapi/linux/ccs.h index 9ddec24c1401..2896d3bfdb5e 100644 --- a/include/uapi/linux/ccs.h +++ b/include/uapi/linux/ccs.h @@ -12,5 +12,7 @@ #define V4L2_CID_CCS_ANALOGUE_GAIN_C1 (V4L2_CID_USER_CCS_BASE + 4) #define V4L2_CID_CCS_ANALOGUE_LINEAR_GAIN (V4L2_CID_USER_CCS_BASE + 5) #define V4L2_CID_CCS_ANALOGUE_EXPONENTIAL_GAIN (V4L2_CID_USER_CCS_BASE + 6) +#define V4L2_CID_CCS_SHADING_CORRECTION (V4L2_CID_USER_CCS_BASE + 8) +#define V4L2_CID_CCS_LUMINANCE_CORRECTION_LEVEL (V4L2_CID_USER_CCS_BASE + 9) #endif -- cgit v1.2.3 From 33039a8880710199007621be38dc8f498a0cd5ca Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 25 Sep 2020 10:30:32 +0200 Subject: media: ccs: Add shading correction and luminance correction level controls Add controls for supporting lens shading correction, including colour shading and luminance correction level. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 46 ++++++++++++++++++++++++++++++++++++++++ drivers/media/i2c/ccs/ccs.h | 1 + 2 files changed, 47 insertions(+) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 7591a52a41a4..12c30fb0f37a 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -756,6 +756,19 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_TEST_PATTERN_GREENB: rval = ccs_write(sensor, TEST_DATA_GREENB, ctrl->val); + break; + case V4L2_CID_CCS_SHADING_CORRECTION: + rval = ccs_write(sensor, SHADING_CORRECTION_EN, + ctrl->val ? CCS_SHADING_CORRECTION_EN_ENABLE : + 0); + + if (!rval && sensor->luminance_level) + v4l2_ctrl_activate(sensor->luminance_level, ctrl->val); + + break; + case V4L2_CID_CCS_LUMINANCE_CORRECTION_LEVEL: + rval = ccs_write(sensor, LUMINANCE_CORRECTION_LEVEL, ctrl->val); + break; case V4L2_CID_PIXEL_RATE: /* For v4l2_ctrl_s_ctrl_int64() used internally. */ @@ -877,6 +890,39 @@ static int ccs_init_controls(struct ccs_sensor *sensor) } } + if (CCS_LIM(sensor, SHADING_CORRECTION_CAPABILITY) & + (CCS_SHADING_CORRECTION_CAPABILITY_COLOR_SHADING | + CCS_SHADING_CORRECTION_CAPABILITY_LUMINANCE_CORRECTION)) { + const struct v4l2_ctrl_config ctrl_cfg = { + .name = "Shading Correction", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .id = V4L2_CID_CCS_SHADING_CORRECTION, + .ops = &ccs_ctrl_ops, + .max = 1, + .step = 1, + }; + + v4l2_ctrl_new_custom(&sensor->pixel_array->ctrl_handler, + &ctrl_cfg, NULL); + } + + if (CCS_LIM(sensor, SHADING_CORRECTION_CAPABILITY) & + CCS_SHADING_CORRECTION_CAPABILITY_LUMINANCE_CORRECTION) { + const struct v4l2_ctrl_config ctrl_cfg = { + .name = "Luminance Correction Level", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .id = V4L2_CID_CCS_LUMINANCE_CORRECTION_LEVEL, + .ops = &ccs_ctrl_ops, + .max = 255, + .step = 1, + .def = 128, + }; + + sensor->luminance_level = + v4l2_ctrl_new_custom(&sensor->pixel_array->ctrl_handler, + &ctrl_cfg, NULL); + } + if (CCS_LIM(sensor, DIGITAL_GAIN_CAPABILITY) == CCS_DIGITAL_GAIN_CAPABILITY_GLOBAL || CCS_LIM(sensor, DIGITAL_GAIN_CAPABILITY) == diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index 9fc3333f6c4e..cc33c9ba3165 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -268,6 +268,7 @@ struct ccs_sensor { struct v4l2_ctrl *vblank; struct v4l2_ctrl *hblank; struct v4l2_ctrl *pixel_rate_parray; + struct v4l2_ctrl *luminance_level; /* src controls */ struct v4l2_ctrl *link_freq; struct v4l2_ctrl *pixel_rate_csi; -- cgit v1.2.3 From d27be0ad943b22d491d1f00e2edbb93a87d58376 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 7 Oct 2020 13:09:19 +0200 Subject: media: ccs: Get the endpoint by port rather than any next endpoint Get the first endpoint from port 0 instead of the next one, whatever it might be. There are no other ports so there's no functional change. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 12c30fb0f37a..11c6de7f55aa 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -3090,7 +3090,8 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev) int i; int rval; - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); if (!ep) return -ENODEV; -- cgit v1.2.3 From 105676ce5940517d2a8a4be32f54c9ff8a021ca5 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 8 Oct 2020 10:56:48 +0200 Subject: media: ccs: Don't change the I²C address just for software reset The sensor's address was changed before resetting and changing the address again. Don't do it before reset as it's useless. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 11c6de7f55aa..da7a6bd2c820 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -1552,14 +1552,6 @@ static int ccs_power_on(struct device *dev) * is found. */ - if (sensor->hwcfg.i2c_addr_alt) { - rval = ccs_change_cci_addr(sensor); - if (rval) { - dev_err(dev, "cci address change error\n"); - goto out_cci_addr_fail; - } - } - rval = ccs_write(sensor, SOFTWARE_RESET, CCS_SOFTWARE_RESET_ON); if (rval < 0) { dev_err(dev, "software reset failed\n"); -- cgit v1.2.3 From e1988e7ad0015e9d8252b515d5aa23a58428022c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 8 Oct 2020 10:59:26 +0200 Subject: media: ccs: Only do software reset if we have no hardware reset The driver always used software reset after the sensor's power sequence that includes a hardware reset if we have a reset GPIO. Do not use software reset if we just brought the sensor up from hardware reset state. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index da7a6bd2c820..fdf2e83eeac3 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -1552,10 +1552,12 @@ static int ccs_power_on(struct device *dev) * is found. */ - rval = ccs_write(sensor, SOFTWARE_RESET, CCS_SOFTWARE_RESET_ON); - if (rval < 0) { - dev_err(dev, "software reset failed\n"); - goto out_cci_addr_fail; + if (!sensor->reset && !sensor->xshutdown) { + rval = ccs_write(sensor, SOFTWARE_RESET, CCS_SOFTWARE_RESET_ON); + if (rval < 0) { + dev_err(dev, "software reset failed\n"); + goto out_cci_addr_fail; + } } if (sensor->hwcfg.i2c_addr_alt) { -- cgit v1.2.3 From 51fc72e541b44d5ba52673b1e2b492ffc164b71c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 8 Oct 2020 12:09:07 +0200 Subject: media: ccs: Wait until software reset is done Verify the software reset has been completed until proceeding. The spec does not guarantee a delay but presumably 100 ms should be enough. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index fdf2e83eeac3..e1b3c5693e01 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -1553,11 +1553,26 @@ static int ccs_power_on(struct device *dev) */ if (!sensor->reset && !sensor->xshutdown) { + u8 retry = 100; + u32 reset; + rval = ccs_write(sensor, SOFTWARE_RESET, CCS_SOFTWARE_RESET_ON); if (rval < 0) { dev_err(dev, "software reset failed\n"); goto out_cci_addr_fail; } + + do { + rval = ccs_read(sensor, SOFTWARE_RESET, &reset); + reset = !rval && reset == CCS_SOFTWARE_RESET_OFF; + if (reset) + break; + + usleep_range(1000, 2000); + } while (--retry); + + if (!reset) + return -EIO; } if (sensor->hwcfg.i2c_addr_alt) { -- cgit v1.2.3 From 2fed6c84dc6fb9e374466d8a0ba86eb6ca7868f8 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 8 Oct 2020 18:24:39 +0200 Subject: media: ccs: Hardware requires a delay after starting the clock of lifting reset A CCS compliant device requires a delay before the first I²C transaction after pulling xshutdown up or starting the external clock. This is what the driver does. However, if neither is actually there, there's no need for the delay. This also has the effect of removing an unnecessary delay on ACPI systems where ACPI is responsible for the power-up sequence. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index e1b3c5693e01..fae8ceded861 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -1515,7 +1515,6 @@ static int ccs_power_on(struct device *dev) struct ccs_sensor *sensor = container_of(ssd, struct ccs_sensor, ssds[0]); const struct ccs_device *ccsdev = device_get_match_data(dev); - unsigned int sleep; int rval; rval = regulator_bulk_enable(ARRAY_SIZE(ccs_regulators), @@ -1525,21 +1524,25 @@ static int ccs_power_on(struct device *dev) return rval; } - rval = clk_prepare_enable(sensor->ext_clk); - if (rval < 0) { - dev_dbg(dev, "failed to enable xclk\n"); - goto out_xclk_fail; - } + if (sensor->reset || sensor->xshutdown || sensor->ext_clk) { + unsigned int sleep; + + rval = clk_prepare_enable(sensor->ext_clk); + if (rval < 0) { + dev_dbg(dev, "failed to enable xclk\n"); + goto out_xclk_fail; + } - gpiod_set_value(sensor->reset, 0); - gpiod_set_value(sensor->xshutdown, 1); + gpiod_set_value(sensor->reset, 0); + gpiod_set_value(sensor->xshutdown, 1); - if (ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA) - sleep = SMIAPP_RESET_DELAY(sensor->hwcfg.ext_clk); - else - sleep = 5000; + if (ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA) + sleep = SMIAPP_RESET_DELAY(sensor->hwcfg.ext_clk); + else + sleep = 5000; - usleep_range(sleep, sleep); + usleep_range(sleep, sleep); + } /* * Failures to respond to the address change command have been noticed. -- cgit v1.2.3 From 2f23ecbf40c8c6572017a036505695e1c2ebb7c4 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 8 Oct 2020 18:43:56 +0200 Subject: media: ccs: Add a sanity check for external clock frequency The driver depends on the external clock frequency. Add a sanity check for the frequency, by returning an error from probe if it's zero. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index fae8ceded861..08fce285f73a 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -3275,6 +3275,11 @@ static int ccs_probe(struct i2c_client *client) return -EINVAL; } + if (!sensor->hwcfg.ext_clk) { + dev_err(&client->dev, "cannot work with xclk frequency 0\n"); + return -EINVAL; + } + sensor->reset = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(sensor->reset)) -- cgit v1.2.3 From 9c3d7e5549ebc507c8ec1c516fc163a94d27976d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 9 Oct 2020 12:57:06 +0200 Subject: media: ccs: Support and default to auto PHY control CCS supports three variants of PHY timing control, auto, UI based and manual. The driver previously assumed UI based control that requires updating the link rate to the sensor. Now default to automatic control instead, and only write the link rate to the sensor in UI mode. If neither auto or UI control is supported, return an error in probe. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 54 ++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 08fce285f73a..a8f591c95bc2 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -383,15 +383,22 @@ static int ccs_pll_configure(struct ccs_sensor *sensor) if (rval < 0) return rval; - /* Lane op clock ratio does not apply here. */ - rval = ccs_write(sensor, REQUESTED_LINK_RATE, - DIV_ROUND_UP(pll->op_bk.sys_clk_freq_hz, - 1000000 / 256 / 256) * - (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ? - sensor->pll.csi2.lanes : 1) << - (pll->flags & CCS_PLL_FLAG_OP_SYS_DDR ? 1 : 0)); - if (rval < 0 || sensor->pll.flags & CCS_PLL_FLAG_NO_OP_CLOCKS) - return rval; + if (!(CCS_LIM(sensor, PHY_CTRL_CAPABILITY) & + CCS_PHY_CTRL_CAPABILITY_AUTO_PHY_CTL)) { + /* Lane op clock ratio does not apply here. */ + rval = ccs_write(sensor, REQUESTED_LINK_RATE, + DIV_ROUND_UP(pll->op_bk.sys_clk_freq_hz, + 1000000 / 256 / 256) * + (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ? + sensor->pll.csi2.lanes : 1) << + (pll->flags & CCS_PLL_FLAG_OP_SYS_DDR ? + 1 : 0)); + if (rval < 0) + return rval; + } + + if (sensor->pll.flags & CCS_PLL_FLAG_NO_OP_CLOCKS) + return 0; rval = ccs_write(sensor, OP_PIX_CLK_DIV, pll->op_bk.pix_clk_div); if (rval < 0) @@ -1504,6 +1511,28 @@ static int ccs_write_msr_regs(struct ccs_sensor *sensor) sensor->mdata.num_module_manufacturer_regs); } +static int ccs_update_phy_ctrl(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u8 val; + + if (!sensor->ccs_limits) + return 0; + + if (CCS_LIM(sensor, PHY_CTRL_CAPABILITY) & + CCS_PHY_CTRL_CAPABILITY_AUTO_PHY_CTL) { + val = CCS_PHY_CTRL_AUTO; + } else if (CCS_LIM(sensor, PHY_CTRL_CAPABILITY) & + CCS_PHY_CTRL_CAPABILITY_UI_PHY_CTL) { + val = CCS_PHY_CTRL_UI; + } else { + dev_err(&client->dev, "manual PHY control not supported\n"); + return -EINVAL; + } + + return ccs_write(sensor, PHY_CTRL, val); +} + static int ccs_power_on(struct device *dev) { struct v4l2_subdev *subdev = dev_get_drvdata(dev); @@ -1620,8 +1649,7 @@ static int ccs_power_on(struct device *dev) goto out_cci_addr_fail; } - /* DPHY control done by sensor based on requested link rate */ - rval = ccs_write(sensor, PHY_CTRL, CCS_PHY_CTRL_UI); + rval = ccs_update_phy_ctrl(sensor); if (rval < 0) goto out_cci_addr_fail; @@ -3348,6 +3376,10 @@ static int ccs_probe(struct i2c_client *client) goto out_free_ccs_limits; } + rval = ccs_update_phy_ctrl(sensor); + if (rval < 0) + goto out_free_ccs_limits; + /* * Handle Sensor Module orientation on the board. * -- cgit v1.2.3 From 81499d338995fa288464c585f49211cfaa6ffdce Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 16 Nov 2020 18:25:12 +0100 Subject: media: Documentation: Include CCS PLL calculator to CCS driver documentation Include existing CCS PLL calculator kerneldoc documentation to the documentation build. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/media/drivers/ccs/ccs.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/driver-api/media/drivers/ccs/ccs.rst b/Documentation/driver-api/media/drivers/ccs/ccs.rst index f49e971f2d92..b461c8aa2a16 100644 --- a/Documentation/driver-api/media/drivers/ccs/ccs.rst +++ b/Documentation/driver-api/media/drivers/ccs/ccs.rst @@ -79,4 +79,17 @@ definitions: -l drivers/media/i2c/ccs/ccs-limits.c \ -c Documentation/driver-api/media/drivers/ccs/ccs-regs.asc +CCS PLL calculator +================== + +The CCS PLL calculator is used to compute the PLL configuration, given sensor's +capabilities as well as board configuration and user specified configuration. As +the configuration space that encompasses all these configurations is vast, the +PLL calculator isn't entirely trivial. Yet it is relatively simple to use for a +driver. + +The PLL model implemented by the PLL calculator corresponds to MIPI CCS 1.1. + +.. kernel-doc:: drivers/media/i2c/ccs-pll.h + **Copyright** |copy| 2020 Intel Corporation -- cgit v1.2.3 From 8a75e8dcd2ef409e2bc7bf3141549cd636b4696b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 26 Nov 2020 21:01:54 +0100 Subject: media: ccs-pll: Switch from standard integer types to kernel ones The preferred integer types in the kernel are the Linux specific ones, switch from standard C types to u32 and alike. The patch has been produced with the following Coccinelle spatch, with few alignment adjustments: @@ typedef uint32_t; typedef u32; @@ - uint32_t + u32 @@ typedef uint16_t; typedef u16; @@ - uint16_t + u16 @@ typedef uint8_t; typedef u8; @@ - uint8_t + u8 Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs-pll.c | 114 ++++++++++++++++++++++---------------------- drivers/media/i2c/ccs-pll.h | 86 ++++++++++++++++----------------- 2 files changed, 100 insertions(+), 100 deletions(-) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index eb7b6f01f623..530085693a56 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -17,20 +17,20 @@ #include "ccs-pll.h" /* Return an even number or one. */ -static inline uint32_t clk_div_even(uint32_t a) +static inline u32 clk_div_even(u32 a) { - return max_t(uint32_t, 1, a & ~1); + return max_t(u32, 1, a & ~1); } /* Return an even number or one. */ -static inline uint32_t clk_div_even_up(uint32_t a) +static inline u32 clk_div_even_up(u32 a) { if (a == 1) return 1; return (a + 1) & ~1; } -static inline uint32_t is_one_or_even(uint32_t a) +static inline u32 is_one_or_even(u32 a) { if (a == 1) return 1; @@ -40,13 +40,13 @@ static inline uint32_t is_one_or_even(uint32_t a) return 1; } -static inline uint32_t one_or_more(uint32_t a) +static inline u32 one_or_more(u32 a) { return a ?: 1; } -static int bounds_check(struct device *dev, uint32_t val, - uint32_t min, uint32_t max, const char *prefix, +static int bounds_check(struct device *dev, u32 val, + u32 min, u32 max, const char *prefix, char *str) { if (val >= min && val <= max) @@ -138,12 +138,12 @@ static void print_pll(struct device *dev, struct ccs_pll *pll) pll->flags & PLL_FL(OP_PIX_DDR) ? " op-pix-ddr" : ""); } -static uint32_t op_sys_ddr(uint32_t flags) +static u32 op_sys_ddr(u32 flags) { return flags & CCS_PLL_FLAG_OP_SYS_DDR ? 1 : 0; } -static uint32_t op_pix_ddr(uint32_t flags) +static u32 op_pix_ddr(u32 flags) { return flags & CCS_PLL_FLAG_OP_PIX_DDR ? 1 : 0; } @@ -250,8 +250,8 @@ static int check_ext_bounds(struct device *dev, struct ccs_pll *pll) static void ccs_pll_find_vt_sys_div(struct device *dev, const struct ccs_pll_limits *lim, struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr, - uint16_t min_vt_div, uint16_t max_vt_div, - uint16_t *min_sys_div, uint16_t *max_sys_div) + u16 min_vt_div, u16 max_vt_div, + u16 *min_sys_div, u16 *max_sys_div) { /* * Find limits for sys_clk_div. Not all values are possible with all @@ -259,11 +259,11 @@ ccs_pll_find_vt_sys_div(struct device *dev, const struct ccs_pll_limits *lim, */ *min_sys_div = lim->vt_bk.min_sys_clk_div; dev_dbg(dev, "min_sys_div: %u\n", *min_sys_div); - *min_sys_div = max_t(uint16_t, *min_sys_div, + *min_sys_div = max_t(u16, *min_sys_div, DIV_ROUND_UP(min_vt_div, lim->vt_bk.max_pix_clk_div)); dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", *min_sys_div); - *min_sys_div = max_t(uint16_t, *min_sys_div, + *min_sys_div = max_t(u16, *min_sys_div, pll_fr->pll_op_clk_freq_hz / lim->vt_bk.max_sys_clk_freq_hz); dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", *min_sys_div); @@ -272,11 +272,11 @@ ccs_pll_find_vt_sys_div(struct device *dev, const struct ccs_pll_limits *lim, *max_sys_div = lim->vt_bk.max_sys_clk_div; dev_dbg(dev, "max_sys_div: %u\n", *max_sys_div); - *max_sys_div = min_t(uint16_t, *max_sys_div, + *max_sys_div = min_t(u16, *max_sys_div, DIV_ROUND_UP(max_vt_div, lim->vt_bk.min_pix_clk_div)); dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", *max_sys_div); - *max_sys_div = min_t(uint16_t, *max_sys_div, + *max_sys_div = min_t(u16, *max_sys_div, DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz, lim->vt_bk.min_pix_clk_freq_hz)); dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", *max_sys_div); @@ -289,15 +289,15 @@ ccs_pll_find_vt_sys_div(struct device *dev, const struct ccs_pll_limits *lim, static inline int __ccs_pll_calculate_vt_tree(struct device *dev, const struct ccs_pll_limits *lim, - struct ccs_pll *pll, uint32_t mul, uint32_t div) + struct ccs_pll *pll, u32 mul, u32 div) { const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr; const struct ccs_pll_branch_limits_bk *lim_bk = &lim->vt_bk; struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr; struct ccs_pll_branch_bk *pll_bk = &pll->vt_bk; - uint32_t more_mul; - uint16_t best_pix_div = SHRT_MAX >> 1, best_div; - uint16_t vt_div, min_sys_div, max_sys_div, sys_div; + u32 more_mul; + u16 best_pix_div = SHRT_MAX >> 1, best_div; + u16 vt_div, min_sys_div, max_sys_div, sys_div; pll_fr->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz / pll_fr->pre_pll_clk_div; @@ -331,7 +331,7 @@ __ccs_pll_calculate_vt_tree(struct device *dev, for (sys_div = min_sys_div; sys_div <= max_sys_div; sys_div += 2 - (sys_div & 1)) { - uint16_t pix_div; + u16 pix_div; if (vt_div % sys_div) continue; @@ -379,9 +379,9 @@ static int ccs_pll_calculate_vt_tree(struct device *dev, { const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr; struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr; - uint16_t min_pre_pll_clk_div = lim_fr->min_pre_pll_clk_div; - uint16_t max_pre_pll_clk_div = lim_fr->max_pre_pll_clk_div; - uint32_t pre_mul, pre_div; + u16 min_pre_pll_clk_div = lim_fr->min_pre_pll_clk_div; + u16 max_pre_pll_clk_div = lim_fr->max_pre_pll_clk_div; + u32 pre_mul, pre_div; pre_div = gcd(pll->pixel_rate_csi, pll->ext_clk_freq_hz * pll->vt_lanes); @@ -390,11 +390,11 @@ static int ccs_pll_calculate_vt_tree(struct device *dev, /* Make sure PLL input frequency is within limits */ max_pre_pll_clk_div = - min_t(uint16_t, max_pre_pll_clk_div, + min_t(u16, max_pre_pll_clk_div, DIV_ROUND_UP(pll->ext_clk_freq_hz, lim_fr->min_pll_ip_clk_freq_hz)); - min_pre_pll_clk_div = max_t(uint16_t, min_pre_pll_clk_div, + min_pre_pll_clk_div = max_t(u16, min_pre_pll_clk_div, pll->ext_clk_freq_hz / lim_fr->max_pll_ip_clk_freq_hz); @@ -406,7 +406,7 @@ static int ccs_pll_calculate_vt_tree(struct device *dev, pll_fr->pre_pll_clk_div += (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 : 2 - (pll_fr->pre_pll_clk_div & 1)) { - uint32_t mul, div; + u32 mul, div; int rval; div = gcd(pre_mul * pll_fr->pre_pll_clk_div, pre_div); @@ -440,13 +440,13 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim, const struct ccs_pll_branch_limits_bk *op_lim_bk, struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr, struct ccs_pll_branch_bk *op_pll_bk, bool cphy, - uint32_t phy_const) + u32 phy_const) { - uint16_t sys_div; - uint16_t best_pix_div = SHRT_MAX >> 1; - uint16_t vt_op_binning_div; - uint16_t min_vt_div, max_vt_div, vt_div; - uint16_t min_sys_div, max_sys_div; + u16 sys_div; + u16 best_pix_div = SHRT_MAX >> 1; + u16 vt_op_binning_div; + u16 min_vt_div, max_vt_div, vt_div; + u16 min_sys_div, max_sys_div; if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) goto out_calc_pixel_rate; @@ -500,18 +500,18 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim, /* Find smallest and biggest allowed vt divisor. */ dev_dbg(dev, "min_vt_div: %u\n", min_vt_div); - min_vt_div = max_t(uint16_t, min_vt_div, + min_vt_div = max_t(u16, min_vt_div, DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz, lim->vt_bk.max_pix_clk_freq_hz)); dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n", min_vt_div); - min_vt_div = max_t(uint16_t, min_vt_div, lim->vt_bk.min_pix_clk_div - * lim->vt_bk.min_sys_clk_div); + min_vt_div = max_t(u16, min_vt_div, lim->vt_bk.min_pix_clk_div + * lim->vt_bk.min_sys_clk_div); dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div); max_vt_div = lim->vt_bk.max_sys_clk_div * lim->vt_bk.max_pix_clk_div; dev_dbg(dev, "max_vt_div: %u\n", max_vt_div); - max_vt_div = min_t(uint16_t, max_vt_div, + max_vt_div = min_t(u16, max_vt_div, DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz, lim->vt_bk.min_pix_clk_freq_hz)); dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n", @@ -526,12 +526,12 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim, * divisor. */ for (vt_div = min_vt_div; vt_div <= max_vt_div; vt_div++) { - uint16_t __max_sys_div = vt_div & 1 ? 1 : max_sys_div; + u16 __max_sys_div = vt_div & 1 ? 1 : max_sys_div; for (sys_div = min_sys_div; sys_div <= __max_sys_div; sys_div += 2 - (sys_div & 1)) { - uint16_t pix_div; - uint16_t rounded_div; + u16 pix_div; + u16 rounded_div; pix_div = DIV_ROUND_UP(vt_div, sys_div); @@ -588,9 +588,9 @@ ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim, const struct ccs_pll_branch_limits_fr *op_lim_fr, const struct ccs_pll_branch_limits_bk *op_lim_bk, struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr, - struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul, - uint32_t div, uint32_t op_sys_clk_freq_hz_sdr, uint32_t l, - bool cphy, uint32_t phy_const) + struct ccs_pll_branch_bk *op_pll_bk, u32 mul, + u32 div, u32 op_sys_clk_freq_hz_sdr, u32 l, + bool cphy, u32 phy_const) { /* * Higher multipliers (and divisors) are often required than @@ -598,9 +598,9 @@ ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim, * There are limits for all values in the clock tree. These * are the minimum and maximum multiplier for mul. */ - uint32_t more_mul_min, more_mul_max; - uint32_t more_mul_factor; - uint32_t i; + u32 more_mul_min, more_mul_max; + u32 more_mul_factor; + u32 i; /* * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be @@ -614,7 +614,7 @@ ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim, more_mul_max); /* Don't go above max pll op frequency. */ more_mul_max = - min_t(uint32_t, + min_t(u32, more_mul_max, op_lim_fr->max_pll_op_clk_freq_hz / (pll->ext_clk_freq_hz / @@ -706,14 +706,14 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, struct ccs_pll_branch_fr *op_pll_fr; struct ccs_pll_branch_bk *op_pll_bk; bool cphy = pll->bus_type == CCS_PLL_BUS_TYPE_CSI2_CPHY; - uint32_t phy_const = cphy ? CPHY_CONST : DPHY_CONST; - uint32_t op_sys_clk_freq_hz_sdr; - uint16_t min_op_pre_pll_clk_div; - uint16_t max_op_pre_pll_clk_div; - uint32_t mul, div; - uint32_t l = (!pll->op_bits_per_lane || - pll->op_bits_per_lane >= pll->bits_per_pixel) ? 1 : 2; - uint32_t i; + u32 phy_const = cphy ? CPHY_CONST : DPHY_CONST; + u32 op_sys_clk_freq_hz_sdr; + u16 min_op_pre_pll_clk_div; + u16 max_op_pre_pll_clk_div; + u32 mul, div; + u32 l = (!pll->op_bits_per_lane || + pll->op_bits_per_lane >= pll->bits_per_pixel) ? 1 : 2; + u32 i; int rval = -EINVAL; if (!(pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)) { @@ -797,11 +797,11 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n", op_lim_fr->min_pre_pll_clk_div, op_lim_fr->max_pre_pll_clk_div); max_op_pre_pll_clk_div = - min_t(uint16_t, op_lim_fr->max_pre_pll_clk_div, + min_t(u16, op_lim_fr->max_pre_pll_clk_div, clk_div_even(pll->ext_clk_freq_hz / op_lim_fr->min_pll_ip_clk_freq_hz)); min_op_pre_pll_clk_div = - max_t(uint16_t, op_lim_fr->min_pre_pll_clk_div, + max_t(u16, op_lim_fr->min_pre_pll_clk_div, clk_div_even_up( DIV_ROUND_UP(pll->ext_clk_freq_hz, op_lim_fr->max_pll_ip_clk_freq_hz))); @@ -815,7 +815,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, dev_dbg(dev, "mul %u / div %u\n", mul, div); min_op_pre_pll_clk_div = - max_t(uint16_t, min_op_pre_pll_clk_div, + max_t(u16, min_op_pre_pll_clk_div, clk_div_even_up( mul / one_or_more( diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h index b97d7ff50ea5..6eb1b1c68e1e 100644 --- a/drivers/media/i2c/ccs-pll.h +++ b/drivers/media/i2c/ccs-pll.h @@ -44,10 +44,10 @@ * @pll_op_clk_freq_hz: PLL output clock frequency */ struct ccs_pll_branch_fr { - uint16_t pre_pll_clk_div; - uint16_t pll_multiplier; - uint32_t pll_ip_clk_freq_hz; - uint32_t pll_op_clk_freq_hz; + u16 pre_pll_clk_div; + u16 pll_multiplier; + u32 pll_ip_clk_freq_hz; + u32 pll_op_clk_freq_hz; }; /** @@ -61,10 +61,10 @@ struct ccs_pll_branch_fr { * @pix_clk_freq_hz: Pixel clock frequency */ struct ccs_pll_branch_bk { - uint16_t sys_clk_div; - uint16_t pix_clk_div; - uint32_t sys_clk_freq_hz; - uint32_t pix_clk_freq_hz; + u16 sys_clk_div; + u16 pix_clk_div; + u32 sys_clk_freq_hz; + u32 pix_clk_freq_hz; }; /** @@ -97,21 +97,21 @@ struct ccs_pll_branch_bk { */ struct ccs_pll { /* input values */ - uint8_t bus_type; - uint8_t op_lanes; - uint8_t vt_lanes; + u8 bus_type; + u8 op_lanes; + u8 vt_lanes; struct { - uint8_t lanes; + u8 lanes; } csi2; - uint8_t binning_horizontal; - uint8_t binning_vertical; - uint8_t scale_m; - uint8_t scale_n; - uint8_t bits_per_pixel; - uint8_t op_bits_per_lane; - uint16_t flags; - uint32_t link_freq; - uint32_t ext_clk_freq_hz; + u8 binning_horizontal; + u8 binning_vertical; + u8 scale_m; + u8 scale_n; + u8 bits_per_pixel; + u8 op_bits_per_lane; + u16 flags; + u32 link_freq; + u32 ext_clk_freq_hz; /* output values */ struct ccs_pll_branch_fr vt_fr; @@ -119,8 +119,8 @@ struct ccs_pll { struct ccs_pll_branch_fr op_fr; struct ccs_pll_branch_bk op_bk; - uint32_t pixel_rate_csi; - uint32_t pixel_rate_pixel_array; + u32 pixel_rate_csi; + u32 pixel_rate_pixel_array; }; /** @@ -136,14 +136,14 @@ struct ccs_pll { * @max_pll_op_clk_freq_hz: Maximum PLL output clock frequency */ struct ccs_pll_branch_limits_fr { - uint16_t min_pre_pll_clk_div; - uint16_t max_pre_pll_clk_div; - uint32_t min_pll_ip_clk_freq_hz; - uint32_t max_pll_ip_clk_freq_hz; - uint16_t min_pll_multiplier; - uint16_t max_pll_multiplier; - uint32_t min_pll_op_clk_freq_hz; - uint32_t max_pll_op_clk_freq_hz; + u16 min_pre_pll_clk_div; + u16 max_pre_pll_clk_div; + u32 min_pll_ip_clk_freq_hz; + u32 max_pll_ip_clk_freq_hz; + u16 min_pll_multiplier; + u16 max_pll_multiplier; + u32 min_pll_op_clk_freq_hz; + u32 max_pll_op_clk_freq_hz; }; /** @@ -159,14 +159,14 @@ struct ccs_pll_branch_limits_fr { * @max_pix_clk_freq_hz: Maximum pixel clock frequency */ struct ccs_pll_branch_limits_bk { - uint16_t min_sys_clk_div; - uint16_t max_sys_clk_div; - uint32_t min_sys_clk_freq_hz; - uint32_t max_sys_clk_freq_hz; - uint16_t min_pix_clk_div; - uint16_t max_pix_clk_div; - uint32_t min_pix_clk_freq_hz; - uint32_t max_pix_clk_freq_hz; + u16 min_sys_clk_div; + u16 max_sys_clk_div; + u32 min_sys_clk_freq_hz; + u32 max_sys_clk_freq_hz; + u16 min_pix_clk_div; + u16 max_pix_clk_div; + u32 min_pix_clk_freq_hz; + u32 max_pix_clk_freq_hz; }; /** @@ -183,8 +183,8 @@ struct ccs_pll_branch_limits_bk { */ struct ccs_pll_limits { /* Strict PLL limits */ - uint32_t min_ext_clk_freq_hz; - uint32_t max_ext_clk_freq_hz; + u32 min_ext_clk_freq_hz; + u32 max_ext_clk_freq_hz; struct ccs_pll_branch_limits_fr vt_fr; struct ccs_pll_branch_limits_bk vt_bk; @@ -192,8 +192,8 @@ struct ccs_pll_limits { struct ccs_pll_branch_limits_bk op_bk; /* Other relevant limits */ - uint32_t min_line_length_pck_bin; - uint32_t min_line_length_pck; + u32 min_line_length_pck_bin; + u32 min_line_length_pck; }; struct device; -- cgit v1.2.3 From dffbdf3775d55fa637c456f4ed3025e426e467ec Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 26 Nov 2020 21:02:54 +0100 Subject: media: ccs: Switch from standard integer types to kernel ones The preferred integer types in the kernel are the Linux specific ones, switch from standard C types to u32 and alike. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-core.c | 2 +- drivers/media/i2c/ccs/ccs-reg-access.c | 21 ++++++++++----------- drivers/media/i2c/ccs/ccs.h | 6 +++--- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index a8f591c95bc2..15afbb4f5b31 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -1190,7 +1190,7 @@ static void ccs_update_blanking(struct ccs_sensor *sensor) { struct v4l2_ctrl *vblank = sensor->vblank; struct v4l2_ctrl *hblank = sensor->hblank; - uint16_t min_fll, max_fll, min_llp, max_llp, min_lbp; + u16 min_fll, max_fll, min_llp, max_llp, min_lbp; int min, max; if (sensor->binning_vertical > 1 || sensor->binning_horizontal > 1) { diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c index 5f0705952198..25993445f4fe 100644 --- a/drivers/media/i2c/ccs/ccs-reg-access.c +++ b/drivers/media/i2c/ccs/ccs-reg-access.c @@ -17,11 +17,10 @@ #include "ccs.h" #include "ccs-limits.h" -static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, - uint32_t phloat) +static u32 float_to_u32_mul_1000000(struct i2c_client *client, u32 phloat) { - int32_t exp; - uint64_t man; + s32 exp; + u64 man; if (phloat >= 0x80000000) { dev_err(&client->dev, "this is a negative number\n"); @@ -137,11 +136,11 @@ static int ____ccs_read_addr_8only(struct ccs_sensor *sensor, u16 reg, unsigned int ccs_reg_width(u32 reg) { if (reg & CCS_FL_16BIT) - return sizeof(uint16_t); + return sizeof(u16); if (reg & CCS_FL_32BIT) - return sizeof(uint32_t); + return sizeof(u32); - return sizeof(uint8_t); + return sizeof(u8); } static u32 ireal32_to_u32_mul_1000000(struct i2c_client *client, u32 val) @@ -205,7 +204,7 @@ static int __ccs_read_data(struct ccs_reg *regs, size_t num_regs, size_t i; for (i = 0; i < num_regs; i++, regs++) { - uint8_t *data; + u8 *data; if (regs->addr + regs->len < CCS_REG_ADDR(reg) + width) continue; @@ -216,13 +215,13 @@ static int __ccs_read_data(struct ccs_reg *regs, size_t num_regs, data = ®s->value[CCS_REG_ADDR(reg) - regs->addr]; switch (width) { - case sizeof(uint8_t): + case sizeof(u8): *val = *data; break; - case sizeof(uint16_t): + case sizeof(u16): *val = get_unaligned_be16(data); break; - case sizeof(uint32_t): + case sizeof(u32): *val = get_unaligned_be32(data); break; default: diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index cc33c9ba3165..6beac375cc48 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -84,11 +84,11 @@ struct ccs_hwconfig { unsigned short i2c_addr_dfl; /* Default i2c addr */ unsigned short i2c_addr_alt; /* Alternate i2c addr */ - uint32_t ext_clk; /* sensor external clk */ + u32 ext_clk; /* sensor external clk */ unsigned int lanes; /* Number of CSI-2 lanes */ - uint32_t csi_signalling_mode; /* CCS_CSI_SIGNALLING_MODE_* */ - uint64_t *op_sys_clock; + u32 csi_signalling_mode; /* CCS_CSI_SIGNALLING_MODE_* */ + u64 *op_sys_clock; enum ccs_module_board_orient module_board_orient; -- cgit v1.2.3 From ca59318b9a5f677fbf2e774aad2f25cd580a03c8 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 7 Dec 2020 17:34:57 +0100 Subject: media: Revert "media: ccs-pll: Fix MODULE_LICENSE" This reverts commit b3c0115e34adcabe12fce8845e24ca6f04c1554e. As per Documentation/process/license-rules.rst "GPL v2" exists only for historical reasons and has the same meaning as "GPL". So revert this patch. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs-pll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index 530085693a56..aeb87ba6fe37 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -883,4 +883,4 @@ EXPORT_SYMBOL_GPL(ccs_pll_calculate); MODULE_AUTHOR("Sakari Ailus "); MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ PLL calculator"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 38cfa52c4e879afead89a56d32a8c59326f13607 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 11 Dec 2020 12:30:09 +0100 Subject: media: ccs: Small definition cleanup struct device is used in ccs-data.h but no definition is included. Add a forward definition. Also ccs-data.c includes linux/types.h but this is already included in ccs-data.h which ccs-data.c includes. Do don't include linux/types.h in ccs-data.c. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-data.c | 1 - drivers/media/i2c/ccs/ccs-data.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ccs/ccs-data.c b/drivers/media/i2c/ccs/ccs-data.c index 9a6097b088bd..59338a6704af 100644 --- a/drivers/media/i2c/ccs/ccs-data.c +++ b/drivers/media/i2c/ccs/ccs-data.c @@ -10,7 +10,6 @@ #include #include #include -#include #include "ccs-data-defs.h" diff --git a/drivers/media/i2c/ccs/ccs-data.h b/drivers/media/i2c/ccs/ccs-data.h index 50d6508b24f3..c75d480c8792 100644 --- a/drivers/media/i2c/ccs/ccs-data.h +++ b/drivers/media/i2c/ccs/ccs-data.h @@ -10,6 +10,8 @@ #include +struct device; + /** * struct ccs_data_block_version - CCS static data version * @version_major: Major version number -- cgit v1.2.3 From b9dbfebb18ef2def4966eb6d10a04d0cf83d29a3 Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Wed, 2 Dec 2020 14:12:51 +0100 Subject: media: staging: media: imx: Kconfig: support VIDEO_IMX7_CSI for imx8m As described in NXPs' linux tree, the imx8m SoC includes the same CSI bridge hardware that is part of imx7d. We should be able to use the "fsl,imx7-csi" driver for imx8m directly. Since ipuv3 is not relevant for imx8m, move that dependency to VIDEO_IMX_CSI. That makes VIDEO_IMX7_CSI available to support imx8m too. Signed-off-by: Martin Kepplinger Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/Kconfig | 9 +++++---- drivers/staging/media/imx/Makefile | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig index f555aac8a9d5..15322dc3124a 100644 --- a/drivers/staging/media/imx/Kconfig +++ b/drivers/staging/media/imx/Kconfig @@ -2,7 +2,7 @@ config VIDEO_IMX_MEDIA tristate "i.MX5/6 V4L2 media core driver" depends on ARCH_MXC || COMPILE_TEST - depends on VIDEO_V4L2 && IMX_IPUV3_CORE + depends on VIDEO_V4L2 select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API depends on HAS_DMA @@ -14,21 +14,22 @@ config VIDEO_IMX_MEDIA driver for the i.MX5/6 SOC. if VIDEO_IMX_MEDIA -menu "i.MX5/6/7 Media Sub devices" +menu "i.MX5/6/7/8 Media Sub devices" config VIDEO_IMX_CSI tristate "i.MX5/6 Camera Sensor Interface driver" depends on VIDEO_IMX_MEDIA && VIDEO_DEV && I2C + depends on IMX_IPUV3_CORE default y help A video4linux camera sensor interface driver for i.MX5/6. config VIDEO_IMX7_CSI - tristate "i.MX6UL/L / i.MX7 Camera Sensor Interface driver" + tristate "i.MX6UL/L / i.MX7 / i.MX8M Camera Sensor Interface driver" depends on VIDEO_IMX_MEDIA && VIDEO_DEV && I2C default y help Enable support for video4linux camera sensor interface driver for - i.MX6UL/L or i.MX7. + i.MX6UL/L, i.MX7 or i.MX8M. endmenu endif diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile index 9bd9e873ba7c..69cc5da04a2e 100644 --- a/drivers/staging/media/imx/Makefile +++ b/drivers/staging/media/imx/Makefile @@ -8,9 +8,9 @@ imx-media-common-objs := imx-media-capture.o imx-media-dev-common.o \ imx6-media-csi-objs := imx-media-csi.o imx-media-fim.o -obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx6-media.o obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-common.o +obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-media.o obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-media-csi.o obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o -- cgit v1.2.3 From bb2216548a2b13cf2942a058b475438a7a6bb028 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 4 Jan 2021 21:34:39 +0100 Subject: media: imx: Unregister csc/scaler only if registered The csc/scaler device pointer (imxmd->m2m_vdev) is assigned after the imx media device v4l2-async probe completes, therefore we need to check if the device is non-NULL before trying to unregister it. This can be the case if the non-completed imx media device is unbinded (or the driver is removed), leading to a kernel oops. Fixes: a8ef0488cc59 ("media: imx: add csc/scaler mem2mem device") Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-dev.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 6d2205461e56..338b8bd0bb07 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -53,6 +53,7 @@ static int imx6_media_probe_complete(struct v4l2_async_notifier *notifier) imxmd->m2m_vdev = imx_media_csc_scaler_device_init(imxmd); if (IS_ERR(imxmd->m2m_vdev)) { ret = PTR_ERR(imxmd->m2m_vdev); + imxmd->m2m_vdev = NULL; goto unlock; } @@ -107,10 +108,14 @@ static int imx_media_remove(struct platform_device *pdev) v4l2_info(&imxmd->v4l2_dev, "Removing imx-media\n"); + if (imxmd->m2m_vdev) { + imx_media_csc_scaler_device_unregister(imxmd->m2m_vdev); + imxmd->m2m_vdev = NULL; + } + v4l2_async_notifier_unregister(&imxmd->notifier); imx_media_unregister_ipu_internal_subdevs(imxmd); v4l2_async_notifier_cleanup(&imxmd->notifier); - imx_media_csc_scaler_device_unregister(imxmd->m2m_vdev); media_device_unregister(&imxmd->md); v4l2_device_unregister(&imxmd->v4l2_dev); media_device_cleanup(&imxmd->md); -- cgit v1.2.3 From 89b14485caa4b7b2eaf70be0064f0978e68ebeee Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 4 Jan 2021 21:34:40 +0100 Subject: media: imx: Fix csc/scaler unregister The csc/scaler device private struct is released by ipu_csc_scaler_video_device_release(), which can be called by video_unregister_device() if there are no users of the underlying struct video device. Therefore, the mutex can't be held when calling video_unregister_device() as its memory may be freed by it, leading to a kernel oops. Fortunately, the fix is quite simple as no locking is needed when calling video_unregister_device(): v4l2-core already has its own internal locking, and the structures are also properly refcounted. Fixes: a8ef0488cc59 ("media: imx: add csc/scaler mem2mem device") Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csc-scaler.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csc-scaler.c b/drivers/staging/media/imx/imx-media-csc-scaler.c index fab1155a5958..63a0204502a8 100644 --- a/drivers/staging/media/imx/imx-media-csc-scaler.c +++ b/drivers/staging/media/imx/imx-media-csc-scaler.c @@ -869,11 +869,7 @@ void imx_media_csc_scaler_device_unregister(struct imx_media_video_dev *vdev) struct ipu_csc_scaler_priv *priv = vdev_to_priv(vdev); struct video_device *vfd = priv->vdev.vfd; - mutex_lock(&priv->mutex); - video_unregister_device(vfd); - - mutex_unlock(&priv->mutex); } struct imx_media_video_dev * -- cgit v1.2.3 From 7c720d77aae38b79f5bc3a0e0c82dcf8139aaf07 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 4 Jan 2021 21:34:41 +0100 Subject: media: imx: Clean capture unregister No locking is needed to call video_unregister_device(). Drop it. Also, drop the superfluous video_is_registered() call, which is done by video_unregister_device(), and re-order media_entity_cleanup() and video_unregister_device() calls. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-capture.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index c1931eb2540e..e10ce103a5b4 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -816,14 +816,8 @@ void imx_media_capture_device_unregister(struct imx_media_video_dev *vdev) struct capture_priv *priv = to_capture_priv(vdev); struct video_device *vfd = priv->vdev.vfd; - mutex_lock(&priv->mutex); - - if (video_is_registered(vfd)) { - video_unregister_device(vfd); - media_entity_cleanup(&vfd->entity); - } - - mutex_unlock(&priv->mutex); + media_entity_cleanup(&vfd->entity); + video_unregister_device(vfd); } EXPORT_SYMBOL_GPL(imx_media_capture_device_unregister); -- cgit v1.2.3 From 36fe4655d06c7d9185412514dfbb4a37ec936b5a Mon Sep 17 00:00:00 2001 From: Enrico Weigelt Date: Thu, 3 Dec 2020 13:47:57 +0100 Subject: media: drivers: staging: media: remove unneeded MODULE_VERSION() call Remove MODULE_VERSION(), as it doesn't seem to serve any practical purpose. For in-tree drivers, the kernel version matters. The code received lots of changes, but module version remained constant, since the driver landed in mainline. So, this version doesn't seem have any practical meaning anymore. Signed-off-by: Enrico Weigelt Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/omap4iss/iss.c | 1 - drivers/staging/media/omap4iss/iss_video.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index e06ea7ea1e50..dae9073e7d3c 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -1349,4 +1349,3 @@ module_platform_driver(iss_driver); MODULE_DESCRIPTION("TI OMAP4 ISS driver"); MODULE_AUTHOR("Sergio Aguirre "); MODULE_LICENSE("GPL"); -MODULE_VERSION(ISS_VIDEO_DRIVER_VERSION); diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h index 8b3dd92021e1..526281bf0051 100644 --- a/drivers/staging/media/omap4iss/iss_video.h +++ b/drivers/staging/media/omap4iss/iss_video.h @@ -18,7 +18,6 @@ #include #define ISS_VIDEO_DRIVER_NAME "issvideo" -#define ISS_VIDEO_DRIVER_VERSION "0.0.2" struct iss_device; struct iss_video; -- cgit v1.2.3 From d7a7d721064c548042b019cd0d4d62e0bb878d71 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 4 Dec 2020 00:07:30 +0100 Subject: media: ti-vpe: cal: avoid FIELD_GET assertion FIELD_GET() must only be used with a mask that is a compile-time constant: drivers/media/platform/ti-vpe/cal.h: In function 'cal_read_field': include/linux/compiler_types.h:320:38: error: call to '__compiletime_assert_247' declared with attribute error: FIELD_GET: mask is not constant include/linux/bitfield.h:46:3: note: in expansion of macro 'BUILD_BUG_ON_MSG' 46 | BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask), \ | ^~~~~~~~~~~~~~~~ drivers/media/platform/ti-vpe/cal.h:220:9: note: in expansion of macro 'FIELD_GET' 220 | return FIELD_GET(mask, cal_read(cal, offset)); | ^~~~~~~~~ The problem here is that the function is not always inlined. Mark it __always_inline to avoid the problem. Signed-off-by: Arnd Bergmann Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 60f5f7480b17..d471b7f82519 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -259,7 +259,7 @@ static inline void cal_write(struct cal_dev *cal, u32 offset, u32 val) iowrite32(val, cal->base + offset); } -static inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask) +static __always_inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask) { return FIELD_GET(mask, cal_read(cal, offset)); } -- cgit v1.2.3 From 4397efebf039be58e98c81a194a26100eba597bb Mon Sep 17 00:00:00 2001 From: Zhang Changzhong Date: Fri, 4 Dec 2020 09:29:34 +0100 Subject: media: mtk-vcodec: fix error return code in vdec_vp9_decode() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Fixes: dea42fb79f4f ("media: mtk-vcodec: reset segment data then trig decoder") Reported-by: Hulk Robot Signed-off-by: Zhang Changzhong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c index 5ea153a68522..d9880210b2ab 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c @@ -890,7 +890,8 @@ static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size); if (vsi->show_frame & BIT(2)) { - if (vpu_dec_start(&inst->vpu, NULL, 0)) { + ret = vpu_dec_start(&inst->vpu, NULL, 0); + if (ret) { mtk_vcodec_err(inst, "vpu trig decoder failed"); goto DECODE_ERROR; } -- cgit v1.2.3 From 2c405f6bb5d65ec2e50eb97fd4f2d0c273306f26 Mon Sep 17 00:00:00 2001 From: Travis Carter Date: Sat, 5 Dec 2020 00:51:50 +0100 Subject: media: staging:hantro: Fixed "replace comma with semicolon" Warning Corrected the following Warning: drivers/staging/media/hantro/hantro_v4l2.c:319: WARNING: Possible comma where semicolon could be used Signed-off-by: Travis Carter Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_v4l2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c index b668a82d40ad..e1081c16f56a 100644 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ b/drivers/staging/media/hantro/hantro_v4l2.c @@ -316,7 +316,7 @@ hantro_reset_fmt(struct v4l2_pix_format_mplane *fmt, fmt->pixelformat = vpu_fmt->fourcc; fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_JPEG, + fmt->colorspace = V4L2_COLORSPACE_JPEG; fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; fmt->quantization = V4L2_QUANTIZATION_DEFAULT; fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; -- cgit v1.2.3 From a819678d0dcc3cec41a5fd6d359ce5e86f5c58be Mon Sep 17 00:00:00 2001 From: Travis Carter Date: Sat, 5 Dec 2020 00:37:43 +0100 Subject: media: staging:rkvdec: Fixed "replace comma with semicolon" Warning Corrected the following Warning: drivers/staging/media/rkvdec/rkvdec.c:133: WARNING: Possible comma where semicolon could be used Signed-off-by: Travis Carter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkvdec/rkvdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index aa4f8c287618..d3eb81ee8dc2 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -143,7 +143,7 @@ static void rkvdec_reset_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f, memset(f, 0, sizeof(*f)); f->fmt.pix_mp.pixelformat = fourcc; f->fmt.pix_mp.field = V4L2_FIELD_NONE; - f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709, + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; -- cgit v1.2.3 From b00481bdca2d77fdae5f71517c09fd3b30eba57d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 9 Dec 2020 07:50:34 +0100 Subject: media: camss: Fix signedness bug in video_enum_fmt() This test has a problem because we want to know if "k" is -1 or a positive value less than "f->index". But the "f->index" variable is a u32 so if "k == -1" then -1 gets type promoted to UINT_MAX which is larger than "f->index". I've added an explicit test to check for -1. Fixes: a3d412d4b9f3 ("media: Revert "media: camss: Make use of V4L2_CAP_IO_MC"") Signed-off-by: Dan Carpenter Reviewed-by: Robert Foss Reviewed-by: Andrey Konovalov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index bd9334af1c73..2fa3214775d5 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -579,7 +579,7 @@ static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) break; } - if (k < f->index) + if (k == -1 || k < f->index) /* * All the unique pixel formats matching the arguments * have been enumerated (k >= 0 and f->index > 0), or -- cgit v1.2.3 From 9c67ed2ab299123872be14a3dc2ea44ce7e4538b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 9 Dec 2020 07:51:30 +0100 Subject: media: camss: missing error code in msm_video_register() This error path returns success but it should return -EINVAL. Fixes: cba3819d1e93 ("media: camss: Format configuration per hardware version") Signed-off-by: Dan Carpenter Reviewed-by: Robert Foss Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 2fa3214775d5..97cea7c4d769 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -961,6 +961,7 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, video->nformats = ARRAY_SIZE(formats_rdi_8x96); } } else { + ret = -EINVAL; goto error_video_register; } -- cgit v1.2.3 From d170a5f09394ec44c2f43386d1b966e327948384 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Wed, 9 Dec 2020 08:46:58 +0100 Subject: media: MAINTAINERS: correct entry in Amlogic GE2D driver section Commit aa821b2b9269 ("media: MAINTAINERS: Add myself as maintainer of the Amlogic GE2D driver") introduced a new MAINTAINERS section, but the file entry points to the wrong location. Hence, ./scripts/get_maintainer.pl --self-test=patterns warns: warning: no file matches F: drivers/media/meson/ge2d/ Adjust the entry to the actual location of the driver. Signed-off-by: Lukas Bulwahn Acked-by: Neil Armstrong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 7faa7ae50716..47fad204d6aa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11553,7 +11553,7 @@ L: linux-amlogic@lists.infradead.org S: Supported T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/amlogic,axg-ge2d.yaml -F: drivers/media/meson/ge2d/ +F: drivers/media/platform/meson/ge2d/ MESON NAND CONTROLLER DRIVER FOR AMLOGIC SOCS M: Liang Yang -- cgit v1.2.3 From c532fe0b7579d9f86b7ddf4d62df19ce4765a00e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 11 Dec 2020 14:38:06 +0100 Subject: media: sh_vou: Drop bogus __refdata annotation Since commit 4c62e9764ab403d4 ("Drivers: media: remove __dev* attributes.") in v3.8, the SuperH Video Output Unit driver no longer has any code or data located in initmem, hence there is no need to annotate the sh_vou structure with __refdata. Drop the annotation, to avoid suppressing future section warnings. Signed-off-by: Geert Uytterhoeven Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index b22dc1d72527..4ac48441f22c 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1355,7 +1355,7 @@ static int sh_vou_remove(struct platform_device *pdev) return 0; } -static struct platform_driver __refdata sh_vou = { +static struct platform_driver sh_vou = { .remove = sh_vou_remove, .driver = { .name = "sh-vou", -- cgit v1.2.3 From 4fc81486d02e9f9f17ec12a4e33e919c05712016 Mon Sep 17 00:00:00 2001 From: Sebastian Fricke Date: Sat, 12 Dec 2020 19:53:06 +0100 Subject: media: rkisp1: isp: Add the enum_frame_size ioctl Implement the VIDIOC_SUBDEV_ENUM_FRAME_SIZE ioctl for the isp entity, check if the mbus code is valid for the given pad. This call is not available for the parameter or metadata pads of the RkISP1. Signed-off-by: Sebastian Fricke Acked-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/rockchip/rkisp1/rkisp1-isp.c | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index 889982d8ca41..2e5b57e3aedc 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -600,6 +600,39 @@ static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; } +static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct rkisp1_isp_mbus_info *mbus_info; + + if (fse->pad == RKISP1_ISP_PAD_SINK_PARAMS || + fse->pad == RKISP1_ISP_PAD_SOURCE_STATS) + return -ENOTTY; + + if (fse->index > 0) + return -EINVAL; + + mbus_info = rkisp1_isp_mbus_info_get(fse->code); + if (!mbus_info) + return -EINVAL; + + if (!(mbus_info->direction & RKISP1_ISP_SD_SINK) && + fse->pad == RKISP1_ISP_PAD_SINK_VIDEO) + return -EINVAL; + + if (!(mbus_info->direction & RKISP1_ISP_SD_SRC) && + fse->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) + return -EINVAL; + + fse->min_width = RKISP1_ISP_MIN_WIDTH; + fse->max_width = RKISP1_ISP_MAX_WIDTH; + fse->min_height = RKISP1_ISP_MIN_HEIGHT; + fse->max_height = RKISP1_ISP_MAX_HEIGHT; + + return 0; +} + static int rkisp1_isp_init_config(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg) { @@ -880,6 +913,7 @@ static int rkisp1_subdev_link_validate(struct media_link *link) static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = { .enum_mbus_code = rkisp1_isp_enum_mbus_code, + .enum_frame_size = rkisp1_isp_enum_frame_size, .get_selection = rkisp1_isp_get_selection, .set_selection = rkisp1_isp_set_selection, .init_cfg = rkisp1_isp_init_config, -- cgit v1.2.3 From 7113469dafc2d545fa4fa9bc649c31dc27db492e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 12 Dec 2020 18:41:19 +0100 Subject: media: vsp1: Fix an error handling path in the probe function A previous 'rcar_fcp_get()' call must be undone in the error handling path, as already done in the remove function. Fixes: 94fcdf829793 ("[media] v4l: vsp1: Add FCP support") Signed-off-by: Christophe JAILLET Reviewed-by: Geert Uytterhoeven Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index dc62533cf32c..aa66e4f5f3f3 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -882,8 +882,10 @@ static int vsp1_probe(struct platform_device *pdev) } done: - if (ret) + if (ret) { pm_runtime_disable(&pdev->dev); + rcar_fcp_put(vsp1->fcp); + } return ret; } -- cgit v1.2.3 From 485da30473d708e5e835dc05c815bb03925cb7a3 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Wed, 16 Dec 2020 12:45:48 +0100 Subject: media: vsp1: Use BIT macro for feature identification These entries can only ever be single bits. Make use of the BIT macro accordingly. Signed-off-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 56c62122a81a..37cf33c7e6ca 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -44,16 +44,16 @@ struct vsp1_uif; #define VSP1_MAX_UIF 2 #define VSP1_MAX_WPF 4 -#define VSP1_HAS_LUT (1 << 1) -#define VSP1_HAS_SRU (1 << 2) -#define VSP1_HAS_BRU (1 << 3) -#define VSP1_HAS_CLU (1 << 4) -#define VSP1_HAS_WPF_VFLIP (1 << 5) -#define VSP1_HAS_WPF_HFLIP (1 << 6) -#define VSP1_HAS_HGO (1 << 7) -#define VSP1_HAS_HGT (1 << 8) -#define VSP1_HAS_BRS (1 << 9) -#define VSP1_HAS_EXT_DL (1 << 10) +#define VSP1_HAS_LUT BIT(1) +#define VSP1_HAS_SRU BIT(2) +#define VSP1_HAS_BRU BIT(3) +#define VSP1_HAS_CLU BIT(4) +#define VSP1_HAS_WPF_VFLIP BIT(5) +#define VSP1_HAS_WPF_HFLIP BIT(6) +#define VSP1_HAS_HGO BIT(7) +#define VSP1_HAS_HGT BIT(8) +#define VSP1_HAS_BRS BIT(9) +#define VSP1_HAS_EXT_DL BIT(10) struct vsp1_device_info { u32 version; -- cgit v1.2.3 From dbfa04ec61b7e68259efcdef39b1c637bfedf264 Mon Sep 17 00:00:00 2001 From: Nigel Christian Date: Fri, 18 Dec 2020 07:31:17 +0100 Subject: media: cec: fix trivial style warnings Comment has 'then' repeated twice. Let's clean it up. Use unsigned int to maintain naming consistency. Signed-off-by: Nigel Christian Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-adap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index d5d5d28d0b36..79fa36de8a04 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1296,7 +1296,7 @@ static int cec_config_log_addr(struct cec_adapter *adap, /* * If we are unable to get an OK or a NACK after max_retries attempts * (and note that each attempt already consists of four polls), then - * then we assume that something is really weird and that it is not a + * we assume that something is really weird and that it is not a * good idea to try and claim this logical address. */ if (i == max_retries) @@ -1735,7 +1735,7 @@ int __cec_s_log_addrs(struct cec_adapter *adap, const u8 feature_sz = ARRAY_SIZE(log_addrs->features[0]); u8 *features = log_addrs->features[i]; bool op_is_dev_features = false; - unsigned j; + unsigned int j; log_addrs->log_addr[i] = CEC_LOG_ADDR_INVALID; if (log_addrs->log_addr_type[i] > CEC_LOG_ADDR_TYPE_UNREGISTERED) { -- cgit v1.2.3 From a26efd1961a18b91ae4cd2e433adbcf865b40fa3 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Mon, 28 Dec 2020 14:02:05 +0100 Subject: media: em28xx: Fix use-after-free in em28xx_alloc_urbs When kzalloc() fails, em28xx_uninit_usb_xfer() will free usb_bufs->buf and set it to NULL. Thus the later access to usb_bufs->buf[i] will lead to null pointer dereference. Also the kfree(usb_bufs->buf) after that is redundant. Fixes: d571b592c6206 ("media: em28xx: don't use coherent buffer for DMA transfers") Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index e6088b5d1b80..3daa64bb1e1d 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -956,14 +956,10 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, usb_bufs->buf[i] = kzalloc(sb_size, GFP_KERNEL); if (!usb_bufs->buf[i]) { - em28xx_uninit_usb_xfer(dev, mode); - for (i--; i >= 0; i--) kfree(usb_bufs->buf[i]); - kfree(usb_bufs->buf); - usb_bufs->buf = NULL; - + em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } -- cgit v1.2.3 From cf73a660111c98f20cedb4abad7b46718c182333 Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Mon, 28 Dec 2020 14:51:04 +0100 Subject: media: platform: davinci: Use DEFINE_SPINLOCK() for spinlock spinlock can be initialized automatically with DEFINE_SPINLOCK() rather than explicitly calling spin_lock_init(). Signed-off-by: Zheng Yongjun Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index 5e67994e62cc..f1ce10828b8e 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -41,7 +41,7 @@ MODULE_ALIAS("platform:" VPIF_DRIVER_NAME); #define VPIF_CH2_MAX_MODES 15 #define VPIF_CH3_MAX_MODES 2 -spinlock_t vpif_lock; +DEFINE_SPINLOCK(vpif_lock); EXPORT_SYMBOL_GPL(vpif_lock); void __iomem *vpif_base; @@ -437,7 +437,6 @@ static int vpif_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get(&pdev->dev); - spin_lock_init(&vpif_lock); dev_info(&pdev->dev, "vpif probe success\n"); /* -- cgit v1.2.3 From 15d0c52241ecb1c9d802506bff6f5c3f7872c0df Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sat, 2 Jan 2021 07:27:22 +0100 Subject: media: media/pci: Fix memleak in empress_init When vb2_queue_init() fails, dev->empress_dev should be released just like other error handling paths. Fixes: 2ada815fc48bb ("[media] saa7134: convert to vb2") Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7134/saa7134-empress.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 39e3c7f8c5b4..76a37fbd8458 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -282,8 +282,11 @@ static int empress_init(struct saa7134_dev *dev) q->lock = &dev->lock; q->dev = &dev->pci->dev; err = vb2_queue_init(q); - if (err) + if (err) { + video_device_release(dev->empress_dev); + dev->empress_dev = NULL; return err; + } dev->empress_dev->queue = q; dev->empress_dev->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; -- cgit v1.2.3 From 76aaf8a96771c16365b8510f1fb97738dc88026e Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sat, 2 Jan 2021 09:26:37 +0100 Subject: media: tm6000: Fix memleak in tm6000_start_stream When usb_clear_halt() fails, dvb->bulk_urb->transfer_buffer and dvb->bulk_urb should be freed just like when usb_submit_urb() fails. Fixes: 3169c9b26fffa ("V4L/DVB (12788): tm6000: Add initial DVB-T support") Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-dvb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c index 19c90fa9e443..293a460f4616 100644 --- a/drivers/media/usb/tm6000/tm6000-dvb.c +++ b/drivers/media/usb/tm6000/tm6000-dvb.c @@ -141,6 +141,10 @@ static int tm6000_start_stream(struct tm6000_core *dev) if (ret < 0) { printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n", ret, __func__); + + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; return ret; } else printk(KERN_ERR "tm6000: pipe reset\n"); -- cgit v1.2.3 From 69c9e825e812ec6d663e64ebf14bd3bc7f37e2c7 Mon Sep 17 00:00:00 2001 From: Matwey V. Kornilov Date: Mon, 4 Jan 2021 18:00:07 +0100 Subject: media: pwc: Use correct device for DMA This fixes the following newly introduced warning: [ 15.518253] ------------[ cut here ]------------ [ 15.518941] WARNING: CPU: 0 PID: 246 at kernel/dma/mapping.c:149 dma_map_page_attrs+0x1a8/0x1d0 [ 15.520634] Modules linked in: pwc videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_common videodev mc efivarfs [ 15.522335] CPU: 0 PID: 246 Comm: v4l2-test Not tainted 5.11.0-rc1+ #1 [ 15.523281] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 [ 15.524438] RIP: 0010:dma_map_page_attrs+0x1a8/0x1d0 [ 15.525135] Code: 10 5b 5d 41 5c 41 5d c3 4d 89 d0 eb d7 4d 89 c8 89 e9 48 89 da e8 68 29 00 00 eb d1 48 89 f2 48 2b 50 18 48 89 d0 eb 83 0f 0b <0f> 0b 48 c7 c0 ff ff ff ff eb b8 48 89 d9 48 8b 40 40 e8 61 69 d2 [ 15.527938] RSP: 0018:ffffa2694047bca8 EFLAGS: 00010246 [ 15.528716] RAX: 0000000000000000 RBX: 0000000000002580 RCX: 0000000000000000 [ 15.529782] RDX: 0000000000000000 RSI: ffffcdce000ecc00 RDI: ffffa0b4bdb888a0 [ 15.530849] RBP: 0000000000000002 R08: 0000000000000002 R09: 0000000000000000 [ 15.531881] R10: 0000000000000004 R11: 000000000002d8c0 R12: 0000000000000000 [ 15.532911] R13: ffffa0b4bdb88800 R14: ffffa0b483820000 R15: ffffa0b4bdb888a0 [ 15.533942] FS: 00007fc5fbb5e4c0(0000) GS:ffffa0b4fc000000(0000) knlGS:0000000000000000 [ 15.535141] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 15.535988] CR2: 00007fc5fb6ea138 CR3: 0000000003812000 CR4: 00000000001506f0 [ 15.537025] Call Trace: [ 15.537425] start_streaming+0x2e9/0x4b0 [pwc] [ 15.538143] vb2_start_streaming+0x5e/0x110 [videobuf2_common] [ 15.538989] vb2_core_streamon+0x107/0x140 [videobuf2_common] [ 15.539831] __video_do_ioctl+0x18f/0x4a0 [videodev] [ 15.540670] video_usercopy+0x13a/0x5b0 [videodev] [ 15.541349] ? video_put_user+0x230/0x230 [videodev] [ 15.542096] ? selinux_file_ioctl+0x143/0x200 [ 15.542752] v4l2_ioctl+0x40/0x50 [videodev] [ 15.543360] __x64_sys_ioctl+0x89/0xc0 [ 15.543930] do_syscall_64+0x33/0x40 [ 15.544448] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 15.545236] RIP: 0033:0x7fc5fb671587 [ 15.545780] Code: b3 66 90 48 8b 05 11 49 2c 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d e1 48 2c 00 f7 d8 64 89 01 48 [ 15.548486] RSP: 002b:00007fff0f71f038 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 [ 15.549578] RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007fc5fb671587 [ 15.550664] RDX: 00007fff0f71f060 RSI: 0000000040045612 RDI: 0000000000000003 [ 15.551706] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 [ 15.552738] R10: 0000000000000000 R11: 0000000000000246 R12: 00007fff0f71f060 [ 15.553817] R13: 00007fff0f71f1d0 R14: 0000000000de1270 R15: 0000000000000000 [ 15.554914] ---[ end trace 7be03122966c2486 ]--- Fixes: 1161db6776bd ("media: usb: pwc: Don't use coherent DMA buffers for ISO transfer") Signed-off-by: Matwey V. Kornilov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc-if.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 61869636ec61..5e3339cc31c0 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -155,16 +155,17 @@ static const struct video_device pwc_template = { /***************************************************************************/ /* Private functions */ -static void *pwc_alloc_urb_buffer(struct device *dev, +static void *pwc_alloc_urb_buffer(struct usb_device *dev, size_t size, dma_addr_t *dma_handle) { + struct device *dmadev = dev->bus->sysdev; void *buffer = kmalloc(size, GFP_KERNEL); if (!buffer) return NULL; - *dma_handle = dma_map_single(dev, buffer, size, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, *dma_handle)) { + *dma_handle = dma_map_single(dmadev, buffer, size, DMA_FROM_DEVICE); + if (dma_mapping_error(dmadev, *dma_handle)) { kfree(buffer); return NULL; } @@ -172,12 +173,14 @@ static void *pwc_alloc_urb_buffer(struct device *dev, return buffer; } -static void pwc_free_urb_buffer(struct device *dev, +static void pwc_free_urb_buffer(struct usb_device *dev, size_t size, void *buffer, dma_addr_t dma_handle) { - dma_unmap_single(dev, dma_handle, size, DMA_FROM_DEVICE); + struct device *dmadev = dev->bus->sysdev; + + dma_unmap_single(dmadev, dma_handle, size, DMA_FROM_DEVICE); kfree(buffer); } @@ -282,6 +285,7 @@ static void pwc_frame_complete(struct pwc_device *pdev) static void pwc_isoc_handler(struct urb *urb) { struct pwc_device *pdev = (struct pwc_device *)urb->context; + struct device *dmadev = urb->dev->bus->sysdev; int i, fst, flen; unsigned char *iso_buf = NULL; @@ -328,7 +332,7 @@ static void pwc_isoc_handler(struct urb *urb) /* Reset ISOC error counter. We did get here, after all. */ pdev->visoc_errors = 0; - dma_sync_single_for_cpu(&urb->dev->dev, + dma_sync_single_for_cpu(dmadev, urb->transfer_dma, urb->transfer_buffer_length, DMA_FROM_DEVICE); @@ -379,7 +383,7 @@ static void pwc_isoc_handler(struct urb *urb) pdev->vlast_packet_size = flen; } - dma_sync_single_for_device(&urb->dev->dev, + dma_sync_single_for_device(dmadev, urb->transfer_dma, urb->transfer_buffer_length, DMA_FROM_DEVICE); @@ -461,7 +465,7 @@ retry: urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->transfer_buffer_length = ISO_BUFFER_SIZE; - urb->transfer_buffer = pwc_alloc_urb_buffer(&udev->dev, + urb->transfer_buffer = pwc_alloc_urb_buffer(udev, urb->transfer_buffer_length, &urb->transfer_dma); if (urb->transfer_buffer == NULL) { @@ -524,7 +528,7 @@ static void pwc_iso_free(struct pwc_device *pdev) if (urb) { PWC_DEBUG_MEMORY("Freeing URB\n"); if (urb->transfer_buffer) - pwc_free_urb_buffer(&urb->dev->dev, + pwc_free_urb_buffer(urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); -- cgit v1.2.3 From d497fcdab02996a4510d5dd0d743447c737c317a Mon Sep 17 00:00:00 2001 From: Zhang Changzhong Date: Fri, 4 Dec 2020 09:27:58 +0100 Subject: media: aspeed: fix error return code in aspeed_video_setup_video() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Fixes: d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver") Reported-by: Hulk Robot Signed-off-by: Zhang Changzhong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/aspeed-video.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index c46a79eace98..f2c4dadd6a0e 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -1551,12 +1551,12 @@ static int aspeed_video_setup_video(struct aspeed_video *video) V4L2_JPEG_CHROMA_SUBSAMPLING_420, mask, V4L2_JPEG_CHROMA_SUBSAMPLING_444); - if (video->ctrl_handler.error) { + rc = video->ctrl_handler.error; + if (rc) { v4l2_ctrl_handler_free(&video->ctrl_handler); v4l2_device_unregister(v4l2_dev); - dev_err(video->dev, "Failed to init controls: %d\n", - video->ctrl_handler.error); + dev_err(video->dev, "Failed to init controls: %d\n", rc); return rc; } -- cgit v1.2.3 From cc82fd691a3a7e8b46bd94fe7cacb2835015df22 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 14 Dec 2020 13:57:03 +0100 Subject: media: venus: use contig vb2 ops This driver uses the SG vb2 ops, but effectively only ever accesses the first entry of the SG table, indicating that it expects a flat layout. Switch it to use the contiguous ops to make sure this expected invariant is always enforced. Since the device is supposed to be behind an IOMMU this should have little to none practical consequences beyond making the driver not rely on a particular behavior of the SG implementation. Reported-by: Tomasz Figa Signed-off-by: Alexandre Courbot Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 2 +- drivers/media/platform/qcom/venus/helpers.c | 9 ++------- drivers/media/platform/qcom/venus/vdec.c | 6 +++--- drivers/media/platform/qcom/venus/venc.c | 6 +++--- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index e419b18613c6..f0c6295a6d50 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -548,7 +548,7 @@ config VIDEO_QCOM_VENUS depends on INTERCONNECT || !INTERCONNECT select QCOM_MDT_LOADER if ARCH_QCOM select QCOM_SCM if ARCH_QCOM - select VIDEOBUF2_DMA_SG + select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help This is a V4L2 driver for Qualcomm Venus video accelerator diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 50439eb1ffea..859d260f002b 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include @@ -1284,14 +1284,9 @@ int venus_helper_vb2_buf_init(struct vb2_buffer *vb) struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct venus_buffer *buf = to_venus_buffer(vbuf); - struct sg_table *sgt; - - sgt = vb2_dma_sg_plane_desc(vb, 0); - if (!sgt) - return -EFAULT; buf->size = vb2_plane_size(vb, 0); - buf->dma_addr = sg_dma_address(sgt->sgl); + buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) list_add_tail(&buf->reg_list, &inst->registeredbufs); diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 8488411204c3..3fb277c81aca 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include "hfi_venus_io.h" #include "hfi_parser.h" @@ -1461,7 +1461,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->ops = &vdec_vb2_ops; - src_vq->mem_ops = &vb2_dma_sg_memops; + src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct venus_buffer); src_vq->allow_zero_bytesused = 1; @@ -1475,7 +1475,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->ops = &vdec_vb2_ops; - dst_vq->mem_ops = &vb2_dma_sg_memops; + dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct venus_buffer); dst_vq->allow_zero_bytesused = 1; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 1c61602c5de1..a09550cd1dba 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -1001,7 +1001,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->ops = &venc_vb2_ops; - src_vq->mem_ops = &vb2_dma_sg_memops; + src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct venus_buffer); src_vq->allow_zero_bytesused = 1; @@ -1017,7 +1017,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->ops = &venc_vb2_ops; - dst_vq->mem_ops = &vb2_dma_sg_memops; + dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct venus_buffer); dst_vq->allow_zero_bytesused = 1; -- cgit v1.2.3 From acf8a57d8caf5ceabbe50774953fe04745ad1a50 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 28 Sep 2020 18:44:29 +0200 Subject: media: venus: vdec: Fix non reliable setting of LAST flag In real use of dynamic-resolution-change it is observed that the LAST buffer flag (which marks the last decoded buffer with the resolution before the resolution-change event) is not reliably set. Fix this by set the LAST buffer flag on next queued capture buffer after the resolution-change event. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 5 ++-- drivers/media/platform/qcom/venus/helpers.c | 6 ++++ drivers/media/platform/qcom/venus/vdec.c | 45 ++++++++++++++++------------- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index f03ed427accd..db0e6738281e 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -285,7 +285,6 @@ enum venus_dec_state { VENUS_DEC_STATE_DRAIN = 5, VENUS_DEC_STATE_DECODING = 6, VENUS_DEC_STATE_DRC = 7, - VENUS_DEC_STATE_DRC_FLUSH_DONE = 8, }; struct venus_ts_metadata { @@ -350,7 +349,7 @@ struct venus_ts_metadata { * @priv: a private for HFI operations callbacks * @session_type: the type of the session (decoder or encoder) * @hprop: a union used as a holder by get property - * @last_buf: last capture buffer for dynamic-resoluton-change + * @next_buf_last: a flag to mark next queued capture buffer as last */ struct venus_inst { struct list_head list; @@ -413,7 +412,7 @@ struct venus_inst { union hfi_get_property hprop; unsigned int core_acquired: 1; unsigned int bit_depth; - struct vb2_buffer *last_buf; + bool next_buf_last; }; #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 859d260f002b..c25eecb12658 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -1342,6 +1342,12 @@ void venus_helper_vb2_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(m2m_ctx, vbuf); + /* Skip processing queued capture buffers after LAST flag */ + if (inst->session_type == VIDC_SESSION_TYPE_DEC && + V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && + inst->codec_state == VENUS_DEC_STATE_DRC) + goto unlock; + cache_payload(inst, vb); if (inst->session_type == VIDC_SESSION_TYPE_ENC && diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 3fb277c81aca..3aa740ac57b2 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -917,10 +917,6 @@ static int vdec_start_capture(struct venus_inst *inst) return 0; reconfigure: - ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, true); - if (ret) - return ret; - ret = vdec_output_conf(inst); if (ret) return ret; @@ -948,6 +944,8 @@ reconfigure: venus_pm_load_scale(inst); + inst->next_buf_last = false; + ret = hfi_session_continue(inst); if (ret) goto free_dpb_bufs; @@ -988,6 +986,7 @@ static int vdec_start_output(struct venus_inst *inst) venus_helper_init_instance(inst); inst->sequence_out = 0; inst->reconfig = false; + inst->next_buf_last = false; ret = vdec_set_properties(inst); if (ret) @@ -1081,9 +1080,7 @@ static int vdec_stop_capture(struct venus_inst *inst) inst->codec_state = VENUS_DEC_STATE_STOPPED; break; case VENUS_DEC_STATE_DRC: - WARN_ON(1); - fallthrough; - case VENUS_DEC_STATE_DRC_FLUSH_DONE: + ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, true); inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; venus_helper_free_dpb_bufs(inst); break; @@ -1207,9 +1204,28 @@ static void vdec_buf_cleanup(struct vb2_buffer *vb) static void vdec_vb2_buf_queue(struct vb2_buffer *vb) { struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + static const struct v4l2_event eos = { .type = V4L2_EVENT_EOS }; vdec_pm_get_put(inst); + mutex_lock(&inst->lock); + + if (inst->next_buf_last && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && + inst->codec_state == VENUS_DEC_STATE_DRC) { + vbuf->flags |= V4L2_BUF_FLAG_LAST; + vbuf->sequence = inst->sequence_cap++; + vbuf->field = V4L2_FIELD_NONE; + vb2_set_plane_payload(vb, 0, 0); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + v4l2_event_queue_fh(&inst->fh, &eos); + inst->next_buf_last = false; + mutex_unlock(&inst->lock); + return; + } + + mutex_unlock(&inst->lock); + venus_helper_vb2_buf_queue(vb); } @@ -1253,13 +1269,6 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, vb->timestamp = timestamp_us * NSEC_PER_USEC; vbuf->sequence = inst->sequence_cap++; - if (inst->last_buf == vb) { - inst->last_buf = NULL; - vbuf->flags |= V4L2_BUF_FLAG_LAST; - vb2_set_plane_payload(vb, 0, 0); - vb->timestamp = 0; - } - if (vbuf->flags & V4L2_BUF_FLAG_LAST) { const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; @@ -1359,12 +1368,9 @@ static void vdec_event_change(struct venus_inst *inst, */ if (!sufficient && inst->codec_state == VENUS_DEC_STATE_DRC) { - struct vb2_v4l2_buffer *last; int ret; - last = v4l2_m2m_last_dst_buf(inst->m2m_ctx); - if (last) - inst->last_buf = &last->vb2_buf; + inst->next_buf_last = true; ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, false); if (ret) @@ -1413,8 +1419,7 @@ static void vdec_event_notify(struct venus_inst *inst, u32 event, static void vdec_flush_done(struct venus_inst *inst) { - if (inst->codec_state == VENUS_DEC_STATE_DRC) - inst->codec_state = VENUS_DEC_STATE_DRC_FLUSH_DONE; + dev_dbg(inst->core->dev_dec, VDBGH "flush done\n"); } static const struct hfi_inst_ops vdec_hfi_ops = { -- cgit v1.2.3 From a4ca67af8b831a781ac53060c5d5c3dccaf7676e Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 28 Sep 2020 18:44:30 +0200 Subject: media: venus: vdec: Make decoder return LAST flag for sufficient event This makes the decoder to behaives equally for sufficient and insufficient events. After this change the LAST buffer flag will be set when the new resolution (in dynamic-resolution-change state) is smaller then the old bitstream resolution. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/vdec.c | 42 +++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 3aa740ac57b2..a3e64d1af913 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -637,6 +637,7 @@ static int vdec_output_conf(struct venus_inst *inst) { struct venus_core *core = inst->core; struct hfi_enable en = { .enable = 1 }; + struct hfi_buffer_requirements bufreq; u32 width = inst->out_width; u32 height = inst->out_height; u32 out_fmt, out2_fmt; @@ -712,6 +713,23 @@ static int vdec_output_conf(struct venus_inst *inst) } if (IS_V3(core) || IS_V4(core)) { + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); + if (ret) + return ret; + + if (bufreq.size > inst->output_buf_size) + return -EINVAL; + + if (inst->dpb_fmt) { + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT2, + &bufreq); + if (ret) + return ret; + + if (bufreq.size > inst->output2_buf_size) + return -EINVAL; + } + if (inst->output2_buf_size) { ret = venus_helper_set_bufsize(inst, inst->output2_buf_size, @@ -1346,19 +1364,15 @@ static void vdec_event_change(struct venus_inst *inst, dev_dbg(dev, VDBGM "event %s sufficient resources (%ux%u)\n", sufficient ? "" : "not", ev_data->width, ev_data->height); - if (sufficient) { - hfi_session_continue(inst); - } else { - switch (inst->codec_state) { - case VENUS_DEC_STATE_INIT: - inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; - break; - case VENUS_DEC_STATE_DECODING: - inst->codec_state = VENUS_DEC_STATE_DRC; - break; - default: - break; - } + switch (inst->codec_state) { + case VENUS_DEC_STATE_INIT: + inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; + break; + case VENUS_DEC_STATE_DECODING: + inst->codec_state = VENUS_DEC_STATE_DRC; + break; + default: + break; } /* @@ -1367,7 +1381,7 @@ static void vdec_event_change(struct venus_inst *inst, * itself doesn't mark the last decoder output buffer with HFI EOS flag. */ - if (!sufficient && inst->codec_state == VENUS_DEC_STATE_DRC) { + if (inst->codec_state == VENUS_DEC_STATE_DRC) { int ret; inst->next_buf_last = true; -- cgit v1.2.3 From 21560ddf782688ca7f6acbdbf4efc68fa55f353b Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 28 Sep 2020 18:44:31 +0200 Subject: media: venus: helpers: Lock outside of buffer queue helper After adding more logic in vdec buf_queue vb2 op it is not practical to have two lock/unlock for one decoder buf_queue. So move the instance lock in encoder and decoder vb2 buf_queue operations. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 11 +++-------- drivers/media/platform/qcom/venus/vdec.c | 3 +-- drivers/media/platform/qcom/venus/venc.c | 11 ++++++++++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index c25eecb12658..c2a82cf43361 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -1338,34 +1338,29 @@ void venus_helper_vb2_buf_queue(struct vb2_buffer *vb) struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; int ret; - mutex_lock(&inst->lock); - v4l2_m2m_buf_queue(m2m_ctx, vbuf); /* Skip processing queued capture buffers after LAST flag */ if (inst->session_type == VIDC_SESSION_TYPE_DEC && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && inst->codec_state == VENUS_DEC_STATE_DRC) - goto unlock; + return; cache_payload(inst, vb); if (inst->session_type == VIDC_SESSION_TYPE_ENC && !(inst->streamon_out && inst->streamon_cap)) - goto unlock; + return; if (vb2_start_streaming_called(vb->vb2_queue)) { ret = is_buf_refed(inst, vbuf); if (ret) - goto unlock; + return; ret = session_process_buf(inst, vbuf); if (ret) return_buf_error(inst, vbuf); } - -unlock: - mutex_unlock(&inst->lock); } EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_queue); diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index a3e64d1af913..708af6adafc1 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1242,9 +1242,8 @@ static void vdec_vb2_buf_queue(struct vb2_buffer *vb) return; } - mutex_unlock(&inst->lock); - venus_helper_vb2_buf_queue(vb); + mutex_unlock(&inst->lock); } static const struct vb2_ops vdec_vb2_ops = { diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index a09550cd1dba..248bae1c5e80 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -929,13 +929,22 @@ bufs_done: return ret; } +static void venc_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + + mutex_lock(&inst->lock); + venus_helper_vb2_buf_queue(vb); + mutex_unlock(&inst->lock); +} + static const struct vb2_ops venc_vb2_ops = { .queue_setup = venc_queue_setup, .buf_init = venus_helper_vb2_buf_init, .buf_prepare = venus_helper_vb2_buf_prepare, .start_streaming = venc_start_streaming, .stop_streaming = venus_helper_vb2_stop_streaming, - .buf_queue = venus_helper_vb2_buf_queue, + .buf_queue = venc_vb2_buf_queue, }; static void venc_buf_done(struct venus_inst *inst, unsigned int buf_type, -- cgit v1.2.3 From d5ee32d7e5929592ad9b6e7a919dcdf89d05221b Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 2 Dec 2020 06:34:24 +0100 Subject: media: venus: preserve DRC state across seeks DRC events can happen virtually at anytime, including when we are starting a seek. Should this happen, we must make sure to return to the DRC state, otherwise the firmware will expect buffers of the new resolution whereas userspace will still work with the old one. Returning to the DRC state upon resume for seeking makes sure that the client will get the DRC event and will reallocate the buffers to fit the firmware's expectations. Signed-off-by: Alexandre Courbot Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/vdec.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 708af6adafc1..8f060db39989 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -988,7 +988,10 @@ static int vdec_start_output(struct venus_inst *inst) if (inst->codec_state == VENUS_DEC_STATE_SEEK) { ret = venus_helper_process_initial_out_bufs(inst); - inst->codec_state = VENUS_DEC_STATE_DECODING; + if (inst->next_buf_last) + inst->codec_state = VENUS_DEC_STATE_DRC; + else + inst->codec_state = VENUS_DEC_STATE_DECODING; goto done; } @@ -1094,8 +1097,10 @@ static int vdec_stop_capture(struct venus_inst *inst) ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); fallthrough; case VENUS_DEC_STATE_DRAIN: - vdec_cancel_dst_buffers(inst); inst->codec_state = VENUS_DEC_STATE_STOPPED; + fallthrough; + case VENUS_DEC_STATE_SEEK: + vdec_cancel_dst_buffers(inst); break; case VENUS_DEC_STATE_DRC: ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, true); @@ -1117,6 +1122,7 @@ static int vdec_stop_output(struct venus_inst *inst) case VENUS_DEC_STATE_DECODING: case VENUS_DEC_STATE_DRAIN: case VENUS_DEC_STATE_STOPPED: + case VENUS_DEC_STATE_DRC: ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); inst->codec_state = VENUS_DEC_STATE_SEEK; break; @@ -1390,6 +1396,7 @@ static void vdec_event_change(struct venus_inst *inst, dev_dbg(dev, VDBGH "flush output error %d\n", ret); } + inst->next_buf_last = true; inst->reconfig = true; v4l2_event_queue_fh(&inst->fh, &ev); wake_up(&inst->reconf_wait); -- cgit v1.2.3 From c8e8dabcd1a8c7aaedc514052d383a8152119084 Mon Sep 17 00:00:00 2001 From: Fritz Koenig Date: Tue, 1 Dec 2020 05:23:23 +0100 Subject: media: venus: vdec: Handle DRC after drain If the DRC is near the end of the stream the client may send a V4L2_DEC_CMD_STOP before the DRC occurs. V4L2_DEC_CMD_STOP puts the driver into the VENUS_DEC_STATE_DRAIN state. DRC must be aware so that after the DRC event the state can be restored correctly. Signed-off-by: Fritz Koenig Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/vdec.c | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index db0e6738281e..765ab7ed881b 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -413,6 +413,7 @@ struct venus_inst { unsigned int core_acquired: 1; unsigned int bit_depth; bool next_buf_last; + bool drain_active; }; #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 8f060db39989..4af14d8d92f6 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -519,8 +519,10 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) ret = hfi_session_process_buf(inst, &fdata); - if (!ret && inst->codec_state == VENUS_DEC_STATE_DECODING) + if (!ret && inst->codec_state == VENUS_DEC_STATE_DECODING) { inst->codec_state = VENUS_DEC_STATE_DRAIN; + inst->drain_active = true; + } } unlock: @@ -970,9 +972,13 @@ reconfigure: inst->codec_state = VENUS_DEC_STATE_DECODING; + if (inst->drain_active) + inst->codec_state = VENUS_DEC_STATE_DRAIN; + inst->streamon_cap = 1; inst->sequence_cap = 0; inst->reconfig = false; + inst->drain_active = false; return 0; @@ -1098,6 +1104,7 @@ static int vdec_stop_capture(struct venus_inst *inst) fallthrough; case VENUS_DEC_STATE_DRAIN: inst->codec_state = VENUS_DEC_STATE_STOPPED; + inst->drain_active = false; fallthrough; case VENUS_DEC_STATE_SEEK: vdec_cancel_dst_buffers(inst); @@ -1297,8 +1304,10 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, v4l2_event_queue_fh(&inst->fh, &ev); - if (inst->codec_state == VENUS_DEC_STATE_DRAIN) + if (inst->codec_state == VENUS_DEC_STATE_DRAIN) { + inst->drain_active = false; inst->codec_state = VENUS_DEC_STATE_STOPPED; + } } if (!bytesused) @@ -1374,6 +1383,7 @@ static void vdec_event_change(struct venus_inst *inst, inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; break; case VENUS_DEC_STATE_DECODING: + case VENUS_DEC_STATE_DRAIN: inst->codec_state = VENUS_DEC_STATE_DRC; break; default: -- cgit v1.2.3 From 5f2ca73dcca96c3de96a0e4d9ea24ebb46c55d2e Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 4 Dec 2020 11:01:36 +0100 Subject: media: venus: venc: Init the session only once in queue_setup Init the hfi session only once in queue_setup and also cover that with inst->lock. Tested-by: Fritz Koenig Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/venc.c | 85 ++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 248bae1c5e80..700c045125b2 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -725,7 +725,9 @@ static int venc_init_session(struct venus_inst *inst) int ret; ret = hfi_session_init(inst, inst->fmt_cap->pixfmt); - if (ret) + if (ret == -EINVAL) + return 0; + else if (ret) return ret; ret = venus_helper_set_input_resolution(inst, inst->width, @@ -762,17 +764,13 @@ static int venc_out_num_buffers(struct venus_inst *inst, unsigned int *num) struct hfi_buffer_requirements bufreq; int ret; - ret = venc_init_session(inst); + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); if (ret) return ret; - ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); - *num = bufreq.count_actual; - hfi_session_deinit(inst); - - return ret; + return 0; } static int venc_queue_setup(struct vb2_queue *q, @@ -781,7 +779,7 @@ static int venc_queue_setup(struct vb2_queue *q, { struct venus_inst *inst = vb2_get_drv_priv(q); unsigned int num, min = 4; - int ret = 0; + int ret; if (*num_planes) { if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && @@ -803,6 +801,13 @@ static int venc_queue_setup(struct vb2_queue *q, return 0; } + mutex_lock(&inst->lock); + ret = venc_init_session(inst); + mutex_unlock(&inst->lock); + + if (ret) + return ret; + switch (q->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: *num_planes = inst->fmt_out->num_planes; @@ -838,6 +843,49 @@ static int venc_queue_setup(struct vb2_queue *q, return ret; } +static int venc_buf_init(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + + inst->buf_count++; + + return venus_helper_vb2_buf_init(vb); +} + +static void venc_release_session(struct venus_inst *inst) +{ + int ret; + + mutex_lock(&inst->lock); + + ret = hfi_session_deinit(inst); + if (ret || inst->session_error) + hfi_session_abort(inst); + + mutex_unlock(&inst->lock); + + venus_pm_load_scale(inst); + INIT_LIST_HEAD(&inst->registeredbufs); + venus_pm_release_core(inst); +} + +static void venc_buf_cleanup(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct venus_buffer *buf = to_venus_buffer(vbuf); + + mutex_lock(&inst->lock); + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + if (!list_empty(&inst->registeredbufs)) + list_del_init(&buf->reg_list); + mutex_unlock(&inst->lock); + + inst->buf_count--; + if (!inst->buf_count) + venc_release_session(inst); +} + static int venc_verify_conf(struct venus_inst *inst) { enum hfi_version ver = inst->core->res->hfi_version; @@ -888,38 +936,32 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count) inst->sequence_cap = 0; inst->sequence_out = 0; - ret = venc_init_session(inst); - if (ret) - goto bufs_done; - ret = venus_pm_acquire_core(inst); if (ret) - goto deinit_sess; + goto error; ret = venc_set_properties(inst); if (ret) - goto deinit_sess; + goto error; ret = venc_verify_conf(inst); if (ret) - goto deinit_sess; + goto error; ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, inst->num_output_bufs, 0); if (ret) - goto deinit_sess; + goto error; ret = venus_helper_vb2_start_streaming(inst); if (ret) - goto deinit_sess; + goto error; mutex_unlock(&inst->lock); return 0; -deinit_sess: - hfi_session_deinit(inst); -bufs_done: +error: venus_helper_buffers_done(inst, q->type, VB2_BUF_STATE_QUEUED); if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) inst->streamon_out = 0; @@ -940,7 +982,8 @@ static void venc_vb2_buf_queue(struct vb2_buffer *vb) static const struct vb2_ops venc_vb2_ops = { .queue_setup = venc_queue_setup, - .buf_init = venus_helper_vb2_buf_init, + .buf_init = venc_buf_init, + .buf_cleanup = venc_buf_cleanup, .buf_prepare = venus_helper_vb2_buf_prepare, .start_streaming = venc_start_streaming, .stop_streaming = venus_helper_vb2_stop_streaming, -- cgit v1.2.3 From 20891170f339a8754312a877f3d17f0e5dadd599 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 4 Dec 2020 11:01:37 +0100 Subject: media: venus: Limit HFI sessions to the maximum supported Currently we rely on firmware to return error when we reach the maximum supported number of sessions. But this errors are happened at reqbuf time which is a bit later. The more reasonable way looks like is to return the error on driver open. To achieve that modify hfi_session_create to return error when we reach maximum count of sessions and thus refuse open. Tested-by: Fritz Koenig Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/hfi.c | 16 +++++++++++++--- drivers/media/platform/qcom/venus/hfi_parser.c | 3 +++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 765ab7ed881b..34f300bc31ea 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -96,6 +96,7 @@ struct venus_format { #define MAX_CAP_ENTRIES 32 #define MAX_ALLOC_MODE_ENTRIES 16 #define MAX_CODEC_NUM 32 +#define MAX_SESSIONS 16 struct raw_formats { u32 buftype; diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index 638ed5cfe05e..8786609d269e 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -175,6 +175,8 @@ static int wait_session_msg(struct venus_inst *inst) int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops) { struct venus_core *core = inst->core; + bool max; + int ret; if (!ops) return -EINVAL; @@ -184,11 +186,19 @@ int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops) inst->ops = ops; mutex_lock(&core->lock); - list_add_tail(&inst->list, &core->instances); - atomic_inc(&core->insts_count); + + max = atomic_add_unless(&core->insts_count, 1, + core->max_sessions_supported); + if (!max) { + ret = -EAGAIN; + } else { + list_add_tail(&inst->list, &core->instances); + ret = 0; + } + mutex_unlock(&core->lock); - return 0; + return ret; } EXPORT_SYMBOL_GPL(hfi_session_create); diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 363ee2a65453..52898633a8e6 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -276,6 +276,9 @@ u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf, words_count--; } + if (!core->max_sessions_supported) + core->max_sessions_supported = MAX_SESSIONS; + parser_fini(inst, codecs, domain); return HFI_ERR_NONE; -- cgit v1.2.3 From 7f339fdc3756e193136a167bfcfdfbfdaec42306 Mon Sep 17 00:00:00 2001 From: Vikash Garodia Date: Fri, 4 Dec 2020 11:01:38 +0100 Subject: media: venus: request for interrupt from venus For synchronous commands, update the message queue variable. This would inform video firmware to raise interrupt on host CPU whenever there is a response for such commands. Signed-off-by: Vikash Garodia Tested-by: Fritz Koenig Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_venus.c | 77 +++++++++++++++------------ 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 4be4a75ddcb6..3d16afc461bb 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -372,7 +372,7 @@ static void venus_soft_int(struct venus_hfi_device *hdev) } static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev, - void *pkt) + void *pkt, bool sync) { struct device *dev = hdev->core->dev; struct hfi_pkt_hdr *cmd_packet; @@ -394,18 +394,29 @@ static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev, return ret; } + if (sync) { + /* + * Inform video hardware to raise interrupt for synchronous + * commands + */ + queue = &hdev->queues[IFACEQ_MSG_IDX]; + queue->qhdr->rx_req = 1; + /* ensure rx_req is updated in memory */ + wmb(); + } + if (rx_req) venus_soft_int(hdev); return 0; } -static int venus_iface_cmdq_write(struct venus_hfi_device *hdev, void *pkt) +static int venus_iface_cmdq_write(struct venus_hfi_device *hdev, void *pkt, bool sync) { int ret; mutex_lock(&hdev->lock); - ret = venus_iface_cmdq_write_nolock(hdev, pkt); + ret = venus_iface_cmdq_write_nolock(hdev, pkt, sync); mutex_unlock(&hdev->lock); return ret; @@ -428,7 +439,7 @@ static int venus_hfi_core_set_resource(struct venus_core *core, u32 id, if (ret) return ret; - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -778,7 +789,7 @@ static int venus_sys_set_debug(struct venus_hfi_device *hdev, u32 debug) pkt_sys_debug_config(pkt, HFI_DEBUG_MODE_QUEUE, debug); - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -795,7 +806,7 @@ static int venus_sys_set_coverage(struct venus_hfi_device *hdev, u32 mode) pkt_sys_coverage_config(pkt, mode); - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -816,7 +827,7 @@ static int venus_sys_set_idle_message(struct venus_hfi_device *hdev, pkt_sys_idle_indicator(pkt, enable); - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -834,7 +845,7 @@ static int venus_sys_set_power_control(struct venus_hfi_device *hdev, pkt_sys_power_control(pkt, enable); - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -885,14 +896,14 @@ static int venus_sys_set_default_properties(struct venus_hfi_device *hdev) return ret; } -static int venus_session_cmd(struct venus_inst *inst, u32 pkt_type) +static int venus_session_cmd(struct venus_inst *inst, u32 pkt_type, bool sync) { struct venus_hfi_device *hdev = to_hfi_priv(inst->core); struct hfi_session_pkt pkt; pkt_session_cmd(&pkt, pkt_type, inst); - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, sync); } static void venus_flush_debug_queue(struct venus_hfi_device *hdev) @@ -922,7 +933,7 @@ static int venus_prepare_power_collapse(struct venus_hfi_device *hdev, pkt_sys_pc_prep(&pkt); - ret = venus_iface_cmdq_write(hdev, &pkt); + ret = venus_iface_cmdq_write(hdev, &pkt, false); if (ret) return ret; @@ -1064,13 +1075,13 @@ static int venus_core_init(struct venus_core *core) venus_set_state(hdev, VENUS_STATE_INIT); - ret = venus_iface_cmdq_write(hdev, &pkt); + ret = venus_iface_cmdq_write(hdev, &pkt, false); if (ret) return ret; pkt_sys_image_version(&version_pkt); - ret = venus_iface_cmdq_write(hdev, &version_pkt); + ret = venus_iface_cmdq_write(hdev, &version_pkt, false); if (ret) dev_warn(dev, "failed to send image version pkt to fw\n"); @@ -1099,7 +1110,7 @@ static int venus_core_ping(struct venus_core *core, u32 cookie) pkt_sys_ping(&pkt, cookie); - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, false); } static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type) @@ -1112,7 +1123,7 @@ static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type) if (ret) return ret; - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, false); } static int venus_session_init(struct venus_inst *inst, u32 session_type, @@ -1130,7 +1141,7 @@ static int venus_session_init(struct venus_inst *inst, u32 session_type, if (ret) goto err; - ret = venus_iface_cmdq_write(hdev, &pkt); + ret = venus_iface_cmdq_write(hdev, &pkt, true); if (ret) goto err; @@ -1151,7 +1162,7 @@ static int venus_session_end(struct venus_inst *inst) dev_warn(dev, "fw coverage msg ON failed\n"); } - return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_END); + return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_END, true); } static int venus_session_abort(struct venus_inst *inst) @@ -1160,7 +1171,7 @@ static int venus_session_abort(struct venus_inst *inst) venus_flush_debug_queue(hdev); - return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_ABORT); + return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_ABORT, true); } static int venus_session_flush(struct venus_inst *inst, u32 flush_mode) @@ -1173,22 +1184,22 @@ static int venus_session_flush(struct venus_inst *inst, u32 flush_mode) if (ret) return ret; - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, true); } static int venus_session_start(struct venus_inst *inst) { - return venus_session_cmd(inst, HFI_CMD_SESSION_START); + return venus_session_cmd(inst, HFI_CMD_SESSION_START, true); } static int venus_session_stop(struct venus_inst *inst) { - return venus_session_cmd(inst, HFI_CMD_SESSION_STOP); + return venus_session_cmd(inst, HFI_CMD_SESSION_STOP, true); } static int venus_session_continue(struct venus_inst *inst) { - return venus_session_cmd(inst, HFI_CMD_SESSION_CONTINUE); + return venus_session_cmd(inst, HFI_CMD_SESSION_CONTINUE, false); } static int venus_session_etb(struct venus_inst *inst, @@ -1205,7 +1216,7 @@ static int venus_session_etb(struct venus_inst *inst, if (ret) return ret; - ret = venus_iface_cmdq_write(hdev, &pkt); + ret = venus_iface_cmdq_write(hdev, &pkt, false); } else if (session_type == VIDC_SESSION_TYPE_ENC) { struct hfi_session_empty_buffer_uncompressed_plane0_pkt pkt; @@ -1213,7 +1224,7 @@ static int venus_session_etb(struct venus_inst *inst, if (ret) return ret; - ret = venus_iface_cmdq_write(hdev, &pkt); + ret = venus_iface_cmdq_write(hdev, &pkt, false); } else { ret = -EINVAL; } @@ -1232,7 +1243,7 @@ static int venus_session_ftb(struct venus_inst *inst, if (ret) return ret; - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, false); } static int venus_session_set_buffers(struct venus_inst *inst, @@ -1252,7 +1263,7 @@ static int venus_session_set_buffers(struct venus_inst *inst, if (ret) return ret; - return venus_iface_cmdq_write(hdev, pkt); + return venus_iface_cmdq_write(hdev, pkt, false); } static int venus_session_unset_buffers(struct venus_inst *inst, @@ -1272,17 +1283,17 @@ static int venus_session_unset_buffers(struct venus_inst *inst, if (ret) return ret; - return venus_iface_cmdq_write(hdev, pkt); + return venus_iface_cmdq_write(hdev, pkt, true); } static int venus_session_load_res(struct venus_inst *inst) { - return venus_session_cmd(inst, HFI_CMD_SESSION_LOAD_RESOURCES); + return venus_session_cmd(inst, HFI_CMD_SESSION_LOAD_RESOURCES, true); } static int venus_session_release_res(struct venus_inst *inst) { - return venus_session_cmd(inst, HFI_CMD_SESSION_RELEASE_RESOURCES); + return venus_session_cmd(inst, HFI_CMD_SESSION_RELEASE_RESOURCES, true); } static int venus_session_parse_seq_hdr(struct venus_inst *inst, u32 seq_hdr, @@ -1299,7 +1310,7 @@ static int venus_session_parse_seq_hdr(struct venus_inst *inst, u32 seq_hdr, if (ret) return ret; - ret = venus_iface_cmdq_write(hdev, pkt); + ret = venus_iface_cmdq_write(hdev, pkt, false); if (ret) return ret; @@ -1320,7 +1331,7 @@ static int venus_session_get_seq_hdr(struct venus_inst *inst, u32 seq_hdr, if (ret) return ret; - return venus_iface_cmdq_write(hdev, pkt); + return venus_iface_cmdq_write(hdev, pkt, false); } static int venus_session_set_property(struct venus_inst *inst, u32 ptype, @@ -1339,7 +1350,7 @@ static int venus_session_set_property(struct venus_inst *inst, u32 ptype, if (ret) return ret; - return venus_iface_cmdq_write(hdev, pkt); + return venus_iface_cmdq_write(hdev, pkt, false); } static int venus_session_get_property(struct venus_inst *inst, u32 ptype) @@ -1352,7 +1363,7 @@ static int venus_session_get_property(struct venus_inst *inst, u32 ptype) if (ret) return ret; - return venus_iface_cmdq_write(hdev, &pkt); + return venus_iface_cmdq_write(hdev, &pkt, true); } static int venus_resume(struct venus_core *core) -- cgit v1.2.3 From e922a33e0228fa314ffc4f70b3b9ffbc4aad1bbe Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 4 Dec 2020 11:01:39 +0100 Subject: media: venus: hfi: Correct session init return error The hfi_session_init can be called many times and it returns EINVAL when the session was already initialized. This error code (EINVAL) is confusing for the callers. Change hfi_session_init to return EALREADY error code when the session has been already initialized. Tested-by: Fritz Koenig Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi.c | 2 +- drivers/media/platform/qcom/venus/vdec.c | 2 +- drivers/media/platform/qcom/venus/venc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index 8786609d269e..0f2482367e06 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -221,7 +221,7 @@ int hfi_session_init(struct venus_inst *inst, u32 pixfmt) mutex_unlock(&core->lock); if (inst->state != INST_UNINIT) - return -EINVAL; + return -EALREADY; inst->hfi_codec = to_codec_type(pixfmt); reinit_completion(&inst->done); diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 4af14d8d92f6..5fe77a7d8139 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -761,7 +761,7 @@ static int vdec_session_init(struct venus_inst *inst) int ret; ret = hfi_session_init(inst, inst->fmt_out->pixfmt); - if (ret == -EINVAL) + if (ret == -EALREADY) return 0; else if (ret) return ret; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 700c045125b2..8cc0695df492 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -725,7 +725,7 @@ static int venc_init_session(struct venus_inst *inst) int ret; ret = hfi_session_init(inst, inst->fmt_cap->pixfmt); - if (ret == -EINVAL) + if (ret == -EALREADY) return 0; else if (ret) return ret; -- cgit v1.2.3 From ddd1fc49b60822b29d476564a4b8509565cc51ab Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 11 Nov 2020 15:37:51 +0100 Subject: media: venus: helpers: Calculate properly compressed buffer size For resolutions below 720p the size of the compressed buffer must be bigger. Correct this by checking the resolution when calculating buffer size and multiply by eight. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index c2a82cf43361..11f35e36149b 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -986,6 +986,8 @@ u32 venus_helper_get_framesz(u32 v4l2_fmt, u32 width, u32 height) if (compressed) { sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2; + if (width < 1280 || height < 720) + sz *= 8; return ALIGN(sz, SZ_4K); } -- cgit v1.2.3 From d33a94412ed1081f30d904cab54faea7c7b839fc Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 11 Nov 2020 15:37:52 +0100 Subject: media: venus: pm_helpers: Check instance state when calculate instance frequency Skip calculating instance frequency if it is not in running state. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/pm_helpers.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index a3850261d697..89f20c2255ea 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -939,6 +939,9 @@ static unsigned long calculate_inst_freq(struct venus_inst *inst, mbs_per_sec = load_per_instance(inst); + if (inst->state != INST_START) + return 0; + vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq; /* 21 / 20 is overhead factor */ vpp_freq += vpp_freq / 20; -- cgit v1.2.3 From d4bdba7b1cab5fce4d04bfc781b477aa6c535202 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 17 Aug 2020 16:28:20 +0200 Subject: media: venus: Delete not used core caps The core caps are filled but not used, delete them. In case we need them we can re-introduce. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 6 ------ drivers/media/platform/qcom/venus/hfi_venus.c | 3 --- 2 files changed, 9 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 34f300bc31ea..29f5e25a7f9a 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -146,7 +146,6 @@ struct venus_caps { * @enc_codecs: encoders supported by this core * @dec_codecs: decoders supported by this core * @max_sessions_supported: holds the maximum number of sessions - * @core_caps: core capabilities * @priv: a private filed for HFI operations * @ops: the core HFI operations * @work: a delayed work for handling system fatal error @@ -192,11 +191,6 @@ struct venus_core { unsigned long enc_codecs; unsigned long dec_codecs; unsigned int max_sessions_supported; -#define ENC_ROTATION_CAPABILITY 0x1 -#define ENC_SCALING_CAPABILITY 0x2 -#define ENC_DEINTERLACE_CAPABILITY 0x4 -#define DEC_MULTI_STREAM_CAPABILITY 0x8 - unsigned int core_caps; void *priv; const struct hfi_ops *ops; struct delayed_work work; diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 3d16afc461bb..50e03f8fc278 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -1602,9 +1602,6 @@ int venus_hfi_create(struct venus_core *core) hdev->suspended = true; core->priv = hdev; core->ops = &venus_hfi_ops; - core->core_caps = ENC_ROTATION_CAPABILITY | ENC_SCALING_CAPABILITY | - ENC_DEINTERLACE_CAPABILITY | - DEC_MULTI_STREAM_CAPABILITY; ret = venus_interface_queues_init(hdev); if (ret) -- cgit v1.2.3 From c7f50ce507d54ff0518d2f55971a6a8dab6ae8a5 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 29 Jul 2020 15:17:05 +0200 Subject: media: venus: Add more capabilities and VP9 profile/levels Add more caps and VP9 definitions for newer Venus versions. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_helper.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 60ee2479f7a6..1f1c3faa4631 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -364,6 +364,13 @@ #define HFI_HEVC_TIER_MAIN 0x1 #define HFI_HEVC_TIER_HIGH0 0x2 +#define HFI_VPX_PROFILE_MAIN 0x00000001 + +#define HFI_VPX_LEVEL_VERSION_0 0x00000001 +#define HFI_VPX_LEVEL_VERSION_1 0x00000002 +#define HFI_VPX_LEVEL_VERSION_2 0x00000004 +#define HFI_VPX_LEVEL_VERSION_3 0x00000008 + /* VP9 Profile 0, 8-bit */ #define HFI_VP9_PROFILE_P0 0x00000001 /* VP9 Profile 2, 10-bit */ @@ -571,7 +578,18 @@ struct hfi_bitrate { #define HFI_CAPABILITY_LCU_SIZE 0x14 #define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS 0x15 #define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE 0x16 +#define HFI_CAPABILITY_I_FRAME_QP 0x20 +#define HFI_CAPABILITY_P_FRAME_QP 0x21 +#define HFI_CAPABILITY_B_FRAME_QP 0x22 +#define HFI_CAPABILITY_RATE_CONTROL_MODES 0x23 +#define HFI_CAPABILITY_BLUR_WIDTH 0x24 +#define HFI_CAPABILITY_BLUR_HEIGHT 0x25 +#define HFI_CAPABILITY_SLICE_BYTE 0x27 +#define HFI_CAPABILITY_SLICE_MB 0x28 #define HFI_CAPABILITY_MAX_VIDEOCORES 0x2b +#define HFI_CAPABILITY_MAX_WORKMODES 0x2c +#define HFI_CAPABILITY_ROTATION 0x2f +#define HFI_CAPABILITY_COLOR_SPACE_CONVERSION 0x30 struct hfi_capability { u32 capability_type; -- cgit v1.2.3 From aa6033892b1d8ea956ce0358867806e171a620d1 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 5 Aug 2020 09:36:06 +0200 Subject: media: venus: Create hfi platform and move vpp/vsp there Introduce a new hfi platform to cover differences between hfi versions. As a start move vpp/vsp freq data in that hfi platform, more platform data will come later. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/Makefile | 3 +- drivers/media/platform/qcom/venus/core.c | 17 ------ drivers/media/platform/qcom/venus/core.h | 12 +---- drivers/media/platform/qcom/venus/helpers.c | 54 +++++++++---------- drivers/media/platform/qcom/venus/helpers.h | 2 +- drivers/media/platform/qcom/venus/hfi_platform.c | 49 ++++++++++++++++++ drivers/media/platform/qcom/venus/hfi_platform.h | 34 ++++++++++++ .../media/platform/qcom/venus/hfi_platform_v4.c | 60 ++++++++++++++++++++++ drivers/media/platform/qcom/venus/pm_helpers.c | 9 ++-- drivers/media/platform/qcom/venus/vdec.c | 6 +-- drivers/media/platform/qcom/venus/venc.c | 6 +-- 11 files changed, 179 insertions(+), 73 deletions(-) create mode 100644 drivers/media/platform/qcom/venus/hfi_platform.c create mode 100644 drivers/media/platform/qcom/venus/hfi_platform.h create mode 100644 drivers/media/platform/qcom/venus/hfi_platform_v4.c diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile index dfc636865709..09ebf4671692 100644 --- a/drivers/media/platform/qcom/venus/Makefile +++ b/drivers/media/platform/qcom/venus/Makefile @@ -3,7 +3,8 @@ venus-core-objs += core.o helpers.o firmware.o \ hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \ - hfi_parser.o pm_helpers.o dbgfs.o + hfi_parser.o pm_helpers.o dbgfs.o \ + hfi_platform.o hfi_platform_v4.o \ venus-dec-objs += vdec.o vdec_ctrls.o venus-enc-objs += venc.o venc_ctrls.o diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index bdd293faaad0..6bcd5659ffcc 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -486,17 +486,6 @@ static const struct freq_tbl sdm845_freq_table[] = { { 244800, 100000000 }, /* 1920x1080@30 */ }; -static const struct codec_freq_data sdm845_codec_freq_data[] = { - { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 }, - { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 }, - { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10 }, - { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10 }, - { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10 }, - { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10 }, - { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10 }, - { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10 }, -}; - static const struct bw_tbl sdm845_bw_table_enc[] = { { 1944000, 1612000, 0, 2416000, 0 }, /* 3840x2160@60 */ { 972000, 951000, 0, 1434000, 0 }, /* 3840x2160@30 */ @@ -518,8 +507,6 @@ static const struct venus_resources sdm845_res = { .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc), .bw_tbl_dec = sdm845_bw_table_dec, .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec), - .codec_freq_data = sdm845_codec_freq_data, - .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), .clks = {"core", "iface", "bus" }, .clks_num = 3, .vcodec0_clks = { "core", "bus" }, @@ -541,8 +528,6 @@ static const struct venus_resources sdm845_res_v2 = { .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc), .bw_tbl_dec = sdm845_bw_table_dec, .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec), - .codec_freq_data = sdm845_codec_freq_data, - .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), .clks = {"core", "iface", "bus" }, .clks_num = 3, .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, @@ -592,8 +577,6 @@ static const struct venus_resources sc7180_res = { .bw_tbl_enc_size = ARRAY_SIZE(sc7180_bw_table_enc), .bw_tbl_dec = sc7180_bw_table_dec, .bw_tbl_dec_size = ARRAY_SIZE(sc7180_bw_table_dec), - .codec_freq_data = sdm845_codec_freq_data, - .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), .clks = {"core", "iface", "bus" }, .clks_num = 3, .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 29f5e25a7f9a..53187e4b208f 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -36,13 +36,6 @@ struct reg_val { u32 value; }; -struct codec_freq_data { - u32 pixfmt; - u32 session_type; - unsigned long vpp_freq; - unsigned long vsp_freq; -}; - struct bw_tbl { u32 mbs_per_sec; u32 avg; @@ -61,8 +54,6 @@ struct venus_resources { unsigned int bw_tbl_dec_size; const struct reg_val *reg_tbl; unsigned int reg_tbl_size; - const struct codec_freq_data *codec_freq_data; - unsigned int codec_freq_data_size; const char * const clks[VIDC_CLKS_NUM_MAX]; unsigned int clks_num; const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; @@ -266,7 +257,8 @@ struct venus_buffer { struct clock_data { u32 core_id; unsigned long freq; - const struct codec_freq_data *codec_freq_data; + unsigned long vpp_freq; + unsigned long vsp_freq; }; #define to_venus_buffer(ptr) container_of(ptr, struct venus_buffer, vb) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 11f35e36149b..4e2617cfac57 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -15,6 +15,7 @@ #include "helpers.h" #include "hfi_helper.h" #include "pm_helpers.h" +#include "hfi_platform.h" struct intbuf { struct list_head list; @@ -1042,36 +1043,6 @@ int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode) } EXPORT_SYMBOL_GPL(venus_helper_set_work_mode); -int venus_helper_init_codec_freq_data(struct venus_inst *inst) -{ - const struct codec_freq_data *data; - unsigned int i, data_size; - u32 pixfmt; - int ret = 0; - - if (!IS_V4(inst->core)) - return 0; - - data = inst->core->res->codec_freq_data; - data_size = inst->core->res->codec_freq_data_size; - pixfmt = inst->session_type == VIDC_SESSION_TYPE_DEC ? - inst->fmt_out->pixfmt : inst->fmt_cap->pixfmt; - - for (i = 0; i < data_size; i++) { - if (data[i].pixfmt == pixfmt && - data[i].session_type == inst->session_type) { - inst->clk_data.codec_freq_data = &data[i]; - break; - } - } - - if (!inst->clk_data.codec_freq_data) - ret = -EINVAL; - - return ret; -} -EXPORT_SYMBOL_GPL(venus_helper_init_codec_freq_data); - int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs, unsigned int output2_bufs) @@ -1527,6 +1498,29 @@ void venus_helper_m2m_job_abort(void *priv) } EXPORT_SYMBOL_GPL(venus_helper_m2m_job_abort); +int venus_helper_session_init(struct venus_inst *inst) +{ + enum hfi_version version = inst->core->res->hfi_version; + u32 session_type = inst->session_type; + u32 codec; + int ret; + + codec = inst->session_type == VIDC_SESSION_TYPE_DEC ? + inst->fmt_out->pixfmt : inst->fmt_cap->pixfmt; + + ret = hfi_session_init(inst, codec); + if (ret) + return ret; + + inst->clk_data.vpp_freq = hfi_platform_get_codec_vpp_freq(version, codec, + session_type); + inst->clk_data.vsp_freq = hfi_platform_get_codec_vsp_freq(version, codec, + session_type); + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_session_init); + void venus_helper_init_instance(struct venus_inst *inst) { if (inst->session_type == VIDC_SESSION_TYPE_DEC) { diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index a4a0562bc83f..5407979bf234 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -33,7 +33,6 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, unsigned int width, unsigned int height, u32 buftype); int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode); -int venus_helper_init_codec_freq_data(struct venus_inst *inst); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs, unsigned int output2_bufs); @@ -48,6 +47,7 @@ unsigned int venus_helper_get_opb_size(struct venus_inst *inst); void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf); void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx); void venus_helper_init_instance(struct venus_inst *inst); +int venus_helper_session_init(struct venus_inst *inst); int venus_helper_get_out_fmts(struct venus_inst *inst, u32 fmt, u32 *out_fmt, u32 *out2_fmt, bool ubwc); int venus_helper_alloc_dpb_bufs(struct venus_inst *inst); diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c new file mode 100644 index 000000000000..65559cae21aa --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_platform.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#include "hfi_platform.h" + +const struct hfi_platform *hfi_platform_get(enum hfi_version version) +{ + switch (version) { + case HFI_VERSION_4XX: + return &hfi_plat_v4; + default: + break; + } + + return NULL; +} + +unsigned long +hfi_platform_get_codec_vpp_freq(enum hfi_version version, u32 codec, u32 session_type) +{ + const struct hfi_platform *plat; + unsigned long freq = 0; + + plat = hfi_platform_get(version); + if (!plat) + return 0; + + if (plat->codec_vpp_freq) + freq = plat->codec_vpp_freq(session_type, codec); + + return freq; +} + +unsigned long +hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec, u32 session_type) +{ + const struct hfi_platform *plat; + unsigned long freq = 0; + + plat = hfi_platform_get(version); + if (!plat) + return 0; + + if (plat->codec_vpp_freq) + freq = plat->codec_vsp_freq(session_type, codec); + + return freq; +} diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h new file mode 100644 index 000000000000..8b07ecbb4c82 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef __HFI_PLATFORM_H__ +#define __HFI_PLATFORM_H__ + +#include +#include + +#include "hfi.h" +#include "hfi_helper.h" + +struct hfi_platform_codec_freq_data { + u32 pixfmt; + u32 session_type; + unsigned long vpp_freq; + unsigned long vsp_freq; +}; + +struct hfi_platform { + unsigned long (*codec_vpp_freq)(u32 session_type, u32 codec); + unsigned long (*codec_vsp_freq)(u32 session_type, u32 codec); +}; + +extern const struct hfi_platform hfi_plat_v4; + +const struct hfi_platform *hfi_platform_get(enum hfi_version version); +unsigned long hfi_platform_get_codec_vpp_freq(enum hfi_version version, u32 codec, + u32 session_type); +unsigned long hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec, + u32 session_type); +#endif diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c new file mode 100644 index 000000000000..4fc2fd04ca9d --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#include "hfi_platform.h" + +static const struct hfi_platform_codec_freq_data codec_freq_data[] = { + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10 }, +}; + +static const struct hfi_platform_codec_freq_data * +get_codec_freq_data(u32 session_type, u32 pixfmt) +{ + const struct hfi_platform_codec_freq_data *data = codec_freq_data; + unsigned int i, data_size = ARRAY_SIZE(codec_freq_data); + const struct hfi_platform_codec_freq_data *found = NULL; + + for (i = 0; i < data_size; i++) { + if (data[i].pixfmt == pixfmt && data[i].session_type == session_type) { + found = &data[i]; + break; + } + } + + return found; +} + +static unsigned long codec_vpp_freq(u32 session_type, u32 codec) +{ + const struct hfi_platform_codec_freq_data *data; + + data = get_codec_freq_data(session_type, codec); + if (data) + return data->vpp_freq; + + return 0; +} + +static unsigned long codec_vsp_freq(u32 session_type, u32 codec) +{ + const struct hfi_platform_codec_freq_data *data; + + data = get_codec_freq_data(session_type, codec); + if (data) + return data->vsp_freq; + + return 0; +} + +const struct hfi_platform hfi_plat_v4 = { + .codec_vpp_freq = codec_vpp_freq, + .codec_vsp_freq = codec_vsp_freq, +}; diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 89f20c2255ea..0011c3aa3a73 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -18,6 +18,7 @@ #include "hfi_parser.h" #include "hfi_venus_io.h" #include "pm_helpers.h" +#include "hfi_platform.h" static bool legacy_binding; @@ -510,7 +511,7 @@ min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load) if (inst_pos->state != INST_START) continue; - vpp_freq = inst_pos->clk_data.codec_freq_data->vpp_freq; + vpp_freq = inst_pos->clk_data.vpp_freq; coreid = inst_pos->clk_data.core_id; mbs_per_sec = load_per_instance(inst_pos); @@ -559,7 +560,7 @@ static int decide_core(struct venus_inst *inst) return 0; inst_load = load_per_instance(inst); - inst_load *= inst->clk_data.codec_freq_data->vpp_freq; + inst_load *= inst->clk_data.vpp_freq; max_freq = core->res->freq_tbl[0].freq; min_loaded_core(inst, &min_coreid, &min_load); @@ -942,10 +943,10 @@ static unsigned long calculate_inst_freq(struct venus_inst *inst, if (inst->state != INST_START) return 0; - vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq; + vpp_freq = mbs_per_sec * inst->clk_data.vpp_freq; /* 21 / 20 is overhead factor */ vpp_freq += vpp_freq / 20; - vsp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vsp_freq; + vsp_freq = mbs_per_sec * inst->clk_data.vsp_freq; /* 10 / 7 is overhead factor */ if (inst->session_type == VIDC_SESSION_TYPE_ENC) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 5fe77a7d8139..fd06416521a0 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -760,7 +760,7 @@ static int vdec_session_init(struct venus_inst *inst) { int ret; - ret = hfi_session_init(inst, inst->fmt_out->pixfmt); + ret = venus_helper_session_init(inst); if (ret == -EALREADY) return 0; else if (ret) @@ -771,10 +771,6 @@ static int vdec_session_init(struct venus_inst *inst) if (ret) goto deinit; - ret = venus_helper_init_codec_freq_data(inst); - if (ret) - goto deinit; - return 0; deinit: hfi_session_deinit(inst); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 8cc0695df492..6e830fc83b09 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -724,7 +724,7 @@ static int venc_init_session(struct venus_inst *inst) { int ret; - ret = hfi_session_init(inst, inst->fmt_cap->pixfmt); + ret = venus_helper_session_init(inst); if (ret == -EALREADY) return 0; else if (ret) @@ -745,10 +745,6 @@ static int venc_init_session(struct venus_inst *inst) if (ret) goto deinit; - ret = venus_helper_init_codec_freq_data(inst); - if (ret) - goto deinit; - ret = venc_set_properties(inst); if (ret) goto deinit; -- cgit v1.2.3 From 8f3b41dcfb9a0fa2d2ca0af51c3eebd670dc153b Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 26 Aug 2020 13:04:09 +0200 Subject: media: venus: Rename venus_caps to hfi_plat_caps Now when we have hfi platform make venus capabilities an hfi platform capabilities. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 30 +++--------------------- drivers/media/platform/qcom/venus/helpers.c | 6 ++--- drivers/media/platform/qcom/venus/hfi_parser.c | 18 +++++++------- drivers/media/platform/qcom/venus/hfi_parser.h | 2 +- drivers/media/platform/qcom/venus/hfi_platform.h | 25 ++++++++++++++++++++ 5 files changed, 41 insertions(+), 40 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 53187e4b208f..8e14c39695ba 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -14,6 +14,7 @@ #include "dbgfs.h" #include "hfi.h" +#include "hfi_platform.h" #define VDBGL "VenusLow : " #define VDBGM "VenusMed : " @@ -82,31 +83,6 @@ struct venus_format { u32 flags; }; -#define MAX_PLANES 4 -#define MAX_FMT_ENTRIES 32 -#define MAX_CAP_ENTRIES 32 -#define MAX_ALLOC_MODE_ENTRIES 16 -#define MAX_CODEC_NUM 32 -#define MAX_SESSIONS 16 - -struct raw_formats { - u32 buftype; - u32 fmt; -}; - -struct venus_caps { - u32 codec; - u32 domain; - bool cap_bufs_mode_dynamic; - unsigned int num_caps; - struct hfi_capability caps[MAX_CAP_ENTRIES]; - unsigned int num_pl; - struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT]; - unsigned int num_fmts; - struct raw_formats fmts[MAX_FMT_ENTRIES]; - bool valid; /* used only for Venus v1xx */ -}; - /** * struct venus_core - holds core parameters valid for all instances * @@ -185,7 +161,7 @@ struct venus_core { void *priv; const struct hfi_ops *ops; struct delayed_work work; - struct venus_caps caps[MAX_CODEC_NUM]; + struct hfi_plat_caps caps[MAX_CODEC_NUM]; unsigned int codecs_count; unsigned int core0_usage_count; unsigned int core1_usage_count; @@ -420,7 +396,7 @@ static inline void *to_hfi_priv(struct venus_core *core) return core->priv; } -static inline struct venus_caps * +static inline struct hfi_plat_caps * venus_caps_by_codec(struct venus_core *core, u32 codec, u32 domain) { unsigned int c; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 4e2617cfac57..4b1978625f1b 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -481,7 +481,7 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) static bool is_dynamic_bufmode(struct venus_inst *inst) { struct venus_core *core = inst->core; - struct venus_caps *caps; + struct hfi_plat_caps *caps; /* * v4 doesn't send BUFFER_ALLOC_MODE_SUPPORTED property and supports @@ -1531,7 +1531,7 @@ void venus_helper_init_instance(struct venus_inst *inst) } EXPORT_SYMBOL_GPL(venus_helper_init_instance); -static bool find_fmt_from_caps(struct venus_caps *caps, u32 buftype, u32 fmt) +static bool find_fmt_from_caps(struct hfi_plat_caps *caps, u32 buftype, u32 fmt) { unsigned int i; @@ -1548,7 +1548,7 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, u32 *out_fmt, u32 *out2_fmt, bool ubwc) { struct venus_core *core = inst->core; - struct venus_caps *caps; + struct hfi_plat_caps *caps; u32 ubwc_fmt, fmt = to_hfi_raw_fmt(v4l2_fmt); bool found, found_ubwc; diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 52898633a8e6..1c91801d87aa 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -11,12 +11,12 @@ #include "hfi_helper.h" #include "hfi_parser.h" -typedef void (*func)(struct venus_caps *cap, const void *data, +typedef void (*func)(struct hfi_plat_caps *cap, const void *data, unsigned int size); static void init_codecs(struct venus_core *core) { - struct venus_caps *caps = core->caps, *cap; + struct hfi_plat_caps *caps = core->caps, *cap; unsigned long bit; for_each_set_bit(bit, &core->dec_codecs, MAX_CODEC_NUM) { @@ -34,11 +34,11 @@ static void init_codecs(struct venus_core *core) } } -static void for_each_codec(struct venus_caps *caps, unsigned int caps_num, +static void for_each_codec(struct hfi_plat_caps *caps, unsigned int caps_num, u32 codecs, u32 domain, func cb, void *data, unsigned int size) { - struct venus_caps *cap; + struct hfi_plat_caps *cap; unsigned int i; for (i = 0; i < caps_num; i++) { @@ -51,7 +51,7 @@ static void for_each_codec(struct venus_caps *caps, unsigned int caps_num, } static void -fill_buf_mode(struct venus_caps *cap, const void *data, unsigned int num) +fill_buf_mode(struct hfi_plat_caps *cap, const void *data, unsigned int num) { const u32 *type = data; @@ -81,7 +81,7 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data) } } -static void fill_profile_level(struct venus_caps *cap, const void *data, +static void fill_profile_level(struct hfi_plat_caps *cap, const void *data, unsigned int num) { const struct hfi_profile_level *pl = data; @@ -107,7 +107,7 @@ parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data) } static void -fill_caps(struct venus_caps *cap, const void *data, unsigned int num) +fill_caps(struct hfi_plat_caps *cap, const void *data, unsigned int num) { const struct hfi_capability *caps = data; @@ -132,7 +132,7 @@ parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data) fill_caps, caps_arr, num_caps); } -static void fill_raw_fmts(struct venus_caps *cap, const void *fmts, +static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts, unsigned int num_fmts) { const struct raw_formats *formats = fmts; @@ -211,7 +211,7 @@ static void parser_init(struct venus_inst *inst, u32 *codecs, u32 *domain) static void parser_fini(struct venus_inst *inst, u32 codecs, u32 domain) { - struct venus_caps *caps, *cap; + struct hfi_plat_caps *caps, *cap; unsigned int i; u32 dom; diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h index 264e6dd2415f..7f59d82110f9 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.h +++ b/drivers/media/platform/qcom/venus/hfi_parser.h @@ -16,7 +16,7 @@ static inline u32 get_cap(struct venus_inst *inst, u32 type, u32 which) { struct venus_core *core = inst->core; struct hfi_capability *cap = NULL; - struct venus_caps *caps; + struct hfi_plat_caps *caps; unsigned int i; caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h index 8b07ecbb4c82..679423232255 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.h +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -12,6 +12,31 @@ #include "hfi.h" #include "hfi_helper.h" +#define MAX_PLANES 4 +#define MAX_FMT_ENTRIES 32 +#define MAX_CAP_ENTRIES 32 +#define MAX_ALLOC_MODE_ENTRIES 16 +#define MAX_CODEC_NUM 32 +#define MAX_SESSIONS 16 + +struct raw_formats { + u32 buftype; + u32 fmt; +}; + +struct hfi_plat_caps { + u32 codec; + u32 domain; + bool cap_bufs_mode_dynamic; + unsigned int num_caps; + struct hfi_capability caps[MAX_CAP_ENTRIES]; + unsigned int num_pl; + struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT]; + unsigned int num_fmts; + struct raw_formats fmts[MAX_FMT_ENTRIES]; + bool valid; /* used only for Venus v1xx */ +}; + struct hfi_platform_codec_freq_data { u32 pixfmt; u32 session_type; -- cgit v1.2.3 From 9822291e031f6d7149ae4f3fc00bd9c33ac2a084 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 26 Aug 2020 13:07:07 +0200 Subject: media: venus: hfi_plat: Add codecs and capabilities ops Add ops to get the supported by the platform codecs and capabilities. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_platform.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h index 679423232255..50512d142662 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.h +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -47,6 +47,8 @@ struct hfi_platform_codec_freq_data { struct hfi_platform { unsigned long (*codec_vpp_freq)(u32 session_type, u32 codec); unsigned long (*codec_vsp_freq)(u32 session_type, u32 codec); + void (*codecs)(u32 *enc_codecs, u32 *dec_codecs, u32 *count); + const struct hfi_plat_caps *(*capabilities)(unsigned int *entries); }; extern const struct hfi_platform hfi_plat_v4; -- cgit v1.2.3 From 8b88cabef404ef07440ce90730c47b0f10935c8e Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 26 Aug 2020 13:08:46 +0200 Subject: media: venus: hfi_plat_v4: Populate codecs and capabilities for v4 Add new file for Venus hfi v4 with supported codecs and capabilities. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/qcom/venus/hfi_platform_v4.c | 259 +++++++++++++++++++++ 1 file changed, 259 insertions(+) diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c index 4fc2fd04ca9d..3848bb6d7408 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v4.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c @@ -4,6 +4,263 @@ */ #include "hfi_platform.h" +static const struct hfi_plat_caps caps[] = { +{ + .codec = HFI_VIDEO_CODEC_H264, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52}, + .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52}, + .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52}, + .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52}, + .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52}, + .num_pl = 5, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_HEVC, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0 << 28}, + .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0 << 28}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010}, + .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10}, + .num_fmts = 7, +}, { + .codec = HFI_VIDEO_CODEC_VP8, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0}, + .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1}, + .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2}, + .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3}, + .num_pl = 4, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_VP9, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_VP9_PROFILE_P0, 200}, + .pl[1] = {HFI_VP9_PROFILE_P2_10B, 200}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010}, + .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10}, + .num_fmts = 7, +}, { + .codec = HFI_VIDEO_CODEC_MPEG2, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 1920, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 1920, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 8160, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 40000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 244800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 30, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 1, 1}, + .num_caps = 10, + .pl[0] = {HFI_MPEG2_PROFILE_SIMPLE, HFI_MPEG2_LEVEL_H14}, + .pl[1] = {HFI_MPEG2_PROFILE_MAIN, HFI_MPEG2_LEVEL_H14}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_H264, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 51, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 51, 1}, + .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 51, 1}, + .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .num_caps = 21, + .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52}, + .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52}, + .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52}, + .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52}, + .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52}, + .num_pl = 5, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_HEVC, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 32, 32, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 63, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 63, 1}, + .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 63, 1}, + .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .num_caps = 24, + .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_VP8, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 240, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 3, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 127, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, + .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, + .num_caps = 23, + .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0}, + .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1}, + .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2}, + .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3}, + .num_pl = 4, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +} }; + +static const struct hfi_plat_caps *get_capabilities(unsigned int *entries) +{ + *entries = ARRAY_SIZE(caps); + return caps; +} + +static void get_codecs(u32 *enc_codecs, u32 *dec_codecs, u32 *count) +{ + *enc_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC | + HFI_VIDEO_CODEC_VP8; + *dec_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC | + HFI_VIDEO_CODEC_VP8 | HFI_VIDEO_CODEC_VP9 | + HFI_VIDEO_CODEC_MPEG2; + *count = 8; +} + static const struct hfi_platform_codec_freq_data codec_freq_data[] = { { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 }, { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 }, @@ -57,4 +314,6 @@ static unsigned long codec_vsp_freq(u32 session_type, u32 codec) const struct hfi_platform hfi_plat_v4 = { .codec_vpp_freq = codec_vpp_freq, .codec_vsp_freq = codec_vsp_freq, + .codecs = get_codecs, + .capabilities = get_capabilities, }; -- cgit v1.2.3 From 367b619ae70d25a0ccbbb7cf97db80cf2660657a Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 26 Aug 2020 13:35:47 +0200 Subject: media: venus: hfi_plat: Add platform ops for getting number of VPP pipes Starting from v6 we have one more hfi property which will be needed to calculate buffer sizes/count for particular codec and session type. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_platform.c | 14 ++++++++++++++ drivers/media/platform/qcom/venus/hfi_platform.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c index 65559cae21aa..06f46900cae8 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.c +++ b/drivers/media/platform/qcom/venus/hfi_platform.c @@ -47,3 +47,17 @@ hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec, u32 session return freq; } + +u8 hfi_platform_num_vpp_pipes(enum hfi_version version) +{ + const struct hfi_platform *plat; + + plat = hfi_platform_get(version); + if (!plat) + return 0; + + if (plat->num_vpp_pipes) + return plat->num_vpp_pipes(); + + return 0; +} diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h index 50512d142662..5cc67c0c0542 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.h +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -49,6 +49,7 @@ struct hfi_platform { unsigned long (*codec_vsp_freq)(u32 session_type, u32 codec); void (*codecs)(u32 *enc_codecs, u32 *dec_codecs, u32 *count); const struct hfi_plat_caps *(*capabilities)(unsigned int *entries); + u8 (*num_vpp_pipes)(void); }; extern const struct hfi_platform hfi_plat_v4; @@ -58,4 +59,5 @@ unsigned long hfi_platform_get_codec_vpp_freq(enum hfi_version version, u32 code u32 session_type); unsigned long hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec, u32 session_type); +u8 hfi_platform_num_vpp_pipes(enum hfi_version version); #endif -- cgit v1.2.3 From 869d77e7062906a84c2726dfc2433755b36a57e3 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 26 Aug 2020 13:40:10 +0200 Subject: media: venus: hfi_plat_v6: Populate capabilities for v6 Add new hfi platform file with capabilities of hfi v6. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/Makefile | 1 + drivers/media/platform/qcom/venus/hfi_platform.c | 2 + drivers/media/platform/qcom/venus/hfi_platform.h | 1 + .../media/platform/qcom/venus/hfi_platform_v6.c | 325 +++++++++++++++++++++ 4 files changed, 329 insertions(+) create mode 100644 drivers/media/platform/qcom/venus/hfi_platform_v6.c diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile index 09ebf4671692..a5d2da537c51 100644 --- a/drivers/media/platform/qcom/venus/Makefile +++ b/drivers/media/platform/qcom/venus/Makefile @@ -5,6 +5,7 @@ venus-core-objs += core.o helpers.o firmware.o \ hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \ hfi_parser.o pm_helpers.o dbgfs.o \ hfi_platform.o hfi_platform_v4.o \ + hfi_platform_v6.o \ venus-dec-objs += vdec.o vdec_ctrls.o venus-enc-objs += venc.o venc_ctrls.o diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c index 06f46900cae8..8f47804e973f 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.c +++ b/drivers/media/platform/qcom/venus/hfi_platform.c @@ -9,6 +9,8 @@ const struct hfi_platform *hfi_platform_get(enum hfi_version version) switch (version) { case HFI_VERSION_4XX: return &hfi_plat_v4; + case HFI_VERSION_6XX: + return &hfi_plat_v6; default: break; } diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h index 5cc67c0c0542..845737a50463 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.h +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -53,6 +53,7 @@ struct hfi_platform { }; extern const struct hfi_platform hfi_plat_v4; +extern const struct hfi_platform hfi_plat_v6; const struct hfi_platform *hfi_platform_get(enum hfi_version version); unsigned long hfi_platform_get_codec_vpp_freq(enum hfi_version version, u32 codec, diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c new file mode 100644 index 000000000000..e76d69a66b6f --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#include "hfi_platform.h" + +static const struct hfi_plat_caps caps[] = { +{ + .codec = HFI_VIDEO_CODEC_H264, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 5760, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 5760, 1}, + /* ((5760 * 2880) / 256) */ + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 36, 64800, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 200000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 36, 1958400, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1}, + .num_caps = 9, + .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52}, + .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52}, + .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52}, + .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52}, + .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52}, + .num_pl = 5, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_HEVC, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010}, + .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10}, + .num_fmts = 7, +}, { + .codec = HFI_VIDEO_CODEC_VP8, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0}, + .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1}, + .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2}, + .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3}, + .num_pl = 4, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_VP9, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, + .num_caps = 10, + .pl[0] = {HFI_VP9_PROFILE_P0, 200}, + .pl[1] = {HFI_VP9_PROFILE_P2_10B, 200}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[4] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .fmts[5] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_P010}, + .fmts[6] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_YUV420_TP10}, + .num_fmts = 7, +}, { + .codec = HFI_VIDEO_CODEC_MPEG2, + .domain = VIDC_SESSION_TYPE_DEC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 1920, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 1920, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 8160, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 40000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 244800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 30, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 1, 1}, + .num_caps = 10, + .pl[0] = {HFI_MPEG2_PROFILE_SIMPLE, HFI_MPEG2_LEVEL_H14}, + .pl[1] = {HFI_MPEG2_PROFILE_MAIN, HFI_MPEG2_LEVEL_H14}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_OUTPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[1] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV12}, + .fmts[3] = {HFI_BUFFER_OUTPUT2, HFI_COLOR_FORMAT_NV21}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_H264, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 51, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 51, 1}, + .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 51, 1}, + .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .num_caps = 21, + .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52}, + .pl[1] = {HFI_H264_PROFILE_MAIN, HFI_H264_LEVEL_52}, + .pl[2] = {HFI_H264_PROFILE_HIGH, HFI_H264_LEVEL_52}, + .pl[3] = {HFI_H264_PROFILE_CONSTRAINED_BASE, HFI_H264_LEVEL_52}, + .pl[4] = {HFI_H264_PROFILE_CONSTRAINED_HIGH, HFI_H264_LEVEL_52}, + .num_pl = 5, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_HEVC, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 32, 32, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 63, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 63, 1}, + .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 63, 1}, + .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .num_caps = 24, + .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, + .num_pl = 2, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +}, { + .codec = HFI_VIDEO_CODEC_VP8, + .domain = VIDC_SESSION_TYPE_ENC, + .cap_bufs_mode_dynamic = true, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 240, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 3, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1}, + .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 127, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, + .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, + .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, + .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, + .num_caps = 23, + .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0}, + .pl[1] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_1}, + .pl[2] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_2}, + .pl[3] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_3}, + .num_pl = 4, + .fmts[0] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12}, + .fmts[1] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_NV12_UBWC}, + .fmts[2] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_YUV420_TP10_UBWC}, + .fmts[3] = {HFI_BUFFER_INPUT, HFI_COLOR_FORMAT_P010}, + .num_fmts = 4, +} }; + +static const struct hfi_plat_caps *get_capabilities(unsigned int *entries) +{ + *entries = ARRAY_SIZE(caps); + return caps; +} + +static void get_codecs(u32 *enc_codecs, u32 *dec_codecs, u32 *count) +{ + *enc_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC | + HFI_VIDEO_CODEC_VP8; + *dec_codecs = HFI_VIDEO_CODEC_H264 | HFI_VIDEO_CODEC_HEVC | + HFI_VIDEO_CODEC_VP8 | HFI_VIDEO_CODEC_VP9 | + HFI_VIDEO_CODEC_MPEG2; + *count = 8; +} + +static const struct hfi_platform_codec_freq_data codec_freq_data[] = { + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 25 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 25 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 60 }, + { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 25 }, + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 25 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 25 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 60 }, + { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 60 }, +}; + +static const struct hfi_platform_codec_freq_data * +get_codec_freq_data(u32 session_type, u32 pixfmt) +{ + const struct hfi_platform_codec_freq_data *data = codec_freq_data; + unsigned int i, data_size = ARRAY_SIZE(codec_freq_data); + const struct hfi_platform_codec_freq_data *found = NULL; + + for (i = 0; i < data_size; i++) { + if (data[i].pixfmt == pixfmt && data[i].session_type == session_type) { + found = &data[i]; + break; + } + } + + return found; +} + +static unsigned long codec_vpp_freq(u32 session_type, u32 codec) +{ + const struct hfi_platform_codec_freq_data *data; + + data = get_codec_freq_data(session_type, codec); + if (data) + return data->vpp_freq; + + return 0; +} + +static unsigned long codec_vsp_freq(u32 session_type, u32 codec) +{ + const struct hfi_platform_codec_freq_data *data; + + data = get_codec_freq_data(session_type, codec); + if (data) + return data->vsp_freq; + + return 0; +} + +static u8 num_vpp_pipes(void) +{ + return 4; +} + +const struct hfi_platform hfi_plat_v6 = { + .codec_vpp_freq = codec_vpp_freq, + .codec_vsp_freq = codec_vsp_freq, + .codecs = get_codecs, + .capabilities = get_capabilities, + .num_vpp_pipes = num_vpp_pipes, +}; -- cgit v1.2.3 From 05ec881b218c9a9500ce6f0b1363034608708040 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 26 Aug 2020 14:20:13 +0200 Subject: media: venus: hfi_plat: Add hfi platform buffers ops >From Venus v6 and beyond the buffer size and count have to be calculated in the v4l2 driver instead of getting them from firmware. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_plat_bufs.h | 35 +++++++++++++++++++++++ drivers/media/platform/qcom/venus/hfi_platform.h | 3 ++ 2 files changed, 38 insertions(+) create mode 100644 drivers/media/platform/qcom/venus/hfi_plat_bufs.h diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs.h b/drivers/media/platform/qcom/venus/hfi_plat_bufs.h new file mode 100644 index 000000000000..6dfecaf5b0bd --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef __HFI_PLATFORM_BUFFERS_H__ +#define __HFI_PLATFORM_BUFFERS_H__ + +#include +#include "hfi_helper.h" + +struct hfi_plat_buffers_params { + u32 width; + u32 height; + u32 codec; + u32 hfi_color_fmt; + enum hfi_version version; + u32 num_vpp_pipes; + union { + struct { + u32 max_mbs_per_frame; + u32 buffer_size_limit; + bool is_secondary_output; + bool is_interlaced; + } dec; + struct { + u32 work_mode; + u32 rc_type; + u32 num_b_frames; + bool is_tenbit; + } enc; + }; +}; + +#endif diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h index 845737a50463..3819bb2b36bd 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.h +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -10,6 +10,7 @@ #include #include "hfi.h" +#include "hfi_plat_bufs.h" #include "hfi_helper.h" #define MAX_PLANES 4 @@ -50,6 +51,8 @@ struct hfi_platform { void (*codecs)(u32 *enc_codecs, u32 *dec_codecs, u32 *count); const struct hfi_plat_caps *(*capabilities)(unsigned int *entries); u8 (*num_vpp_pipes)(void); + int (*bufreq)(struct hfi_plat_buffers_params *params, u32 session_type, + u32 buftype, struct hfi_buffer_requirements *bufreq); }; extern const struct hfi_platform hfi_plat_v4; -- cgit v1.2.3 From 3a75bf4e792587d7fd82242aa6c61ec6df4c0fcc Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 26 Aug 2020 14:36:20 +0200 Subject: media: venus: Add platform buffers for v6 Add a new file for hfi platform buffer size and count calculations for v6. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/Makefile | 2 +- drivers/media/platform/qcom/venus/hfi_plat_bufs.h | 3 + .../media/platform/qcom/venus/hfi_plat_bufs_v6.c | 1317 ++++++++++++++++++++ .../media/platform/qcom/venus/hfi_platform_v6.c | 1 + 4 files changed, 1322 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile index a5d2da537c51..91ee6be10292 100644 --- a/drivers/media/platform/qcom/venus/Makefile +++ b/drivers/media/platform/qcom/venus/Makefile @@ -5,7 +5,7 @@ venus-core-objs += core.o helpers.o firmware.o \ hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \ hfi_parser.o pm_helpers.o dbgfs.o \ hfi_platform.o hfi_platform_v4.o \ - hfi_platform_v6.o \ + hfi_platform_v6.o hfi_plat_bufs_v6.o \ venus-dec-objs += vdec.o vdec_ctrls.o venus-enc-objs += venc.o venc_ctrls.o diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs.h b/drivers/media/platform/qcom/venus/hfi_plat_bufs.h index 6dfecaf5b0bd..52a51a3b964a 100644 --- a/drivers/media/platform/qcom/venus/hfi_plat_bufs.h +++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs.h @@ -32,4 +32,7 @@ struct hfi_plat_buffers_params { }; }; +int hfi_plat_bufreq_v6(struct hfi_plat_buffers_params *params, u32 session_type, + u32 buftype, struct hfi_buffer_requirements *bufreq); + #endif diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c new file mode 100644 index 000000000000..072e349dd46c --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c @@ -0,0 +1,1317 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#include +#include +#include + +#include "hfi.h" +#include "hfi_plat_bufs.h" +#include "helpers.h" + +#define MIN_INPUT_BUFFERS 4 +#define MIN_ENC_OUTPUT_BUFFERS 4 + +#define NV12_UBWC_Y_TILE_WIDTH 32 +#define NV12_UBWC_Y_TILE_HEIGHT 8 +#define NV12_UBWC_UV_TILE_WIDTH 16 +#define NV12_UBWC_UV_TILE_HEIGHT 8 +#define TP10_UBWC_Y_TILE_WIDTH 48 +#define TP10_UBWC_Y_TILE_HEIGHT 4 +#define METADATA_STRIDE_MULTIPLE 64 +#define METADATA_HEIGHT_MULTIPLE 16 +#define HFI_DMA_ALIGNMENT 256 + +#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64 +#define MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE 64 +#define MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE 64 +#define MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE 640 +#define MAX_FE_NBR_DATA_CB_LINE_BUFFER_SIZE 320 +#define MAX_FE_NBR_DATA_CR_LINE_BUFFER_SIZE 320 + +#define MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE (128 / 8) +#define MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE (128 / 8) +#define MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE (128 / 8) + +#define MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE (64 * 2 * 3) +#define MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE (32 * 2 * 3) +#define MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE (16 * 2 * 3) + +#define MAX_TILE_COLUMNS 32 /* 8K/256 */ + +#define NUM_HW_PIC_BUF 10 +#define BIN_BUFFER_THRESHOLD (1280 * 736) +#define H264D_MAX_SLICE 1800 +/* sizeof(h264d_buftab_t) aligned to 256 */ +#define SIZE_H264D_BUFTAB_T 256 +/* sizeof(h264d_hw_pic_t) aligned to 32 */ +#define SIZE_H264D_HW_PIC_T BIT(11) +#define SIZE_H264D_BSE_CMD_PER_BUF (32 * 4) +#define SIZE_H264D_VPP_CMD_PER_BUF 512 + +/* Line Buffer definitions, One for Luma and 1/2 for each Chroma */ +#define SIZE_H264D_LB_FE_TOP_DATA(width, height) \ + (MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * ALIGN((width), 16) * 3) + +#define SIZE_H264D_LB_FE_TOP_CTRL(width, height) \ + (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((width) + 15) >> 4)) + +#define SIZE_H264D_LB_FE_LEFT_CTRL(width, height) \ + (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((height) + 15) >> 4)) + +#define SIZE_H264D_LB_SE_TOP_CTRL(width, height) \ + (MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((width) + 15) >> 4)) + +#define SIZE_H264D_LB_SE_LEFT_CTRL(width, height) \ + (MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * (((height) + 15) >> 4)) + +#define SIZE_H264D_LB_PE_TOP_DATA(width, height) \ + (MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE * (((width) + 15) >> 4)) + +#define SIZE_H264D_LB_VSP_TOP(width, height) (((((width) + 15) >> 4) << 7)) + +#define SIZE_H264D_LB_RECON_DMA_METADATA_WR(width, height) \ + (ALIGN((height), 16) * 32) + +#define SIZE_H264D_QP(width, height) \ + ((((width) + 63) >> 6) * (((height) + 63) >> 6) * 128) + +#define SIZE_HW_PIC(size_per_buf) (NUM_HW_PIC_BUF * (size_per_buf)) + +#define H264_CABAC_HDR_RATIO_HD_TOT 1 +#define H264_CABAC_RES_RATIO_HD_TOT 3 + +/* + * Some content need more bin buffer, but limit buffer + * size for high resolution + */ +#define NUM_SLIST_BUF_H264 (256 + 32) +#define SIZE_SLIST_BUF_H264 512 +#define LCU_MAX_SIZE_PELS 64 +#define LCU_MIN_SIZE_PELS 16 + +#define H265D_MAX_SLICE 600 +#define SIZE_H265D_HW_PIC_T SIZE_H264D_HW_PIC_T +#define SIZE_H265D_BSE_CMD_PER_BUF (16 * sizeof(u32)) +#define SIZE_H265D_VPP_CMD_PER_BUF 256 + +#define SIZE_H265D_LB_FE_TOP_DATA(width, height) \ + (MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * (ALIGN(width, 64) + 8) * 2) + +#define SIZE_H265D_LB_FE_TOP_CTRL(width, height) \ + (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * \ + (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) + +#define SIZE_H265D_LB_FE_LEFT_CTRL(width, height) \ + (MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * \ + (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) + +#define SIZE_H265D_LB_SE_TOP_CTRL(width, height) \ + ((LCU_MAX_SIZE_PELS / 8 * (128 / 8)) * (((width) + 15) >> 4)) + +static inline u32 size_h265d_lb_se_left_ctrl(u32 width, u32 height) +{ + u32 x, y, z; + + x = ((height + 16 - 1) / 8) * MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE; + y = ((height + 32 - 1) / 8) * MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE; + z = ((height + 64 - 1) / 8) * MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE; + + return max3(x, y, z); +} + +#define SIZE_H265D_LB_PE_TOP_DATA(width, height) \ + (MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE * \ + (ALIGN(width, LCU_MIN_SIZE_PELS) / LCU_MIN_SIZE_PELS)) + +#define SIZE_H265D_LB_VSP_TOP(width, height) ((((width) + 63) >> 6) * 128) + +#define SIZE_H265D_LB_VSP_LEFT(width, height) ((((height) + 63) >> 6) * 128) + +#define SIZE_H265D_LB_RECON_DMA_METADATA_WR(width, height) \ + SIZE_H264D_LB_RECON_DMA_METADATA_WR(width, height) + +#define SIZE_H265D_QP(width, height) SIZE_H264D_QP(width, height) + +#define H265_CABAC_HDR_RATIO_HD_TOT 2 +#define H265_CABAC_RES_RATIO_HD_TOT 2 + +/* + * Some content need more bin buffer, but limit buffer size + * for high resolution + */ +#define SIZE_SLIST_BUF_H265 BIT(10) +#define NUM_SLIST_BUF_H265 (80 + 20) +#define H265_NUM_TILE_COL 32 +#define H265_NUM_TILE_ROW 128 +#define H265_NUM_TILE (H265_NUM_TILE_ROW * H265_NUM_TILE_COL + 1) + +static inline u32 size_vpxd_lb_fe_left_ctrl(u32 width, u32 height) +{ + u32 x, y, z; + + x = ((height + 15) >> 4) * MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE; + y = ((height + 31) >> 5) * MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE; + z = ((height + 63) >> 6) * MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE; + + return max3(x, y, z); +} + +#define SIZE_VPXD_LB_FE_TOP_CTRL(width, height) \ + (((ALIGN(width, 64) + 8) * 10 * 2)) /* small line */ +#define SIZE_VPXD_LB_SE_TOP_CTRL(width, height) \ + ((((width) + 15) >> 4) * MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE) + +static inline u32 size_vpxd_lb_se_left_ctrl(u32 width, u32 height) +{ + u32 x, y, z; + + x = ((height + 15) >> 4) * MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE; + y = ((height + 31) >> 5) * MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE; + z = ((height + 63) >> 6) * MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE; + + return max3(x, y, z); +} + +#define SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height) \ + ALIGN((ALIGN(height, 16) / (4 / 2)) * 64, 32) +#define SIZE_VP8D_LB_FE_TOP_DATA(width, height) \ + ((ALIGN(width, 16) + 8) * 10 * 2) +#define SIZE_VP9D_LB_FE_TOP_DATA(width, height) \ + ((ALIGN(ALIGN(width, 16), 64) + 8) * 10 * 2) +#define SIZE_VP8D_LB_PE_TOP_DATA(width, height) \ + ((ALIGN(width, 16) >> 4) * 64) +#define SIZE_VP9D_LB_PE_TOP_DATA(width, height) \ + ((ALIGN(ALIGN(width, 16), 64) >> 6) * 176) +#define SIZE_VP8D_LB_VSP_TOP(width, height) \ + (((ALIGN(width, 16) >> 4) * 64 / 2) + 256) +#define SIZE_VP9D_LB_VSP_TOP(width, height) \ + (((ALIGN(ALIGN(width, 16), 64) >> 6) * 64 * 8) + 256) + +#define HFI_IRIS2_VP9D_COMV_SIZE \ + ((((8192 + 63) >> 6) * ((4320 + 63) >> 6) * 8 * 8 * 2 * 8)) + +#define VPX_DECODER_FRAME_CONCURENCY_LVL 2 +#define VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_NUM 1 +#define VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_DEN 2 +#define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_NUM 3 +#define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_DEN 2 + +#define VP8_NUM_FRAME_INFO_BUF (5 + 1) +#define VP9_NUM_FRAME_INFO_BUF (8 + 2 + 1 + 8) +#define VP8_NUM_PROBABILITY_TABLE_BUF VP8_NUM_FRAME_INFO_BUF +#define VP9_NUM_PROBABILITY_TABLE_BUF (VP9_NUM_FRAME_INFO_BUF + 4) +#define VP8_PROB_TABLE_SIZE 3840 +#define VP9_PROB_TABLE_SIZE 3840 + +#define VP9_UDC_HEADER_BUF_SIZE (3 * 128) +#define MAX_SUPERFRAME_HEADER_LEN 34 +#define CCE_TILE_OFFSET_SIZE ALIGN(32 * 4 * 4, 32) + +#define QMATRIX_SIZE (sizeof(u32) * 128 + 256) +#define MP2D_QPDUMP_SIZE 115200 +#define HFI_IRIS2_ENC_PERSIST_SIZE 102400 +#define HFI_MAX_COL_FRAME 6 +#define HFI_VENUS_VENC_TRE_WB_BUFF_SIZE (65 << 4) /* in Bytes */ +#define HFI_VENUS_VENC_DB_LINE_BUFF_PER_MB 512 +#define HFI_VENUS_VPPSG_MAX_REGISTERS 2048 +#define HFI_VENUS_WIDTH_ALIGNMENT 128 +#define HFI_VENUS_WIDTH_TEN_BIT_ALIGNMENT 192 +#define HFI_VENUS_HEIGHT_ALIGNMENT 32 + +#define SYSTEM_LAL_TILE10 192 +#define NUM_MBS_720P (((1280 + 15) >> 4) * ((720 + 15) >> 4)) +#define NUM_MBS_4K (((4096 + 15) >> 4) * ((2304 + 15) >> 4)) +#define MB_SIZE_IN_PIXEL (16 * 16) +#define HDR10PLUS_PAYLOAD_SIZE 1024 +#define HDR10_HIST_EXTRADATA_SIZE 4096 + +static u32 size_vpss_lb(u32 width, u32 height, u32 num_vpp_pipes) +{ + u32 vpss_4tap_top_buffer_size, vpss_div2_top_buffer_size; + u32 vpss_4tap_left_buffer_size, vpss_div2_left_buffer_size; + u32 opb_wr_top_line_luma_buf_size, opb_wr_top_line_chroma_buf_size; + u32 opb_lb_wr_llb_y_buffer_size, opb_lb_wr_llb_uv_buffer_size; + u32 macrotiling_size; + u32 size = 0; + + vpss_4tap_top_buffer_size = 0; + vpss_div2_top_buffer_size = 0; + vpss_4tap_left_buffer_size = 0; + vpss_div2_left_buffer_size = 0; + + macrotiling_size = 32; + opb_wr_top_line_luma_buf_size = + ALIGN(width, macrotiling_size) / macrotiling_size * 256; + opb_wr_top_line_luma_buf_size = + ALIGN(opb_wr_top_line_luma_buf_size, HFI_DMA_ALIGNMENT) + + (MAX_TILE_COLUMNS - 1) * 256; + opb_wr_top_line_luma_buf_size = + max(opb_wr_top_line_luma_buf_size, (32 * ALIGN(height, 16))); + opb_wr_top_line_chroma_buf_size = opb_wr_top_line_luma_buf_size; + opb_lb_wr_llb_y_buffer_size = ALIGN((ALIGN(height, 16) / 2) * 64, 32); + opb_lb_wr_llb_uv_buffer_size = opb_lb_wr_llb_y_buffer_size; + size = num_vpp_pipes * + 2 * (vpss_4tap_top_buffer_size + vpss_div2_top_buffer_size) + + 2 * (vpss_4tap_left_buffer_size + vpss_div2_left_buffer_size) + + opb_wr_top_line_luma_buf_size + + opb_wr_top_line_chroma_buf_size + + opb_lb_wr_llb_uv_buffer_size + + opb_lb_wr_llb_y_buffer_size; + + return size; +} + +static u32 size_h264d_hw_bin_buffer(u32 width, u32 height) +{ + u32 size_yuv, size_bin_hdr, size_bin_res; + u32 size = 0; + u32 product; + + product = width * height; + size_yuv = (product <= BIN_BUFFER_THRESHOLD) ? + ((BIN_BUFFER_THRESHOLD * 3) >> 1) : ((product * 3) >> 1); + + size_bin_hdr = size_yuv * H264_CABAC_HDR_RATIO_HD_TOT; + size_bin_res = size_yuv * H264_CABAC_RES_RATIO_HD_TOT; + size_bin_hdr = ALIGN(size_bin_hdr, HFI_DMA_ALIGNMENT); + size_bin_res = ALIGN(size_bin_res, HFI_DMA_ALIGNMENT); + size = size_bin_hdr + size_bin_res; + + return size; +} + +static u32 h264d_scratch_size(u32 width, u32 height, bool is_interlaced) +{ + u32 aligned_width = ALIGN(width, 16); + u32 aligned_height = ALIGN(height, 16); + u32 size = 0; + + if (!is_interlaced) + size = size_h264d_hw_bin_buffer(aligned_width, aligned_height); + + return size; +} + +static u32 size_h265d_hw_bin_buffer(u32 width, u32 height) +{ + u32 size_yuv, size_bin_hdr, size_bin_res; + u32 size = 0; + u32 product; + + product = width * height; + size_yuv = (product <= BIN_BUFFER_THRESHOLD) ? + ((BIN_BUFFER_THRESHOLD * 3) >> 1) : ((product * 3) >> 1); + size_bin_hdr = size_yuv * H265_CABAC_HDR_RATIO_HD_TOT; + size_bin_res = size_yuv * H265_CABAC_RES_RATIO_HD_TOT; + size_bin_hdr = ALIGN(size_bin_hdr, HFI_DMA_ALIGNMENT); + size_bin_res = ALIGN(size_bin_res, HFI_DMA_ALIGNMENT); + size = size_bin_hdr + size_bin_res; + + return size; +} + +static u32 h265d_scratch_size(u32 width, u32 height, bool is_interlaced) +{ + u32 aligned_width = ALIGN(width, 16); + u32 aligned_height = ALIGN(height, 16); + u32 size = 0; + + if (!is_interlaced) + size = size_h265d_hw_bin_buffer(aligned_width, aligned_height); + + return size; +} + +static u32 vpxd_scratch_size(u32 width, u32 height, bool is_interlaced) +{ + u32 aligned_width = ALIGN(width, 16); + u32 aligned_height = ALIGN(height, 16); + u32 size_yuv = aligned_width * aligned_height * 3 / 2; + u32 size = 0; + + if (!is_interlaced) { + u32 binbuffer1_size, binbufer2_size; + + binbuffer1_size = max_t(u32, size_yuv, + ((BIN_BUFFER_THRESHOLD * 3) >> 1)); + binbuffer1_size *= VPX_DECODER_FRAME_CONCURENCY_LVL * + VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_NUM / + VPX_DECODER_FRAME_BIN_HDR_BUDGET_RATIO_DEN; + binbufer2_size = max_t(u32, size_yuv, + ((BIN_BUFFER_THRESHOLD * 3) >> 1)); + binbufer2_size *= VPX_DECODER_FRAME_CONCURENCY_LVL * + VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_NUM / + VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_DEN; + size = ALIGN(binbuffer1_size + binbufer2_size, + HFI_DMA_ALIGNMENT); + } + + return size; +} + +static u32 mpeg2d_scratch_size(u32 width, u32 height, bool is_interlaced) +{ + return 0; +} + +static u32 calculate_enc_output_frame_size(u32 width, u32 height, u32 rc_type) +{ + u32 aligned_width, aligned_height; + u32 mbs_per_frame; + u32 frame_size; + + /* + * Encoder output size calculation: 32 Align width/height + * For resolution < 720p : YUVsize * 4 + * For resolution > 720p & <= 4K : YUVsize / 2 + * For resolution > 4k : YUVsize / 4 + * Initially frame_size = YUVsize * 2; + */ + aligned_width = ALIGN(width, 32); + aligned_height = ALIGN(height, 32); + mbs_per_frame = (ALIGN(aligned_height, 16) * + ALIGN(aligned_width, 16)) / 256; + frame_size = width * height * 3; + + if (mbs_per_frame < NUM_MBS_720P) + frame_size = frame_size << 1; + else if (mbs_per_frame <= NUM_MBS_4K) + frame_size = frame_size >> 2; + else + frame_size = frame_size >> 3; + + if (rc_type == HFI_RATE_CONTROL_OFF || rc_type == HFI_RATE_CONTROL_CQ) + frame_size = frame_size << 1; + + /* + * In case of opaque color format bitdepth will be known + * with first ETB, buffers allocated already with 8 bit + * won't be sufficient for 10 bit + * calculate size considering 10-bit by default + * For 10-bit cases size = size * 1.25 + */ + frame_size *= 5; + frame_size /= 4; + + return ALIGN(frame_size, SZ_4K); +} + +static u32 calculate_enc_scratch_size(u32 width, u32 height, u32 work_mode, + u32 lcu_size, u32 num_vpp_pipes, + u32 rc_type) +{ + u32 aligned_width, aligned_height, bitstream_size; + u32 total_bitbin_buffers, size_single_pipe, bitbin_size; + u32 sao_bin_buffer_size, padded_bin_size, size; + + aligned_width = ALIGN(width, lcu_size); + aligned_height = ALIGN(height, lcu_size); + bitstream_size = + calculate_enc_output_frame_size(width, height, rc_type); + + bitstream_size = ALIGN(bitstream_size, HFI_DMA_ALIGNMENT); + + if (work_mode == VIDC_WORK_MODE_2) { + total_bitbin_buffers = 3; + bitbin_size = bitstream_size * 17 / 10; + bitbin_size = ALIGN(bitbin_size, HFI_DMA_ALIGNMENT); + } else { + total_bitbin_buffers = 1; + bitstream_size = aligned_width * aligned_height * 3; + bitbin_size = ALIGN(bitstream_size, HFI_DMA_ALIGNMENT); + } + + if (num_vpp_pipes > 2) + size_single_pipe = bitbin_size / 2; + else + size_single_pipe = bitbin_size; + + size_single_pipe = ALIGN(size_single_pipe, HFI_DMA_ALIGNMENT); + sao_bin_buffer_size = + (64 * (((width + 32) * (height + 32)) >> 10)) + 384; + padded_bin_size = ALIGN(size_single_pipe, HFI_DMA_ALIGNMENT); + size_single_pipe = sao_bin_buffer_size + padded_bin_size; + size_single_pipe = ALIGN(size_single_pipe, HFI_DMA_ALIGNMENT); + bitbin_size = size_single_pipe * num_vpp_pipes; + size = ALIGN(bitbin_size, HFI_DMA_ALIGNMENT) * + total_bitbin_buffers + 512; + + return size; +} + +static u32 h264e_scratch_size(u32 width, u32 height, u32 work_mode, + u32 num_vpp_pipes, u32 rc_type) +{ + return calculate_enc_scratch_size(width, height, work_mode, 16, + num_vpp_pipes, rc_type); +} + +static u32 h265e_scratch_size(u32 width, u32 height, u32 work_mode, + u32 num_vpp_pipes, u32 rc_type) +{ + return calculate_enc_scratch_size(width, height, work_mode, 32, + num_vpp_pipes, rc_type); +} + +static u32 vp8e_scratch_size(u32 width, u32 height, u32 work_mode, + u32 num_vpp_pipes, u32 rc_type) +{ + return calculate_enc_scratch_size(width, height, work_mode, 16, + num_vpp_pipes, rc_type); +} + +static u32 hfi_iris2_h264d_comv_size(u32 width, u32 height, + u32 yuv_buf_min_count) +{ + u32 frame_width_in_mbs = ((width + 15) >> 4); + u32 frame_height_in_mbs = ((height + 15) >> 4); + u32 col_mv_aligned_width = (frame_width_in_mbs << 6); + u32 col_zero_aligned_width = (frame_width_in_mbs << 2); + u32 col_zero_size = 0, size_colloc = 0, comv_size = 0; + + col_mv_aligned_width = ALIGN(col_mv_aligned_width, 16); + col_zero_aligned_width = ALIGN(col_zero_aligned_width, 16); + col_zero_size = + col_zero_aligned_width * ((frame_height_in_mbs + 1) >> 1); + col_zero_size = ALIGN(col_zero_size, 64); + col_zero_size <<= 1; + col_zero_size = ALIGN(col_zero_size, 512); + size_colloc = col_mv_aligned_width * ((frame_height_in_mbs + 1) >> 1); + size_colloc = ALIGN(size_colloc, 64); + size_colloc <<= 1; + size_colloc = ALIGN(size_colloc, 512); + size_colloc += (col_zero_size + SIZE_H264D_BUFTAB_T * 2); + comv_size = size_colloc * yuv_buf_min_count; + comv_size += 512; + + return comv_size; +} + +static u32 size_h264d_bse_cmd_buf(u32 height) +{ + u32 aligned_height = ALIGN(height, 32); + + return min_t(u32, (((aligned_height + 15) >> 4) * 3 * 4), + H264D_MAX_SLICE) * SIZE_H264D_BSE_CMD_PER_BUF; +} + +static u32 size_h264d_vpp_cmd_buf(u32 height) +{ + u32 aligned_height = ALIGN(height, 32); + + return min_t(u32, (((aligned_height + 15) >> 4) * 3 * 4), + H264D_MAX_SLICE) * SIZE_H264D_VPP_CMD_PER_BUF; +} + +static u32 hfi_iris2_h264d_non_comv_size(u32 width, u32 height, + u32 num_vpp_pipes) +{ + u32 size_bse, size_vpp, size; + + size_bse = size_h264d_bse_cmd_buf(height); + size_vpp = size_h264d_vpp_cmd_buf(height); + size = + ALIGN(size_bse, HFI_DMA_ALIGNMENT) + + ALIGN(size_vpp, HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_HW_PIC(SIZE_H264D_HW_PIC_T), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_FE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_FE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_FE_LEFT_CTRL(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_H264D_LB_SE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_SE_LEFT_CTRL(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_H264D_LB_PE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H264D_LB_RECON_DMA_METADATA_WR(width, height), + HFI_DMA_ALIGNMENT) * 2 + + ALIGN(SIZE_H264D_QP(width, height), HFI_DMA_ALIGNMENT); + + return ALIGN(size, HFI_DMA_ALIGNMENT); +} + +static u32 size_h265d_bse_cmd_buf(u32 width, u32 height) +{ + u32 size; + + size = (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + NUM_HW_PIC_BUF; + size = min_t(u32, size, H265D_MAX_SLICE + 1); + size = 2 * size * SIZE_H265D_BSE_CMD_PER_BUF; + + return ALIGN(size, HFI_DMA_ALIGNMENT); +} + +static u32 size_h265d_vpp_cmd_buf(u32 width, u32 height) +{ + u32 size; + + size = (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + NUM_HW_PIC_BUF; + size = min_t(u32, size, H265D_MAX_SLICE + 1); + size = ALIGN(size, 4); + size = 2 * size * SIZE_H265D_VPP_CMD_PER_BUF; + + return ALIGN(size, HFI_DMA_ALIGNMENT); +} + +static u32 hfi_iris2_h265d_comv_size(u32 width, u32 height, + u32 yuv_buf_count_min) +{ + u32 size; + + size = ALIGN(((((width + 15) >> 4) * ((height + 15) >> 4)) << 8), 512); + size *= yuv_buf_count_min; + size += 512; + + return size; +} + +static u32 hfi_iris2_h265d_non_comv_size(u32 width, u32 height, + u32 num_vpp_pipes) +{ + u32 size_bse, size_vpp, size; + + size_bse = size_h265d_bse_cmd_buf(width, height); + size_vpp = size_h265d_vpp_cmd_buf(width, height); + size = + ALIGN(size_bse, HFI_DMA_ALIGNMENT) + + ALIGN(size_vpp, HFI_DMA_ALIGNMENT) + + ALIGN(NUM_HW_PIC_BUF * 20 * 22 * 4, HFI_DMA_ALIGNMENT) + + ALIGN(2 * sizeof(u16) * + (ALIGN(width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + (ALIGN(height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_HW_PIC(SIZE_H265D_HW_PIC_T), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_FE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_FE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_FE_LEFT_CTRL(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_h265d_lb_se_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_H265D_LB_SE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_PE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_H265D_LB_VSP_LEFT(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_H265D_LB_RECON_DMA_METADATA_WR(width, height), + HFI_DMA_ALIGNMENT) + * 4 + + ALIGN(SIZE_H265D_QP(width, height), HFI_DMA_ALIGNMENT); + + return ALIGN(size, HFI_DMA_ALIGNMENT); +} + +static u32 hfi_iris2_vp8d_comv_size(u32 width, u32 height, + u32 yuv_min_buf_count) +{ + return (((width + 15) >> 4) * ((height + 15) >> 4) * 8 * 2); +} + +static u32 h264d_scratch1_size(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes) +{ + u32 co_mv_size, nonco_mv_size, vpss_lb_size = 0; + + co_mv_size = hfi_iris2_h264d_comv_size(width, height, min_buf_count); + nonco_mv_size = hfi_iris2_h264d_non_comv_size(width, height, + num_vpp_pipes); + if (split_mode_enabled) + vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes); + + return co_mv_size + nonco_mv_size + vpss_lb_size; +} + +static u32 h265d_scratch1_size(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes) +{ + u32 co_mv_size, nonco_mv_size, vpss_lb_size = 0; + + co_mv_size = hfi_iris2_h265d_comv_size(width, height, min_buf_count); + nonco_mv_size = hfi_iris2_h265d_non_comv_size(width, height, + num_vpp_pipes); + if (split_mode_enabled) + vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes); + + return co_mv_size + nonco_mv_size + vpss_lb_size + + HDR10_HIST_EXTRADATA_SIZE; +} + +static u32 vp8d_scratch1_size(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes) +{ + u32 vpss_lb_size = 0, size; + + size = hfi_iris2_vp8d_comv_size(width, height, 0); + size += ALIGN(size_vpxd_lb_fe_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_vpxd_lb_se_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_VP8D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_FE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + 2 * ALIGN(SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_SE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP8D_LB_PE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP8D_LB_FE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT); + if (split_mode_enabled) + vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes); + + size += vpss_lb_size; + + return size; +} + +static u32 vp9d_scratch1_size(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes) +{ + u32 vpss_lb_size = 0; + u32 size; + + size = + ALIGN(size_vpxd_lb_fe_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_vpxd_lb_se_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_VP9D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_FE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + 2 * ALIGN(SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_SE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP9D_LB_PE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP9D_LB_FE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT); + + if (split_mode_enabled) + vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes); + + size += vpss_lb_size + HDR10_HIST_EXTRADATA_SIZE; + + return size; +} + +static u32 mpeg2d_scratch1_size(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes) +{ + u32 vpss_lb_size = 0; + u32 size; + + size = + ALIGN(size_vpxd_lb_fe_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_vpxd_lb_se_left_ctrl(width, height), + HFI_DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(SIZE_VP8D_LB_VSP_TOP(width, height), HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_FE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + 2 * ALIGN(SIZE_VPXD_LB_RECON_DMA_METADATA_WR(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VPXD_LB_SE_TOP_CTRL(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP8D_LB_PE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT) + + ALIGN(SIZE_VP8D_LB_FE_TOP_DATA(width, height), + HFI_DMA_ALIGNMENT); + + if (split_mode_enabled) + vpss_lb_size = size_vpss_lb(width, height, num_vpp_pipes); + + size += vpss_lb_size; + + return size; +} + +static u32 +calculate_enc_scratch1_size(u32 width, u32 height, u32 lcu_size, u32 num_ref, + bool ten_bit, u32 num_vpp_pipes, bool is_h265) +{ + u32 line_buf_ctrl_size, line_buf_data_size, leftline_buf_ctrl_size; + u32 line_buf_sde_size, sps_pps_slice_hdr, topline_buf_ctrl_size_FE; + u32 leftline_buf_ctrl_size_FE, line_buf_recon_pix_size; + u32 leftline_buf_recon_pix_size, lambda_lut_size, override_buffer_size; + u32 col_mv_buf_size, vpp_reg_buffer_size, ir_buffer_size; + u32 vpss_line_buf, leftline_buf_meta_recony, h265e_colrcbuf_size; + u32 h265e_framerc_bufsize, h265e_lcubitcnt_bufsize; + u32 h265e_lcubitmap_bufsize, se_stats_bufsize; + u32 bse_reg_buffer_size, bse_slice_cmd_buffer_size, slice_info_bufsize; + u32 line_buf_ctrl_size_buffid2, slice_cmd_buffer_size; + u32 width_lcu_num, height_lcu_num, width_coded, height_coded; + u32 frame_num_lcu, linebuf_meta_recon_uv, topline_bufsize_fe_1stg_sao; + u32 size, bit_depth, num_lcu_mb; + u32 vpss_line_buffer_size_1; + + width_lcu_num = (width + lcu_size - 1) / lcu_size; + height_lcu_num = (height + lcu_size - 1) / lcu_size; + frame_num_lcu = width_lcu_num * height_lcu_num; + width_coded = width_lcu_num * lcu_size; + height_coded = height_lcu_num * lcu_size; + num_lcu_mb = (height_coded / lcu_size) * + ((width_coded + lcu_size * 8) / lcu_size); + slice_info_bufsize = 256 + (frame_num_lcu << 4); + slice_info_bufsize = ALIGN(slice_info_bufsize, HFI_DMA_ALIGNMENT); + line_buf_ctrl_size = ALIGN(width_coded, HFI_DMA_ALIGNMENT); + line_buf_ctrl_size_buffid2 = ALIGN(width_coded, HFI_DMA_ALIGNMENT); + + bit_depth = ten_bit ? 10 : 8; + line_buf_data_size = + (((((bit_depth * width_coded + 1024) + + (HFI_DMA_ALIGNMENT - 1)) & (~(HFI_DMA_ALIGNMENT - 1))) * 1) + + (((((bit_depth * width_coded + 1024) >> 1) + + (HFI_DMA_ALIGNMENT - 1)) & (~(HFI_DMA_ALIGNMENT - 1))) * 2)); + + leftline_buf_ctrl_size = is_h265 ? + ((height_coded + 32) / 32 * 4 * 16) : + ((height_coded + 15) / 16 * 5 * 16); + + if (num_vpp_pipes > 1) { + leftline_buf_ctrl_size += 512; + leftline_buf_ctrl_size = + ALIGN(leftline_buf_ctrl_size, 512) * num_vpp_pipes; + } + + leftline_buf_ctrl_size = + ALIGN(leftline_buf_ctrl_size, HFI_DMA_ALIGNMENT); + leftline_buf_recon_pix_size = (((ten_bit + 1) * 2 * + (height_coded) + HFI_DMA_ALIGNMENT) + + (HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1) & + (~((HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1)) * 1; + + topline_buf_ctrl_size_FE = is_h265 ? (64 * (width_coded >> 5)) : + (HFI_DMA_ALIGNMENT + 16 * (width_coded >> 4)); + topline_buf_ctrl_size_FE = + ALIGN(topline_buf_ctrl_size_FE, HFI_DMA_ALIGNMENT); + leftline_buf_ctrl_size_FE = + (((HFI_DMA_ALIGNMENT + 64 * (height_coded >> 4)) + + (HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1) & + (~((HFI_DMA_ALIGNMENT << (num_vpp_pipes - 1)) - 1)) * 1) * + num_vpp_pipes; + leftline_buf_meta_recony = (HFI_DMA_ALIGNMENT + 64 * + ((height_coded) / (8 * (ten_bit ? 4 : 8)))); + leftline_buf_meta_recony = + ALIGN(leftline_buf_meta_recony, HFI_DMA_ALIGNMENT); + leftline_buf_meta_recony = leftline_buf_meta_recony * num_vpp_pipes; + linebuf_meta_recon_uv = (HFI_DMA_ALIGNMENT + 64 * + ((height_coded) / (4 * (ten_bit ? 4 : 8)))); + linebuf_meta_recon_uv = ALIGN(linebuf_meta_recon_uv, HFI_DMA_ALIGNMENT); + linebuf_meta_recon_uv = linebuf_meta_recon_uv * num_vpp_pipes; + line_buf_recon_pix_size = ((ten_bit ? 3 : 2) * width_coded); + line_buf_recon_pix_size = + ALIGN(line_buf_recon_pix_size, HFI_DMA_ALIGNMENT); + slice_cmd_buffer_size = ALIGN(20480, HFI_DMA_ALIGNMENT); + sps_pps_slice_hdr = 2048 + 4096; + col_mv_buf_size = is_h265 ? (16 * ((frame_num_lcu << 2) + 32)) : + (3 * 16 * (width_lcu_num * height_lcu_num + 32)); + col_mv_buf_size = + ALIGN(col_mv_buf_size, HFI_DMA_ALIGNMENT) * (num_ref + 1); + h265e_colrcbuf_size = + (((width_lcu_num + 7) >> 3) * 16 * 2 * height_lcu_num); + if (num_vpp_pipes > 1) + h265e_colrcbuf_size = + ALIGN(h265e_colrcbuf_size, HFI_DMA_ALIGNMENT) * + num_vpp_pipes; + + h265e_colrcbuf_size = ALIGN(h265e_colrcbuf_size, HFI_DMA_ALIGNMENT) * + HFI_MAX_COL_FRAME; + h265e_framerc_bufsize = (is_h265) ? (256 + 16 * + (14 + (((height_coded >> 5) + 7) >> 3))) : + (256 + 16 * (14 + (((height_coded >> 4) + 7) >> 3))); + h265e_framerc_bufsize *= 6; /* multiply by max numtilescol */ + if (num_vpp_pipes > 1) + h265e_framerc_bufsize = + ALIGN(h265e_framerc_bufsize, HFI_DMA_ALIGNMENT) * + num_vpp_pipes; + + h265e_framerc_bufsize = ALIGN(h265e_framerc_bufsize, 512) * + HFI_MAX_COL_FRAME; + h265e_lcubitcnt_bufsize = 256 + 4 * frame_num_lcu; + h265e_lcubitcnt_bufsize = + ALIGN(h265e_lcubitcnt_bufsize, HFI_DMA_ALIGNMENT); + h265e_lcubitmap_bufsize = 256 + (frame_num_lcu >> 3); + h265e_lcubitmap_bufsize = + ALIGN(h265e_lcubitmap_bufsize, HFI_DMA_ALIGNMENT); + line_buf_sde_size = 256 + 16 * (width_coded >> 4); + line_buf_sde_size = ALIGN(line_buf_sde_size, HFI_DMA_ALIGNMENT); + if ((width_coded * height_coded) > (4096 * 2160)) + se_stats_bufsize = 0; + else if ((width_coded * height_coded) > (1920 * 1088)) + se_stats_bufsize = (40 * 4 * frame_num_lcu + 256 + 256); + else + se_stats_bufsize = (1024 * frame_num_lcu + 256 + 256); + + se_stats_bufsize = ALIGN(se_stats_bufsize, HFI_DMA_ALIGNMENT) * 2; + bse_slice_cmd_buffer_size = (((8192 << 2) + 7) & (~7)) * 6; + bse_reg_buffer_size = (((512 << 3) + 7) & (~7)) * 4; + vpp_reg_buffer_size = + (((HFI_VENUS_VPPSG_MAX_REGISTERS << 3) + 31) & (~31)) * 10; + lambda_lut_size = 256 * 11; + override_buffer_size = 16 * ((num_lcu_mb + 7) >> 3); + override_buffer_size = + ALIGN(override_buffer_size, HFI_DMA_ALIGNMENT) * 2; + ir_buffer_size = (((frame_num_lcu << 1) + 7) & (~7)) * 3; + vpss_line_buffer_size_1 = (((8192 >> 2) << 5) * num_vpp_pipes) + 64; + vpss_line_buf = + (((((max(width_coded, height_coded) + 3) >> 2) << 5) + 256) * + 16) + vpss_line_buffer_size_1; + topline_bufsize_fe_1stg_sao = 16 * (width_coded >> 5); + topline_bufsize_fe_1stg_sao = + ALIGN(topline_bufsize_fe_1stg_sao, HFI_DMA_ALIGNMENT); + + size = + line_buf_ctrl_size + line_buf_data_size + + line_buf_ctrl_size_buffid2 + leftline_buf_ctrl_size + + vpss_line_buf + col_mv_buf_size + topline_buf_ctrl_size_FE + + leftline_buf_ctrl_size_FE + line_buf_recon_pix_size + + leftline_buf_recon_pix_size + + leftline_buf_meta_recony + linebuf_meta_recon_uv + + h265e_colrcbuf_size + h265e_framerc_bufsize + + h265e_lcubitcnt_bufsize + h265e_lcubitmap_bufsize + + line_buf_sde_size + + topline_bufsize_fe_1stg_sao + override_buffer_size + + bse_reg_buffer_size + vpp_reg_buffer_size + sps_pps_slice_hdr + + slice_cmd_buffer_size + bse_slice_cmd_buffer_size + + ir_buffer_size + slice_info_bufsize + lambda_lut_size + + se_stats_bufsize + 1024; + + return size; +} + +static u32 h264e_scratch1_size(u32 width, u32 height, u32 num_ref, bool ten_bit, + u32 num_vpp_pipes) +{ + return calculate_enc_scratch1_size(width, height, 16, num_ref, ten_bit, + num_vpp_pipes, false); +} + +static u32 h265e_scratch1_size(u32 width, u32 height, u32 num_ref, bool ten_bit, + u32 num_vpp_pipes) +{ + return calculate_enc_scratch1_size(width, height, 32, num_ref, ten_bit, + num_vpp_pipes, true); +} + +static u32 vp8e_scratch1_size(u32 width, u32 height, u32 num_ref, bool ten_bit, + u32 num_vpp_pipes) +{ + return calculate_enc_scratch1_size(width, height, 16, num_ref, ten_bit, + 1, false); +} + +static u32 ubwc_metadata_plane_stride(u32 width, u32 metadata_stride_multi, + u32 tile_width_pels) +{ + return ALIGN(((width + (tile_width_pels - 1)) / tile_width_pels), + metadata_stride_multi); +} + +static u32 ubwc_metadata_plane_bufheight(u32 height, u32 metadata_height_multi, + u32 tile_height_pels) +{ + return ALIGN(((height + (tile_height_pels - 1)) / tile_height_pels), + metadata_height_multi); +} + +static u32 ubwc_metadata_plane_buffer_size(u32 metadata_stride, + u32 metadata_buf_height) +{ + return ALIGN(metadata_stride * metadata_buf_height, SZ_4K); +} + +static u32 enc_scratch2_size(u32 width, u32 height, u32 num_ref, bool ten_bit) +{ + u32 aligned_width, aligned_height, chroma_height, ref_buf_height; + u32 luma_size, chroma_size; + u32 metadata_stride, meta_buf_height, meta_size_y, meta_size_c; + u32 ref_luma_stride_bytes, ref_chroma_height_bytes; + u32 ref_buf_size, ref_stride; + u32 size; + + if (!ten_bit) { + aligned_height = ALIGN(height, HFI_VENUS_HEIGHT_ALIGNMENT); + chroma_height = height >> 1; + chroma_height = ALIGN(chroma_height, + HFI_VENUS_HEIGHT_ALIGNMENT); + aligned_width = ALIGN(width, HFI_VENUS_WIDTH_ALIGNMENT); + metadata_stride = + ubwc_metadata_plane_stride(width, 64, + NV12_UBWC_Y_TILE_WIDTH); + meta_buf_height = + ubwc_metadata_plane_bufheight(height, 16, + NV12_UBWC_Y_TILE_HEIGHT); + meta_size_y = ubwc_metadata_plane_buffer_size(metadata_stride, + meta_buf_height); + meta_size_c = ubwc_metadata_plane_buffer_size(metadata_stride, + meta_buf_height); + size = (aligned_height + chroma_height) * aligned_width + + meta_size_y + meta_size_c; + size = (size * (num_ref + 3)) + 4096; + } else { + ref_buf_height = (height + (HFI_VENUS_HEIGHT_ALIGNMENT - 1)) + & (~(HFI_VENUS_HEIGHT_ALIGNMENT - 1)); + ref_luma_stride_bytes = + ((width + SYSTEM_LAL_TILE10 - 1) / SYSTEM_LAL_TILE10) * + SYSTEM_LAL_TILE10; + ref_stride = 4 * (ref_luma_stride_bytes / 3); + ref_stride = (ref_stride + (128 - 1)) & (~(128 - 1)); + luma_size = ref_buf_height * ref_stride; + ref_chroma_height_bytes = (((height + 1) >> 1) + + (32 - 1)) & (~(32 - 1)); + chroma_size = ref_stride * ref_chroma_height_bytes; + luma_size = (luma_size + (SZ_4K - 1)) & (~(SZ_4K - 1)); + chroma_size = (chroma_size + (SZ_4K - 1)) & (~(SZ_4K - 1)); + ref_buf_size = luma_size + chroma_size; + metadata_stride = + ubwc_metadata_plane_stride(width, + METADATA_STRIDE_MULTIPLE, + TP10_UBWC_Y_TILE_WIDTH); + meta_buf_height = + ubwc_metadata_plane_bufheight(height, + METADATA_HEIGHT_MULTIPLE, + TP10_UBWC_Y_TILE_HEIGHT); + meta_size_y = ubwc_metadata_plane_buffer_size(metadata_stride, + meta_buf_height); + meta_size_c = ubwc_metadata_plane_buffer_size(metadata_stride, + meta_buf_height); + size = ref_buf_size + meta_size_y + meta_size_c; + size = (size * (num_ref + 3)) + 4096; + } + + return size; +} + +static u32 enc_persist_size(void) +{ + return HFI_IRIS2_ENC_PERSIST_SIZE; +} + +static u32 h264d_persist1_size(void) +{ + return ALIGN((SIZE_SLIST_BUF_H264 * NUM_SLIST_BUF_H264), + HFI_DMA_ALIGNMENT); +} + +static u32 h265d_persist1_size(void) +{ + return ALIGN((SIZE_SLIST_BUF_H265 * NUM_SLIST_BUF_H265 + H265_NUM_TILE + * sizeof(u32)), HFI_DMA_ALIGNMENT); +} + +static u32 vp8d_persist1_size(void) +{ + return ALIGN(VP8_NUM_PROBABILITY_TABLE_BUF * VP8_PROB_TABLE_SIZE, + HFI_DMA_ALIGNMENT); +} + +static u32 vp9d_persist1_size(void) +{ + return + ALIGN(VP9_NUM_PROBABILITY_TABLE_BUF * VP9_PROB_TABLE_SIZE, + HFI_DMA_ALIGNMENT) + + ALIGN(HFI_IRIS2_VP9D_COMV_SIZE, HFI_DMA_ALIGNMENT) + + ALIGN(MAX_SUPERFRAME_HEADER_LEN, HFI_DMA_ALIGNMENT) + + ALIGN(VP9_UDC_HEADER_BUF_SIZE, HFI_DMA_ALIGNMENT) + + ALIGN(VP9_NUM_FRAME_INFO_BUF * CCE_TILE_OFFSET_SIZE, + HFI_DMA_ALIGNMENT); +} + +static u32 mpeg2d_persist1_size(void) +{ + return QMATRIX_SIZE + MP2D_QPDUMP_SIZE; +} + +struct dec_bufsize_ops { + u32 (*scratch)(u32 width, u32 height, bool is_interlaced); + u32 (*scratch1)(u32 width, u32 height, u32 min_buf_count, + bool split_mode_enabled, u32 num_vpp_pipes); + u32 (*persist1)(void); +}; + +struct enc_bufsize_ops { + u32 (*scratch)(u32 width, u32 height, u32 work_mode, u32 num_vpp_pipes, + u32 rc_type); + u32 (*scratch1)(u32 width, u32 height, u32 num_ref, bool ten_bit, + u32 num_vpp_pipes); + u32 (*scratch2)(u32 width, u32 height, u32 num_ref, bool ten_bit); + u32 (*persist)(void); +}; + +static struct dec_bufsize_ops dec_h264_ops = { + .scratch = h264d_scratch_size, + .scratch1 = h264d_scratch1_size, + .persist1 = h264d_persist1_size, +}; + +static struct dec_bufsize_ops dec_h265_ops = { + .scratch = h265d_scratch_size, + .scratch1 = h265d_scratch1_size, + .persist1 = h265d_persist1_size, +}; + +static struct dec_bufsize_ops dec_vp8_ops = { + .scratch = vpxd_scratch_size, + .scratch1 = vp8d_scratch1_size, + .persist1 = vp8d_persist1_size, +}; + +static struct dec_bufsize_ops dec_vp9_ops = { + .scratch = vpxd_scratch_size, + .scratch1 = vp9d_scratch1_size, + .persist1 = vp9d_persist1_size, +}; + +static struct dec_bufsize_ops dec_mpeg2_ops = { + .scratch = mpeg2d_scratch_size, + .scratch1 = mpeg2d_scratch1_size, + .persist1 = mpeg2d_persist1_size, +}; + +static struct enc_bufsize_ops enc_h264_ops = { + .scratch = h264e_scratch_size, + .scratch1 = h264e_scratch1_size, + .scratch2 = enc_scratch2_size, + .persist = enc_persist_size, +}; + +static struct enc_bufsize_ops enc_h265_ops = { + .scratch = h265e_scratch_size, + .scratch1 = h265e_scratch1_size, + .scratch2 = enc_scratch2_size, + .persist = enc_persist_size, +}; + +static struct enc_bufsize_ops enc_vp8_ops = { + .scratch = vp8e_scratch_size, + .scratch1 = vp8e_scratch1_size, + .scratch2 = enc_scratch2_size, + .persist = enc_persist_size, +}; + +static u32 +calculate_dec_input_frame_size(u32 width, u32 height, u32 codec, + u32 max_mbs_per_frame, u32 buffer_size_limit) +{ + u32 frame_size, num_mbs; + u32 div_factor = 1; + u32 base_res_mbs = NUM_MBS_4K; + + /* + * Decoder input size calculation: + * If clip is 8k buffer size is calculated for 8k : 8k mbs/4 + * For 8k cases we expect width/height to be set always. + * In all other cases size is calculated for 4k: + * 4k mbs for VP8/VP9 and 4k/2 for remaining codecs + */ + num_mbs = (ALIGN(height, 16) * ALIGN(width, 16)) / 256; + if (num_mbs > NUM_MBS_4K) { + div_factor = 4; + base_res_mbs = max_mbs_per_frame; + } else { + base_res_mbs = NUM_MBS_4K; + if (codec == V4L2_PIX_FMT_VP9) + div_factor = 1; + else + div_factor = 2; + } + + frame_size = base_res_mbs * MB_SIZE_IN_PIXEL * 3 / 2 / div_factor; + + /* multiply by 10/8 (1.25) to get size for 10 bit case */ + if (codec == V4L2_PIX_FMT_VP9 || codec == V4L2_PIX_FMT_HEVC) + frame_size = frame_size + (frame_size >> 2); + + if (buffer_size_limit && buffer_size_limit < frame_size) + frame_size = buffer_size_limit; + + return ALIGN(frame_size, SZ_4K); +} + +static int output_buffer_count(u32 session_type, u32 codec) +{ + u32 output_min_count; + + if (session_type == VIDC_SESSION_TYPE_DEC) { + switch (codec) { + case V4L2_PIX_FMT_MPEG2: + case V4L2_PIX_FMT_VP8: + output_min_count = 6; + break; + case V4L2_PIX_FMT_VP9: + output_min_count = 9; + break; + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_HEVC: + default: + output_min_count = 8; + break; + } + } else { + output_min_count = MIN_ENC_OUTPUT_BUFFERS; + } + + return output_min_count; +} + +static int bufreq_dec(struct hfi_plat_buffers_params *params, u32 buftype, + struct hfi_buffer_requirements *bufreq) +{ + enum hfi_version version = params->version; + u32 codec = params->codec; + u32 width = params->width, height = params->height, out_min_count; + struct dec_bufsize_ops *dec_ops; + bool is_secondary_output = params->dec.is_secondary_output; + bool is_interlaced = params->dec.is_interlaced; + u32 max_mbs_per_frame = params->dec.max_mbs_per_frame; + u32 buffer_size_limit = params->dec.buffer_size_limit; + u32 num_vpp_pipes = params->num_vpp_pipes; + + switch (codec) { + case V4L2_PIX_FMT_H264: + dec_ops = &dec_h264_ops; + break; + case V4L2_PIX_FMT_HEVC: + dec_ops = &dec_h265_ops; + break; + case V4L2_PIX_FMT_VP8: + dec_ops = &dec_vp8_ops; + break; + case V4L2_PIX_FMT_VP9: + dec_ops = &dec_vp9_ops; + break; + case V4L2_PIX_FMT_MPEG2: + dec_ops = &dec_mpeg2_ops; + break; + default: + return -EINVAL; + } + + out_min_count = output_buffer_count(VIDC_SESSION_TYPE_DEC, codec); + + bufreq->type = buftype; + bufreq->region_size = 0; + bufreq->count_min = 1; + bufreq->count_actual = 1; + bufreq->hold_count = 1; + bufreq->contiguous = 1; + bufreq->alignment = 256; + + if (buftype == HFI_BUFFER_INPUT) { + bufreq->count_min = MIN_INPUT_BUFFERS; + bufreq->size = + calculate_dec_input_frame_size(width, height, codec, + max_mbs_per_frame, + buffer_size_limit); + } else if (buftype == HFI_BUFFER_OUTPUT || + buftype == HFI_BUFFER_OUTPUT2) { + bufreq->count_min = out_min_count; + bufreq->size = + venus_helper_get_framesz_raw(params->hfi_color_fmt, + width, height); + } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH(version)) { + bufreq->size = dec_ops->scratch(width, height, is_interlaced); + } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH_1(version)) { + bufreq->size = dec_ops->scratch1(width, height, out_min_count, + is_secondary_output, + num_vpp_pipes); + } else if (buftype == HFI_BUFFER_INTERNAL_PERSIST_1) { + bufreq->size = dec_ops->persist1(); + } else { + return -EINVAL; + } + + return 0; +} + +int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype, + struct hfi_buffer_requirements *bufreq) +{ + enum hfi_version version = params->version; + struct enc_bufsize_ops *enc_ops; + u32 width = params->width; + u32 height = params->height; + bool is_tenbit = params->enc.is_tenbit; + u32 num_bframes = params->enc.num_b_frames; + u32 codec = params->codec; + u32 work_mode = params->enc.work_mode; + u32 rc_type = params->enc.rc_type; + u32 num_vpp_pipes = params->num_vpp_pipes; + u32 num_ref; + + switch (codec) { + case V4L2_PIX_FMT_H264: + enc_ops = &enc_h264_ops; + break; + case V4L2_PIX_FMT_HEVC: + enc_ops = &enc_h265_ops; + break; + case V4L2_PIX_FMT_VP8: + enc_ops = &enc_vp8_ops; + break; + default: + return -EINVAL; + } + + num_ref = num_bframes > 0 ? num_bframes + 1 : 1; + + bufreq->type = buftype; + bufreq->region_size = 0; + bufreq->count_min = 1; + bufreq->count_actual = 1; + bufreq->hold_count = 1; + bufreq->contiguous = 1; + bufreq->alignment = 256; + + if (buftype == HFI_BUFFER_INPUT) { + bufreq->count_min = MIN_INPUT_BUFFERS; + bufreq->size = + venus_helper_get_framesz_raw(params->hfi_color_fmt, + width, height); + } else if (buftype == HFI_BUFFER_OUTPUT || + buftype == HFI_BUFFER_OUTPUT2) { + bufreq->count_min = + output_buffer_count(VIDC_SESSION_TYPE_ENC, codec); + bufreq->size = calculate_enc_output_frame_size(width, height, + rc_type); + } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH(version)) { + bufreq->size = enc_ops->scratch(width, height, work_mode, + num_vpp_pipes, rc_type); + } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH_1(version)) { + bufreq->size = enc_ops->scratch1(width, height, num_ref, + is_tenbit, num_vpp_pipes); + } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH_2(version)) { + bufreq->size = enc_ops->scratch2(width, height, num_ref, + is_tenbit); + } else if (buftype == HFI_BUFFER_INTERNAL_PERSIST) { + bufreq->size = enc_ops->persist(); + } else { + return -EINVAL; + } + + return 0; +} + +int hfi_plat_bufreq_v6(struct hfi_plat_buffers_params *params, u32 session_type, + u32 buftype, struct hfi_buffer_requirements *bufreq) +{ + if (session_type == VIDC_SESSION_TYPE_DEC) + return bufreq_dec(params, buftype, bufreq); + else + return bufreq_enc(params, buftype, bufreq); +} diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c index e76d69a66b6f..2278be13cb90 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c @@ -322,4 +322,5 @@ const struct hfi_platform hfi_plat_v6 = { .codecs = get_codecs, .capabilities = get_capabilities, .num_vpp_pipes = num_vpp_pipes, + .bufreq = hfi_plat_bufreq_v6, }; -- cgit v1.2.3 From e29929266be1ac0e40121f56b5c13b52c281db06 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 26 Aug 2020 14:39:13 +0200 Subject: media: venus: Get codecs and capabilities from hfi platform Wire up hfi platform codec and capabilities instead of getting them from firmware. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_parser.c | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 1c91801d87aa..2c63988cb321 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -228,11 +228,49 @@ static void parser_fini(struct venus_inst *inst, u32 codecs, u32 domain) } } +static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) +{ + const struct hfi_platform *plat; + const struct hfi_plat_caps *caps = NULL; + u32 enc_codecs, dec_codecs, count; + unsigned int entries; + + if (inst) + return 0; + + plat = hfi_platform_get(core->res->hfi_version); + if (!plat) + return -EINVAL; + + if (plat->codecs) + plat->codecs(&enc_codecs, &dec_codecs, &count); + + if (plat->capabilities) + caps = plat->capabilities(&entries); + + if (!caps || !entries || !count) + return -EINVAL; + + core->enc_codecs = enc_codecs; + core->dec_codecs = dec_codecs; + core->codecs_count = count; + core->max_sessions_supported = MAX_SESSIONS; + memset(core->caps, 0, sizeof(*caps) * MAX_CODEC_NUM); + memcpy(core->caps, caps, sizeof(*caps) * entries); + + return 0; +} + u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf, u32 size) { unsigned int words_count = size >> 2; u32 *word = buf, *data, codecs = 0, domain = 0; + int ret; + + ret = hfi_platform_parser(core, inst); + if (!ret) + return HFI_ERR_NONE; if (size % 4) return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; -- cgit v1.2.3 From b8201f3ebc4cbfd949a3bcf583b25a484e21f2f0 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 26 Aug 2020 14:42:52 +0200 Subject: media: venus: vdec,core: Handle picture structure event Handle progressive/interlaced bitstream event by similar way as bit depth. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/vdec.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 8e14c39695ba..dfc13b2f371f 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -375,6 +375,7 @@ struct venus_inst { union hfi_get_property hprop; unsigned int core_acquired: 1; unsigned int bit_depth; + unsigned int pic_struct; bool next_buf_last; bool drain_active; }; diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index fd06416521a0..e4dc97f00fc3 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1371,6 +1371,9 @@ static void vdec_event_change(struct venus_inst *inst, if (inst->bit_depth != ev_data->bit_depth) inst->bit_depth = ev_data->bit_depth; + if (inst->pic_struct != ev_data->pic_struct) + inst->pic_struct = ev_data->pic_struct; + dev_dbg(dev, VDBGM "event %s sufficient resources (%ux%u)\n", sufficient ? "" : "not", ev_data->width, ev_data->height); @@ -1539,6 +1542,7 @@ static int vdec_open(struct file *file) inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; inst->core_acquired = false; inst->bit_depth = VIDC_BITDEPTH_8; + inst->pic_struct = HFI_INTERLACE_FRAME_PROGRESSIVE; init_waitqueue_head(&inst->reconf_wait); venus_helper_init_instance(inst); -- cgit v1.2.3 From 7371093f983d35d60a7fac3a6f082de7fefe3648 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 26 Aug 2020 14:44:15 +0200 Subject: media: venus: helpers: Wire up hfi platform buffer requirements Now when everything is in place wire up buffer requirements from hfi platform buffers to the buffer requirements helper. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 50 ++++++++++++++++++++++++++ drivers/media/platform/qcom/venus/hfi_parser.h | 5 +++ 2 files changed, 55 insertions(+) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 4b1978625f1b..a20b45f35f3d 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -16,6 +16,7 @@ #include "hfi_helper.h" #include "pm_helpers.h" #include "hfi_platform.h" +#include "hfi_parser.h" struct intbuf { struct list_head list; @@ -553,6 +554,51 @@ static u32 to_hfi_raw_fmt(u32 v4l2_fmt) return 0; } +static int platform_get_bufreq(struct venus_inst *inst, u32 buftype, + struct hfi_buffer_requirements *req) +{ + enum hfi_version version = inst->core->res->hfi_version; + const struct hfi_platform *hfi_plat; + struct hfi_plat_buffers_params params; + bool is_dec = inst->session_type == VIDC_SESSION_TYPE_DEC; + struct venc_controls *enc_ctr = &inst->controls.enc; + + hfi_plat = hfi_platform_get(version); + + if (!hfi_plat || !hfi_plat->bufreq) + return -EINVAL; + + params.version = version; + params.num_vpp_pipes = hfi_platform_num_vpp_pipes(version); + + if (is_dec) { + params.width = inst->width; + params.height = inst->height; + params.codec = inst->fmt_out->pixfmt; + params.hfi_color_fmt = to_hfi_raw_fmt(inst->fmt_cap->pixfmt); + params.dec.max_mbs_per_frame = mbs_per_frame_max(inst); + params.dec.buffer_size_limit = 0; + params.dec.is_secondary_output = + inst->opb_buftype == HFI_BUFFER_OUTPUT2; + params.dec.is_interlaced = + inst->pic_struct != HFI_INTERLACE_FRAME_PROGRESSIVE ? + true : false; + } else { + params.width = inst->out_width; + params.height = inst->out_height; + params.codec = inst->fmt_cap->pixfmt; + params.hfi_color_fmt = to_hfi_raw_fmt(inst->fmt_out->pixfmt); + params.enc.work_mode = VIDC_WORK_MODE_2; + params.enc.rc_type = HFI_RATE_CONTROL_OFF; + if (enc_ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ) + params.enc.rc_type = HFI_RATE_CONTROL_CQ; + params.enc.num_b_frames = enc_ctr->num_b_frames; + params.enc.is_tenbit = inst->bit_depth == VIDC_BITDEPTH_10; + } + + return hfi_plat->bufreq(¶ms, inst->session_type, buftype, req); +} + int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, struct hfi_buffer_requirements *req) { @@ -564,6 +610,10 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, if (req) memset(req, 0, sizeof(*req)); + ret = platform_get_bufreq(inst, type, req); + if (!ret) + return 0; + ret = hfi_session_get_property(inst, ptype, &hprop); if (ret) return ret; diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h index 7f59d82110f9..5751d0140700 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.h +++ b/drivers/media/platform/qcom/venus/hfi_parser.h @@ -112,4 +112,9 @@ static inline u32 core_num_max(struct venus_inst *inst) return cap_max(inst, HFI_CAPABILITY_MAX_VIDEOCORES); } +static inline u32 mbs_per_frame_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_MBS_PER_FRAME); +} + #endif -- cgit v1.2.3 From 4487e0215560392bd11c9de08d60824d72c89cd9 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sun, 27 Dec 2020 14:45:01 +0100 Subject: media: ir_toy: add another IR Droid device This device is also supported. Cc: stable@vger.kernel.org Tested-by: Georgi Bakalski Reported-by: Georgi Bakalski Signed-off-by: Sean Young Reviewed-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir_toy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c index e0242c9b6aeb..3e729a17b35f 100644 --- a/drivers/media/rc/ir_toy.c +++ b/drivers/media/rc/ir_toy.c @@ -491,6 +491,7 @@ static void irtoy_disconnect(struct usb_interface *intf) static const struct usb_device_id irtoy_table[] = { { USB_DEVICE_INTERFACE_CLASS(0x04d8, 0xfd08, USB_CLASS_CDC_DATA) }, + { USB_DEVICE_INTERFACE_CLASS(0x04d8, 0xf58b, USB_CLASS_CDC_DATA) }, { } }; -- cgit v1.2.3 From 1b43bad31fb0e00f45baf5b05bd21eb8d8ce7f58 Mon Sep 17 00:00:00 2001 From: James Reynolds Date: Tue, 22 Dec 2020 13:07:04 +0100 Subject: media: mceusb: Fix potential out-of-bounds shift When processing a MCE_RSP_GETPORTSTATUS command, the bit index to set in ir->txports_cabled comes from response data, and isn't validated. As ir->txports_cabled is a u8, nothing should be done if the bit index is greater than 7. Cc: stable@vger.kernel.org Reported-by: syzbot+ec3b3128c576e109171d@syzkaller.appspotmail.com Signed-off-by: James Reynolds Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index f1dbd059ed08..c8d63673e131 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1169,7 +1169,7 @@ static void mceusb_handle_command(struct mceusb_dev *ir, u8 *buf_in) switch (subcmd) { /* the one and only 5-byte return value command */ case MCE_RSP_GETPORTSTATUS: - if (buf_in[5] == 0) + if (buf_in[5] == 0 && *hi < 8) ir->txports_cabled |= 1 << *hi; break; -- cgit v1.2.3 From 9db0fcde36e3f3cc844be77de311d569212c545d Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Tue, 22 Dec 2020 14:32:14 +0100 Subject: media: cxd2841er: use DIV_ROUND_UP to calculate timeout Don't open-code DIV_ROUND_UP() kernel macro. Signed-off-by: Zheng Yongjun Reported-by: kernel test robot Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/cxd2841er.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 758c95bc3b11..5431f922f55e 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -3338,7 +3338,7 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe) cxd2841er_tuner_set(fe); cxd2841er_tune_done(priv); - timeout = ((3000000 + (symbol_rate - 1)) / symbol_rate) + 150; + timeout = DIV_ROUND_UP(3000000, symbol_rate) + 150; i = 0; do { -- cgit v1.2.3 From ae56e038f72d8192ecb63233520c86c52c1d9ce6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 5 Jan 2021 18:12:28 +0100 Subject: media: ccs: Make (non-)use of uninitialised variables more robust GCC with W=2 level of kernel compiler warnings warns about the use of uninitialised variables in a few locations. While these uninitialised variables were not used in reality, this still produced compiler warnings. Address this by assigning the variables to NULL and checking for NULL in places it is not expected, returning -EIO in that case. This provides at least some sanity checking at runtime as the compiler appears unable to do that at compile time. Reported-by: Hans Verkuil Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ccs/ccs-data.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-data.c b/drivers/media/i2c/ccs/ccs-data.c index 59338a6704af..8444be7247b2 100644 --- a/drivers/media/i2c/ccs/ccs-data.c +++ b/drivers/media/i2c/ccs/ccs-data.c @@ -214,7 +214,7 @@ static int ccs_data_parse_regs(struct bin_container *bin, size_t *__num_regs, const void *payload, const void *endp, struct device *dev) { - struct ccs_reg *regs_base, *regs; + struct ccs_reg *regs_base = NULL, *regs = NULL; size_t num_regs = 0; u16 addr = 0; @@ -285,6 +285,9 @@ static int ccs_data_parse_regs(struct bin_container *bin, if (!bin->base) { bin_reserve(bin, len); } else if (__regs) { + if (!regs) + return -EIO; + regs->addr = addr; regs->len = len; regs->value = bin_alloc(bin, len); @@ -305,8 +308,12 @@ static int ccs_data_parse_regs(struct bin_container *bin, if (__num_regs) *__num_regs = num_regs; - if (bin->base && __regs) + if (bin->base && __regs) { + if (!regs_base) + return -EIO; + *__regs = regs_base; + } return 0; } @@ -425,7 +432,7 @@ static int ccs_data_parse_rules(struct bin_container *bin, size_t *__num_rules, const void *payload, const void *endp, struct device *dev) { - struct ccs_rule *rules_base, *rules = NULL, *next_rule; + struct ccs_rule *rules_base = NULL, *rules = NULL, *next_rule = NULL; size_t num_rules = 0; const void *__next_rule = payload; int rval; @@ -483,6 +490,9 @@ static int ccs_data_parse_rules(struct bin_container *bin, } else { unsigned int i; + if (!next_rule) + return -EIO; + rules = next_rule; next_rule++; @@ -555,6 +565,9 @@ static int ccs_data_parse_rules(struct bin_container *bin, bin_reserve(bin, sizeof(*rules) * num_rules); *__num_rules = num_rules; } else { + if (!rules_base) + return -EIO; + *__rules = rules_base; } @@ -690,7 +703,7 @@ static int ccs_data_parse_pdaf(struct bin_container *bin, struct ccs_pdaf_pix_lo } for (i = 0; i < max_block_type_id; i++) { - struct ccs_pdaf_pix_loc_pixel_desc_group *pdgroup; + struct ccs_pdaf_pix_loc_pixel_desc_group *pdgroup = NULL; unsigned int j; if (!is_contained(__num_pixel_descs, endp)) @@ -721,6 +734,9 @@ static int ccs_data_parse_pdaf(struct bin_container *bin, struct ccs_pdaf_pix_lo if (!bin->base) continue; + if (!pdgroup) + return -EIO; + pdesc = &pdgroup->descs[j]; pdesc->pixel_type = __pixel_desc->pixel_type; pdesc->small_offset_x = __pixel_desc->small_offset_x; -- cgit v1.2.3 From 99d0cbe4be78ca33f06089521a8a0d76a20cdbb7 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Thu, 24 Dec 2020 12:25:33 +0100 Subject: media: v4l2-ctrl: Add frame-specific min/max qp controls for hevc - Adds min/max qp controls for B frame for h264. - Adds min/max qp controls for I/P/B frames for hevc similar to h264. - Update valid range of min/max qp for hevc to accommodate 10 bit. Signed-off-by: Dikshita Agarwal Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../userspace-api/media/v4l/ext-ctrls-codec.rst | 52 +++++++++++++++++++++- drivers/media/v4l2-core/v4l2-ctrls.c | 8 ++++ include/uapi/linux/v4l2-controls.h | 9 ++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst index 454ecd9a0f83..90c60add3fc0 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst @@ -1182,6 +1182,18 @@ enum v4l2_mpeg_video_h264_entropy_mode - V4L2_CID_MPEG_VIDEO_H264_MAX_QP is also set, the quantization parameter should be chosen to meet both requirements. +``V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP (integer)`` + Minimum quantization parameter for the H264 B frame to limit B frame + quality to a range. Valid range: from 0 to 51. If + V4L2_CID_MPEG_VIDEO_H264_MIN_QP is also set, the quantization parameter + should be chosen to meet both requirements. + +``V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP (integer)`` + Maximum quantization parameter for the H264 B frame to limit B frame + quality to a range. Valid range: from 0 to 51. If + V4L2_CID_MPEG_VIDEO_H264_MAX_QP is also set, the quantization parameter + should be chosen to meet both requirements. + ``V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (integer)`` Quantization parameter for an I frame for MPEG4. Valid range: from 1 to 31. @@ -2628,11 +2640,11 @@ HEVC/H.265 Control IDs ``V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP (integer)`` Minimum quantization parameter for HEVC. - Valid range: from 0 to 51. + Valid range: from 0 to 51 for 8 bit and from 0 to 63 for 10 bit. ``V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP (integer)`` Maximum quantization parameter for HEVC. - Valid range: from 0 to 51. + Valid range: from 0 to 51 for 8 bit and from 0 to 63 for 10 bit. ``V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP (integer)`` Quantization parameter for an I frame for HEVC. @@ -2649,6 +2661,42 @@ HEVC/H.265 Control IDs Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. +``V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MIN_QP (integer)`` + Minimum quantization parameter for the HEVC I frame to limit I frame + quality to a range. Valid range: from 0 to 51 for 8 bit and from 0 to 63 for 10 bit. + If V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP is also set, the quantization parameter + should be chosen to meet both requirements. + +``V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MAX_QP (integer)`` + Maximum quantization parameter for the HEVC I frame to limit I frame + quality to a range. Valid range: from 0 to 51 for 8 bit and from 0 to 63 for 10 bit. + If V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP is also set, the quantization parameter + should be chosen to meet both requirements. + +``V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MIN_QP (integer)`` + Minimum quantization parameter for the HEVC P frame to limit P frame + quality to a range. Valid range: from 0 to 51 for 8 bit and from 0 to 63 for 10 bit. + If V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP is also set, the quantization parameter + should be chosen to meet both requirements. + +``V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MAX_QP (integer)`` + Maximum quantization parameter for the HEVC P frame to limit P frame + quality to a range. Valid range: from 0 to 51 for 8 bit and from 0 to 63 for 10 bit. + If V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP is also set, the quantization parameter + should be chosen to meet both requirements. + +``V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP (integer)`` + Minimum quantization parameter for the HEVC B frame to limit B frame + quality to a range. Valid range: from 0 to 51 for 8 bit and from 0 to 63 for 10 bit. + If V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP is also set, the quantization parameter + should be chosen to meet both requirements. + +``V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP (integer)`` + Maximum quantization parameter for the HEVC B frame to limit B frame + quality to a range. Valid range: from 0 to 51 for 8 bit and from 0 to 63 for 10 bit. + If V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP is also set, the quantization parameter + should be chosen to meet both requirements. + ``V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP (boolean)`` HIERARCHICAL_QP allows the host to specify the quantization parameter values for each temporal layer through HIERARCHICAL_QP_LAYER. This is diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5cbe0ffbf501..410e54141c21 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -920,6 +920,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP: return "H264 I-Frame Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP: return "H264 P-Frame Minimum QP Value"; case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP: return "H264 P-Frame Maximum QP Value"; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP: return "H264 B-Frame Minimum QP Value"; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP: return "H264 B-Frame Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: return "MPEG2 Level"; case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: return "MPEG2 Profile"; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value"; @@ -969,6 +971,12 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP: return "HEVC B-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: return "HEVC Minimum QP Value"; case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: return "HEVC Maximum QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MIN_QP: return "HEVC I-Frame Minimum QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MAX_QP: return "HEVC I-Frame Maximum QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MIN_QP: return "HEVC P-Frame Minimum QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MAX_QP: return "HEVC P-Frame Maximum QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP: return "HEVC B-Frame Minimum QP Value"; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP: return "HEVC B-Frame Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: return "HEVC Profile"; case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: return "HEVC Level"; case V4L2_CID_MPEG_VIDEO_HEVC_TIER: return "HEVC Tier"; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 9ead7a8386c8..a7130f47199e 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -590,6 +590,8 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type { #define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP (V4L2_CID_CODEC_BASE+386) #define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP (V4L2_CID_CODEC_BASE+387) #define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP (V4L2_CID_CODEC_BASE+388) +#define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP (V4L2_CID_CODEC_BASE+389) +#define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP (V4L2_CID_CODEC_BASE+390) #define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_CODEC_BASE+400) #define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_CODEC_BASE+401) #define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_CODEC_BASE+402) @@ -780,6 +782,13 @@ enum v4l2_mpeg_video_frame_skip_mode { V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT = 2, }; +#define V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MIN_QP (V4L2_CID_CODEC_BASE + 647) +#define V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MAX_QP (V4L2_CID_CODEC_BASE + 648) +#define V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MIN_QP (V4L2_CID_CODEC_BASE + 649) +#define V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MAX_QP (V4L2_CID_CODEC_BASE + 650) +#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP (V4L2_CID_CODEC_BASE + 651) +#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP (V4L2_CID_CODEC_BASE + 652) + /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_CODEC_CX2341X_BASE (V4L2_CTRL_CLASS_CODEC | 0x1000) #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_CODEC_CX2341X_BASE+0) -- cgit v1.2.3 From 4ca134ee98238c3aaf0bb51094716373d8ae8720 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Thu, 24 Dec 2020 12:25:34 +0100 Subject: media: v4l2-ctrl: Add layer wise bitrate controls for h264 Adds bitrate control for all coding layers for h264 same as hevc. Signed-off-by: Dikshita Agarwal Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../userspace-api/media/v4l/ext-ctrls-codec.rst | 20 ++++++++++++++++++++ drivers/media/v4l2-core/v4l2-ctrls.c | 7 +++++++ include/uapi/linux/v4l2-controls.h | 7 +++++++ 3 files changed, 34 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst index 90c60add3fc0..400774ceffaa 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst @@ -1513,6 +1513,26 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - * - Bit 16:32 - Layer number +``V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR (integer)`` + Indicates bit rate (bps) for hierarchical coding layer 0 for H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR (integer)`` + Indicates bit rate (bps) for hierarchical coding layer 1 for H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR (integer)`` + Indicates bit rate (bps) for hierarchical coding layer 2 for H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR (integer)`` + Indicates bit rate (bps) for hierarchical coding layer 3 for H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR (integer)`` + Indicates bit rate (bps) for hierarchical coding layer 4 for H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR (integer)`` + Indicates bit rate (bps) for hierarchical coding layer 5 for H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L6_BR (integer)`` + Indicates bit rate (bps) for hierarchical coding layer 6 for H264 encoder. .. _v4l2-mpeg-mpeg2: diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 410e54141c21..16ab54f676e2 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -922,6 +922,13 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP: return "H264 P-Frame Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP: return "H264 B-Frame Minimum QP Value"; case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP: return "H264 B-Frame Maximum QP Value"; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR: return "H264 Hierarchical Lay 0 Bitrate"; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR: return "H264 Hierarchical Lay 1 Bitrate"; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR: return "H264 Hierarchical Lay 2 Bitrate"; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR: return "H264 Hierarchical Lay 3 Bitrate"; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR: return "H264 Hierarchical Lay 4 Bitrate"; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR: return "H264 Hierarchical Lay 5 Bitrate"; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L6_BR: return "H264 Hierarchical Lay 6 Bitrate"; case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: return "MPEG2 Level"; case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: return "MPEG2 Profile"; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value"; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index a7130f47199e..0bd967543357 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -592,6 +592,13 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type { #define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP (V4L2_CID_CODEC_BASE+388) #define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP (V4L2_CID_CODEC_BASE+389) #define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP (V4L2_CID_CODEC_BASE+390) +#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR (V4L2_CID_CODEC_BASE+391) +#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR (V4L2_CID_CODEC_BASE+392) +#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR (V4L2_CID_CODEC_BASE+393) +#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR (V4L2_CID_CODEC_BASE+394) +#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR (V4L2_CID_CODEC_BASE+395) +#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR (V4L2_CID_CODEC_BASE+396) +#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L6_BR (V4L2_CID_CODEC_BASE+397) #define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_CODEC_BASE+400) #define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_CODEC_BASE+401) #define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_CODEC_BASE+402) -- cgit v1.2.3 From 74c895974fd3566806ea3ce42f9bb660eb54b696 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Thu, 24 Dec 2020 12:25:35 +0100 Subject: media: venus: venc: Add support for frame-specific min/max qp controls Add support for frame type specific min and max qp controls for encoder. This is a preparation patch to support v6. Signed-off-by: Dikshita Agarwal Acked-by: Stanimir Varbanov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 18 ++++ drivers/media/platform/qcom/venus/venc.c | 21 +++-- drivers/media/platform/qcom/venus/venc_ctrls.c | 114 +++++++++++++++++++++++-- 3 files changed, 142 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index dfc13b2f371f..a26bde0976d6 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -192,10 +192,28 @@ struct venc_controls { u32 h264_b_qp; u32 h264_min_qp; u32 h264_max_qp; + u32 h264_i_min_qp; + u32 h264_i_max_qp; + u32 h264_p_min_qp; + u32 h264_p_max_qp; + u32 h264_b_min_qp; + u32 h264_b_max_qp; u32 h264_loop_filter_mode; s32 h264_loop_filter_alpha; s32 h264_loop_filter_beta; + u32 hevc_i_qp; + u32 hevc_p_qp; + u32 hevc_b_qp; + u32 hevc_min_qp; + u32 hevc_max_qp; + u32 hevc_i_min_qp; + u32 hevc_i_max_qp; + u32 hevc_p_min_qp; + u32 hevc_p_max_qp; + u32 hevc_b_min_qp; + u32 hevc_b_max_qp; + u32 vp8_min_qp; u32 vp8_max_qp; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 6e830fc83b09..e4775ec97a87 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -669,17 +669,28 @@ static int venc_set_properties(struct venus_inst *inst) return ret; ptype = HFI_PROPERTY_PARAM_VENC_SESSION_QP; - quant.qp_i = ctr->h264_i_qp; - quant.qp_p = ctr->h264_p_qp; - quant.qp_b = ctr->h264_b_qp; + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + quant.qp_i = ctr->hevc_i_qp; + quant.qp_p = ctr->hevc_p_qp; + quant.qp_b = ctr->hevc_b_qp; + } else { + quant.qp_i = ctr->h264_i_qp; + quant.qp_p = ctr->h264_p_qp; + quant.qp_b = ctr->h264_b_qp; + } quant.layer_id = 0; ret = hfi_session_set_property(inst, ptype, &quant); if (ret) return ret; ptype = HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE; - quant_range.min_qp = ctr->h264_min_qp; - quant_range.max_qp = ctr->h264_max_qp; + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + quant_range.min_qp = ctr->hevc_min_qp; + quant_range.max_qp = ctr->hevc_max_qp; + } else { + quant_range.min_qp = ctr->h264_min_qp; + quant_range.max_qp = ctr->h264_max_qp; + } quant_range.layer_id = 0; ret = hfi_session_set_property(inst, ptype, &quant_range); if (ret) diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index cf860e6446c0..496ad4de6af3 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -135,9 +135,60 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: ctr->h264_min_qp = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP: + ctr->h264_i_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP: + ctr->h264_p_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP: + ctr->h264_b_min_qp = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: ctr->h264_max_qp = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP: + ctr->h264_i_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP: + ctr->h264_p_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP: + ctr->h264_b_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: + ctr->hevc_i_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP: + ctr->hevc_p_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP: + ctr->hevc_b_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: + ctr->hevc_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MIN_QP: + ctr->hevc_i_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MIN_QP: + ctr->hevc_p_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP: + ctr->hevc_b_min_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: + ctr->hevc_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MAX_QP: + ctr->hevc_i_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MAX_QP: + ctr->hevc_p_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP: + ctr->hevc_b_max_qp = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: ctr->multi_slice_mode = ctrl->val; break; @@ -223,7 +274,7 @@ int venc_ctrl_init(struct venus_inst *inst) { int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 33); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 50); if (ret) return ret; @@ -311,19 +362,70 @@ int venc_ctrl_init(struct venus_inst *inst) BITRATE_STEP, BITRATE_DEFAULT_PEAK); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26); + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 1, 51, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP, 1, 51, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP, 1, 51, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP, 1, 51, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 1, 51, 1, 51); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP, 1, 51, 1, 51); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP, 1, 51, 1, 51); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP, 1, 51, 1, 51); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, 1, 63, 1, 26); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP, 1, 63, 1, 28); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP, 1, 63, 1, 30); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, 1, 63, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MIN_QP, 1, 63, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MIN_QP, 1, 63, 1, 1); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP, 1, 63, 1, 1); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28); + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, 1, 63, 1, 63); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30); + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MAX_QP, 1, 63, 1, 63); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 1, 51, 1, 1); + V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MAX_QP, 1, 63, 1, 63); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_MAX_QP, 1, 51, 1, 51); + V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP, 1, 63, 1, 63); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, SLICE_BYTE_SIZE_MIN, -- cgit v1.2.3 From 6bde70da98f6b5f5dbd61d2fa9c1e7efe83b0402 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Mon, 4 Jan 2021 06:41:53 +0100 Subject: media: v4l2-ctrl: Add base layer priority id control. This control indicates the priority id to be applied to base layer. [hverkuil: renumbered V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID] Signed-off-by: Dikshita Agarwal Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst | 9 +++++++++ drivers/media/v4l2-core/v4l2-ctrls.c | 1 + include/uapi/linux/v4l2-controls.h | 1 + 3 files changed, 11 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst index 400774ceffaa..00944e97d638 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst @@ -3637,3 +3637,12 @@ enum v4l2_mpeg_video_hevc_size_of_length_field - - Selecting this value specifies that HEVC slices are expected to be prefixed by Annex B start codes. According to :ref:`hevc` valid start codes can be 3-bytes 0x000001 or 4-bytes 0x00000001. + +``V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID (integer)`` + Specifies a priority identifier for the NAL unit, which will be applied to + the base layer. By default this value is set to 0 for the base layer, + and the next layer will have the priority ID assigned as 1, 2, 3 and so on. + The video encoder can't decide the priority id to be applied to a layer, + so this has to come from client. + This is applicable to H264 and valid Range is from 0 to 63. + Source Rec. ITU-T H.264 (06/2019); G.7.4.1.1, G.8.8.1. diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 16ab54f676e2..f7b310240af2 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -950,6 +950,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE: return "Vertical MV Search Range"; case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: return "Repeat Sequence Header"; case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: return "Force Key Frame"; + case V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID: return "Base Layer Priority ID"; case V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS: return "MPEG-2 Slice Parameters"; case V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION: return "MPEG-2 Quantization Matrices"; case V4L2_CID_FWHT_I_FRAME_QP: return "FWHT I-Frame QP Value"; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 0bd967543357..039c0d7add1b 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -427,6 +427,7 @@ enum v4l2_mpeg_video_multi_slice_mode { #define V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE (V4L2_CID_CODEC_BASE+227) #define V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (V4L2_CID_CODEC_BASE+228) #define V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (V4L2_CID_CODEC_BASE+229) +#define V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID (V4L2_CID_CODEC_BASE+230) /* CIDs for the MPEG-2 Part 2 (H.262) codec */ #define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL (V4L2_CID_CODEC_BASE+270) -- cgit v1.2.3 From e98ce77b57530a1da9ae2dc8c886aaf0a09adbaf Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Mon, 4 Jan 2021 06:41:54 +0100 Subject: media: venus: venc : Add support for priority ID control. Add support for base layer priority ID control in encoder. This is a preparation patch to support v6. [hverkuil: changed 54 to 51 in v4l2_ctrl_handler_init] Signed-off-by: Dikshita Agarwal Acked-by: Stanimir Varbanov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 2 ++ drivers/media/platform/qcom/venus/venc_ctrls.c | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index a26bde0976d6..b984d508ed71 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -236,6 +236,8 @@ struct venc_controls { u32 hevc; u32 vp9; } level; + + u32 base_priority_id; }; struct venus_buffer { diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 496ad4de6af3..9fbe8388a938 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -259,6 +259,9 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE: ctr->frame_skip_mode = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID: + ctr->base_priority_id = ctrl->val; + break; default: return -EINVAL; } @@ -274,7 +277,7 @@ int venc_ctrl_init(struct venus_inst *inst) { int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 50); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 51); if (ret) return ret; @@ -476,6 +479,10 @@ int venc_ctrl_init(struct venus_inst *inst) (1 << V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT)), V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID, 0, + 6, 1, 0); + ret = inst->ctrl_handler.error; if (ret) goto err; -- cgit v1.2.3 From 9bac67214fbf4b5f23463f7742ccf69bfe684cbd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 7 Jan 2021 11:47:25 +0100 Subject: media: imx7: csi: Fix regression for parallel cameras on i.MX6UL Commit 86e02d07871c ("media: imx5/6/7: csi: Mark a bound video mux as a CSI mux") made an incorrect assumption that for imx7-media-csi, the bound subdev must always be a CSI mux. On i.MX6UL/i.MX6ULL there is no CSI mux at all, so do not return an error when the entity is not a video mux and assign the IMX_MEDIA_GRP_ID_CSI_MUX group id only when appropriate. This is the same approach as done in imx-media-csi.c and it fixes the csi probe regression on i.MX6UL. Tested on a imx6ull-evk board. Fixes: 86e02d07871c ("media: imx5/6/7: csi: Mark a bound video mux as a CSI mux") Signed-off-by: Fabio Estevam Signed-off-by: Rui Miguel Silva Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx7-media-csi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c index a3f3df901704..31e36168f9d0 100644 --- a/drivers/staging/media/imx/imx7-media-csi.c +++ b/drivers/staging/media/imx/imx7-media-csi.c @@ -1164,12 +1164,12 @@ static int imx7_csi_notify_bound(struct v4l2_async_notifier *notifier, struct imx7_csi *csi = imx7_csi_notifier_to_dev(notifier); struct media_pad *sink = &csi->sd.entity.pads[IMX7_CSI_PAD_SINK]; - /* The bound subdev must always be the CSI mux */ - if (WARN_ON(sd->entity.function != MEDIA_ENT_F_VID_MUX)) - return -ENXIO; - - /* Mark it as such via its group id */ - sd->grp_id = IMX_MEDIA_GRP_ID_CSI_MUX; + /* + * If the subdev is a video mux, it must be one of the CSI + * muxes. Mark it as such via its group id. + */ + if (sd->entity.function == MEDIA_ENT_F_VID_MUX) + sd->grp_id = IMX_MEDIA_GRP_ID_CSI_MUX; return v4l2_create_fwnode_links_to_pad(sd, sink); } -- cgit v1.2.3 From f5ffb81f51376eb5a12e8c4cb4871426c65bb2af Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 7 Jan 2021 11:47:26 +0100 Subject: media: imx7: csi: Fix pad link validation We can not make the assumption that the bound subdev is always a CSI mux, in i.MX6UL/i.MX6ULL that is not the case. So, just get the entity selected by source directly upstream from the CSI. Fixes: 86e02d07871c ("media: imx5/6/7: csi: Mark a bound video mux as a CSI mux") Reported-by: Fabio Estevam Signed-off-by: Rui Miguel Silva Tested-by: Fabio Estevam Tested-by: Sébastien Szymanski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx7-media-csi.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c index 31e36168f9d0..ac52b1daf991 100644 --- a/drivers/staging/media/imx/imx7-media-csi.c +++ b/drivers/staging/media/imx/imx7-media-csi.c @@ -499,6 +499,7 @@ static int imx7_csi_pad_link_validate(struct v4l2_subdev *sd, struct v4l2_subdev_format *sink_fmt) { struct imx7_csi *csi = v4l2_get_subdevdata(sd); + struct media_entity *src; struct media_pad *pad; int ret; @@ -509,11 +510,21 @@ static int imx7_csi_pad_link_validate(struct v4l2_subdev *sd, if (!csi->src_sd) return -EPIPE; + src = &csi->src_sd->entity; + + /* + * if the source is neither a CSI MUX or CSI-2 get the one directly + * upstream from this CSI + */ + if (src->function != MEDIA_ENT_F_VID_IF_BRIDGE && + src->function != MEDIA_ENT_F_VID_MUX) + src = &csi->sd.entity; + /* - * find the entity that is selected by the CSI mux. This is needed + * find the entity that is selected by the source. This is needed * to distinguish between a parallel or CSI-2 pipeline. */ - pad = imx_media_pipeline_pad(&csi->src_sd->entity, 0, 0, true); + pad = imx_media_pipeline_pad(src, 0, 0, true); if (!pad) return -ENODEV; -- cgit v1.2.3 From 3b3bf0e38959581351fdd75509d449ab923809f2 Mon Sep 17 00:00:00 2001 From: Zou Wei Date: Tue, 12 Jan 2021 02:27:14 +0100 Subject: media: ti-vpe: cal: Mark cal_camerarx_media_ops with static keyword Fix the following sparse warning: drivers/media/platform/ti-vpe/cal-camerarx.c:783:32: warning: symbol 'cal_camerarx_media_ops' was not declared. Should it be static? Signed-off-by: Zou Wei Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-camerarx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index 1920f36137b8..dd48017859cd 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -780,7 +780,7 @@ static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { .pad = &cal_camerarx_pad_ops, }; -struct media_entity_operations cal_camerarx_media_ops = { +static struct media_entity_operations cal_camerarx_media_ops = { .link_validate = v4l2_subdev_link_validate, }; -- cgit v1.2.3 From 5a402af5e19f215689e8bf3cc244c21d94eba3c4 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 13 Jan 2021 10:00:27 +0100 Subject: media: ti-vpe: cal: fix write to unallocated memory The asd allocated with v4l2_async_notifier_add_fwnode_subdev() must be of size cal_v4l2_async_subdev, otherwise access to cal_v4l2_async_subdev->phy will go to unallocated memory. Fixes: 8fcb7576ad19 ("media: ti-vpe: cal: Allow multiple contexts per subdev notifier") Signed-off-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 293cbac905b3..5fb627811c6b 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -607,7 +607,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) */ struct cal_v4l2_async_subdev { - struct v4l2_async_subdev asd; + struct v4l2_async_subdev asd; /* Must be first */ struct cal_camerarx *phy; }; @@ -694,7 +694,7 @@ static int cal_async_notifier_register(struct cal_dev *cal) fwnode = of_fwnode_handle(phy->sensor_node); asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, fwnode, - sizeof(*asd)); + sizeof(*casd)); if (IS_ERR(asd)) { phy_err(phy, "Failed to add subdev to notifier\n"); ret = PTR_ERR(asd); -- cgit v1.2.3 From 480fad61d301f094976394c38d7d89ce324711bb Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Fri, 8 Jan 2021 10:21:19 +0100 Subject: media: zoran: convert comma to semicolon Replace a comma between expression statements by a semicolon. Signed-off-by: Zheng Yongjun Acked-by: Corentin Labbe Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/zoran/zoran_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c index d9f8b21edf6a..e8902f824d6c 100644 --- a/drivers/staging/media/zoran/zoran_driver.c +++ b/drivers/staging/media/zoran/zoran_driver.c @@ -1020,7 +1020,7 @@ int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq) vq->buf_struct_size = sizeof(struct zr_buffer); vq->ops = &zr_video_qops; vq->mem_ops = &vb2_dma_contig_memops; - vq->gfp_flags = GFP_DMA32, + vq->gfp_flags = GFP_DMA32; vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vq->min_buffers_needed = 9; vq->lock = &zr->lock; -- cgit v1.2.3 From 321af22a3d2f6ed1fb1737c8588c01f6fec8a7b8 Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Fri, 8 Jan 2021 10:21:35 +0100 Subject: media: atomisp: convert comma to semicolon Replace a comma between expression statements by a semicolon. Signed-off-by: Zheng Yongjun Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/sh_css_params.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index 24fc497bd491..9fad28b97201 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -949,7 +949,7 @@ sh_css_set_black_frame(struct ia_css_stream *stream, params = stream->isp_params_configs; height = raw_black_frame->info.res.height; - width = raw_black_frame->info.padded_width, + width = raw_black_frame->info.padded_width; ptr = raw_black_frame->data + raw_black_frame->planes.raw.offset; @@ -1442,8 +1442,8 @@ static int sh_css_params_default_morph_table( IA_CSS_ENTER_PRIVATE(""); - step = (ISP_VEC_NELEMS / 16) * 128, - width = binary->morph_tbl_width, + step = (ISP_VEC_NELEMS / 16) * 128; + width = binary->morph_tbl_width; height = binary->morph_tbl_height; tab = ia_css_morph_table_allocate(width, height); -- cgit v1.2.3 From 41f42b6e693dc9822f83bc832de508600b00535f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 4 Jan 2021 17:58:07 +0100 Subject: media: dt-bindings: Convert video-interfaces.txt properties to schemas Convert video-interfaces.txt to DT schema. As it contains a mixture of device level and endpoint properties, split it up into 2 schemas. Binding schemas will need to reference both the graph.yaml and video-interfaces.yaml schemas. The exact schema depends on how many ports and endpoints for the binding. A single port with a single endpoint looks similar to this: port: $ref: /schemas/graph.yaml#/$defs/port-base properties: endpoint: $ref: video-interfaces.yaml# unevaluatedProperties: false properties: bus-width: enum: [ 8, 10, 12, 16 ] pclk-sample: true hsync-active: true vsync-active: true required: - bus-width additionalProperties: false Acked-by: Sakari Ailus Acked-by: Jacopo Mondi Acked-by: Guennadi Liakhovetski Acked-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/video-interface-devices.yaml | 406 +++++++++++++ .../devicetree/bindings/media/video-interfaces.txt | 640 +-------------------- .../bindings/media/video-interfaces.yaml | 344 +++++++++++ 3 files changed, 751 insertions(+), 639 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/video-interface-devices.yaml create mode 100644 Documentation/devicetree/bindings/media/video-interfaces.yaml diff --git a/Documentation/devicetree/bindings/media/video-interface-devices.yaml b/Documentation/devicetree/bindings/media/video-interface-devices.yaml new file mode 100644 index 000000000000..4527f56a5a6e --- /dev/null +++ b/Documentation/devicetree/bindings/media/video-interface-devices.yaml @@ -0,0 +1,406 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/video-interface-devices.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common bindings for video receiver and transmitter devices + +maintainers: + - Jacopo Mondi + - Sakari Ailus + +properties: + flash-leds: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + An array of phandles, each referring to a flash LED, a sub-node of the LED + driver device node. + + lens-focus: + $ref: /schemas/types.yaml#/definitions/phandle + description: + A phandle to the node of the focus lens controller. + + rotation: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 90, 180, 270 ] + description: | + The camera rotation is expressed as the angular difference in degrees + between two reference systems, one relative to the camera module, and one + defined on the external world scene to be captured when projected on the + image sensor pixel array. + + A camera sensor has a 2-dimensional reference system 'Rc' defined by its + pixel array read-out order. The origin is set to the first pixel being + read out, the X-axis points along the column read-out direction towards + the last columns, and the Y-axis along the row read-out direction towards + the last row. + + A typical example for a sensor with a 2592x1944 pixel array matrix + observed from the front is: + + 2591 X-axis 0 + <------------------------+ 0 + .......... ... ..........! + .......... ... ..........! Y-axis + ... ! + .......... ... ..........! + .......... ... ..........! 1943 + V + + The external world scene reference system 'Rs' is a 2-dimensional + reference system on the focal plane of the camera module. The origin is + placed on the top-left corner of the visible scene, the X-axis points + towards the right, and the Y-axis points towards the bottom of the scene. + The top, bottom, left and right directions are intentionally not defined + and depend on the environment in which the camera is used. + + A typical example of a (very common) picture of a shark swimming from left + to right, as seen from the camera, is: + + 0 X-axis + 0 +-------------------------------------> + ! + ! + ! + ! |\____)\___ + ! ) _____ __`< + ! |/ )/ + ! + ! + ! + V + Y-axis + + with the reference system 'Rs' placed on the camera focal plane: + + ¸.·˙! + ¸.·˙ ! + _ ¸.·˙ ! + +-/ \-+¸.·˙ ! + | (o) | ! Camera focal plane + +-----+˙·.¸ ! + ˙·.¸ ! + ˙·.¸ ! + ˙·.¸! + + When projected on the sensor's pixel array, the image and the associated + reference system 'Rs' are typically (but not always) inverted, due to the + camera module's lens optical inversion effect. + + Assuming the above represented scene of the swimming shark, the lens + inversion projects the scene and its reference system onto the sensor + pixel array, seen from the front of the camera sensor, as follows: + + Y-axis + ^ + ! + ! + ! + ! |\_____)\__ + ! ) ____ ___.< + ! |/ )/ + ! + ! + ! + 0 +-------------------------------------> + 0 X-axis + + Note the shark being upside-down. + + The resulting projected reference system is named 'Rp'. + + The camera rotation property is then defined as the angular difference in + the counter-clockwise direction between the camera reference system 'Rc' + and the projected scene reference system 'Rp'. It is expressed in degrees + as a number in the range [0, 360[. + + Examples + + 0 degrees camera rotation: + + + Y-Rp + ^ + Y-Rc ! + ^ ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! 0 +-------------------------------------> + ! 0 X-Rp + 0 +-------------------------------------> + 0 X-Rc + + + X-Rc 0 + <------------------------------------+ 0 + X-Rp 0 ! + <------------------------------------+ 0 ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! V + ! Y-Rc + V + Y-Rp + + 90 degrees camera rotation: + + 0 Y-Rc + 0 +--------------------> + ! Y-Rp + ! ^ + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! 0 +-------------------------------------> + ! 0 X-Rp + ! + ! + ! + ! + V + X-Rc + + 180 degrees camera rotation: + + 0 + <------------------------------------+ 0 + X-Rc ! + Y-Rp ! + ^ ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! V + ! Y-Rc + 0 +-------------------------------------> + 0 X-Rp + + 270 degrees camera rotation: + + 0 Y-Rc + 0 +--------------------> + ! 0 + ! <-----------------------------------+ 0 + ! X-Rp ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! ! + ! V + ! Y-Rp + ! + ! + ! + ! + V + X-Rc + + + Example one - Webcam + + A camera module installed on the user facing part of a laptop screen + casing used for video calls. The captured images are meant to be displayed + in landscape mode (width > height) on the laptop screen. + + The camera is typically mounted upside-down to compensate the lens optical + inversion effect: + + Y-Rp + Y-Rc ^ + ^ ! + ! ! + ! ! |\_____)\__ + ! ! ) ____ ___.< + ! ! |/ )/ + ! ! + ! ! + ! ! + ! 0 +-------------------------------------> + ! 0 X-Rp + 0 +-------------------------------------> + 0 X-Rc + + The two reference systems are aligned, the resulting camera rotation is + 0 degrees, no rotation correction needs to be applied to the resulting + image once captured to memory buffers to correctly display it to users: + + +--------------------------------------+ + ! ! + ! ! + ! ! + ! |\____)\___ ! + ! ) _____ __`< ! + ! |/ )/ ! + ! ! + ! ! + ! ! + +--------------------------------------+ + + If the camera sensor is not mounted upside-down to compensate for the lens + optical inversion, the two reference systems will not be aligned, with + 'Rp' being rotated 180 degrees relatively to 'Rc': + + + X-Rc 0 + <------------------------------------+ 0 + ! + Y-Rp ! + ^ ! + ! ! + ! |\_____)\__ ! + ! ) ____ ___.< ! + ! |/ )/ ! + ! ! + ! ! + ! V + ! Y-Rc + 0 +-------------------------------------> + 0 X-Rp + + The image once captured to memory will then be rotated by 180 degrees: + + +--------------------------------------+ + ! ! + ! ! + ! ! + ! __/(_____/| ! + ! >.___ ____ ( ! + ! \( \| ! + ! ! + ! ! + ! ! + +--------------------------------------+ + + A software rotation correction of 180 degrees should be applied to + correctly display the image: + + +--------------------------------------+ + ! ! + ! ! + ! ! + ! |\____)\___ ! + ! ) _____ __`< ! + ! |/ )/ ! + ! ! + ! ! + ! ! + +--------------------------------------+ + + Example two - Phone camera + + A camera installed on the back side of a mobile device facing away from + the user. The captured images are meant to be displayed in portrait mode + (height > width) to match the device screen orientation and the device + usage orientation used when taking the picture. + + The camera sensor is typically mounted with its pixel array longer side + aligned to the device longer side, upside-down mounted to compensate for + the lens optical inversion effect: + + 0 Y-Rc + 0 +--------------------> + ! Y-Rp + ! ^ + ! ! + ! ! + ! ! + ! ! |\_____)\__ + ! ! ) ____ ___.< + ! ! |/ )/ + ! ! + ! ! + ! ! + ! 0 +-------------------------------------> + ! 0 X-Rp + ! + ! + ! + ! + V + X-Rc + + The two reference systems are not aligned and the 'Rp' reference system is + rotated by 90 degrees in the counter-clockwise direction relatively to the + 'Rc' reference system. + + The image once captured to memory will be rotated: + + +-------------------------------------+ + | _ _ | + | \ / | + | | | | + | | | | + | | > | + | < | | + | | | | + | . | + | V | + +-------------------------------------+ + + A correction of 90 degrees in counter-clockwise direction has to be + applied to correctly display the image in portrait mode on the device + screen: + + +--------------------+ + | | + | | + | | + | | + | | + | | + | |\____)\___ | + | ) _____ __`< | + | |/ )/ | + | | + | | + | | + | | + | | + +--------------------+ + + orientation: + description: + The orientation of a device (typically an image sensor or a flash LED) + describing its mounting position relative to the usage orientation of the + system where the device is installed on. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + # Front. The device is mounted on the front facing side of the system. For + # mobile devices such as smartphones, tablets and laptops the front side + # is the user facing side. + - 0 + # Back. The device is mounted on the back side of the system, which is + # defined as the opposite side of the front facing one. + - 1 + # External. The device is not attached directly to the system but is + # attached in a way that allows it to move freely. + - 2 + +additionalProperties: true + +... diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt index 3920f25a9123..8fcf5f52bf5b 100644 --- a/Documentation/devicetree/bindings/media/video-interfaces.txt +++ b/Documentation/devicetree/bindings/media/video-interfaces.txt @@ -1,639 +1 @@ -Common bindings for video receiver and transmitter interfaces - -General concept ---------------- - -Video data pipelines usually consist of external devices, e.g. camera sensors, -controlled over an I2C, SPI or UART bus, and SoC internal IP blocks, including -video DMA engines and video data processors. - -SoC internal blocks are described by DT nodes, placed similarly to other SoC -blocks. External devices are represented as child nodes of their respective -bus controller nodes, e.g. I2C. - -Data interfaces on all video devices are described by their child 'port' nodes. -Configuration of a port depends on other devices participating in the data -transfer and is described by 'endpoint' subnodes. - -device { - ... - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - ... - endpoint@0 { ... }; - endpoint@1 { ... }; - }; - port@1 { ... }; - }; -}; - -If a port can be configured to work with more than one remote device on the same -bus, an 'endpoint' child node must be provided for each of them. If more than -one port is present in a device node or there is more than one endpoint at a -port, or port node needs to be associated with a selected hardware interface, -a common scheme using '#address-cells', '#size-cells' and 'reg' properties is -used. - -All 'port' nodes can be grouped under optional 'ports' node, which allows to -specify #address-cells, #size-cells properties independently for the 'port' -and 'endpoint' nodes and any child device nodes a device might have. - -Two 'endpoint' nodes are linked with each other through their 'remote-endpoint' -phandles. An endpoint subnode of a device contains all properties needed for -configuration of this device for data exchange with other device. In most -cases properties at the peer 'endpoint' nodes will be identical, however they -might need to be different when there is any signal modifications on the bus -between two devices, e.g. there are logic signal inverters on the lines. - -It is allowed for multiple endpoints at a port to be active simultaneously, -where supported by a device. For example, in case where a data interface of -a device is partitioned into multiple data busses, e.g. 16-bit input port -divided into two separate ITU-R BT.656 8-bit busses. In such case bus-width -and data-shift properties can be used to assign physical data lines to each -endpoint node (logical bus). - -Documenting bindings for devices --------------------------------- - -All required and optional bindings the device supports shall be explicitly -documented in device DT binding documentation. This also includes port and -endpoint nodes for the device, including unit-addresses and reg properties where -relevant. - -Please also see Documentation/devicetree/bindings/graph.txt . - -Required properties -------------------- - -If there is more than one 'port' or more than one 'endpoint' node or 'reg' -property is present in port and/or endpoint nodes the following properties -are required in a relevant parent node: - - - #address-cells : number of cells required to define port/endpoint - identifier, should be 1. - - #size-cells : should be zero. - - -Optional properties -------------------- - -- flash-leds: An array of phandles, each referring to a flash LED, a sub-node - of the LED driver device node. - -- lens-focus: A phandle to the node of the focus lens controller. - -- rotation: The camera rotation is expressed as the angular difference in - degrees between two reference systems, one relative to the camera module, and - one defined on the external world scene to be captured when projected on the - image sensor pixel array. - - A camera sensor has a 2-dimensional reference system 'Rc' defined by - its pixel array read-out order. The origin is set to the first pixel - being read out, the X-axis points along the column read-out direction - towards the last columns, and the Y-axis along the row read-out - direction towards the last row. - - A typical example for a sensor with a 2592x1944 pixel array matrix - observed from the front is: - - 2591 X-axis 0 - <------------------------+ 0 - .......... ... ..........! - .......... ... ..........! Y-axis - ... ! - .......... ... ..........! - .......... ... ..........! 1943 - V - - The external world scene reference system 'Rs' is a 2-dimensional - reference system on the focal plane of the camera module. The origin is - placed on the top-left corner of the visible scene, the X-axis points - towards the right, and the Y-axis points towards the bottom of the - scene. The top, bottom, left and right directions are intentionally not - defined and depend on the environment in which the camera is used. - - A typical example of a (very common) picture of a shark swimming from - left to right, as seen from the camera, is: - - 0 X-axis - 0 +-------------------------------------> - ! - ! - ! - ! |\____)\___ - ! ) _____ __`< - ! |/ )/ - ! - ! - ! - V - Y-axis - - with the reference system 'Rs' placed on the camera focal plane: - - ¸.·˙! - ¸.·˙ ! - _ ¸.·˙ ! - +-/ \-+¸.·˙ ! - | (o) | ! Camera focal plane - +-----+˙·.¸ ! - ˙·.¸ ! - ˙·.¸ ! - ˙·.¸! - - When projected on the sensor's pixel array, the image and the associated - reference system 'Rs' are typically (but not always) inverted, due to - the camera module's lens optical inversion effect. - - Assuming the above represented scene of the swimming shark, the lens - inversion projects the scene and its reference system onto the sensor - pixel array, seen from the front of the camera sensor, as follows: - - Y-axis - ^ - ! - ! - ! - ! |\_____)\__ - ! ) ____ ___.< - ! |/ )/ - ! - ! - ! - 0 +-------------------------------------> - 0 X-axis - - Note the shark being upside-down. - - The resulting projected reference system is named 'Rp'. - - The camera rotation property is then defined as the angular difference - in the counter-clockwise direction between the camera reference system - 'Rc' and the projected scene reference system 'Rp'. It is expressed in - degrees as a number in the range [0, 360[. - - Examples - - 0 degrees camera rotation: - - - Y-Rp - ^ - Y-Rc ! - ^ ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! 0 +-------------------------------------> - ! 0 X-Rp - 0 +-------------------------------------> - 0 X-Rc - - - X-Rc 0 - <------------------------------------+ 0 - X-Rp 0 ! - <------------------------------------+ 0 ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! V - ! Y-Rc - V - Y-Rp - - 90 degrees camera rotation: - - 0 Y-Rc - 0 +--------------------> - ! Y-Rp - ! ^ - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! 0 +-------------------------------------> - ! 0 X-Rp - ! - ! - ! - ! - V - X-Rc - - 180 degrees camera rotation: - - 0 - <------------------------------------+ 0 - X-Rc ! - Y-Rp ! - ^ ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! V - ! Y-Rc - 0 +-------------------------------------> - 0 X-Rp - - 270 degrees camera rotation: - - 0 Y-Rc - 0 +--------------------> - ! 0 - ! <-----------------------------------+ 0 - ! X-Rp ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! ! - ! V - ! Y-Rp - ! - ! - ! - ! - V - X-Rc - - - Example one - Webcam - - A camera module installed on the user facing part of a laptop screen - casing used for video calls. The captured images are meant to be - displayed in landscape mode (width > height) on the laptop screen. - - The camera is typically mounted upside-down to compensate the lens - optical inversion effect: - - Y-Rp - Y-Rc ^ - ^ ! - ! ! - ! ! |\_____)\__ - ! ! ) ____ ___.< - ! ! |/ )/ - ! ! - ! ! - ! ! - ! 0 +-------------------------------------> - ! 0 X-Rp - 0 +-------------------------------------> - 0 X-Rc - - The two reference systems are aligned, the resulting camera rotation is - 0 degrees, no rotation correction needs to be applied to the resulting - image once captured to memory buffers to correctly display it to users: - - +--------------------------------------+ - ! ! - ! ! - ! ! - ! |\____)\___ ! - ! ) _____ __`< ! - ! |/ )/ ! - ! ! - ! ! - ! ! - +--------------------------------------+ - - If the camera sensor is not mounted upside-down to compensate for the - lens optical inversion, the two reference systems will not be aligned, - with 'Rp' being rotated 180 degrees relatively to 'Rc': - - - X-Rc 0 - <------------------------------------+ 0 - ! - Y-Rp ! - ^ ! - ! ! - ! |\_____)\__ ! - ! ) ____ ___.< ! - ! |/ )/ ! - ! ! - ! ! - ! V - ! Y-Rc - 0 +-------------------------------------> - 0 X-Rp - - The image once captured to memory will then be rotated by 180 degrees: - - +--------------------------------------+ - ! ! - ! ! - ! ! - ! __/(_____/| ! - ! >.___ ____ ( ! - ! \( \| ! - ! ! - ! ! - ! ! - +--------------------------------------+ - - A software rotation correction of 180 degrees should be applied to - correctly display the image: - - +--------------------------------------+ - ! ! - ! ! - ! ! - ! |\____)\___ ! - ! ) _____ __`< ! - ! |/ )/ ! - ! ! - ! ! - ! ! - +--------------------------------------+ - - Example two - Phone camera - - A camera installed on the back side of a mobile device facing away from - the user. The captured images are meant to be displayed in portrait mode - (height > width) to match the device screen orientation and the device - usage orientation used when taking the picture. - - The camera sensor is typically mounted with its pixel array longer side - aligned to the device longer side, upside-down mounted to compensate for - the lens optical inversion effect: - - 0 Y-Rc - 0 +--------------------> - ! Y-Rp - ! ^ - ! ! - ! ! - ! ! - ! ! |\_____)\__ - ! ! ) ____ ___.< - ! ! |/ )/ - ! ! - ! ! - ! ! - ! 0 +-------------------------------------> - ! 0 X-Rp - ! - ! - ! - ! - V - X-Rc - - The two reference systems are not aligned and the 'Rp' reference - system is rotated by 90 degrees in the counter-clockwise direction - relatively to the 'Rc' reference system. - - The image once captured to memory will be rotated: - - +-------------------------------------+ - | _ _ | - | \ / | - | | | | - | | | | - | | > | - | < | | - | | | | - | . | - | V | - +-------------------------------------+ - - A correction of 90 degrees in counter-clockwise direction has to be - applied to correctly display the image in portrait mode on the device - screen: - - +--------------------+ - | | - | | - | | - | | - | | - | | - | |\____)\___ | - | ) _____ __`< | - | |/ )/ | - | | - | | - | | - | | - | | - +--------------------+ - -- orientation: The orientation of a device (typically an image sensor or a flash - LED) describing its mounting position relative to the usage orientation of the - system where the device is installed on. - Possible values are: - 0 - Front. The device is mounted on the front facing side of the system. - For mobile devices such as smartphones, tablets and laptops the front side is - the user facing side. - 1 - Back. The device is mounted on the back side of the system, which is - defined as the opposite side of the front facing one. - 2 - External. The device is not attached directly to the system but is - attached in a way that allows it to move freely. - -Optional endpoint properties ----------------------------- - -- remote-endpoint: phandle to an 'endpoint' subnode of a remote device node. -- slave-mode: a boolean property indicating that the link is run in slave mode. - The default when this property is not specified is master mode. In the slave - mode horizontal and vertical synchronization signals are provided to the - slave device (data source) by the master device (data sink). In the master - mode the data source device is also the source of the synchronization signals. -- bus-type: data bus type. Possible values are: - 1 - MIPI CSI-2 C-PHY - 2 - MIPI CSI1 - 3 - CCP2 - 4 - MIPI CSI-2 D-PHY - 5 - Parallel - 6 - Bt.656 -- bus-width: number of data lines actively used, valid for the parallel busses. -- data-shift: on the parallel data busses, if bus-width is used to specify the - number of data lines, data-shift can be used to specify which data lines are - used, e.g. "bus-width=<8>; data-shift=<2>;" means, that lines 9:2 are used. -- hsync-active: active state of the HSYNC signal, 0/1 for LOW/HIGH respectively. -- vsync-active: active state of the VSYNC signal, 0/1 for LOW/HIGH respectively. - Note, that if HSYNC and VSYNC polarities are not specified, embedded - synchronization may be required, where supported. -- data-active: similar to HSYNC and VSYNC, specifies data line polarity. -- data-enable-active: similar to HSYNC and VSYNC, specifies the data enable - signal polarity. -- field-even-active: field signal level during the even field data transmission. -- pclk-sample: sample data on rising (1) or falling (0) edge of the pixel clock - signal. -- sync-on-green-active: active state of Sync-on-green (SoG) signal, 0/1 for - LOW/HIGH respectively. -- data-lanes: an array of physical data lane indexes. Position of an entry - determines the logical lane number, while the value of an entry indicates - physical lane, e.g. for 2-lane MIPI CSI-2 bus we could have - "data-lanes = <1 2>;", assuming the clock lane is on hardware lane 0. - If the hardware does not support lane reordering, monotonically - incremented values shall be used from 0 or 1 onwards, depending on - whether or not there is also a clock lane. This property is valid for - serial busses only (e.g. MIPI CSI-2). -- clock-lanes: an array of physical clock lane indexes. Position of an entry - determines the logical lane number, while the value of an entry indicates - physical lane, e.g. for a MIPI CSI-2 bus we could have "clock-lanes = <0>;", - which places the clock lane on hardware lane 0. This property is valid for - serial busses only (e.g. MIPI CSI-2). Note that for the MIPI CSI-2 bus this - array contains only one entry. -- clock-noncontinuous: a boolean property to allow MIPI CSI-2 non-continuous - clock mode. -- link-frequencies: Allowed data bus frequencies. For MIPI CSI-2, for - instance, this is the actual frequency of the bus, not bits per clock per - lane value. An array of 64-bit unsigned integers. -- lane-polarities: an array of polarities of the lanes starting from the clock - lane and followed by the data lanes in the same order as in data-lanes. - Valid values are 0 (normal) and 1 (inverted). The length of the array - should be the combined length of data-lanes and clock-lanes properties. - If the lane-polarities property is omitted, the value must be interpreted - as 0 (normal). This property is valid for serial busses only. -- strobe: Whether the clock signal is used as clock (0) or strobe (1). Used - with CCP2, for instance. - -Example -------- - -The example snippet below describes two data pipelines. ov772x and imx074 are -camera sensors with a parallel and serial (MIPI CSI-2) video bus respectively. -Both sensors are on the I2C control bus corresponding to the i2c0 controller -node. ov772x sensor is linked directly to the ceu0 video host interface. -imx074 is linked to ceu0 through the MIPI CSI-2 receiver (csi2). ceu0 has a -(single) DMA engine writing captured data to memory. ceu0 node has a single -'port' node which may indicate that at any time only one of the following data -pipelines can be active: ov772x -> ceu0 or imx074 -> csi2 -> ceu0. - - ceu0: ceu@fe910000 { - compatible = "renesas,sh-mobile-ceu"; - reg = <0xfe910000 0xa0>; - interrupts = <0x880>; - - mclk: master_clock { - compatible = "renesas,ceu-clock"; - #clock-cells = <1>; - clock-frequency = <50000000>; /* Max clock frequency */ - clock-output-names = "mclk"; - }; - - port { - #address-cells = <1>; - #size-cells = <0>; - - /* Parallel bus endpoint */ - ceu0_1: endpoint@1 { - reg = <1>; /* Local endpoint # */ - remote = <&ov772x_1_1>; /* Remote phandle */ - bus-width = <8>; /* Used data lines */ - data-shift = <2>; /* Lines 9:2 are used */ - - /* If hsync-active/vsync-active are missing, - embedded BT.656 sync is used */ - hsync-active = <0>; /* Active low */ - vsync-active = <0>; /* Active low */ - data-active = <1>; /* Active high */ - pclk-sample = <1>; /* Rising */ - }; - - /* MIPI CSI-2 bus endpoint */ - ceu0_0: endpoint@0 { - reg = <0>; - remote = <&csi2_2>; - }; - }; - }; - - i2c0: i2c@fff20000 { - ... - ov772x_1: camera@21 { - compatible = "ovti,ov772x"; - reg = <0x21>; - vddio-supply = <®ulator1>; - vddcore-supply = <®ulator2>; - - clock-frequency = <20000000>; - clocks = <&mclk 0>; - clock-names = "xclk"; - - port { - /* With 1 endpoint per port no need for addresses. */ - ov772x_1_1: endpoint { - bus-width = <8>; - remote-endpoint = <&ceu0_1>; - hsync-active = <1>; - vsync-active = <0>; /* Who came up with an - inverter here ?... */ - data-active = <1>; - pclk-sample = <1>; - }; - }; - }; - - imx074: camera@1a { - compatible = "sony,imx074"; - reg = <0x1a>; - vddio-supply = <®ulator1>; - vddcore-supply = <®ulator2>; - - clock-frequency = <30000000>; /* Shared clock with ov772x_1 */ - clocks = <&mclk 0>; - clock-names = "sysclk"; /* Assuming this is the - name in the datasheet */ - port { - imx074_1: endpoint { - clock-lanes = <0>; - data-lanes = <1 2>; - remote-endpoint = <&csi2_1>; - }; - }; - }; - }; - - csi2: csi2@ffc90000 { - compatible = "renesas,sh-mobile-csi2"; - reg = <0xffc90000 0x1000>; - interrupts = <0x17a0>; - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - compatible = "renesas,csi2c"; /* One of CSI2I and CSI2C. */ - reg = <1>; /* CSI-2 PHY #1 of 2: PHY_S, - PHY_M has port address 0, - is unused. */ - csi2_1: endpoint { - clock-lanes = <0>; - data-lanes = <2 1>; - remote-endpoint = <&imx074_1>; - }; - }; - port@2 { - reg = <2>; /* port 2: link to the CEU */ - - csi2_2: endpoint { - remote-endpoint = <&ceu0_0>; - }; - }; - }; +This file has moved to video-interfaces.yaml and video-interface-devices.yaml. diff --git a/Documentation/devicetree/bindings/media/video-interfaces.yaml b/Documentation/devicetree/bindings/media/video-interfaces.yaml new file mode 100644 index 000000000000..0a7a73fd59f2 --- /dev/null +++ b/Documentation/devicetree/bindings/media/video-interfaces.yaml @@ -0,0 +1,344 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/video-interfaces.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common bindings for video receiver and transmitter interface endpoints + +maintainers: + - Sakari Ailus + - Laurent Pinchart + +description: | + Video data pipelines usually consist of external devices, e.g. camera sensors, + controlled over an I2C, SPI or UART bus, and SoC internal IP blocks, including + video DMA engines and video data processors. + + SoC internal blocks are described by DT nodes, placed similarly to other SoC + blocks. External devices are represented as child nodes of their respective + bus controller nodes, e.g. I2C. + + Data interfaces on all video devices are described by their child 'port' nodes. + Configuration of a port depends on other devices participating in the data + transfer and is described by 'endpoint' subnodes. + + device { + ... + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + ... + endpoint@0 { ... }; + endpoint@1 { ... }; + }; + port@1 { ... }; + }; + }; + + If a port can be configured to work with more than one remote device on the same + bus, an 'endpoint' child node must be provided for each of them. If more than + one port is present in a device node or there is more than one endpoint at a + port, or port node needs to be associated with a selected hardware interface, + a common scheme using '#address-cells', '#size-cells' and 'reg' properties is + used. + + All 'port' nodes can be grouped under optional 'ports' node, which allows to + specify #address-cells, #size-cells properties independently for the 'port' + and 'endpoint' nodes and any child device nodes a device might have. + + Two 'endpoint' nodes are linked with each other through their 'remote-endpoint' + phandles. An endpoint subnode of a device contains all properties needed for + configuration of this device for data exchange with other device. In most + cases properties at the peer 'endpoint' nodes will be identical, however they + might need to be different when there is any signal modifications on the bus + between two devices, e.g. there are logic signal inverters on the lines. + + It is allowed for multiple endpoints at a port to be active simultaneously, + where supported by a device. For example, in case where a data interface of + a device is partitioned into multiple data busses, e.g. 16-bit input port + divided into two separate ITU-R BT.656 8-bit busses. In such case bus-width + and data-shift properties can be used to assign physical data lines to each + endpoint node (logical bus). + + Documenting bindings for devices + -------------------------------- + + All required and optional bindings the device supports shall be explicitly + documented in device DT binding documentation. This also includes port and + endpoint nodes for the device, including unit-addresses and reg properties + where relevant. + +allOf: + - $ref: /schemas/graph.yaml#/$defs/endpoint-base + +properties: + slave-mode: + type: boolean + description: + Indicates that the link is run in slave mode. The default when this + property is not specified is master mode. In the slave mode horizontal and + vertical synchronization signals are provided to the slave device (data + source) by the master device (data sink). In the master mode the data + source device is also the source of the synchronization signals. + + bus-type: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 1 # MIPI CSI-2 C-PHY + - 2 # MIPI CSI1 + - 3 # CCP2 + - 4 # MIPI CSI-2 D-PHY + - 5 # Parallel + - 6 # BT.656 + description: + Data bus type. + + bus-width: + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 64 + description: + Number of data lines actively used, valid for the parallel busses. + + data-shift: + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 64 + description: + On the parallel data busses, if bus-width is used to specify the number of + data lines, data-shift can be used to specify which data lines are used, + e.g. "bus-width=<8>; data-shift=<2>;" means, that lines 9:2 are used. + + hsync-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Active state of the HSYNC signal, 0/1 for LOW/HIGH respectively. + + vsync-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Active state of the VSYNC signal, 0/1 for LOW/HIGH respectively. Note, + that if HSYNC and VSYNC polarities are not specified, embedded + synchronization may be required, where supported. + + data-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Similar to HSYNC and VSYNC, specifies data line polarity. + + data-enable-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Similar to HSYNC and VSYNC, specifies the data enable signal polarity. + + field-even-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Field signal level during the even field data transmission. + + pclk-sample: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Sample data on rising (1) or falling (0) edge of the pixel clock signal. + + sync-on-green-active: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Active state of Sync-on-green (SoG) signal, 0/1 for LOW/HIGH respectively. + + data-lanes: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 8 + items: + # Assume up to 9 physical lane indices + maximum: 8 + description: + An array of physical data lane indexes. Position of an entry determines + the logical lane number, while the value of an entry indicates physical + lane, e.g. for 2-lane MIPI CSI-2 bus we could have "data-lanes = <1 2>;", + assuming the clock lane is on hardware lane 0. If the hardware does not + support lane reordering, monotonically incremented values shall be used + from 0 or 1 onwards, depending on whether or not there is also a clock + lane. This property is valid for serial busses only (e.g. MIPI CSI-2). + + clock-lanes: + $ref: /schemas/types.yaml#/definitions/uint32 + # Assume up to 9 physical lane indices + maximum: 8 + description: + Physical clock lane index. Position of an entry determines the logical + lane number, while the value of an entry indicates physical lane, e.g. for + a MIPI CSI-2 bus we could have "clock-lanes = <0>;", which places the + clock lane on hardware lane 0. This property is valid for serial busses + only (e.g. MIPI CSI-2). + + clock-noncontinuous: + type: boolean + description: + Allow MIPI CSI-2 non-continuous clock mode. + + link-frequencies: + $ref: /schemas/types.yaml#/definitions/uint64-array + description: + Allowed data bus frequencies. For MIPI CSI-2, for instance, this is the + actual frequency of the bus, not bits per clock per lane value. An array + of 64-bit unsigned integers. + + lane-polarities: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 9 + items: + enum: [ 0, 1 ] + description: + An array of polarities of the lanes starting from the clock lane and + followed by the data lanes in the same order as in data-lanes. Valid + values are 0 (normal) and 1 (inverted). The length of the array should be + the combined length of data-lanes and clock-lanes properties. If the + lane-polarities property is omitted, the value must be interpreted as 0 + (normal). This property is valid for serial busses only. + + strobe: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Whether the clock signal is used as clock (0) or strobe (1). Used with + CCP2, for instance. + +additionalProperties: true + +examples: + # The example snippet below describes two data pipelines. ov772x and imx074 + # are camera sensors with a parallel and serial (MIPI CSI-2) video bus + # respectively. Both sensors are on the I2C control bus corresponding to the + # i2c0 controller node. ov772x sensor is linked directly to the ceu0 video + # host interface. imx074 is linked to ceu0 through the MIPI CSI-2 receiver + # (csi2). ceu0 has a (single) DMA engine writing captured data to memory. + # ceu0 node has a single 'port' node which may indicate that at any time + # only one of the following data pipelines can be active: + # ov772x -> ceu0 or imx074 -> csi2 -> ceu0. + - | + ceu@fe910000 { + compatible = "renesas,sh-mobile-ceu"; + reg = <0xfe910000 0xa0>; + interrupts = <0x880>; + + mclk: master_clock { + compatible = "renesas,ceu-clock"; + #clock-cells = <1>; + clock-frequency = <50000000>; /* Max clock frequency */ + clock-output-names = "mclk"; + }; + + port { + #address-cells = <1>; + #size-cells = <0>; + + /* Parallel bus endpoint */ + ceu0_1: endpoint@1 { + reg = <1>; /* Local endpoint # */ + remote-endpoint = <&ov772x_1_1>; /* Remote phandle */ + bus-width = <8>; /* Used data lines */ + data-shift = <2>; /* Lines 9:2 are used */ + + /* If hsync-active/vsync-active are missing, + embedded BT.656 sync is used */ + hsync-active = <0>; /* Active low */ + vsync-active = <0>; /* Active low */ + data-active = <1>; /* Active high */ + pclk-sample = <1>; /* Rising */ + }; + + /* MIPI CSI-2 bus endpoint */ + ceu0_0: endpoint@0 { + reg = <0>; + remote-endpoint = <&csi2_2>; + }; + }; + }; + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera@21 { + compatible = "ovti,ov772x"; + reg = <0x21>; + vddio-supply = <®ulator1>; + vddcore-supply = <®ulator2>; + + clock-frequency = <20000000>; + clocks = <&mclk 0>; + clock-names = "xclk"; + + port { + /* With 1 endpoint per port no need for addresses. */ + ov772x_1_1: endpoint { + bus-width = <8>; + remote-endpoint = <&ceu0_1>; + hsync-active = <1>; + vsync-active = <0>; /* Who came up with an + inverter here ?... */ + data-active = <1>; + pclk-sample = <1>; + }; + }; + }; + + camera@1a { + compatible = "sony,imx074"; + reg = <0x1a>; + vddio-supply = <®ulator1>; + vddcore-supply = <®ulator2>; + + clock-frequency = <30000000>; /* Shared clock with ov772x_1 */ + clocks = <&mclk 0>; + clock-names = "sysclk"; /* Assuming this is the + name in the datasheet */ + port { + imx074_1: endpoint { + clock-lanes = <0>; + data-lanes = <1 2>; + remote-endpoint = <&csi2_1>; + }; + }; + }; + }; + + csi2: csi2@ffc90000 { + compatible = "renesas,sh-mobile-csi2"; + reg = <0xffc90000 0x1000>; + interrupts = <0x17a0>; + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + compatible = "renesas,csi2c"; /* One of CSI2I and CSI2C. */ + reg = <1>; /* CSI-2 PHY #1 of 2: PHY_S, + PHY_M has port address 0, + is unused. */ + csi2_1: endpoint { + clock-lanes = <0>; + data-lanes = <2 1>; + remote-endpoint = <&imx074_1>; + }; + }; + port@2 { + reg = <2>; /* port 2: link to the CEU */ + + csi2_2: endpoint { + remote-endpoint = <&ceu0_0>; + }; + }; + }; + +... -- cgit v1.2.3 From 066a94e28a23e04c0e9cb293f9ead56d409d7e41 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 4 Jan 2021 17:58:08 +0100 Subject: media: dt-bindings: media: Use graph and video-interfaces schemas Now that we have graph and video-interfaces schemas, rework the media related schemas to use them. Cc: Jacopo Mondi Signed-off-by: Rob Herring Reviewed-by: Laurent Pinchart Acked-by: Maxime Ripard Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/allwinner,sun4i-a10-csi.yaml | 11 +- .../bindings/media/allwinner,sun6i-a31-csi.yaml | 12 +-- .../devicetree/bindings/media/i2c/adv7180.yaml | 36 ++----- .../devicetree/bindings/media/i2c/adv7604.yaml | 37 ++----- .../bindings/media/i2c/aptina,mt9v111.yaml | 4 +- .../bindings/media/i2c/imi,rdacm2x-gmsl.yaml | 30 +----- .../devicetree/bindings/media/i2c/imx219.yaml | 21 ++-- .../bindings/media/i2c/maxim,max9286.yaml | 101 ++++-------------- .../devicetree/bindings/media/i2c/mipi-ccs.yaml | 17 ++-- .../devicetree/bindings/media/i2c/ov5647.yaml | 76 ++++++++++++++ .../devicetree/bindings/media/i2c/ov8856.yaml | 22 ++-- .../bindings/media/i2c/ovti,ov02a10.yaml | 29 +++--- .../devicetree/bindings/media/i2c/ovti,ov2680.yaml | 6 +- .../devicetree/bindings/media/i2c/ovti,ov772x.yaml | 9 +- .../devicetree/bindings/media/i2c/sony,imx214.yaml | 25 ++--- .../devicetree/bindings/media/i2c/sony,imx274.yaml | 3 +- .../bindings/media/marvell,mmp2-ccic.yaml | 15 +-- .../devicetree/bindings/media/nxp,imx7-csi.yaml | 5 +- .../bindings/media/nxp,imx7-mipi-csi2.yaml | 32 ++---- .../devicetree/bindings/media/renesas,ceu.yaml | 17 +--- .../devicetree/bindings/media/renesas,csi2.yaml | 54 ++-------- .../devicetree/bindings/media/renesas,vin.yaml | 113 +++------------------ .../devicetree/bindings/media/rockchip-isp1.yaml | 40 +------- .../devicetree/bindings/media/st,stm32-dcmi.yaml | 18 +--- .../devicetree/bindings/media/ti,cal.yaml | 55 +++------- .../bindings/media/xilinx/xlnx,csi2rxss.yaml | 39 ++----- 26 files changed, 244 insertions(+), 583 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/i2c/ov5647.yaml diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml index 09318830db47..6ced94064215 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml @@ -67,14 +67,14 @@ properties: interconnect-names: const: dma-mem - # See ./video-interfaces.txt for details port: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base additionalProperties: false properties: endpoint: - type: object + $ref: video-interfaces.yaml# + unevaluatedProperties: false properties: bus-width: @@ -83,7 +83,6 @@ properties: data-active: true hsync-active: true pclk-sample: true - remote-endpoint: true vsync-active: true required: @@ -91,12 +90,8 @@ properties: - data-active - hsync-active - pclk-sample - - remote-endpoint - vsync-active - required: - - endpoint - required: - compatible - reg diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml index 1fd9b5532a21..8b568072a069 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml @@ -40,17 +40,15 @@ properties: resets: maxItems: 1 - # See ./video-interfaces.txt for details port: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base properties: endpoint: - type: object + $ref: video-interfaces.yaml# + unevaluatedProperties: false properties: - remote-endpoint: true - bus-width: enum: [ 8, 10, 12, 16 ] @@ -60,10 +58,6 @@ properties: required: - bus-width - - remote-endpoint - - required: - - endpoint additionalProperties: false diff --git a/Documentation/devicetree/bindings/media/i2c/adv7180.yaml b/Documentation/devicetree/bindings/media/i2c/adv7180.yaml index d8c54f9d9506..bcfd93739b4f 100644 --- a/Documentation/devicetree/bindings/media/i2c/adv7180.yaml +++ b/Documentation/devicetree/bindings/media/i2c/adv7180.yaml @@ -36,17 +36,9 @@ properties: maxItems: 1 port: - type: object - description: - A node containing a single endpoint as doucmented in - Documentation/devicetree/bindings/media/video-interfaces.txt - - ports: - type: object - description: - A node containing input and output port nodes with endpoint definitions - as documented in - Documentation/devicetree/bindings/media/video-interfaces.txt + $ref: /schemas/graph.yaml#/properties/port + + ports: true additionalProperties: false @@ -80,25 +72,20 @@ allOf: then: properties: ports: + $ref: /schemas/graph.yaml#/properties/ports properties: - '#address-cells': - const: 1 - '#size-cells': - const: 0 port@3: - type: object + $ref: /schemas/graph.yaml#/properties/port description: Output port patternProperties: "^port@[0-2]$": - type: object + $ref: /schemas/graph.yaml#/properties/port description: Input port required: - port@3 - additionalProperties: false - required: - ports @@ -110,25 +97,20 @@ allOf: then: properties: ports: + $ref: /schemas/graph.yaml#/properties/ports properties: - '#address-cells': - const: 1 - '#size-cells': - const: 0 port@6: - type: object + $ref: /schemas/graph.yaml#/properties/port description: Output port patternProperties: "^port@[0-5]$": - type: object + $ref: /schemas/graph.yaml#/properties/port description: Input port required: - port@6 - additionalProperties: false - required: - ports diff --git a/Documentation/devicetree/bindings/media/i2c/adv7604.yaml b/Documentation/devicetree/bindings/media/i2c/adv7604.yaml index 407baddfaa1d..df634b0c1f8c 100644 --- a/Documentation/devicetree/bindings/media/i2c/adv7604.yaml +++ b/Documentation/devicetree/bindings/media/i2c/adv7604.yaml @@ -64,16 +64,12 @@ properties: description: Select which input is selected after reset. - ports: - type: object - description: - A node containing input and output port nodes with endpoint definitions - as documented in - Documentation/devicetree/bindings/media/video-interfaces.txt + ports: true required: - compatible - reg + - ports additionalProperties: false @@ -86,26 +82,19 @@ allOf: then: properties: ports: + $ref: /schemas/graph.yaml#/properties/ports properties: - '#address-cells': - const: 1 - '#size-cells': - const: 0 port@0: - type: object + $ref: /schemas/graph.yaml#/properties/port description: Input port + port@1: - type: object + $ref: /schemas/graph.yaml#/properties/port description: Output port required: - port@1 - additionalProperties: false - - required: - - ports - - if: properties: compatible: @@ -114,28 +103,20 @@ allOf: then: properties: ports: + $ref: /schemas/graph.yaml#/properties/ports properties: - '#address-cells': - const: 1 - '#size-cells': - const: 0 port@2: - type: object + $ref: /schemas/graph.yaml#/properties/port description: Output port patternProperties: "^port@[0-1]$": - type: object + $ref: /schemas/graph.yaml#/properties/port description: Input port required: - port@2 - additionalProperties: false - - required: - - ports - examples: - | #include diff --git a/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.yaml b/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.yaml index ff9546e95d05..e53b8d65f381 100644 --- a/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.yaml +++ b/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.yaml @@ -41,9 +41,9 @@ properties: maxItems: 1 port: - type: object + $ref: /schemas/graph.yaml#/properties/port description: | - Output video port. See ../video-interfaces.txt. + Output video port. required: - compatible diff --git a/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml b/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml index 3dc06c628e64..e57575c44930 100644 --- a/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml +++ b/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml @@ -86,33 +86,9 @@ properties: maxItems: 3 port: - type: object - additionalProperties: false - description: -| - Connection to the remote GMSL endpoint are modelled using the OF graph - bindings in accordance with the video interface bindings defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. - - The device node contains a single "port" child node with a single - "endpoint" sub-device. - - properties: - endpoint: - type: object - additionalProperties: false - - properties: - remote-endpoint: - description: -| - phandle to the remote GMSL endpoint sub-node in the remote node - port. - maxItems: 1 - - required: - - remote-endpoint - - required: - - endpoint + $ref: /schemas/graph.yaml#/properties/port + description: + Connection to the remote GMSL endpoint. required: - compatible diff --git a/Documentation/devicetree/bindings/media/i2c/imx219.yaml b/Documentation/devicetree/bindings/media/i2c/imx219.yaml index dfc4d29a4f04..012c0565d8ae 100644 --- a/Documentation/devicetree/bindings/media/i2c/imx219.yaml +++ b/Documentation/devicetree/bindings/media/i2c/imx219.yaml @@ -44,12 +44,15 @@ properties: Reference to the GPIO connected to the xclr pin, if any. Must be released (set high) after all supplies are applied. - # See ../video-interfaces.txt for more details port: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + properties: endpoint: - type: object + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + properties: data-lanes: description: |- @@ -60,16 +63,8 @@ properties: - const: 1 - const: 2 - clock-noncontinuous: - type: boolean - description: |- - MIPI CSI-2 clock is non-continuous if this property is present, - otherwise it's continuous. - - link-frequencies: - $ref: /schemas/types.yaml#/definitions/uint64-array - description: - Allowed data bus frequencies. + clock-noncontinuous: true + link-frequencies: true required: - link-frequencies diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml index 68ee8c7d9e79..bf4fa56ffcc7 100644 --- a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml @@ -51,81 +51,41 @@ properties: const: 2 ports: - type: object - description: | - The connections to the MAX9286 GMSL and its endpoint nodes are modelled - using the OF graph bindings in accordance with the video interface - bindings defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. - - The following table lists the port number corresponding to each device - port. - - Port Description - ---------------------------------------- - Port 0 GMSL Input 0 - Port 1 GMSL Input 1 - Port 2 GMSL Input 2 - Port 3 GMSL Input 3 - Port 4 CSI-2 Output + $ref: /schemas/graph.yaml#/properties/ports properties: - '#address-cells': - const: 1 - - '#size-cells': - const: 0 - - port@[0-3]: - type: object - properties: - reg: - enum: [ 0, 1, 2, 3 ] - - endpoint: - type: object - - properties: - remote-endpoint: - description: | - phandle to the remote GMSL source endpoint subnode in the - remote node port. + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: GMSL Input 0 - required: - - remote-endpoint + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: GMSL Input 1 - required: - - reg - - endpoint + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: GMSL Input 2 - additionalProperties: false + port@3: + $ref: /schemas/graph.yaml#/properties/port + description: GMSL Input 3 port@4: - type: object - properties: - reg: - const: 4 + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI-2 Output + properties: endpoint: - type: object + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false properties: - remote-endpoint: - description: phandle to the remote CSI-2 sink endpoint. - - data-lanes: - description: array of physical CSI-2 data lane indexes. + data-lanes: true required: - - remote-endpoint - data-lanes - required: - - reg - - endpoint - - additionalProperties: false - required: - port@4 @@ -183,25 +143,8 @@ properties: requirements of the currently connected remote device. port: - type: object - - properties: - endpoint: - type: object - - properties: - remote-endpoint: - description: phandle to the MAX9286 sink endpoint. - - required: - - remote-endpoint - - additionalProperties: false - - required: - - endpoint - - additionalProperties: false + $ref: /schemas/graph.yaml#/properties/port + description: Connection to the MAX9286 sink. required: - compatible diff --git a/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml b/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml index bb3528315f20..701f4e0d138f 100644 --- a/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml +++ b/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml @@ -71,19 +71,18 @@ properties: enum: [ 0, 180 ] port: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + properties: endpoint: - type: object + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + properties: - link-frequencies: - $ref: /schemas/types.yaml#/definitions/uint64-array - description: List of allowed data link frequencies. - data-lanes: - minItems: 1 - maxItems: 8 + link-frequencies: true + data-lanes: true bus-type: - description: The type of the data bus. oneOf: - const: 1 # CSI-2 C-PHY - const: 3 # CCP2 diff --git a/Documentation/devicetree/bindings/media/i2c/ov5647.yaml b/Documentation/devicetree/bindings/media/i2c/ov5647.yaml new file mode 100644 index 000000000000..3b1ea9da437a --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ov5647.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ov5647.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Omnivision OV5647 raw image sensor + +maintainers: + - Dave Stevenson + - Jacopo Mondi + +description: |- + The OV5647 is a raw image sensor with MIPI CSI-2 and CCP2 image data + interfaces and CCI (I2C compatible) control bus. + +properties: + compatible: + const: ovti,ov5647 + + reg: + description: I2C device address. + maxItems: 1 + + clocks: + description: Reference to the xclk clock. + maxItems: 1 + + pwdn-gpios: + description: Reference to the GPIO connected to the pwdn pin. Active high. + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + clock-noncontinuous: true + + additionalProperties: false + +required: + - compatible + - reg + - clocks + - port + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + ov5647: camera@36 { + compatible = "ovti,ov5647"; + reg = <0x36>; + clocks = <&camera_clk>; + pwdn-gpios = <&pioE 29 GPIO_ACTIVE_HIGH>; + + port { + camera_out: endpoint { + remote-endpoint = <&csi1_ep1>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/i2c/ov8856.yaml b/Documentation/devicetree/bindings/media/i2c/ov8856.yaml index cde85553fd01..baf92aaaf049 100644 --- a/Documentation/devicetree/bindings/media/i2c/ov8856.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ov8856.yaml @@ -57,16 +57,13 @@ properties: active low. port: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base additionalProperties: false - description: - A node containing an output port node with an endpoint definition - as documented in - Documentation/devicetree/bindings/media/video-interfaces.txt properties: endpoint: - type: object + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false properties: data-lanes: @@ -79,18 +76,14 @@ properties: - const: 4 link-frequencies: - $ref: /schemas/types.yaml#/definitions/uint64-array - description: - Allowed data bus frequencies. 360000000, 180000000 Hz or both - are supported by the driver. - + description: Frequencies listed are driver, not h/w limitations. + maxItems: 2 + items: + enum: [ 360000000, 180000000 ] required: - link-frequencies - required: - - endpoint - required: - compatible - reg @@ -139,4 +132,3 @@ examples: }; }; ... - diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml index 1c3879ec4122..63a040944f3d 100644 --- a/Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml @@ -17,6 +17,9 @@ description: |- @ 1600x1200 (UXGA) resolution transferred over a 1-lane MIPI interface. The sensor output is available via CSI-2 serial data output. +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + properties: compatible: const: ovti,ov02a10 @@ -66,42 +69,34 @@ properties: maxItems: 1 rotation: - description: - Definition of the sensor's placement. - allOf: - - $ref: "/schemas/types.yaml#/definitions/uint32" - - enum: - - 0 # Sensor Mounted Upright - - 180 # Sensor Mounted Upside Down - default: 0 - - # See ../video-interfaces.txt for details + enum: + - 0 # Sensor Mounted Upright + - 180 # Sensor Mounted Upside Down + default: 0 + port: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base additionalProperties: false description: Output port node, single endpoint describing the CSI-2 transmitter. properties: endpoint: - type: object - additionalProperties: false + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false properties: link-frequencies: true ovti,mipi-clock-voltage: - allOf: - - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: "/schemas/types.yaml#/definitions/uint32" description: Definition of MIPI clock voltage unit. This entry corresponds to the link speed defined by the 'link-frequencies' property. If present, the value shall be in the range of 0-4. default: 4 - remote-endpoint: true required: - link-frequencies - - remote-endpoint required: - endpoint diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml index 43bf749807e1..cf456f8d9ddc 100644 --- a/Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml @@ -50,11 +50,9 @@ properties: Definition of the regulator used as digital power supply. port: - type: object + $ref: /schemas/graph.yaml#/properties/port description: - A node containing an output port node with an endpoint definition - as documented in - Documentation/devicetree/bindings/media/video-interfaces.txt + A node containing an output port node. required: - compatible diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov772x.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov772x.yaml index 6866c2cdac50..44529425ce3a 100644 --- a/Documentation/devicetree/bindings/media/i2c/ovti,ov772x.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov772x.yaml @@ -37,13 +37,14 @@ properties: maxItems: 1 port: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base description: | - Video output port. See ../video-interfaces.txt. + Video output port. properties: endpoint: - type: object + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false properties: bus-type: @@ -91,8 +92,6 @@ properties: required: - bus-type - unevaluatedProperties: false - additionalProperties: false required: diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml index eb12526a462f..c9760f895b3e 100644 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml @@ -15,6 +15,9 @@ description: | interface. Image data is sent through MIPI CSI-2, through 2 or 4 lanes at a maximum throughput of 1.2Gbps/lane. +allOf: + - $ref: ../video-interface-devices.yaml# + properties: compatible: const: sony,imx214 @@ -44,25 +47,21 @@ properties: vddd-supply: description: Chip digital core regulator (1.12V). - flash-leds: - description: See ../video-interfaces.txt - - lens-focus: - description: See ../video-interfaces.txt + flash-leds: true + lens-focus: true port: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base description: | - Video output port. See ../video-interfaces.txt. + Video output port. properties: endpoint: - type: object + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false properties: data-lanes: - $ref: /schemas/types.yaml#/definitions/uint32-array - description: See ../video-interfaces.txt anyOf: - items: - const: 1 @@ -73,16 +72,12 @@ properties: - const: 3 - const: 4 - link-frequencies: - $ref: /schemas/types.yaml#/definitions/uint64-array - description: See ../video-interfaces.txt + link-frequencies: true required: - data-lanes - link-frequencies - unevaluatedProperties: false - additionalProperties: false required: diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx274.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx274.yaml index a66acb20d59b..4271fc3cc623 100644 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx274.yaml +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx274.yaml @@ -41,8 +41,7 @@ properties: description: Sensor digital IO 1.2 V supply. port: - type: object - description: Output video port. See ../video-interfaces.txt. + $ref: /schemas/graph.yaml#/properties/port required: - compatible diff --git a/Documentation/devicetree/bindings/media/marvell,mmp2-ccic.yaml b/Documentation/devicetree/bindings/media/marvell,mmp2-ccic.yaml index 49bff738aca5..9e85c70d1a1f 100644 --- a/Documentation/devicetree/bindings/media/marvell,mmp2-ccic.yaml +++ b/Documentation/devicetree/bindings/media/marvell,mmp2-ccic.yaml @@ -24,29 +24,20 @@ properties: maxItems: 1 port: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base additionalProperties: false properties: endpoint: - type: object - additionalProperties: false + $ref: video-interfaces.yaml# + unevaluatedProperties: false - # Properties described in - # Documentation/devicetree/bindings/media/video-interfaces.txt properties: - remote-endpoint: true hsync-active: true vsync-active: true pclk-sample: true bus-type: true - required: - - remote-endpoint - - required: - - endpoint - clocks: minItems: 1 maxItems: 3 diff --git a/Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml b/Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml index 4e81a47e60ac..d91575b8ebb9 100644 --- a/Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml +++ b/Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml @@ -33,10 +33,7 @@ properties: - const: mclk port: - type: object - description: - A node containing input port nodes with endpoint definitions as documented - in Documentation/devicetree/bindings/media/video-interfaces.txt + $ref: /schemas/graph.yaml#/properties/port required: - compatible diff --git a/Documentation/devicetree/bindings/media/nxp,imx7-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/nxp,imx7-mipi-csi2.yaml index 0668332959e7..be47a7b62ca9 100644 --- a/Documentation/devicetree/bindings/media/nxp,imx7-mipi-csi2.yaml +++ b/Documentation/devicetree/bindings/media/nxp,imx7-mipi-csi2.yaml @@ -58,35 +58,22 @@ properties: Differential receiver (HS-RX) settle time ports: - type: object - description: - A node containing input and output port nodes with endpoint definitions - as documented in - Documentation/devicetree/bindings/media/video-interfaces.txt + $ref: /schemas/graph.yaml#/properties/ports properties: - '#address-cells': - const: 1 - - '#size-cells': - const: 0 - port@0: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false description: Input port node, single endpoint describing the CSI-2 transmitter. properties: - reg: - const: 0 - endpoint: - type: object + $ref: video-interfaces.yaml# + unevaluatedProperties: false properties: data-lanes: - $ref: /schemas/types.yaml#/definitions/uint32-array - description: See ../video-interfaces.txt oneOf: - items: - const: 1 @@ -94,18 +81,11 @@ properties: - const: 1 - const: 2 - remote-endpoint: true - required: - data-lanes - - remote-endpoint - - additionalProperties: false - - additionalProperties: false port@1: - type: object + $ref: /schemas/graph.yaml#/properties/port description: Output port node diff --git a/Documentation/devicetree/bindings/media/renesas,ceu.yaml b/Documentation/devicetree/bindings/media/renesas,ceu.yaml index c7e1e4fe67e6..50e0740af15a 100644 --- a/Documentation/devicetree/bindings/media/renesas,ceu.yaml +++ b/Documentation/devicetree/bindings/media/renesas,ceu.yaml @@ -34,18 +34,15 @@ properties: maxItems: 1 port: - type: object - additionalProperties: false + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false properties: endpoint: - type: object - additionalProperties: false + $ref: video-interfaces.yaml# + unevaluatedProperties: false - # Properties described in - # Documentation/devicetree/bindings/media/video-interfaces.txt properties: - remote-endpoint: true hsync-active: true vsync-active: true field-even-active: false @@ -53,12 +50,6 @@ properties: enum: [8, 16] default: 8 - required: - - remote-endpoint - - required: - - endpoint - required: - compatible - reg diff --git a/Documentation/devicetree/bindings/media/renesas,csi2.yaml b/Documentation/devicetree/bindings/media/renesas,csi2.yaml index 533c2f181db7..20396f1be999 100644 --- a/Documentation/devicetree/bindings/media/renesas,csi2.yaml +++ b/Documentation/devicetree/bindings/media/renesas,csi2.yaml @@ -46,24 +46,19 @@ properties: maxItems: 1 ports: - type: object - description: - A node containing input and output port nodes with endpoint definitions - as documented in - Documentation/devicetree/bindings/media/video-interfaces.txt + $ref: /schemas/graph.yaml#/properties/ports properties: port@0: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false description: Input port node, single endpoint describing the CSI-2 transmitter. properties: - reg: - const: 0 - endpoint: - type: object + $ref: video-interfaces.yaml# + unevaluatedProperties: false properties: clock-lanes: @@ -72,50 +67,19 @@ properties: data-lanes: maxItems: 1 - remote-endpoint: true - required: - clock-lanes - data-lanes - - remote-endpoint - - additionalProperties: false - - additionalProperties: false port@1: - type: object + $ref: /schemas/graph.yaml#/properties/port description: Output port node, multiple endpoints describing all the R-Car VIN modules connected the CSI-2 receiver. - properties: - '#address-cells': - const: 1 - - '#size-cells': - const: 0 - - reg: - const: 1 - - patternProperties: - "^endpoint@[0-9a-f]$": - type: object - - properties: - reg: - maxItems: 1 - - remote-endpoint: true - - required: - - reg - - remote-endpoint - - additionalProperties: false - - additionalProperties: false + required: + - port@0 + - port@1 required: - compatible diff --git a/Documentation/devicetree/bindings/media/renesas,vin.yaml b/Documentation/devicetree/bindings/media/renesas,vin.yaml index ad2fe660364b..fe7c4cbfe4ba 100644 --- a/Documentation/devicetree/bindings/media/renesas,vin.yaml +++ b/Documentation/devicetree/bindings/media/renesas,vin.yaml @@ -69,15 +69,15 @@ properties: #The per-board settings for Gen2 and RZ/G1 platforms: port: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false description: - A node containing a parallel input with a single endpoint definitions as - documented in - Documentation/devicetree/bindings/media/video-interfaces.txt + A node containing a parallel input properties: endpoint: - type: object + $ref: video-interfaces.yaml# + unevaluatedProperties: false properties: hsync-active: @@ -106,15 +106,6 @@ properties: data-active: true - remote-endpoint: true - - required: - - remote-endpoint - - additionalProperties: false - - additionalProperties: false - #The per-board settings for Gen3 and RZ/G2 platforms: renesas,id: description: VIN channel number @@ -123,23 +114,18 @@ properties: maximum: 15 ports: - type: object - description: - A node containing input nodes with endpoint definitions as documented in - Documentation/devicetree/bindings/media/video-interfaces.txt + $ref: /schemas/graph.yaml#/properties/ports properties: port@0: - type: object + $ref: /schemas/graph.yaml#/properties/port description: Input port node, single endpoint describing a parallel input source. properties: - reg: - const: 0 - endpoint: - type: object + $ref: video-interfaces.yaml# + unevaluatedProperties: false properties: hsync-active: @@ -168,98 +154,29 @@ properties: data-active: true - remote-endpoint: true - - required: - - remote-endpoint - - additionalProperties: false - - required: - - endpoint - - additionalProperties: false - port@1: - type: object + $ref: /schemas/graph.yaml#/properties/port description: Input port node, multiple endpoints describing all the R-Car CSI-2 modules connected the VIN. properties: - '#address-cells': - const: 1 - - '#size-cells': - const: 0 - - reg: - const: 1 - endpoint@0: - type: object + $ref: /schemas/graph.yaml#/properties/endpoint description: Endpoint connected to CSI20. - properties: - reg: - const: 0 - - remote-endpoint: true - - required: - - reg - - remote-endpoint - - additionalProperties: false - endpoint@1: - type: object + $ref: /schemas/graph.yaml#/properties/endpoint description: Endpoint connected to CSI21. - properties: - reg: - const: 1 - - remote-endpoint: true - - required: - - reg - - remote-endpoint - - additionalProperties: false - endpoint@2: - type: object + $ref: /schemas/graph.yaml#/properties/endpoint description: Endpoint connected to CSI40. - properties: - reg: - const: 2 - - remote-endpoint: true - - required: - - reg - - remote-endpoint - - additionalProperties: false - endpoint@3: - type: object + $ref: /schemas/graph.yaml#/properties/endpoint description: Endpoint connected to CSI41. - properties: - reg: - const: 3 - - remote-endpoint: true - - required: - - reg - - remote-endpoint - - additionalProperties: false - anyOf: - required: - endpoint@0 @@ -270,8 +187,6 @@ properties: - required: - endpoint@3 - additionalProperties: false - required: - compatible - reg diff --git a/Documentation/devicetree/bindings/media/rockchip-isp1.yaml b/Documentation/devicetree/bindings/media/rockchip-isp1.yaml index 2004c054ed1a..a6b1eff879ed 100644 --- a/Documentation/devicetree/bindings/media/rockchip-isp1.yaml +++ b/Documentation/devicetree/bindings/media/rockchip-isp1.yaml @@ -56,56 +56,26 @@ properties: power-domains: maxItems: 1 - # See ./video-interfaces.txt for details ports: - type: object - additionalProperties: false + $ref: /schemas/graph.yaml#/properties/ports properties: - "#address-cells": - const: 1 - - "#size-cells": - const: 0 - port@0: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false description: connection point for sensors at MIPI-DPHY RX0 - additionalProperties: false properties: - "#address-cells": - const: 1 - - "#size-cells": - const: 0 - - reg: - const: 0 - - patternProperties: endpoint: - type: object - additionalProperties: false + $ref: video-interfaces.yaml# + unevaluatedProperties: false properties: - reg: - maxItems: 1 - data-lanes: minItems: 1 maxItems: 4 - remote-endpoint: true - - required: - - reg - - "#address-cells" - - "#size-cells" - required: - - "#address-cells" - - "#size-cells" - port@0 required: diff --git a/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml b/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml index c18574bb3e81..41e1d0cd80e5 100644 --- a/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml +++ b/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml @@ -37,16 +37,15 @@ properties: maxItems: 1 port: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false description: - DCMI supports a single port node with parallel bus. It should contain - one 'port' child node with child 'endpoint' node. Please refer to the - bindings defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. + DCMI supports a single port node with parallel bus. properties: endpoint: - type: object + $ref: video-interfaces.yaml# + unevaluatedProperties: false properties: bus-type: @@ -57,8 +56,6 @@ properties: enum: [8, 10, 12, 14] default: 8 - remote-endpoint: true - allOf: - if: properties: @@ -73,14 +70,9 @@ properties: enum: [8] required: - - remote-endpoint - bus-type - pclk-sample - unevaluatedProperties: false - - additionalProperties: false - required: - compatible - reg diff --git a/Documentation/devicetree/bindings/media/ti,cal.yaml b/Documentation/devicetree/bindings/media/ti,cal.yaml index 5e066629287d..65177cd69514 100644 --- a/Documentation/devicetree/bindings/media/ti,cal.yaml +++ b/Documentation/devicetree/bindings/media/ti,cal.yaml @@ -15,10 +15,7 @@ description: |- processing capability to connect CSI2 image-sensor modules to the DRA72x device. - CAL supports 2 camera port nodes on MIPI bus. Each CSI2 camera port nodes - should contain a 'port' child node with child 'endpoint' node. Please - refer to the bindings defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. + CAL supports 2 camera port nodes on MIPI bus. properties: compatible: @@ -67,31 +64,19 @@ properties: Documentation/devicetree/bindings/power/power_domain.txt maxItems: 1 - # See ./video-interfaces.txt for details ports: - type: object - additionalProperties: false + $ref: /schemas/graph.yaml#/properties/ports properties: - "#address-cells": - const: 1 - - "#size-cells": - const: 0 - port@0: - type: object - additionalProperties: false + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI2 Port #0 properties: - reg: - const: 0 - description: CSI2 Port #0 - - patternProperties: endpoint: - type: object - additionalProperties: false + $ref: video-interfaces.yaml# + unevaluatedProperties: false properties: clock-lanes: @@ -101,24 +86,15 @@ properties: minItems: 1 maxItems: 4 - remote-endpoint: true - - required: - - reg - port@1: - type: object - additionalProperties: false + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI2 Port #1 properties: - reg: - const: 1 - description: CSI2 Port #1 - - patternProperties: endpoint: - type: object - additionalProperties: false + $ref: video-interfaces.yaml# + unevaluatedProperties: false properties: clock-lanes: @@ -128,14 +104,7 @@ properties: minItems: 1 maxItems: 4 - remote-endpoint: true - - required: - - reg - required: - - "#address-cells" - - "#size-cells" - port@0 required: diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.yaml b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.yaml index 2961a5b6872f..7d77823dbb7a 100644 --- a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.yaml +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.yaml @@ -97,24 +97,21 @@ properties: maxItems: 1 ports: - type: object + $ref: /schemas/graph.yaml#/properties/ports properties: port@0: - type: object + $ref: /schemas/graph.yaml#/$defs/port-base description: | Input / sink port node, single endpoint describing the CSI-2 transmitter. properties: - reg: - const: 0 - endpoint: - type: object + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false properties: - data-lanes: description: | This is required only in the sink port 0 endpoint which @@ -130,41 +127,17 @@ properties: - const: 3 - const: 4 - remote-endpoint: true - required: - data-lanes - - remote-endpoint - - additionalProperties: false - additionalProperties: false + unevaluatedProperties: false port@1: - type: object + $ref: /schemas/graph.yaml#/properties/port description: | Output / source port node, endpoint describing modules connected the CSI-2 receiver. - properties: - - reg: - const: 1 - - endpoint: - type: object - - properties: - - remote-endpoint: true - - required: - - remote-endpoint - - additionalProperties: false - - additionalProperties: false - required: - compatible - reg -- cgit v1.2.3 From 36e4f2b2e3f7a9d8f9f08bc68a05d64d23b34c3d Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Tue, 5 Jan 2021 15:47:19 +0100 Subject: media: i2c: ov5648/ov8865: Minor cosmetic fixes This solves a few minor cosmetic issues picked up by checkpatch for the OV5648 and OV8865 drivers. Signed-off-by: Paul Kocialkowski Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5648.c | 3 ++- drivers/media/i2c/ov8865.c | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c index 609aa67b54ce..110190b322e2 100644 --- a/drivers/media/i2c/ov5648.c +++ b/drivers/media/i2c/ov5648.c @@ -496,7 +496,8 @@ container_of(s, struct ov5648_sensor, subdev) #define ov5648_ctrl_subdev(c) \ - (&container_of(c->handler, struct ov5648_sensor, ctrls.handler)->subdev) + (&container_of((c)->handler, struct ov5648_sensor, \ + ctrls.handler)->subdev) /* Data structures */ diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index fda5a55979aa..ab4804d5b285 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -223,7 +223,6 @@ #define OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN BIT(1) #define OV8865_FORMAT2_SYNC_HBIN_EN BIT(0) - #define OV8865_INC_Y_ODD_REG 0x382a #define OV8865_INC_Y_ODD(v) ((v) & GENMASK(4, 0)) #define OV8865_INC_Y_EVEN_REG 0x382b @@ -460,7 +459,8 @@ container_of(s, struct ov8865_sensor, subdev) #define ov8865_ctrl_subdev(c) \ - (&container_of(c->handler, struct ov8865_sensor, ctrls.handler)->subdev) + (&container_of((c)->handler, struct ov8865_sensor, \ + ctrls.handler)->subdev) /* Data structures */ @@ -1598,8 +1598,8 @@ static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor, return ret; ret = ov8865_update_bits(sensor, OV8865_PCLK_SEL_REG, - OV8865_PCLK_SEL_PCLK_DIV_MASK, - OV8865_PCLK_SEL_PCLK_DIV(config->pclk_div)); + OV8865_PCLK_SEL_PCLK_DIV_MASK, + OV8865_PCLK_SEL_PCLK_DIV(config->pclk_div)); if (ret) return ret; -- cgit v1.2.3 From 54c261891ced2566cdd5a54c84fcc94a7322912a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 13 Jan 2021 09:34:41 +0100 Subject: media: Documentation: media: Fix recently introduced build warning in subdev docs A reference to the sub-device pad ops was not follwed by a whitespace, resulting in a warning during documentation build. Fix it. Reported-by: Stephen Rothwell Fixes: 25c8d9a7689e ("media: Documentation: v4l: Document that link_validate op is valid for sink only") Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/media/v4l2-subdev.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/media/v4l2-subdev.rst b/Documentation/driver-api/media/v4l2-subdev.rst index 6d5c799c49fe..0e82c77cf3e2 100644 --- a/Documentation/driver-api/media/v4l2-subdev.rst +++ b/Documentation/driver-api/media/v4l2-subdev.rst @@ -123,7 +123,7 @@ Don't forget to cleanup the media entity before the sub-device is destroyed: media_entity_cleanup(&sd->entity); If a sub-device driver implements sink pads, the subdev driver may set the -link_validate field in :c:type:`v4l2_subdev_pad_ops`to provide its own link +link_validate field in :c:type:`v4l2_subdev_pad_ops` to provide its own link validation function. For every link in the pipeline, the link_validate pad operation of the sink end of the link is called. In both cases the driver is still responsible for validating the correctness of the format configuration -- cgit v1.2.3 From fb5ec981adf08b94e6ce27ca16b7765c94f4513c Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:24 +0100 Subject: media: software_node: Fix refcounts in software_node_get_next_child() The software_node_get_next_child() function currently does not hold references to the child software_node that it finds or put the ref that is held against the old child - fix that. Fixes: 59abd83672f7 ("drivers: base: Introducing software nodes to the firmware node framework") Reviewed-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Scally Reviewed-by: Heikki Krogerus Acked-by: Greg Kroah-Hartman Acked-by: Rafael J. Wysocki Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/base/swnode.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 4a4b2008fbc2..4fcc1a6fb724 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -443,14 +443,18 @@ software_node_get_next_child(const struct fwnode_handle *fwnode, struct swnode *c = to_swnode(child); if (!p || list_empty(&p->children) || - (c && list_is_last(&c->entry, &p->children))) + (c && list_is_last(&c->entry, &p->children))) { + fwnode_handle_put(child); return NULL; + } if (c) c = list_next_entry(c, entry); else c = list_first_entry(&p->children, struct swnode, entry); - return &c->fwnode; + + fwnode_handle_put(child); + return fwnode_handle_get(&c->fwnode); } static struct fwnode_handle * -- cgit v1.2.3 From ec9ded4fa864b1eeafb496902b014f5d5dd52994 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 7 Jan 2021 14:28:25 +0100 Subject: media: ipu3-cio2: Add headers that ipu3-cio2.h is direct user of Add headers that ipu3-cio2.h is direct user of. Signed-off-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Scally Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/intel/ipu3/ipu3-cio2.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h index ccf0b85ae36f..62187ab5ae43 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h @@ -4,8 +4,26 @@ #ifndef __IPU3_CIO2_H #define __IPU3_CIO2_H +#include +#include +#include +#include #include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct cio2_fbpt_entry; /* defined here, after the first usage */ +struct pci_dev; + #define CIO2_NAME "ipu3-cio2" #define CIO2_DEVICE_NAME "Intel IPU3 CIO2" #define CIO2_ENTITY_NAME "ipu3-csi2" -- cgit v1.2.3 From 5273382d03763277aaf8c6a2d6088e2afaee0cf0 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:26 +0100 Subject: media: device property: Return true in fwnode_device_is_available for NULL ops Some types of fwnode_handle do not implement the device_is_available() check, such as those created by software_nodes. There isn't really a meaningful way to check for the availability of a device that doesn't actually exist, so if the check isn't implemented just assume that the "device" is present. Suggested-by: Laurent Pinchart Reviewed-by: Laurent Pinchart Reviewed-by: Andy Shevchenko Signed-off-by: Daniel Scally Reviewed-by: Heikki Krogerus Acked-by: Greg Kroah-Hartman Acked-by: Rafael J. Wysocki Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/base/property.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/base/property.c b/drivers/base/property.c index 35b95c6ac0c6..0bf5260f14c6 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -837,9 +837,15 @@ EXPORT_SYMBOL_GPL(fwnode_handle_put); /** * fwnode_device_is_available - check if a device is available for use * @fwnode: Pointer to the fwnode of the device. + * + * For fwnode node types that don't implement the .device_is_available() + * operation, this function returns true. */ bool fwnode_device_is_available(const struct fwnode_handle *fwnode) { + if (!fwnode_has_op(fwnode, device_is_available)) + return true; + return fwnode_call_bool_op(fwnode, device_is_available); } EXPORT_SYMBOL_GPL(fwnode_device_is_available); -- cgit v1.2.3 From acd418bfcfc415cf5e6414b6d1c6acfec850f290 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:27 +0100 Subject: media: device property: Call fwnode_graph_get_endpoint_by_id() for fwnode->secondary This function is used to find fwnode endpoints against a device. In some instances those endpoints are software nodes which are children of fwnode->secondary. Add support to fwnode_graph_get_endpoint_by_id() to find those endpoints by recursively calling itself passing the ptr to fwnode->secondary in the event no endpoint is found for the primary. Reviewed-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Scally Acked-by: Greg Kroah-Hartman Acked-by: Rafael J. Wysocki Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/base/property.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index 0bf5260f14c6..1421e9548857 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -1215,7 +1215,14 @@ fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode, best_ep_id = fwnode_ep.id; } - return best_ep; + if (best_ep) + return best_ep; + + if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) + return fwnode_graph_get_endpoint_by_id(fwnode->secondary, port, + endpoint, flags); + + return NULL; } EXPORT_SYMBOL_GPL(fwnode_graph_get_endpoint_by_id); -- cgit v1.2.3 From d9b1103bc622b20498c1d2fb9c00805b9d6f2921 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:28 +0100 Subject: media: software_node: Enforce parent before child ordering of nodes arrays Registering software_nodes with the .parent member set to point to a currently unregistered software_node has the potential for problems, so enforce parent -> child ordering in arrays passed in to software_node_register_nodes(). Software nodes that are children of another software node should be unregistered before their parent. To allow easy unregistering of an array of software_nodes ordered parent to child, reverse the order in which software_node_unregister_nodes() unregisters software_nodes. Suggested-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Scally Reviewed-by: Heikki Krogerus Acked-by: Greg Kroah-Hartman Acked-by: Rafael J. Wysocki Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/base/swnode.c | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 4fcc1a6fb724..166c5cc73f39 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -692,7 +692,11 @@ out_err: * software_node_register_nodes - Register an array of software nodes * @nodes: Zero terminated array of software nodes to be registered * - * Register multiple software nodes at once. + * Register multiple software nodes at once. If any node in the array + * has its .parent pointer set (which can only be to another software_node), + * then its parent **must** have been registered before it is; either outside + * of this function or by ordering the array such that parent comes before + * child. */ int software_node_register_nodes(const struct software_node *nodes) { @@ -700,14 +704,23 @@ int software_node_register_nodes(const struct software_node *nodes) int i; for (i = 0; nodes[i].name; i++) { - ret = software_node_register(&nodes[i]); - if (ret) { - software_node_unregister_nodes(nodes); - return ret; + const struct software_node *parent = nodes[i].parent; + + if (parent && !software_node_to_swnode(parent)) { + ret = -EINVAL; + goto err_unregister_nodes; } + + ret = software_node_register(&nodes[i]); + if (ret) + goto err_unregister_nodes; } return 0; + +err_unregister_nodes: + software_node_unregister_nodes(nodes); + return ret; } EXPORT_SYMBOL_GPL(software_node_register_nodes); @@ -715,18 +728,23 @@ EXPORT_SYMBOL_GPL(software_node_register_nodes); * software_node_unregister_nodes - Unregister an array of software nodes * @nodes: Zero terminated array of software nodes to be unregistered * - * Unregister multiple software nodes at once. + * Unregister multiple software nodes at once. If parent pointers are set up + * in any of the software nodes then the array **must** be ordered such that + * parents come before their children. * - * NOTE: Be careful using this call if the nodes had parent pointers set up in - * them before registering. If so, it is wiser to remove the nodes - * individually, in the correct order (child before parent) instead of relying - * on the sequential order of the list of nodes in the array. + * NOTE: If you are uncertain whether the array is ordered such that + * parents will be unregistered before their children, it is wiser to + * remove the nodes individually, in the correct order (child before + * parent). */ void software_node_unregister_nodes(const struct software_node *nodes) { - int i; + unsigned int i = 0; + + while (nodes[i].name) + i++; - for (i = 0; nodes[i].name; i++) + while (i--) software_node_unregister(&nodes[i]); } EXPORT_SYMBOL_GPL(software_node_unregister_nodes); -- cgit v1.2.3 From fc002f0f23e28026bb680fa34e3797de9edefdbb Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:29 +0100 Subject: media: software_node: unregister software_nodes in reverse order To maintain consistency with software_node_unregister_nodes(), reverse the order in which the software_node_unregister_node_group() function unregisters nodes. Reported-by: kernel test robot Reported-by: Dan Carpenter Reviewed-by: Laurent Pinchart Suggested-by: Andy Shevchenko Signed-off-by: Daniel Scally Reviewed-by: Heikki Krogerus Acked-by: Greg Kroah-Hartman Acked-by: Rafael J. Wysocki Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/base/swnode.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 166c5cc73f39..6f7443c6d3b5 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -779,16 +779,23 @@ EXPORT_SYMBOL_GPL(software_node_register_node_group); * software_node_unregister_node_group - Unregister a group of software nodes * @node_group: NULL terminated array of software node pointers to be unregistered * - * Unregister multiple software nodes at once. + * Unregister multiple software nodes at once. The array will be unwound in + * reverse order (i.e. last entry first) and thus if any members of the array are + * children of another member then the children must appear later in the list such + * that they are unregistered first. */ -void software_node_unregister_node_group(const struct software_node **node_group) +void software_node_unregister_node_group( + const struct software_node **node_group) { - unsigned int i; + unsigned int i = 0; if (!node_group) return; - for (i = 0; node_group[i]; i++) + while (node_group[i]) + i++; + + while (i--) software_node_unregister(node_group[i]); } EXPORT_SYMBOL_GPL(software_node_unregister_node_group); -- cgit v1.2.3 From 529b56a854c5a8bd5e6f045cdcbbcd21a1616fbd Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:30 +0100 Subject: media: device property: Define format macros for ports and endpoints OF, ACPI and software_nodes all implement graphs including nodes for ports and endpoints. These are all intended to be named with a common schema, as "port@n" and "endpoint@n" where n is an unsigned int representing the index of the node. To ensure commonality across the subsystems, provide a set of macros to define the format. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Scally Reviewed-by: Heikki Krogerus Acked-by: Rafael J. Wysocki Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/linux/fwnode.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index fde4ad97564c..77414e431e89 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -50,6 +50,13 @@ struct fwnode_endpoint { const struct fwnode_handle *local_fwnode; }; +/* + * ports and endpoints defined as software_nodes should all follow a common + * naming scheme; use these macros to ensure commonality. + */ +#define SWNODE_GRAPH_PORT_NAME_FMT "port@%u" +#define SWNODE_GRAPH_ENDPOINT_NAME_FMT "endpoint@%u" + #define NR_FWNODE_REFERENCE_ARGS 8 /** -- cgit v1.2.3 From 000c08fda62cbb5fcee9d2c60b5ecff3ef4c2267 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 7 Jan 2021 14:28:31 +0100 Subject: media: software_node: Add support for fwnode_graph*() family of functions This implements the remaining .graph_*() callbacks in the fwnode operations structure for the software nodes. That makes the fwnode_graph_*() functions available in the drivers also when software nodes are used. The implementation tries to mimic the "OF graph" as much as possible, but there is no support for the "reg" device property. The ports will need to have the index in their name which starts with "port@" (for example "port@0", "port@1", ...) and endpoints will use the index of the software node that is given to them during creation. The port nodes can also be grouped under a specially named "ports" subnode, just like in DT, if necessary. The remote-endpoints are reference properties under the endpoint nodes that are named "remote-endpoint". Reviewed-by: Laurent Pinchart Reviewed-by: Andy Shevchenko Signed-off-by: Heikki Krogerus Co-developed-by: Daniel Scally Signed-off-by: Daniel Scally Acked-by: Greg Kroah-Hartman Acked-by: Rafael J. Wysocki Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/base/swnode.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 6f7443c6d3b5..9104a0abd531 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -540,6 +540,115 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, return 0; } +static struct fwnode_handle * +swnode_graph_find_next_port(const struct fwnode_handle *parent, + struct fwnode_handle *port) +{ + struct fwnode_handle *old = port; + + while ((port = software_node_get_next_child(parent, old))) { + /* + * fwnode ports have naming style "port@", so we search for any + * children that follow that convention. + */ + if (!strncmp(to_swnode(port)->node->name, "port@", + strlen("port@"))) + return port; + old = port; + } + + return NULL; +} + +static struct fwnode_handle * +software_node_graph_get_next_endpoint(const struct fwnode_handle *fwnode, + struct fwnode_handle *endpoint) +{ + struct swnode *swnode = to_swnode(fwnode); + struct fwnode_handle *parent; + struct fwnode_handle *port; + + if (!swnode) + return NULL; + + if (endpoint) { + port = software_node_get_parent(endpoint); + parent = software_node_get_parent(port); + } else { + parent = software_node_get_named_child_node(fwnode, "ports"); + if (!parent) + parent = software_node_get(&swnode->fwnode); + + port = swnode_graph_find_next_port(parent, NULL); + } + + for (; port; port = swnode_graph_find_next_port(parent, port)) { + endpoint = software_node_get_next_child(port, endpoint); + if (endpoint) { + fwnode_handle_put(port); + break; + } + } + + fwnode_handle_put(parent); + + return endpoint; +} + +static struct fwnode_handle * +software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode) +{ + struct swnode *swnode = to_swnode(fwnode); + const struct software_node_ref_args *ref; + const struct property_entry *prop; + + if (!swnode) + return NULL; + + prop = property_entry_get(swnode->node->properties, "remote-endpoint"); + if (!prop || prop->type != DEV_PROP_REF || prop->is_inline) + return NULL; + + ref = prop->pointer; + + return software_node_get(software_node_fwnode(ref[0].node)); +} + +static struct fwnode_handle * +software_node_graph_get_port_parent(struct fwnode_handle *fwnode) +{ + struct swnode *swnode = to_swnode(fwnode); + + swnode = swnode->parent; + if (swnode && !strcmp(swnode->node->name, "ports")) + swnode = swnode->parent; + + return swnode ? software_node_get(&swnode->fwnode) : NULL; +} + +static int +software_node_graph_parse_endpoint(const struct fwnode_handle *fwnode, + struct fwnode_endpoint *endpoint) +{ + struct swnode *swnode = to_swnode(fwnode); + const char *parent_name = swnode->parent->node->name; + int ret; + + if (strlen("port@") >= strlen(parent_name) || + strncmp(parent_name, "port@", strlen("port@"))) + return -EINVAL; + + /* Ports have naming style "port@n", we need to select the n */ + ret = kstrtou32(parent_name + strlen("port@"), 10, &endpoint->port); + if (ret) + return ret; + + endpoint->id = swnode->id; + endpoint->local_fwnode = fwnode; + + return 0; +} + static const struct fwnode_operations software_node_ops = { .get = software_node_get, .put = software_node_put, @@ -551,7 +660,11 @@ static const struct fwnode_operations software_node_ops = { .get_parent = software_node_get_parent, .get_next_child_node = software_node_get_next_child, .get_named_child_node = software_node_get_named_child_node, - .get_reference_args = software_node_get_reference_args + .get_reference_args = software_node_get_reference_args, + .graph_get_next_endpoint = software_node_graph_get_next_endpoint, + .graph_get_remote_endpoint = software_node_graph_get_remote_endpoint, + .graph_get_port_parent = software_node_graph_get_port_parent, + .graph_parse_endpoint = software_node_graph_parse_endpoint, }; /* -------------------------------------------------------------------------- */ -- cgit v1.2.3 From f0328be57568c7954061284a533c98f91bd9938f Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:32 +0100 Subject: media: lib/test_printf.c: Use helper function to unwind array of software_nodes Use the software_node_unregister_nodes() helper function to unwind this array in a cleaner way. Acked-by: Petr Mladek Reviewed-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Reviewed-by: Sergey Senozhatsky Suggested-by: Andy Shevchenko Signed-off-by: Daniel Scally Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- lib/test_printf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/test_printf.c b/lib/test_printf.c index 7ac87f18a10f..7d60f24240a4 100644 --- a/lib/test_printf.c +++ b/lib/test_printf.c @@ -644,9 +644,7 @@ static void __init fwnode_pointer(void) test(second_name, "%pfwP", software_node_fwnode(&softnodes[1])); test(third_name, "%pfwP", software_node_fwnode(&softnodes[2])); - software_node_unregister(&softnodes[2]); - software_node_unregister(&softnodes[1]); - software_node_unregister(&softnodes[0]); + software_node_unregister_nodes(softnodes); } static void __init -- cgit v1.2.3 From 900104c8483736f8e022ff3b084c0928e895d903 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:33 +0100 Subject: media: ipu3-cio2: Add T: entry to MAINTAINERS Development for the ipu3-cio2 driver is taking place in media_tree, but there's no T: entry in MAINTAINERS to denote that - rectify that oversight Reviewed-by: Laurent Pinchart Reviewed-by: Andy Shevchenko Signed-off-by: Daniel Scally Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 47fad204d6aa..e6d8674dc90a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9017,6 +9017,7 @@ M: Bingbu Cao R: Tianshu Qiu L: linux-media@vger.kernel.org S: Maintained +T: git git://linuxtv.org/media_tree.git F: Documentation/userspace-api/media/v4l/pixfmt-srggb10-ipu3.rst F: drivers/media/pci/intel/ipu3/ -- cgit v1.2.3 From acec1ff638a636f95a68271ca9b60c670a023d74 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:34 +0100 Subject: media: ipu3-cio2: Rename ipu3-cio2.c ipu3-cio2 driver needs extending with multiple files; rename the main source file and specify the renamed file in Makefile to accommodate that. Suggested-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Reviewed-by: Andy Shevchenko Signed-off-by: Daniel Scally Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/intel/ipu3/Makefile | 2 + drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 2026 +++++++++++++++++++++++++ drivers/media/pci/intel/ipu3/ipu3-cio2.c | 2026 ------------------------- 3 files changed, 2028 insertions(+), 2026 deletions(-) create mode 100644 drivers/media/pci/intel/ipu3/ipu3-cio2-main.c delete mode 100644 drivers/media/pci/intel/ipu3/ipu3-cio2.c diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile index 98ddd5beafe0..429d516452e4 100644 --- a/drivers/media/pci/intel/ipu3/Makefile +++ b/drivers/media/pci/intel/ipu3/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o + +ipu3-cio2-y += ipu3-cio2-main.o diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c new file mode 100644 index 000000000000..e8ea69d30bfd --- /dev/null +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c @@ -0,0 +1,2026 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017,2020 Intel Corporation + * + * Based partially on Intel IPU4 driver written by + * Sakari Ailus + * Samu Onkalo + * Jouni Högander + * Jouni Ukkonen + * Antti Laakso + * et al. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipu3-cio2.h" + +struct ipu3_cio2_fmt { + u32 mbus_code; + u32 fourcc; + u8 mipicode; + u8 bpp; +}; + +/* + * These are raw formats used in Intel's third generation of + * Image Processing Unit known as IPU3. + * 10bit raw bayer packed, 32 bytes for every 25 pixels, + * last LSB 6 bits unused. + */ +static const struct ipu3_cio2_fmt formats[] = { + { /* put default entry at beginning */ + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .fourcc = V4L2_PIX_FMT_IPU3_SGRBG10, + .mipicode = 0x2b, + .bpp = 10, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .fourcc = V4L2_PIX_FMT_IPU3_SGBRG10, + .mipicode = 0x2b, + .bpp = 10, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .fourcc = V4L2_PIX_FMT_IPU3_SBGGR10, + .mipicode = 0x2b, + .bpp = 10, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .fourcc = V4L2_PIX_FMT_IPU3_SRGGB10, + .mipicode = 0x2b, + .bpp = 10, + }, +}; + +/* + * cio2_find_format - lookup color format by fourcc or/and media bus code + * @pixelformat: fourcc to match, ignored if null + * @mbus_code: media bus code to match, ignored if null + */ +static const struct ipu3_cio2_fmt *cio2_find_format(const u32 *pixelformat, + const u32 *mbus_code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (pixelformat && *pixelformat != formats[i].fourcc) + continue; + if (mbus_code && *mbus_code != formats[i].mbus_code) + continue; + + return &formats[i]; + } + + return NULL; +} + +static inline u32 cio2_bytesperline(const unsigned int width) +{ + /* + * 64 bytes for every 50 pixels, the line length + * in bytes is multiple of 64 (line end alignment). + */ + return DIV_ROUND_UP(width, 50) * 64; +} + +/**************** FBPT operations ****************/ + +static void cio2_fbpt_exit_dummy(struct cio2_device *cio2) +{ + if (cio2->dummy_lop) { + dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, + cio2->dummy_lop, cio2->dummy_lop_bus_addr); + cio2->dummy_lop = NULL; + } + if (cio2->dummy_page) { + dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, + cio2->dummy_page, cio2->dummy_page_bus_addr); + cio2->dummy_page = NULL; + } +} + +static int cio2_fbpt_init_dummy(struct cio2_device *cio2) +{ + unsigned int i; + + cio2->dummy_page = dma_alloc_coherent(&cio2->pci_dev->dev, PAGE_SIZE, + &cio2->dummy_page_bus_addr, + GFP_KERNEL); + cio2->dummy_lop = dma_alloc_coherent(&cio2->pci_dev->dev, PAGE_SIZE, + &cio2->dummy_lop_bus_addr, + GFP_KERNEL); + if (!cio2->dummy_page || !cio2->dummy_lop) { + cio2_fbpt_exit_dummy(cio2); + return -ENOMEM; + } + /* + * List of Pointers(LOP) contains 1024x32b pointers to 4KB page each + * Initialize each entry to dummy_page bus base address. + */ + for (i = 0; i < CIO2_LOP_ENTRIES; i++) + cio2->dummy_lop[i] = PFN_DOWN(cio2->dummy_page_bus_addr); + + return 0; +} + +static void cio2_fbpt_entry_enable(struct cio2_device *cio2, + struct cio2_fbpt_entry entry[CIO2_MAX_LOPS]) +{ + /* + * The CPU first initializes some fields in fbpt, then sets + * the VALID bit, this barrier is to ensure that the DMA(device) + * does not see the VALID bit enabled before other fields are + * initialized; otherwise it could lead to havoc. + */ + dma_wmb(); + + /* + * Request interrupts for start and completion + * Valid bit is applicable only to 1st entry + */ + entry[0].first_entry.ctrl = CIO2_FBPT_CTRL_VALID | + CIO2_FBPT_CTRL_IOC | CIO2_FBPT_CTRL_IOS; +} + +/* Initialize fpbt entries to point to dummy frame */ +static void cio2_fbpt_entry_init_dummy(struct cio2_device *cio2, + struct cio2_fbpt_entry + entry[CIO2_MAX_LOPS]) +{ + unsigned int i; + + entry[0].first_entry.first_page_offset = 0; + entry[1].second_entry.num_of_pages = CIO2_LOP_ENTRIES * CIO2_MAX_LOPS; + entry[1].second_entry.last_page_available_bytes = PAGE_SIZE - 1; + + for (i = 0; i < CIO2_MAX_LOPS; i++) + entry[i].lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr); + + cio2_fbpt_entry_enable(cio2, entry); +} + +/* Initialize fpbt entries to point to a given buffer */ +static void cio2_fbpt_entry_init_buf(struct cio2_device *cio2, + struct cio2_buffer *b, + struct cio2_fbpt_entry + entry[CIO2_MAX_LOPS]) +{ + struct vb2_buffer *vb = &b->vbb.vb2_buf; + unsigned int length = vb->planes[0].length; + int remaining, i; + + entry[0].first_entry.first_page_offset = b->offset; + remaining = length + entry[0].first_entry.first_page_offset; + entry[1].second_entry.num_of_pages = PFN_UP(remaining); + /* + * last_page_available_bytes has the offset of the last byte in the + * last page which is still accessible by DMA. DMA cannot access + * beyond this point. Valid range for this is from 0 to 4095. + * 0 indicates 1st byte in the page is DMA accessible. + * 4095 (PAGE_SIZE - 1) means every single byte in the last page + * is available for DMA transfer. + */ + remaining = offset_in_page(remaining) ?: PAGE_SIZE; + entry[1].second_entry.last_page_available_bytes = remaining - 1; + /* Fill FBPT */ + remaining = length; + i = 0; + while (remaining > 0) { + entry->lop_page_addr = PFN_DOWN(b->lop_bus_addr[i]); + remaining -= CIO2_LOP_ENTRIES * PAGE_SIZE; + entry++; + i++; + } + + /* + * The first not meaningful FBPT entry should point to a valid LOP + */ + entry->lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr); + + cio2_fbpt_entry_enable(cio2, entry); +} + +static int cio2_fbpt_init(struct cio2_device *cio2, struct cio2_queue *q) +{ + struct device *dev = &cio2->pci_dev->dev; + + q->fbpt = dma_alloc_coherent(dev, CIO2_FBPT_SIZE, &q->fbpt_bus_addr, + GFP_KERNEL); + if (!q->fbpt) + return -ENOMEM; + + return 0; +} + +static void cio2_fbpt_exit(struct cio2_queue *q, struct device *dev) +{ + dma_free_coherent(dev, CIO2_FBPT_SIZE, q->fbpt, q->fbpt_bus_addr); +} + +/**************** CSI2 hardware setup ****************/ + +/* + * The CSI2 receiver has several parameters affecting + * the receiver timings. These depend on the MIPI bus frequency + * F in Hz (sensor transmitter rate) as follows: + * register value = (A/1e9 + B * UI) / COUNT_ACC + * where + * UI = 1 / (2 * F) in seconds + * COUNT_ACC = counter accuracy in seconds + * For IPU3 COUNT_ACC = 0.0625 + * + * A and B are coefficients from the table below, + * depending whether the register minimum or maximum value is + * calculated. + * Minimum Maximum + * Clock lane A B A B + * reg_rx_csi_dly_cnt_termen_clane 0 0 38 0 + * reg_rx_csi_dly_cnt_settle_clane 95 -8 300 -16 + * Data lanes + * reg_rx_csi_dly_cnt_termen_dlane0 0 0 35 4 + * reg_rx_csi_dly_cnt_settle_dlane0 85 -2 145 -6 + * reg_rx_csi_dly_cnt_termen_dlane1 0 0 35 4 + * reg_rx_csi_dly_cnt_settle_dlane1 85 -2 145 -6 + * reg_rx_csi_dly_cnt_termen_dlane2 0 0 35 4 + * reg_rx_csi_dly_cnt_settle_dlane2 85 -2 145 -6 + * reg_rx_csi_dly_cnt_termen_dlane3 0 0 35 4 + * reg_rx_csi_dly_cnt_settle_dlane3 85 -2 145 -6 + * + * We use the minimum values of both A and B. + */ + +/* + * shift for keeping value range suitable for 32-bit integer arithmetic + */ +#define LIMIT_SHIFT 8 + +static s32 cio2_rx_timing(s32 a, s32 b, s64 freq, int def) +{ + const u32 accinv = 16; /* invert of counter resolution */ + const u32 uiinv = 500000000; /* 1e9 / 2 */ + s32 r; + + freq >>= LIMIT_SHIFT; + + if (WARN_ON(freq <= 0 || freq > S32_MAX)) + return def; + /* + * b could be 0, -2 or -8, so |accinv * b| is always + * less than (1 << ds) and thus |r| < 500000000. + */ + r = accinv * b * (uiinv >> LIMIT_SHIFT); + r = r / (s32)freq; + /* max value of a is 95 */ + r += accinv * a; + + return r; +}; + +/* Calculate the delay value for termination enable of clock lane HS Rx */ +static int cio2_csi2_calc_timing(struct cio2_device *cio2, struct cio2_queue *q, + struct cio2_csi2_timing *timing, + unsigned int bpp, unsigned int lanes) +{ + struct device *dev = &cio2->pci_dev->dev; + s64 freq; + + if (!q->sensor) + return -ENODEV; + + freq = v4l2_get_link_rate(q->sensor->ctrl_handler, bpp, lanes); + if (freq < 0) { + dev_err(dev, "error %lld, invalid link_freq\n", freq); + return freq; + } + + timing->clk_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_A, + CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_B, + freq, + CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT); + timing->clk_settle = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_SETTLE_CLANE_A, + CIO2_CSIRX_DLY_CNT_SETTLE_CLANE_B, + freq, + CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT); + timing->dat_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_DLANE_A, + CIO2_CSIRX_DLY_CNT_TERMEN_DLANE_B, + freq, + CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT); + timing->dat_settle = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_SETTLE_DLANE_A, + CIO2_CSIRX_DLY_CNT_SETTLE_DLANE_B, + freq, + CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT); + + dev_dbg(dev, "freq ct value is %d\n", timing->clk_termen); + dev_dbg(dev, "freq cs value is %d\n", timing->clk_settle); + dev_dbg(dev, "freq dt value is %d\n", timing->dat_termen); + dev_dbg(dev, "freq ds value is %d\n", timing->dat_settle); + + return 0; +}; + +static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q) +{ + static const int NUM_VCS = 4; + static const int SID; /* Stream id */ + static const int ENTRY; + static const int FBPT_WIDTH = DIV_ROUND_UP(CIO2_MAX_LOPS, + CIO2_FBPT_SUBENTRY_UNIT); + const u32 num_buffers1 = CIO2_MAX_BUFFERS - 1; + const struct ipu3_cio2_fmt *fmt; + void __iomem *const base = cio2->base; + u8 lanes, csi2bus = q->csi2.port; + u8 sensor_vc = SENSOR_VIR_CH_DFLT; + struct cio2_csi2_timing timing; + int i, r; + + fmt = cio2_find_format(NULL, &q->subdev_fmt.code); + if (!fmt) + return -EINVAL; + + lanes = q->csi2.lanes; + + r = cio2_csi2_calc_timing(cio2, q, &timing, fmt->bpp, lanes); + if (r) + return r; + + writel(timing.clk_termen, q->csi_rx_base + + CIO2_REG_CSIRX_DLY_CNT_TERMEN(CIO2_CSIRX_DLY_CNT_CLANE_IDX)); + writel(timing.clk_settle, q->csi_rx_base + + CIO2_REG_CSIRX_DLY_CNT_SETTLE(CIO2_CSIRX_DLY_CNT_CLANE_IDX)); + + for (i = 0; i < lanes; i++) { + writel(timing.dat_termen, q->csi_rx_base + + CIO2_REG_CSIRX_DLY_CNT_TERMEN(i)); + writel(timing.dat_settle, q->csi_rx_base + + CIO2_REG_CSIRX_DLY_CNT_SETTLE(i)); + } + + writel(CIO2_PBM_WMCTRL1_MIN_2CK | + CIO2_PBM_WMCTRL1_MID1_2CK | + CIO2_PBM_WMCTRL1_MID2_2CK, base + CIO2_REG_PBM_WMCTRL1); + writel(CIO2_PBM_WMCTRL2_HWM_2CK << CIO2_PBM_WMCTRL2_HWM_2CK_SHIFT | + CIO2_PBM_WMCTRL2_LWM_2CK << CIO2_PBM_WMCTRL2_LWM_2CK_SHIFT | + CIO2_PBM_WMCTRL2_OBFFWM_2CK << + CIO2_PBM_WMCTRL2_OBFFWM_2CK_SHIFT | + CIO2_PBM_WMCTRL2_TRANSDYN << CIO2_PBM_WMCTRL2_TRANSDYN_SHIFT | + CIO2_PBM_WMCTRL2_OBFF_MEM_EN, base + CIO2_REG_PBM_WMCTRL2); + writel(CIO2_PBM_ARB_CTRL_LANES_DIV << + CIO2_PBM_ARB_CTRL_LANES_DIV_SHIFT | + CIO2_PBM_ARB_CTRL_LE_EN | + CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN << + CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN_SHIFT | + CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP << + CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP_SHIFT, + base + CIO2_REG_PBM_ARB_CTRL); + writel(CIO2_CSIRX_STATUS_DLANE_HS_MASK, + q->csi_rx_base + CIO2_REG_CSIRX_STATUS_DLANE_HS); + writel(CIO2_CSIRX_STATUS_DLANE_LP_MASK, + q->csi_rx_base + CIO2_REG_CSIRX_STATUS_DLANE_LP); + + writel(CIO2_FB_HPLL_FREQ, base + CIO2_REG_FB_HPLL_FREQ); + writel(CIO2_ISCLK_RATIO, base + CIO2_REG_ISCLK_RATIO); + + /* Configure MIPI backend */ + for (i = 0; i < NUM_VCS; i++) + writel(1, q->csi_rx_base + CIO2_REG_MIPIBE_SP_LUT_ENTRY(i)); + + /* There are 16 short packet LUT entry */ + for (i = 0; i < 16; i++) + writel(CIO2_MIPIBE_LP_LUT_ENTRY_DISREGARD, + q->csi_rx_base + CIO2_REG_MIPIBE_LP_LUT_ENTRY(i)); + writel(CIO2_MIPIBE_GLOBAL_LUT_DISREGARD, + q->csi_rx_base + CIO2_REG_MIPIBE_GLOBAL_LUT_DISREGARD); + + writel(CIO2_INT_EN_EXT_IE_MASK, base + CIO2_REG_INT_EN_EXT_IE); + writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK); + writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE); + writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_EDGE); + writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_LEVEL_NOT_PULSE); + writel(CIO2_INT_EN_EXT_OE_MASK, base + CIO2_REG_INT_EN_EXT_OE); + + writel(CIO2_REG_INT_EN_IRQ | CIO2_INT_IOC(CIO2_DMA_CHAN) | + CIO2_REG_INT_EN_IOS(CIO2_DMA_CHAN), + base + CIO2_REG_INT_EN); + + writel((CIO2_PXM_PXF_FMT_CFG_BPP_10 | CIO2_PXM_PXF_FMT_CFG_PCK_64B) + << CIO2_PXM_PXF_FMT_CFG_SID0_SHIFT, + base + CIO2_REG_PXM_PXF_FMT_CFG0(csi2bus)); + writel(SID << CIO2_MIPIBE_LP_LUT_ENTRY_SID_SHIFT | + sensor_vc << CIO2_MIPIBE_LP_LUT_ENTRY_VC_SHIFT | + fmt->mipicode << CIO2_MIPIBE_LP_LUT_ENTRY_FORMAT_TYPE_SHIFT, + q->csi_rx_base + CIO2_REG_MIPIBE_LP_LUT_ENTRY(ENTRY)); + writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_COMP_FORMAT(sensor_vc)); + writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_FORCE_RAW8); + writel(0, base + CIO2_REG_PXM_SID2BID0(csi2bus)); + + writel(lanes, q->csi_rx_base + CIO2_REG_CSIRX_NOF_ENABLED_LANES); + writel(CIO2_CGC_PRIM_TGE | + CIO2_CGC_SIDE_TGE | + CIO2_CGC_XOSC_TGE | + CIO2_CGC_D3I3_TGE | + CIO2_CGC_CSI2_INTERFRAME_TGE | + CIO2_CGC_CSI2_PORT_DCGE | + CIO2_CGC_SIDE_DCGE | + CIO2_CGC_PRIM_DCGE | + CIO2_CGC_ROSC_DCGE | + CIO2_CGC_XOSC_DCGE | + CIO2_CGC_CLKGATE_HOLDOFF << CIO2_CGC_CLKGATE_HOLDOFF_SHIFT | + CIO2_CGC_CSI_CLKGATE_HOLDOFF + << CIO2_CGC_CSI_CLKGATE_HOLDOFF_SHIFT, base + CIO2_REG_CGC); + writel(CIO2_LTRCTRL_LTRDYNEN, base + CIO2_REG_LTRCTRL); + writel(CIO2_LTRVAL0_VAL << CIO2_LTRVAL02_VAL_SHIFT | + CIO2_LTRVAL0_SCALE << CIO2_LTRVAL02_SCALE_SHIFT | + CIO2_LTRVAL1_VAL << CIO2_LTRVAL13_VAL_SHIFT | + CIO2_LTRVAL1_SCALE << CIO2_LTRVAL13_SCALE_SHIFT, + base + CIO2_REG_LTRVAL01); + writel(CIO2_LTRVAL2_VAL << CIO2_LTRVAL02_VAL_SHIFT | + CIO2_LTRVAL2_SCALE << CIO2_LTRVAL02_SCALE_SHIFT | + CIO2_LTRVAL3_VAL << CIO2_LTRVAL13_VAL_SHIFT | + CIO2_LTRVAL3_SCALE << CIO2_LTRVAL13_SCALE_SHIFT, + base + CIO2_REG_LTRVAL23); + + for (i = 0; i < CIO2_NUM_DMA_CHAN; i++) { + writel(0, base + CIO2_REG_CDMABA(i)); + writel(0, base + CIO2_REG_CDMAC0(i)); + writel(0, base + CIO2_REG_CDMAC1(i)); + } + + /* Enable DMA */ + writel(PFN_DOWN(q->fbpt_bus_addr), base + CIO2_REG_CDMABA(CIO2_DMA_CHAN)); + + writel(num_buffers1 << CIO2_CDMAC0_FBPT_LEN_SHIFT | + FBPT_WIDTH << CIO2_CDMAC0_FBPT_WIDTH_SHIFT | + CIO2_CDMAC0_DMA_INTR_ON_FE | + CIO2_CDMAC0_FBPT_UPDATE_FIFO_FULL | + CIO2_CDMAC0_DMA_EN | + CIO2_CDMAC0_DMA_INTR_ON_FS | + CIO2_CDMAC0_DMA_HALTED, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN)); + + writel(1 << CIO2_CDMAC1_LINENUMUPDATE_SHIFT, + base + CIO2_REG_CDMAC1(CIO2_DMA_CHAN)); + + writel(0, base + CIO2_REG_PBM_FOPN_ABORT); + + writel(CIO2_PXM_FRF_CFG_CRC_TH << CIO2_PXM_FRF_CFG_CRC_TH_SHIFT | + CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NR | + CIO2_PXM_FRF_CFG_MSK_ECC_RE | + CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NE, + base + CIO2_REG_PXM_FRF_CFG(q->csi2.port)); + + /* Clear interrupts */ + writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_CLEAR); + writel(~0, base + CIO2_REG_INT_STS_EXT_OE); + writel(~0, base + CIO2_REG_INT_STS_EXT_IE); + writel(~0, base + CIO2_REG_INT_STS); + + /* Enable devices, starting from the last device in the pipe */ + writel(1, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE); + writel(1, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE); + + return 0; +} + +static void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q) +{ + void __iomem *const base = cio2->base; + unsigned int i; + u32 value; + int ret; + + /* Disable CSI receiver and MIPI backend devices */ + writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK); + writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE); + writel(0, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE); + writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE); + + /* Halt DMA */ + writel(0, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN)); + ret = readl_poll_timeout(base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN), + value, value & CIO2_CDMAC0_DMA_HALTED, + 4000, 2000000); + if (ret) + dev_err(&cio2->pci_dev->dev, + "DMA %i can not be halted\n", CIO2_DMA_CHAN); + + for (i = 0; i < CIO2_NUM_PORTS; i++) { + writel(readl(base + CIO2_REG_PXM_FRF_CFG(i)) | + CIO2_PXM_FRF_CFG_ABORT, base + CIO2_REG_PXM_FRF_CFG(i)); + writel(readl(base + CIO2_REG_PBM_FOPN_ABORT) | + CIO2_PBM_FOPN_ABORT(i), base + CIO2_REG_PBM_FOPN_ABORT); + } +} + +static void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan) +{ + struct device *dev = &cio2->pci_dev->dev; + struct cio2_queue *q = cio2->cur_queue; + struct cio2_fbpt_entry *entry; + u64 ns = ktime_get_ns(); + + if (dma_chan >= CIO2_QUEUES) { + dev_err(dev, "bad DMA channel %i\n", dma_chan); + return; + } + + entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS]; + if (entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID) { + dev_warn(&cio2->pci_dev->dev, + "no ready buffers found on DMA channel %u\n", + dma_chan); + return; + } + + /* Find out which buffer(s) are ready */ + do { + struct cio2_buffer *b; + + b = q->bufs[q->bufs_first]; + if (b) { + unsigned int received = entry[1].second_entry.num_of_bytes; + unsigned long payload = + vb2_get_plane_payload(&b->vbb.vb2_buf, 0); + + q->bufs[q->bufs_first] = NULL; + atomic_dec(&q->bufs_queued); + dev_dbg(&cio2->pci_dev->dev, + "buffer %i done\n", b->vbb.vb2_buf.index); + + b->vbb.vb2_buf.timestamp = ns; + b->vbb.field = V4L2_FIELD_NONE; + b->vbb.sequence = atomic_read(&q->frame_sequence); + if (payload != received) + dev_warn(dev, + "payload length is %lu, received %u\n", + payload, received); + vb2_buffer_done(&b->vbb.vb2_buf, VB2_BUF_STATE_DONE); + } + atomic_inc(&q->frame_sequence); + cio2_fbpt_entry_init_dummy(cio2, entry); + q->bufs_first = (q->bufs_first + 1) % CIO2_MAX_BUFFERS; + entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS]; + } while (!(entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID)); +} + +static void cio2_queue_event_sof(struct cio2_device *cio2, struct cio2_queue *q) +{ + /* + * For the user space camera control algorithms it is essential + * to know when the reception of a frame has begun. That's often + * the best timing information to get from the hardware. + */ + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + .u.frame_sync.frame_sequence = atomic_read(&q->frame_sequence), + }; + + v4l2_event_queue(q->subdev.devnode, &event); +} + +static const char *const cio2_irq_errs[] = { + "single packet header error corrected", + "multiple packet header errors detected", + "payload checksum (CRC) error", + "fifo overflow", + "reserved short packet data type detected", + "reserved long packet data type detected", + "incomplete long packet detected", + "frame sync error", + "line sync error", + "DPHY start of transmission error", + "DPHY synchronization error", + "escape mode error", + "escape mode trigger event", + "escape mode ultra-low power state for data lane(s)", + "escape mode ultra-low power state exit for clock lane", + "inter-frame short packet discarded", + "inter-frame long packet discarded", + "non-matching Long Packet stalled", +}; + +static const char *const cio2_port_errs[] = { + "ECC recoverable", + "DPHY not recoverable", + "ECC not recoverable", + "CRC error", + "INTERFRAMEDATA", + "PKT2SHORT", + "PKT2LONG", +}; + +static void cio2_irq_handle_once(struct cio2_device *cio2, u32 int_status) +{ + void __iomem *const base = cio2->base; + struct device *dev = &cio2->pci_dev->dev; + + if (int_status & CIO2_INT_IOOE) { + /* + * Interrupt on Output Error: + * 1) SRAM is full and FS received, or + * 2) An invalid bit detected by DMA. + */ + u32 oe_status, oe_clear; + + oe_clear = readl(base + CIO2_REG_INT_STS_EXT_OE); + oe_status = oe_clear; + + if (oe_status & CIO2_INT_EXT_OE_DMAOE_MASK) { + dev_err(dev, "DMA output error: 0x%x\n", + (oe_status & CIO2_INT_EXT_OE_DMAOE_MASK) + >> CIO2_INT_EXT_OE_DMAOE_SHIFT); + oe_status &= ~CIO2_INT_EXT_OE_DMAOE_MASK; + } + if (oe_status & CIO2_INT_EXT_OE_OES_MASK) { + dev_err(dev, "DMA output error on CSI2 buses: 0x%x\n", + (oe_status & CIO2_INT_EXT_OE_OES_MASK) + >> CIO2_INT_EXT_OE_OES_SHIFT); + oe_status &= ~CIO2_INT_EXT_OE_OES_MASK; + } + writel(oe_clear, base + CIO2_REG_INT_STS_EXT_OE); + if (oe_status) + dev_warn(dev, "unknown interrupt 0x%x on OE\n", + oe_status); + int_status &= ~CIO2_INT_IOOE; + } + + if (int_status & CIO2_INT_IOC_MASK) { + /* DMA IO done -- frame ready */ + u32 clr = 0; + unsigned int d; + + for (d = 0; d < CIO2_NUM_DMA_CHAN; d++) + if (int_status & CIO2_INT_IOC(d)) { + clr |= CIO2_INT_IOC(d); + cio2_buffer_done(cio2, d); + } + int_status &= ~clr; + } + + if (int_status & CIO2_INT_IOS_IOLN_MASK) { + /* DMA IO starts or reached specified line */ + u32 clr = 0; + unsigned int d; + + for (d = 0; d < CIO2_NUM_DMA_CHAN; d++) + if (int_status & CIO2_INT_IOS_IOLN(d)) { + clr |= CIO2_INT_IOS_IOLN(d); + if (d == CIO2_DMA_CHAN) + cio2_queue_event_sof(cio2, + cio2->cur_queue); + } + int_status &= ~clr; + } + + if (int_status & (CIO2_INT_IOIE | CIO2_INT_IOIRQ)) { + /* CSI2 receiver (error) interrupt */ + u32 ie_status, ie_clear; + unsigned int port; + + ie_clear = readl(base + CIO2_REG_INT_STS_EXT_IE); + ie_status = ie_clear; + + for (port = 0; port < CIO2_NUM_PORTS; port++) { + u32 port_status = (ie_status >> (port * 8)) & 0xff; + u32 err_mask = BIT_MASK(ARRAY_SIZE(cio2_port_errs)) - 1; + void __iomem *const csi_rx_base = + base + CIO2_REG_PIPE_BASE(port); + unsigned int i; + + while (port_status & err_mask) { + i = ffs(port_status) - 1; + dev_err(dev, "port %i error %s\n", + port, cio2_port_errs[i]); + ie_status &= ~BIT(port * 8 + i); + port_status &= ~BIT(i); + } + + if (ie_status & CIO2_INT_EXT_IE_IRQ(port)) { + u32 csi2_status, csi2_clear; + + csi2_status = readl(csi_rx_base + + CIO2_REG_IRQCTRL_STATUS); + csi2_clear = csi2_status; + err_mask = + BIT_MASK(ARRAY_SIZE(cio2_irq_errs)) - 1; + + while (csi2_status & err_mask) { + i = ffs(csi2_status) - 1; + dev_err(dev, + "CSI-2 receiver port %i: %s\n", + port, cio2_irq_errs[i]); + csi2_status &= ~BIT(i); + } + + writel(csi2_clear, + csi_rx_base + CIO2_REG_IRQCTRL_CLEAR); + if (csi2_status) + dev_warn(dev, + "unknown CSI2 error 0x%x on port %i\n", + csi2_status, port); + + ie_status &= ~CIO2_INT_EXT_IE_IRQ(port); + } + } + + writel(ie_clear, base + CIO2_REG_INT_STS_EXT_IE); + if (ie_status) + dev_warn(dev, "unknown interrupt 0x%x on IE\n", + ie_status); + + int_status &= ~(CIO2_INT_IOIE | CIO2_INT_IOIRQ); + } + + if (int_status) + dev_warn(dev, "unknown interrupt 0x%x on INT\n", int_status); +} + +static irqreturn_t cio2_irq(int irq, void *cio2_ptr) +{ + struct cio2_device *cio2 = cio2_ptr; + void __iomem *const base = cio2->base; + struct device *dev = &cio2->pci_dev->dev; + u32 int_status; + + int_status = readl(base + CIO2_REG_INT_STS); + dev_dbg(dev, "isr enter - interrupt status 0x%x\n", int_status); + if (!int_status) + return IRQ_NONE; + + do { + writel(int_status, base + CIO2_REG_INT_STS); + cio2_irq_handle_once(cio2, int_status); + int_status = readl(base + CIO2_REG_INT_STS); + if (int_status) + dev_dbg(dev, "pending status 0x%x\n", int_status); + } while (int_status); + + return IRQ_HANDLED; +} + +/**************** Videobuf2 interface ****************/ + +static void cio2_vb2_return_all_buffers(struct cio2_queue *q, + enum vb2_buffer_state state) +{ + unsigned int i; + + for (i = 0; i < CIO2_MAX_BUFFERS; i++) { + if (q->bufs[i]) { + atomic_dec(&q->bufs_queued); + vb2_buffer_done(&q->bufs[i]->vbb.vb2_buf, + state); + q->bufs[i] = NULL; + } + } +} + +static int cio2_vb2_queue_setup(struct vb2_queue *vq, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct cio2_device *cio2 = vb2_get_drv_priv(vq); + struct cio2_queue *q = vb2q_to_cio2_queue(vq); + unsigned int i; + + *num_planes = q->format.num_planes; + + for (i = 0; i < *num_planes; ++i) { + sizes[i] = q->format.plane_fmt[i].sizeimage; + alloc_devs[i] = &cio2->pci_dev->dev; + } + + *num_buffers = clamp_val(*num_buffers, 1, CIO2_MAX_BUFFERS); + + /* Initialize buffer queue */ + for (i = 0; i < CIO2_MAX_BUFFERS; i++) { + q->bufs[i] = NULL; + cio2_fbpt_entry_init_dummy(cio2, &q->fbpt[i * CIO2_MAX_LOPS]); + } + atomic_set(&q->bufs_queued, 0); + q->bufs_first = 0; + q->bufs_next = 0; + + return 0; +} + +/* Called after each buffer is allocated */ +static int cio2_vb2_buf_init(struct vb2_buffer *vb) +{ + struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue); + struct device *dev = &cio2->pci_dev->dev; + struct cio2_buffer *b = + container_of(vb, struct cio2_buffer, vbb.vb2_buf); + unsigned int pages = PFN_UP(vb->planes[0].length); + unsigned int lops = DIV_ROUND_UP(pages + 1, CIO2_LOP_ENTRIES); + struct sg_table *sg; + struct sg_dma_page_iter sg_iter; + unsigned int i, j; + + if (lops <= 0 || lops > CIO2_MAX_LOPS) { + dev_err(dev, "%s: bad buffer size (%i)\n", __func__, + vb->planes[0].length); + return -ENOSPC; /* Should never happen */ + } + + memset(b->lop, 0, sizeof(b->lop)); + /* Allocate LOP table */ + for (i = 0; i < lops; i++) { + b->lop[i] = dma_alloc_coherent(dev, PAGE_SIZE, + &b->lop_bus_addr[i], GFP_KERNEL); + if (!b->lop[i]) + goto fail; + } + + /* Fill LOP */ + sg = vb2_dma_sg_plane_desc(vb, 0); + if (!sg) + return -ENOMEM; + + if (sg->nents && sg->sgl) + b->offset = sg->sgl->offset; + + i = j = 0; + for_each_sg_dma_page(sg->sgl, &sg_iter, sg->nents, 0) { + if (!pages--) + break; + b->lop[i][j] = PFN_DOWN(sg_page_iter_dma_address(&sg_iter)); + j++; + if (j == CIO2_LOP_ENTRIES) { + i++; + j = 0; + } + } + + b->lop[i][j] = PFN_DOWN(cio2->dummy_page_bus_addr); + return 0; +fail: + while (i--) + dma_free_coherent(dev, PAGE_SIZE, b->lop[i], b->lop_bus_addr[i]); + return -ENOMEM; +} + +/* Transfer buffer ownership to cio2 */ +static void cio2_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue); + struct cio2_queue *q = + container_of(vb->vb2_queue, struct cio2_queue, vbq); + struct cio2_buffer *b = + container_of(vb, struct cio2_buffer, vbb.vb2_buf); + struct cio2_fbpt_entry *entry; + unsigned long flags; + unsigned int i, j, next = q->bufs_next; + int bufs_queued = atomic_inc_return(&q->bufs_queued); + u32 fbpt_rp; + + dev_dbg(&cio2->pci_dev->dev, "queue buffer %d\n", vb->index); + + /* + * This code queues the buffer to the CIO2 DMA engine, which starts + * running once streaming has started. It is possible that this code + * gets pre-empted due to increased CPU load. Upon this, the driver + * does not get an opportunity to queue new buffers to the CIO2 DMA + * engine. When the DMA engine encounters an FBPT entry without the + * VALID bit set, the DMA engine halts, which requires a restart of + * the DMA engine and sensor, to continue streaming. + * This is not desired and is highly unlikely given that there are + * 32 FBPT entries that the DMA engine needs to process, to run into + * an FBPT entry, without the VALID bit set. We try to mitigate this + * by disabling interrupts for the duration of this queueing. + */ + local_irq_save(flags); + + fbpt_rp = (readl(cio2->base + CIO2_REG_CDMARI(CIO2_DMA_CHAN)) + >> CIO2_CDMARI_FBPT_RP_SHIFT) + & CIO2_CDMARI_FBPT_RP_MASK; + + /* + * fbpt_rp is the fbpt entry that the dma is currently working + * on, but since it could jump to next entry at any time, + * assume that we might already be there. + */ + fbpt_rp = (fbpt_rp + 1) % CIO2_MAX_BUFFERS; + + if (bufs_queued <= 1 || fbpt_rp == next) + /* Buffers were drained */ + next = (fbpt_rp + 1) % CIO2_MAX_BUFFERS; + + for (i = 0; i < CIO2_MAX_BUFFERS; i++) { + /* + * We have allocated CIO2_MAX_BUFFERS circularly for the + * hw, the user has requested N buffer queue. The driver + * ensures N <= CIO2_MAX_BUFFERS and guarantees that whenever + * user queues a buffer, there necessarily is a free buffer. + */ + if (!q->bufs[next]) { + q->bufs[next] = b; + entry = &q->fbpt[next * CIO2_MAX_LOPS]; + cio2_fbpt_entry_init_buf(cio2, b, entry); + local_irq_restore(flags); + q->bufs_next = (next + 1) % CIO2_MAX_BUFFERS; + for (j = 0; j < vb->num_planes; j++) + vb2_set_plane_payload(vb, j, + q->format.plane_fmt[j].sizeimage); + return; + } + + dev_dbg(&cio2->pci_dev->dev, "entry %i was full!\n", next); + next = (next + 1) % CIO2_MAX_BUFFERS; + } + + local_irq_restore(flags); + dev_err(&cio2->pci_dev->dev, "error: all cio2 entries were full!\n"); + atomic_dec(&q->bufs_queued); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); +} + +/* Called when each buffer is freed */ +static void cio2_vb2_buf_cleanup(struct vb2_buffer *vb) +{ + struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue); + struct cio2_buffer *b = + container_of(vb, struct cio2_buffer, vbb.vb2_buf); + unsigned int i; + + /* Free LOP table */ + for (i = 0; i < CIO2_MAX_LOPS; i++) { + if (b->lop[i]) + dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, + b->lop[i], b->lop_bus_addr[i]); + } +} + +static int cio2_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct cio2_queue *q = vb2q_to_cio2_queue(vq); + struct cio2_device *cio2 = vb2_get_drv_priv(vq); + int r; + + cio2->cur_queue = q; + atomic_set(&q->frame_sequence, 0); + + r = pm_runtime_get_sync(&cio2->pci_dev->dev); + if (r < 0) { + dev_info(&cio2->pci_dev->dev, "failed to set power %d\n", r); + pm_runtime_put_noidle(&cio2->pci_dev->dev); + return r; + } + + r = media_pipeline_start(&q->vdev.entity, &q->pipe); + if (r) + goto fail_pipeline; + + r = cio2_hw_init(cio2, q); + if (r) + goto fail_hw; + + /* Start streaming on sensor */ + r = v4l2_subdev_call(q->sensor, video, s_stream, 1); + if (r) + goto fail_csi2_subdev; + + cio2->streaming = true; + + return 0; + +fail_csi2_subdev: + cio2_hw_exit(cio2, q); +fail_hw: + media_pipeline_stop(&q->vdev.entity); +fail_pipeline: + dev_dbg(&cio2->pci_dev->dev, "failed to start streaming (%d)\n", r); + cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_QUEUED); + pm_runtime_put(&cio2->pci_dev->dev); + + return r; +} + +static void cio2_vb2_stop_streaming(struct vb2_queue *vq) +{ + struct cio2_queue *q = vb2q_to_cio2_queue(vq); + struct cio2_device *cio2 = vb2_get_drv_priv(vq); + + if (v4l2_subdev_call(q->sensor, video, s_stream, 0)) + dev_err(&cio2->pci_dev->dev, + "failed to stop sensor streaming\n"); + + cio2_hw_exit(cio2, q); + synchronize_irq(cio2->pci_dev->irq); + cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_ERROR); + media_pipeline_stop(&q->vdev.entity); + pm_runtime_put(&cio2->pci_dev->dev); + cio2->streaming = false; +} + +static const struct vb2_ops cio2_vb2_ops = { + .buf_init = cio2_vb2_buf_init, + .buf_queue = cio2_vb2_buf_queue, + .buf_cleanup = cio2_vb2_buf_cleanup, + .queue_setup = cio2_vb2_queue_setup, + .start_streaming = cio2_vb2_start_streaming, + .stop_streaming = cio2_vb2_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/**************** V4L2 interface ****************/ + +static int cio2_v4l2_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct cio2_device *cio2 = video_drvdata(file); + + strscpy(cap->driver, CIO2_NAME, sizeof(cap->driver)); + strscpy(cap->card, CIO2_DEVICE_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "PCI:%s", pci_name(cio2->pci_dev)); + + return 0; +} + +static int cio2_v4l2_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(formats)) + return -EINVAL; + + f->pixelformat = formats[f->index].fourcc; + + return 0; +} + +/* The format is validated in cio2_video_link_validate() */ +static int cio2_v4l2_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct cio2_queue *q = file_to_cio2_queue(file); + + f->fmt.pix_mp = q->format; + + return 0; +} + +static int cio2_v4l2_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + const struct ipu3_cio2_fmt *fmt; + struct v4l2_pix_format_mplane *mpix = &f->fmt.pix_mp; + + fmt = cio2_find_format(&mpix->pixelformat, NULL); + if (!fmt) + fmt = &formats[0]; + + /* Only supports up to 4224x3136 */ + if (mpix->width > CIO2_IMAGE_MAX_WIDTH) + mpix->width = CIO2_IMAGE_MAX_WIDTH; + if (mpix->height > CIO2_IMAGE_MAX_HEIGHT) + mpix->height = CIO2_IMAGE_MAX_HEIGHT; + + mpix->num_planes = 1; + mpix->pixelformat = fmt->fourcc; + mpix->colorspace = V4L2_COLORSPACE_RAW; + mpix->field = V4L2_FIELD_NONE; + memset(mpix->reserved, 0, sizeof(mpix->reserved)); + mpix->plane_fmt[0].bytesperline = cio2_bytesperline(mpix->width); + mpix->plane_fmt[0].sizeimage = mpix->plane_fmt[0].bytesperline * + mpix->height; + memset(mpix->plane_fmt[0].reserved, 0, + sizeof(mpix->plane_fmt[0].reserved)); + + /* use default */ + mpix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mpix->quantization = V4L2_QUANTIZATION_DEFAULT; + mpix->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + return 0; +} + +static int cio2_v4l2_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct cio2_queue *q = file_to_cio2_queue(file); + + cio2_v4l2_try_fmt(file, fh, f); + q->format = f->fmt.pix_mp; + + return 0; +} + +static int +cio2_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) +{ + if (input->index > 0) + return -EINVAL; + + strscpy(input->name, "camera", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int +cio2_video_g_input(struct file *file, void *fh, unsigned int *input) +{ + *input = 0; + + return 0; +} + +static int +cio2_video_s_input(struct file *file, void *fh, unsigned int input) +{ + return input == 0 ? 0 : -EINVAL; +} + +static const struct v4l2_file_operations cio2_v4l2_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +static const struct v4l2_ioctl_ops cio2_v4l2_ioctl_ops = { + .vidioc_querycap = cio2_v4l2_querycap, + .vidioc_enum_fmt_vid_cap = cio2_v4l2_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = cio2_v4l2_g_fmt, + .vidioc_s_fmt_vid_cap_mplane = cio2_v4l2_s_fmt, + .vidioc_try_fmt_vid_cap_mplane = cio2_v4l2_try_fmt, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_enum_input = cio2_video_enum_input, + .vidioc_g_input = cio2_video_g_input, + .vidioc_s_input = cio2_video_s_input, +}; + +static int cio2_subdev_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + if (sub->type != V4L2_EVENT_FRAME_SYNC) + return -EINVAL; + + /* Line number. For now only zero accepted. */ + if (sub->id != 0) + return -EINVAL; + + return v4l2_event_subscribe(fh, sub, 0, NULL); +} + +static int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + const struct v4l2_mbus_framefmt fmt_default = { + .width = 1936, + .height = 1096, + .code = formats[0].mbus_code, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, + }; + + /* Initialize try_fmt */ + format = v4l2_subdev_get_try_format(sd, fh->pad, CIO2_PAD_SINK); + *format = fmt_default; + + /* same as sink */ + format = v4l2_subdev_get_try_format(sd, fh->pad, CIO2_PAD_SOURCE); + *format = fmt_default; + + return 0; +} + +/* + * cio2_subdev_get_fmt - Handle get format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @cfg: V4L2 subdev pad config + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int cio2_subdev_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev); + + mutex_lock(&q->subdev_lock); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + else + fmt->format = q->subdev_fmt; + + mutex_unlock(&q->subdev_lock); + + return 0; +} + +/* + * cio2_subdev_set_fmt - Handle set format by pads subdev method + * @sd : pointer to v4l2 subdev structure + * @cfg: V4L2 subdev pad config + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int cio2_subdev_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev); + struct v4l2_mbus_framefmt *mbus; + u32 mbus_code = fmt->format.code; + unsigned int i; + + /* + * Only allow setting sink pad format; + * source always propagates from sink + */ + if (fmt->pad == CIO2_PAD_SOURCE) + return cio2_subdev_get_fmt(sd, cfg, fmt); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + mbus = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + else + mbus = &q->subdev_fmt; + + fmt->format.code = formats[0].mbus_code; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].mbus_code == mbus_code) { + fmt->format.code = mbus_code; + break; + } + } + + fmt->format.width = min(fmt->format.width, CIO2_IMAGE_MAX_WIDTH); + fmt->format.height = min(fmt->format.height, CIO2_IMAGE_MAX_HEIGHT); + fmt->format.field = V4L2_FIELD_NONE; + + mutex_lock(&q->subdev_lock); + *mbus = fmt->format; + mutex_unlock(&q->subdev_lock); + + return 0; +} + +static int cio2_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(formats)) + return -EINVAL; + + code->code = formats[code->index].mbus_code; + return 0; +} + +static int cio2_subdev_link_validate_get_format(struct media_pad *pad, + struct v4l2_subdev_format *fmt) +{ + if (is_media_entity_v4l2_subdev(pad->entity)) { + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(pad->entity); + + fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt->pad = pad->index; + return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); + } + + return -EINVAL; +} + +static int cio2_video_link_validate(struct media_link *link) +{ + struct video_device *vd = container_of(link->sink->entity, + struct video_device, entity); + struct cio2_queue *q = container_of(vd, struct cio2_queue, vdev); + struct cio2_device *cio2 = video_get_drvdata(vd); + struct v4l2_subdev_format source_fmt; + int ret; + + if (!media_entity_remote_pad(link->sink->entity->pads)) { + dev_info(&cio2->pci_dev->dev, + "video node %s pad not connected\n", vd->name); + return -ENOTCONN; + } + + ret = cio2_subdev_link_validate_get_format(link->source, &source_fmt); + if (ret < 0) + return 0; + + if (source_fmt.format.width != q->format.width || + source_fmt.format.height != q->format.height) { + dev_err(&cio2->pci_dev->dev, + "Wrong width or height %ux%u (%ux%u expected)\n", + q->format.width, q->format.height, + source_fmt.format.width, source_fmt.format.height); + return -EINVAL; + } + + if (!cio2_find_format(&q->format.pixelformat, &source_fmt.format.code)) + return -EINVAL; + + return 0; +} + +static const struct v4l2_subdev_core_ops cio2_subdev_core_ops = { + .subscribe_event = cio2_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_internal_ops cio2_subdev_internal_ops = { + .open = cio2_subdev_open, +}; + +static const struct v4l2_subdev_pad_ops cio2_subdev_pad_ops = { + .link_validate = v4l2_subdev_link_validate_default, + .get_fmt = cio2_subdev_get_fmt, + .set_fmt = cio2_subdev_set_fmt, + .enum_mbus_code = cio2_subdev_enum_mbus_code, +}; + +static const struct v4l2_subdev_ops cio2_subdev_ops = { + .core = &cio2_subdev_core_ops, + .pad = &cio2_subdev_pad_ops, +}; + +/******* V4L2 sub-device asynchronous registration callbacks***********/ + +struct sensor_async_subdev { + struct v4l2_async_subdev asd; + struct csi2_bus_info csi2; +}; + +/* The .bound() notifier callback when a match is found */ +static int cio2_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct cio2_device *cio2 = container_of(notifier, + struct cio2_device, notifier); + struct sensor_async_subdev *s_asd = container_of(asd, + struct sensor_async_subdev, asd); + struct cio2_queue *q; + + if (cio2->queue[s_asd->csi2.port].sensor) + return -EBUSY; + + q = &cio2->queue[s_asd->csi2.port]; + + q->csi2 = s_asd->csi2; + q->sensor = sd; + q->csi_rx_base = cio2->base + CIO2_REG_PIPE_BASE(q->csi2.port); + + return 0; +} + +/* The .unbind callback */ +static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct cio2_device *cio2 = container_of(notifier, + struct cio2_device, notifier); + struct sensor_async_subdev *s_asd = container_of(asd, + struct sensor_async_subdev, asd); + + cio2->queue[s_asd->csi2.port].sensor = NULL; +} + +/* .complete() is called after all subdevices have been located */ +static int cio2_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct cio2_device *cio2 = container_of(notifier, struct cio2_device, + notifier); + struct sensor_async_subdev *s_asd; + struct v4l2_async_subdev *asd; + struct cio2_queue *q; + unsigned int pad; + int ret; + + list_for_each_entry(asd, &cio2->notifier.asd_list, asd_list) { + s_asd = container_of(asd, struct sensor_async_subdev, asd); + q = &cio2->queue[s_asd->csi2.port]; + + for (pad = 0; pad < q->sensor->entity.num_pads; pad++) + if (q->sensor->entity.pads[pad].flags & + MEDIA_PAD_FL_SOURCE) + break; + + if (pad == q->sensor->entity.num_pads) { + dev_err(&cio2->pci_dev->dev, + "failed to find src pad for %s\n", + q->sensor->name); + return -ENXIO; + } + + ret = media_create_pad_link( + &q->sensor->entity, pad, + &q->subdev.entity, CIO2_PAD_SINK, + 0); + if (ret) { + dev_err(&cio2->pci_dev->dev, + "failed to create link for %s\n", + q->sensor->name); + return ret; + } + } + + return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev); +} + +static const struct v4l2_async_notifier_operations cio2_async_ops = { + .bound = cio2_notifier_bound, + .unbind = cio2_notifier_unbind, + .complete = cio2_notifier_complete, +}; + +static int cio2_parse_firmware(struct cio2_device *cio2) +{ + unsigned int i; + int ret; + + for (i = 0; i < CIO2_NUM_PORTS; i++) { + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct sensor_async_subdev *s_asd = NULL; + struct fwnode_handle *ep; + + ep = fwnode_graph_get_endpoint_by_id( + dev_fwnode(&cio2->pci_dev->dev), i, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + + if (!ep) + continue; + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) + goto err_parse; + + s_asd = kzalloc(sizeof(*s_asd), GFP_KERNEL); + if (!s_asd) { + ret = -ENOMEM; + goto err_parse; + } + + s_asd->csi2.port = vep.base.port; + s_asd->csi2.lanes = vep.bus.mipi_csi2.num_data_lanes; + + ret = v4l2_async_notifier_add_fwnode_remote_subdev( + &cio2->notifier, ep, &s_asd->asd); + if (ret) + goto err_parse; + + fwnode_handle_put(ep); + + continue; + +err_parse: + fwnode_handle_put(ep); + kfree(s_asd); + return ret; + } + + /* + * Proceed even without sensors connected to allow the device to + * suspend. + */ + cio2->notifier.ops = &cio2_async_ops; + ret = v4l2_async_notifier_register(&cio2->v4l2_dev, &cio2->notifier); + if (ret) + dev_err(&cio2->pci_dev->dev, + "failed to register async notifier : %d\n", ret); + + return ret; +} + +/**************** Queue initialization ****************/ +static const struct media_entity_operations cio2_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct media_entity_operations cio2_video_entity_ops = { + .link_validate = cio2_video_link_validate, +}; + +static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q) +{ + static const u32 default_width = 1936; + static const u32 default_height = 1096; + const struct ipu3_cio2_fmt dflt_fmt = formats[0]; + + struct video_device *vdev = &q->vdev; + struct vb2_queue *vbq = &q->vbq; + struct v4l2_subdev *subdev = &q->subdev; + struct v4l2_mbus_framefmt *fmt; + int r; + + /* Initialize miscellaneous variables */ + mutex_init(&q->lock); + mutex_init(&q->subdev_lock); + + /* Initialize formats to default values */ + fmt = &q->subdev_fmt; + fmt->width = default_width; + fmt->height = default_height; + fmt->code = dflt_fmt.mbus_code; + fmt->field = V4L2_FIELD_NONE; + + q->format.width = default_width; + q->format.height = default_height; + q->format.pixelformat = dflt_fmt.fourcc; + q->format.colorspace = V4L2_COLORSPACE_RAW; + q->format.field = V4L2_FIELD_NONE; + q->format.num_planes = 1; + q->format.plane_fmt[0].bytesperline = + cio2_bytesperline(q->format.width); + q->format.plane_fmt[0].sizeimage = q->format.plane_fmt[0].bytesperline * + q->format.height; + + /* Initialize fbpt */ + r = cio2_fbpt_init(cio2, q); + if (r) + goto fail_fbpt; + + /* Initialize media entities */ + q->subdev_pads[CIO2_PAD_SINK].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + q->subdev_pads[CIO2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &cio2_media_ops; + subdev->internal_ops = &cio2_subdev_internal_ops; + r = media_entity_pads_init(&subdev->entity, CIO2_PADS, q->subdev_pads); + if (r) { + dev_err(&cio2->pci_dev->dev, + "failed initialize subdev media entity (%d)\n", r); + goto fail_subdev_media_entity; + } + + q->vdev_pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + vdev->entity.ops = &cio2_video_entity_ops; + r = media_entity_pads_init(&vdev->entity, 1, &q->vdev_pad); + if (r) { + dev_err(&cio2->pci_dev->dev, + "failed initialize videodev media entity (%d)\n", r); + goto fail_vdev_media_entity; + } + + /* Initialize subdev */ + v4l2_subdev_init(subdev, &cio2_subdev_ops); + subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + subdev->owner = THIS_MODULE; + snprintf(subdev->name, sizeof(subdev->name), + CIO2_ENTITY_NAME " %td", q - cio2->queue); + subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + v4l2_set_subdevdata(subdev, cio2); + r = v4l2_device_register_subdev(&cio2->v4l2_dev, subdev); + if (r) { + dev_err(&cio2->pci_dev->dev, + "failed initialize subdev (%d)\n", r); + goto fail_subdev; + } + + /* Initialize vbq */ + vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + vbq->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF; + vbq->ops = &cio2_vb2_ops; + vbq->mem_ops = &vb2_dma_sg_memops; + vbq->buf_struct_size = sizeof(struct cio2_buffer); + vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vbq->min_buffers_needed = 1; + vbq->drv_priv = cio2; + vbq->lock = &q->lock; + r = vb2_queue_init(vbq); + if (r) { + dev_err(&cio2->pci_dev->dev, + "failed to initialize videobuf2 queue (%d)\n", r); + goto fail_subdev; + } + + /* Initialize vdev */ + snprintf(vdev->name, sizeof(vdev->name), + "%s %td", CIO2_NAME, q - cio2->queue); + vdev->release = video_device_release_empty; + vdev->fops = &cio2_v4l2_fops; + vdev->ioctl_ops = &cio2_v4l2_ioctl_ops; + vdev->lock = &cio2->lock; + vdev->v4l2_dev = &cio2->v4l2_dev; + vdev->queue = &q->vbq; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; + video_set_drvdata(vdev, cio2); + r = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (r) { + dev_err(&cio2->pci_dev->dev, + "failed to register video device (%d)\n", r); + goto fail_vdev; + } + + /* Create link from CIO2 subdev to output node */ + r = media_create_pad_link( + &subdev->entity, CIO2_PAD_SOURCE, &vdev->entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (r) + goto fail_link; + + return 0; + +fail_link: + vb2_video_unregister_device(&q->vdev); +fail_vdev: + v4l2_device_unregister_subdev(subdev); +fail_subdev: + media_entity_cleanup(&vdev->entity); +fail_vdev_media_entity: + media_entity_cleanup(&subdev->entity); +fail_subdev_media_entity: + cio2_fbpt_exit(q, &cio2->pci_dev->dev); +fail_fbpt: + mutex_destroy(&q->subdev_lock); + mutex_destroy(&q->lock); + + return r; +} + +static void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q) +{ + vb2_video_unregister_device(&q->vdev); + media_entity_cleanup(&q->vdev.entity); + v4l2_device_unregister_subdev(&q->subdev); + media_entity_cleanup(&q->subdev.entity); + cio2_fbpt_exit(q, &cio2->pci_dev->dev); + mutex_destroy(&q->subdev_lock); + mutex_destroy(&q->lock); +} + +static int cio2_queues_init(struct cio2_device *cio2) +{ + int i, r; + + for (i = 0; i < CIO2_QUEUES; i++) { + r = cio2_queue_init(cio2, &cio2->queue[i]); + if (r) + break; + } + + if (i == CIO2_QUEUES) + return 0; + + for (i--; i >= 0; i--) + cio2_queue_exit(cio2, &cio2->queue[i]); + + return r; +} + +static void cio2_queues_exit(struct cio2_device *cio2) +{ + unsigned int i; + + for (i = 0; i < CIO2_QUEUES; i++) + cio2_queue_exit(cio2, &cio2->queue[i]); +} + +/**************** PCI interface ****************/ + +static int cio2_pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + struct cio2_device *cio2; + int r; + + cio2 = devm_kzalloc(&pci_dev->dev, sizeof(*cio2), GFP_KERNEL); + if (!cio2) + return -ENOMEM; + cio2->pci_dev = pci_dev; + + r = pcim_enable_device(pci_dev); + if (r) { + dev_err(&pci_dev->dev, "failed to enable device (%d)\n", r); + return r; + } + + dev_info(&pci_dev->dev, "device 0x%x (rev: 0x%x)\n", + pci_dev->device, pci_dev->revision); + + r = pcim_iomap_regions(pci_dev, 1 << CIO2_PCI_BAR, pci_name(pci_dev)); + if (r) { + dev_err(&pci_dev->dev, "failed to remap I/O memory (%d)\n", r); + return -ENODEV; + } + + cio2->base = pcim_iomap_table(pci_dev)[CIO2_PCI_BAR]; + + pci_set_drvdata(pci_dev, cio2); + + pci_set_master(pci_dev); + + r = pci_set_dma_mask(pci_dev, CIO2_DMA_MASK); + if (r) { + dev_err(&pci_dev->dev, "failed to set DMA mask (%d)\n", r); + return -ENODEV; + } + + r = pci_enable_msi(pci_dev); + if (r) { + dev_err(&pci_dev->dev, "failed to enable MSI (%d)\n", r); + return r; + } + + r = cio2_fbpt_init_dummy(cio2); + if (r) + return r; + + mutex_init(&cio2->lock); + + cio2->media_dev.dev = &cio2->pci_dev->dev; + strscpy(cio2->media_dev.model, CIO2_DEVICE_NAME, + sizeof(cio2->media_dev.model)); + snprintf(cio2->media_dev.bus_info, sizeof(cio2->media_dev.bus_info), + "PCI:%s", pci_name(cio2->pci_dev)); + cio2->media_dev.hw_revision = 0; + + media_device_init(&cio2->media_dev); + r = media_device_register(&cio2->media_dev); + if (r < 0) + goto fail_mutex_destroy; + + cio2->v4l2_dev.mdev = &cio2->media_dev; + r = v4l2_device_register(&pci_dev->dev, &cio2->v4l2_dev); + if (r) { + dev_err(&pci_dev->dev, + "failed to register V4L2 device (%d)\n", r); + goto fail_media_device_unregister; + } + + r = cio2_queues_init(cio2); + if (r) + goto fail_v4l2_device_unregister; + + v4l2_async_notifier_init(&cio2->notifier); + + /* Register notifier for subdevices we care */ + r = cio2_parse_firmware(cio2); + if (r) + goto fail_clean_notifier; + + r = devm_request_irq(&pci_dev->dev, pci_dev->irq, cio2_irq, + IRQF_SHARED, CIO2_NAME, cio2); + if (r) { + dev_err(&pci_dev->dev, "failed to request IRQ (%d)\n", r); + goto fail_clean_notifier; + } + + pm_runtime_put_noidle(&pci_dev->dev); + pm_runtime_allow(&pci_dev->dev); + + return 0; + +fail_clean_notifier: + v4l2_async_notifier_unregister(&cio2->notifier); + v4l2_async_notifier_cleanup(&cio2->notifier); + cio2_queues_exit(cio2); +fail_v4l2_device_unregister: + v4l2_device_unregister(&cio2->v4l2_dev); +fail_media_device_unregister: + media_device_unregister(&cio2->media_dev); + media_device_cleanup(&cio2->media_dev); +fail_mutex_destroy: + mutex_destroy(&cio2->lock); + cio2_fbpt_exit_dummy(cio2); + + return r; +} + +static void cio2_pci_remove(struct pci_dev *pci_dev) +{ + struct cio2_device *cio2 = pci_get_drvdata(pci_dev); + + media_device_unregister(&cio2->media_dev); + v4l2_async_notifier_unregister(&cio2->notifier); + v4l2_async_notifier_cleanup(&cio2->notifier); + cio2_queues_exit(cio2); + cio2_fbpt_exit_dummy(cio2); + v4l2_device_unregister(&cio2->v4l2_dev); + media_device_cleanup(&cio2->media_dev); + mutex_destroy(&cio2->lock); +} + +static int __maybe_unused cio2_runtime_suspend(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cio2_device *cio2 = pci_get_drvdata(pci_dev); + void __iomem *const base = cio2->base; + u16 pm; + + writel(CIO2_D0I3C_I3, base + CIO2_REG_D0I3C); + dev_dbg(dev, "cio2 runtime suspend.\n"); + + pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm); + pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT; + pm |= CIO2_PMCSR_D3; + pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm); + + return 0; +} + +static int __maybe_unused cio2_runtime_resume(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cio2_device *cio2 = pci_get_drvdata(pci_dev); + void __iomem *const base = cio2->base; + u16 pm; + + writel(CIO2_D0I3C_RR, base + CIO2_REG_D0I3C); + dev_dbg(dev, "cio2 runtime resume.\n"); + + pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm); + pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT; + pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm); + + return 0; +} + +/* + * Helper function to advance all the elements of a circular buffer by "start" + * positions + */ +static void arrange(void *ptr, size_t elem_size, size_t elems, size_t start) +{ + struct { + size_t begin, end; + } arr[2] = { + { 0, start - 1 }, + { start, elems - 1 }, + }; + +#define CHUNK_SIZE(a) ((a)->end - (a)->begin + 1) + + /* Loop as long as we have out-of-place entries */ + while (CHUNK_SIZE(&arr[0]) && CHUNK_SIZE(&arr[1])) { + size_t size0, i; + + /* + * Find the number of entries that can be arranged on this + * iteration. + */ + size0 = min(CHUNK_SIZE(&arr[0]), CHUNK_SIZE(&arr[1])); + + /* Swap the entries in two parts of the array. */ + for (i = 0; i < size0; i++) { + u8 *d = ptr + elem_size * (arr[1].begin + i); + u8 *s = ptr + elem_size * (arr[0].begin + i); + size_t j; + + for (j = 0; j < elem_size; j++) + swap(d[j], s[j]); + } + + if (CHUNK_SIZE(&arr[0]) > CHUNK_SIZE(&arr[1])) { + /* The end of the first array remains unarranged. */ + arr[0].begin += size0; + } else { + /* + * The first array is fully arranged so we proceed + * handling the next one. + */ + arr[0].begin = arr[1].begin; + arr[0].end = arr[1].begin + size0 - 1; + arr[1].begin += size0; + } + } +} + +static void cio2_fbpt_rearrange(struct cio2_device *cio2, struct cio2_queue *q) +{ + unsigned int i, j; + + for (i = 0, j = q->bufs_first; i < CIO2_MAX_BUFFERS; + i++, j = (j + 1) % CIO2_MAX_BUFFERS) + if (q->bufs[j]) + break; + + if (i == CIO2_MAX_BUFFERS) + return; + + if (j) { + arrange(q->fbpt, sizeof(struct cio2_fbpt_entry) * CIO2_MAX_LOPS, + CIO2_MAX_BUFFERS, j); + arrange(q->bufs, sizeof(struct cio2_buffer *), + CIO2_MAX_BUFFERS, j); + } + + /* + * DMA clears the valid bit when accessing the buffer. + * When stopping stream in suspend callback, some of the buffers + * may be in invalid state. After resume, when DMA meets the invalid + * buffer, it will halt and stop receiving new data. + * To avoid DMA halting, set the valid bit for all buffers in FBPT. + */ + for (i = 0; i < CIO2_MAX_BUFFERS; i++) + cio2_fbpt_entry_enable(cio2, q->fbpt + i * CIO2_MAX_LOPS); +} + +static int __maybe_unused cio2_suspend(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cio2_device *cio2 = pci_get_drvdata(pci_dev); + struct cio2_queue *q = cio2->cur_queue; + + dev_dbg(dev, "cio2 suspend\n"); + if (!cio2->streaming) + return 0; + + /* Stop stream */ + cio2_hw_exit(cio2, q); + synchronize_irq(pci_dev->irq); + + pm_runtime_force_suspend(dev); + + /* + * Upon resume, hw starts to process the fbpt entries from beginning, + * so relocate the queued buffs to the fbpt head before suspend. + */ + cio2_fbpt_rearrange(cio2, q); + q->bufs_first = 0; + q->bufs_next = 0; + + return 0; +} + +static int __maybe_unused cio2_resume(struct device *dev) +{ + struct cio2_device *cio2 = dev_get_drvdata(dev); + struct cio2_queue *q = cio2->cur_queue; + int r; + + dev_dbg(dev, "cio2 resume\n"); + if (!cio2->streaming) + return 0; + /* Start stream */ + r = pm_runtime_force_resume(&cio2->pci_dev->dev); + if (r < 0) { + dev_err(&cio2->pci_dev->dev, + "failed to set power %d\n", r); + return r; + } + + r = cio2_hw_init(cio2, q); + if (r) + dev_err(dev, "fail to init cio2 hw\n"); + + return r; +} + +static const struct dev_pm_ops cio2_pm_ops = { + SET_RUNTIME_PM_OPS(&cio2_runtime_suspend, &cio2_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(&cio2_suspend, &cio2_resume) +}; + +static const struct pci_device_id cio2_pci_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, CIO2_PCI_ID) }, + { } +}; + +MODULE_DEVICE_TABLE(pci, cio2_pci_id_table); + +static struct pci_driver cio2_pci_driver = { + .name = CIO2_NAME, + .id_table = cio2_pci_id_table, + .probe = cio2_pci_probe, + .remove = cio2_pci_remove, + .driver = { + .pm = &cio2_pm_ops, + }, +}; + +module_pci_driver(cio2_pci_driver); + +MODULE_AUTHOR("Tuukka Toivonen "); +MODULE_AUTHOR("Tianshu Qiu "); +MODULE_AUTHOR("Jian Xu Zheng"); +MODULE_AUTHOR("Yuning Pu "); +MODULE_AUTHOR("Yong Zhi "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("IPU3 CIO2 driver"); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c deleted file mode 100644 index e8ea69d30bfd..000000000000 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ /dev/null @@ -1,2026 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2017,2020 Intel Corporation - * - * Based partially on Intel IPU4 driver written by - * Sakari Ailus - * Samu Onkalo - * Jouni Högander - * Jouni Ukkonen - * Antti Laakso - * et al. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ipu3-cio2.h" - -struct ipu3_cio2_fmt { - u32 mbus_code; - u32 fourcc; - u8 mipicode; - u8 bpp; -}; - -/* - * These are raw formats used in Intel's third generation of - * Image Processing Unit known as IPU3. - * 10bit raw bayer packed, 32 bytes for every 25 pixels, - * last LSB 6 bits unused. - */ -static const struct ipu3_cio2_fmt formats[] = { - { /* put default entry at beginning */ - .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, - .fourcc = V4L2_PIX_FMT_IPU3_SGRBG10, - .mipicode = 0x2b, - .bpp = 10, - }, { - .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, - .fourcc = V4L2_PIX_FMT_IPU3_SGBRG10, - .mipicode = 0x2b, - .bpp = 10, - }, { - .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, - .fourcc = V4L2_PIX_FMT_IPU3_SBGGR10, - .mipicode = 0x2b, - .bpp = 10, - }, { - .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, - .fourcc = V4L2_PIX_FMT_IPU3_SRGGB10, - .mipicode = 0x2b, - .bpp = 10, - }, -}; - -/* - * cio2_find_format - lookup color format by fourcc or/and media bus code - * @pixelformat: fourcc to match, ignored if null - * @mbus_code: media bus code to match, ignored if null - */ -static const struct ipu3_cio2_fmt *cio2_find_format(const u32 *pixelformat, - const u32 *mbus_code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (pixelformat && *pixelformat != formats[i].fourcc) - continue; - if (mbus_code && *mbus_code != formats[i].mbus_code) - continue; - - return &formats[i]; - } - - return NULL; -} - -static inline u32 cio2_bytesperline(const unsigned int width) -{ - /* - * 64 bytes for every 50 pixels, the line length - * in bytes is multiple of 64 (line end alignment). - */ - return DIV_ROUND_UP(width, 50) * 64; -} - -/**************** FBPT operations ****************/ - -static void cio2_fbpt_exit_dummy(struct cio2_device *cio2) -{ - if (cio2->dummy_lop) { - dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, - cio2->dummy_lop, cio2->dummy_lop_bus_addr); - cio2->dummy_lop = NULL; - } - if (cio2->dummy_page) { - dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, - cio2->dummy_page, cio2->dummy_page_bus_addr); - cio2->dummy_page = NULL; - } -} - -static int cio2_fbpt_init_dummy(struct cio2_device *cio2) -{ - unsigned int i; - - cio2->dummy_page = dma_alloc_coherent(&cio2->pci_dev->dev, PAGE_SIZE, - &cio2->dummy_page_bus_addr, - GFP_KERNEL); - cio2->dummy_lop = dma_alloc_coherent(&cio2->pci_dev->dev, PAGE_SIZE, - &cio2->dummy_lop_bus_addr, - GFP_KERNEL); - if (!cio2->dummy_page || !cio2->dummy_lop) { - cio2_fbpt_exit_dummy(cio2); - return -ENOMEM; - } - /* - * List of Pointers(LOP) contains 1024x32b pointers to 4KB page each - * Initialize each entry to dummy_page bus base address. - */ - for (i = 0; i < CIO2_LOP_ENTRIES; i++) - cio2->dummy_lop[i] = PFN_DOWN(cio2->dummy_page_bus_addr); - - return 0; -} - -static void cio2_fbpt_entry_enable(struct cio2_device *cio2, - struct cio2_fbpt_entry entry[CIO2_MAX_LOPS]) -{ - /* - * The CPU first initializes some fields in fbpt, then sets - * the VALID bit, this barrier is to ensure that the DMA(device) - * does not see the VALID bit enabled before other fields are - * initialized; otherwise it could lead to havoc. - */ - dma_wmb(); - - /* - * Request interrupts for start and completion - * Valid bit is applicable only to 1st entry - */ - entry[0].first_entry.ctrl = CIO2_FBPT_CTRL_VALID | - CIO2_FBPT_CTRL_IOC | CIO2_FBPT_CTRL_IOS; -} - -/* Initialize fpbt entries to point to dummy frame */ -static void cio2_fbpt_entry_init_dummy(struct cio2_device *cio2, - struct cio2_fbpt_entry - entry[CIO2_MAX_LOPS]) -{ - unsigned int i; - - entry[0].first_entry.first_page_offset = 0; - entry[1].second_entry.num_of_pages = CIO2_LOP_ENTRIES * CIO2_MAX_LOPS; - entry[1].second_entry.last_page_available_bytes = PAGE_SIZE - 1; - - for (i = 0; i < CIO2_MAX_LOPS; i++) - entry[i].lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr); - - cio2_fbpt_entry_enable(cio2, entry); -} - -/* Initialize fpbt entries to point to a given buffer */ -static void cio2_fbpt_entry_init_buf(struct cio2_device *cio2, - struct cio2_buffer *b, - struct cio2_fbpt_entry - entry[CIO2_MAX_LOPS]) -{ - struct vb2_buffer *vb = &b->vbb.vb2_buf; - unsigned int length = vb->planes[0].length; - int remaining, i; - - entry[0].first_entry.first_page_offset = b->offset; - remaining = length + entry[0].first_entry.first_page_offset; - entry[1].second_entry.num_of_pages = PFN_UP(remaining); - /* - * last_page_available_bytes has the offset of the last byte in the - * last page which is still accessible by DMA. DMA cannot access - * beyond this point. Valid range for this is from 0 to 4095. - * 0 indicates 1st byte in the page is DMA accessible. - * 4095 (PAGE_SIZE - 1) means every single byte in the last page - * is available for DMA transfer. - */ - remaining = offset_in_page(remaining) ?: PAGE_SIZE; - entry[1].second_entry.last_page_available_bytes = remaining - 1; - /* Fill FBPT */ - remaining = length; - i = 0; - while (remaining > 0) { - entry->lop_page_addr = PFN_DOWN(b->lop_bus_addr[i]); - remaining -= CIO2_LOP_ENTRIES * PAGE_SIZE; - entry++; - i++; - } - - /* - * The first not meaningful FBPT entry should point to a valid LOP - */ - entry->lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr); - - cio2_fbpt_entry_enable(cio2, entry); -} - -static int cio2_fbpt_init(struct cio2_device *cio2, struct cio2_queue *q) -{ - struct device *dev = &cio2->pci_dev->dev; - - q->fbpt = dma_alloc_coherent(dev, CIO2_FBPT_SIZE, &q->fbpt_bus_addr, - GFP_KERNEL); - if (!q->fbpt) - return -ENOMEM; - - return 0; -} - -static void cio2_fbpt_exit(struct cio2_queue *q, struct device *dev) -{ - dma_free_coherent(dev, CIO2_FBPT_SIZE, q->fbpt, q->fbpt_bus_addr); -} - -/**************** CSI2 hardware setup ****************/ - -/* - * The CSI2 receiver has several parameters affecting - * the receiver timings. These depend on the MIPI bus frequency - * F in Hz (sensor transmitter rate) as follows: - * register value = (A/1e9 + B * UI) / COUNT_ACC - * where - * UI = 1 / (2 * F) in seconds - * COUNT_ACC = counter accuracy in seconds - * For IPU3 COUNT_ACC = 0.0625 - * - * A and B are coefficients from the table below, - * depending whether the register minimum or maximum value is - * calculated. - * Minimum Maximum - * Clock lane A B A B - * reg_rx_csi_dly_cnt_termen_clane 0 0 38 0 - * reg_rx_csi_dly_cnt_settle_clane 95 -8 300 -16 - * Data lanes - * reg_rx_csi_dly_cnt_termen_dlane0 0 0 35 4 - * reg_rx_csi_dly_cnt_settle_dlane0 85 -2 145 -6 - * reg_rx_csi_dly_cnt_termen_dlane1 0 0 35 4 - * reg_rx_csi_dly_cnt_settle_dlane1 85 -2 145 -6 - * reg_rx_csi_dly_cnt_termen_dlane2 0 0 35 4 - * reg_rx_csi_dly_cnt_settle_dlane2 85 -2 145 -6 - * reg_rx_csi_dly_cnt_termen_dlane3 0 0 35 4 - * reg_rx_csi_dly_cnt_settle_dlane3 85 -2 145 -6 - * - * We use the minimum values of both A and B. - */ - -/* - * shift for keeping value range suitable for 32-bit integer arithmetic - */ -#define LIMIT_SHIFT 8 - -static s32 cio2_rx_timing(s32 a, s32 b, s64 freq, int def) -{ - const u32 accinv = 16; /* invert of counter resolution */ - const u32 uiinv = 500000000; /* 1e9 / 2 */ - s32 r; - - freq >>= LIMIT_SHIFT; - - if (WARN_ON(freq <= 0 || freq > S32_MAX)) - return def; - /* - * b could be 0, -2 or -8, so |accinv * b| is always - * less than (1 << ds) and thus |r| < 500000000. - */ - r = accinv * b * (uiinv >> LIMIT_SHIFT); - r = r / (s32)freq; - /* max value of a is 95 */ - r += accinv * a; - - return r; -}; - -/* Calculate the delay value for termination enable of clock lane HS Rx */ -static int cio2_csi2_calc_timing(struct cio2_device *cio2, struct cio2_queue *q, - struct cio2_csi2_timing *timing, - unsigned int bpp, unsigned int lanes) -{ - struct device *dev = &cio2->pci_dev->dev; - s64 freq; - - if (!q->sensor) - return -ENODEV; - - freq = v4l2_get_link_rate(q->sensor->ctrl_handler, bpp, lanes); - if (freq < 0) { - dev_err(dev, "error %lld, invalid link_freq\n", freq); - return freq; - } - - timing->clk_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_A, - CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_B, - freq, - CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT); - timing->clk_settle = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_SETTLE_CLANE_A, - CIO2_CSIRX_DLY_CNT_SETTLE_CLANE_B, - freq, - CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT); - timing->dat_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_DLANE_A, - CIO2_CSIRX_DLY_CNT_TERMEN_DLANE_B, - freq, - CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT); - timing->dat_settle = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_SETTLE_DLANE_A, - CIO2_CSIRX_DLY_CNT_SETTLE_DLANE_B, - freq, - CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT); - - dev_dbg(dev, "freq ct value is %d\n", timing->clk_termen); - dev_dbg(dev, "freq cs value is %d\n", timing->clk_settle); - dev_dbg(dev, "freq dt value is %d\n", timing->dat_termen); - dev_dbg(dev, "freq ds value is %d\n", timing->dat_settle); - - return 0; -}; - -static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q) -{ - static const int NUM_VCS = 4; - static const int SID; /* Stream id */ - static const int ENTRY; - static const int FBPT_WIDTH = DIV_ROUND_UP(CIO2_MAX_LOPS, - CIO2_FBPT_SUBENTRY_UNIT); - const u32 num_buffers1 = CIO2_MAX_BUFFERS - 1; - const struct ipu3_cio2_fmt *fmt; - void __iomem *const base = cio2->base; - u8 lanes, csi2bus = q->csi2.port; - u8 sensor_vc = SENSOR_VIR_CH_DFLT; - struct cio2_csi2_timing timing; - int i, r; - - fmt = cio2_find_format(NULL, &q->subdev_fmt.code); - if (!fmt) - return -EINVAL; - - lanes = q->csi2.lanes; - - r = cio2_csi2_calc_timing(cio2, q, &timing, fmt->bpp, lanes); - if (r) - return r; - - writel(timing.clk_termen, q->csi_rx_base + - CIO2_REG_CSIRX_DLY_CNT_TERMEN(CIO2_CSIRX_DLY_CNT_CLANE_IDX)); - writel(timing.clk_settle, q->csi_rx_base + - CIO2_REG_CSIRX_DLY_CNT_SETTLE(CIO2_CSIRX_DLY_CNT_CLANE_IDX)); - - for (i = 0; i < lanes; i++) { - writel(timing.dat_termen, q->csi_rx_base + - CIO2_REG_CSIRX_DLY_CNT_TERMEN(i)); - writel(timing.dat_settle, q->csi_rx_base + - CIO2_REG_CSIRX_DLY_CNT_SETTLE(i)); - } - - writel(CIO2_PBM_WMCTRL1_MIN_2CK | - CIO2_PBM_WMCTRL1_MID1_2CK | - CIO2_PBM_WMCTRL1_MID2_2CK, base + CIO2_REG_PBM_WMCTRL1); - writel(CIO2_PBM_WMCTRL2_HWM_2CK << CIO2_PBM_WMCTRL2_HWM_2CK_SHIFT | - CIO2_PBM_WMCTRL2_LWM_2CK << CIO2_PBM_WMCTRL2_LWM_2CK_SHIFT | - CIO2_PBM_WMCTRL2_OBFFWM_2CK << - CIO2_PBM_WMCTRL2_OBFFWM_2CK_SHIFT | - CIO2_PBM_WMCTRL2_TRANSDYN << CIO2_PBM_WMCTRL2_TRANSDYN_SHIFT | - CIO2_PBM_WMCTRL2_OBFF_MEM_EN, base + CIO2_REG_PBM_WMCTRL2); - writel(CIO2_PBM_ARB_CTRL_LANES_DIV << - CIO2_PBM_ARB_CTRL_LANES_DIV_SHIFT | - CIO2_PBM_ARB_CTRL_LE_EN | - CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN << - CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN_SHIFT | - CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP << - CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP_SHIFT, - base + CIO2_REG_PBM_ARB_CTRL); - writel(CIO2_CSIRX_STATUS_DLANE_HS_MASK, - q->csi_rx_base + CIO2_REG_CSIRX_STATUS_DLANE_HS); - writel(CIO2_CSIRX_STATUS_DLANE_LP_MASK, - q->csi_rx_base + CIO2_REG_CSIRX_STATUS_DLANE_LP); - - writel(CIO2_FB_HPLL_FREQ, base + CIO2_REG_FB_HPLL_FREQ); - writel(CIO2_ISCLK_RATIO, base + CIO2_REG_ISCLK_RATIO); - - /* Configure MIPI backend */ - for (i = 0; i < NUM_VCS; i++) - writel(1, q->csi_rx_base + CIO2_REG_MIPIBE_SP_LUT_ENTRY(i)); - - /* There are 16 short packet LUT entry */ - for (i = 0; i < 16; i++) - writel(CIO2_MIPIBE_LP_LUT_ENTRY_DISREGARD, - q->csi_rx_base + CIO2_REG_MIPIBE_LP_LUT_ENTRY(i)); - writel(CIO2_MIPIBE_GLOBAL_LUT_DISREGARD, - q->csi_rx_base + CIO2_REG_MIPIBE_GLOBAL_LUT_DISREGARD); - - writel(CIO2_INT_EN_EXT_IE_MASK, base + CIO2_REG_INT_EN_EXT_IE); - writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK); - writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE); - writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_EDGE); - writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_LEVEL_NOT_PULSE); - writel(CIO2_INT_EN_EXT_OE_MASK, base + CIO2_REG_INT_EN_EXT_OE); - - writel(CIO2_REG_INT_EN_IRQ | CIO2_INT_IOC(CIO2_DMA_CHAN) | - CIO2_REG_INT_EN_IOS(CIO2_DMA_CHAN), - base + CIO2_REG_INT_EN); - - writel((CIO2_PXM_PXF_FMT_CFG_BPP_10 | CIO2_PXM_PXF_FMT_CFG_PCK_64B) - << CIO2_PXM_PXF_FMT_CFG_SID0_SHIFT, - base + CIO2_REG_PXM_PXF_FMT_CFG0(csi2bus)); - writel(SID << CIO2_MIPIBE_LP_LUT_ENTRY_SID_SHIFT | - sensor_vc << CIO2_MIPIBE_LP_LUT_ENTRY_VC_SHIFT | - fmt->mipicode << CIO2_MIPIBE_LP_LUT_ENTRY_FORMAT_TYPE_SHIFT, - q->csi_rx_base + CIO2_REG_MIPIBE_LP_LUT_ENTRY(ENTRY)); - writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_COMP_FORMAT(sensor_vc)); - writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_FORCE_RAW8); - writel(0, base + CIO2_REG_PXM_SID2BID0(csi2bus)); - - writel(lanes, q->csi_rx_base + CIO2_REG_CSIRX_NOF_ENABLED_LANES); - writel(CIO2_CGC_PRIM_TGE | - CIO2_CGC_SIDE_TGE | - CIO2_CGC_XOSC_TGE | - CIO2_CGC_D3I3_TGE | - CIO2_CGC_CSI2_INTERFRAME_TGE | - CIO2_CGC_CSI2_PORT_DCGE | - CIO2_CGC_SIDE_DCGE | - CIO2_CGC_PRIM_DCGE | - CIO2_CGC_ROSC_DCGE | - CIO2_CGC_XOSC_DCGE | - CIO2_CGC_CLKGATE_HOLDOFF << CIO2_CGC_CLKGATE_HOLDOFF_SHIFT | - CIO2_CGC_CSI_CLKGATE_HOLDOFF - << CIO2_CGC_CSI_CLKGATE_HOLDOFF_SHIFT, base + CIO2_REG_CGC); - writel(CIO2_LTRCTRL_LTRDYNEN, base + CIO2_REG_LTRCTRL); - writel(CIO2_LTRVAL0_VAL << CIO2_LTRVAL02_VAL_SHIFT | - CIO2_LTRVAL0_SCALE << CIO2_LTRVAL02_SCALE_SHIFT | - CIO2_LTRVAL1_VAL << CIO2_LTRVAL13_VAL_SHIFT | - CIO2_LTRVAL1_SCALE << CIO2_LTRVAL13_SCALE_SHIFT, - base + CIO2_REG_LTRVAL01); - writel(CIO2_LTRVAL2_VAL << CIO2_LTRVAL02_VAL_SHIFT | - CIO2_LTRVAL2_SCALE << CIO2_LTRVAL02_SCALE_SHIFT | - CIO2_LTRVAL3_VAL << CIO2_LTRVAL13_VAL_SHIFT | - CIO2_LTRVAL3_SCALE << CIO2_LTRVAL13_SCALE_SHIFT, - base + CIO2_REG_LTRVAL23); - - for (i = 0; i < CIO2_NUM_DMA_CHAN; i++) { - writel(0, base + CIO2_REG_CDMABA(i)); - writel(0, base + CIO2_REG_CDMAC0(i)); - writel(0, base + CIO2_REG_CDMAC1(i)); - } - - /* Enable DMA */ - writel(PFN_DOWN(q->fbpt_bus_addr), base + CIO2_REG_CDMABA(CIO2_DMA_CHAN)); - - writel(num_buffers1 << CIO2_CDMAC0_FBPT_LEN_SHIFT | - FBPT_WIDTH << CIO2_CDMAC0_FBPT_WIDTH_SHIFT | - CIO2_CDMAC0_DMA_INTR_ON_FE | - CIO2_CDMAC0_FBPT_UPDATE_FIFO_FULL | - CIO2_CDMAC0_DMA_EN | - CIO2_CDMAC0_DMA_INTR_ON_FS | - CIO2_CDMAC0_DMA_HALTED, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN)); - - writel(1 << CIO2_CDMAC1_LINENUMUPDATE_SHIFT, - base + CIO2_REG_CDMAC1(CIO2_DMA_CHAN)); - - writel(0, base + CIO2_REG_PBM_FOPN_ABORT); - - writel(CIO2_PXM_FRF_CFG_CRC_TH << CIO2_PXM_FRF_CFG_CRC_TH_SHIFT | - CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NR | - CIO2_PXM_FRF_CFG_MSK_ECC_RE | - CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NE, - base + CIO2_REG_PXM_FRF_CFG(q->csi2.port)); - - /* Clear interrupts */ - writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_CLEAR); - writel(~0, base + CIO2_REG_INT_STS_EXT_OE); - writel(~0, base + CIO2_REG_INT_STS_EXT_IE); - writel(~0, base + CIO2_REG_INT_STS); - - /* Enable devices, starting from the last device in the pipe */ - writel(1, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE); - writel(1, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE); - - return 0; -} - -static void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q) -{ - void __iomem *const base = cio2->base; - unsigned int i; - u32 value; - int ret; - - /* Disable CSI receiver and MIPI backend devices */ - writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK); - writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE); - writel(0, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE); - writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE); - - /* Halt DMA */ - writel(0, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN)); - ret = readl_poll_timeout(base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN), - value, value & CIO2_CDMAC0_DMA_HALTED, - 4000, 2000000); - if (ret) - dev_err(&cio2->pci_dev->dev, - "DMA %i can not be halted\n", CIO2_DMA_CHAN); - - for (i = 0; i < CIO2_NUM_PORTS; i++) { - writel(readl(base + CIO2_REG_PXM_FRF_CFG(i)) | - CIO2_PXM_FRF_CFG_ABORT, base + CIO2_REG_PXM_FRF_CFG(i)); - writel(readl(base + CIO2_REG_PBM_FOPN_ABORT) | - CIO2_PBM_FOPN_ABORT(i), base + CIO2_REG_PBM_FOPN_ABORT); - } -} - -static void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan) -{ - struct device *dev = &cio2->pci_dev->dev; - struct cio2_queue *q = cio2->cur_queue; - struct cio2_fbpt_entry *entry; - u64 ns = ktime_get_ns(); - - if (dma_chan >= CIO2_QUEUES) { - dev_err(dev, "bad DMA channel %i\n", dma_chan); - return; - } - - entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS]; - if (entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID) { - dev_warn(&cio2->pci_dev->dev, - "no ready buffers found on DMA channel %u\n", - dma_chan); - return; - } - - /* Find out which buffer(s) are ready */ - do { - struct cio2_buffer *b; - - b = q->bufs[q->bufs_first]; - if (b) { - unsigned int received = entry[1].second_entry.num_of_bytes; - unsigned long payload = - vb2_get_plane_payload(&b->vbb.vb2_buf, 0); - - q->bufs[q->bufs_first] = NULL; - atomic_dec(&q->bufs_queued); - dev_dbg(&cio2->pci_dev->dev, - "buffer %i done\n", b->vbb.vb2_buf.index); - - b->vbb.vb2_buf.timestamp = ns; - b->vbb.field = V4L2_FIELD_NONE; - b->vbb.sequence = atomic_read(&q->frame_sequence); - if (payload != received) - dev_warn(dev, - "payload length is %lu, received %u\n", - payload, received); - vb2_buffer_done(&b->vbb.vb2_buf, VB2_BUF_STATE_DONE); - } - atomic_inc(&q->frame_sequence); - cio2_fbpt_entry_init_dummy(cio2, entry); - q->bufs_first = (q->bufs_first + 1) % CIO2_MAX_BUFFERS; - entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS]; - } while (!(entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID)); -} - -static void cio2_queue_event_sof(struct cio2_device *cio2, struct cio2_queue *q) -{ - /* - * For the user space camera control algorithms it is essential - * to know when the reception of a frame has begun. That's often - * the best timing information to get from the hardware. - */ - struct v4l2_event event = { - .type = V4L2_EVENT_FRAME_SYNC, - .u.frame_sync.frame_sequence = atomic_read(&q->frame_sequence), - }; - - v4l2_event_queue(q->subdev.devnode, &event); -} - -static const char *const cio2_irq_errs[] = { - "single packet header error corrected", - "multiple packet header errors detected", - "payload checksum (CRC) error", - "fifo overflow", - "reserved short packet data type detected", - "reserved long packet data type detected", - "incomplete long packet detected", - "frame sync error", - "line sync error", - "DPHY start of transmission error", - "DPHY synchronization error", - "escape mode error", - "escape mode trigger event", - "escape mode ultra-low power state for data lane(s)", - "escape mode ultra-low power state exit for clock lane", - "inter-frame short packet discarded", - "inter-frame long packet discarded", - "non-matching Long Packet stalled", -}; - -static const char *const cio2_port_errs[] = { - "ECC recoverable", - "DPHY not recoverable", - "ECC not recoverable", - "CRC error", - "INTERFRAMEDATA", - "PKT2SHORT", - "PKT2LONG", -}; - -static void cio2_irq_handle_once(struct cio2_device *cio2, u32 int_status) -{ - void __iomem *const base = cio2->base; - struct device *dev = &cio2->pci_dev->dev; - - if (int_status & CIO2_INT_IOOE) { - /* - * Interrupt on Output Error: - * 1) SRAM is full and FS received, or - * 2) An invalid bit detected by DMA. - */ - u32 oe_status, oe_clear; - - oe_clear = readl(base + CIO2_REG_INT_STS_EXT_OE); - oe_status = oe_clear; - - if (oe_status & CIO2_INT_EXT_OE_DMAOE_MASK) { - dev_err(dev, "DMA output error: 0x%x\n", - (oe_status & CIO2_INT_EXT_OE_DMAOE_MASK) - >> CIO2_INT_EXT_OE_DMAOE_SHIFT); - oe_status &= ~CIO2_INT_EXT_OE_DMAOE_MASK; - } - if (oe_status & CIO2_INT_EXT_OE_OES_MASK) { - dev_err(dev, "DMA output error on CSI2 buses: 0x%x\n", - (oe_status & CIO2_INT_EXT_OE_OES_MASK) - >> CIO2_INT_EXT_OE_OES_SHIFT); - oe_status &= ~CIO2_INT_EXT_OE_OES_MASK; - } - writel(oe_clear, base + CIO2_REG_INT_STS_EXT_OE); - if (oe_status) - dev_warn(dev, "unknown interrupt 0x%x on OE\n", - oe_status); - int_status &= ~CIO2_INT_IOOE; - } - - if (int_status & CIO2_INT_IOC_MASK) { - /* DMA IO done -- frame ready */ - u32 clr = 0; - unsigned int d; - - for (d = 0; d < CIO2_NUM_DMA_CHAN; d++) - if (int_status & CIO2_INT_IOC(d)) { - clr |= CIO2_INT_IOC(d); - cio2_buffer_done(cio2, d); - } - int_status &= ~clr; - } - - if (int_status & CIO2_INT_IOS_IOLN_MASK) { - /* DMA IO starts or reached specified line */ - u32 clr = 0; - unsigned int d; - - for (d = 0; d < CIO2_NUM_DMA_CHAN; d++) - if (int_status & CIO2_INT_IOS_IOLN(d)) { - clr |= CIO2_INT_IOS_IOLN(d); - if (d == CIO2_DMA_CHAN) - cio2_queue_event_sof(cio2, - cio2->cur_queue); - } - int_status &= ~clr; - } - - if (int_status & (CIO2_INT_IOIE | CIO2_INT_IOIRQ)) { - /* CSI2 receiver (error) interrupt */ - u32 ie_status, ie_clear; - unsigned int port; - - ie_clear = readl(base + CIO2_REG_INT_STS_EXT_IE); - ie_status = ie_clear; - - for (port = 0; port < CIO2_NUM_PORTS; port++) { - u32 port_status = (ie_status >> (port * 8)) & 0xff; - u32 err_mask = BIT_MASK(ARRAY_SIZE(cio2_port_errs)) - 1; - void __iomem *const csi_rx_base = - base + CIO2_REG_PIPE_BASE(port); - unsigned int i; - - while (port_status & err_mask) { - i = ffs(port_status) - 1; - dev_err(dev, "port %i error %s\n", - port, cio2_port_errs[i]); - ie_status &= ~BIT(port * 8 + i); - port_status &= ~BIT(i); - } - - if (ie_status & CIO2_INT_EXT_IE_IRQ(port)) { - u32 csi2_status, csi2_clear; - - csi2_status = readl(csi_rx_base + - CIO2_REG_IRQCTRL_STATUS); - csi2_clear = csi2_status; - err_mask = - BIT_MASK(ARRAY_SIZE(cio2_irq_errs)) - 1; - - while (csi2_status & err_mask) { - i = ffs(csi2_status) - 1; - dev_err(dev, - "CSI-2 receiver port %i: %s\n", - port, cio2_irq_errs[i]); - csi2_status &= ~BIT(i); - } - - writel(csi2_clear, - csi_rx_base + CIO2_REG_IRQCTRL_CLEAR); - if (csi2_status) - dev_warn(dev, - "unknown CSI2 error 0x%x on port %i\n", - csi2_status, port); - - ie_status &= ~CIO2_INT_EXT_IE_IRQ(port); - } - } - - writel(ie_clear, base + CIO2_REG_INT_STS_EXT_IE); - if (ie_status) - dev_warn(dev, "unknown interrupt 0x%x on IE\n", - ie_status); - - int_status &= ~(CIO2_INT_IOIE | CIO2_INT_IOIRQ); - } - - if (int_status) - dev_warn(dev, "unknown interrupt 0x%x on INT\n", int_status); -} - -static irqreturn_t cio2_irq(int irq, void *cio2_ptr) -{ - struct cio2_device *cio2 = cio2_ptr; - void __iomem *const base = cio2->base; - struct device *dev = &cio2->pci_dev->dev; - u32 int_status; - - int_status = readl(base + CIO2_REG_INT_STS); - dev_dbg(dev, "isr enter - interrupt status 0x%x\n", int_status); - if (!int_status) - return IRQ_NONE; - - do { - writel(int_status, base + CIO2_REG_INT_STS); - cio2_irq_handle_once(cio2, int_status); - int_status = readl(base + CIO2_REG_INT_STS); - if (int_status) - dev_dbg(dev, "pending status 0x%x\n", int_status); - } while (int_status); - - return IRQ_HANDLED; -} - -/**************** Videobuf2 interface ****************/ - -static void cio2_vb2_return_all_buffers(struct cio2_queue *q, - enum vb2_buffer_state state) -{ - unsigned int i; - - for (i = 0; i < CIO2_MAX_BUFFERS; i++) { - if (q->bufs[i]) { - atomic_dec(&q->bufs_queued); - vb2_buffer_done(&q->bufs[i]->vbb.vb2_buf, - state); - q->bufs[i] = NULL; - } - } -} - -static int cio2_vb2_queue_setup(struct vb2_queue *vq, - unsigned int *num_buffers, - unsigned int *num_planes, - unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct cio2_device *cio2 = vb2_get_drv_priv(vq); - struct cio2_queue *q = vb2q_to_cio2_queue(vq); - unsigned int i; - - *num_planes = q->format.num_planes; - - for (i = 0; i < *num_planes; ++i) { - sizes[i] = q->format.plane_fmt[i].sizeimage; - alloc_devs[i] = &cio2->pci_dev->dev; - } - - *num_buffers = clamp_val(*num_buffers, 1, CIO2_MAX_BUFFERS); - - /* Initialize buffer queue */ - for (i = 0; i < CIO2_MAX_BUFFERS; i++) { - q->bufs[i] = NULL; - cio2_fbpt_entry_init_dummy(cio2, &q->fbpt[i * CIO2_MAX_LOPS]); - } - atomic_set(&q->bufs_queued, 0); - q->bufs_first = 0; - q->bufs_next = 0; - - return 0; -} - -/* Called after each buffer is allocated */ -static int cio2_vb2_buf_init(struct vb2_buffer *vb) -{ - struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue); - struct device *dev = &cio2->pci_dev->dev; - struct cio2_buffer *b = - container_of(vb, struct cio2_buffer, vbb.vb2_buf); - unsigned int pages = PFN_UP(vb->planes[0].length); - unsigned int lops = DIV_ROUND_UP(pages + 1, CIO2_LOP_ENTRIES); - struct sg_table *sg; - struct sg_dma_page_iter sg_iter; - unsigned int i, j; - - if (lops <= 0 || lops > CIO2_MAX_LOPS) { - dev_err(dev, "%s: bad buffer size (%i)\n", __func__, - vb->planes[0].length); - return -ENOSPC; /* Should never happen */ - } - - memset(b->lop, 0, sizeof(b->lop)); - /* Allocate LOP table */ - for (i = 0; i < lops; i++) { - b->lop[i] = dma_alloc_coherent(dev, PAGE_SIZE, - &b->lop_bus_addr[i], GFP_KERNEL); - if (!b->lop[i]) - goto fail; - } - - /* Fill LOP */ - sg = vb2_dma_sg_plane_desc(vb, 0); - if (!sg) - return -ENOMEM; - - if (sg->nents && sg->sgl) - b->offset = sg->sgl->offset; - - i = j = 0; - for_each_sg_dma_page(sg->sgl, &sg_iter, sg->nents, 0) { - if (!pages--) - break; - b->lop[i][j] = PFN_DOWN(sg_page_iter_dma_address(&sg_iter)); - j++; - if (j == CIO2_LOP_ENTRIES) { - i++; - j = 0; - } - } - - b->lop[i][j] = PFN_DOWN(cio2->dummy_page_bus_addr); - return 0; -fail: - while (i--) - dma_free_coherent(dev, PAGE_SIZE, b->lop[i], b->lop_bus_addr[i]); - return -ENOMEM; -} - -/* Transfer buffer ownership to cio2 */ -static void cio2_vb2_buf_queue(struct vb2_buffer *vb) -{ - struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue); - struct cio2_queue *q = - container_of(vb->vb2_queue, struct cio2_queue, vbq); - struct cio2_buffer *b = - container_of(vb, struct cio2_buffer, vbb.vb2_buf); - struct cio2_fbpt_entry *entry; - unsigned long flags; - unsigned int i, j, next = q->bufs_next; - int bufs_queued = atomic_inc_return(&q->bufs_queued); - u32 fbpt_rp; - - dev_dbg(&cio2->pci_dev->dev, "queue buffer %d\n", vb->index); - - /* - * This code queues the buffer to the CIO2 DMA engine, which starts - * running once streaming has started. It is possible that this code - * gets pre-empted due to increased CPU load. Upon this, the driver - * does not get an opportunity to queue new buffers to the CIO2 DMA - * engine. When the DMA engine encounters an FBPT entry without the - * VALID bit set, the DMA engine halts, which requires a restart of - * the DMA engine and sensor, to continue streaming. - * This is not desired and is highly unlikely given that there are - * 32 FBPT entries that the DMA engine needs to process, to run into - * an FBPT entry, without the VALID bit set. We try to mitigate this - * by disabling interrupts for the duration of this queueing. - */ - local_irq_save(flags); - - fbpt_rp = (readl(cio2->base + CIO2_REG_CDMARI(CIO2_DMA_CHAN)) - >> CIO2_CDMARI_FBPT_RP_SHIFT) - & CIO2_CDMARI_FBPT_RP_MASK; - - /* - * fbpt_rp is the fbpt entry that the dma is currently working - * on, but since it could jump to next entry at any time, - * assume that we might already be there. - */ - fbpt_rp = (fbpt_rp + 1) % CIO2_MAX_BUFFERS; - - if (bufs_queued <= 1 || fbpt_rp == next) - /* Buffers were drained */ - next = (fbpt_rp + 1) % CIO2_MAX_BUFFERS; - - for (i = 0; i < CIO2_MAX_BUFFERS; i++) { - /* - * We have allocated CIO2_MAX_BUFFERS circularly for the - * hw, the user has requested N buffer queue. The driver - * ensures N <= CIO2_MAX_BUFFERS and guarantees that whenever - * user queues a buffer, there necessarily is a free buffer. - */ - if (!q->bufs[next]) { - q->bufs[next] = b; - entry = &q->fbpt[next * CIO2_MAX_LOPS]; - cio2_fbpt_entry_init_buf(cio2, b, entry); - local_irq_restore(flags); - q->bufs_next = (next + 1) % CIO2_MAX_BUFFERS; - for (j = 0; j < vb->num_planes; j++) - vb2_set_plane_payload(vb, j, - q->format.plane_fmt[j].sizeimage); - return; - } - - dev_dbg(&cio2->pci_dev->dev, "entry %i was full!\n", next); - next = (next + 1) % CIO2_MAX_BUFFERS; - } - - local_irq_restore(flags); - dev_err(&cio2->pci_dev->dev, "error: all cio2 entries were full!\n"); - atomic_dec(&q->bufs_queued); - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -} - -/* Called when each buffer is freed */ -static void cio2_vb2_buf_cleanup(struct vb2_buffer *vb) -{ - struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue); - struct cio2_buffer *b = - container_of(vb, struct cio2_buffer, vbb.vb2_buf); - unsigned int i; - - /* Free LOP table */ - for (i = 0; i < CIO2_MAX_LOPS; i++) { - if (b->lop[i]) - dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, - b->lop[i], b->lop_bus_addr[i]); - } -} - -static int cio2_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct cio2_queue *q = vb2q_to_cio2_queue(vq); - struct cio2_device *cio2 = vb2_get_drv_priv(vq); - int r; - - cio2->cur_queue = q; - atomic_set(&q->frame_sequence, 0); - - r = pm_runtime_get_sync(&cio2->pci_dev->dev); - if (r < 0) { - dev_info(&cio2->pci_dev->dev, "failed to set power %d\n", r); - pm_runtime_put_noidle(&cio2->pci_dev->dev); - return r; - } - - r = media_pipeline_start(&q->vdev.entity, &q->pipe); - if (r) - goto fail_pipeline; - - r = cio2_hw_init(cio2, q); - if (r) - goto fail_hw; - - /* Start streaming on sensor */ - r = v4l2_subdev_call(q->sensor, video, s_stream, 1); - if (r) - goto fail_csi2_subdev; - - cio2->streaming = true; - - return 0; - -fail_csi2_subdev: - cio2_hw_exit(cio2, q); -fail_hw: - media_pipeline_stop(&q->vdev.entity); -fail_pipeline: - dev_dbg(&cio2->pci_dev->dev, "failed to start streaming (%d)\n", r); - cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_QUEUED); - pm_runtime_put(&cio2->pci_dev->dev); - - return r; -} - -static void cio2_vb2_stop_streaming(struct vb2_queue *vq) -{ - struct cio2_queue *q = vb2q_to_cio2_queue(vq); - struct cio2_device *cio2 = vb2_get_drv_priv(vq); - - if (v4l2_subdev_call(q->sensor, video, s_stream, 0)) - dev_err(&cio2->pci_dev->dev, - "failed to stop sensor streaming\n"); - - cio2_hw_exit(cio2, q); - synchronize_irq(cio2->pci_dev->irq); - cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_ERROR); - media_pipeline_stop(&q->vdev.entity); - pm_runtime_put(&cio2->pci_dev->dev); - cio2->streaming = false; -} - -static const struct vb2_ops cio2_vb2_ops = { - .buf_init = cio2_vb2_buf_init, - .buf_queue = cio2_vb2_buf_queue, - .buf_cleanup = cio2_vb2_buf_cleanup, - .queue_setup = cio2_vb2_queue_setup, - .start_streaming = cio2_vb2_start_streaming, - .stop_streaming = cio2_vb2_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -/**************** V4L2 interface ****************/ - -static int cio2_v4l2_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct cio2_device *cio2 = video_drvdata(file); - - strscpy(cap->driver, CIO2_NAME, sizeof(cap->driver)); - strscpy(cap->card, CIO2_DEVICE_NAME, sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), - "PCI:%s", pci_name(cio2->pci_dev)); - - return 0; -} - -static int cio2_v4l2_enum_fmt(struct file *file, void *fh, - struct v4l2_fmtdesc *f) -{ - if (f->index >= ARRAY_SIZE(formats)) - return -EINVAL; - - f->pixelformat = formats[f->index].fourcc; - - return 0; -} - -/* The format is validated in cio2_video_link_validate() */ -static int cio2_v4l2_g_fmt(struct file *file, void *fh, struct v4l2_format *f) -{ - struct cio2_queue *q = file_to_cio2_queue(file); - - f->fmt.pix_mp = q->format; - - return 0; -} - -static int cio2_v4l2_try_fmt(struct file *file, void *fh, struct v4l2_format *f) -{ - const struct ipu3_cio2_fmt *fmt; - struct v4l2_pix_format_mplane *mpix = &f->fmt.pix_mp; - - fmt = cio2_find_format(&mpix->pixelformat, NULL); - if (!fmt) - fmt = &formats[0]; - - /* Only supports up to 4224x3136 */ - if (mpix->width > CIO2_IMAGE_MAX_WIDTH) - mpix->width = CIO2_IMAGE_MAX_WIDTH; - if (mpix->height > CIO2_IMAGE_MAX_HEIGHT) - mpix->height = CIO2_IMAGE_MAX_HEIGHT; - - mpix->num_planes = 1; - mpix->pixelformat = fmt->fourcc; - mpix->colorspace = V4L2_COLORSPACE_RAW; - mpix->field = V4L2_FIELD_NONE; - memset(mpix->reserved, 0, sizeof(mpix->reserved)); - mpix->plane_fmt[0].bytesperline = cio2_bytesperline(mpix->width); - mpix->plane_fmt[0].sizeimage = mpix->plane_fmt[0].bytesperline * - mpix->height; - memset(mpix->plane_fmt[0].reserved, 0, - sizeof(mpix->plane_fmt[0].reserved)); - - /* use default */ - mpix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - mpix->quantization = V4L2_QUANTIZATION_DEFAULT; - mpix->xfer_func = V4L2_XFER_FUNC_DEFAULT; - - return 0; -} - -static int cio2_v4l2_s_fmt(struct file *file, void *fh, struct v4l2_format *f) -{ - struct cio2_queue *q = file_to_cio2_queue(file); - - cio2_v4l2_try_fmt(file, fh, f); - q->format = f->fmt.pix_mp; - - return 0; -} - -static int -cio2_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) -{ - if (input->index > 0) - return -EINVAL; - - strscpy(input->name, "camera", sizeof(input->name)); - input->type = V4L2_INPUT_TYPE_CAMERA; - - return 0; -} - -static int -cio2_video_g_input(struct file *file, void *fh, unsigned int *input) -{ - *input = 0; - - return 0; -} - -static int -cio2_video_s_input(struct file *file, void *fh, unsigned int input) -{ - return input == 0 ? 0 : -EINVAL; -} - -static const struct v4l2_file_operations cio2_v4l2_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops cio2_v4l2_ioctl_ops = { - .vidioc_querycap = cio2_v4l2_querycap, - .vidioc_enum_fmt_vid_cap = cio2_v4l2_enum_fmt, - .vidioc_g_fmt_vid_cap_mplane = cio2_v4l2_g_fmt, - .vidioc_s_fmt_vid_cap_mplane = cio2_v4l2_s_fmt, - .vidioc_try_fmt_vid_cap_mplane = cio2_v4l2_try_fmt, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_enum_input = cio2_video_enum_input, - .vidioc_g_input = cio2_video_g_input, - .vidioc_s_input = cio2_video_s_input, -}; - -static int cio2_subdev_subscribe_event(struct v4l2_subdev *sd, - struct v4l2_fh *fh, - struct v4l2_event_subscription *sub) -{ - if (sub->type != V4L2_EVENT_FRAME_SYNC) - return -EINVAL; - - /* Line number. For now only zero accepted. */ - if (sub->id != 0) - return -EINVAL; - - return v4l2_event_subscribe(fh, sub, 0, NULL); -} - -static int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format; - const struct v4l2_mbus_framefmt fmt_default = { - .width = 1936, - .height = 1096, - .code = formats[0].mbus_code, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_RAW, - .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, - .quantization = V4L2_QUANTIZATION_DEFAULT, - .xfer_func = V4L2_XFER_FUNC_DEFAULT, - }; - - /* Initialize try_fmt */ - format = v4l2_subdev_get_try_format(sd, fh->pad, CIO2_PAD_SINK); - *format = fmt_default; - - /* same as sink */ - format = v4l2_subdev_get_try_format(sd, fh->pad, CIO2_PAD_SOURCE); - *format = fmt_default; - - return 0; -} - -/* - * cio2_subdev_get_fmt - Handle get format by pads subdev method - * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad config - * @fmt: pointer to v4l2 subdev format structure - * return -EINVAL or zero on success - */ -static int cio2_subdev_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev); - - mutex_lock(&q->subdev_lock); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); - else - fmt->format = q->subdev_fmt; - - mutex_unlock(&q->subdev_lock); - - return 0; -} - -/* - * cio2_subdev_set_fmt - Handle set format by pads subdev method - * @sd : pointer to v4l2 subdev structure - * @cfg: V4L2 subdev pad config - * @fmt: pointer to v4l2 subdev format structure - * return -EINVAL or zero on success - */ -static int cio2_subdev_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev); - struct v4l2_mbus_framefmt *mbus; - u32 mbus_code = fmt->format.code; - unsigned int i; - - /* - * Only allow setting sink pad format; - * source always propagates from sink - */ - if (fmt->pad == CIO2_PAD_SOURCE) - return cio2_subdev_get_fmt(sd, cfg, fmt); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - mbus = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); - else - mbus = &q->subdev_fmt; - - fmt->format.code = formats[0].mbus_code; - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].mbus_code == mbus_code) { - fmt->format.code = mbus_code; - break; - } - } - - fmt->format.width = min(fmt->format.width, CIO2_IMAGE_MAX_WIDTH); - fmt->format.height = min(fmt->format.height, CIO2_IMAGE_MAX_HEIGHT); - fmt->format.field = V4L2_FIELD_NONE; - - mutex_lock(&q->subdev_lock); - *mbus = fmt->format; - mutex_unlock(&q->subdev_lock); - - return 0; -} - -static int cio2_subdev_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index >= ARRAY_SIZE(formats)) - return -EINVAL; - - code->code = formats[code->index].mbus_code; - return 0; -} - -static int cio2_subdev_link_validate_get_format(struct media_pad *pad, - struct v4l2_subdev_format *fmt) -{ - if (is_media_entity_v4l2_subdev(pad->entity)) { - struct v4l2_subdev *sd = - media_entity_to_v4l2_subdev(pad->entity); - - fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; - fmt->pad = pad->index; - return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); - } - - return -EINVAL; -} - -static int cio2_video_link_validate(struct media_link *link) -{ - struct video_device *vd = container_of(link->sink->entity, - struct video_device, entity); - struct cio2_queue *q = container_of(vd, struct cio2_queue, vdev); - struct cio2_device *cio2 = video_get_drvdata(vd); - struct v4l2_subdev_format source_fmt; - int ret; - - if (!media_entity_remote_pad(link->sink->entity->pads)) { - dev_info(&cio2->pci_dev->dev, - "video node %s pad not connected\n", vd->name); - return -ENOTCONN; - } - - ret = cio2_subdev_link_validate_get_format(link->source, &source_fmt); - if (ret < 0) - return 0; - - if (source_fmt.format.width != q->format.width || - source_fmt.format.height != q->format.height) { - dev_err(&cio2->pci_dev->dev, - "Wrong width or height %ux%u (%ux%u expected)\n", - q->format.width, q->format.height, - source_fmt.format.width, source_fmt.format.height); - return -EINVAL; - } - - if (!cio2_find_format(&q->format.pixelformat, &source_fmt.format.code)) - return -EINVAL; - - return 0; -} - -static const struct v4l2_subdev_core_ops cio2_subdev_core_ops = { - .subscribe_event = cio2_subdev_subscribe_event, - .unsubscribe_event = v4l2_event_subdev_unsubscribe, -}; - -static const struct v4l2_subdev_internal_ops cio2_subdev_internal_ops = { - .open = cio2_subdev_open, -}; - -static const struct v4l2_subdev_pad_ops cio2_subdev_pad_ops = { - .link_validate = v4l2_subdev_link_validate_default, - .get_fmt = cio2_subdev_get_fmt, - .set_fmt = cio2_subdev_set_fmt, - .enum_mbus_code = cio2_subdev_enum_mbus_code, -}; - -static const struct v4l2_subdev_ops cio2_subdev_ops = { - .core = &cio2_subdev_core_ops, - .pad = &cio2_subdev_pad_ops, -}; - -/******* V4L2 sub-device asynchronous registration callbacks***********/ - -struct sensor_async_subdev { - struct v4l2_async_subdev asd; - struct csi2_bus_info csi2; -}; - -/* The .bound() notifier callback when a match is found */ -static int cio2_notifier_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct cio2_device *cio2 = container_of(notifier, - struct cio2_device, notifier); - struct sensor_async_subdev *s_asd = container_of(asd, - struct sensor_async_subdev, asd); - struct cio2_queue *q; - - if (cio2->queue[s_asd->csi2.port].sensor) - return -EBUSY; - - q = &cio2->queue[s_asd->csi2.port]; - - q->csi2 = s_asd->csi2; - q->sensor = sd; - q->csi_rx_base = cio2->base + CIO2_REG_PIPE_BASE(q->csi2.port); - - return 0; -} - -/* The .unbind callback */ -static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct cio2_device *cio2 = container_of(notifier, - struct cio2_device, notifier); - struct sensor_async_subdev *s_asd = container_of(asd, - struct sensor_async_subdev, asd); - - cio2->queue[s_asd->csi2.port].sensor = NULL; -} - -/* .complete() is called after all subdevices have been located */ -static int cio2_notifier_complete(struct v4l2_async_notifier *notifier) -{ - struct cio2_device *cio2 = container_of(notifier, struct cio2_device, - notifier); - struct sensor_async_subdev *s_asd; - struct v4l2_async_subdev *asd; - struct cio2_queue *q; - unsigned int pad; - int ret; - - list_for_each_entry(asd, &cio2->notifier.asd_list, asd_list) { - s_asd = container_of(asd, struct sensor_async_subdev, asd); - q = &cio2->queue[s_asd->csi2.port]; - - for (pad = 0; pad < q->sensor->entity.num_pads; pad++) - if (q->sensor->entity.pads[pad].flags & - MEDIA_PAD_FL_SOURCE) - break; - - if (pad == q->sensor->entity.num_pads) { - dev_err(&cio2->pci_dev->dev, - "failed to find src pad for %s\n", - q->sensor->name); - return -ENXIO; - } - - ret = media_create_pad_link( - &q->sensor->entity, pad, - &q->subdev.entity, CIO2_PAD_SINK, - 0); - if (ret) { - dev_err(&cio2->pci_dev->dev, - "failed to create link for %s\n", - q->sensor->name); - return ret; - } - } - - return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev); -} - -static const struct v4l2_async_notifier_operations cio2_async_ops = { - .bound = cio2_notifier_bound, - .unbind = cio2_notifier_unbind, - .complete = cio2_notifier_complete, -}; - -static int cio2_parse_firmware(struct cio2_device *cio2) -{ - unsigned int i; - int ret; - - for (i = 0; i < CIO2_NUM_PORTS; i++) { - struct v4l2_fwnode_endpoint vep = { - .bus_type = V4L2_MBUS_CSI2_DPHY - }; - struct sensor_async_subdev *s_asd = NULL; - struct fwnode_handle *ep; - - ep = fwnode_graph_get_endpoint_by_id( - dev_fwnode(&cio2->pci_dev->dev), i, 0, - FWNODE_GRAPH_ENDPOINT_NEXT); - - if (!ep) - continue; - - ret = v4l2_fwnode_endpoint_parse(ep, &vep); - if (ret) - goto err_parse; - - s_asd = kzalloc(sizeof(*s_asd), GFP_KERNEL); - if (!s_asd) { - ret = -ENOMEM; - goto err_parse; - } - - s_asd->csi2.port = vep.base.port; - s_asd->csi2.lanes = vep.bus.mipi_csi2.num_data_lanes; - - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &cio2->notifier, ep, &s_asd->asd); - if (ret) - goto err_parse; - - fwnode_handle_put(ep); - - continue; - -err_parse: - fwnode_handle_put(ep); - kfree(s_asd); - return ret; - } - - /* - * Proceed even without sensors connected to allow the device to - * suspend. - */ - cio2->notifier.ops = &cio2_async_ops; - ret = v4l2_async_notifier_register(&cio2->v4l2_dev, &cio2->notifier); - if (ret) - dev_err(&cio2->pci_dev->dev, - "failed to register async notifier : %d\n", ret); - - return ret; -} - -/**************** Queue initialization ****************/ -static const struct media_entity_operations cio2_media_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static const struct media_entity_operations cio2_video_entity_ops = { - .link_validate = cio2_video_link_validate, -}; - -static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q) -{ - static const u32 default_width = 1936; - static const u32 default_height = 1096; - const struct ipu3_cio2_fmt dflt_fmt = formats[0]; - - struct video_device *vdev = &q->vdev; - struct vb2_queue *vbq = &q->vbq; - struct v4l2_subdev *subdev = &q->subdev; - struct v4l2_mbus_framefmt *fmt; - int r; - - /* Initialize miscellaneous variables */ - mutex_init(&q->lock); - mutex_init(&q->subdev_lock); - - /* Initialize formats to default values */ - fmt = &q->subdev_fmt; - fmt->width = default_width; - fmt->height = default_height; - fmt->code = dflt_fmt.mbus_code; - fmt->field = V4L2_FIELD_NONE; - - q->format.width = default_width; - q->format.height = default_height; - q->format.pixelformat = dflt_fmt.fourcc; - q->format.colorspace = V4L2_COLORSPACE_RAW; - q->format.field = V4L2_FIELD_NONE; - q->format.num_planes = 1; - q->format.plane_fmt[0].bytesperline = - cio2_bytesperline(q->format.width); - q->format.plane_fmt[0].sizeimage = q->format.plane_fmt[0].bytesperline * - q->format.height; - - /* Initialize fbpt */ - r = cio2_fbpt_init(cio2, q); - if (r) - goto fail_fbpt; - - /* Initialize media entities */ - q->subdev_pads[CIO2_PAD_SINK].flags = MEDIA_PAD_FL_SINK | - MEDIA_PAD_FL_MUST_CONNECT; - q->subdev_pads[CIO2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - subdev->entity.ops = &cio2_media_ops; - subdev->internal_ops = &cio2_subdev_internal_ops; - r = media_entity_pads_init(&subdev->entity, CIO2_PADS, q->subdev_pads); - if (r) { - dev_err(&cio2->pci_dev->dev, - "failed initialize subdev media entity (%d)\n", r); - goto fail_subdev_media_entity; - } - - q->vdev_pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; - vdev->entity.ops = &cio2_video_entity_ops; - r = media_entity_pads_init(&vdev->entity, 1, &q->vdev_pad); - if (r) { - dev_err(&cio2->pci_dev->dev, - "failed initialize videodev media entity (%d)\n", r); - goto fail_vdev_media_entity; - } - - /* Initialize subdev */ - v4l2_subdev_init(subdev, &cio2_subdev_ops); - subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; - subdev->owner = THIS_MODULE; - snprintf(subdev->name, sizeof(subdev->name), - CIO2_ENTITY_NAME " %td", q - cio2->queue); - subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; - v4l2_set_subdevdata(subdev, cio2); - r = v4l2_device_register_subdev(&cio2->v4l2_dev, subdev); - if (r) { - dev_err(&cio2->pci_dev->dev, - "failed initialize subdev (%d)\n", r); - goto fail_subdev; - } - - /* Initialize vbq */ - vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - vbq->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF; - vbq->ops = &cio2_vb2_ops; - vbq->mem_ops = &vb2_dma_sg_memops; - vbq->buf_struct_size = sizeof(struct cio2_buffer); - vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vbq->min_buffers_needed = 1; - vbq->drv_priv = cio2; - vbq->lock = &q->lock; - r = vb2_queue_init(vbq); - if (r) { - dev_err(&cio2->pci_dev->dev, - "failed to initialize videobuf2 queue (%d)\n", r); - goto fail_subdev; - } - - /* Initialize vdev */ - snprintf(vdev->name, sizeof(vdev->name), - "%s %td", CIO2_NAME, q - cio2->queue); - vdev->release = video_device_release_empty; - vdev->fops = &cio2_v4l2_fops; - vdev->ioctl_ops = &cio2_v4l2_ioctl_ops; - vdev->lock = &cio2->lock; - vdev->v4l2_dev = &cio2->v4l2_dev; - vdev->queue = &q->vbq; - vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; - video_set_drvdata(vdev, cio2); - r = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (r) { - dev_err(&cio2->pci_dev->dev, - "failed to register video device (%d)\n", r); - goto fail_vdev; - } - - /* Create link from CIO2 subdev to output node */ - r = media_create_pad_link( - &subdev->entity, CIO2_PAD_SOURCE, &vdev->entity, 0, - MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); - if (r) - goto fail_link; - - return 0; - -fail_link: - vb2_video_unregister_device(&q->vdev); -fail_vdev: - v4l2_device_unregister_subdev(subdev); -fail_subdev: - media_entity_cleanup(&vdev->entity); -fail_vdev_media_entity: - media_entity_cleanup(&subdev->entity); -fail_subdev_media_entity: - cio2_fbpt_exit(q, &cio2->pci_dev->dev); -fail_fbpt: - mutex_destroy(&q->subdev_lock); - mutex_destroy(&q->lock); - - return r; -} - -static void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q) -{ - vb2_video_unregister_device(&q->vdev); - media_entity_cleanup(&q->vdev.entity); - v4l2_device_unregister_subdev(&q->subdev); - media_entity_cleanup(&q->subdev.entity); - cio2_fbpt_exit(q, &cio2->pci_dev->dev); - mutex_destroy(&q->subdev_lock); - mutex_destroy(&q->lock); -} - -static int cio2_queues_init(struct cio2_device *cio2) -{ - int i, r; - - for (i = 0; i < CIO2_QUEUES; i++) { - r = cio2_queue_init(cio2, &cio2->queue[i]); - if (r) - break; - } - - if (i == CIO2_QUEUES) - return 0; - - for (i--; i >= 0; i--) - cio2_queue_exit(cio2, &cio2->queue[i]); - - return r; -} - -static void cio2_queues_exit(struct cio2_device *cio2) -{ - unsigned int i; - - for (i = 0; i < CIO2_QUEUES; i++) - cio2_queue_exit(cio2, &cio2->queue[i]); -} - -/**************** PCI interface ****************/ - -static int cio2_pci_probe(struct pci_dev *pci_dev, - const struct pci_device_id *id) -{ - struct cio2_device *cio2; - int r; - - cio2 = devm_kzalloc(&pci_dev->dev, sizeof(*cio2), GFP_KERNEL); - if (!cio2) - return -ENOMEM; - cio2->pci_dev = pci_dev; - - r = pcim_enable_device(pci_dev); - if (r) { - dev_err(&pci_dev->dev, "failed to enable device (%d)\n", r); - return r; - } - - dev_info(&pci_dev->dev, "device 0x%x (rev: 0x%x)\n", - pci_dev->device, pci_dev->revision); - - r = pcim_iomap_regions(pci_dev, 1 << CIO2_PCI_BAR, pci_name(pci_dev)); - if (r) { - dev_err(&pci_dev->dev, "failed to remap I/O memory (%d)\n", r); - return -ENODEV; - } - - cio2->base = pcim_iomap_table(pci_dev)[CIO2_PCI_BAR]; - - pci_set_drvdata(pci_dev, cio2); - - pci_set_master(pci_dev); - - r = pci_set_dma_mask(pci_dev, CIO2_DMA_MASK); - if (r) { - dev_err(&pci_dev->dev, "failed to set DMA mask (%d)\n", r); - return -ENODEV; - } - - r = pci_enable_msi(pci_dev); - if (r) { - dev_err(&pci_dev->dev, "failed to enable MSI (%d)\n", r); - return r; - } - - r = cio2_fbpt_init_dummy(cio2); - if (r) - return r; - - mutex_init(&cio2->lock); - - cio2->media_dev.dev = &cio2->pci_dev->dev; - strscpy(cio2->media_dev.model, CIO2_DEVICE_NAME, - sizeof(cio2->media_dev.model)); - snprintf(cio2->media_dev.bus_info, sizeof(cio2->media_dev.bus_info), - "PCI:%s", pci_name(cio2->pci_dev)); - cio2->media_dev.hw_revision = 0; - - media_device_init(&cio2->media_dev); - r = media_device_register(&cio2->media_dev); - if (r < 0) - goto fail_mutex_destroy; - - cio2->v4l2_dev.mdev = &cio2->media_dev; - r = v4l2_device_register(&pci_dev->dev, &cio2->v4l2_dev); - if (r) { - dev_err(&pci_dev->dev, - "failed to register V4L2 device (%d)\n", r); - goto fail_media_device_unregister; - } - - r = cio2_queues_init(cio2); - if (r) - goto fail_v4l2_device_unregister; - - v4l2_async_notifier_init(&cio2->notifier); - - /* Register notifier for subdevices we care */ - r = cio2_parse_firmware(cio2); - if (r) - goto fail_clean_notifier; - - r = devm_request_irq(&pci_dev->dev, pci_dev->irq, cio2_irq, - IRQF_SHARED, CIO2_NAME, cio2); - if (r) { - dev_err(&pci_dev->dev, "failed to request IRQ (%d)\n", r); - goto fail_clean_notifier; - } - - pm_runtime_put_noidle(&pci_dev->dev); - pm_runtime_allow(&pci_dev->dev); - - return 0; - -fail_clean_notifier: - v4l2_async_notifier_unregister(&cio2->notifier); - v4l2_async_notifier_cleanup(&cio2->notifier); - cio2_queues_exit(cio2); -fail_v4l2_device_unregister: - v4l2_device_unregister(&cio2->v4l2_dev); -fail_media_device_unregister: - media_device_unregister(&cio2->media_dev); - media_device_cleanup(&cio2->media_dev); -fail_mutex_destroy: - mutex_destroy(&cio2->lock); - cio2_fbpt_exit_dummy(cio2); - - return r; -} - -static void cio2_pci_remove(struct pci_dev *pci_dev) -{ - struct cio2_device *cio2 = pci_get_drvdata(pci_dev); - - media_device_unregister(&cio2->media_dev); - v4l2_async_notifier_unregister(&cio2->notifier); - v4l2_async_notifier_cleanup(&cio2->notifier); - cio2_queues_exit(cio2); - cio2_fbpt_exit_dummy(cio2); - v4l2_device_unregister(&cio2->v4l2_dev); - media_device_cleanup(&cio2->media_dev); - mutex_destroy(&cio2->lock); -} - -static int __maybe_unused cio2_runtime_suspend(struct device *dev) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - struct cio2_device *cio2 = pci_get_drvdata(pci_dev); - void __iomem *const base = cio2->base; - u16 pm; - - writel(CIO2_D0I3C_I3, base + CIO2_REG_D0I3C); - dev_dbg(dev, "cio2 runtime suspend.\n"); - - pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm); - pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT; - pm |= CIO2_PMCSR_D3; - pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm); - - return 0; -} - -static int __maybe_unused cio2_runtime_resume(struct device *dev) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - struct cio2_device *cio2 = pci_get_drvdata(pci_dev); - void __iomem *const base = cio2->base; - u16 pm; - - writel(CIO2_D0I3C_RR, base + CIO2_REG_D0I3C); - dev_dbg(dev, "cio2 runtime resume.\n"); - - pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm); - pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT; - pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm); - - return 0; -} - -/* - * Helper function to advance all the elements of a circular buffer by "start" - * positions - */ -static void arrange(void *ptr, size_t elem_size, size_t elems, size_t start) -{ - struct { - size_t begin, end; - } arr[2] = { - { 0, start - 1 }, - { start, elems - 1 }, - }; - -#define CHUNK_SIZE(a) ((a)->end - (a)->begin + 1) - - /* Loop as long as we have out-of-place entries */ - while (CHUNK_SIZE(&arr[0]) && CHUNK_SIZE(&arr[1])) { - size_t size0, i; - - /* - * Find the number of entries that can be arranged on this - * iteration. - */ - size0 = min(CHUNK_SIZE(&arr[0]), CHUNK_SIZE(&arr[1])); - - /* Swap the entries in two parts of the array. */ - for (i = 0; i < size0; i++) { - u8 *d = ptr + elem_size * (arr[1].begin + i); - u8 *s = ptr + elem_size * (arr[0].begin + i); - size_t j; - - for (j = 0; j < elem_size; j++) - swap(d[j], s[j]); - } - - if (CHUNK_SIZE(&arr[0]) > CHUNK_SIZE(&arr[1])) { - /* The end of the first array remains unarranged. */ - arr[0].begin += size0; - } else { - /* - * The first array is fully arranged so we proceed - * handling the next one. - */ - arr[0].begin = arr[1].begin; - arr[0].end = arr[1].begin + size0 - 1; - arr[1].begin += size0; - } - } -} - -static void cio2_fbpt_rearrange(struct cio2_device *cio2, struct cio2_queue *q) -{ - unsigned int i, j; - - for (i = 0, j = q->bufs_first; i < CIO2_MAX_BUFFERS; - i++, j = (j + 1) % CIO2_MAX_BUFFERS) - if (q->bufs[j]) - break; - - if (i == CIO2_MAX_BUFFERS) - return; - - if (j) { - arrange(q->fbpt, sizeof(struct cio2_fbpt_entry) * CIO2_MAX_LOPS, - CIO2_MAX_BUFFERS, j); - arrange(q->bufs, sizeof(struct cio2_buffer *), - CIO2_MAX_BUFFERS, j); - } - - /* - * DMA clears the valid bit when accessing the buffer. - * When stopping stream in suspend callback, some of the buffers - * may be in invalid state. After resume, when DMA meets the invalid - * buffer, it will halt and stop receiving new data. - * To avoid DMA halting, set the valid bit for all buffers in FBPT. - */ - for (i = 0; i < CIO2_MAX_BUFFERS; i++) - cio2_fbpt_entry_enable(cio2, q->fbpt + i * CIO2_MAX_LOPS); -} - -static int __maybe_unused cio2_suspend(struct device *dev) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - struct cio2_device *cio2 = pci_get_drvdata(pci_dev); - struct cio2_queue *q = cio2->cur_queue; - - dev_dbg(dev, "cio2 suspend\n"); - if (!cio2->streaming) - return 0; - - /* Stop stream */ - cio2_hw_exit(cio2, q); - synchronize_irq(pci_dev->irq); - - pm_runtime_force_suspend(dev); - - /* - * Upon resume, hw starts to process the fbpt entries from beginning, - * so relocate the queued buffs to the fbpt head before suspend. - */ - cio2_fbpt_rearrange(cio2, q); - q->bufs_first = 0; - q->bufs_next = 0; - - return 0; -} - -static int __maybe_unused cio2_resume(struct device *dev) -{ - struct cio2_device *cio2 = dev_get_drvdata(dev); - struct cio2_queue *q = cio2->cur_queue; - int r; - - dev_dbg(dev, "cio2 resume\n"); - if (!cio2->streaming) - return 0; - /* Start stream */ - r = pm_runtime_force_resume(&cio2->pci_dev->dev); - if (r < 0) { - dev_err(&cio2->pci_dev->dev, - "failed to set power %d\n", r); - return r; - } - - r = cio2_hw_init(cio2, q); - if (r) - dev_err(dev, "fail to init cio2 hw\n"); - - return r; -} - -static const struct dev_pm_ops cio2_pm_ops = { - SET_RUNTIME_PM_OPS(&cio2_runtime_suspend, &cio2_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(&cio2_suspend, &cio2_resume) -}; - -static const struct pci_device_id cio2_pci_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, CIO2_PCI_ID) }, - { } -}; - -MODULE_DEVICE_TABLE(pci, cio2_pci_id_table); - -static struct pci_driver cio2_pci_driver = { - .name = CIO2_NAME, - .id_table = cio2_pci_id_table, - .probe = cio2_pci_probe, - .remove = cio2_pci_remove, - .driver = { - .pm = &cio2_pm_ops, - }, -}; - -module_pci_driver(cio2_pci_driver); - -MODULE_AUTHOR("Tuukka Toivonen "); -MODULE_AUTHOR("Tianshu Qiu "); -MODULE_AUTHOR("Jian Xu Zheng"); -MODULE_AUTHOR("Yuning Pu "); -MODULE_AUTHOR("Yong Zhi "); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("IPU3 CIO2 driver"); -- cgit v1.2.3 From 06c85233121fabc69ffe6124953cd5304dbf3afd Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:35 +0100 Subject: media: v4l2-core: v4l2-async: Check sd->fwnode->secondary in match_fwnode() Where the fwnode graph is comprised of software_nodes, these will be assigned as the secondary to dev->fwnode. Check the v4l2_subdev's fwnode for a secondary and attempt to match against it during match_fwnode() to accommodate that possibility. Reviewed-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Scally Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index e3ab003a6c85..9dd896d085ec 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -87,6 +87,14 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, if (sd->fwnode == asd->match.fwnode) return true; + /* + * Check the same situation for any possible secondary assigned to the + * subdev's fwnode + */ + if (!IS_ERR_OR_NULL(sd->fwnode->secondary) && + sd->fwnode->secondary == asd->match.fwnode) + return true; + /* * Otherwise, check if the sd fwnode and the asd fwnode refer to an * endpoint or a device. If they're of the same type, there's no match. -- cgit v1.2.3 From bf263f64e804a74ace9bd37f49341d68a653e5e6 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:36 +0100 Subject: media: ACPI / bus: Add acpi_dev_get_next_match_dev() and helper macro To ensure we handle situations in which multiple sensors of the same model (and therefore _HID) are present in a system, we need to be able to iterate over devices matching a known _HID but unknown _UID and _HRV - add acpi_dev_get_next_match_dev() to accommodate that possibility and change acpi_dev_get_first_match_dev() to simply call the new function with a NULL starting point. Add an iterator macro for convenience. Reviewed-by: Andy Shevchenko Suggested-by: Andy Shevchenko Signed-off-by: Daniel Scally Acked-by: Rafael J. Wysocki Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/acpi/utils.c | 30 ++++++++++++++++++++++++++---- include/acpi/acpi_bus.h | 7 +++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index d5411a166685..ddca1550cce6 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -843,12 +843,13 @@ bool acpi_dev_present(const char *hid, const char *uid, s64 hrv) EXPORT_SYMBOL(acpi_dev_present); /** - * acpi_dev_get_first_match_dev - Return the first match of ACPI device + * acpi_dev_get_next_match_dev - Return the next match of ACPI device + * @adev: Pointer to the previous acpi_device matching this @hid, @uid and @hrv * @hid: Hardware ID of the device. * @uid: Unique ID of the device, pass NULL to not check _UID * @hrv: Hardware Revision of the device, pass -1 to not check _HRV * - * Return the first match of ACPI device if a matching device was present + * Return the next match of ACPI device if another matching device was present * at the moment of invocation, or NULL otherwise. * * The caller is responsible to call put_device() on the returned device. @@ -856,8 +857,9 @@ EXPORT_SYMBOL(acpi_dev_present); * See additional information in acpi_dev_present() as well. */ struct acpi_device * -acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv) +acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv) { + struct device *start = adev ? &adev->dev : NULL; struct acpi_dev_match_info match = {}; struct device *dev; @@ -865,9 +867,29 @@ acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv) match.uid = uid; match.hrv = hrv; - dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb); + dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb); return dev ? to_acpi_device(dev) : NULL; } +EXPORT_SYMBOL(acpi_dev_get_next_match_dev); + +/** + * acpi_dev_get_first_match_dev - Return the first match of ACPI device + * @hid: Hardware ID of the device. + * @uid: Unique ID of the device, pass NULL to not check _UID + * @hrv: Hardware Revision of the device, pass -1 to not check _HRV + * + * Return the first match of ACPI device if a matching device was present + * at the moment of invocation, or NULL otherwise. + * + * The caller is responsible to call put_device() on the returned device. + * + * See additional information in acpi_dev_present() as well. + */ +struct acpi_device * +acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv) +{ + return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv); +} EXPORT_SYMBOL(acpi_dev_get_first_match_dev); /* diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 6d1879bf9440..02a716a0af5d 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -683,9 +683,16 @@ static inline bool acpi_device_can_poweroff(struct acpi_device *adev) bool acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2); +struct acpi_device * +acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv); struct acpi_device * acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv); +#define for_each_acpi_dev_match(adev, hid, uid, hrv) \ + for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv); \ + adev; \ + adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv)) + static inline void acpi_dev_put(struct acpi_device *adev) { put_device(&adev->dev); -- cgit v1.2.3 From 0eeded3671dff9648a31d0573c9fe54bc0fd959f Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:37 +0100 Subject: media: v4l2-fwnode: Include v4l2_fwnode_bus_type V4L2 fwnode bus types are enumerated in v4l2-fwnode.c, meaning they aren't available to the rest of the kernel. Move the enum to the corresponding header so that I can use the label to refer to those values. Suggested-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Reviewed-by: Andy Shevchenko Signed-off-by: Daniel Scally Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 11 ----------- include/media/v4l2-fwnode.h | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 5353e37eb950..c1c2b3060532 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -28,17 +28,6 @@ #include #include -enum v4l2_fwnode_bus_type { - V4L2_FWNODE_BUS_TYPE_GUESS = 0, - V4L2_FWNODE_BUS_TYPE_CSI2_CPHY, - V4L2_FWNODE_BUS_TYPE_CSI1, - V4L2_FWNODE_BUS_TYPE_CCP2, - V4L2_FWNODE_BUS_TYPE_CSI2_DPHY, - V4L2_FWNODE_BUS_TYPE_PARALLEL, - V4L2_FWNODE_BUS_TYPE_BT656, - NR_OF_V4L2_FWNODE_BUS_TYPE, -}; - static const struct v4l2_fwnode_bus_conv { enum v4l2_fwnode_bus_type fwnode_bus_type; enum v4l2_mbus_type mbus_type; diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index a0b55d70b526..80d21ad8d603 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -213,6 +213,28 @@ struct v4l2_fwnode_connector { } connector; }; +/** + * enum v4l2_fwnode_bus_type - Video bus types defined by firmware properties + * @V4L2_FWNODE_BUS_TYPE_GUESS: Default value if no bus-type fwnode property + * @V4L2_FWNODE_BUS_TYPE_CSI2_CPHY: MIPI CSI-2 bus, C-PHY physical layer + * @V4L2_FWNODE_BUS_TYPE_CSI1: MIPI CSI-1 bus + * @V4L2_FWNODE_BUS_TYPE_CCP2: SMIA Compact Camera Port 2 bus + * @V4L2_FWNODE_BUS_TYPE_CSI2_DPHY: MIPI CSI-2 bus, D-PHY physical layer + * @V4L2_FWNODE_BUS_TYPE_PARALLEL: Camera Parallel Interface bus + * @V4L2_FWNODE_BUS_TYPE_BT656: BT.656 video format bus-type + * @NR_OF_V4L2_FWNODE_BUS_TYPE: Number of bus-types + */ +enum v4l2_fwnode_bus_type { + V4L2_FWNODE_BUS_TYPE_GUESS = 0, + V4L2_FWNODE_BUS_TYPE_CSI2_CPHY, + V4L2_FWNODE_BUS_TYPE_CSI1, + V4L2_FWNODE_BUS_TYPE_CCP2, + V4L2_FWNODE_BUS_TYPE_CSI2_DPHY, + V4L2_FWNODE_BUS_TYPE_PARALLEL, + V4L2_FWNODE_BUS_TYPE_BT656, + NR_OF_V4L2_FWNODE_BUS_TYPE +}; + /** * v4l2_fwnode_endpoint_parse() - parse all fwnode node properties * @fwnode: pointer to the endpoint's fwnode handle -- cgit v1.2.3 From 803abec64ef9d31ba068088e90fc20556ab5f605 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 7 Jan 2021 14:28:38 +0100 Subject: media: ipu3-cio2: Add cio2-bridge to ipu3-cio2 driver Currently on platforms designed for Windows, connections between CIO2 and sensors are not properly defined in DSDT. This patch extends the ipu3-cio2 driver to compensate by building software_node connections, parsing the connection properties from the sensor's SSDB buffer. [Sakari Ailus: Make cio2_bridge_init static inline to a fix compiler warning, wrapped a bunch of long lines.] Suggested-by: Jordan Hand Reviewed-by: Laurent Pinchart Reviewed-by: Andy Shevchenko Reviewed-by: Kieran Bingham Signed-off-by: Daniel Scally Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + drivers/media/pci/intel/ipu3/Kconfig | 18 ++ drivers/media/pci/intel/ipu3/Makefile | 1 + drivers/media/pci/intel/ipu3/cio2-bridge.c | 314 ++++++++++++++++++++++++++ drivers/media/pci/intel/ipu3/cio2-bridge.h | 125 ++++++++++ drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 34 +++ drivers/media/pci/intel/ipu3/ipu3-cio2.h | 6 + 7 files changed, 499 insertions(+) create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.c create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.h diff --git a/MAINTAINERS b/MAINTAINERS index e6d8674dc90a..138aaafbb50d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9014,6 +9014,7 @@ INTEL IPU3 CSI-2 CIO2 DRIVER M: Yong Zhi M: Sakari Ailus M: Bingbu Cao +M: Dan Scally R: Tianshu Qiu L: linux-media@vger.kernel.org S: Maintained diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig index 7a805201034b..24f4e79fe0cb 100644 --- a/drivers/media/pci/intel/ipu3/Kconfig +++ b/drivers/media/pci/intel/ipu3/Kconfig @@ -17,3 +17,21 @@ config VIDEO_IPU3_CIO2 Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2 connected camera. The module will be called ipu3-cio2. + +config CIO2_BRIDGE + bool "IPU3 CIO2 Sensors Bridge" + depends on VIDEO_IPU3_CIO2 + help + This extension provides an API for the ipu3-cio2 driver to create + connections to cameras that are hidden in the SSDB buffer in ACPI. + It can be used to enable support for cameras in detachable / hybrid + devices that ship with Windows. + + Say Y here if your device is a detachable / hybrid laptop that comes + with Windows installed by the OEM, for example: + + - Microsoft Surface models (except Surface Pro 3) + - The Lenovo Miix line (for example the 510, 520, 710 and 720) + - Dell 7285 + + If in doubt, say N here. diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile index 429d516452e4..933777e6ea8a 100644 --- a/drivers/media/pci/intel/ipu3/Makefile +++ b/drivers/media/pci/intel/ipu3/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o ipu3-cio2-y += ipu3-cio2-main.o +ipu3-cio2-$(CONFIG_CIO2_BRIDGE) += cio2-bridge.o diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c new file mode 100644 index 000000000000..c2199042d3db --- /dev/null +++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Author: Dan Scally */ + +#include +#include +#include +#include +#include + +#include "cio2-bridge.h" + +/* + * Extend this array with ACPI Hardware IDs of devices known to be working + * plus the number of link-frequencies expected by their drivers, along with + * the frequency values in hertz. This is somewhat opportunistic way of adding + * support for this for now in the hopes of a better source for the information + * (possibly some encoded value in the SSDB buffer that we're unaware of) + * becoming apparent in the future. + * + * Do not add an entry for a sensor that is not actually supported. + */ +static const struct cio2_sensor_config cio2_supported_sensors[] = { + /* Omnivision OV5693 */ + CIO2_SENSOR_CONFIG("INT33BE", 0), + /* Omnivision OV2680 */ + CIO2_SENSOR_CONFIG("OVTI2680", 0), +}; + +static const struct cio2_property_names prop_names = { + .clock_frequency = "clock-frequency", + .rotation = "rotation", + .bus_type = "bus-type", + .data_lanes = "data-lanes", + .remote_endpoint = "remote-endpoint", + .link_frequencies = "link-frequencies", +}; + +static int cio2_bridge_read_acpi_buffer(struct acpi_device *adev, char *id, + void *data, u32 size) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + int ret = 0; + + status = acpi_evaluate_object(adev->handle, id, NULL, &buffer); + if (ACPI_FAILURE(status)) + return -ENODEV; + + obj = buffer.pointer; + if (!obj) { + dev_err(&adev->dev, "Couldn't locate ACPI buffer\n"); + return -ENODEV; + } + + if (obj->type != ACPI_TYPE_BUFFER) { + dev_err(&adev->dev, "Not an ACPI buffer\n"); + ret = -ENODEV; + goto out_free_buff; + } + + if (obj->buffer.length > size) { + dev_err(&adev->dev, "Given buffer is too small\n"); + ret = -EINVAL; + goto out_free_buff; + } + + memcpy(data, obj->buffer.pointer, obj->buffer.length); + +out_free_buff: + kfree(buffer.pointer); + return ret; +} + +static void cio2_bridge_create_fwnode_properties( + struct cio2_sensor *sensor, + struct cio2_bridge *bridge, + const struct cio2_sensor_config *cfg) +{ + sensor->prop_names = prop_names; + + sensor->local_ref[0].node = &sensor->swnodes[SWNODE_CIO2_ENDPOINT]; + sensor->remote_ref[0].node = &sensor->swnodes[SWNODE_SENSOR_ENDPOINT]; + + sensor->dev_properties[0] = PROPERTY_ENTRY_U32( + sensor->prop_names.clock_frequency, + sensor->ssdb.mclkspeed); + sensor->dev_properties[1] = PROPERTY_ENTRY_U8( + sensor->prop_names.rotation, + sensor->ssdb.degree); + + sensor->ep_properties[0] = PROPERTY_ENTRY_U32( + sensor->prop_names.bus_type, + V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); + sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( + sensor->prop_names.data_lanes, + bridge->data_lanes, + sensor->ssdb.lanes); + sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY( + sensor->prop_names.remote_endpoint, + sensor->local_ref); + + if (cfg->nr_link_freqs > 0) + sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN( + sensor->prop_names.link_frequencies, + cfg->link_freqs, + cfg->nr_link_freqs); + + sensor->cio2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN( + sensor->prop_names.data_lanes, + bridge->data_lanes, + sensor->ssdb.lanes); + sensor->cio2_properties[1] = PROPERTY_ENTRY_REF_ARRAY( + sensor->prop_names.remote_endpoint, + sensor->remote_ref); +} + +static void cio2_bridge_init_swnode_names(struct cio2_sensor *sensor) +{ + snprintf(sensor->node_names.remote_port, + sizeof(sensor->node_names.remote_port), + SWNODE_GRAPH_PORT_NAME_FMT, sensor->ssdb.link); + snprintf(sensor->node_names.port, + sizeof(sensor->node_names.port), + SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */ + snprintf(sensor->node_names.endpoint, + sizeof(sensor->node_names.endpoint), + SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */ +} + +static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge, + struct cio2_sensor *sensor) +{ + struct software_node *nodes = sensor->swnodes; + + cio2_bridge_init_swnode_names(sensor); + + nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name, + sensor->dev_properties); + nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port, + &nodes[SWNODE_SENSOR_HID]); + nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT( + sensor->node_names.endpoint, + &nodes[SWNODE_SENSOR_PORT], + sensor->ep_properties); + nodes[SWNODE_CIO2_PORT] = NODE_PORT(sensor->node_names.remote_port, + &bridge->cio2_hid_node); + nodes[SWNODE_CIO2_ENDPOINT] = NODE_ENDPOINT( + sensor->node_names.endpoint, + &nodes[SWNODE_CIO2_PORT], + sensor->cio2_properties); +} + +static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge) +{ + struct cio2_sensor *sensor; + unsigned int i; + + for (i = 0; i < bridge->n_sensors; i++) { + sensor = &bridge->sensors[i]; + software_node_unregister_nodes(sensor->swnodes); + acpi_dev_put(sensor->adev); + } +} + +static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg, + struct cio2_bridge *bridge, + struct pci_dev *cio2) +{ + struct fwnode_handle *fwnode; + struct cio2_sensor *sensor; + struct acpi_device *adev; + int ret; + + for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { + if (!adev->status.enabled) + continue; + + if (bridge->n_sensors >= CIO2_NUM_PORTS) { + dev_err(&cio2->dev, "Exceeded available CIO2 ports\n"); + cio2_bridge_unregister_sensors(bridge); + ret = -EINVAL; + goto err_out; + } + + sensor = &bridge->sensors[bridge->n_sensors]; + sensor->adev = adev; + strscpy(sensor->name, cfg->hid, sizeof(sensor->name)); + + ret = cio2_bridge_read_acpi_buffer(adev, "SSDB", + &sensor->ssdb, + sizeof(sensor->ssdb)); + if (ret) + goto err_put_adev; + + if (sensor->ssdb.lanes > CIO2_MAX_LANES) { + dev_err(&adev->dev, + "Number of lanes in SSDB is invalid\n"); + ret = -EINVAL; + goto err_put_adev; + } + + cio2_bridge_create_fwnode_properties(sensor, bridge, cfg); + cio2_bridge_create_connection_swnodes(bridge, sensor); + + ret = software_node_register_nodes(sensor->swnodes); + if (ret) + goto err_put_adev; + + fwnode = software_node_fwnode(&sensor->swnodes[ + SWNODE_SENSOR_HID]); + if (!fwnode) { + ret = -ENODEV; + goto err_free_swnodes; + } + + adev->fwnode.secondary = fwnode; + + dev_info(&cio2->dev, "Found supported sensor %s\n", + acpi_dev_name(adev)); + + bridge->n_sensors++; + } + + return 0; + +err_free_swnodes: + software_node_unregister_nodes(sensor->swnodes); +err_put_adev: + acpi_dev_put(sensor->adev); +err_out: + return ret; +} + +static int cio2_bridge_connect_sensors(struct cio2_bridge *bridge, + struct pci_dev *cio2) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(cio2_supported_sensors); i++) { + const struct cio2_sensor_config *cfg = + &cio2_supported_sensors[i]; + + ret = cio2_bridge_connect_sensor(cfg, bridge, cio2); + if (ret) + goto err_unregister_sensors; + } + + return 0; + +err_unregister_sensors: + cio2_bridge_unregister_sensors(bridge); + return ret; +} + +int cio2_bridge_init(struct pci_dev *cio2) +{ + struct device *dev = &cio2->dev; + struct fwnode_handle *fwnode; + struct cio2_bridge *bridge; + unsigned int i; + int ret; + + bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return -ENOMEM; + + strscpy(bridge->cio2_node_name, CIO2_HID, + sizeof(bridge->cio2_node_name)); + bridge->cio2_hid_node.name = bridge->cio2_node_name; + + ret = software_node_register(&bridge->cio2_hid_node); + if (ret < 0) { + dev_err(dev, "Failed to register the CIO2 HID node\n"); + goto err_free_bridge; + } + + /* + * Map the lane arrangement, which is fixed for the IPU3 (meaning we + * only need one, rather than one per sensor). We include it as a + * member of the struct cio2_bridge rather than a global variable so + * that it survives if the module is unloaded along with the rest of + * the struct. + */ + for (i = 0; i < CIO2_MAX_LANES; i++) + bridge->data_lanes[i] = i + 1; + + ret = cio2_bridge_connect_sensors(bridge, cio2); + if (ret || bridge->n_sensors == 0) + goto err_unregister_cio2; + + dev_info(dev, "Connected %d cameras\n", bridge->n_sensors); + + fwnode = software_node_fwnode(&bridge->cio2_hid_node); + if (!fwnode) { + dev_err(dev, "Error getting fwnode from cio2 software_node\n"); + ret = -ENODEV; + goto err_unregister_sensors; + } + + set_secondary_fwnode(dev, fwnode); + + return 0; + +err_unregister_sensors: + cio2_bridge_unregister_sensors(bridge); +err_unregister_cio2: + software_node_unregister(&bridge->cio2_hid_node); +err_free_bridge: + kfree(bridge); + + return ret; +} diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.h b/drivers/media/pci/intel/ipu3/cio2-bridge.h new file mode 100644 index 000000000000..dd0ffcafa489 --- /dev/null +++ b/drivers/media/pci/intel/ipu3/cio2-bridge.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Author: Dan Scally */ +#ifndef __CIO2_BRIDGE_H +#define __CIO2_BRIDGE_H + +#include +#include + +#include "ipu3-cio2.h" + +#define CIO2_HID "INT343E" +#define CIO2_MAX_LANES 4 +#define MAX_NUM_LINK_FREQS 3 + +#define CIO2_SENSOR_CONFIG(_HID, _NR, ...) \ + (const struct cio2_sensor_config) { \ + .hid = _HID, \ + .nr_link_freqs = _NR, \ + .link_freqs = { __VA_ARGS__ } \ + } + +#define NODE_SENSOR(_HID, _PROPS) \ + (const struct software_node) { \ + .name = _HID, \ + .properties = _PROPS, \ + } + +#define NODE_PORT(_PORT, _SENSOR_NODE) \ + (const struct software_node) { \ + .name = _PORT, \ + .parent = _SENSOR_NODE, \ + } + +#define NODE_ENDPOINT(_EP, _PORT, _PROPS) \ + (const struct software_node) { \ + .name = _EP, \ + .parent = _PORT, \ + .properties = _PROPS, \ + } + +enum cio2_sensor_swnodes { + SWNODE_SENSOR_HID, + SWNODE_SENSOR_PORT, + SWNODE_SENSOR_ENDPOINT, + SWNODE_CIO2_PORT, + SWNODE_CIO2_ENDPOINT, + SWNODE_COUNT +}; + +/* Data representation as it is in ACPI SSDB buffer */ +struct cio2_sensor_ssdb { + u8 version; + u8 sku; + u8 guid_csi2[16]; + u8 devfunction; + u8 bus; + u32 dphylinkenfuses; + u32 clockdiv; + u8 link; + u8 lanes; + u32 csiparams[10]; + u32 maxlanespeed; + u8 sensorcalibfileidx; + u8 sensorcalibfileidxInMBZ[3]; + u8 romtype; + u8 vcmtype; + u8 platforminfo; + u8 platformsubinfo; + u8 flash; + u8 privacyled; + u8 degree; + u8 mipilinkdefined; + u32 mclkspeed; + u8 controllogicid; + u8 reserved1[3]; + u8 mclkport; + u8 reserved2[13]; +} __packed; + +struct cio2_property_names { + char clock_frequency[16]; + char rotation[9]; + char bus_type[9]; + char data_lanes[11]; + char remote_endpoint[16]; + char link_frequencies[17]; +}; + +struct cio2_node_names { + char port[7]; + char endpoint[11]; + char remote_port[7]; +}; + +struct cio2_sensor_config { + const char *hid; + const u8 nr_link_freqs; + const u64 link_freqs[MAX_NUM_LINK_FREQS]; +}; + +struct cio2_sensor { + char name[ACPI_ID_LEN]; + struct acpi_device *adev; + + struct software_node swnodes[6]; + struct cio2_node_names node_names; + + struct cio2_sensor_ssdb ssdb; + struct cio2_property_names prop_names; + struct property_entry ep_properties[5]; + struct property_entry dev_properties[3]; + struct property_entry cio2_properties[3]; + struct software_node_ref_args local_ref[1]; + struct software_node_ref_args remote_ref[1]; +}; + +struct cio2_bridge { + char cio2_node_name[ACPI_ID_LEN]; + struct software_node cio2_hid_node; + u32 data_lanes[4]; + unsigned int n_sensors; + struct cio2_sensor sensors[CIO2_NUM_PORTS]; +}; + +#endif diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c index e8ea69d30bfd..8ab682b38f17 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c @@ -1702,11 +1702,28 @@ static void cio2_queues_exit(struct cio2_device *cio2) cio2_queue_exit(cio2, &cio2->queue[i]); } +static int cio2_check_fwnode_graph(struct fwnode_handle *fwnode) +{ + struct fwnode_handle *endpoint; + + if (IS_ERR_OR_NULL(fwnode)) + return -EINVAL; + + endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (endpoint) { + fwnode_handle_put(endpoint); + return 0; + } + + return cio2_check_fwnode_graph(fwnode->secondary); +} + /**************** PCI interface ****************/ static int cio2_pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { + struct fwnode_handle *fwnode = dev_fwnode(&pci_dev->dev); struct cio2_device *cio2; int r; @@ -1715,6 +1732,23 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, return -ENOMEM; cio2->pci_dev = pci_dev; + /* + * On some platforms no connections to sensors are defined in firmware, + * if the device has no endpoints then we can try to build those as + * software_nodes parsed from SSDB. + */ + r = cio2_check_fwnode_graph(fwnode); + if (r) { + if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) { + dev_err(&pci_dev->dev, "fwnode graph has no endpoints connected\n"); + return -EINVAL; + } + + r = cio2_bridge_init(pci_dev); + if (r) + return r; + } + r = pcim_enable_device(pci_dev); if (r) { dev_err(&pci_dev->dev, "failed to enable device (%d)\n", r); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h index 62187ab5ae43..3806d7f04d69 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h @@ -455,4 +455,10 @@ static inline struct cio2_queue *vb2q_to_cio2_queue(struct vb2_queue *vq) return container_of(vq, struct cio2_queue, vbq); } +#if IS_ENABLED(CONFIG_CIO2_BRIDGE) +int cio2_bridge_init(struct pci_dev *cio2); +#else +static inline int cio2_bridge_init(struct pci_dev *cio2) { return 0; } +#endif + #endif -- cgit v1.2.3 From 586bb700b3256a1cb38935f83c76e6522ac53d64 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 18 Jan 2021 07:00:45 +0100 Subject: media: sunxi-cir: Clean up dead register writes The register writes during driver removal occur after the device is already put back in reset, so they never had any effect. Acked-by: Maxime Ripard Signed-off-by: Samuel Holland Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/sunxi-cir.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 8555c7798706..0a7f7eab3cc3 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -342,22 +342,12 @@ exit_reset_assert: static int sunxi_ir_remove(struct platform_device *pdev) { - unsigned long flags; struct sunxi_ir *ir = platform_get_drvdata(pdev); clk_disable_unprepare(ir->clk); clk_disable_unprepare(ir->apb_clk); reset_control_assert(ir->rst); - spin_lock_irqsave(&ir->ir_lock, flags); - /* disable IR IRQ */ - writel(0, ir->base + SUNXI_IR_RXINT_REG); - /* clear All Rx Interrupt Status */ - writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); - /* disable IR */ - writel(0, ir->base + SUNXI_IR_CTL_REG); - spin_unlock_irqrestore(&ir->ir_lock, flags); - rc_unregister_device(ir->rc); return 0; } -- cgit v1.2.3 From d1036eb43f71b5f8051e42ea1504dd5fd303025d Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 18 Jan 2021 07:00:46 +0100 Subject: media: sunxi-cir: Remove unnecessary spinlock Only one register, SUNXI_IR_CIR_REG, is accessed from outside the interrupt handler, and that register is not accessed from inside it. As there is no overlap between different contexts, no lock is needed. Acked-by: Maxime Ripard Signed-off-by: Samuel Holland Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/sunxi-cir.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 0a7f7eab3cc3..48be400421cd 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -86,7 +86,6 @@ struct sunxi_ir_quirks { }; struct sunxi_ir { - spinlock_t ir_lock; struct rc_dev *rc; void __iomem *base; int irq; @@ -105,8 +104,6 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) struct sunxi_ir *ir = dev_id; struct ir_raw_event rawir = {}; - spin_lock(&ir->ir_lock); - status = readl(ir->base + SUNXI_IR_RXSTA_REG); /* clean all pending statuses */ @@ -137,8 +134,6 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) ir_raw_event_handle(ir->rc); } - spin_unlock(&ir->ir_lock); - return IRQ_HANDLED; } @@ -160,17 +155,14 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) { struct sunxi_ir *ir = rc_dev->priv; unsigned int base_clk = clk_get_rate(ir->clk); - unsigned long flags; unsigned int ithr = sunxi_usec_to_ithr(base_clk, timeout); dev_dbg(rc_dev->dev.parent, "setting idle threshold to %u\n", ithr); - spin_lock_irqsave(&ir->ir_lock, flags); /* Set noise threshold and idle threshold */ writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), ir->base + SUNXI_IR_CIR_REG); - spin_unlock_irqrestore(&ir->ir_lock, flags); rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ithr); @@ -199,8 +191,6 @@ static int sunxi_ir_probe(struct platform_device *pdev) return -ENODEV; } - spin_lock_init(&ir->ir_lock); - ir->fifo_size = quirks->fifo_size; /* Clock */ -- cgit v1.2.3 From 8f9061fa773c726b4311d65954284b6dfce44485 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 18 Jan 2021 07:00:47 +0100 Subject: media: sunxi-cir: Factor out hardware initialization In preparation for adding suspend/resume hooks, factor out the hardware initialization from the driver probe/remove functions. The timeout programmed during init is taken from the `struct rc_dev` so it is maintained across an exit/init cycle. This resolves some trivial issues with the probe function: throwing away the error from clk_prepare_enable and using the wrong type for the temporary register value. It also fixes the order of the remove function to unregister the RC device before turning off the hardware. This prevents userspace from triggering register writes (via LIRC_SET_REC_TIMEOUT) while the hardware is disabled. Acked-by: Maxime Ripard Signed-off-by: Samuel Holland Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/sunxi-cir.c | 128 +++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 54 deletions(-) diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 48be400421cd..2d099da8d3cc 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -169,10 +169,74 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) return 0; } +static int sunxi_ir_hw_init(struct device *dev) +{ + struct sunxi_ir *ir = dev_get_drvdata(dev); + u32 tmp; + int ret; + + ret = reset_control_deassert(ir->rst); + if (ret) + return ret; + + ret = clk_prepare_enable(ir->apb_clk); + if (ret) { + dev_err(dev, "failed to enable apb clk\n"); + goto exit_assert_reset; + } + + ret = clk_prepare_enable(ir->clk); + if (ret) { + dev_err(dev, "failed to enable ir clk\n"); + goto exit_disable_apb_clk; + } + + /* Enable CIR Mode */ + writel(REG_CTL_MD, ir->base + SUNXI_IR_CTL_REG); + + /* Set noise threshold and idle threshold */ + sunxi_ir_set_timeout(ir->rc, ir->rc->timeout); + + /* Invert Input Signal */ + writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG); + + /* Clear All Rx Interrupt Status */ + writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); + + /* + * Enable IRQ on overflow, packet end, FIFO available with trigger + * level + */ + writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN | + REG_RXINT_RAI_EN | REG_RXINT_RAL(ir->fifo_size / 2 - 1), + ir->base + SUNXI_IR_RXINT_REG); + + /* Enable IR Module */ + tmp = readl(ir->base + SUNXI_IR_CTL_REG); + writel(tmp | REG_CTL_GEN | REG_CTL_RXEN, ir->base + SUNXI_IR_CTL_REG); + + return 0; + +exit_disable_apb_clk: + clk_disable_unprepare(ir->apb_clk); +exit_assert_reset: + reset_control_assert(ir->rst); + + return ret; +} + +static void sunxi_ir_hw_exit(struct device *dev) +{ + struct sunxi_ir *ir = dev_get_drvdata(dev); + + clk_disable_unprepare(ir->clk); + clk_disable_unprepare(ir->apb_clk); + reset_control_assert(ir->rst); +} + static int sunxi_ir_probe(struct platform_device *pdev) { int ret = 0; - unsigned long tmp = 0; struct device *dev = &pdev->dev; struct device_node *dn = dev->of_node; @@ -213,43 +277,26 @@ static int sunxi_ir_probe(struct platform_device *pdev) ir->rst = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(ir->rst)) return PTR_ERR(ir->rst); - ret = reset_control_deassert(ir->rst); - if (ret) - return ret; } ret = clk_set_rate(ir->clk, b_clk_freq); if (ret) { dev_err(dev, "set ir base clock failed!\n"); - goto exit_reset_assert; + return ret; } dev_dbg(dev, "set base clock frequency to %d Hz.\n", b_clk_freq); - if (clk_prepare_enable(ir->apb_clk)) { - dev_err(dev, "try to enable apb_ir_clk failed\n"); - ret = -EINVAL; - goto exit_reset_assert; - } - - if (clk_prepare_enable(ir->clk)) { - dev_err(dev, "try to enable ir_clk failed\n"); - ret = -EINVAL; - goto exit_clkdisable_apb_clk; - } - /* IO */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ir->base = devm_ioremap_resource(dev, res); if (IS_ERR(ir->base)) { - ret = PTR_ERR(ir->base); - goto exit_clkdisable_clk; + return PTR_ERR(ir->base); } ir->rc = rc_allocate_device(RC_DRIVER_IR_RAW); if (!ir->rc) { dev_err(dev, "failed to allocate device\n"); - ret = -ENOMEM; - goto exit_clkdisable_clk; + return -ENOMEM; } ir->rc->priv = ir; @@ -265,6 +312,7 @@ static int sunxi_ir_probe(struct platform_device *pdev) ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; /* Frequency after IR internal divider with sample period in us */ ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / 64)); + ir->rc->timeout = IR_DEFAULT_TIMEOUT; ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, 0); ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, 255); ir->rc->s_timeout = sunxi_ir_set_timeout; @@ -291,41 +339,15 @@ static int sunxi_ir_probe(struct platform_device *pdev) goto exit_free_dev; } - /* Enable CIR Mode */ - writel(REG_CTL_MD, ir->base+SUNXI_IR_CTL_REG); - - /* Set noise threshold and idle threshold */ - sunxi_ir_set_timeout(ir->rc, IR_DEFAULT_TIMEOUT); - - /* Invert Input Signal */ - writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG); - - /* Clear All Rx Interrupt Status */ - writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); - - /* - * Enable IRQ on overflow, packet end, FIFO available with trigger - * level - */ - writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN | - REG_RXINT_RAI_EN | REG_RXINT_RAL(ir->fifo_size / 2 - 1), - ir->base + SUNXI_IR_RXINT_REG); - - /* Enable IR Module */ - tmp = readl(ir->base + SUNXI_IR_CTL_REG); - writel(tmp | REG_CTL_GEN | REG_CTL_RXEN, ir->base + SUNXI_IR_CTL_REG); + ret = sunxi_ir_hw_init(dev); + if (ret) + goto exit_free_dev; dev_info(dev, "initialized sunXi IR driver\n"); return 0; exit_free_dev: rc_free_device(ir->rc); -exit_clkdisable_clk: - clk_disable_unprepare(ir->clk); -exit_clkdisable_apb_clk: - clk_disable_unprepare(ir->apb_clk); -exit_reset_assert: - reset_control_assert(ir->rst); return ret; } @@ -334,11 +356,9 @@ static int sunxi_ir_remove(struct platform_device *pdev) { struct sunxi_ir *ir = platform_get_drvdata(pdev); - clk_disable_unprepare(ir->clk); - clk_disable_unprepare(ir->apb_clk); - reset_control_assert(ir->rst); - rc_unregister_device(ir->rc); + sunxi_ir_hw_exit(&pdev->dev); + return 0; } -- cgit v1.2.3 From a6f42f5ebb7fa41bb17fd1814554929bf683007f Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 18 Jan 2021 07:00:48 +0100 Subject: media: sunxi-cir: Implement suspend/resume/shutdown callbacks To save power, gate/reset the hardware block while the system is asleep or powered off. Acked-by: Maxime Ripard Signed-off-by: Samuel Holland Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/sunxi-cir.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 2d099da8d3cc..168e1d2c876a 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -234,6 +234,20 @@ static void sunxi_ir_hw_exit(struct device *dev) reset_control_assert(ir->rst); } +static int __maybe_unused sunxi_ir_suspend(struct device *dev) +{ + sunxi_ir_hw_exit(dev); + + return 0; +} + +static int __maybe_unused sunxi_ir_resume(struct device *dev) +{ + return sunxi_ir_hw_init(dev); +} + +static SIMPLE_DEV_PM_OPS(sunxi_ir_pm_ops, sunxi_ir_suspend, sunxi_ir_resume); + static int sunxi_ir_probe(struct platform_device *pdev) { int ret = 0; @@ -362,6 +376,11 @@ static int sunxi_ir_remove(struct platform_device *pdev) return 0; } +static void sunxi_ir_shutdown(struct platform_device *pdev) +{ + sunxi_ir_hw_exit(&pdev->dev); +} + static const struct sunxi_ir_quirks sun4i_a10_ir_quirks = { .has_reset = false, .fifo_size = 16, @@ -397,9 +416,11 @@ MODULE_DEVICE_TABLE(of, sunxi_ir_match); static struct platform_driver sunxi_ir_driver = { .probe = sunxi_ir_probe, .remove = sunxi_ir_remove, + .shutdown = sunxi_ir_shutdown, .driver = { .name = SUNXI_IR_DEV, .of_match_table = sunxi_ir_match, + .pm = &sunxi_ir_pm_ops, }, }; -- cgit v1.2.3 From 9dec0f48a75e0dadca498002d25ef4e143e60194 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 19 Jan 2021 14:53:50 +0100 Subject: media: mceusb: sanity check for prescaler value prescaler larger than 8 would mean the carrier is at most 152Hz, which does not make sense for IR carriers. Reported-by: syzbot+6d31bf169a8265204b8d@syzkaller.appspotmail.com Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index c8d63673e131..5642595a057e 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -701,11 +701,18 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, data[0], data[1]); break; case MCE_RSP_EQIRCFS: + if (!data[0] && !data[1]) { + dev_dbg(dev, "%s: no carrier", inout); + break; + } + // prescaler should make sense + if (data[0] > 8) + break; period = DIV_ROUND_CLOSEST((1U << data[0] * 2) * (data[1] + 1), 10); if (!period) break; - carrier = (1000 * 1000) / period; + carrier = USEC_PER_SEC / period; dev_dbg(dev, "%s carrier of %u Hz (period %uus)", inout, carrier, period); break; -- cgit v1.2.3 From 59a3e78f8cc33901fe39035c1ab681374bba95ad Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 23 Aug 2020 20:13:31 +0200 Subject: media: lmedm04: Fix misuse of comma There's a comma used instead of a semicolon that causes multiple statements to be executed after an if instead of just the intended single statement. Replace the comma with a semicolon. Fixes: 15e1ce33182d ("[media] lmedm04: Fix usb_submit_urb BOGUS urb xfer, pipe 1 != type 3 in interrupt urb") Signed-off-by: Joe Perches Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/lmedm04.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 5a7a9522d46d..9ddda8d68ee0 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -391,7 +391,7 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap) ep = usb_pipe_endpoint(d->udev, lme_int->lme_urb->pipe); if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK) - lme_int->lme_urb->pipe = usb_rcvbulkpipe(d->udev, 0xa), + lme_int->lme_urb->pipe = usb_rcvbulkpipe(d->udev, 0xa); usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC); info("INT Interrupt Service Started"); -- cgit v1.2.3 From add5861769f912af0181f5fbd79dbf19c8211c20 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 3 Oct 2020 11:32:43 +0200 Subject: media: lmedm04: Use GFP_KERNEL for URB allocation/submission. lme2510_int_read is not atomically called so use GFP_KERNEL for usb_alloc_urb and usb_submit_urb which is the first in the chain of interrupt submissions. Signed-off-by: Malcolm Priestley Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/lmedm04.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 9ddda8d68ee0..0f5a1eed5ea9 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -373,7 +373,7 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap) struct lme2510_state *lme_int = adap_to_priv(adap); struct usb_host_endpoint *ep; - lme_int->lme_urb = usb_alloc_urb(0, GFP_ATOMIC); + lme_int->lme_urb = usb_alloc_urb(0, GFP_KERNEL); if (lme_int->lme_urb == NULL) return -ENOMEM; @@ -393,7 +393,7 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap) if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK) lme_int->lme_urb->pipe = usb_rcvbulkpipe(d->udev, 0xa); - usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC); + usb_submit_urb(lme_int->lme_urb, GFP_KERNEL); info("INT Interrupt Service Started"); return 0; -- cgit v1.2.3 From 7b2afdbc00c5e78de12d08000a55d313d8fe6697 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Mon, 24 Aug 2020 23:42:23 +0200 Subject: media: lmedm04: Remove lme2510_kill_urb function. This function is not necessary and largely a remnant of dvb-usb workaround and is now controlled by dvb-usb-v2. Signed-off-by: Malcolm Priestley Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/lmedm04.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 0f5a1eed5ea9..d0672aed3bfe 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -751,20 +751,6 @@ static const char *lme_firmware_switch(struct dvb_usb_device *d, int cold) return fw_lme; } -static int lme2510_kill_urb(struct usb_data_stream *stream) -{ - int i; - - for (i = 0; i < stream->urbs_submitted; i++) { - deb_info(3, "killing URB no. %d.", i); - /* stop the URB */ - usb_kill_urb(stream->urb_list[i]); - } - stream->urbs_submitted = 0; - - return 0; -} - static struct tda10086_config tda10086_config = { .demod_address = 0x0e, .invert = 0, @@ -1198,11 +1184,6 @@ static int lme2510_get_rc_config(struct dvb_usb_device *d, static void lme2510_exit(struct dvb_usb_device *d) { struct lme2510_state *st = d->priv; - struct dvb_usb_adapter *adap = &d->adapter[0]; - - if (adap != NULL) { - lme2510_kill_urb(&adap->stream); - } if (st->lme_urb) { usb_kill_urb(st->lme_urb); -- cgit v1.2.3 From 036bf04f14a1bfc6db771e8e1b8f53eb3f092006 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 15 Jan 2021 11:36:11 +0100 Subject: media: atomisp/pci/hmm: fix wrong printk format Fix this compiler warning on i686: In file included from include/linux/printk.h:409, from include/linux/kernel.h:16, from drivers/staging/media/atomisp/pci/hmm/hmm.c:23: drivers/staging/media/atomisp/pci/hmm/hmm.c: In function 'hmm_alloc': drivers/staging/media/atomisp/pci/hmm/hmm.c:272:3: warning: format '%ld' expects argument of type 'long int', but argument 6 has type 'size_t' {aka 'unsigned int'} [-Wformat=] 272 | "%s: pages: 0x%08x (%ld bytes), type: %d from highmem %d, user ptr %p, cached %d\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Use %zu instead of %ld. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/hmm/hmm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm.c b/drivers/staging/media/atomisp/pci/hmm/hmm.c index e0eaff0f8a22..6a5ee4607089 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm.c @@ -269,7 +269,7 @@ ia_css_ptr hmm_alloc(size_t bytes, enum hmm_bo_type type, hmm_set(bo->start, 0, bytes); dev_dbg(atomisp_dev, - "%s: pages: 0x%08x (%ld bytes), type: %d from highmem %d, user ptr %p, cached %d\n", + "%s: pages: 0x%08x (%zu bytes), type: %d from highmem %d, user ptr %p, cached %d\n", __func__, bo->start, bytes, type, from_highmem, userptr, cached); return bo->start; -- cgit v1.2.3 From 048c96e28674f15c0403deba2104ffba64544a06 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 09:28:02 +0100 Subject: media: v4l2-ctrls.c: fix shift-out-of-bounds in std_validate If a menu has more than 64 items, then don't check menu_skip_mask for items 65 and up. Signed-off-by: Hans Verkuil Reported-by: syzbot+42d8c7c3d3e594b34346@syzkaller.appspotmail.com Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index f7b310240af2..016cf6204cbb 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2181,7 +2181,8 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_INTEGER_MENU: if (ptr.p_s32[idx] < ctrl->minimum || ptr.p_s32[idx] > ctrl->maximum) return -ERANGE; - if (ctrl->menu_skip_mask & (1ULL << ptr.p_s32[idx])) + if (ptr.p_s32[idx] < BITS_PER_LONG_LONG && + (ctrl->menu_skip_mask & BIT_ULL(ptr.p_s32[idx]))) return -EINVAL; if (ctrl->type == V4L2_CTRL_TYPE_MENU && ctrl->qmenu[ptr.p_s32[idx]][0] == '\0') -- cgit v1.2.3 From 8089651cd9edea2663d8e51a2199df8570f8ab45 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 09:33:37 +0100 Subject: media: meson/ge2d: set ret to -ENOMEM Fix this smatch warning: drivers/media/platform/meson/ge2d/ge2d.c:991 ge2d_probe() warn: missing error code 'ret' when video_device_alloc() returns NULL. Signed-off-by: Hans Verkuil Cc: Neil Armstrong Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/meson/ge2d/ge2d.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/meson/ge2d/ge2d.c b/drivers/media/platform/meson/ge2d/ge2d.c index f526501bd473..153612ca96fc 100644 --- a/drivers/media/platform/meson/ge2d/ge2d.c +++ b/drivers/media/platform/meson/ge2d/ge2d.c @@ -988,6 +988,7 @@ static int ge2d_probe(struct platform_device *pdev) vfd = video_device_alloc(); if (!vfd) { v4l2_err(&ge2d->v4l2_dev, "Failed to allocate video device\n"); + ret = -ENOMEM; goto unreg_v4l2_dev; } -- cgit v1.2.3 From 7692057d9cc5f534a47ce1fea6648bfed9f130f8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 09:36:29 +0100 Subject: media: davinci/vpbe.c: ret contains the return code, not err Fixes this smatch warning: drivers/media/platform/davinci/vpbe.c:632 vpbe_initialize() warn: missing error code 'ret' The function returns 'ret', but instead 'err' was set to -ENOMEM. Signed-off-by: Hans Verkuil Cc: "Lad, Prabhakar" Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index fe9468b180e6..5f0aeb744e81 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -628,7 +628,7 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) if (err) { v4l2_err(&vpbe_dev->v4l2_dev, "unable to initialize the OSD device"); - err = -ENOMEM; + ret = -ENOMEM; goto fail_dev_unregister; } } -- cgit v1.2.3 From fec9b0e8491649386559dc154c113f569f908e6e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 09:39:20 +0100 Subject: media: tuners/it913x.c: fix missing error code Fixes this smatch warning: drivers/media/tuners/it913x.c:65 it913x_init() warn: missing error code 'ret' Set ret to -EINVAL if the clock identifier was invalid. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/it913x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/tuners/it913x.c b/drivers/media/tuners/it913x.c index e8e66390be41..7696a28fe407 100644 --- a/drivers/media/tuners/it913x.c +++ b/drivers/media/tuners/it913x.c @@ -62,6 +62,7 @@ static int it913x_init(struct dvb_frontend *fe) break; default: dev_err(&pdev->dev, "unknown clock identifier %d\n", utmp); + ret = -EINVAL; goto err; } -- cgit v1.2.3 From 6e7cca2790a54057ddf64da7843271e192f71ca0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 09:42:38 +0100 Subject: media: i2c/ov8865.c: fix error checks using wrong variable Fix two typos: dvdd -> dovdd and dvdd -> avdd Both clearly copy-and-paste mistakes. Fixes this smatch warning: drivers/media/i2c/ov8865.c:2852 ov8865_probe() warn: passing zero to 'PTR_ERR' Signed-off-by: Hans Verkuil Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov8865.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index ab4804d5b285..b2099e38d0af 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -2839,9 +2839,9 @@ static int ov8865_probe(struct i2c_client *client) /* DOVDD: digital I/O */ sensor->dovdd = devm_regulator_get(dev, "dovdd"); - if (IS_ERR(sensor->dvdd)) { + if (IS_ERR(sensor->dovdd)) { dev_err(dev, "cannot get DOVDD (digital I/O) regulator\n"); - ret = PTR_ERR(sensor->dvdd); + ret = PTR_ERR(sensor->dovdd); goto error_endpoint; } @@ -2849,7 +2849,7 @@ static int ov8865_probe(struct i2c_client *client) sensor->avdd = devm_regulator_get(dev, "avdd"); if (IS_ERR(sensor->avdd)) { dev_err(dev, "cannot get AVDD (analog) regulator\n"); - ret = PTR_ERR(sensor->dvdd); + ret = PTR_ERR(sensor->avdd); goto error_endpoint; } -- cgit v1.2.3 From 3d14284fe3634e873560a5dce90b65dfd409a29d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 09:45:23 +0100 Subject: media: sti/c8sectpfe: set correct return code Fixes this smatch warning: drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c:829 c8sectpfe_probe() warn: missing error code 'ret' Set ret to -EINVAL if the reset gpio was not found. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 5ceb366648b3..a7a6ea666740 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -826,6 +826,7 @@ static int c8sectpfe_probe(struct platform_device *pdev) dev_err(dev, "reset gpio for tsin%d not valid (gpio=%d)\n", tsin->tsin_id, tsin->rst_gpio); + ret = -EINVAL; goto err_node_put; } -- cgit v1.2.3 From 0dfa73608f45918aa1c9a98c6d4c8b86382a4bc2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 09:47:20 +0100 Subject: media: sti/hva: add missing clk_disable_unprepare() Fixes this smatch warning: drivers/media/platform/sti/hva/hva-hw.c:453 hva_hw_runtime_resume() warn: 'hva->clk' not released on lines: 450. Call clk_disable_unprepare() when clk_set_rate() fails. Signed-off-by: Hans Verkuil Cc: Jean-Christophe Trotin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/hva/hva-hw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c index 43f279e2a6a3..f59811e27f51 100644 --- a/drivers/media/platform/sti/hva/hva-hw.c +++ b/drivers/media/platform/sti/hva/hva-hw.c @@ -447,6 +447,7 @@ int hva_hw_runtime_resume(struct device *dev) if (clk_set_rate(hva->clk, CLK_RATE)) { dev_err(dev, "%s failed to set clock frequency\n", HVA_PREFIX); + clk_disable_unprepare(hva->clk); return -EINVAL; } -- cgit v1.2.3 From dd3bca72c4db353080e3de44124d45712d73b4f4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 09:52:53 +0100 Subject: media: pci/ivtv: release memory regions on error Fix this smatch warning: drivers/media/pci/ivtv/ivtv-driver.c:900 ivtv_setup_pci() warn: 'itv->base_addr' not released on lines: 876. One error path didn't release the memory regions. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-driver.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index 28acb14490d5..6e448cb3b51c 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -873,6 +873,11 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, pci_read_config_word(pdev, PCI_COMMAND, &cmd); if (!(cmd & PCI_COMMAND_MASTER)) { IVTV_ERR("Bus Mastering is not enabled\n"); + if (itv->has_cx23415) + release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, + IVTV_DECODER_SIZE); + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); return -ENXIO; } } -- cgit v1.2.3 From add434e551f07554f093193d16a850bdf45ec236 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 09:58:38 +0100 Subject: media: usb/dvb-usb-v2/rtl28xxu.c: clean up code to fix smatch warning Fixes this smatch warning: drivers/media/usb/dvb-usb-v2/rtl28xxu.c:1040 rtl2832u_frontend_attach() warn: missing error code 'ret' It is actually a bogus warning since in this particular case ret isn't meant to be set. But by reworking the code a bit the code is actually a lot more understandable and it fixes this warning. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 35 ++++++++++++--------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 3952cc534b4a..97ed17a141bb 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -944,12 +944,6 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) if (dev->slave_demod) { struct i2c_board_info info = {}; - /* - * We continue on reduced mode, without DVB-T2/C, using master - * demod, when slave demod fails. - */ - ret = 0; - /* attach slave demodulator */ if (dev->slave_demod == SLAVE_DEMOD_MN88472) { struct mn88472_config mn88472_config = {}; @@ -964,14 +958,11 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) info.platform_data = &mn88472_config; request_module(info.type); client = i2c_new_client_device(&d->i2c_adap, &info); - if (!i2c_client_has_driver(client)) { - dev->slave_demod = SLAVE_DEMOD_NONE; + if (!i2c_client_has_driver(client)) goto err_slave_demod_failed; - } if (!try_module_get(client->dev.driver->owner)) { i2c_unregister_device(client); - dev->slave_demod = SLAVE_DEMOD_NONE; goto err_slave_demod_failed; } @@ -986,14 +977,11 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) info.platform_data = &mn88473_config; request_module(info.type); client = i2c_new_client_device(&d->i2c_adap, &info); - if (!i2c_client_has_driver(client)) { - dev->slave_demod = SLAVE_DEMOD_NONE; + if (!i2c_client_has_driver(client)) goto err_slave_demod_failed; - } if (!try_module_get(client->dev.driver->owner)) { i2c_unregister_device(client); - dev->slave_demod = SLAVE_DEMOD_NONE; goto err_slave_demod_failed; } @@ -1009,10 +997,8 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) adap->fe[1] = dvb_attach(cxd2841er_attach_t_c, &cxd2837er_config, &d->i2c_adap); - if (!adap->fe[1]) { - dev->slave_demod = SLAVE_DEMOD_NONE; + if (!adap->fe[1]) goto err_slave_demod_failed; - } adap->fe[1]->id = 1; dev->i2c_client_slave_demod = NULL; } else { @@ -1029,14 +1015,11 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) info.platform_data = &si2168_config; request_module(info.type); client = i2c_new_client_device(&d->i2c_adap, &info); - if (!i2c_client_has_driver(client)) { - dev->slave_demod = SLAVE_DEMOD_NONE; + if (!i2c_client_has_driver(client)) goto err_slave_demod_failed; - } if (!try_module_get(client->dev.driver->owner)) { i2c_unregister_device(client); - dev->slave_demod = SLAVE_DEMOD_NONE; goto err_slave_demod_failed; } @@ -1047,10 +1030,18 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) } } return 0; -err_slave_demod_failed: + err: dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; + +err_slave_demod_failed: + /* + * We continue on reduced mode, without DVB-T2/C, using master + * demod, when slave demod fails. + */ + dev->slave_demod = SLAVE_DEMOD_NONE; + return 0; } static int rtl28xxu_frontend_attach(struct dvb_usb_adapter *adap) -- cgit v1.2.3 From 83104f045dae226ef56b1462c03b21f46d4d868f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 10:03:21 +0100 Subject: media: dvb-frontends/rtl2832.c: fix missing error code Fixes this smatch warning: drivers/media/dvb-frontends/rtl2832.c:702 rtl2832_read_status() warn: missing error code 'ret' 'ret' is indeed not set, so set it to -EINVAL so a proper error code is returned. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/rtl2832.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 01dcc7f1b9b2..dcbeb9f5e12a 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -698,6 +698,7 @@ static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status) goto err; constellation = (u8tmp >> 2) & 0x03; /* [3:2] */ + ret = -EINVAL; if (constellation > CONSTELLATION_NUM - 1) goto err; -- cgit v1.2.3 From e121993ae4b5fa147d43db878108b7c6a7065e7b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 10:09:05 +0100 Subject: media: dvb-frontends/af9033.c: fix missing error codes Fixes two smatch warnings: drivers/media/dvb-frontends/af9033.c:128 af9033_init() warn: missing error code 'ret' drivers/media/dvb-frontends/af9033.c:855 af9033_read_snr() warn: missing error code 'ret' Set error codes correctly. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 6a8d78b6edac..785c49b3d307 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -125,6 +125,7 @@ static int af9033_init(struct dvb_frontend *fe) if (i == ARRAY_SIZE(clock_adc_lut)) { dev_err(&client->dev, "Couldn't find ADC config for clock %d\n", dev->cfg.clock); + ret = -ENODEV; goto err; } @@ -852,6 +853,7 @@ static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr) *snr = *snr * 0xffff / 32; break; default: + ret = -EINVAL; goto err; } } -- cgit v1.2.3 From 7be37332ecfd45a38ea34caa016e066b44bc8115 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 10:11:33 +0100 Subject: media: atomisp/pci: add missing include Fix two smatch warnings: drivers/staging/media/atomisp//pci/ia_css_firmware.h:52:29: warning: 'struct device' declared inside parameter list will not be visible outside of this definition or declaration drivers/staging/media/atomisp//pci/ia_css_control.h:49:24: warning: 'struct device' declared inside parameter list will not be visible outside of this definition or declaration Add '#include ' to ia_css_firmware.h so struct device is defined. ia_css_control.h includes ia_css_firmware.h, so it is sufficient to just modify ia_css_firmware.h. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/ia_css_firmware.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/atomisp/pci/ia_css_firmware.h b/drivers/staging/media/atomisp/pci/ia_css_firmware.h index edab72256494..e5e2f6fb37e0 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_firmware.h +++ b/drivers/staging/media/atomisp/pci/ia_css_firmware.h @@ -20,6 +20,7 @@ * This file contains firmware loading/unloading support functionality */ +#include #include "ia_css_err.h" #include "ia_css_env.h" -- cgit v1.2.3 From 5320f4c1fb88cb68320e36e70c05ce52ed3b0927 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 Jan 2021 10:27:08 +0100 Subject: media: i2c/ov02a10.c: add cast to fix type mismatch By adding this cast '(__force __le16)' this sparse warning is fixed: drivers/media/i2c/ov02a10.c:391:19: warning: cast to restricted __le16 Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov02a10.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c index 8683ffd3287a..60b4bc645334 100644 --- a/drivers/media/i2c/ov02a10.c +++ b/drivers/media/i2c/ov02a10.c @@ -388,7 +388,7 @@ static int ov02a10_check_sensor_id(struct ov02a10 *ov02a10) if (ret < 0) return ret; - chip_id = le16_to_cpu(ret); + chip_id = le16_to_cpu((__force __le16)ret); if ((chip_id & OV02A10_ID_MASK) != OV02A10_ID) { dev_err(&client->dev, "unexpected sensor id(0x%04x)\n", chip_id); -- cgit v1.2.3 From 0a933a7f73d6c545dcbecb4f7a92d272aef4417b Mon Sep 17 00:00:00 2001 From: Daniel W. S. Almeida Date: Thu, 24 Dec 2020 16:04:00 +0100 Subject: media: vidtv: psi: fix missing crc for PMT The PMT write function was refactored and this broke the CRC computation. Fix it. Fixes: db9569f67e2e ("media: vidtv: cleanup PMT write table function") Signed-off-by: Daniel W. S. Almeida Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vidtv/vidtv_psi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_psi.c b/drivers/media/test-drivers/vidtv/vidtv_psi.c index 4511a2a98405..1724bb485e67 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_psi.c +++ b/drivers/media/test-drivers/vidtv/vidtv_psi.c @@ -1164,6 +1164,8 @@ u32 vidtv_psi_pmt_write_into(struct vidtv_psi_pmt_write_args *args) struct vidtv_psi_desc *table_descriptor = args->pmt->descriptor; struct vidtv_psi_table_pmt_stream *stream = args->pmt->stream; struct vidtv_psi_desc *stream_descriptor; + u32 crc = INITIAL_CRC; + u32 nbytes = 0; struct header_write_args h_args = { .dest_buf = args->buf, .dest_offset = args->offset, @@ -1181,6 +1183,7 @@ u32 vidtv_psi_pmt_write_into(struct vidtv_psi_pmt_write_args *args) .new_psi_section = false, .is_crc = false, .dest_buf_sz = args->buf_sz, + .crc = &crc, }; struct desc_write_args d_args = { .dest_buf = args->buf, @@ -1193,8 +1196,6 @@ u32 vidtv_psi_pmt_write_into(struct vidtv_psi_pmt_write_args *args) .pid = args->pid, .dest_buf_sz = args->buf_sz, }; - u32 crc = INITIAL_CRC; - u32 nbytes = 0; vidtv_psi_pmt_table_update_sec_len(args->pmt); -- cgit v1.2.3 From 4671c204dd5fe7f73dc7a47977e779f4a7a95508 Mon Sep 17 00:00:00 2001 From: Daniel W. S. Almeida Date: Thu, 24 Dec 2020 16:04:01 +0100 Subject: media: vidtv: remove unused field from 'struct vidtv_mpeg_ts' Commit 3d1387b3b8f6 ("media: vidtv: fix some warnings") replaced the unused flexible array member at the end of struct vidtv_mpeg_ts with a pointer. This broke the 188-byte alignment since the struct no longer was 4 bytes in size. Fix this by removing this field entirely. Fixes: 3d1387b3b8f6 ("media: vidtv: fix some warnings") Signed-off-by: Daniel W. S. Almeida Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vidtv/vidtv_ts.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_ts.h b/drivers/media/test-drivers/vidtv/vidtv_ts.h index f5e8e1f37f05..09b4ffd02829 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_ts.h +++ b/drivers/media/test-drivers/vidtv/vidtv_ts.h @@ -44,7 +44,6 @@ struct vidtv_mpeg_ts { u8 adaptation_field:1; u8 scrambling:2; } __packed; - struct vidtv_mpeg_ts_adaption *adaption; } __packed; /** -- cgit v1.2.3 From 8d23ada8e72d460c24da50a008209ecd3d7f9c8b Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Tue, 29 Dec 2020 09:55:30 +0100 Subject: media: vidtv: Use kmemdup instead of kzalloc and memcpy Fixes coccicheck warning: drivers/media/test-drivers/vidtv/vidtv_psi.c:509:10-17: WARNING opportunity for kmemdup Signed-off-by: Tian Tao Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vidtv/vidtv_psi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_psi.c b/drivers/media/test-drivers/vidtv/vidtv_psi.c index 1724bb485e67..47ed7907db8d 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_psi.c +++ b/drivers/media/test-drivers/vidtv/vidtv_psi.c @@ -506,10 +506,9 @@ struct vidtv_psi_desc *vidtv_psi_desc_clone(struct vidtv_psi_desc *desc) case REGISTRATION_DESCRIPTOR: default: - curr = kzalloc(sizeof(*desc) + desc->length, GFP_KERNEL); + curr = kmemdup(desc, sizeof(*desc) + desc->length, GFP_KERNEL); if (!curr) return NULL; - memcpy(curr, desc, sizeof(*desc) + desc->length); } if (!curr) -- cgit v1.2.3 From e259572d6ffc036d29d0829f505de2c0149ff0bc Mon Sep 17 00:00:00 2001 From: Daniel W. S. Almeida Date: Wed, 6 Jan 2021 04:04:46 +0100 Subject: media: vidtv: Add media controller support Add media controller support when CONFIG_MEDIA_CONTROLLER_DVB is set so that, in the future, a test sequence in v4l-utils can be written without having to know which /dev/fooX device should be used. [mchehab: avoided usage of C99 comments] Signed-off-by: Daniel W. S. Almeida Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vidtv/vidtv_bridge.c | 28 +++++++++++++++++++++++++ drivers/media/test-drivers/vidtv/vidtv_bridge.h | 6 ++++++ 2 files changed, 34 insertions(+) diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index fc64d0c8492a..20c7345eb2b6 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "vidtv_bridge.h" #include "vidtv_common.h" @@ -501,9 +503,30 @@ static int vidtv_bridge_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dvb); +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + dvb->mdev.dev = &pdev->dev; + + strscpy(dvb->mdev.model, "vidtv", sizeof(dvb->mdev.model)); + strscpy(dvb->mdev.bus_info, "platform:vidtv", sizeof(dvb->mdev.bus_info)); + + media_device_init(&dvb->mdev); + ret = media_device_register(&dvb->mdev); + if (ret) { + dev_err(dvb->mdev.dev, + "media device register failed (err=%d)\n", ret); + goto err_media_device_register; + } + + dvb_register_media_controller(&dvb->adapter, &dvb->mdev); +#endif /* CONFIG_MEDIA_CONTROLLER_DVB */ + dev_info(&pdev->dev, "Successfully initialized vidtv!\n"); return ret; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB +err_media_device_register: + media_device_cleanup(&dvb->mdev); +#endif /* CONFIG_MEDIA_CONTROLLER_DVB */ err_dvb: kfree(dvb); return ret; @@ -516,6 +539,11 @@ static int vidtv_bridge_remove(struct platform_device *pdev) dvb = platform_get_drvdata(pdev); +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + media_device_unregister(&dvb->mdev); + media_device_cleanup(&dvb->mdev); +#endif /* CONFIG_MEDIA_CONTROLLER_DVB */ + mutex_destroy(&dvb->feed_lock); for (i = 0; i < NUM_FE; ++i) { diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.h b/drivers/media/test-drivers/vidtv/vidtv_bridge.h index 2528adaee27d..da8ef7dc1d18 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.h +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "vidtv_mux.h" @@ -42,6 +43,7 @@ * @feed_lock: Protects access to the start/stop stream logic/data. * @streaming: Whether we are streaming now. * @mux: The abstraction responsible for delivering MPEG TS packets to the bridge. + * @mdev: The media_device struct for media controller support. */ struct vidtv_dvb { struct platform_device *pdev; @@ -60,6 +62,10 @@ struct vidtv_dvb { bool streaming; struct vidtv_mux *mux; + +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + struct media_device mdev; +#endif /* CONFIG_MEDIA_CONTROLLER_DVB */ }; #endif // VIDTV_BRIDG_H -- cgit v1.2.3 From 707848ccdf3616762fec1f64c498d8079b452f57 Mon Sep 17 00:00:00 2001 From: Daniel W. S. Almeida Date: Wed, 6 Jan 2021 04:04:47 +0100 Subject: media: vidtv: reinstate sysfs bind attrs Reinstate sysfs bind attrs so that vidtv can be bound and unbound via sysfs. This is useful for automated regression testing in userspace. Signed-off-by: Daniel W. S. Almeida Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vidtv/vidtv_bridge.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index 20c7345eb2b6..600e1c56de86 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -571,7 +571,6 @@ static struct platform_device vidtv_bridge_dev = { static struct platform_driver vidtv_bridge_driver = { .driver = { .name = "vidtv_bridge", - .suppress_bind_attrs = true, }, .probe = vidtv_bridge_probe, .remove = vidtv_bridge_remove, -- cgit v1.2.3 From 0b8f1d4a093a62d47bd8402cf569a2208a9887a1 Mon Sep 17 00:00:00 2001 From: Daniel W. S. Almeida Date: Wed, 6 Jan 2021 04:04:48 +0100 Subject: media: vidtv: use a simpler name in platform_{device|driver} Change from "vidtv_bridge" to simply "vidtv" so that vidtv looks more similar to the other media virtual drivers in /sys/bus/platform. Signed-off-by: Daniel W. S. Almeida Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vidtv/vidtv_bridge.c | 4 ++-- drivers/media/test-drivers/vidtv/vidtv_bridge.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index 600e1c56de86..4ec6a2b3b928 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -564,13 +564,13 @@ static void vidtv_bridge_dev_release(struct device *dev) } static struct platform_device vidtv_bridge_dev = { - .name = "vidtv_bridge", + .name = VIDTV_PDEV_NAME, .dev.release = vidtv_bridge_dev_release, }; static struct platform_driver vidtv_bridge_driver = { .driver = { - .name = "vidtv_bridge", + .name = VIDTV_PDEV_NAME, }, .probe = vidtv_bridge_probe, .remove = vidtv_bridge_remove, diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.h b/drivers/media/test-drivers/vidtv/vidtv_bridge.h index da8ef7dc1d18..de47ce6e7b14 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.h +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.h @@ -16,6 +16,7 @@ * For now, only one frontend is supported. See vidtv_start_streaming() */ #define NUM_FE 1 +#define VIDTV_PDEV_NAME "vidtv" #include #include -- cgit v1.2.3 From ed35980a4d8a194d959826cc82ef4c9f78796a3f Mon Sep 17 00:00:00 2001 From: Daniel W. S. Almeida Date: Wed, 6 Jan 2021 04:04:49 +0100 Subject: media: vidtv: print message when driver is removed Print a message when the driver is removed so that we get some visual confirmation when unbinding vidtv. Signed-off-by: Daniel W. S. Almeida Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vidtv/vidtv_bridge.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index 4ec6a2b3b928..6ab17a83bced 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -555,6 +555,7 @@ static int vidtv_bridge_remove(struct platform_device *pdev) dvb_dmxdev_release(&dvb->dmx_dev); dvb_dmx_release(&dvb->demux); dvb_unregister_adapter(&dvb->adapter); + dev_info(&pdev->dev, "Successfully removed vidtv\n"); return 0; } -- cgit v1.2.3 From 7072db89572135f28cad65f15877bf7e67cf2ff8 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Wed, 23 Dec 2020 12:06:58 +0100 Subject: media: cedrus: Remove checking for required controls According to v4l2 request api specifications, it's allowed to skip control if its content isn't changed for performance reasons. Cedrus driver predates that, so it has implemented mechanism to check if all required controls are included in one request. Conform to specifications with removing that mechanism. Note that this mechanism with static required flag isn't very good anyway because need for control is usually signaled in other controls. Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver") Signed-off-by: Jernej Skrabec Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus.c | 49 ----------------------------- drivers/staging/media/sunxi/cedrus/cedrus.h | 1 - 2 files changed, 50 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index ddad5d274ee8..7bd9291c8d5f 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -34,56 +34,48 @@ static const struct cedrus_control cedrus_controls[] = { .id = V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS, }, .codec = CEDRUS_CODEC_MPEG2, - .required = true, }, { .cfg = { .id = V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION, }, .codec = CEDRUS_CODEC_MPEG2, - .required = false, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, }, .codec = CEDRUS_CODEC_H264, - .required = true, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_SLICE_PARAMS, }, .codec = CEDRUS_CODEC_H264, - .required = true, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_SPS, }, .codec = CEDRUS_CODEC_H264, - .required = true, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_PPS, }, .codec = CEDRUS_CODEC_H264, - .required = true, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, }, .codec = CEDRUS_CODEC_H264, - .required = false, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS, }, .codec = CEDRUS_CODEC_H264, - .required = false, }, { .cfg = { @@ -92,7 +84,6 @@ static const struct cedrus_control cedrus_controls[] = { .def = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, }, .codec = CEDRUS_CODEC_H264, - .required = false, }, { .cfg = { @@ -101,7 +92,6 @@ static const struct cedrus_control cedrus_controls[] = { .def = V4L2_STATELESS_H264_START_CODE_NONE, }, .codec = CEDRUS_CODEC_H264, - .required = false, }, /* * We only expose supported profiles information, @@ -120,28 +110,24 @@ static const struct cedrus_control cedrus_controls[] = { BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), }, .codec = CEDRUS_CODEC_H264, - .required = false, }, { .cfg = { .id = V4L2_CID_MPEG_VIDEO_HEVC_SPS, }, .codec = CEDRUS_CODEC_H265, - .required = true, }, { .cfg = { .id = V4L2_CID_MPEG_VIDEO_HEVC_PPS, }, .codec = CEDRUS_CODEC_H265, - .required = true, }, { .cfg = { .id = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS, }, .codec = CEDRUS_CODEC_H265, - .required = true, }, { .cfg = { @@ -150,7 +136,6 @@ static const struct cedrus_control cedrus_controls[] = { .def = V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED, }, .codec = CEDRUS_CODEC_H265, - .required = false, }, { .cfg = { @@ -159,14 +144,12 @@ static const struct cedrus_control cedrus_controls[] = { .def = V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE, }, .codec = CEDRUS_CODEC_H265, - .required = false, }, { .cfg = { .id = V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER, }, .codec = CEDRUS_CODEC_VP8, - .required = true, }, }; @@ -227,12 +210,8 @@ static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx) static int cedrus_request_validate(struct media_request *req) { struct media_request_object *obj; - struct v4l2_ctrl_handler *parent_hdl, *hdl; struct cedrus_ctx *ctx = NULL; - struct v4l2_ctrl *ctrl_test; unsigned int count; - unsigned int i; - int ret = 0; list_for_each_entry(obj, &req->objects, list) { struct vb2_buffer *vb; @@ -259,34 +238,6 @@ static int cedrus_request_validate(struct media_request *req) return -EINVAL; } - parent_hdl = &ctx->hdl; - - hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl); - if (!hdl) { - v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control(s)\n"); - return -ENOENT; - } - - for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) { - if (cedrus_controls[i].codec != ctx->current_codec || - !cedrus_controls[i].required) - continue; - - ctrl_test = v4l2_ctrl_request_hdl_ctrl_find(hdl, - cedrus_controls[i].cfg.id); - if (!ctrl_test) { - v4l2_info(&ctx->dev->v4l2_dev, - "Missing required codec control\n"); - ret = -ENOENT; - break; - } - } - - v4l2_ctrl_request_hdl_put(hdl); - - if (ret) - return ret; - return vb2_request_validate(req); } diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index c96077aaef49..251a6a660351 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -56,7 +56,6 @@ enum cedrus_h264_pic_type { struct cedrus_control { struct v4l2_ctrl_config cfg; enum cedrus_codec codec; - unsigned char required:1; }; struct cedrus_h264_run { -- cgit v1.2.3 From 625993166b551d633917ca35d4afb7b46d7451b4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 20 Nov 2020 17:27:18 +0100 Subject: media: atomisp: Fix a buffer overflow in debug code The "pad" variable is a user controlled string and we haven't properly clamped it at this point so the debug code could print from beyond the of the array. Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_subdev.c | 24 ++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index 52b9fb18c87f..dcc2dd981ca6 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -349,12 +349,20 @@ static int isp_subdev_get_selection(struct v4l2_subdev *sd, return 0; } -static char *atomisp_pad_str[] = { "ATOMISP_SUBDEV_PAD_SINK", - "ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE", - "ATOMISP_SUBDEV_PAD_SOURCE_VF", - "ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW", - "ATOMISP_SUBDEV_PAD_SOURCE_VIDEO" - }; +static const char *atomisp_pad_str(unsigned int pad) +{ + static const char *const pad_str[] = { + "ATOMISP_SUBDEV_PAD_SINK", + "ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE", + "ATOMISP_SUBDEV_PAD_SOURCE_VF", + "ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW", + "ATOMISP_SUBDEV_PAD_SOURCE_VIDEO", + }; + + if (pad >= ARRAY_SIZE(pad_str)) + return "ATOMISP_INVALID_PAD"; + return pad_str[pad]; +} int atomisp_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, @@ -378,7 +386,7 @@ int atomisp_subdev_set_selection(struct v4l2_subdev *sd, dev_dbg(isp->dev, "sel: pad %s tgt %s l %d t %d w %d h %d which %s f 0x%8.8x\n", - atomisp_pad_str[pad], target == V4L2_SEL_TGT_CROP + atomisp_pad_str(pad), target == V4L2_SEL_TGT_CROP ? "V4L2_SEL_TGT_CROP" : "V4L2_SEL_TGT_COMPOSE", r->left, r->top, r->width, r->height, which == V4L2_SUBDEV_FORMAT_TRY ? "V4L2_SUBDEV_FORMAT_TRY" @@ -612,7 +620,7 @@ void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd, enum atomisp_input_stream_id stream_id; dev_dbg(isp->dev, "ffmt: pad %s w %d h %d code 0x%8.8x which %s\n", - atomisp_pad_str[pad], ffmt->width, ffmt->height, ffmt->code, + atomisp_pad_str(pad), ffmt->width, ffmt->height, ffmt->code, which == V4L2_SUBDEV_FORMAT_TRY ? "V4L2_SUBDEV_FORMAT_TRY" : "V4L2_SUBDEV_FORMAT_ACTIVE"); -- cgit v1.2.3 From af7ab66225111d0612a38e63eae6cd4af66d43e6 Mon Sep 17 00:00:00 2001 From: Gustavo A. R. Silva Date: Fri, 20 Nov 2020 19:26:09 +0100 Subject: media: dvb-frontends: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix multiple warnings by explicitly adding multiple break and a return statements instead of just letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/cx24120.c | 1 + drivers/media/dvb-frontends/dib0090.c | 2 ++ drivers/media/dvb-frontends/drxk_hard.c | 1 + drivers/media/dvb-frontends/m88rs2000.c | 1 + 4 files changed, 5 insertions(+) diff --git a/drivers/media/dvb-frontends/cx24120.c b/drivers/media/dvb-frontends/cx24120.c index 2464b63fe0cf..d8acd582c711 100644 --- a/drivers/media/dvb-frontends/cx24120.c +++ b/drivers/media/dvb-frontends/cx24120.c @@ -363,6 +363,7 @@ static void cx24120_check_cmd(struct cx24120_state *state, u8 id) case CMD_DISEQC_BURST: cx24120_msg_mpeg_output_global_config(state, 0); /* Old driver would do a msleep(100) here */ + return; default: return; } diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c index 08a85831e917..903da33642df 100644 --- a/drivers/media/dvb-frontends/dib0090.c +++ b/drivers/media/dvb-frontends/dib0090.c @@ -1765,6 +1765,8 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front dib0090_write_reg(state, 0x1f, 0x7); *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */ state->calibrate &= ~DC_CAL; + break; + default: break; } diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index a57470bf71bf..d7fc2595f15b 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -3294,6 +3294,7 @@ static int dvbt_sc_command(struct drxk_state *state, case OFDM_SC_RA_RAM_CMD_USER_IO: case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM: status = read16(state, OFDM_SC_RA_RAM_PARAM0__A, &(param0)); + break; /* All commands yielding 0 results */ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING: case OFDM_SC_RA_RAM_CMD_SET_TIMER: diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 39cbb3ea1c9d..b294ba87e934 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -390,6 +390,7 @@ static int m88rs2000_tab_set(struct m88rs2000_state *state, case 0xff: if (tab[i].reg == 0xaa && tab[i].val == 0xff) return 0; + break; case 0x00: break; default: -- cgit v1.2.3 From 623cd8b13f633e64d0776b2ce8a29fdeaf93d42e Mon Sep 17 00:00:00 2001 From: Gustavo A. R. Silva Date: Fri, 20 Nov 2020 19:26:16 +0100 Subject: media: usb: dvb-usb-v2: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix multiple warnings by explicitly adding a couple of break statements instead of just letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9015.c | 1 + drivers/media/usb/dvb-usb-v2/lmedm04.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index c70b3cef3176..d33514acc2b5 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -51,6 +51,7 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) if (((req->addr & 0xff00) == 0xff00) || ((req->addr & 0xff00) == 0xae00)) state->buf[0] = WRITE_VIRTUAL_MEMORY; + break; case WRITE_VIRTUAL_MEMORY: case COPY_FIRMWARE: case DOWNLOAD_FIRMWARE: diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index d0672aed3bfe..1b6d4e4c52ca 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -336,6 +336,7 @@ static void lme2510_int_response(struct urb *lme_urb) st->signal_level = ibuf[5]; st->signal_sn = ibuf[4]; st->time_key = ibuf[7]; + break; default: break; } -- cgit v1.2.3 From 45fe926241bc0e09cde4b0c2a07ce2bfc6c8d12e Mon Sep 17 00:00:00 2001 From: Gustavo A. R. Silva Date: Fri, 20 Nov 2020 19:36:50 +0100 Subject: media: atomisp: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a break statement instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/runtime/isys/src/rx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/atomisp/pci/runtime/isys/src/rx.c b/drivers/staging/media/atomisp/pci/runtime/isys/src/rx.c index b4813cd50daa..4a18da6bf0c1 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isys/src/rx.c +++ b/drivers/staging/media/atomisp/pci/runtime/isys/src/rx.c @@ -368,6 +368,7 @@ static mipi_predictor_t sh_css_csi2_compression_type_2_mipi_predictor( break; case IA_CSS_CSI2_COMPRESSION_TYPE_2: predictor = MIPI_PREDICTOR_TYPE2 - 1; + break; default: break; } -- cgit v1.2.3 From 97735d3a55c86cb238fa4c7469175d344df5bd26 Mon Sep 17 00:00:00 2001 From: Gustavo A. R. Silva Date: Fri, 20 Nov 2020 19:36:57 +0100 Subject: media: dvb_frontend: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a break statement instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 06ea30a689d7..fb35697dd93c 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -984,6 +984,7 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe) fe->ops.info.symbol_rate_max); return -EINVAL; } + break; default: break; } -- cgit v1.2.3 From bbcab30420f41288250aed8eea9d0cc059a6307d Mon Sep 17 00:00:00 2001 From: Gustavo A. R. Silva Date: Fri, 20 Nov 2020 19:37:02 +0100 Subject: media: rcar_jpu: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a break statement instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_jpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 9b99ff368698..87466edf8a5e 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -648,6 +648,7 @@ static u8 jpu_parse_hdr(void *buffer, unsigned long size, unsigned int *width, if (get_word_be(&jpeg_buffer, &word)) return 0; skip(&jpeg_buffer, (long)word - 2); + break; case 0: break; default: -- cgit v1.2.3 From 69518b52e923c4372945b3ee30f0759397d71de7 Mon Sep 17 00:00:00 2001 From: Gustavo A. R. Silva Date: Fri, 20 Nov 2020 19:37:08 +0100 Subject: media: saa7134: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a break statement instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 [hverkuil: fix checkpatch TAB indendation issues] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7134/saa7134-tvaudio.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c index 5cc4ef21f9d3..aa0895d2d735 100644 --- a/drivers/media/pci/saa7134/saa7134-tvaudio.c +++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c @@ -871,23 +871,24 @@ void saa7134_enable_i2s(struct saa7134_dev *dev) switch (dev->pci->device) { case PCI_DEVICE_ID_PHILIPS_SAA7133: case PCI_DEVICE_ID_PHILIPS_SAA7135: - /* Set I2S format (SONY)  */ - saa_writeb(SAA7133_I2S_AUDIO_CONTROL, 0x00); - /* Start I2S */ - saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x11); - break; + /* Set I2S format (SONY)  */ + saa_writeb(SAA7133_I2S_AUDIO_CONTROL, 0x00); + /* Start I2S */ + saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x11); + break; case PCI_DEVICE_ID_PHILIPS_SAA7134: - i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01; + i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01; - /* enable I2S audio output for the mpeg encoder */ - saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80); - saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format); - saa_writeb(SAA7134_I2S_OUTPUT_LEVEL, 0x0F); - saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01); + /* enable I2S audio output for the mpeg encoder */ + saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80); + saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format); + saa_writeb(SAA7134_I2S_OUTPUT_LEVEL, 0x0F); + saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01); + break; default: - break; + break; } } -- cgit v1.2.3 From fcf8d018bdca0453b8d6359062e6bc1512d04c38 Mon Sep 17 00:00:00 2001 From: Luo Meng Date: Wed, 25 Nov 2020 02:34:37 +0100 Subject: media: qm1d1c0042: fix error return code in qm1d1c0042_init() Fix to return a negative error code from the error handling case instead of 0 in function qm1d1c0042_init(), as done elsewhere in this function. Fixes: ab4d14528fdf ("[media] em28xx: add support for PLEX PX-BCUD (ISDB-S)") Reported-by: Hulk Robot Signed-off-by: Luo Meng Acked-by: Akihiro Tsukada Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/qm1d1c0042.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c index 0e26d22f0b26..53aa2558f71e 100644 --- a/drivers/media/tuners/qm1d1c0042.c +++ b/drivers/media/tuners/qm1d1c0042.c @@ -343,8 +343,10 @@ static int qm1d1c0042_init(struct dvb_frontend *fe) if (val == reg_initval[reg_index][0x00]) break; } - if (reg_index >= QM1D1C0042_NUM_REG_ROWS) + if (reg_index >= QM1D1C0042_NUM_REG_ROWS) { + ret = -EINVAL; goto failed; + } memcpy(state->regs, reg_initval[reg_index], QM1D1C0042_NUM_REGS); usleep_range(2000, 3000); -- cgit v1.2.3 From c0011fe210c5835889cd5918f62b7cf0b3f26bc2 Mon Sep 17 00:00:00 2001 From: Evan Benn Date: Thu, 26 Nov 2020 04:08:42 +0100 Subject: media: mtk-vcodec: Fix order of log arguments Fix order of log arguments. [hverkuil: fix checkpatch parenthesis alignment warnings] Signed-off-by: Evan Benn Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c index a3c7a380c930..70580c2525ba 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c @@ -27,13 +27,13 @@ int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, int command, if (!ret) { status = -1; /* timeout */ - mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout time=%ums out %d %d!", - ctx->id, ctx->type, command, timeout_ms, - ctx->int_cond, ctx->int_type); + mtk_v4l2_err("[%d] ctx->type=%d, cmd=%d, wait_event_interruptible_timeout time=%ums out %d %d!", + ctx->id, ctx->type, command, timeout_ms, + ctx->int_cond, ctx->int_type); } else if (-ERESTARTSYS == ret) { - mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout interrupted by a signal %d %d", - ctx->id, ctx->type, command, ctx->int_cond, - ctx->int_type); + mtk_v4l2_err("[%d] ctx->type=%d, cmd=%d, wait_event_interruptible_timeout interrupted by a signal %d %d", + ctx->id, ctx->type, command, ctx->int_cond, + ctx->int_type); status = -1; } -- cgit v1.2.3 From 4d2e37340ec9dccb594e98481474f06e3b712841 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 4 Dec 2020 00:17:03 +0100 Subject: media: rc: select CONFIG_BITREVERSE where needed A number of remote control drivers require the bitreverse helper, and run into a link error when it is disabled: arm-linux-gnueabi-ld: drivers/media/rc/img-ir/img-ir-nec.o: in function `img_ir_nec_scancode': img-ir-nec.c:(.text+0x10c): undefined reference to `byte_rev_table' arm-linux-gnueabi-ld: drivers/media/rc/img-ir/img-ir-nec.o: in function `img_ir_nec_filter': img-ir-nec.c:(.text+0x2dc): undefined reference to `byte_rev_table' arm-linux-gnueabi-ld: drivers/media/usb/cx231xx/cx231xx-input.o: in function `get_key_isdbt': cx231xx-input.c:(.text+0x38c): undefined reference to `byte_rev_table' arm-linux-gnueabi-ld: drivers/media/usb/em28xx/em28xx-input.o: in function `em28xx_get_key_em_haup': em28xx-input.c:(.text+0x1704): undefined reference to `byte_rev_table' Signed-off-by: Arnd Bergmann Acked-by: Sean Young Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/Kconfig | 2 ++ drivers/media/rc/img-ir/Kconfig | 1 + drivers/media/usb/cx231xx/Kconfig | 1 + drivers/media/usb/em28xx/Kconfig | 1 + 4 files changed, 5 insertions(+) diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 2c0ee2e5b446..8a4b4040be45 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -92,6 +92,7 @@ config IR_SONY_DECODER config IR_SANYO_DECODER tristate "Enable IR raw decoder for the Sanyo protocol" depends on RC_CORE + select BITREVERSE help Enable this option if you have an infrared remote control which @@ -101,6 +102,7 @@ config IR_SANYO_DECODER config IR_SHARP_DECODER tristate "Enable IR raw decoder for the Sharp protocol" depends on RC_CORE + select BITREVERSE help Enable this option if you have an infrared remote control which diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig index 5c0508f2719f..a80cfcd87a95 100644 --- a/drivers/media/rc/img-ir/Kconfig +++ b/drivers/media/rc/img-ir/Kconfig @@ -30,6 +30,7 @@ config IR_IMG_HW config IR_IMG_NEC bool "NEC protocol support" depends on IR_IMG_HW + select BITREVERSE help Say Y here to enable support for the NEC, extended NEC, and 32-bit NEC protocols in the ImgTec infrared decoder block. diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 2fe2b2d335ba..b80661b8375f 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -18,6 +18,7 @@ config VIDEO_CX231XX_RC bool "Conexant cx231xx Remote Controller additional support" depends on RC_CORE=y || RC_CORE=VIDEO_CX231XX depends on VIDEO_CX231XX + select BITREVERSE default y help cx231xx hardware has a builtin RX/TX support. However, a few diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index f2031a933e54..8a24731b373a 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -77,5 +77,6 @@ config VIDEO_EM28XX_RC depends on VIDEO_EM28XX depends on !(RC_CORE=m && VIDEO_EM28XX=y) default VIDEO_EM28XX + select BITREVERSE help Enables Remote Controller support on em28xx driver. -- cgit v1.2.3 From fed3f55dfca8d8ea4b405b3e4cfd9ba46f4740ef Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 11 Jan 2021 15:54:38 +0100 Subject: media: mtk-mdp: Do not zero reserved fields Core code already clears reserved fields of struct v4l2_pix_format_mplane, check commit 4e1e0eb0e074 ("media: v4l2-ioctl: Zero v4l2_plane_pix_format reserved fields"). Cc: Matthias Brugger Signed-off-by: Ricardo Ribalda Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index 724c7333b6e5..ace4528cdc5e 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -199,7 +199,6 @@ static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx, pix_mp->ycbcr_enc = ctx->ycbcr_enc; pix_mp->quantization = ctx->quant; } - memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); max_w = variant->pix_max->target_rot_dis_w; max_h = variant->pix_max->target_rot_dis_h; @@ -247,8 +246,6 @@ static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx, pix_mp->plane_fmt[i].bytesperline = bpl; if (pix_mp->plane_fmt[i].sizeimage < sizeimage) pix_mp->plane_fmt[i].sizeimage = sizeimage; - memset(pix_mp->plane_fmt[i].reserved, 0, - sizeof(pix_mp->plane_fmt[i].reserved)); mtk_mdp_dbg(2, "[%d] p%d, bpl:%d, sizeimage:%u (%u)", ctx->id, i, bpl, pix_mp->plane_fmt[i].sizeimage, sizeimage); } -- cgit v1.2.3 From 4701825e09873870c6854cebb020cbde7382d0ac Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 11 Jan 2021 15:54:40 +0100 Subject: media: fdp1: Do not zero reserved fields Core code already clears reserved fields of struct v4l2_pix_format_mplane, check commit 4e1e0eb0e074 ("media: v4l2-ioctl: Zero v4l2_plane_pix_format reserved fields"). Cc: Matthias Brugger Signed-off-by: Ricardo Ribalda Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_fdp1.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c index c9448de885b6..01c1fbb97bf6 100644 --- a/drivers/media/platform/rcar_fdp1.c +++ b/drivers/media/platform/rcar_fdp1.c @@ -1439,8 +1439,6 @@ static void fdp1_compute_stride(struct v4l2_pix_format_mplane *pix, pix->plane_fmt[i].sizeimage = pix->plane_fmt[i].bytesperline * pix->height / vsub; - memset(pix->plane_fmt[i].reserved, 0, - sizeof(pix->plane_fmt[i].reserved)); } if (fmt->num_planes == 3) { @@ -1448,8 +1446,6 @@ static void fdp1_compute_stride(struct v4l2_pix_format_mplane *pix, pix->plane_fmt[2].bytesperline = pix->plane_fmt[1].bytesperline; pix->plane_fmt[2].sizeimage = pix->plane_fmt[1].sizeimage; - memset(pix->plane_fmt[2].reserved, 0, - sizeof(pix->plane_fmt[2].reserved)); } } -- cgit v1.2.3 From 7b472a76fccd7f201d16affc09fcc28a06bee4a4 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 11 Jan 2021 15:54:41 +0100 Subject: media: jpu: Do not zero reserved fields Core code already clears reserved fields of struct v4l2_pix_format_mplane, check commit 4e1e0eb0e074 ("media: v4l2-ioctl: Zero v4l2_plane_pix_format reserved fields"). Cc: Mikhail Ulyanov Signed-off-by: Ricardo Ribalda Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_jpu.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 87466edf8a5e..a7c198c17deb 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -794,7 +794,6 @@ static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo, pix->colorspace = fmt->colorspace; pix->field = V4L2_FIELD_NONE; pix->num_planes = fmt->num_planes; - memset(pix->reserved, 0, sizeof(pix->reserved)); jpu_bound_align_image(&pix->width, JPU_WIDTH_MIN, JPU_WIDTH_MAX, fmt->h_align, &pix->height, JPU_HEIGHT_MIN, @@ -809,8 +808,6 @@ static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo, pix->plane_fmt[0].sizeimage = JPU_JPEG_HDR_SIZE + (JPU_JPEG_MAX_BYTES_PER_PIXEL * w * h); pix->plane_fmt[0].bytesperline = 0; - memset(pix->plane_fmt[0].reserved, 0, - sizeof(pix->plane_fmt[0].reserved)); } else { unsigned int i, bpl = 0; @@ -823,8 +820,6 @@ static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo, for (i = 0; i < pix->num_planes; ++i) { pix->plane_fmt[i].bytesperline = bpl; pix->plane_fmt[i].sizeimage = bpl * h * fmt->bpp[i] / 8; - memset(pix->plane_fmt[i].reserved, 0, - sizeof(pix->plane_fmt[i].reserved)); } } -- cgit v1.2.3 From b7d2c99a69a1de0e08e8c463b58ff2faf910a7cd Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 11 Jan 2021 15:54:43 +0100 Subject: media: ti-vpe: Do not zero reserved fields Core code already clears reserved fields of struct v4l2_pix_format_mplane, check commit 4e1e0eb0e074 ("media: v4l2-ioctl: Zero v4l2_plane_pix_format reserved fields"). Cc: Benoit Parrot Signed-off-by: Ricardo Ribalda Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 779dd74b82d0..10251b787674 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1683,7 +1683,6 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, } } - memset(pix->reserved, 0, sizeof(pix->reserved)); for (i = 0; i < pix->num_planes; i++) { plane_fmt = &pix->plane_fmt[i]; depth = fmt->vpdma_fmt[i]->depth; @@ -1713,7 +1712,6 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, plane_fmt->bytesperline * depth) >> 3; } - memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved)); } return 0; -- cgit v1.2.3 From ed2fb2f57430dfb030aaecd92bced752d999dd2f Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 11 Jan 2021 15:54:44 +0100 Subject: media: vicodec: Do not zero reserved fields Core code already clears reserved fields of struct v4l2_pix_format_mplane, check commit 4e1e0eb0e074 ("media: v4l2-ioctl: Zero v4l2_plane_pix_format reserved fields"). Signed-off-by: Ricardo Ribalda Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vicodec/vicodec-core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index 025f3ff77302..33f1c893c1b6 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -811,9 +811,6 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) pix_mp->xfer_func = ctx->state.xfer_func; pix_mp->ycbcr_enc = ctx->state.ycbcr_enc; pix_mp->quantization = ctx->state.quantization; - memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); - memset(pix_mp->plane_fmt[0].reserved, 0, - sizeof(pix_mp->plane_fmt[0].reserved)); break; default: return -EINVAL; @@ -886,8 +883,6 @@ static int vidioc_try_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) info->sizeimage_mult / info->sizeimage_div; if (pix_mp->pixelformat == V4L2_PIX_FMT_FWHT) plane->sizeimage += sizeof(struct fwht_cframe_hdr); - memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); - memset(plane->reserved, 0, sizeof(plane->reserved)); break; default: return -EINVAL; -- cgit v1.2.3 From f5cc14e420e8a8934f54913bbf933bbb8af060aa Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Tue, 12 Jan 2021 13:20:52 +0100 Subject: media: sun4i-csi: Do not zero reserved fields Core code already clears reserved fields of struct v4l2_pix_format_mplane, check commit 4e1e0eb0e074 ("media: v4l2-ioctl: Zero v4l2_plane_pix_format reserved fields"). Cc: Maxime Ripard Cc: Chen-Yu Tsai Reviewed-by: Kieran Bingham Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 1a2f65d83a6c..4785faddf630 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -113,8 +113,6 @@ static void _sun4i_csi_try_fmt(struct sun4i_csi *csi, pix->num_planes = _fmt->num_planes; pix->pixelformat = _fmt->fourcc; - memset(pix->reserved, 0, sizeof(pix->reserved)); - /* Align the width and height on the subsampling */ width = ALIGN(pix->width, _fmt->hsub); height = ALIGN(pix->height, _fmt->vsub); @@ -131,8 +129,6 @@ static void _sun4i_csi_try_fmt(struct sun4i_csi *csi, bpl = pix->width / hsub * _fmt->bpp[i] / 8; pix->plane_fmt[i].bytesperline = bpl; pix->plane_fmt[i].sizeimage = bpl * pix->height / vsub; - memset(pix->plane_fmt[i].reserved, 0, - sizeof(pix->plane_fmt[i].reserved)); } } -- cgit v1.2.3 From 204cffafd4f5cf3916d1b494280f946998f27440 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Tue, 12 Jan 2021 13:20:53 +0100 Subject: media: mtk-vcodec: Do not zero reserved fields Core code already clears reserved fields of struct v4l2_pix_format_mplane, check commit 4e1e0eb0e074 ("media: v4l2-ioctl: Zero v4l2_plane_pix_format reserved fields"). [hverkuil: drop unused variable i] Cc: Matthias Brugger Reviewed-by: Kieran Bingham Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c | 6 ------ drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c | 10 ---------- 2 files changed, 16 deletions(-) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index c768a587a944..56d86e59421e 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -657,7 +657,6 @@ static int vidioc_try_fmt(struct v4l2_format *f, const struct mtk_video_fmt *fmt) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - int i; pix_fmt_mp->field = V4L2_FIELD_NONE; @@ -715,12 +714,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, } } - for (i = 0; i < pix_fmt_mp->num_planes; i++) - memset(&(pix_fmt_mp->plane_fmt[i].reserved[0]), 0x0, - sizeof(pix_fmt_mp->plane_fmt[0].reserved)); - pix_fmt_mp->flags = 0; - memset(&pix_fmt_mp->reserved, 0x0, sizeof(pix_fmt_mp->reserved)); return 0; } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index 21de1431cfcb..8c917969c2f1 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -121,7 +121,6 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, return -EINVAL; f->pixelformat = formats[f->index].fourcc; - memset(f->reserved, 0, sizeof(f->reserved)); return 0; } @@ -252,7 +251,6 @@ static int vidioc_try_fmt(struct v4l2_format *f, const struct mtk_video_fmt *fmt) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - int i; pix_fmt_mp->field = V4L2_FIELD_NONE; @@ -320,13 +318,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, } } - for (i = 0; i < pix_fmt_mp->num_planes; i++) - memset(&(pix_fmt_mp->plane_fmt[i].reserved[0]), 0x0, - sizeof(pix_fmt_mp->plane_fmt[0].reserved)); - pix_fmt_mp->flags = 0; - memset(&pix_fmt_mp->reserved, 0x0, - sizeof(pix_fmt_mp->reserved)); return 0; } @@ -532,8 +524,6 @@ static int vidioc_venc_g_fmt(struct file *file, void *priv, for (i = 0; i < pix->num_planes; i++) { pix->plane_fmt[i].bytesperline = q_data->bytesperline[i]; pix->plane_fmt[i].sizeimage = q_data->sizeimage[i]; - memset(&(pix->plane_fmt[i].reserved[0]), 0x0, - sizeof(pix->plane_fmt[i].reserved)); } pix->flags = 0; -- cgit v1.2.3 From 873a623fd42de4b3dc910ce37e1b664de9e41235 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 15 Jan 2021 22:24:15 +0100 Subject: media: saa7164: switch from 'pci_' to 'dma_' API The wrappers in include/linux/pci-dma-compat.h should go away. The patch has been generated with the coccinelle script below and has been hand modified to replace GFP_ with a correct flag. It has been compile tested. When memory is allocated in 'saa7164_buffer_alloc()' GFP_KERNEL can be used because this function is already using this flag just a few lines above. @@ @@ - PCI_DMA_BIDIRECTIONAL + DMA_BIDIRECTIONAL @@ @@ - PCI_DMA_TODEVICE + DMA_TO_DEVICE @@ @@ - PCI_DMA_FROMDEVICE + DMA_FROM_DEVICE @@ @@ - PCI_DMA_NONE + DMA_NONE @@ expression e1, e2, e3; @@ - pci_alloc_consistent(e1, e2, e3) + dma_alloc_coherent(&e1->dev, e2, e3, GFP_) @@ expression e1, e2, e3; @@ - pci_zalloc_consistent(e1, e2, e3) + dma_alloc_coherent(&e1->dev, e2, e3, GFP_) @@ expression e1, e2, e3, e4; @@ - pci_free_consistent(e1, e2, e3, e4) + dma_free_coherent(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_map_single(e1, e2, e3, e4) + dma_map_single(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_unmap_single(e1, e2, e3, e4) + dma_unmap_single(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4, e5; @@ - pci_map_page(e1, e2, e3, e4, e5) + dma_map_page(&e1->dev, e2, e3, e4, e5) @@ expression e1, e2, e3, e4; @@ - pci_unmap_page(e1, e2, e3, e4) + dma_unmap_page(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_map_sg(e1, e2, e3, e4) + dma_map_sg(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_unmap_sg(e1, e2, e3, e4) + dma_unmap_sg(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_single_for_cpu(e1, e2, e3, e4) + dma_sync_single_for_cpu(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_single_for_device(e1, e2, e3, e4) + dma_sync_single_for_device(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_sg_for_cpu(e1, e2, e3, e4) + dma_sync_sg_for_cpu(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_sg_for_device(e1, e2, e3, e4) + dma_sync_sg_for_device(&e1->dev, e2, e3, e4) @@ expression e1, e2; @@ - pci_dma_mapping_error(e1, e2) + dma_mapping_error(&e1->dev, e2) @@ expression e1, e2; @@ - pci_set_dma_mask(e1, e2) + dma_set_mask(&e1->dev, e2) @@ expression e1, e2; @@ - pci_set_consistent_dma_mask(e1, e2) + dma_set_coherent_mask(&e1->dev, e2) Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7164/saa7164-buffer.c | 16 +++++++++------- drivers/media/pci/saa7164/saa7164-core.c | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/media/pci/saa7164/saa7164-buffer.c b/drivers/media/pci/saa7164/saa7164-buffer.c index 245d9db280aa..89c5b79a5b24 100644 --- a/drivers/media/pci/saa7164/saa7164-buffer.c +++ b/drivers/media/pci/saa7164/saa7164-buffer.c @@ -103,13 +103,13 @@ struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port, buf->pt_size = (SAA7164_PT_ENTRIES * sizeof(u64)) + 0x1000; /* Allocate contiguous memory */ - buf->cpu = pci_alloc_consistent(port->dev->pci, buf->pci_size, - &buf->dma); + buf->cpu = dma_alloc_coherent(&port->dev->pci->dev, buf->pci_size, + &buf->dma, GFP_KERNEL); if (!buf->cpu) goto fail1; - buf->pt_cpu = pci_alloc_consistent(port->dev->pci, buf->pt_size, - &buf->pt_dma); + buf->pt_cpu = dma_alloc_coherent(&port->dev->pci->dev, buf->pt_size, + &buf->pt_dma, GFP_KERNEL); if (!buf->pt_cpu) goto fail2; @@ -137,7 +137,8 @@ struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port, goto ret; fail2: - pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma); + dma_free_coherent(&port->dev->pci->dev, buf->pci_size, buf->cpu, + buf->dma); fail1: kfree(buf); @@ -160,8 +161,9 @@ int saa7164_buffer_dealloc(struct saa7164_buffer *buf) if (buf->flags != SAA7164_BUFFER_FREE) log_warn(" freeing a non-free buffer\n"); - pci_free_consistent(dev->pci, buf->pci_size, buf->cpu, buf->dma); - pci_free_consistent(dev->pci, buf->pt_size, buf->pt_cpu, buf->pt_dma); + dma_free_coherent(&dev->pci->dev, buf->pci_size, buf->cpu, buf->dma); + dma_free_coherent(&dev->pci->dev, buf->pt_size, buf->pt_cpu, + buf->pt_dma); kfree(buf); diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index f3a4e575a782..7973ae42873a 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -1273,7 +1273,7 @@ static int saa7164_initdev(struct pci_dev *pci_dev, pci_set_master(pci_dev); /* TODO */ - err = pci_set_dma_mask(pci_dev, 0xffffffff); + err = dma_set_mask(&pci_dev->dev, 0xffffffff); if (err) { printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); goto fail_irq; -- cgit v1.2.3 From b2de3643c5024fc4fd128ba7767c7fb8b714bea7 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 16 Jan 2021 22:21:46 +0100 Subject: media: cx25821: Fix a bug when reallocating some dma memory This function looks like a realloc. However, if 'risc->cpu != NULL', the memory will be freed, but never reallocated with the bigger 'size'. Explicitly set 'risc->cpu' to NULL, so that the reallocation is correctly performed a few lines below. [hverkuil: NULL != risc->cpu -> risc->cpu] Fixes: 5ede94c70553 ("[media] cx25821: remove bogus btcx_risc dependency) Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx25821/cx25821-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 6f8ffab8840f..07b6d0c49bbf 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -976,8 +976,10 @@ int cx25821_riscmem_alloc(struct pci_dev *pci, __le32 *cpu; dma_addr_t dma = 0; - if (NULL != risc->cpu && risc->size < size) + if (risc->cpu && risc->size < size) { pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); + risc->cpu = NULL; + } if (NULL == risc->cpu) { cpu = pci_zalloc_consistent(pci, size, &dma); if (NULL == cpu) -- cgit v1.2.3 From a04e187d231086a1313fd635ac42bdbc997137ad Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sun, 17 Jan 2021 23:21:38 +0100 Subject: media: mtk-vcodec: fix argument used when DEBUG is defined When DEBUG is defined this error occurs drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c:306:41: error: ‘i’ undeclared (first use in this function) mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[VENC_SYS]); Reviewing the old line mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[i]); All the i's need to be changed to VENC_SYS. Fix a similar error for VENC_LT_SYS. Fixes: 0dc4b3286125 ("media: mtk-vcodec: venc: support SCP firmware") Signed-off-by: Tom Rix Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c index dfb42e19bf81..be3842e6ca47 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c @@ -303,7 +303,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) ret = PTR_ERR((__force void *)dev->reg_base[VENC_SYS]); goto err_res; } - mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[VENC_SYS]); + mtk_v4l2_debug(2, "reg[%d] base=0x%p", VENC_SYS, dev->reg_base[VENC_SYS]); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { @@ -332,7 +332,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) ret = PTR_ERR((__force void *)dev->reg_base[VENC_LT_SYS]); goto err_res; } - mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[VENC_LT_SYS]); + mtk_v4l2_debug(2, "reg[%d] base=0x%p", VENC_LT_SYS, dev->reg_base[VENC_LT_SYS]); dev->enc_lt_irq = platform_get_irq(pdev, 1); irq_set_status_flags(dev->enc_lt_irq, IRQ_NOAUTOEN); -- cgit v1.2.3 From 031b9212eeee365443aaef013360ea6cded7b2c4 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Mon, 18 Jan 2021 14:45:13 +0100 Subject: media: pxa_camera: declare variable when DEBUG is defined When DEBUG is defined this error occurs drivers/media/platform/pxa_camera.c:1410:7: error: ‘i’ undeclared (first use in this function) for (i = 0; i < vb->num_planes; i++) ^ The variable 'i' is missing, so declare it. Fixes: 6f28435d1c15 ("[media] media: platform: pxa_camera: trivial move of functions") Signed-off-by: Tom Rix Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index b664ce7558a1..75fad9689c90 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -1386,6 +1386,9 @@ static int pxac_vb2_prepare(struct vb2_buffer *vb) struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vb->vb2_queue); struct pxa_buffer *buf = vb2_to_pxa_buffer(vb); int ret = 0; +#ifdef DEBUG + int i; +#endif switch (pcdev->channels) { case 1: -- cgit v1.2.3 From 319c4bd41a36095854ce0352abc48943a2d00053 Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Thu, 14 Jan 2021 19:01:47 +0100 Subject: media: v4l2-ioctl: print capabilities in v4l_print_create_buffers() Print capabilities field from struct v4l2_create_buffers for better debugging. Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 8d5d9c39c162..31d1342e61e8 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -518,9 +518,9 @@ static void v4l_print_create_buffers(const void *arg, bool write_only) { const struct v4l2_create_buffers *p = arg; - pr_cont("index=%d, count=%d, memory=%s, ", - p->index, p->count, - prt_names(p->memory, v4l2_memory_names)); + pr_cont("index=%d, count=%d, memory=%s, capabilities=0x%08x, ", + p->index, p->count, prt_names(p->memory, v4l2_memory_names), + p->capabilities); v4l_print_format(&p->format, write_only); } -- cgit v1.2.3 From b7da24739f31f96e3ba1f3c8d9cf83851134f9ce Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Thu, 14 Jan 2021 19:01:49 +0100 Subject: media: videobuf2-v4l2: remove redundant error test request_fd is validated under media_request_get_by_fd() just below this check. Thus remove it. Suggested-by: Tomasz Figa Signed-off-by: Helen Koike Reviewed-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 96d3b2b2aa31..bb642c0775d1 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -488,11 +488,6 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md !q->ops->buf_out_validate)) return -EINVAL; - if (b->request_fd < 0) { - dprintk(q, 1, "%s: request_fd < 0\n", opname); - return -EINVAL; - } - req = media_request_get_by_fd(mdev, b->request_fd); if (IS_ERR(req)) { dprintk(q, 1, "%s: invalid request_fd\n", opname); -- cgit v1.2.3 From c4f115355c53a0df9ac3a8a33f31ff23970c2737 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Fri, 15 Jan 2021 01:21:45 +0100 Subject: media: rcar-vin: Do not try to stop stream if not running Do not attempt to stop the streaming if the stream is not running. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 48280ddb15b9..f30dafbdf61c 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -1301,6 +1301,11 @@ void rvin_stop_streaming(struct rvin_dev *vin) spin_lock_irqsave(&vin->qlock, flags); + if (vin->state == STOPPED) { + spin_unlock_irqrestore(&vin->qlock, flags); + return; + } + vin->state = STOPPING; /* Wait until only scratch buffer is used, max 3 interrupts. */ -- cgit v1.2.3 From bdd59592b29b4047f450131d2490702332a5a467 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Fri, 15 Jan 2021 01:21:46 +0100 Subject: media: rcar-vin: Route events to correct video device The event route for VIN running with a media controller (Gen3) is incorrect as all events are only routed to the video device that are used to register the async notifier. Remedy this be examining which subdevice generated the event and route it to all VIN(s) that are connected to that subdevice. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 42 +++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index e6ea2b7991b8..457a65bf6b66 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -966,12 +966,9 @@ void rvin_v4l2_unregister(struct rvin_dev *vin) video_unregister_device(&vin->vdev); } -static void rvin_notify(struct v4l2_subdev *sd, - unsigned int notification, void *arg) +static void rvin_notify_video_device(struct rvin_dev *vin, + unsigned int notification, void *arg) { - struct rvin_dev *vin = - container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev); - switch (notification) { case V4L2_DEVICE_NOTIFY_EVENT: v4l2_event_queue(&vin->vdev, arg); @@ -981,6 +978,41 @@ static void rvin_notify(struct v4l2_subdev *sd, } } +static void rvin_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct v4l2_subdev *remote; + struct rvin_group *group; + struct media_pad *pad; + struct rvin_dev *vin = + container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev); + unsigned int i; + + /* If no media controller, no need to route the event. */ + if (!vin->info->use_mc) { + rvin_notify_video_device(vin, notification, arg); + return; + } + + group = vin->group; + + for (i = 0; i < RCAR_VIN_NUM; i++) { + vin = group->vin[i]; + if (!vin) + continue; + + pad = media_entity_remote_pad(&vin->pad); + if (!pad) + continue; + + remote = media_entity_to_v4l2_subdev(pad->entity); + if (remote != sd) + continue; + + rvin_notify_video_device(vin, notification, arg); + } +} + int rvin_v4l2_register(struct rvin_dev *vin) { struct video_device *vdev = &vin->vdev; -- cgit v1.2.3 From 65fba0b15e99606f094d688bc562168834d214dd Mon Sep 17 00:00:00 2001 From: Maxim Plotnikov Date: Tue, 1 Dec 2020 01:14:27 +0100 Subject: media: Fix RTL2832 not depending on REGMAP_I2C Prevents this compile time error: ERROR: modpost: "__regmap_init_i2c" [drivers/media/dvb-frontends/rtl2832.ko] undefined! Signed-off-by: Maxim Plotnikov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 643b851a6b60..6a1ef277fea4 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -515,7 +515,7 @@ config DVB_RTL2830 config DVB_RTL2832 tristate "Realtek RTL2832 DVB-T" depends on DVB_CORE && I2C && I2C_MUX - select REGMAP + select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. -- cgit v1.2.3 From 711ae4f6f3535c1638836e6c52db03bc324a8b4c Mon Sep 17 00:00:00 2001 From: ivan tkachenko Date: Wed, 23 Dec 2020 22:43:54 +0100 Subject: media: hdmi: cec: replace broken link to HDMI specs Current link died, according to Wayback Machine, back in 2017. And the website is completely down since 2019. Moreover, there was a custom cover on that PDF, i.e. it was modified. According to HDMI licence agreement (LA), HDMI specification and technical information are supposed to be hosted on www.hdmi.org exclusively, and not redistributed by third-parties. Sure, there are still many more or less reliable "mirrors" out there with a direct download straight from a search engine's page. However, for example, from FPGA4fun[1] website it was removed "per HDMI LA request". Unfortunately, the official download page is protected by email CAPTCHA, but that seems to be the only legit way to obtain a copy. [1] https://www.fpga4fun.com/HDMI.html Signed-off-by: ivan tkachenko Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/media/cec-core.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/media/cec-core.rst b/Documentation/driver-api/media/cec-core.rst index a26dc87eee8f..56345eae9a26 100644 --- a/Documentation/driver-api/media/cec-core.rst +++ b/Documentation/driver-api/media/cec-core.rst @@ -26,7 +26,7 @@ It is documented in the HDMI 1.4 specification with the new 2.0 bits documented in the HDMI 2.0 specification. But for most of the features the freely available HDMI 1.3a specification is sufficient: -http://www.microprocessor.org/HDMISpecification13a.pdf +https://www.hdmi.org/spec/index CEC Adapter Interface -- cgit v1.2.3 From b05bb3bfa583dd9ea6f8b255b55d0d97d8ba370d Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 15 Jan 2021 22:34:03 +0100 Subject: media: smipcie: switch from 'pci_' to 'dma_' API The wrappers in include/linux/pci-dma-compat.h should go away. The patch has been generated with the coccinelle script below and has been hand modified to replace GFP_ with a correct flag. It has been compile tested. When memory is allocated in 'smi_port_init()' GFP_KERNEL can be used because this function is called only from the probe function and no lock is taken in between. The call chain is: smi_probe() --> smi_port_attach() --> smi_port_init() @@ @@ - PCI_DMA_BIDIRECTIONAL + DMA_BIDIRECTIONAL @@ @@ - PCI_DMA_TODEVICE + DMA_TO_DEVICE @@ @@ - PCI_DMA_FROMDEVICE + DMA_FROM_DEVICE @@ @@ - PCI_DMA_NONE + DMA_NONE @@ expression e1, e2, e3; @@ - pci_alloc_consistent(e1, e2, e3) + dma_alloc_coherent(&e1->dev, e2, e3, GFP_) @@ expression e1, e2, e3; @@ - pci_zalloc_consistent(e1, e2, e3) + dma_alloc_coherent(&e1->dev, e2, e3, GFP_) @@ expression e1, e2, e3, e4; @@ - pci_free_consistent(e1, e2, e3, e4) + dma_free_coherent(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_map_single(e1, e2, e3, e4) + dma_map_single(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_unmap_single(e1, e2, e3, e4) + dma_unmap_single(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4, e5; @@ - pci_map_page(e1, e2, e3, e4, e5) + dma_map_page(&e1->dev, e2, e3, e4, e5) @@ expression e1, e2, e3, e4; @@ - pci_unmap_page(e1, e2, e3, e4) + dma_unmap_page(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_map_sg(e1, e2, e3, e4) + dma_map_sg(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_unmap_sg(e1, e2, e3, e4) + dma_unmap_sg(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_single_for_cpu(e1, e2, e3, e4) + dma_sync_single_for_cpu(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_single_for_device(e1, e2, e3, e4) + dma_sync_single_for_device(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_sg_for_cpu(e1, e2, e3, e4) + dma_sync_sg_for_cpu(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_sg_for_device(e1, e2, e3, e4) + dma_sync_sg_for_device(&e1->dev, e2, e3, e4) @@ expression e1, e2; @@ - pci_dma_mapping_error(e1, e2) + dma_mapping_error(&e1->dev, e2) @@ expression e1, e2; @@ - pci_set_dma_mask(e1, e2) + dma_set_mask(&e1->dev, e2) @@ expression e1, e2; @@ - pci_set_consistent_dma_mask(e1, e2) + dma_set_coherent_mask(&e1->dev, e2) Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/smipcie/smipcie-main.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/media/pci/smipcie/smipcie-main.c b/drivers/media/pci/smipcie/smipcie-main.c index e7604b7ecc8d..0c300d019d9c 100644 --- a/drivers/media/pci/smipcie/smipcie-main.c +++ b/drivers/media/pci/smipcie/smipcie-main.c @@ -351,13 +351,15 @@ static void smi_dma_xfer(struct tasklet_struct *t) static void smi_port_dma_free(struct smi_port *port) { if (port->cpu_addr[0]) { - pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE, - port->cpu_addr[0], port->dma_addr[0]); + dma_free_coherent(&port->dev->pci_dev->dev, + SMI_TS_DMA_BUF_SIZE, port->cpu_addr[0], + port->dma_addr[0]); port->cpu_addr[0] = NULL; } if (port->cpu_addr[1]) { - pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE, - port->cpu_addr[1], port->dma_addr[1]); + dma_free_coherent(&port->dev->pci_dev->dev, + SMI_TS_DMA_BUF_SIZE, port->cpu_addr[1], + port->dma_addr[1]); port->cpu_addr[1] = NULL; } } @@ -398,9 +400,10 @@ static int smi_port_init(struct smi_port *port, int dmaChanUsed) } if (port->_dmaInterruptCH0) { - port->cpu_addr[0] = pci_alloc_consistent(port->dev->pci_dev, - SMI_TS_DMA_BUF_SIZE, - &port->dma_addr[0]); + port->cpu_addr[0] = dma_alloc_coherent(&port->dev->pci_dev->dev, + SMI_TS_DMA_BUF_SIZE, + &port->dma_addr[0], + GFP_KERNEL); if (!port->cpu_addr[0]) { dev_err(&port->dev->pci_dev->dev, "Port[%d] DMA CH0 memory allocation failed!\n", @@ -410,9 +413,10 @@ static int smi_port_init(struct smi_port *port, int dmaChanUsed) } if (port->_dmaInterruptCH1) { - port->cpu_addr[1] = pci_alloc_consistent(port->dev->pci_dev, - SMI_TS_DMA_BUF_SIZE, - &port->dma_addr[1]); + port->cpu_addr[1] = dma_alloc_coherent(&port->dev->pci_dev->dev, + SMI_TS_DMA_BUF_SIZE, + &port->dma_addr[1], + GFP_KERNEL); if (!port->cpu_addr[1]) { dev_err(&port->dev->pci_dev->dev, "Port[%d] DMA CH1 memory allocation failed!\n", @@ -963,7 +967,7 @@ static int smi_probe(struct pci_dev *pdev, const struct pci_device_id *id) } /* should we set to 32bit DMA? */ - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret < 0) goto err_pci_iounmap; -- cgit v1.2.3 From 9d3b7ca42d6f70556c10ce3f6bdb32457077d947 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sat, 16 Jan 2021 17:40:38 +0100 Subject: media: imx6-mipi-csi2: Call remote subdev get_mbus_config to get active lanes Currently, the CSI2 subdevice is using the data-lanes from the neareast endpoint to config the CSI2 lanes. While this may work, the proper way to configure the hardware is to obtain the remote subdevice in v4l2_async_notifier_operations.bound(), and then call get_mbus_config using the remote subdevice to get the active lanes. Signed-off-by: Ezequiel Garcia Reviewed-by: Laurent Pinchart Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx6-mipi-csi2.c | 108 +++++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 12 deletions(-) diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index 94d87d27d389..6b58899dcefe 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -42,7 +42,10 @@ struct csi2_dev { struct clk *pllref_clk; struct clk *pix_clk; /* what is this? */ void __iomem *base; - struct v4l2_fwnode_bus_mipi_csi2 bus; + + struct v4l2_subdev *remote; + unsigned int remote_pad; + unsigned short data_lanes; /* lock to protect all members below */ struct mutex lock; @@ -138,10 +141,8 @@ static void csi2_enable(struct csi2_dev *csi2, bool enable) } } -static void csi2_set_lanes(struct csi2_dev *csi2) +static void csi2_set_lanes(struct csi2_dev *csi2, unsigned int lanes) { - int lanes = csi2->bus.num_data_lanes; - writel(lanes - 1, csi2->base + CSI2_N_LANES); } @@ -250,13 +251,12 @@ static int __maybe_unused csi2_dphy_wait_ulp(struct csi2_dev *csi2) } /* Waits for low-power LP-11 state on data and clock lanes. */ -static void csi2_dphy_wait_stopstate(struct csi2_dev *csi2) +static void csi2_dphy_wait_stopstate(struct csi2_dev *csi2, unsigned int lanes) { u32 mask, reg; int ret; - mask = PHY_STOPSTATECLK | (((1 << csi2->bus.num_data_lanes) - 1) << - PHY_STOPSTATEDATA_BIT); + mask = PHY_STOPSTATECLK | (((1 << lanes) - 1) << PHY_STOPSTATEDATA_BIT); ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg, (reg & mask) == mask, 0, 500000); @@ -300,8 +300,65 @@ static void csi2ipu_gasket_init(struct csi2_dev *csi2) writel(reg, csi2->base + CSI2IPU_GASKET); } +static int csi2_get_active_lanes(struct csi2_dev *csi2, unsigned int *lanes) +{ + struct v4l2_mbus_config mbus_config = { 0 }; + unsigned int num_lanes = UINT_MAX; + int ret; + + *lanes = csi2->data_lanes; + + ret = v4l2_subdev_call(csi2->remote, pad, get_mbus_config, + csi2->remote_pad, &mbus_config); + if (ret == -ENOIOCTLCMD) { + dev_dbg(csi2->dev, "No remote mbus configuration available\n"); + return 0; + } + + if (ret) { + dev_err(csi2->dev, "Failed to get remote mbus configuration\n"); + return ret; + } + + if (mbus_config.type != V4L2_MBUS_CSI2_DPHY) { + dev_err(csi2->dev, "Unsupported media bus type %u\n", + mbus_config.type); + return -EINVAL; + } + + switch (mbus_config.flags & V4L2_MBUS_CSI2_LANES) { + case V4L2_MBUS_CSI2_1_LANE: + num_lanes = 1; + break; + case V4L2_MBUS_CSI2_2_LANE: + num_lanes = 2; + break; + case V4L2_MBUS_CSI2_3_LANE: + num_lanes = 3; + break; + case V4L2_MBUS_CSI2_4_LANE: + num_lanes = 4; + break; + default: + num_lanes = csi2->data_lanes; + break; + } + + if (num_lanes > csi2->data_lanes) { + dev_err(csi2->dev, + "Unsupported mbus config: too many data lanes %u\n", + num_lanes); + return -EINVAL; + } + + *lanes = num_lanes; + + return 0; +} + static int csi2_start(struct csi2_dev *csi2) { + unsigned int lanes; int ret; ret = clk_prepare_enable(csi2->pix_clk); @@ -316,12 +373,16 @@ static int csi2_start(struct csi2_dev *csi2) if (ret) goto err_disable_clk; + ret = csi2_get_active_lanes(csi2, &lanes); + if (ret) + goto err_disable_clk; + /* Step 4 */ - csi2_set_lanes(csi2); + csi2_set_lanes(csi2, lanes); csi2_enable(csi2, true); /* Step 5 */ - csi2_dphy_wait_stopstate(csi2); + csi2_dphy_wait_stopstate(csi2, lanes); /* Step 6 */ ret = v4l2_subdev_call(csi2->src_sd, video, s_stream, 1); @@ -544,12 +605,35 @@ static int csi2_notify_bound(struct v4l2_async_notifier *notifier, { struct csi2_dev *csi2 = notifier_to_dev(notifier); struct media_pad *sink = &csi2->sd.entity.pads[CSI2_SINK_PAD]; + int pad; + + pad = media_entity_get_fwnode_pad(&sd->entity, asd->match.fwnode, + MEDIA_PAD_FL_SOURCE); + if (pad < 0) { + dev_err(csi2->dev, "Failed to find pad for %s\n", sd->name); + return pad; + } + + csi2->remote = sd; + csi2->remote_pad = pad; + + dev_dbg(csi2->dev, "Bound %s pad: %d\n", sd->name, pad); return v4l2_create_fwnode_links_to_pad(sd, sink); } +static void csi2_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct csi2_dev *csi2 = notifier_to_dev(notifier); + + csi2->remote = NULL; +} + static const struct v4l2_async_notifier_operations csi2_notify_ops = { .bound = csi2_notify_bound, + .unbind = csi2_notify_unbind, }; static int csi2_async_register(struct csi2_dev *csi2) @@ -572,10 +656,10 @@ static int csi2_async_register(struct csi2_dev *csi2) if (ret) goto err_parse; - csi2->bus = vep.bus.mipi_csi2; + csi2->data_lanes = vep.bus.mipi_csi2.num_data_lanes; - dev_dbg(csi2->dev, "data lanes: %d\n", csi2->bus.num_data_lanes); - dev_dbg(csi2->dev, "flags: 0x%08x\n", csi2->bus.flags); + dev_dbg(csi2->dev, "data lanes: %d\n", vep.bus.mipi_csi2.num_data_lanes); + dev_dbg(csi2->dev, "flags: 0x%08x\n", vep.bus.mipi_csi2.flags); asd = kzalloc(sizeof(*asd), GFP_KERNEL); if (!asd) { -- cgit v1.2.3 From ea354b6ddd6f09be29424f41fa75a3e637fea234 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 21 Jan 2021 07:44:00 +0100 Subject: media: zr364xx: fix memory leaks in probe() Syzbot discovered that the probe error handling doesn't clean up the resources allocated in zr364xx_board_init(). There are several related bugs in this code so I have re-written the error handling. 1) Introduce a new function zr364xx_board_uninit() which cleans up the resources in zr364xx_board_init(). 2) In zr364xx_board_init() if the call to zr364xx_start_readpipe() fails then release the "cam->buffer.frame[i].lpvbits" memory before returning. This way every function either allocates everything successfully or it cleans up after itself. 3) Re-write the probe function so that each failure path goto frees the most recent allocation. That way we don't free anything before it has been allocated and we can also verify that everything is freed. 4) Originally, in the probe function the "cam->v4l2_dev.release" pointer was set to "zr364xx_release" near the start but I moved that assignment to the end, after everything had succeeded. The release function was never actually called during the probe cleanup process, but with this change I wanted to make it clear that we don't want to call zr364xx_release() until everything is allocated successfully. Next I re-wrote the zr364xx_release() function. Ideally this would have been a simple matter of copy and pasting the cleanup code from probe and adding an additional call to video_unregister_device(). But there are a couple quirks to note. 1) The probe function does not call videobuf_mmap_free() and I don't know where the videobuf_mmap is allocated. I left the code as-is to avoid introducing a bug in code I don't understand. 2) The zr364xx_board_uninit() has a call to zr364xx_stop_readpipe() which is a change from the original behavior with regards to unloading the driver. Calling zr364xx_stop_readpipe() on a stopped pipe is not a problem so this is safe and is potentially a bugfix. Reported-by: syzbot+b4d54814b339b5c6bbd4@syzkaller.appspotmail.com Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/zr364xx/zr364xx.c | 49 +++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 1e1c6b4d1874..d29b861367ea 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -1181,15 +1181,11 @@ out: return err; } -static void zr364xx_release(struct v4l2_device *v4l2_dev) +static void zr364xx_board_uninit(struct zr364xx_camera *cam) { - struct zr364xx_camera *cam = - container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev); unsigned long i; - v4l2_device_unregister(&cam->v4l2_dev); - - videobuf_mmap_free(&cam->vb_vidq); + zr364xx_stop_readpipe(cam); /* release sys buffers */ for (i = 0; i < FRAMES; i++) { @@ -1200,9 +1196,19 @@ static void zr364xx_release(struct v4l2_device *v4l2_dev) cam->buffer.frame[i].lpvbits = NULL; } - v4l2_ctrl_handler_free(&cam->ctrl_handler); /* release transfer buffer */ kfree(cam->pipe->transfer_buffer); +} + +static void zr364xx_release(struct v4l2_device *v4l2_dev) +{ + struct zr364xx_camera *cam = + container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev); + + videobuf_mmap_free(&cam->vb_vidq); + v4l2_ctrl_handler_free(&cam->ctrl_handler); + zr364xx_board_uninit(cam); + v4l2_device_unregister(&cam->v4l2_dev); kfree(cam); } @@ -1376,11 +1382,14 @@ static int zr364xx_board_init(struct zr364xx_camera *cam) /* start read pipe */ err = zr364xx_start_readpipe(cam); if (err) - goto err_free; + goto err_free_frames; DBG(": board initialized\n"); return 0; +err_free_frames: + for (i = 0; i < FRAMES; i++) + vfree(cam->buffer.frame[i].lpvbits); err_free: kfree(cam->pipe->transfer_buffer); cam->pipe->transfer_buffer = NULL; @@ -1409,12 +1418,10 @@ static int zr364xx_probe(struct usb_interface *intf, if (!cam) return -ENOMEM; - cam->v4l2_dev.release = zr364xx_release; err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); if (err < 0) { dev_err(&udev->dev, "couldn't register v4l2_device\n"); - kfree(cam); - return err; + goto free_cam; } hdl = &cam->ctrl_handler; v4l2_ctrl_handler_init(hdl, 1); @@ -1423,7 +1430,7 @@ static int zr364xx_probe(struct usb_interface *intf, if (hdl->error) { err = hdl->error; dev_err(&udev->dev, "couldn't register control\n"); - goto fail; + goto unregister; } /* save the init method used by this camera */ cam->method = id->driver_info; @@ -1496,7 +1503,7 @@ static int zr364xx_probe(struct usb_interface *intf, if (!cam->read_endpoint) { err = -ENOMEM; dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); - goto fail; + goto unregister; } /* v4l */ @@ -1507,10 +1514,11 @@ static int zr364xx_probe(struct usb_interface *intf, /* load zr364xx board specific */ err = zr364xx_board_init(cam); - if (!err) - err = v4l2_ctrl_handler_setup(hdl); if (err) - goto fail; + goto unregister; + err = v4l2_ctrl_handler_setup(hdl); + if (err) + goto board_uninit; spin_lock_init(&cam->slock); @@ -1525,16 +1533,21 @@ static int zr364xx_probe(struct usb_interface *intf, err = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1); if (err) { dev_err(&udev->dev, "video_register_device failed\n"); - goto fail; + goto free_handler; } + cam->v4l2_dev.release = zr364xx_release; dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n", video_device_node_name(&cam->vdev)); return 0; -fail: +free_handler: v4l2_ctrl_handler_free(hdl); +board_uninit: + zr364xx_board_uninit(cam); +unregister: v4l2_device_unregister(&cam->v4l2_dev); +free_cam: kfree(cam); return err; } -- cgit v1.2.3 From b400b6f28af040d55b4eb397ea7b8ece368c6b12 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 20 Nov 2020 11:57:48 +0100 Subject: media: uvcvideo: Force UVC version to 1.0a for 1bcf:0b40 The Shenzhen Aoni Electronic Co.,Ltd 2K FHD camera reports a UVC 1.10 version, but implements UVC 1.0a as shown by the UVC probe control being 26 bytes long. Force the UVC version for that device. Signed-off-by: Laurent Pinchart Reported-by: Doncho Minkov Tested-by: Doncho Minkov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 17 +++++++++++++++++ drivers/media/usb/uvc/uvcvideo.h | 1 + 2 files changed, 18 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index ddb9eaa11be7..ae970f19bfca 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2274,6 +2274,12 @@ static int uvc_probe(struct usb_interface *intf, "linux-uvc-devel mailing list.\n"); } + if (dev->info->uvc_version) { + dev->uvc_version = dev->info->uvc_version; + uvc_printk(KERN_INFO, "Forcing UVC version to %u.%02x\n", + dev->uvc_version >> 8, dev->uvc_version & 0xff); + } + /* Register the V4L2 device. */ if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) goto error; @@ -2923,6 +2929,17 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, + /* Shenzhen Aoni Electronic Co.,Ltd 2K FHD camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1bcf, + .idProduct = 0x0b40, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){ + .uvc_version = 0x010a, + } }, /* SiGma Micro USB Web Camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index a3dfacf069c4..8ec9eca07f06 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -635,6 +635,7 @@ static inline u32 uvc_urb_index(const struct uvc_urb *uvc_urb) struct uvc_device_info { u32 quirks; u32 meta_format; + u16 uvc_version; }; struct uvc_device { -- cgit v1.2.3 From dc9455ffae02d7b7fb51ba1e007fffcb9dc5d890 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 20 Dec 2020 15:11:13 +0100 Subject: media: uvcvideo: Accept invalid bFormatIndex and bFrameIndex values The Renkforce RF AC4K 300 Action Cam 4K reports invalid bFormatIndex and bFrameIndex values when negotiating the video probe and commit controls. The UVC descriptors report a single supported format and frame size, with bFormatIndex and bFrameIndex both equal to 2, but the video probe and commit controls report bFormatIndex and bFrameIndex set to 1. The device otherwise operates correctly, but the driver rejects the values and fails the format try operation. Fix it by ignoring the invalid indices, and assuming that the format and frame requested by the driver are accepted by the device. Link: https://bugzilla.kernel.org/show_bug.cgi?id=210767 Fixes: 8a652a17e3c0 ("media: uvcvideo: Ensure all probed info is returned to v4l2") Reported-by: Till Dörges Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_v4l2.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index fa06bfa174ad..c7172b8952a9 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -248,7 +248,9 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, goto done; /* After the probe, update fmt with the values returned from - * negotiation with the device. + * negotiation with the device. Some devices return invalid bFormatIndex + * and bFrameIndex values, in which case we can only assume they have + * accepted the requested format as-is. */ for (i = 0; i < stream->nformats; ++i) { if (probe->bFormatIndex == stream->format[i].index) { @@ -257,11 +259,10 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, } } - if (i == stream->nformats) { - uvc_trace(UVC_TRACE_FORMAT, "Unknown bFormatIndex %u\n", + if (i == stream->nformats) + uvc_trace(UVC_TRACE_FORMAT, + "Unknown bFormatIndex %u, using default\n", probe->bFormatIndex); - return -EINVAL; - } for (i = 0; i < format->nframes; ++i) { if (probe->bFrameIndex == format->frame[i].bFrameIndex) { @@ -270,11 +271,10 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, } } - if (i == format->nframes) { - uvc_trace(UVC_TRACE_FORMAT, "Unknown bFrameIndex %u\n", + if (i == format->nframes) + uvc_trace(UVC_TRACE_FORMAT, + "Unknown bFrameIndex %u, using default\n", probe->bFrameIndex); - return -EINVAL; - } fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; -- cgit v1.2.3 From 351509c604dcb065305a165d7552058c2cbc447d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 23 Dec 2020 14:35:17 +0100 Subject: media: uvcvideo: Move guid to entity Instead of having multiple copies of the entity guid on the code, move it to the entity structure. Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 30 ++++-------------------------- drivers/media/usb/uvc/uvc_driver.c | 25 +++++++++++++++++++++++-- drivers/media/usb/uvc/uvcvideo.h | 2 +- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 011e69427b7c..9f6174a10e73 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -826,31 +826,10 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping, * Terminal and unit management */ -static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; -static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; -static const u8 uvc_media_transport_input_guid[16] = - UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; - static int uvc_entity_match_guid(const struct uvc_entity *entity, - const u8 guid[16]) + const u8 guid[16]) { - switch (UVC_ENTITY_TYPE(entity)) { - case UVC_ITT_CAMERA: - return memcmp(uvc_camera_guid, guid, 16) == 0; - - case UVC_ITT_MEDIA_TRANSPORT_INPUT: - return memcmp(uvc_media_transport_input_guid, guid, 16) == 0; - - case UVC_VC_PROCESSING_UNIT: - return memcmp(uvc_processing_guid, guid, 16) == 0; - - case UVC_VC_EXTENSION_UNIT: - return memcmp(entity->extension.guidExtensionCode, - guid, 16) == 0; - - default: - return 0; - } + return memcmp(entity->guid, guid, sizeof(entity->guid)) == 0; } /* ------------------------------------------------------------------------ @@ -1776,8 +1755,7 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, if (data == NULL) return -ENOMEM; - memcpy(info->entity, ctrl->entity->extension.guidExtensionCode, - sizeof(info->entity)); + memcpy(info->entity, ctrl->entity->guid, sizeof(info->entity)); info->index = ctrl->index; info->selector = ctrl->index + 1; @@ -1883,7 +1861,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, if (!found) { uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n", - entity->extension.guidExtensionCode, xqry->selector); + entity->guid, xqry->selector); return -ENOENT; } diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index ae970f19bfca..be9700b422f6 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1019,6 +1019,11 @@ error: return ret; } +static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; +static const u8 uvc_media_transport_input_guid[16] = + UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; +static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; + static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, unsigned int num_pads, unsigned int extra_size) { @@ -1038,6 +1043,22 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, entity->id = id; entity->type = type; + /* + * Set the GUID for standard entity types. For extension units, the GUID + * is initialized by the caller. + */ + switch (type) { + case UVC_ITT_CAMERA: + memcpy(entity->guid, uvc_camera_guid, 16); + break; + case UVC_ITT_MEDIA_TRANSPORT_INPUT: + memcpy(entity->guid, uvc_media_transport_input_guid, 16); + break; + case UVC_VC_PROCESSING_UNIT: + memcpy(entity->guid, uvc_processing_guid, 16); + break; + } + entity->num_links = 0; entity->num_pads = num_pads; entity->pads = ((void *)(entity + 1)) + extra_size; @@ -1109,7 +1130,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, if (unit == NULL) return -ENOMEM; - memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); + memcpy(unit->guid, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; @@ -1368,7 +1389,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, if (unit == NULL) return -ENOMEM; - memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); + memcpy(unit->guid, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 8ec9eca07f06..459b2450accf 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -304,6 +304,7 @@ struct uvc_entity { u8 id; u16 type; char name[64]; + u8 guid[16]; /* Media controller-related fields. */ struct video_device *vdev; @@ -342,7 +343,6 @@ struct uvc_entity { } selector; struct { - u8 guidExtensionCode[16]; u8 bNumControls; u8 bControlSize; u8 *bmControls; -- cgit v1.2.3 From cae79e50d1222010fde8c522410c315f74d35c40 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 23 Dec 2020 14:35:18 +0100 Subject: media: uvcvideo: Allow extra entities Increase the size of the id, to avoid collisions with entities implemented by the driver that are not part of the UVC device. Entities exposed by the UVC device use IDs 0-255, extra entities implemented by the driver (such as the GPIO entity) use IDs 256 and up. Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 2 +- drivers/media/usb/uvc/uvcvideo.h | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index be9700b422f6..dd2e4998e564 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1024,7 +1024,7 @@ static const u8 uvc_media_transport_input_guid[16] = UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; -static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, +static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id, unsigned int num_pads, unsigned int extra_size) { struct uvc_entity *entity; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 459b2450accf..2643d9adfd4a 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -301,7 +301,12 @@ struct uvc_entity { * chain. */ unsigned int flags; - u8 id; + /* + * Entities exposed by the UVC device use IDs 0-255, extra entities + * implemented by the driver (such as the GPIO entity) use IDs 256 and + * up. + */ + u16 id; u16 type; char name[64]; u8 guid[16]; -- cgit v1.2.3 From 7532dad6634031d083df7af606fac655b8d08b5c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 23 Dec 2020 14:35:19 +0100 Subject: media: uvcvideo: Allow entities with no pads Avoid an underflow while calculating the number of inputs for entities with zero pads. Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index dd2e4998e564..a9b94fb2c064 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1033,7 +1033,10 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id, unsigned int i; extra_size = roundup(extra_size, sizeof(*entity->pads)); - num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; + if (num_pads) + num_inputs = type & UVC_TERM_OUTPUT ? num_pads : num_pads - 1; + else + num_inputs = 0; size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads + num_inputs; entity = kzalloc(size, GFP_KERNEL); @@ -1065,7 +1068,7 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id, for (i = 0; i < num_inputs; ++i) entity->pads[i].flags = MEDIA_PAD_FL_SINK; - if (!UVC_ENTITY_IS_OTERM(entity)) + if (!UVC_ENTITY_IS_OTERM(entity) && num_pads) entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; entity->bNrInPins = num_inputs; -- cgit v1.2.3 From d9c8763e61295be0a21dc04ad9c379d5d17c3d86 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 23 Dec 2020 14:35:20 +0100 Subject: media: uvcvideo: Provide sync and async uvc_ctrl_status_event Split the functionality of void uvc_ctrl_status_event_work in two, so it can be called by functions outside interrupt context and not part of an URB. Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 25 +++++++++++++++---------- drivers/media/usb/uvc/uvc_status.c | 3 ++- drivers/media/usb/uvc/uvcvideo.h | 4 +++- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 9f6174a10e73..4d43f4c3e349 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1254,17 +1254,12 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain, uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes); } -static void uvc_ctrl_status_event_work(struct work_struct *work) +void uvc_ctrl_status_event(struct uvc_video_chain *chain, + struct uvc_control *ctrl, const u8 *data) { - struct uvc_device *dev = container_of(work, struct uvc_device, - async_ctrl.work); - struct uvc_ctrl_work *w = &dev->async_ctrl; - struct uvc_video_chain *chain = w->chain; struct uvc_control_mapping *mapping; - struct uvc_control *ctrl = w->ctrl; struct uvc_fh *handle; unsigned int i; - int ret; mutex_lock(&chain->ctrl_mutex); @@ -1272,7 +1267,7 @@ static void uvc_ctrl_status_event_work(struct work_struct *work) ctrl->handle = NULL; list_for_each_entry(mapping, &ctrl->info.mappings, list) { - s32 value = __uvc_ctrl_get_value(mapping, w->data); + s32 value = __uvc_ctrl_get_value(mapping, data); /* * handle may be NULL here if the device sends auto-update @@ -1291,6 +1286,16 @@ static void uvc_ctrl_status_event_work(struct work_struct *work) } mutex_unlock(&chain->ctrl_mutex); +} + +static void uvc_ctrl_status_event_work(struct work_struct *work) +{ + struct uvc_device *dev = container_of(work, struct uvc_device, + async_ctrl.work); + struct uvc_ctrl_work *w = &dev->async_ctrl; + int ret; + + uvc_ctrl_status_event(w->chain, w->ctrl, w->data); /* Resubmit the URB. */ w->urb->interval = dev->int_ep->desc.bInterval; @@ -1300,8 +1305,8 @@ static void uvc_ctrl_status_event_work(struct work_struct *work) ret); } -bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain, - struct uvc_control *ctrl, const u8 *data) +bool uvc_ctrl_status_event_async(struct urb *urb, struct uvc_video_chain *chain, + struct uvc_control *ctrl, const u8 *data) { struct uvc_device *dev = chain->dev; struct uvc_ctrl_work *w = &dev->async_ctrl; diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 2bdb0ff203f8..3e26d82a906d 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -179,7 +179,8 @@ static bool uvc_event_control(struct urb *urb, switch (status->bAttribute) { case UVC_CTRL_VALUE_CHANGE: - return uvc_ctrl_status_event(urb, chain, ctrl, status->bValue); + return uvc_ctrl_status_event_async(urb, chain, ctrl, + status->bValue); case UVC_CTRL_INFO_CHANGE: case UVC_CTRL_FAILURE_CHANGE: diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 2643d9adfd4a..950708f81fa8 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -844,7 +844,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, int uvc_ctrl_init_device(struct uvc_device *dev); void uvc_ctrl_cleanup_device(struct uvc_device *dev); int uvc_ctrl_restore_values(struct uvc_device *dev); -bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain, +bool uvc_ctrl_status_event_async(struct urb *urb, struct uvc_video_chain *chain, + struct uvc_control *ctrl, const u8 *data); +void uvc_ctrl_status_event(struct uvc_video_chain *chain, struct uvc_control *ctrl, const u8 *data); int uvc_ctrl_begin(struct uvc_video_chain *chain); -- cgit v1.2.3 From 65900c581d014499f0f8ceabfc02c652e9a88771 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 23 Dec 2020 14:35:21 +0100 Subject: media: uvcvideo: Allow entity-defined get_info and get_cur Allows controls to get their properties and current value from an entity-defined function instead of via a query to the USB device. Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 22 ++++++++++++++++++---- drivers/media/usb/uvc/uvcvideo.h | 5 +++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 4d43f4c3e349..1a5e85368af4 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -980,10 +980,20 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain, return -EACCES; if (!ctrl->loaded) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, - chain->dev->intfnum, ctrl->info.selector, + if (ctrl->entity->get_cur) { + ret = ctrl->entity->get_cur(chain->dev, + ctrl->entity, + ctrl->info.selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info.size); + } else { + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, + ctrl->entity->id, + chain->dev->intfnum, + ctrl->info.selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info.size); + } if (ret < 0) return ret; @@ -1692,8 +1702,12 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev, if (data == NULL) return -ENOMEM; - ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, - info->selector, data, 1); + if (ctrl->entity->get_info) + ret = ctrl->entity->get_info(dev, ctrl->entity, + ctrl->info.selector, data); + else + ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, + dev->intfnum, info->selector, data, 1); if (!ret) info->flags |= (data[0] & UVC_CONTROL_CAP_GET ? UVC_CTRL_FLAG_GET_CUR : 0) diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 950708f81fa8..e85213998b8c 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -358,6 +358,11 @@ struct uvc_entity { u8 bNrInPins; u8 *baSourceID; + int (*get_info)(struct uvc_device *dev, struct uvc_entity *entity, + u8 cs, u8 *caps); + int (*get_cur)(struct uvc_device *dev, struct uvc_entity *entity, + u8 cs, void *data, u16 size); + unsigned int ncontrols; struct uvc_control *controls; }; -- cgit v1.2.3 From 2886477ff98740cc3333cf785e4de0b1ff3d7a28 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 23 Dec 2020 14:35:22 +0100 Subject: media: uvcvideo: Implement UVC_EXT_GPIO_UNIT Some devices can implement a physical switch to disable the input of the camera on demand. Think of it like an elegant privacy sticker. The system can read the status of the privacy switch via a GPIO. It is important to know the status of the switch, e.g. to notify the user when the camera will produce black frames and a videochat application is used. In some systems, the GPIO is connected to the main SoC instead of the camera controller, with the connection reported by the system firmware (ACPI or DT). In that case, the UVC device isn't aware of the GPIO. We need to implement a virtual entity to handle the GPIO fully on the driver side. For example, for ACPI-based systems, the GPIO is reported in the USB device object: Scope (\_SB.PCI0.XHCI.RHUB.HS07) { /.../ Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.PCI0.GPIO", 0x00, ResourceConsumer, , ) { // Pin list 0x0064 } }) Name (_DSD, Package (0x02) // _DSD: Device-Specific Data { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */, Package (0x01) { Package (0x02) { "privacy-gpio", Package (0x04) { \_SB.PCI0.XHCI.RHUB.HS07, Zero, Zero, One } } } }) } Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 3 + drivers/media/usb/uvc/uvc_driver.c | 127 +++++++++++++++++++++++++++++++++++++ drivers/media/usb/uvc/uvc_entity.c | 1 + drivers/media/usb/uvc/uvcvideo.h | 16 +++++ 4 files changed, 147 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 1a5e85368af4..e0ab55583dd8 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -2291,6 +2291,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev) } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) { bmControls = entity->camera.bmControls; bControlSize = entity->camera.bControlSize; + } else if (UVC_ENTITY_TYPE(entity) == UVC_EXT_GPIO_UNIT) { + bmControls = entity->gpio.bmControls; + bControlSize = entity->gpio.bControlSize; } /* Remove bogus/blacklisted controls */ diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index a9b94fb2c064..d01e93e1d79b 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -1020,6 +1021,7 @@ error: } static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; +static const u8 uvc_gpio_guid[16] = UVC_GUID_EXT_GPIO_CONTROLLER; static const u8 uvc_media_transport_input_guid[16] = UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; @@ -1051,6 +1053,9 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id, * is initialized by the caller. */ switch (type) { + case UVC_EXT_GPIO_UNIT: + memcpy(entity->guid, uvc_gpio_guid, 16); + break; case UVC_ITT_CAMERA: memcpy(entity->guid, uvc_camera_guid, 16); break; @@ -1464,6 +1469,108 @@ next_descriptor: return 0; } +/* ----------------------------------------------------------------------------- + * Privacy GPIO + */ + +static void uvc_gpio_event(struct uvc_device *dev) +{ + struct uvc_entity *unit = dev->gpio_unit; + struct uvc_video_chain *chain; + u8 new_val; + + if (!unit) + return; + + new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy); + + /* GPIO entities are always on the first chain. */ + chain = list_first_entry(&dev->chains, struct uvc_video_chain, list); + uvc_ctrl_status_event(chain, unit->controls, &new_val); +} + +static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity, + u8 cs, void *data, u16 size) +{ + if (cs != UVC_CT_PRIVACY_CONTROL || size < 1) + return -EINVAL; + + *(u8 *)data = gpiod_get_value_cansleep(entity->gpio.gpio_privacy); + + return 0; +} + +static int uvc_gpio_get_info(struct uvc_device *dev, struct uvc_entity *entity, + u8 cs, u8 *caps) +{ + if (cs != UVC_CT_PRIVACY_CONTROL) + return -EINVAL; + + *caps = UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_AUTOUPDATE; + return 0; +} + +static irqreturn_t uvc_gpio_irq(int irq, void *data) +{ + struct uvc_device *dev = data; + + uvc_gpio_event(dev); + return IRQ_HANDLED; +} + +static int uvc_gpio_parse(struct uvc_device *dev) +{ + struct uvc_entity *unit; + struct gpio_desc *gpio_privacy; + int irq; + + gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy", + GPIOD_IN); + if (IS_ERR_OR_NULL(gpio_privacy)) + return PTR_ERR_OR_ZERO(gpio_privacy); + + unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1); + if (!unit) + return -ENOMEM; + + irq = gpiod_to_irq(gpio_privacy); + if (irq < 0) { + if (irq != EPROBE_DEFER) + dev_err(&dev->udev->dev, + "No IRQ for privacy GPIO (%d)\n", irq); + return irq; + } + + unit->gpio.gpio_privacy = gpio_privacy; + unit->gpio.irq = irq; + unit->gpio.bControlSize = 1; + unit->gpio.bmControls = (u8 *)unit + sizeof(*unit); + unit->gpio.bmControls[0] = 1; + unit->get_cur = uvc_gpio_get_cur; + unit->get_info = uvc_gpio_get_info; + strncpy(unit->name, "GPIO", sizeof(unit->name) - 1); + + list_add_tail(&unit->list, &dev->entities); + + dev->gpio_unit = unit; + + return 0; +} + +static int uvc_gpio_init_irq(struct uvc_device *dev) +{ + struct uvc_entity *unit = dev->gpio_unit; + + if (!unit || unit->gpio.irq < 0) + return 0; + + return devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL, + uvc_gpio_irq, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING, + "uvc_privacy_gpio", dev); +} + /* ------------------------------------------------------------------------ * UVC device scan */ @@ -1953,6 +2060,13 @@ static int uvc_scan_device(struct uvc_device *dev) return -1; } + /* Add GPIO entity to the first chain. */ + if (dev->gpio_unit) { + chain = list_first_entry(&dev->chains, + struct uvc_video_chain, list); + list_add_tail(&dev->gpio_unit->chain, &chain->entities); + } + return 0; } @@ -2285,6 +2399,12 @@ static int uvc_probe(struct usb_interface *intf, goto error; } + /* Parse the associated GPIOs. */ + if (uvc_gpio_parse(dev) < 0) { + uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC GPIOs\n"); + goto error; + } + uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n", dev->uvc_version >> 8, dev->uvc_version & 0xff, udev->product ? udev->product : "", @@ -2335,6 +2455,13 @@ static int uvc_probe(struct usb_interface *intf, "supported.\n", ret); } + ret = uvc_gpio_init_irq(dev); + if (ret < 0) { + dev_err(&dev->udev->dev, + "Unable to request privacy GPIO IRQ (%d)\n", ret); + goto error; + } + uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); usb_enable_autosuspend(udev); return 0; diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c index ca3a9c2eec27..6a9ba5b498db 100644 --- a/drivers/media/usb/uvc/uvc_entity.c +++ b/drivers/media/usb/uvc/uvc_entity.c @@ -105,6 +105,7 @@ static int uvc_mc_init_entity(struct uvc_video_chain *chain, case UVC_OTT_DISPLAY: case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: case UVC_EXTERNAL_VENDOR_SPECIFIC: + case UVC_EXT_GPIO_UNIT: default: function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; break; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index e85213998b8c..0e026753aa1b 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -6,6 +6,7 @@ #error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead." #endif /* __KERNEL__ */ +#include #include #include #include @@ -37,6 +38,8 @@ (UVC_ENTITY_IS_TERM(entity) && \ ((entity)->type & 0x8000) == UVC_TERM_OUTPUT) +#define UVC_EXT_GPIO_UNIT 0x7ffe +#define UVC_EXT_GPIO_UNIT_ID 0x100 /* ------------------------------------------------------------------------ * GUIDs @@ -56,6 +59,9 @@ #define UVC_GUID_UVC_SELECTOR \ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02} +#define UVC_GUID_EXT_GPIO_CONTROLLER \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03} #define UVC_GUID_FORMAT_MJPEG \ { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \ @@ -212,6 +218,7 @@ * Structures. */ +struct gpio_desc; struct uvc_device; /* TODO: Put the most frequently accessed fields at the beginning of @@ -353,6 +360,13 @@ struct uvc_entity { u8 *bmControls; u8 *bmControlsType; } extension; + + struct { + u8 bControlSize; + u8 *bmControls; + struct gpio_desc *gpio_privacy; + int irq; + } gpio; }; u8 bNrInPins; @@ -691,6 +705,8 @@ struct uvc_device { struct uvc_control *ctrl; const void *data; } async_ctrl; + + struct uvc_entity *gpio_unit; }; enum uvc_handle_state { -- cgit v1.2.3 From 6f6a87eb8266dcdc843d10c5d0ba17c98cc484ce Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 23 Dec 2020 14:35:23 +0100 Subject: media: uvcvideo: Add Privacy control based on EXT_GPIO Add a new control and mapping for Privacy controls connected to UVC_GUID_EXT_GPIO_CONTROLLERs. Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index e0ab55583dd8..58ad63751baa 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -347,6 +347,14 @@ static const struct uvc_control_info uvc_ctrls[] = { | UVC_CTRL_FLAG_RESTORE | UVC_CTRL_FLAG_AUTO_UPDATE, }, + { + .entity = UVC_GUID_EXT_GPIO_CONTROLLER, + .selector = UVC_CT_PRIVACY_CONTROL, + .index = 0, + .size = 1, + .flags = UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_AUTO_UPDATE, + }, }; static const struct uvc_menu_info power_line_frequency_controls[] = { @@ -735,6 +743,16 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, + { + .id = V4L2_CID_PRIVACY, + .name = "Privacy", + .entity = UVC_GUID_EXT_GPIO_CONTROLLER, + .selector = UVC_CT_PRIVACY_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, }; /* ------------------------------------------------------------------------ -- cgit v1.2.3 From 69df09547e7a310dd6c73c423e8826b93050b8a9 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 23 Dec 2020 14:35:24 +0100 Subject: media: uvcvideo: Use dev_ printk aliases Replace all the uses of printk() and uvc_printk() with its equivalent dev_ alias macros. Modify uvc_warn_once() macro to use dev_info instead printk(). They are more standard across the kernel tree and provide more context about the error. Suggested-by: Joe Perches Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 12 ++++----- drivers/media/usb/uvc/uvc_driver.c | 54 ++++++++++++++++++++------------------ drivers/media/usb/uvc/uvc_entity.c | 10 ++++--- drivers/media/usb/uvc/uvc_status.c | 13 ++++----- drivers/media/usb/uvc/uvc_video.c | 48 +++++++++++++++++---------------- drivers/media/usb/uvc/uvcvideo.h | 25 ++++++++---------- 6 files changed, 83 insertions(+), 79 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 58ad63751baa..b0241daaacce 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1329,8 +1329,8 @@ static void uvc_ctrl_status_event_work(struct work_struct *work) w->urb->interval = dev->int_ep->desc.bInterval; ret = usb_submit_urb(w->urb, GFP_KERNEL); if (ret < 0) - uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", - ret); + dev_err(&dev->udev->dev, + "Failed to resubmit status URB (%d).\n", ret); } bool uvc_ctrl_status_event_async(struct urb *urb, struct uvc_video_chain *chain, @@ -2010,10 +2010,10 @@ int uvc_ctrl_restore_values(struct uvc_device *dev) if (!ctrl->initialized || !ctrl->modified || (ctrl->info.flags & UVC_CTRL_FLAG_RESTORE) == 0) continue; - - printk(KERN_INFO "restoring control %pUl/%u/%u\n", - ctrl->info.entity, ctrl->info.index, - ctrl->info.selector); + dev_info(&dev->udev->dev, + "restoring control %pUl/%u/%u\n", + ctrl->info.entity, ctrl->info.index, + ctrl->info.selector); ctrl->dirty = 1; } diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index d01e93e1d79b..97056417b87e 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -535,8 +535,8 @@ static int uvc_parse_format(struct uvc_device *dev, sizeof(format->name)); format->fcc = fmtdesc->fcc; } else { - uvc_printk(KERN_INFO, "Unknown video format %pUl\n", - &buffer[5]); + dev_info(&streaming->intf->dev, + "Unknown video format %pUl\n", &buffer[5]); snprintf(format->name, sizeof(format->name), "%pUl\n", &buffer[5]); format->fcc = 0; @@ -2056,7 +2056,7 @@ static int uvc_scan_device(struct uvc_device *dev) uvc_scan_fallback(dev); if (list_empty(&dev->chains)) { - uvc_printk(KERN_INFO, "No valid video chain found.\n"); + dev_info(&dev->udev->dev, "No valid video chain found.\n"); return -1; } @@ -2215,8 +2215,9 @@ int uvc_register_video_device(struct uvc_device *dev, ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { - uvc_printk(KERN_ERR, "Failed to register %s device (%d).\n", - v4l2_type_names[type], ret); + dev_err(&stream->intf->dev, + "Failed to register %s device (%d).\n", + v4l2_type_names[type], ret); return ret; } @@ -2232,8 +2233,8 @@ static int uvc_register_video(struct uvc_device *dev, /* Initialize the streaming interface with default parameters. */ ret = uvc_video_init(stream); if (ret < 0) { - uvc_printk(KERN_ERR, "Failed to initialize the device (%d).\n", - ret); + dev_err(&stream->intf->dev, + "Failed to initialize the device (%d).\n", ret); return ret; } @@ -2267,8 +2268,9 @@ static int uvc_register_terms(struct uvc_device *dev, stream = uvc_stream_by_id(dev, term->id); if (stream == NULL) { - uvc_printk(KERN_INFO, "No streaming interface found " - "for terminal %u.", term->id); + dev_info(&dev->udev->dev, + "No streaming interface found for terminal %u.", + term->id); continue; } @@ -2301,8 +2303,8 @@ static int uvc_register_chains(struct uvc_device *dev) #ifdef CONFIG_MEDIA_CONTROLLER ret = uvc_mc_register_entities(chain); if (ret < 0) - uvc_printk(KERN_INFO, - "Failed to register entities (%d).\n", ret); + dev_info(&dev->udev->dev, + "Failed to register entities (%d).\n", ret); #endif } @@ -2405,23 +2407,24 @@ static int uvc_probe(struct usb_interface *intf, goto error; } - uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n", - dev->uvc_version >> 8, dev->uvc_version & 0xff, - udev->product ? udev->product : "", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); + dev_info(&dev->udev->dev, "Found UVC %u.%02x device %s (%04x:%04x)\n", + dev->uvc_version >> 8, dev->uvc_version & 0xff, + udev->product ? udev->product : "", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); if (dev->quirks != dev->info->quirks) { - uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module " - "parameter for testing purpose.\n", dev->quirks); - uvc_printk(KERN_INFO, "Please report required quirks to the " - "linux-uvc-devel mailing list.\n"); + dev_info(&dev->udev->dev, + "Forcing device quirks to 0x%x by module parameter for testing purpose.\n", + dev->quirks); + dev_info(&dev->udev->dev, + "Please report required quirks to the linux-uvc-devel mailing list.\n"); } if (dev->info->uvc_version) { dev->uvc_version = dev->info->uvc_version; - uvc_printk(KERN_INFO, "Forcing UVC version to %u.%02x\n", - dev->uvc_version >> 8, dev->uvc_version & 0xff); + dev_info(&dev->udev->dev, "Forcing UVC version to %u.%02x\n", + dev->uvc_version >> 8, dev->uvc_version & 0xff); } /* Register the V4L2 device. */ @@ -2450,9 +2453,9 @@ static int uvc_probe(struct usb_interface *intf, /* Initialize the interrupt URB. */ if ((ret = uvc_status_init(dev)) < 0) { - uvc_printk(KERN_INFO, "Unable to initialize the status " - "endpoint (%d), status interrupt will not be " - "supported.\n", ret); + dev_info(&dev->udev->dev, + "Unable to initialize the status endpoint (%d), status interrupt will not be supported.\n", + ret); } ret = uvc_gpio_init_irq(dev); @@ -3170,7 +3173,6 @@ static int __init uvc_init(void) return ret; } - printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n"); return 0; } diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c index 6a9ba5b498db..7c4d2f93d351 100644 --- a/drivers/media/usb/uvc/uvc_entity.c +++ b/drivers/media/usb/uvc/uvc_entity.c @@ -140,8 +140,9 @@ int uvc_mc_register_entities(struct uvc_video_chain *chain) list_for_each_entry(entity, &chain->entities, chain) { ret = uvc_mc_init_entity(chain, entity); if (ret < 0) { - uvc_printk(KERN_INFO, "Failed to initialize entity for " - "entity %u\n", entity->id); + dev_info(&chain->dev->udev->dev, + "Failed to initialize entity for entity %u\n", + entity->id); return ret; } } @@ -149,8 +150,9 @@ int uvc_mc_register_entities(struct uvc_video_chain *chain) list_for_each_entry(entity, &chain->entities, chain) { ret = uvc_mc_create_links(chain, entity); if (ret < 0) { - uvc_printk(KERN_INFO, "Failed to create links for " - "entity %u\n", entity->id); + dev_info(&chain->dev->udev->dev, + "Failed to create links for entity %u\n", + entity->id); return ret; } } diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 3e26d82a906d..4999fa585844 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -209,8 +209,9 @@ static void uvc_status_complete(struct urb *urb) return; default: - uvc_printk(KERN_WARNING, "Non-zero status (%d) in status " - "completion handler.\n", urb->status); + dev_warn(&dev->udev->dev, + "Non-zero status (%d) in status completion handler.\n", + urb->status); return; } @@ -244,10 +245,10 @@ static void uvc_status_complete(struct urb *urb) /* Resubmit the URB. */ urb->interval = dev->int_ep->desc.bInterval; - if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { - uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", - ret); - } + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) + dev_err(&dev->udev->dev, + "Failed to resubmit status URB (%d).\n", ret); } int uvc_status_init(struct uvc_device *dev) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index a6a441d92b94..71e643e6d006 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -76,9 +76,9 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, if (likely(ret == size)) return 0; - uvc_printk(KERN_ERR, - "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n", - uvc_query_name(query), cs, unit, ret, size); + dev_err(&dev->udev->dev, + "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n", + uvc_query_name(query), cs, unit, ret, size); if (ret != -EPIPE) return ret; @@ -254,9 +254,9 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, ret = -EIO; goto out; } else if (ret != size) { - uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : " - "%d (exp. %u).\n", query, probe ? "probe" : "commit", - ret, size); + dev_err(&stream->intf->dev, + "Failed to query (%u) UVC %s control : %d (exp. %u).\n", + query, probe ? "probe" : "commit", ret, size); ret = -EIO; goto out; } @@ -334,9 +334,9 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, size, uvc_timeout_param); if (ret != size) { - uvc_printk(KERN_ERR, "Failed to set UVC %s control : " - "%d (exp. %u).\n", probe ? "probe" : "commit", - ret, size); + dev_err(&stream->intf->dev, + "Failed to set UVC %s control : %d (exp. %u).\n", + probe ? "probe" : "commit", ret, size); ret = -EIO; } @@ -1120,8 +1120,8 @@ static void uvc_video_copy_data_work(struct work_struct *work) ret = usb_submit_urb(uvc_urb->urb, GFP_KERNEL); if (ret < 0) - uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n", - ret); + dev_err(&uvc_urb->stream->intf->dev, + "Failed to resubmit video URB (%d).\n", ret); } static void uvc_video_decode_data(struct uvc_urb *uvc_urb, @@ -1507,8 +1507,9 @@ static void uvc_video_complete(struct urb *urb) break; default: - uvc_printk(KERN_WARNING, "Non-zero status (%d) in video " - "completion handler.\n", urb->status); + dev_warn(&stream->intf->dev, + "Non-zero status (%d) in video completion handler.\n", + urb->status); fallthrough; case -ENOENT: /* usb_poison_urb() called. */ if (stream->frozen) @@ -1545,9 +1546,8 @@ static void uvc_video_complete(struct urb *urb) if (!uvc_urb->async_operations) { ret = usb_submit_urb(uvc_urb->urb, GFP_ATOMIC); if (ret < 0) - uvc_printk(KERN_ERR, - "Failed to resubmit video URB (%d).\n", - ret); + dev_err(&stream->intf->dev, + "Failed to resubmit video URB (%d).\n", ret); return; } @@ -1893,8 +1893,9 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream, for_each_uvc_urb(uvc_urb, stream) { ret = usb_submit_urb(uvc_urb->urb, gfp_flags); if (ret < 0) { - uvc_printk(KERN_ERR, "Failed to submit URB %u (%d).\n", - uvc_urb_index(uvc_urb), ret); + dev_err(&stream->intf->dev, + "Failed to submit URB %u (%d).\n", + uvc_urb_index(uvc_urb), ret); uvc_video_stop_transfer(stream, 1); return ret; } @@ -1989,7 +1990,8 @@ int uvc_video_init(struct uvc_streaming *stream) int ret; if (stream->nformats == 0) { - uvc_printk(KERN_INFO, "No supported video formats found.\n"); + dev_info(&stream->intf->dev, + "No supported video formats found.\n"); return -EINVAL; } @@ -2029,8 +2031,8 @@ int uvc_video_init(struct uvc_streaming *stream) } if (format->nframes == 0) { - uvc_printk(KERN_INFO, "No frame descriptor found for the " - "default format.\n"); + dev_info(&stream->intf->dev, + "No frame descriptor found for the default format.\n"); return -EINVAL; } @@ -2064,8 +2066,8 @@ int uvc_video_init(struct uvc_streaming *stream) if (stream->intf->num_altsetting == 1) stream->decode = uvc_video_encode_bulk; else { - uvc_printk(KERN_INFO, "Isochronous endpoints are not " - "supported for video output devices.\n"); + dev_info(&stream->intf->dev, + "Isochronous endpoints are not supported for video output devices.\n"); return -EINVAL; } } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 0e026753aa1b..11f61ff7cd86 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -752,20 +752,17 @@ extern unsigned int uvc_trace_param; extern unsigned int uvc_timeout_param; extern unsigned int uvc_hw_timestamps_param; -#define uvc_trace(flag, msg...) \ - do { \ - if (uvc_trace_param & flag) \ - printk(KERN_DEBUG "uvcvideo: " msg); \ - } while (0) - -#define uvc_warn_once(dev, warn, msg...) \ - do { \ - if (!test_and_set_bit(warn, &dev->warnings)) \ - printk(KERN_INFO "uvcvideo: " msg); \ - } while (0) - -#define uvc_printk(level, msg...) \ - printk(level "uvcvideo: " msg) +#define uvc_trace(flag, fmt, ...) \ +do { \ + if (uvc_trace_param & flag) \ + printk(KERN_DEBUG "uvcvideo: " fmt, ##__VA_ARGS__); \ +} while (0) + +#define uvc_warn_once(_dev, warn, fmt, ...) \ +do { \ + if (!test_and_set_bit(warn, &(_dev)->warnings)) \ + dev_info(&(_dev)->udev->dev, fmt, ##__VA_ARGS__); \ +} while (0) /* -------------------------------------------------------------------------- * Internal functions. -- cgit v1.2.3 From 59e92bf62771a5ff276f1c845c7b59d812d1dc8a Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 23 Dec 2020 14:35:25 +0100 Subject: media: uvcvideo: New macro uvc_trace_cont Remove all the duplicated code around printk(KERN_CONT, with a new macro. Suggested-by: Joe Perches Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 57 ++++++++++++++------------------------ drivers/media/usb/uvc/uvcvideo.h | 6 ++++ 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 97056417b87e..902c3c0e4062 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1606,8 +1606,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, { switch (UVC_ENTITY_TYPE(entity)) { case UVC_VC_EXTENSION_UNIT: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(KERN_CONT " <- XU %d", entity->id); + uvc_trace_cont(UVC_TRACE_PROBE, " <- XU %d", entity->id); if (entity->bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " @@ -1618,8 +1617,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, break; case UVC_VC_PROCESSING_UNIT: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(KERN_CONT " <- PU %d", entity->id); + uvc_trace_cont(UVC_TRACE_PROBE, " <- PU %d", entity->id); if (chain->processing != NULL) { uvc_trace(UVC_TRACE_DESCR, "Found multiple " @@ -1631,8 +1629,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, break; case UVC_VC_SELECTOR_UNIT: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(KERN_CONT " <- SU %d", entity->id); + uvc_trace_cont(UVC_TRACE_PROBE, " <- SU %d", entity->id); /* Single-input selector units are ignored. */ if (entity->bNrInPins == 1) @@ -1650,27 +1647,22 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, case UVC_ITT_VENDOR_SPECIFIC: case UVC_ITT_CAMERA: case UVC_ITT_MEDIA_TRANSPORT_INPUT: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(KERN_CONT " <- IT %d\n", entity->id); + uvc_trace_cont(UVC_TRACE_PROBE, " <- IT %d\n", entity->id); break; case UVC_OTT_VENDOR_SPECIFIC: case UVC_OTT_DISPLAY: case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(KERN_CONT " OT %d", entity->id); + uvc_trace_cont(UVC_TRACE_PROBE, " OT %d", entity->id); break; case UVC_TT_STREAMING: - if (UVC_ENTITY_IS_ITERM(entity)) { - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(KERN_CONT " <- IT %d\n", entity->id); - } else { - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(KERN_CONT " OT %d", entity->id); - } + if (UVC_ENTITY_IS_ITERM(entity)) + uvc_trace_cont(UVC_TRACE_PROBE, " <- IT %d\n", entity->id); + else + uvc_trace_cont(UVC_TRACE_PROBE, " OT %d", entity->id); break; @@ -1717,13 +1709,11 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, } list_add_tail(&forward->chain, &chain->entities); - if (uvc_trace_param & UVC_TRACE_PROBE) { - if (!found) - printk(KERN_CONT " (->"); + if (!found) + uvc_trace_cont(UVC_TRACE_PROBE, " (->"); - printk(KERN_CONT " XU %d", forward->id); - found = 1; - } + uvc_trace_cont(UVC_TRACE_PROBE, " XU %d", forward->id); + found = 1; break; case UVC_OTT_VENDOR_SPECIFIC: @@ -1737,18 +1727,16 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, } list_add_tail(&forward->chain, &chain->entities); - if (uvc_trace_param & UVC_TRACE_PROBE) { - if (!found) - printk(KERN_CONT " (->"); + if (!found) + uvc_trace_cont(UVC_TRACE_PROBE, " (->"); - printk(KERN_CONT " OT %d", forward->id); - found = 1; - } + uvc_trace_cont(UVC_TRACE_PROBE, " OT %d", forward->id); + found = 1; break; } } if (found) - printk(KERN_CONT ")"); + uvc_trace_cont(UVC_TRACE_PROBE, ")"); return 0; } @@ -1773,8 +1761,7 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, break; } - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(KERN_CONT " <- IT"); + uvc_trace_cont(UVC_TRACE_PROBE, " <- IT"); chain->selector = entity; for (i = 0; i < entity->bNrInPins; ++i) { @@ -1794,15 +1781,13 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, return -EINVAL; } - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(KERN_CONT " %d", term->id); + uvc_trace_cont(UVC_TRACE_PROBE, " %d", term->id); list_add_tail(&term->chain, &chain->entities); uvc_scan_chain_forward(chain, term, entity); } - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(KERN_CONT "\n"); + uvc_trace_cont(UVC_TRACE_PROBE, "\n"); id = 0; break; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 11f61ff7cd86..6687949d93d9 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -758,6 +758,12 @@ do { \ printk(KERN_DEBUG "uvcvideo: " fmt, ##__VA_ARGS__); \ } while (0) +#define uvc_trace_cont(flag, fmt, ...) \ +do { \ + if (uvc_trace_param & flag) \ + pr_cont(fmt, ##__VA_ARGS__); \ +} while (0) + #define uvc_warn_once(_dev, warn, fmt, ...) \ do { \ if (!test_and_set_bit(warn, &(_dev)->warnings)) \ -- cgit v1.2.3 From ed4c5fa4d804f57fa820d8ef8d3054be2ed13bea Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 23 Dec 2020 14:35:26 +0100 Subject: media: uvcvideo: use dev_printk() for uvc_trace() Instead of calling prink() inside uvc_trace, use dev_printk(), which adds context to the output. Now that we are at it, regroup the strings so the messages can be easily "grepable". Suggested-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 66 ++++---- drivers/media/usb/uvc/uvc_driver.c | 298 +++++++++++++++++++------------------ drivers/media/usb/uvc/uvc_isight.c | 16 +- drivers/media/usb/uvc/uvc_queue.c | 9 +- drivers/media/usb/uvc/uvc_status.c | 19 +-- drivers/media/usb/uvc/uvc_v4l2.c | 51 ++++--- drivers/media/usb/uvc/uvc_video.c | 72 +++++---- drivers/media/usb/uvc/uvcvideo.h | 11 +- 8 files changed, 289 insertions(+), 253 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index b0241daaacce..befaeb90e569 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -906,8 +906,8 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, } if (ctrl == NULL && !next) - uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n", - v4l2_id); + uvc_trace(chain->dev, UVC_TRACE_CONTROL, + "Control 0x%08x not found.\n", v4l2_id); return ctrl; } @@ -1800,9 +1800,9 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum, info->selector, data, 2); if (ret < 0) { - uvc_trace(UVC_TRACE_CONTROL, + uvc_trace(dev, UVC_TRACE_CONTROL, "GET_LEN failed on control %pUl/%u (%d).\n", - info->entity, info->selector, ret); + info->entity, info->selector, ret); goto done; } @@ -1813,7 +1813,7 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, ret = uvc_ctrl_get_flags(dev, ctrl, info); if (ret < 0) { - uvc_trace(UVC_TRACE_CONTROL, + uvc_trace(dev, UVC_TRACE_CONTROL, "Failed to get flags for control %pUl/%u (%d).\n", info->entity, info->selector, ret); goto done; @@ -1821,8 +1821,8 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, uvc_ctrl_fixup_xu_info(dev, ctrl, info); - uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, " - "flags { get %u set %u auto %u }.\n", + uvc_trace(dev, UVC_TRACE_CONTROL, + "XU control %pUl/%u queried: len %u, flags { get %u set %u auto %u }.\n", info->entity, info->selector, info->size, (info->flags & UVC_CTRL_FLAG_GET_CUR) ? 1 : 0, (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0, @@ -1851,9 +1851,10 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev, ret = uvc_ctrl_add_info(dev, ctrl, &info); if (ret < 0) - uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize control " - "%pUl/%u on device %s entity %u\n", info.entity, - info.selector, dev->udev->devpath, ctrl->entity->id); + uvc_trace(dev, UVC_TRACE_CONTROL, + "Failed to initialize control %pUl/%u on device %s entity %u\n", + info.entity, info.selector, dev->udev->devpath, + ctrl->entity->id); return ret; } @@ -1881,8 +1882,8 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, } if (!found) { - uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", - xqry->unit); + uvc_trace(chain->dev, UVC_TRACE_CONTROL, + "Extension unit %u not found.\n", xqry->unit); return -ENOENT; } @@ -1897,8 +1898,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, } if (!found) { - uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n", - entity->guid, xqry->selector); + uvc_trace(chain->dev, UVC_TRACE_CONTROL, + "Control %pUl/%u not found.\n", entity->guid, + xqry->selector); return -ENOENT; } @@ -2046,9 +2048,10 @@ static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, ctrl->initialized = 1; - uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s " - "entity %u\n", ctrl->info.entity, ctrl->info.selector, - dev->udev->devpath, ctrl->entity->id); + uvc_trace(dev, UVC_TRACE_CONTROL, + "Added control %pUl/%u to device %s entity %u\n", + ctrl->info.entity, ctrl->info.selector, dev->udev->devpath, + ctrl->entity->id); return 0; } @@ -2085,9 +2088,9 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev, map->set = uvc_set_le_value; list_add_tail(&map->list, &ctrl->info.mappings); - uvc_trace(UVC_TRACE_CONTROL, - "Adding mapping '%s' to control %pUl/%u.\n", - map->name, ctrl->info.entity, ctrl->info.selector); + uvc_trace(dev, UVC_TRACE_CONTROL, + "Adding mapping '%s' to control %pUl/%u.\n", + map->name, ctrl->info.entity, ctrl->info.selector); return 0; } @@ -2103,9 +2106,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, int ret; if (mapping->id & ~V4L2_CTRL_ID_MASK) { - uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control " - "id 0x%08x is invalid.\n", mapping->name, - mapping->id); + uvc_trace(dev, UVC_TRACE_CONTROL, + "Can't add mapping '%s', control id 0x%08x is invalid.\n", + mapping->name, mapping->id); return -EINVAL; } @@ -2150,9 +2153,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, list_for_each_entry(map, &ctrl->info.mappings, list) { if (mapping->id == map->id) { - uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', " - "control id 0x%08x already exists.\n", - mapping->name, mapping->id); + uvc_trace(dev, UVC_TRACE_CONTROL, + "Can't add mapping '%s', control id 0x%08x already exists.\n", + mapping->name, mapping->id); ret = -EEXIST; goto done; } @@ -2161,9 +2164,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, /* Prevent excess memory consumption */ if (atomic_inc_return(&dev->nmappings) > UVC_MAX_CONTROL_MAPPINGS) { atomic_dec(&dev->nmappings); - uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', maximum " - "mappings count (%u) exceeded.\n", mapping->name, - UVC_MAX_CONTROL_MAPPINGS); + uvc_trace(dev, UVC_TRACE_CONTROL, + "Can't add mapping '%s', maximum mappings count (%u) exceeded.\n", + mapping->name, UVC_MAX_CONTROL_MAPPINGS); ret = -ENOMEM; goto done; } @@ -2232,8 +2235,9 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev, !uvc_test_bit(controls, blacklist[i].index)) continue; - uvc_trace(UVC_TRACE_CONTROL, "%u/%u control is black listed, " - "removing it.\n", entity->id, blacklist[i].index); + uvc_trace(dev, UVC_TRACE_CONTROL, + "%u/%u control is black listed, removing it.\n", + entity->id, blacklist[i].index); uvc_clear_bit(controls, blacklist[i].index); } diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 902c3c0e4062..794f1aecd77a 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -520,10 +520,10 @@ static int uvc_parse_format(struct uvc_device *dev, case UVC_VS_FORMAT_FRAME_BASED: n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; if (buflen < n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d FORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); return -EINVAL; } @@ -584,10 +584,10 @@ static int uvc_parse_format(struct uvc_device *dev, case UVC_VS_FORMAT_MJPEG: if (buflen < 11) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d FORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); return -EINVAL; } @@ -600,10 +600,10 @@ static int uvc_parse_format(struct uvc_device *dev, case UVC_VS_FORMAT_DV: if (buflen < 9) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d FORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); return -EINVAL; } @@ -618,9 +618,9 @@ static int uvc_parse_format(struct uvc_device *dev, strscpy(format->name, "HD-DV", sizeof(format->name)); break; default: - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d: unknown DV format %u\n", - dev->udev->devnum, + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d: unknown DV format %u\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, buffer[8]); return -EINVAL; } @@ -647,14 +647,14 @@ static int uvc_parse_format(struct uvc_device *dev, case UVC_VS_FORMAT_STREAM_BASED: /* Not supported yet. */ default: - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d unsupported format %u\n", - dev->udev->devnum, alts->desc.bInterfaceNumber, - buffer[2]); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d unsupported format %u\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, + buffer[2]); return -EINVAL; } - uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name); + uvc_trace(dev, UVC_TRACE_DESCR, "Found format %s.\n", format->name); buflen -= buffer[0]; buffer += buffer[0]; @@ -673,9 +673,10 @@ static int uvc_parse_format(struct uvc_device *dev, n = n ? n : 3; if (buflen < 26 + 4*n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d FRAME error\n", dev->udev->devnum, - alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d FRAME error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); return -EINVAL; } @@ -737,10 +738,10 @@ static int uvc_parse_format(struct uvc_device *dev, frame->dwDefaultFrameInterval; } - uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", - frame->wWidth, frame->wHeight, - 10000000/frame->dwDefaultFrameInterval, - (100000000/frame->dwDefaultFrameInterval)%10); + uvc_trace(dev, UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", + frame->wWidth, frame->wHeight, + 10000000 / frame->dwDefaultFrameInterval, + (100000000 / frame->dwDefaultFrameInterval) % 10); format->nframes++; buflen -= buffer[0]; @@ -756,10 +757,10 @@ static int uvc_parse_format(struct uvc_device *dev, if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && buffer[2] == UVC_VS_COLORFORMAT) { if (buflen < 6) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d COLORFORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d COLORFORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); return -EINVAL; } @@ -791,16 +792,18 @@ static int uvc_parse_streaming(struct uvc_device *dev, if (intf->cur_altsetting->desc.bInterfaceSubClass != UVC_SC_VIDEOSTREAMING) { - uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a " - "video streaming interface\n", dev->udev->devnum, - intf->altsetting[0].desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d interface %d isn't a video streaming interface\n", + dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); return -EINVAL; } if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { - uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already " - "claimed\n", dev->udev->devnum, - intf->altsetting[0].desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d interface %d is already claimed\n", + dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); return -EINVAL; } @@ -822,8 +825,9 @@ static int uvc_parse_streaming(struct uvc_device *dev, if (ep->extralen > 2 && ep->extra[1] == USB_DT_CS_INTERFACE) { - uvc_trace(UVC_TRACE_DESCR, "trying extra data " - "from endpoint %u.\n", i); + uvc_trace(dev, UVC_TRACE_DESCR, + "trying extra data from endpoint %u.\n", + i); buffer = alts->endpoint[i].extra; buflen = alts->endpoint[i].extralen; break; @@ -838,8 +842,8 @@ static int uvc_parse_streaming(struct uvc_device *dev, } if (buflen <= 2) { - uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming " - "interface descriptors found.\n"); + uvc_trace(dev, UVC_TRACE_DESCR, + "no class-specific streaming interface descriptors found.\n"); goto error; } @@ -856,9 +860,9 @@ static int uvc_parse_streaming(struct uvc_device *dev, break; default: - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " - "%d HEADER descriptor not found.\n", dev->udev->devnum, - alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d HEADER descriptor not found.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); goto error; } @@ -866,9 +870,9 @@ static int uvc_parse_streaming(struct uvc_device *dev, n = buflen >= size ? buffer[size-1] : 0; if (buflen < size + p*n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d HEADER descriptor is invalid.\n", - dev->udev->devnum, alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d HEADER descriptor is invalid.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); goto error; } @@ -918,10 +922,10 @@ static int uvc_parse_streaming(struct uvc_device *dev, case UVC_VS_FORMAT_MPEG2TS: case UVC_VS_FORMAT_STREAM_BASED: - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " - "interface %d FORMAT %u is not supported.\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber, _buffer[2]); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d FORMAT %u is not supported.\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber, _buffer[2]); break; case UVC_VS_FRAME_UNCOMPRESSED: @@ -943,9 +947,9 @@ static int uvc_parse_streaming(struct uvc_device *dev, } if (nformats == 0) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " - "%d has no supported formats defined.\n", - dev->udev->devnum, alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d has no supported formats defined.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); goto error; } @@ -992,9 +996,10 @@ static int uvc_parse_streaming(struct uvc_device *dev, } if (buflen) - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " - "%d has %u bytes of trailing descriptor garbage.\n", - dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videostreaming interface %d has %u bytes of trailing descriptor garbage.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, + buflen); /* Parse the alternate settings to find the maximum bandwidth. */ for (i = 0; i < intf->num_altsetting; ++i) { @@ -1127,9 +1132,9 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, n = buflen >= 25 + p ? buffer[22+p] : 0; if (buflen < 25 + p + 2*n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d EXTENSION_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videocontrol interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); break; } @@ -1176,9 +1181,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, n = buflen >= 12 ? buffer[11] : 0; if (buflen < 12 + n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d HEADER error\n", udev->devnum, - alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videocontrol interface %d HEADER error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1189,9 +1194,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, for (i = 0; i < n; ++i) { intf = usb_ifnum_to_if(udev, buffer[12+i]); if (intf == NULL) { - uvc_trace(UVC_TRACE_DESCR, "device %d " - "interface %d doesn't exists\n", - udev->devnum, i); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d interface %d doesn't exists\n", + udev->devnum, i); continue; } @@ -1201,9 +1206,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, case UVC_VC_INPUT_TERMINAL: if (buflen < 8) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d INPUT_TERMINAL error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videocontrol interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1220,11 +1225,10 @@ static int uvc_parse_standard_control(struct uvc_device *dev, */ type = get_unaligned_le16(&buffer[4]); if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d INPUT_TERMINAL %d has invalid " - "type 0x%04x, skipping\n", udev->devnum, - alts->desc.bInterfaceNumber, - buffer[3], type); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videocontrol interface %d INPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", + udev->devnum, alts->desc.bInterfaceNumber, + buffer[3], type); return 0; } @@ -1243,9 +1247,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, } if (buflen < len + n + p) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d INPUT_TERMINAL error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videocontrol interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1290,9 +1294,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, case UVC_VC_OUTPUT_TERMINAL: if (buflen < 9) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d OUTPUT_TERMINAL error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videocontrol interface %d OUTPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1301,10 +1305,10 @@ static int uvc_parse_standard_control(struct uvc_device *dev, */ type = get_unaligned_le16(&buffer[4]); if ((type & 0xff00) == 0) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d OUTPUT_TERMINAL %d has invalid " - "type 0x%04x, skipping\n", udev->devnum, - alts->desc.bInterfaceNumber, buffer[3], type); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videocontrol interface %d OUTPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", + udev->devnum, alts->desc.bInterfaceNumber, + buffer[3], type); return 0; } @@ -1328,9 +1332,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, p = buflen >= 5 ? buffer[4] : 0; if (buflen < 5 || buflen < 6 + p) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d SELECTOR_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videocontrol interface %d SELECTOR_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1354,9 +1358,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, p = dev->uvc_version >= 0x0110 ? 10 : 9; if (buflen < p + n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d PROCESSING_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videocontrol interface %d PROCESSING_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1387,9 +1391,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, n = buflen >= 24 + p ? buffer[22+p] : 0; if (buflen < 24 + p + n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " - "interface %d EXTENSION_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_DESCR, + "device %d videocontrol interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1414,8 +1418,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, break; default: - uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE " - "descriptor (%u)\n", buffer[2]); + uvc_trace(dev, UVC_TRACE_DESCR, + "Found an unknown CS_INTERFACE descriptor (%u)\n", + buffer[2]); break; } @@ -1460,8 +1465,9 @@ next_descriptor: if (usb_endpoint_is_int_in(desc) && le16_to_cpu(desc->wMaxPacketSize) >= 8 && desc->bInterval != 0) { - uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint " - "(addr %02x).\n", desc->bEndpointAddress); + uvc_trace(dev, UVC_TRACE_DESCR, + "Found a Status endpoint (addr %02x).\n", + desc->bEndpointAddress); dev->int_ep = ep; } } @@ -1609,8 +1615,9 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, uvc_trace_cont(UVC_TRACE_PROBE, " <- XU %d", entity->id); if (entity->bNrInPins != 1) { - uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " - "than 1 input pin.\n", entity->id); + uvc_trace(chain->dev, UVC_TRACE_DESCR, + "Extension unit %d has more than 1 input pin.\n", + entity->id); return -1; } @@ -1620,8 +1627,8 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, uvc_trace_cont(UVC_TRACE_PROBE, " <- PU %d", entity->id); if (chain->processing != NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found multiple " - "Processing Units in chain.\n"); + uvc_trace(chain->dev, UVC_TRACE_DESCR, + "Found multiple Processing Units in chain.\n"); return -1; } @@ -1636,8 +1643,8 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, break; if (chain->selector != NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " - "Units in chain.\n"); + uvc_trace(chain->dev, UVC_TRACE_DESCR, + "Found multiple Selector Units in chain.\n"); return -1; } @@ -1667,8 +1674,9 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, break; default: - uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type " - "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity)); + uvc_trace(chain->dev, UVC_TRACE_DESCR, + "Unsupported entity type 0x%04x found in chain.\n", + UVC_ENTITY_TYPE(entity)); return -1; } @@ -1694,16 +1702,17 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, if (forward == prev) continue; if (forward->chain.next || forward->chain.prev) { - uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "entity %d already in chain.\n", forward->id); + uvc_trace(chain->dev, UVC_TRACE_DESCR, + "Found reference to entity %d already in chain.\n", + forward->id); return -EINVAL; } switch (UVC_ENTITY_TYPE(forward)) { case UVC_VC_EXTENSION_UNIT: if (forward->bNrInPins != 1) { - uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " - "has more than 1 input pin.\n", + uvc_trace(chain->dev, UVC_TRACE_DESCR, + "Extension unit %d has more than 1 input pin.\n", entity->id); return -EINVAL; } @@ -1721,8 +1730,9 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: case UVC_TT_STREAMING: if (UVC_ENTITY_IS_ITERM(forward)) { - uvc_trace(UVC_TRACE_DESCR, "Unsupported input " - "terminal %u.\n", forward->id); + uvc_trace(chain->dev, UVC_TRACE_DESCR, + "Unsupported input terminal %u.\n", + forward->id); return -EINVAL; } @@ -1768,16 +1778,16 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, id = entity->baSourceID[i]; term = uvc_entity_by_id(chain->dev, id); if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { - uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " - "input %d isn't connected to an " - "input terminal\n", entity->id, i); + uvc_trace(chain->dev, UVC_TRACE_DESCR, + "Selector unit %d input %d isn't connected to an input terminal\n", + entity->id, i); return -1; } if (term->chain.next || term->chain.prev) { - uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "entity %d already in chain.\n", - term->id); + uvc_trace(chain->dev, UVC_TRACE_DESCR, + "Found reference to entity %d already in chain.\n", + term->id); return -EINVAL; } @@ -1810,8 +1820,8 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, entity = uvc_entity_by_id(chain->dev, id); if (entity == NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "unknown entity %d.\n", id); + uvc_trace(chain->dev, UVC_TRACE_DESCR, + "Found reference to unknown entity %d.\n", id); return -EINVAL; } @@ -1824,7 +1834,7 @@ static int uvc_scan_chain(struct uvc_video_chain *chain, { struct uvc_entity *entity, *prev; - uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:"); + uvc_trace(chain->dev, UVC_TRACE_PROBE, "Scanning UVC chain:"); entity = term; prev = NULL; @@ -1832,8 +1842,9 @@ static int uvc_scan_chain(struct uvc_video_chain *chain, while (entity != NULL) { /* Entity must not be part of an existing chain */ if (entity->chain.next || entity->chain.prev) { - uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "entity %d already in chain.\n", entity->id); + uvc_trace(chain->dev, UVC_TRACE_DESCR, + "Found reference to entity %d already in chain.\n", + entity->id); return -EINVAL; } @@ -1987,7 +1998,7 @@ static int uvc_scan_fallback(struct uvc_device *dev) list_add_tail(&chain->list, &dev->chains); - uvc_trace(UVC_TRACE_PROBE, + uvc_trace(dev, UVC_TRACE_PROBE, "Found a video chain by fallback heuristic (%s).\n", uvc_print_chain(chain)); @@ -2031,7 +2042,8 @@ static int uvc_scan_device(struct uvc_device *dev) continue; } - uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n", + uvc_trace(dev, UVC_TRACE_PROBE, + "Found a valid video chain (%s).\n", uvc_print_chain(chain)); list_add_tail(&chain->list, &dev->chains); @@ -2312,14 +2324,6 @@ static int uvc_probe(struct usb_interface *intf, int function; int ret; - if (id->idVendor && id->idProduct) - uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s " - "(%04x:%04x)\n", udev->devpath, id->idVendor, - id->idProduct); - else - uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n", - udev->devpath); - /* Allocate memory for the device and initialize it. */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) @@ -2339,6 +2343,14 @@ static int uvc_probe(struct usb_interface *intf, dev->quirks = uvc_quirks_param == -1 ? dev->info->quirks : uvc_quirks_param; + if (id->idVendor && id->idProduct) + uvc_trace(dev, UVC_TRACE_PROBE, + "Probing known UVC device %s (%04x:%04x)\n", + udev->devpath, id->idVendor, id->idProduct); + else + uvc_trace(dev, UVC_TRACE_PROBE, + "Probing generic UVC device %s\n", udev->devpath); + if (udev->product != NULL) strscpy(dev->name, udev->product, sizeof(dev->name)); else @@ -2381,14 +2393,14 @@ static int uvc_probe(struct usb_interface *intf, /* Parse the Video Class control descriptor. */ if (uvc_parse_control(dev) < 0) { - uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC " - "descriptors.\n"); + uvc_trace(dev, UVC_TRACE_PROBE, + "Unable to parse UVC descriptors.\n"); goto error; } /* Parse the associated GPIOs. */ if (uvc_gpio_parse(dev) < 0) { - uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC GPIOs\n"); + uvc_trace(dev, UVC_TRACE_PROBE, "Unable to parse UVC GPIOs\n"); goto error; } @@ -2450,7 +2462,7 @@ static int uvc_probe(struct usb_interface *intf, goto error; } - uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); + uvc_trace(dev, UVC_TRACE_PROBE, "UVC device initialized.\n"); usb_enable_autosuspend(udev); return 0; @@ -2482,7 +2494,7 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message) struct uvc_device *dev = usb_get_intfdata(intf); struct uvc_streaming *stream; - uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", + uvc_trace(dev, UVC_TRACE_SUSPEND, "Suspending interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); /* Controls are cached on the fly so they don't need to be saved. */ @@ -2500,8 +2512,8 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message) return uvc_video_suspend(stream); } - uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface " - "mismatch.\n"); + uvc_trace(dev, UVC_TRACE_SUSPEND, + "Suspend: video streaming USB interface mismatch.\n"); return -EINVAL; } @@ -2511,8 +2523,8 @@ static int __uvc_resume(struct usb_interface *intf, int reset) struct uvc_streaming *stream; int ret = 0; - uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", - intf->cur_altsetting->desc.bInterfaceNumber); + uvc_trace(dev, UVC_TRACE_SUSPEND, "Resuming interface %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); if (intf->cur_altsetting->desc.bInterfaceSubClass == UVC_SC_VIDEOCONTROL) { @@ -2540,8 +2552,8 @@ static int __uvc_resume(struct usb_interface *intf, int reset) } } - uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface " - "mismatch.\n"); + uvc_trace(dev, UVC_TRACE_SUSPEND, + "Resume: video streaming USB interface mismatch.\n"); return -EINVAL; } diff --git a/drivers/media/usb/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c index 135fd7fe6852..445c4d39aeff 100644 --- a/drivers/media/usb/uvc/uvc_isight.c +++ b/drivers/media/usb/uvc/uvc_isight.c @@ -40,6 +40,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, 0xde, 0xad, 0xfa, 0xce }; + struct uvc_streaming *stream = uvc_queue_to_stream(queue); unsigned int maxlen, nbytes; u8 *mem; int is_header = 0; @@ -49,15 +50,16 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) || (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) { - uvc_trace(UVC_TRACE_FRAME, "iSight header found\n"); + uvc_trace(stream->dev, UVC_TRACE_FRAME, + "iSight header found\n"); is_header = 1; } /* Synchronize to the input stream by waiting for a header packet. */ if (buf->state != UVC_BUF_STATE_ACTIVE) { if (!is_header) { - uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of " - "sync).\n"); + uvc_trace(stream->dev, UVC_TRACE_FRAME, + "Dropping packet (out of sync).\n"); return 0; } @@ -85,8 +87,8 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, buf->bytesused += nbytes; if (len > maxlen || buf->bytesused == buf->length) { - uvc_trace(UVC_TRACE_FRAME, "Frame complete " - "(overflow).\n"); + uvc_trace(stream->dev, UVC_TRACE_FRAME, + "Frame complete (overflow).\n"); buf->state = UVC_BUF_STATE_DONE; } } @@ -103,8 +105,8 @@ void uvc_video_decode_isight(struct uvc_urb *uvc_urb, struct uvc_buffer *buf, for (i = 0; i < urb->number_of_packets; ++i) { if (urb->iso_frame_desc[i].status < 0) { - uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " - "lost (%d).\n", + uvc_trace(stream->dev, UVC_TRACE_FRAME, + "USB isochronous frame lost (%d).\n", urb->iso_frame_desc[i].status); } diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index cd60c6c1749e..2255daa3e240 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -32,12 +32,6 @@ * the driver. */ -static inline struct uvc_streaming * -uvc_queue_to_stream(struct uvc_video_queue *queue) -{ - return container_of(queue, struct uvc_streaming, queue); -} - static inline struct uvc_buffer *uvc_vbuf_to_buffer(struct vb2_v4l2_buffer *buf) { return container_of(buf, struct uvc_buffer, buf); @@ -109,7 +103,8 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n"); + uvc_trace(uvc_queue_to_stream(queue)->dev, UVC_TRACE_CAPTURE, + "[E] Bytes used out of bounds.\n"); return -EINVAL; } diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 4999fa585844..69434273d2c5 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -93,20 +93,20 @@ static void uvc_event_streaming(struct uvc_device *dev, struct uvc_streaming_status *status, int len) { if (len < 3) { - uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " - "received.\n"); + uvc_trace(dev, UVC_TRACE_STATUS, + "Invalid streaming status event received.\n"); return; } if (status->bEvent == 0) { if (len < 4) return; - uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", + uvc_trace(dev, UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", status->bOriginator, status->bValue[0] ? "pressed" : "released", len); uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]); } else { - uvc_trace(UVC_TRACE_STATUS, + uvc_trace(dev, UVC_TRACE_STATUS, "Stream %u error event %02x len %d.\n", status->bOriginator, status->bEvent, len); } @@ -163,12 +163,12 @@ static bool uvc_event_control(struct urb *urb, if (len < 6 || status->bEvent != 0 || status->bAttribute >= ARRAY_SIZE(attrs)) { - uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " - "received.\n"); + uvc_trace(dev, UVC_TRACE_STATUS, + "Invalid control status event received.\n"); return false; } - uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", + uvc_trace(dev, UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", status->bOriginator, status->bSelector, attrs[status->bAttribute], len); @@ -237,8 +237,9 @@ static void uvc_status_complete(struct urb *urb) } default: - uvc_trace(UVC_TRACE_STATUS, "Unknown status event " - "type %u.\n", dev->status[0]); + uvc_trace(dev, UVC_TRACE_STATUS, + "Unknown status event type %u.\n", + dev->status[0]); break; } } diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index c7172b8952a9..42fac7d302d3 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -75,8 +75,9 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, break; default: - uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type " - "%u.\n", xmap->v4l2_type); + uvc_trace(chain->dev, UVC_TRACE_CONTROL, + "Unsupported V4L2 control type %u.\n", + xmap->v4l2_type); ret = -ENOTTY; goto free_map; } @@ -164,10 +165,11 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, return -EINVAL; fcc = (u8 *)&fmt->fmt.pix.pixelformat; - uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", - fmt->fmt.pix.pixelformat, - fcc[0], fcc[1], fcc[2], fcc[3], - fmt->fmt.pix.width, fmt->fmt.pix.height); + uvc_trace(stream->dev, UVC_TRACE_FORMAT, + "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", + fmt->fmt.pix.pixelformat, + fcc[0], fcc[1], fcc[2], fcc[3], + fmt->fmt.pix.width, fmt->fmt.pix.height); /* Check if the hardware supports the requested format, use the default * format otherwise. @@ -207,16 +209,18 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, } if (frame == NULL) { - uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n", - fmt->fmt.pix.width, fmt->fmt.pix.height); + uvc_trace(stream->dev, UVC_TRACE_FORMAT, + "Unsupported size %ux%u.\n", fmt->fmt.pix.width, + fmt->fmt.pix.height); return -EINVAL; } /* Use the default frame interval. */ interval = frame->dwDefaultFrameInterval; - uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us " - "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval, - (100000000/interval)%10); + uvc_trace(stream->dev, UVC_TRACE_FORMAT, + "Using default frame interval %u.%u us (%u.%u fps).\n", + interval / 10, interval % 10, 10000000 / interval, + (100000000 / interval) % 10); /* Set the format index, frame index and frame interval. */ memset(probe, 0, sizeof(*probe)); @@ -260,7 +264,7 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, } if (i == stream->nformats) - uvc_trace(UVC_TRACE_FORMAT, + uvc_trace(stream->dev, UVC_TRACE_FORMAT, "Unknown bFormatIndex %u, using default\n", probe->bFormatIndex); @@ -272,7 +276,7 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, } if (i == format->nframes) - uvc_trace(UVC_TRACE_FORMAT, + uvc_trace(stream->dev, UVC_TRACE_FORMAT, "Unknown bFrameIndex %u, using default\n", probe->bFrameIndex); @@ -416,8 +420,9 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, interval = uvc_fraction_to_interval(timeperframe.numerator, timeperframe.denominator); - uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n", - timeperframe.numerator, timeperframe.denominator, interval); + uvc_trace(stream->dev, UVC_TRACE_FORMAT, + "Setting frame interval to %u/%u (%u).\n", + timeperframe.numerator, timeperframe.denominator, interval); mutex_lock(&stream->mutex); @@ -545,8 +550,8 @@ static int uvc_v4l2_open(struct file *file) struct uvc_fh *handle; int ret = 0; - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); stream = video_drvdata(file); + uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__); ret = usb_autopm_get_interface(stream->dev->intf); if (ret < 0) @@ -588,7 +593,7 @@ static int uvc_v4l2_release(struct file *file) struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); + uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__); /* Only free resources if this is a privileged handle. */ if (uvc_has_privileges(handle)) @@ -1461,7 +1466,11 @@ static long uvc_v4l2_compat_ioctl32(struct file *file, static ssize_t uvc_v4l2_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); + struct uvc_fh *handle = file->private_data; + struct uvc_streaming *stream = handle->stream; + + uvc_trace(stream->dev, UVC_TRACE_CALLS, + "uvc_v4l2_read: not implemented.\n"); return -EINVAL; } @@ -1470,7 +1479,7 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n"); + uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__); return uvc_queue_mmap(&stream->queue, vma); } @@ -1480,7 +1489,7 @@ static __poll_t uvc_v4l2_poll(struct file *file, poll_table *wait) struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); + uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__); return uvc_queue_poll(&stream->queue, file, wait); } @@ -1493,7 +1502,7 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_get_unmapped_area\n"); + uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__); return uvc_queue_get_unmapped_area(&stream->queue, pgoff); } diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 71e643e6d006..26251ed0c3fd 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -95,7 +95,7 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, if (ret != 1) return ret < 0 ? ret : -EPIPE; - uvc_trace(UVC_TRACE_CONTROL, "Control error %u\n", error); + uvc_trace(dev, UVC_TRACE_CONTROL, "Control error %u\n", error); switch (error) { case 0: @@ -705,8 +705,8 @@ void uvc_video_clock_update(struct uvc_streaming *stream, sof = y; - uvc_trace(UVC_TRACE_CLOCK, "%s: PTS %u y %llu.%06llu SOF %u.%06llu " - "(x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n", + uvc_trace(stream->dev, UVC_TRACE_CLOCK, + "%s: PTS %u y %llu.%06llu SOF %u.%06llu (x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n", stream->dev->name, buf->pts, y >> 16, div_u64((y & 0xffff) * 1000000, 65536), sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), @@ -740,8 +740,8 @@ void uvc_video_clock_update(struct uvc_streaming *stream, timestamp = ktime_to_ns(first->host_time) + y - y1; - uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %llu " - "buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n", + uvc_trace(stream->dev, UVC_TRACE_CLOCK, + "%s: SOF %u.%06llu y %llu ts %llu buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n", stream->dev->name, sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), y, timestamp, vbuf->vb2_buf.timestamp, @@ -875,9 +875,8 @@ static void uvc_video_stats_update(struct uvc_streaming *stream) { struct uvc_stats_frame *frame = &stream->stats.frame; - uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, " - "%u/%u/%u pts (%searly %sinitial), %u/%u scr, " - "last pts/stc/sof %u/%u/%u\n", + uvc_trace(stream->dev, UVC_TRACE_STATS, + "frame %u stats: %u/%u/%u packets, %u/%u/%u pts (%searly %sinitial), %u/%u scr, last pts/stc/sof %u/%u/%u\n", stream->sequence, frame->first_data, frame->nb_packets - frame->nb_empty, frame->nb_packets, frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts, @@ -1039,8 +1038,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, /* Mark the buffer as bad if the error bit is set. */ if (data[1] & UVC_STREAM_ERR) { - uvc_trace(UVC_TRACE_FRAME, "Marking buffer as bad (error bit " - "set).\n"); + uvc_trace(stream->dev, UVC_TRACE_FRAME, + "Marking buffer as bad (error bit set).\n"); buf->error = 1; } @@ -1054,8 +1053,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, */ if (buf->state != UVC_BUF_STATE_ACTIVE) { if (fid == stream->last_fid) { - uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of " - "sync).\n"); + uvc_trace(stream->dev, UVC_TRACE_FRAME, + "Dropping payload (out of sync).\n"); if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && (data[1] & UVC_STREAM_EOF)) stream->last_fid ^= UVC_STREAM_FID; @@ -1086,8 +1085,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, * previous payload had the EOF bit set. */ if (fid != stream->last_fid && buf->bytesused != 0) { - uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit " - "toggled).\n"); + uvc_trace(stream->dev, UVC_TRACE_FRAME, + "Frame complete (FID bit toggled).\n"); buf->state = UVC_BUF_STATE_READY; return -EAGAIN; } @@ -1148,7 +1147,8 @@ static void uvc_video_decode_data(struct uvc_urb *uvc_urb, /* Complete the current frame if the buffer size was exceeded. */ if (len > maxlen) { - uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n"); + uvc_trace(uvc_urb->stream->dev, UVC_TRACE_FRAME, + "Frame complete (overflow).\n"); buf->error = 1; buf->state = UVC_BUF_STATE_READY; } @@ -1161,9 +1161,11 @@ static void uvc_video_decode_end(struct uvc_streaming *stream, { /* Mark the buffer as done if the EOF marker is set. */ if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) { - uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n"); + uvc_trace(stream->dev, UVC_TRACE_FRAME, + "Frame complete (EOF found).\n"); if (data[0] == len) - uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n"); + uvc_trace(stream->dev, UVC_TRACE_FRAME, + "EOF in empty payload.\n"); buf->state = UVC_BUF_STATE_READY; if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) stream->last_fid ^= UVC_STREAM_FID; @@ -1279,7 +1281,7 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream, memcpy(&meta->length, mem, length); meta_buf->bytesused += length + sizeof(meta->ns) + sizeof(meta->sof); - uvc_trace(UVC_TRACE_FRAME, + uvc_trace(stream->dev, UVC_TRACE_FRAME, "%s(): t-sys %lluns, SOF %u, len %u, flags 0x%x, PTS %u, STC %u frame SOF %u\n", __func__, ktime_to_ns(time), meta->sof, meta->length, meta->flags, @@ -1339,8 +1341,9 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb, for (i = 0; i < urb->number_of_packets; ++i) { if (urb->iso_frame_desc[i].status < 0) { - uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " - "lost (%d).\n", urb->iso_frame_desc[i].status); + uvc_trace(stream->dev, UVC_TRACE_FRAME, + "USB isochronous frame lost (%d).\n", + urb->iso_frame_desc[i].status); /* Mark the buffer as faulty. */ if (buf != NULL) buf->error = 1; @@ -1628,15 +1631,16 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, } if (i == UVC_URBS) { - uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers " - "of %ux%u bytes each.\n", UVC_URBS, npackets, - psize); + uvc_trace(stream->dev, UVC_TRACE_VIDEO, + "Allocated %u URB buffers of %ux%u bytes each.\n", + UVC_URBS, npackets, psize); return npackets; } } - uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes " - "per packet).\n", psize); + uvc_trace(stream->dev, UVC_TRACE_VIDEO, + "Failed to allocate URB buffers (%u bytes per packet).\n", + psize); return 0; } @@ -1835,12 +1839,13 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream, bandwidth = stream->ctrl.dwMaxPayloadTransferSize; if (bandwidth == 0) { - uvc_trace(UVC_TRACE_VIDEO, "Device requested null " - "bandwidth, defaulting to lowest.\n"); + uvc_trace(stream->dev, UVC_TRACE_VIDEO, + "Device requested null bandwidth, defaulting to lowest.\n"); bandwidth = 1; } else { - uvc_trace(UVC_TRACE_VIDEO, "Device requested %u " - "B/frame bandwidth.\n", bandwidth); + uvc_trace(stream->dev, UVC_TRACE_VIDEO, + "Device requested %u B/frame bandwidth.\n", + bandwidth); } for (i = 0; i < intf->num_altsetting; ++i) { @@ -1863,13 +1868,14 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream, } if (best_ep == NULL) { - uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting " - "for requested bandwidth.\n"); + uvc_trace(stream->dev, UVC_TRACE_VIDEO, + "No fast enough alt setting for requested bandwidth.\n"); return -EIO; } - uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u " - "(%u B/frame bandwidth).\n", altsetting, best_psize); + uvc_trace(stream->dev, UVC_TRACE_VIDEO, + "Selecting alternate setting %u (%u B/frame bandwidth).\n", + altsetting, best_psize); ret = usb_set_interface(stream->dev->udev, intfnum, altsetting); if (ret < 0) diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 6687949d93d9..34c1e8a024ac 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -752,10 +752,11 @@ extern unsigned int uvc_trace_param; extern unsigned int uvc_timeout_param; extern unsigned int uvc_hw_timestamps_param; -#define uvc_trace(flag, fmt, ...) \ +#define uvc_trace(_dev, flag, fmt, ...) \ do { \ if (uvc_trace_param & flag) \ - printk(KERN_DEBUG "uvcvideo: " fmt, ##__VA_ARGS__); \ + dev_printk(KERN_DEBUG, &(_dev)->udev->dev, fmt, \ + ##__VA_ARGS__); \ } while (0) #define uvc_trace_cont(flag, fmt, ...) \ @@ -817,6 +818,12 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) return vb2_is_streaming(&queue->queue); } +static inline struct uvc_streaming * +uvc_queue_to_stream(struct uvc_video_queue *queue) +{ + return container_of(queue, struct uvc_streaming, queue); +} + /* V4L2 interface */ extern const struct v4l2_ioctl_ops uvc_ioctl_ops; extern const struct v4l2_file_operations uvc_fops; -- cgit v1.2.3 From 9e56380ae62543fc2043715050b80d494b2b1553 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 23 Dec 2020 14:35:27 +0100 Subject: media: uvcvideo: Rename debug functions trace isn't a good name as it's not a trace mechanism, it is a typical debug mechanism. Rename uvc_trace/uvc_trace_cont macros to uvc_dbg/uvc_dbg_cont. Rename uvc_trace_param to uvc_dbg_param Rename UVC_TRACE_ defines to UVC_DBG_ Use ## concatenation in uvc_dbg macros to avoid overly long and repetitive UVC_DBG uses Signed-off-by: Joe Perches Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 81 +++++---- drivers/media/usb/uvc/uvc_driver.c | 343 ++++++++++++++++++------------------- drivers/media/usb/uvc/uvc_isight.c | 17 +- drivers/media/usb/uvc/uvc_queue.c | 4 +- drivers/media/usb/uvc/uvc_status.c | 29 ++-- drivers/media/usb/uvc/uvc_v4l2.c | 57 +++--- drivers/media/usb/uvc/uvc_video.c | 120 +++++++------ drivers/media/usb/uvc/uvcvideo.h | 34 ++-- 8 files changed, 333 insertions(+), 352 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index befaeb90e569..b3dde98499f4 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -906,8 +906,8 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, } if (ctrl == NULL && !next) - uvc_trace(chain->dev, UVC_TRACE_CONTROL, - "Control 0x%08x not found.\n", v4l2_id); + uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n", + v4l2_id); return ctrl; } @@ -1800,9 +1800,9 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum, info->selector, data, 2); if (ret < 0) { - uvc_trace(dev, UVC_TRACE_CONTROL, - "GET_LEN failed on control %pUl/%u (%d).\n", - info->entity, info->selector, ret); + uvc_dbg(dev, CONTROL, + "GET_LEN failed on control %pUl/%u (%d)\n", + info->entity, info->selector, ret); goto done; } @@ -1813,20 +1813,20 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, ret = uvc_ctrl_get_flags(dev, ctrl, info); if (ret < 0) { - uvc_trace(dev, UVC_TRACE_CONTROL, - "Failed to get flags for control %pUl/%u (%d).\n", - info->entity, info->selector, ret); + uvc_dbg(dev, CONTROL, + "Failed to get flags for control %pUl/%u (%d)\n", + info->entity, info->selector, ret); goto done; } uvc_ctrl_fixup_xu_info(dev, ctrl, info); - uvc_trace(dev, UVC_TRACE_CONTROL, - "XU control %pUl/%u queried: len %u, flags { get %u set %u auto %u }.\n", - info->entity, info->selector, info->size, - (info->flags & UVC_CTRL_FLAG_GET_CUR) ? 1 : 0, - (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0, - (info->flags & UVC_CTRL_FLAG_AUTO_UPDATE) ? 1 : 0); + uvc_dbg(dev, CONTROL, + "XU control %pUl/%u queried: len %u, flags { get %u set %u auto %u }\n", + info->entity, info->selector, info->size, + (info->flags & UVC_CTRL_FLAG_GET_CUR) ? 1 : 0, + (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0, + (info->flags & UVC_CTRL_FLAG_AUTO_UPDATE) ? 1 : 0); done: kfree(data); @@ -1851,10 +1851,10 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev, ret = uvc_ctrl_add_info(dev, ctrl, &info); if (ret < 0) - uvc_trace(dev, UVC_TRACE_CONTROL, - "Failed to initialize control %pUl/%u on device %s entity %u\n", - info.entity, info.selector, dev->udev->devpath, - ctrl->entity->id); + uvc_dbg(dev, CONTROL, + "Failed to initialize control %pUl/%u on device %s entity %u\n", + info.entity, info.selector, dev->udev->devpath, + ctrl->entity->id); return ret; } @@ -1882,8 +1882,8 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, } if (!found) { - uvc_trace(chain->dev, UVC_TRACE_CONTROL, - "Extension unit %u not found.\n", xqry->unit); + uvc_dbg(chain->dev, CONTROL, "Extension unit %u not found\n", + xqry->unit); return -ENOENT; } @@ -1898,9 +1898,8 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, } if (!found) { - uvc_trace(chain->dev, UVC_TRACE_CONTROL, - "Control %pUl/%u not found.\n", entity->guid, - xqry->selector); + uvc_dbg(chain->dev, CONTROL, "Control %pUl/%u not found\n", + entity->guid, xqry->selector); return -ENOENT; } @@ -2048,10 +2047,9 @@ static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, ctrl->initialized = 1; - uvc_trace(dev, UVC_TRACE_CONTROL, - "Added control %pUl/%u to device %s entity %u\n", - ctrl->info.entity, ctrl->info.selector, dev->udev->devpath, - ctrl->entity->id); + uvc_dbg(dev, CONTROL, "Added control %pUl/%u to device %s entity %u\n", + ctrl->info.entity, ctrl->info.selector, dev->udev->devpath, + ctrl->entity->id); return 0; } @@ -2088,9 +2086,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev, map->set = uvc_set_le_value; list_add_tail(&map->list, &ctrl->info.mappings); - uvc_trace(dev, UVC_TRACE_CONTROL, - "Adding mapping '%s' to control %pUl/%u.\n", - map->name, ctrl->info.entity, ctrl->info.selector); + uvc_dbg(dev, CONTROL, "Adding mapping '%s' to control %pUl/%u\n", + map->name, ctrl->info.entity, ctrl->info.selector); return 0; } @@ -2106,9 +2103,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, int ret; if (mapping->id & ~V4L2_CTRL_ID_MASK) { - uvc_trace(dev, UVC_TRACE_CONTROL, - "Can't add mapping '%s', control id 0x%08x is invalid.\n", - mapping->name, mapping->id); + uvc_dbg(dev, CONTROL, + "Can't add mapping '%s', control id 0x%08x is invalid\n", + mapping->name, mapping->id); return -EINVAL; } @@ -2153,9 +2150,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, list_for_each_entry(map, &ctrl->info.mappings, list) { if (mapping->id == map->id) { - uvc_trace(dev, UVC_TRACE_CONTROL, - "Can't add mapping '%s', control id 0x%08x already exists.\n", - mapping->name, mapping->id); + uvc_dbg(dev, CONTROL, + "Can't add mapping '%s', control id 0x%08x already exists\n", + mapping->name, mapping->id); ret = -EEXIST; goto done; } @@ -2164,9 +2161,9 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, /* Prevent excess memory consumption */ if (atomic_inc_return(&dev->nmappings) > UVC_MAX_CONTROL_MAPPINGS) { atomic_dec(&dev->nmappings); - uvc_trace(dev, UVC_TRACE_CONTROL, - "Can't add mapping '%s', maximum mappings count (%u) exceeded.\n", - mapping->name, UVC_MAX_CONTROL_MAPPINGS); + uvc_dbg(dev, CONTROL, + "Can't add mapping '%s', maximum mappings count (%u) exceeded\n", + mapping->name, UVC_MAX_CONTROL_MAPPINGS); ret = -ENOMEM; goto done; } @@ -2235,9 +2232,9 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev, !uvc_test_bit(controls, blacklist[i].index)) continue; - uvc_trace(dev, UVC_TRACE_CONTROL, - "%u/%u control is black listed, removing it.\n", - entity->id, blacklist[i].index); + uvc_dbg(dev, CONTROL, + "%u/%u control is black listed, removing it\n", + entity->id, blacklist[i].index); uvc_clear_bit(controls, blacklist[i].index); } diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 794f1aecd77a..1abc122a0977 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -32,7 +32,7 @@ unsigned int uvc_clock_param = CLOCK_MONOTONIC; unsigned int uvc_hw_timestamps_param; unsigned int uvc_no_drop_param; static unsigned int uvc_quirks_param = -1; -unsigned int uvc_trace_param; +unsigned int uvc_dbg_param; unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; /* ------------------------------------------------------------------------ @@ -520,10 +520,10 @@ static int uvc_parse_format(struct uvc_device *dev, case UVC_VS_FORMAT_FRAME_BASED: n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; if (buflen < n) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d FORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); return -EINVAL; } @@ -584,10 +584,10 @@ static int uvc_parse_format(struct uvc_device *dev, case UVC_VS_FORMAT_MJPEG: if (buflen < 11) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d FORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); return -EINVAL; } @@ -600,10 +600,10 @@ static int uvc_parse_format(struct uvc_device *dev, case UVC_VS_FORMAT_DV: if (buflen < 9) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d FORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); return -EINVAL; } @@ -618,10 +618,10 @@ static int uvc_parse_format(struct uvc_device *dev, strscpy(format->name, "HD-DV", sizeof(format->name)); break; default: - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d: unknown DV format %u\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber, buffer[8]); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d: unknown DV format %u\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber, buffer[8]); return -EINVAL; } @@ -647,14 +647,14 @@ static int uvc_parse_format(struct uvc_device *dev, case UVC_VS_FORMAT_STREAM_BASED: /* Not supported yet. */ default: - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d unsupported format %u\n", - dev->udev->devnum, alts->desc.bInterfaceNumber, - buffer[2]); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d unsupported format %u\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, + buffer[2]); return -EINVAL; } - uvc_trace(dev, UVC_TRACE_DESCR, "Found format %s.\n", format->name); + uvc_dbg(dev, DESCR, "Found format %s\n", format->name); buflen -= buffer[0]; buffer += buffer[0]; @@ -673,10 +673,10 @@ static int uvc_parse_format(struct uvc_device *dev, n = n ? n : 3; if (buflen < 26 + 4*n) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d FRAME error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d FRAME error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); return -EINVAL; } @@ -738,10 +738,10 @@ static int uvc_parse_format(struct uvc_device *dev, frame->dwDefaultFrameInterval; } - uvc_trace(dev, UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", - frame->wWidth, frame->wHeight, - 10000000 / frame->dwDefaultFrameInterval, - (100000000 / frame->dwDefaultFrameInterval) % 10); + uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n", + frame->wWidth, frame->wHeight, + 10000000 / frame->dwDefaultFrameInterval, + (100000000 / frame->dwDefaultFrameInterval) % 10); format->nframes++; buflen -= buffer[0]; @@ -757,10 +757,10 @@ static int uvc_parse_format(struct uvc_device *dev, if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && buffer[2] == UVC_VS_COLORFORMAT) { if (buflen < 6) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d COLORFORMAT error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d COLORFORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); return -EINVAL; } @@ -792,18 +792,18 @@ static int uvc_parse_streaming(struct uvc_device *dev, if (intf->cur_altsetting->desc.bInterfaceSubClass != UVC_SC_VIDEOSTREAMING) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d interface %d isn't a video streaming interface\n", - dev->udev->devnum, - intf->altsetting[0].desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d interface %d isn't a video streaming interface\n", + dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); return -EINVAL; } if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d interface %d is already claimed\n", - dev->udev->devnum, - intf->altsetting[0].desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d interface %d is already claimed\n", + dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); return -EINVAL; } @@ -825,9 +825,9 @@ static int uvc_parse_streaming(struct uvc_device *dev, if (ep->extralen > 2 && ep->extra[1] == USB_DT_CS_INTERFACE) { - uvc_trace(dev, UVC_TRACE_DESCR, - "trying extra data from endpoint %u.\n", - i); + uvc_dbg(dev, DESCR, + "trying extra data from endpoint %u\n", + i); buffer = alts->endpoint[i].extra; buflen = alts->endpoint[i].extralen; break; @@ -842,8 +842,8 @@ static int uvc_parse_streaming(struct uvc_device *dev, } if (buflen <= 2) { - uvc_trace(dev, UVC_TRACE_DESCR, - "no class-specific streaming interface descriptors found.\n"); + uvc_dbg(dev, DESCR, + "no class-specific streaming interface descriptors found\n"); goto error; } @@ -860,9 +860,9 @@ static int uvc_parse_streaming(struct uvc_device *dev, break; default: - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d HEADER descriptor not found.\n", - dev->udev->devnum, alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d HEADER descriptor not found\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); goto error; } @@ -870,9 +870,9 @@ static int uvc_parse_streaming(struct uvc_device *dev, n = buflen >= size ? buffer[size-1] : 0; if (buflen < size + p*n) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d HEADER descriptor is invalid.\n", - dev->udev->devnum, alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d HEADER descriptor is invalid\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); goto error; } @@ -922,10 +922,10 @@ static int uvc_parse_streaming(struct uvc_device *dev, case UVC_VS_FORMAT_MPEG2TS: case UVC_VS_FORMAT_STREAM_BASED: - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d FORMAT %u is not supported.\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber, _buffer[2]); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d FORMAT %u is not supported\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber, _buffer[2]); break; case UVC_VS_FRAME_UNCOMPRESSED: @@ -947,9 +947,9 @@ static int uvc_parse_streaming(struct uvc_device *dev, } if (nformats == 0) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d has no supported formats defined.\n", - dev->udev->devnum, alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d has no supported formats defined\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); goto error; } @@ -996,10 +996,9 @@ static int uvc_parse_streaming(struct uvc_device *dev, } if (buflen) - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videostreaming interface %d has %u bytes of trailing descriptor garbage.\n", - dev->udev->devnum, alts->desc.bInterfaceNumber, - buflen); + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d has %u bytes of trailing descriptor garbage\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); /* Parse the alternate settings to find the maximum bandwidth. */ for (i = 0; i < intf->num_altsetting; ++i) { @@ -1132,9 +1131,9 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, n = buflen >= 25 + p ? buffer[22+p] : 0; if (buflen < 25 + p + 2*n) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videocontrol interface %d EXTENSION_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videocontrol interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); break; } @@ -1181,9 +1180,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, n = buflen >= 12 ? buffer[11] : 0; if (buflen < 12 + n) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videocontrol interface %d HEADER error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videocontrol interface %d HEADER error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1194,9 +1193,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, for (i = 0; i < n; ++i) { intf = usb_ifnum_to_if(udev, buffer[12+i]); if (intf == NULL) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d interface %d doesn't exists\n", - udev->devnum, i); + uvc_dbg(dev, DESCR, + "device %d interface %d doesn't exists\n", + udev->devnum, i); continue; } @@ -1206,9 +1205,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, case UVC_VC_INPUT_TERMINAL: if (buflen < 8) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videocontrol interface %d INPUT_TERMINAL error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videocontrol interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1225,10 +1224,10 @@ static int uvc_parse_standard_control(struct uvc_device *dev, */ type = get_unaligned_le16(&buffer[4]); if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videocontrol interface %d INPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", - udev->devnum, alts->desc.bInterfaceNumber, - buffer[3], type); + uvc_dbg(dev, DESCR, + "device %d videocontrol interface %d INPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", + udev->devnum, alts->desc.bInterfaceNumber, + buffer[3], type); return 0; } @@ -1247,9 +1246,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, } if (buflen < len + n + p) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videocontrol interface %d INPUT_TERMINAL error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videocontrol interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1294,9 +1293,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, case UVC_VC_OUTPUT_TERMINAL: if (buflen < 9) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videocontrol interface %d OUTPUT_TERMINAL error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videocontrol interface %d OUTPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1305,10 +1304,10 @@ static int uvc_parse_standard_control(struct uvc_device *dev, */ type = get_unaligned_le16(&buffer[4]); if ((type & 0xff00) == 0) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videocontrol interface %d OUTPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", - udev->devnum, alts->desc.bInterfaceNumber, - buffer[3], type); + uvc_dbg(dev, DESCR, + "device %d videocontrol interface %d OUTPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", + udev->devnum, alts->desc.bInterfaceNumber, + buffer[3], type); return 0; } @@ -1332,9 +1331,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, p = buflen >= 5 ? buffer[4] : 0; if (buflen < 5 || buflen < 6 + p) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videocontrol interface %d SELECTOR_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videocontrol interface %d SELECTOR_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1358,9 +1357,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, p = dev->uvc_version >= 0x0110 ? 10 : 9; if (buflen < p + n) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videocontrol interface %d PROCESSING_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videocontrol interface %d PROCESSING_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1391,9 +1390,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, n = buflen >= 24 + p ? buffer[22+p] : 0; if (buflen < 24 + p + n) { - uvc_trace(dev, UVC_TRACE_DESCR, - "device %d videocontrol interface %d EXTENSION_UNIT error\n", - udev->devnum, alts->desc.bInterfaceNumber); + uvc_dbg(dev, DESCR, + "device %d videocontrol interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } @@ -1418,9 +1417,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, break; default: - uvc_trace(dev, UVC_TRACE_DESCR, - "Found an unknown CS_INTERFACE descriptor (%u)\n", - buffer[2]); + uvc_dbg(dev, DESCR, + "Found an unknown CS_INTERFACE descriptor (%u)\n", + buffer[2]); break; } @@ -1465,9 +1464,9 @@ next_descriptor: if (usb_endpoint_is_int_in(desc) && le16_to_cpu(desc->wMaxPacketSize) >= 8 && desc->bInterval != 0) { - uvc_trace(dev, UVC_TRACE_DESCR, - "Found a Status endpoint (addr %02x).\n", - desc->bEndpointAddress); + uvc_dbg(dev, DESCR, + "Found a Status endpoint (addr %02x)\n", + desc->bEndpointAddress); dev->int_ep = ep; } } @@ -1612,23 +1611,23 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, { switch (UVC_ENTITY_TYPE(entity)) { case UVC_VC_EXTENSION_UNIT: - uvc_trace_cont(UVC_TRACE_PROBE, " <- XU %d", entity->id); + uvc_dbg_cont(PROBE, " <- XU %d", entity->id); if (entity->bNrInPins != 1) { - uvc_trace(chain->dev, UVC_TRACE_DESCR, - "Extension unit %d has more than 1 input pin.\n", - entity->id); + uvc_dbg(chain->dev, DESCR, + "Extension unit %d has more than 1 input pin\n", + entity->id); return -1; } break; case UVC_VC_PROCESSING_UNIT: - uvc_trace_cont(UVC_TRACE_PROBE, " <- PU %d", entity->id); + uvc_dbg_cont(PROBE, " <- PU %d", entity->id); if (chain->processing != NULL) { - uvc_trace(chain->dev, UVC_TRACE_DESCR, - "Found multiple Processing Units in chain.\n"); + uvc_dbg(chain->dev, DESCR, + "Found multiple Processing Units in chain\n"); return -1; } @@ -1636,15 +1635,15 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, break; case UVC_VC_SELECTOR_UNIT: - uvc_trace_cont(UVC_TRACE_PROBE, " <- SU %d", entity->id); + uvc_dbg_cont(PROBE, " <- SU %d", entity->id); /* Single-input selector units are ignored. */ if (entity->bNrInPins == 1) break; if (chain->selector != NULL) { - uvc_trace(chain->dev, UVC_TRACE_DESCR, - "Found multiple Selector Units in chain.\n"); + uvc_dbg(chain->dev, DESCR, + "Found multiple Selector Units in chain\n"); return -1; } @@ -1654,29 +1653,29 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, case UVC_ITT_VENDOR_SPECIFIC: case UVC_ITT_CAMERA: case UVC_ITT_MEDIA_TRANSPORT_INPUT: - uvc_trace_cont(UVC_TRACE_PROBE, " <- IT %d\n", entity->id); + uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id); break; case UVC_OTT_VENDOR_SPECIFIC: case UVC_OTT_DISPLAY: case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: - uvc_trace_cont(UVC_TRACE_PROBE, " OT %d", entity->id); + uvc_dbg_cont(PROBE, " OT %d", entity->id); break; case UVC_TT_STREAMING: if (UVC_ENTITY_IS_ITERM(entity)) - uvc_trace_cont(UVC_TRACE_PROBE, " <- IT %d\n", entity->id); + uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id); else - uvc_trace_cont(UVC_TRACE_PROBE, " OT %d", entity->id); + uvc_dbg_cont(PROBE, " OT %d", entity->id); break; default: - uvc_trace(chain->dev, UVC_TRACE_DESCR, - "Unsupported entity type 0x%04x found in chain.\n", - UVC_ENTITY_TYPE(entity)); + uvc_dbg(chain->dev, DESCR, + "Unsupported entity type 0x%04x found in chain\n", + UVC_ENTITY_TYPE(entity)); return -1; } @@ -1702,26 +1701,26 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, if (forward == prev) continue; if (forward->chain.next || forward->chain.prev) { - uvc_trace(chain->dev, UVC_TRACE_DESCR, - "Found reference to entity %d already in chain.\n", - forward->id); + uvc_dbg(chain->dev, DESCR, + "Found reference to entity %d already in chain\n", + forward->id); return -EINVAL; } switch (UVC_ENTITY_TYPE(forward)) { case UVC_VC_EXTENSION_UNIT: if (forward->bNrInPins != 1) { - uvc_trace(chain->dev, UVC_TRACE_DESCR, - "Extension unit %d has more than 1 input pin.\n", - entity->id); + uvc_dbg(chain->dev, DESCR, + "Extension unit %d has more than 1 input pin\n", + entity->id); return -EINVAL; } list_add_tail(&forward->chain, &chain->entities); if (!found) - uvc_trace_cont(UVC_TRACE_PROBE, " (->"); + uvc_dbg_cont(PROBE, " (->"); - uvc_trace_cont(UVC_TRACE_PROBE, " XU %d", forward->id); + uvc_dbg_cont(PROBE, " XU %d", forward->id); found = 1; break; @@ -1730,23 +1729,23 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: case UVC_TT_STREAMING: if (UVC_ENTITY_IS_ITERM(forward)) { - uvc_trace(chain->dev, UVC_TRACE_DESCR, - "Unsupported input terminal %u.\n", - forward->id); + uvc_dbg(chain->dev, DESCR, + "Unsupported input terminal %u\n", + forward->id); return -EINVAL; } list_add_tail(&forward->chain, &chain->entities); if (!found) - uvc_trace_cont(UVC_TRACE_PROBE, " (->"); + uvc_dbg_cont(PROBE, " (->"); - uvc_trace_cont(UVC_TRACE_PROBE, " OT %d", forward->id); + uvc_dbg_cont(PROBE, " OT %d", forward->id); found = 1; break; } } if (found) - uvc_trace_cont(UVC_TRACE_PROBE, ")"); + uvc_dbg_cont(PROBE, ")"); return 0; } @@ -1771,33 +1770,33 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, break; } - uvc_trace_cont(UVC_TRACE_PROBE, " <- IT"); + uvc_dbg_cont(PROBE, " <- IT"); chain->selector = entity; for (i = 0; i < entity->bNrInPins; ++i) { id = entity->baSourceID[i]; term = uvc_entity_by_id(chain->dev, id); if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { - uvc_trace(chain->dev, UVC_TRACE_DESCR, - "Selector unit %d input %d isn't connected to an input terminal\n", - entity->id, i); + uvc_dbg(chain->dev, DESCR, + "Selector unit %d input %d isn't connected to an input terminal\n", + entity->id, i); return -1; } if (term->chain.next || term->chain.prev) { - uvc_trace(chain->dev, UVC_TRACE_DESCR, - "Found reference to entity %d already in chain.\n", - term->id); + uvc_dbg(chain->dev, DESCR, + "Found reference to entity %d already in chain\n", + term->id); return -EINVAL; } - uvc_trace_cont(UVC_TRACE_PROBE, " %d", term->id); + uvc_dbg_cont(PROBE, " %d", term->id); list_add_tail(&term->chain, &chain->entities); uvc_scan_chain_forward(chain, term, entity); } - uvc_trace_cont(UVC_TRACE_PROBE, "\n"); + uvc_dbg_cont(PROBE, "\n"); id = 0; break; @@ -1820,8 +1819,8 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, entity = uvc_entity_by_id(chain->dev, id); if (entity == NULL) { - uvc_trace(chain->dev, UVC_TRACE_DESCR, - "Found reference to unknown entity %d.\n", id); + uvc_dbg(chain->dev, DESCR, + "Found reference to unknown entity %d\n", id); return -EINVAL; } @@ -1834,7 +1833,7 @@ static int uvc_scan_chain(struct uvc_video_chain *chain, { struct uvc_entity *entity, *prev; - uvc_trace(chain->dev, UVC_TRACE_PROBE, "Scanning UVC chain:"); + uvc_dbg(chain->dev, PROBE, "Scanning UVC chain:"); entity = term; prev = NULL; @@ -1842,9 +1841,9 @@ static int uvc_scan_chain(struct uvc_video_chain *chain, while (entity != NULL) { /* Entity must not be part of an existing chain */ if (entity->chain.next || entity->chain.prev) { - uvc_trace(chain->dev, UVC_TRACE_DESCR, - "Found reference to entity %d already in chain.\n", - entity->id); + uvc_dbg(chain->dev, DESCR, + "Found reference to entity %d already in chain\n", + entity->id); return -EINVAL; } @@ -1998,9 +1997,8 @@ static int uvc_scan_fallback(struct uvc_device *dev) list_add_tail(&chain->list, &dev->chains); - uvc_trace(dev, UVC_TRACE_PROBE, - "Found a video chain by fallback heuristic (%s).\n", - uvc_print_chain(chain)); + uvc_dbg(dev, PROBE, "Found a video chain by fallback heuristic (%s)\n", + uvc_print_chain(chain)); return 0; @@ -2042,9 +2040,8 @@ static int uvc_scan_device(struct uvc_device *dev) continue; } - uvc_trace(dev, UVC_TRACE_PROBE, - "Found a valid video chain (%s).\n", - uvc_print_chain(chain)); + uvc_dbg(dev, PROBE, "Found a valid video chain (%s)\n", + uvc_print_chain(chain)); list_add_tail(&chain->list, &dev->chains); } @@ -2344,12 +2341,11 @@ static int uvc_probe(struct usb_interface *intf, ? dev->info->quirks : uvc_quirks_param; if (id->idVendor && id->idProduct) - uvc_trace(dev, UVC_TRACE_PROBE, - "Probing known UVC device %s (%04x:%04x)\n", - udev->devpath, id->idVendor, id->idProduct); + uvc_dbg(dev, PROBE, "Probing known UVC device %s (%04x:%04x)\n", + udev->devpath, id->idVendor, id->idProduct); else - uvc_trace(dev, UVC_TRACE_PROBE, - "Probing generic UVC device %s\n", udev->devpath); + uvc_dbg(dev, PROBE, "Probing generic UVC device %s\n", + udev->devpath); if (udev->product != NULL) strscpy(dev->name, udev->product, sizeof(dev->name)); @@ -2393,14 +2389,13 @@ static int uvc_probe(struct usb_interface *intf, /* Parse the Video Class control descriptor. */ if (uvc_parse_control(dev) < 0) { - uvc_trace(dev, UVC_TRACE_PROBE, - "Unable to parse UVC descriptors.\n"); + uvc_dbg(dev, PROBE, "Unable to parse UVC descriptors\n"); goto error; } /* Parse the associated GPIOs. */ if (uvc_gpio_parse(dev) < 0) { - uvc_trace(dev, UVC_TRACE_PROBE, "Unable to parse UVC GPIOs\n"); + uvc_dbg(dev, PROBE, "Unable to parse UVC GPIOs\n"); goto error; } @@ -2462,7 +2457,7 @@ static int uvc_probe(struct usb_interface *intf, goto error; } - uvc_trace(dev, UVC_TRACE_PROBE, "UVC device initialized.\n"); + uvc_dbg(dev, PROBE, "UVC device initialized\n"); usb_enable_autosuspend(udev); return 0; @@ -2494,7 +2489,7 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message) struct uvc_device *dev = usb_get_intfdata(intf); struct uvc_streaming *stream; - uvc_trace(dev, UVC_TRACE_SUSPEND, "Suspending interface %u\n", + uvc_dbg(dev, SUSPEND, "Suspending interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); /* Controls are cached on the fly so they don't need to be saved. */ @@ -2512,8 +2507,8 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message) return uvc_video_suspend(stream); } - uvc_trace(dev, UVC_TRACE_SUSPEND, - "Suspend: video streaming USB interface mismatch.\n"); + uvc_dbg(dev, SUSPEND, + "Suspend: video streaming USB interface mismatch\n"); return -EINVAL; } @@ -2523,8 +2518,8 @@ static int __uvc_resume(struct usb_interface *intf, int reset) struct uvc_streaming *stream; int ret = 0; - uvc_trace(dev, UVC_TRACE_SUSPEND, "Resuming interface %u\n", - intf->cur_altsetting->desc.bInterfaceNumber); + uvc_dbg(dev, SUSPEND, "Resuming interface %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); if (intf->cur_altsetting->desc.bInterfaceSubClass == UVC_SC_VIDEOCONTROL) { @@ -2552,8 +2547,8 @@ static int __uvc_resume(struct usb_interface *intf, int reset) } } - uvc_trace(dev, UVC_TRACE_SUSPEND, - "Resume: video streaming USB interface mismatch.\n"); + uvc_dbg(dev, SUSPEND, + "Resume: video streaming USB interface mismatch\n"); return -EINVAL; } @@ -2603,7 +2598,7 @@ module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(quirks, "Forced device quirks"); -module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); +module_param_named(trace, uvc_dbg_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(trace, "Trace level bitmask"); module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); diff --git a/drivers/media/usb/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c index 445c4d39aeff..2578d6ee4829 100644 --- a/drivers/media/usb/uvc/uvc_isight.c +++ b/drivers/media/usb/uvc/uvc_isight.c @@ -50,16 +50,15 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) || (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) { - uvc_trace(stream->dev, UVC_TRACE_FRAME, - "iSight header found\n"); + uvc_dbg(stream->dev, FRAME, "iSight header found\n"); is_header = 1; } /* Synchronize to the input stream by waiting for a header packet. */ if (buf->state != UVC_BUF_STATE_ACTIVE) { if (!is_header) { - uvc_trace(stream->dev, UVC_TRACE_FRAME, - "Dropping packet (out of sync).\n"); + uvc_dbg(stream->dev, FRAME, + "Dropping packet (out of sync)\n"); return 0; } @@ -87,8 +86,8 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, buf->bytesused += nbytes; if (len > maxlen || buf->bytesused == buf->length) { - uvc_trace(stream->dev, UVC_TRACE_FRAME, - "Frame complete (overflow).\n"); + uvc_dbg(stream->dev, FRAME, + "Frame complete (overflow)\n"); buf->state = UVC_BUF_STATE_DONE; } } @@ -105,9 +104,9 @@ void uvc_video_decode_isight(struct uvc_urb *uvc_urb, struct uvc_buffer *buf, for (i = 0; i < urb->number_of_packets; ++i) { if (urb->iso_frame_desc[i].status < 0) { - uvc_trace(stream->dev, UVC_TRACE_FRAME, - "USB isochronous frame lost (%d).\n", - urb->iso_frame_desc[i].status); + uvc_dbg(stream->dev, FRAME, + "USB isochronous frame lost (%d)\n", + urb->iso_frame_desc[i].status); } /* Decode the payload packet. diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 2255daa3e240..21a907d32bb7 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -103,8 +103,8 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { - uvc_trace(uvc_queue_to_stream(queue)->dev, UVC_TRACE_CAPTURE, - "[E] Bytes used out of bounds.\n"); + uvc_dbg(uvc_queue_to_stream(queue)->dev, CAPTURE, + "[E] Bytes used out of bounds\n"); return -EINVAL; } diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 69434273d2c5..753c8226db70 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -93,22 +93,21 @@ static void uvc_event_streaming(struct uvc_device *dev, struct uvc_streaming_status *status, int len) { if (len < 3) { - uvc_trace(dev, UVC_TRACE_STATUS, - "Invalid streaming status event received.\n"); + uvc_dbg(dev, STATUS, + "Invalid streaming status event received\n"); return; } if (status->bEvent == 0) { if (len < 4) return; - uvc_trace(dev, UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", - status->bOriginator, - status->bValue[0] ? "pressed" : "released", len); + uvc_dbg(dev, STATUS, "Button (intf %u) %s len %d\n", + status->bOriginator, + status->bValue[0] ? "pressed" : "released", len); uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]); } else { - uvc_trace(dev, UVC_TRACE_STATUS, - "Stream %u error event %02x len %d.\n", - status->bOriginator, status->bEvent, len); + uvc_dbg(dev, STATUS, "Stream %u error event %02x len %d\n", + status->bOriginator, status->bEvent, len); } } @@ -163,14 +162,13 @@ static bool uvc_event_control(struct urb *urb, if (len < 6 || status->bEvent != 0 || status->bAttribute >= ARRAY_SIZE(attrs)) { - uvc_trace(dev, UVC_TRACE_STATUS, - "Invalid control status event received.\n"); + uvc_dbg(dev, STATUS, "Invalid control status event received\n"); return false; } - uvc_trace(dev, UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", - status->bOriginator, status->bSelector, - attrs[status->bAttribute], len); + uvc_dbg(dev, STATUS, "Control %u/%u %s change len %d\n", + status->bOriginator, status->bSelector, + attrs[status->bAttribute], len); /* Find the control. */ ctrl = uvc_event_find_ctrl(dev, status, &chain); @@ -237,9 +235,8 @@ static void uvc_status_complete(struct urb *urb) } default: - uvc_trace(dev, UVC_TRACE_STATUS, - "Unknown status event type %u.\n", - dev->status[0]); + uvc_dbg(dev, STATUS, "Unknown status event type %u\n", + dev->status[0]); break; } } diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 42fac7d302d3..252136cc885c 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -75,9 +75,8 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, break; default: - uvc_trace(chain->dev, UVC_TRACE_CONTROL, - "Unsupported V4L2 control type %u.\n", - xmap->v4l2_type); + uvc_dbg(chain->dev, CONTROL, + "Unsupported V4L2 control type %u\n", xmap->v4l2_type); ret = -ENOTTY; goto free_map; } @@ -165,11 +164,10 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, return -EINVAL; fcc = (u8 *)&fmt->fmt.pix.pixelformat; - uvc_trace(stream->dev, UVC_TRACE_FORMAT, - "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", - fmt->fmt.pix.pixelformat, - fcc[0], fcc[1], fcc[2], fcc[3], - fmt->fmt.pix.width, fmt->fmt.pix.height); + uvc_dbg(stream->dev, FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u\n", + fmt->fmt.pix.pixelformat, + fcc[0], fcc[1], fcc[2], fcc[3], + fmt->fmt.pix.width, fmt->fmt.pix.height); /* Check if the hardware supports the requested format, use the default * format otherwise. @@ -209,18 +207,17 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, } if (frame == NULL) { - uvc_trace(stream->dev, UVC_TRACE_FORMAT, - "Unsupported size %ux%u.\n", fmt->fmt.pix.width, - fmt->fmt.pix.height); + uvc_dbg(stream->dev, FORMAT, "Unsupported size %ux%u\n", + fmt->fmt.pix.width, fmt->fmt.pix.height); return -EINVAL; } /* Use the default frame interval. */ interval = frame->dwDefaultFrameInterval; - uvc_trace(stream->dev, UVC_TRACE_FORMAT, - "Using default frame interval %u.%u us (%u.%u fps).\n", - interval / 10, interval % 10, 10000000 / interval, - (100000000 / interval) % 10); + uvc_dbg(stream->dev, FORMAT, + "Using default frame interval %u.%u us (%u.%u fps)\n", + interval / 10, interval % 10, 10000000 / interval, + (100000000 / interval) % 10); /* Set the format index, frame index and frame interval. */ memset(probe, 0, sizeof(*probe)); @@ -264,9 +261,9 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, } if (i == stream->nformats) - uvc_trace(stream->dev, UVC_TRACE_FORMAT, - "Unknown bFormatIndex %u, using default\n", - probe->bFormatIndex); + uvc_dbg(stream->dev, FORMAT, + "Unknown bFormatIndex %u, using default\n", + probe->bFormatIndex); for (i = 0; i < format->nframes; ++i) { if (probe->bFrameIndex == format->frame[i].bFrameIndex) { @@ -276,9 +273,9 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, } if (i == format->nframes) - uvc_trace(stream->dev, UVC_TRACE_FORMAT, - "Unknown bFrameIndex %u, using default\n", - probe->bFrameIndex); + uvc_dbg(stream->dev, FORMAT, + "Unknown bFrameIndex %u, using default\n", + probe->bFrameIndex); fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; @@ -420,9 +417,8 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, interval = uvc_fraction_to_interval(timeperframe.numerator, timeperframe.denominator); - uvc_trace(stream->dev, UVC_TRACE_FORMAT, - "Setting frame interval to %u/%u (%u).\n", - timeperframe.numerator, timeperframe.denominator, interval); + uvc_dbg(stream->dev, FORMAT, "Setting frame interval to %u/%u (%u)\n", + timeperframe.numerator, timeperframe.denominator, interval); mutex_lock(&stream->mutex); @@ -551,7 +547,7 @@ static int uvc_v4l2_open(struct file *file) int ret = 0; stream = video_drvdata(file); - uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__); + uvc_dbg(stream->dev, CALLS, "%s\n", __func__); ret = usb_autopm_get_interface(stream->dev->intf); if (ret < 0) @@ -593,7 +589,7 @@ static int uvc_v4l2_release(struct file *file) struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; - uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__); + uvc_dbg(stream->dev, CALLS, "%s\n", __func__); /* Only free resources if this is a privileged handle. */ if (uvc_has_privileges(handle)) @@ -1469,8 +1465,7 @@ static ssize_t uvc_v4l2_read(struct file *file, char __user *data, struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; - uvc_trace(stream->dev, UVC_TRACE_CALLS, - "uvc_v4l2_read: not implemented.\n"); + uvc_dbg(stream->dev, CALLS, "%s: not implemented\n", __func__); return -EINVAL; } @@ -1479,7 +1474,7 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; - uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__); + uvc_dbg(stream->dev, CALLS, "%s\n", __func__); return uvc_queue_mmap(&stream->queue, vma); } @@ -1489,7 +1484,7 @@ static __poll_t uvc_v4l2_poll(struct file *file, poll_table *wait) struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; - uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__); + uvc_dbg(stream->dev, CALLS, "%s\n", __func__); return uvc_queue_poll(&stream->queue, file, wait); } @@ -1502,7 +1497,7 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; - uvc_trace(stream->dev, UVC_TRACE_CALLS, "%s\n", __func__); + uvc_dbg(stream->dev, CALLS, "%s\n", __func__); return uvc_queue_get_unmapped_area(&stream->queue, pgoff); } diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 26251ed0c3fd..f2f565281e63 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -95,7 +95,7 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, if (ret != 1) return ret < 0 ? ret : -EPIPE; - uvc_trace(dev, UVC_TRACE_CONTROL, "Control error %u\n", error); + uvc_dbg(dev, CONTROL, "Control error %u\n", error); switch (error) { case 0: @@ -705,12 +705,12 @@ void uvc_video_clock_update(struct uvc_streaming *stream, sof = y; - uvc_trace(stream->dev, UVC_TRACE_CLOCK, - "%s: PTS %u y %llu.%06llu SOF %u.%06llu (x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n", - stream->dev->name, buf->pts, - y >> 16, div_u64((y & 0xffff) * 1000000, 65536), - sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), - x1, x2, y1, y2, clock->sof_offset); + uvc_dbg(stream->dev, CLOCK, + "%s: PTS %u y %llu.%06llu SOF %u.%06llu (x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n", + stream->dev->name, buf->pts, + y >> 16, div_u64((y & 0xffff) * 1000000, 65536), + sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), + x1, x2, y1, y2, clock->sof_offset); /* Second step, SOF to host clock conversion. */ x1 = (uvc_video_clock_host_sof(first) + 2048) << 16; @@ -740,13 +740,13 @@ void uvc_video_clock_update(struct uvc_streaming *stream, timestamp = ktime_to_ns(first->host_time) + y - y1; - uvc_trace(stream->dev, UVC_TRACE_CLOCK, - "%s: SOF %u.%06llu y %llu ts %llu buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n", - stream->dev->name, - sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), - y, timestamp, vbuf->vb2_buf.timestamp, - x1, first->host_sof, first->dev_sof, - x2, last->host_sof, last->dev_sof, y1, y2); + uvc_dbg(stream->dev, CLOCK, + "%s: SOF %u.%06llu y %llu ts %llu buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n", + stream->dev->name, + sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), + y, timestamp, vbuf->vb2_buf.timestamp, + x1, first->host_sof, first->dev_sof, + x2, last->host_sof, last->dev_sof, y1, y2); /* Update the V4L2 buffer. */ vbuf->vb2_buf.timestamp = timestamp; @@ -875,15 +875,15 @@ static void uvc_video_stats_update(struct uvc_streaming *stream) { struct uvc_stats_frame *frame = &stream->stats.frame; - uvc_trace(stream->dev, UVC_TRACE_STATS, - "frame %u stats: %u/%u/%u packets, %u/%u/%u pts (%searly %sinitial), %u/%u scr, last pts/stc/sof %u/%u/%u\n", - stream->sequence, frame->first_data, - frame->nb_packets - frame->nb_empty, frame->nb_packets, - frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts, - frame->has_early_pts ? "" : "!", - frame->has_initial_pts ? "" : "!", - frame->nb_scr_diffs, frame->nb_scr, - frame->pts, frame->scr_stc, frame->scr_sof); + uvc_dbg(stream->dev, STATS, + "frame %u stats: %u/%u/%u packets, %u/%u/%u pts (%searly %sinitial), %u/%u scr, last pts/stc/sof %u/%u/%u\n", + stream->sequence, frame->first_data, + frame->nb_packets - frame->nb_empty, frame->nb_packets, + frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts, + frame->has_early_pts ? "" : "!", + frame->has_initial_pts ? "" : "!", + frame->nb_scr_diffs, frame->nb_scr, + frame->pts, frame->scr_stc, frame->scr_sof); stream->stats.stream.nb_frames++; stream->stats.stream.nb_packets += stream->stats.frame.nb_packets; @@ -1038,8 +1038,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, /* Mark the buffer as bad if the error bit is set. */ if (data[1] & UVC_STREAM_ERR) { - uvc_trace(stream->dev, UVC_TRACE_FRAME, - "Marking buffer as bad (error bit set).\n"); + uvc_dbg(stream->dev, FRAME, + "Marking buffer as bad (error bit set)\n"); buf->error = 1; } @@ -1053,8 +1053,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, */ if (buf->state != UVC_BUF_STATE_ACTIVE) { if (fid == stream->last_fid) { - uvc_trace(stream->dev, UVC_TRACE_FRAME, - "Dropping payload (out of sync).\n"); + uvc_dbg(stream->dev, FRAME, + "Dropping payload (out of sync)\n"); if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && (data[1] & UVC_STREAM_EOF)) stream->last_fid ^= UVC_STREAM_FID; @@ -1085,8 +1085,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, * previous payload had the EOF bit set. */ if (fid != stream->last_fid && buf->bytesused != 0) { - uvc_trace(stream->dev, UVC_TRACE_FRAME, - "Frame complete (FID bit toggled).\n"); + uvc_dbg(stream->dev, FRAME, + "Frame complete (FID bit toggled)\n"); buf->state = UVC_BUF_STATE_READY; return -EAGAIN; } @@ -1147,8 +1147,8 @@ static void uvc_video_decode_data(struct uvc_urb *uvc_urb, /* Complete the current frame if the buffer size was exceeded. */ if (len > maxlen) { - uvc_trace(uvc_urb->stream->dev, UVC_TRACE_FRAME, - "Frame complete (overflow).\n"); + uvc_dbg(uvc_urb->stream->dev, FRAME, + "Frame complete (overflow)\n"); buf->error = 1; buf->state = UVC_BUF_STATE_READY; } @@ -1161,11 +1161,9 @@ static void uvc_video_decode_end(struct uvc_streaming *stream, { /* Mark the buffer as done if the EOF marker is set. */ if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) { - uvc_trace(stream->dev, UVC_TRACE_FRAME, - "Frame complete (EOF found).\n"); + uvc_dbg(stream->dev, FRAME, "Frame complete (EOF found)\n"); if (data[0] == len) - uvc_trace(stream->dev, UVC_TRACE_FRAME, - "EOF in empty payload.\n"); + uvc_dbg(stream->dev, FRAME, "EOF in empty payload\n"); buf->state = UVC_BUF_STATE_READY; if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) stream->last_fid ^= UVC_STREAM_FID; @@ -1281,13 +1279,13 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream, memcpy(&meta->length, mem, length); meta_buf->bytesused += length + sizeof(meta->ns) + sizeof(meta->sof); - uvc_trace(stream->dev, UVC_TRACE_FRAME, - "%s(): t-sys %lluns, SOF %u, len %u, flags 0x%x, PTS %u, STC %u frame SOF %u\n", - __func__, ktime_to_ns(time), meta->sof, meta->length, - meta->flags, - has_pts ? *(u32 *)meta->buf : 0, - has_scr ? *(u32 *)scr : 0, - has_scr ? *(u32 *)(scr + 4) & 0x7ff : 0); + uvc_dbg(stream->dev, FRAME, + "%s(): t-sys %lluns, SOF %u, len %u, flags 0x%x, PTS %u, STC %u frame SOF %u\n", + __func__, ktime_to_ns(time), meta->sof, meta->length, + meta->flags, + has_pts ? *(u32 *)meta->buf : 0, + has_scr ? *(u32 *)scr : 0, + has_scr ? *(u32 *)(scr + 4) & 0x7ff : 0); } /* ------------------------------------------------------------------------ @@ -1341,9 +1339,9 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb, for (i = 0; i < urb->number_of_packets; ++i) { if (urb->iso_frame_desc[i].status < 0) { - uvc_trace(stream->dev, UVC_TRACE_FRAME, - "USB isochronous frame lost (%d).\n", - urb->iso_frame_desc[i].status); + uvc_dbg(stream->dev, FRAME, + "USB isochronous frame lost (%d)\n", + urb->iso_frame_desc[i].status); /* Mark the buffer as faulty. */ if (buf != NULL) buf->error = 1; @@ -1631,16 +1629,16 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, } if (i == UVC_URBS) { - uvc_trace(stream->dev, UVC_TRACE_VIDEO, - "Allocated %u URB buffers of %ux%u bytes each.\n", - UVC_URBS, npackets, psize); + uvc_dbg(stream->dev, VIDEO, + "Allocated %u URB buffers of %ux%u bytes each\n", + UVC_URBS, npackets, psize); return npackets; } } - uvc_trace(stream->dev, UVC_TRACE_VIDEO, - "Failed to allocate URB buffers (%u bytes per packet).\n", - psize); + uvc_dbg(stream->dev, VIDEO, + "Failed to allocate URB buffers (%u bytes per packet)\n", + psize); return 0; } @@ -1839,13 +1837,13 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream, bandwidth = stream->ctrl.dwMaxPayloadTransferSize; if (bandwidth == 0) { - uvc_trace(stream->dev, UVC_TRACE_VIDEO, - "Device requested null bandwidth, defaulting to lowest.\n"); + uvc_dbg(stream->dev, VIDEO, + "Device requested null bandwidth, defaulting to lowest\n"); bandwidth = 1; } else { - uvc_trace(stream->dev, UVC_TRACE_VIDEO, - "Device requested %u B/frame bandwidth.\n", - bandwidth); + uvc_dbg(stream->dev, VIDEO, + "Device requested %u B/frame bandwidth\n", + bandwidth); } for (i = 0; i < intf->num_altsetting; ++i) { @@ -1868,14 +1866,14 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream, } if (best_ep == NULL) { - uvc_trace(stream->dev, UVC_TRACE_VIDEO, - "No fast enough alt setting for requested bandwidth.\n"); + uvc_dbg(stream->dev, VIDEO, + "No fast enough alt setting for requested bandwidth\n"); return -EIO; } - uvc_trace(stream->dev, UVC_TRACE_VIDEO, - "Selecting alternate setting %u (%u B/frame bandwidth).\n", - altsetting, best_psize); + uvc_dbg(stream->dev, VIDEO, + "Selecting alternate setting %u (%u B/frame bandwidth)\n", + altsetting, best_psize); ret = usb_set_interface(stream->dev->udev, intfnum, altsetting); if (ret < 0) diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 34c1e8a024ac..97df5ecd66c9 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -729,18 +729,18 @@ struct uvc_driver { * Debugging, printing and logging */ -#define UVC_TRACE_PROBE (1 << 0) -#define UVC_TRACE_DESCR (1 << 1) -#define UVC_TRACE_CONTROL (1 << 2) -#define UVC_TRACE_FORMAT (1 << 3) -#define UVC_TRACE_CAPTURE (1 << 4) -#define UVC_TRACE_CALLS (1 << 5) -#define UVC_TRACE_FRAME (1 << 7) -#define UVC_TRACE_SUSPEND (1 << 8) -#define UVC_TRACE_STATUS (1 << 9) -#define UVC_TRACE_VIDEO (1 << 10) -#define UVC_TRACE_STATS (1 << 11) -#define UVC_TRACE_CLOCK (1 << 12) +#define UVC_DBG_PROBE (1 << 0) +#define UVC_DBG_DESCR (1 << 1) +#define UVC_DBG_CONTROL (1 << 2) +#define UVC_DBG_FORMAT (1 << 3) +#define UVC_DBG_CAPTURE (1 << 4) +#define UVC_DBG_CALLS (1 << 5) +#define UVC_DBG_FRAME (1 << 7) +#define UVC_DBG_SUSPEND (1 << 8) +#define UVC_DBG_STATUS (1 << 9) +#define UVC_DBG_VIDEO (1 << 10) +#define UVC_DBG_STATS (1 << 11) +#define UVC_DBG_CLOCK (1 << 12) #define UVC_WARN_MINMAX 0 #define UVC_WARN_PROBE_DEF 1 @@ -748,20 +748,20 @@ struct uvc_driver { extern unsigned int uvc_clock_param; extern unsigned int uvc_no_drop_param; -extern unsigned int uvc_trace_param; +extern unsigned int uvc_dbg_param; extern unsigned int uvc_timeout_param; extern unsigned int uvc_hw_timestamps_param; -#define uvc_trace(_dev, flag, fmt, ...) \ +#define uvc_dbg(_dev, flag, fmt, ...) \ do { \ - if (uvc_trace_param & flag) \ + if (uvc_dbg_param & UVC_DBG_##flag) \ dev_printk(KERN_DEBUG, &(_dev)->udev->dev, fmt, \ ##__VA_ARGS__); \ } while (0) -#define uvc_trace_cont(flag, fmt, ...) \ +#define uvc_dbg_cont(flag, fmt, ...) \ do { \ - if (uvc_trace_param & flag) \ + if (uvc_dbg_param & UVC_DBG_##flag) \ pr_cont(fmt, ##__VA_ARGS__); \ } while (0) -- cgit v1.2.3 From 97cf50b6f8e5ccddab85af4e5008899338e6f220 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Jan 2021 18:17:27 +0100 Subject: media: v4l2-async: Remove V4L2_ASYNC_MATCH_CUSTOM Custom/driver-specific v4l2-async match support was introduced in 2013, as V4L2_ASYNC_BUS_CUSTOM. This type of match never had any user, so it's fair to conclude it's not required and that safe for removal. If the support is ever needed, it can always be restored. Suggested-by: Laurent Pinchart Signed-off-by: Ezequiel Garcia Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 14 -------------- include/media/v4l2-async.h | 17 ----------------- 2 files changed, 31 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 9dd896d085ec..3c141ba4f6d4 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -147,16 +147,6 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, return true; } -static bool match_custom(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) -{ - if (!asd->match.custom.match) - /* Match always */ - return true; - - return asd->match.custom.match(sd->dev, asd); -} - static LIST_HEAD(subdev_list); static LIST_HEAD(notifier_list); static DEFINE_MUTEX(list_lock); @@ -172,9 +162,6 @@ v4l2_async_find_match(struct v4l2_async_notifier *notifier, list_for_each_entry(asd, ¬ifier->waiting, list) { /* bus_type has been verified valid before */ switch (asd->match_type) { - case V4L2_ASYNC_MATCH_CUSTOM: - match = match_custom; - break; case V4L2_ASYNC_MATCH_DEVNAME: match = match_devname; break; @@ -475,7 +462,6 @@ static int v4l2_async_notifier_asd_valid(struct v4l2_async_notifier *notifier, return -EINVAL; switch (asd->match_type) { - case V4L2_ASYNC_MATCH_CUSTOM: case V4L2_ASYNC_MATCH_DEVNAME: case V4L2_ASYNC_MATCH_I2C: case V4L2_ASYNC_MATCH_FWNODE: diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 0e04b5b2ebb0..8ed42188e7c9 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -21,8 +21,6 @@ struct v4l2_async_notifier; * enum v4l2_async_match_type - type of asynchronous subdevice logic to be used * in order to identify a match * - * @V4L2_ASYNC_MATCH_CUSTOM: Match will use the logic provided by &struct - * v4l2_async_subdev.match ops * @V4L2_ASYNC_MATCH_DEVNAME: Match will use the device name * @V4L2_ASYNC_MATCH_I2C: Match will check for I2C adapter ID and address * @V4L2_ASYNC_MATCH_FWNODE: Match will use firmware node @@ -31,7 +29,6 @@ struct v4l2_async_notifier; * algorithm that will be used to match an asynchronous device. */ enum v4l2_async_match_type { - V4L2_ASYNC_MATCH_CUSTOM, V4L2_ASYNC_MATCH_DEVNAME, V4L2_ASYNC_MATCH_I2C, V4L2_ASYNC_MATCH_FWNODE, @@ -58,15 +55,6 @@ enum v4l2_async_match_type { * @match.i2c.address: * I2C address to be matched. * Used if @match_type is %V4L2_ASYNC_MATCH_I2C. - * @match.custom: - * Driver-specific match criteria. - * Used if @match_type is %V4L2_ASYNC_MATCH_CUSTOM. - * @match.custom.match: - * Driver-specific match function to be used if - * %V4L2_ASYNC_MATCH_CUSTOM. - * @match.custom.priv: - * Driver-specific private struct with match parameters - * to be used if %V4L2_ASYNC_MATCH_CUSTOM. * @asd_list: used to add struct v4l2_async_subdev objects to the * master notifier @asd_list * @list: used to link struct v4l2_async_subdev objects, waiting to be @@ -85,11 +73,6 @@ struct v4l2_async_subdev { int adapter_id; unsigned short address; } i2c; - struct { - bool (*match)(struct device *dev, - struct v4l2_async_subdev *sd); - void *priv; - } custom; } match; /* v4l2-async core private: not to be used by drivers */ -- cgit v1.2.3 From 517fd2b6a058bc4f23735ab9a9e6b0d5c56876d1 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Jan 2021 18:17:28 +0100 Subject: media: v4l2-async: Add waiting subdevices debugfs There is currently little to no information available about the reasons why a v4l2-async device hasn't probed completely. Inspired by the "devices_deferred" debugfs file, add a file to list information about the subdevices that are on waiting lists, for each notifier. This is useful to debug v4l2-async subdevices and notifiers, for instance when doing device bring-up. For instance, a typical output would be: $ cat /sys/kernel/debug/video4linux/pending_async_subdevices ipu1_csi1: [fwnode] dev=20e0000.iomuxc-gpr:ipu1_csi1_mux, node=/soc/bus@2000000/iomuxc-gpr@20e0000/ipu1_csi1_mux ipu1_csi0: [fwnode] dev=20e0000.iomuxc-gpr:ipu1_csi0_mux, node=/soc/bus@2000000/iomuxc-gpr@20e0000/ipu1_csi0_mux imx6-mipi-csi2: [fwnode] dev=1-003c, node=/soc/bus@2100000/i2c@21a4000/camera@3c imx-media: Signed-off-by: Ezequiel Garcia Reviewed-by: Laurent Pinchart Tested-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 66 ++++++++++++++++++++++++++++++++++++ drivers/media/v4l2-core/v4l2-dev.c | 5 +++ include/media/v4l2-async.h | 8 +++++ 3 files changed, 79 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 3c141ba4f6d4..3db8e2a92982 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -5,6 +5,7 @@ * Copyright (C) 2012-2013, Guennadi Liakhovetski */ +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include @@ -831,3 +833,67 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) mutex_unlock(&list_lock); } EXPORT_SYMBOL(v4l2_async_unregister_subdev); + +static void print_waiting_subdev(struct seq_file *s, + struct v4l2_async_subdev *asd) +{ + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_DEVNAME: + seq_printf(s, " [devname] dev=%s\n", asd->match.device_name); + break; + case V4L2_ASYNC_MATCH_I2C: + seq_printf(s, " [i2c] dev=%d-%04x\n", asd->match.i2c.adapter_id, + asd->match.i2c.address); + break; + case V4L2_ASYNC_MATCH_FWNODE: { + struct fwnode_handle *devnode, *fwnode = asd->match.fwnode; + + devnode = fwnode_graph_is_endpoint(fwnode) ? + fwnode_graph_get_port_parent(fwnode) : + fwnode_handle_get(fwnode); + + seq_printf(s, " [fwnode] dev=%s, node=%pfw\n", + devnode->dev ? dev_name(devnode->dev) : "nil", + fwnode); + + fwnode_handle_put(devnode); + break; + } + } +} + +static const char * +v4l2_async_notifier_name(struct v4l2_async_notifier *notifier) +{ + if (notifier->v4l2_dev) + return notifier->v4l2_dev->name; + else if (notifier->sd) + return notifier->sd->name; + else + return "nil"; +} + +static int pending_subdevs_show(struct seq_file *s, void *data) +{ + struct v4l2_async_notifier *notif; + struct v4l2_async_subdev *asd; + + mutex_lock(&list_lock); + + list_for_each_entry(notif, ¬ifier_list, list) { + seq_printf(s, "%s:\n", v4l2_async_notifier_name(notif)); + list_for_each_entry(asd, ¬if->waiting, list) + print_waiting_subdev(s, asd); + } + + mutex_unlock(&list_lock); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(pending_subdevs); + +void v4l2_async_debug_init(struct dentry *debugfs_dir) +{ + debugfs_create_file("pending_async_subdevices", 0444, debugfs_dir, NULL, + &pending_subdevs_fops); +} diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index f9cff033d0dc..b6a72d297775 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -14,6 +14,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -38,6 +39,7 @@ __func__, ##arg); \ } while (0) +static struct dentry *v4l2_debugfs_dir; /* * sysfs stuff @@ -1118,6 +1120,8 @@ static int __init videodev_init(void) return -EIO; } + v4l2_debugfs_dir = debugfs_create_dir("video4linux", NULL); + v4l2_async_debug_init(v4l2_debugfs_dir); return 0; } @@ -1125,6 +1129,7 @@ static void __exit videodev_exit(void) { dev_t dev = MKDEV(VIDEO_MAJOR, 0); + debugfs_remove_recursive(v4l2_debugfs_dir); class_unregister(&video_class); unregister_chrdev_region(dev, VIDEO_NUM_DEVICES); } diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 8ed42188e7c9..26296d4cb660 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -11,6 +11,7 @@ #include #include +struct dentry; struct device; struct device_node; struct v4l2_device; @@ -120,6 +121,13 @@ struct v4l2_async_notifier { struct list_head list; }; +/** + * v4l2_async_debug_init - Initialize debugging tools. + * + * @debugfs_dir: pointer to the parent debugfs &struct dentry + */ +void v4l2_async_debug_init(struct dentry *debugfs_dir); + /** * v4l2_async_notifier_init - Initialize a notifier. * -- cgit v1.2.3 From b7cdd6453ca2c2449c5270f2d0ae88b644a1d2fb Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 20 Jan 2021 10:01:48 +0100 Subject: media: i2c/Kconfig: Select FWNODE for OV772x sensor Fix OV772x build breakage by selecting V4L2_FWNODE config: ia64-linux-ld: drivers/media/i2c/ov772x.o: in function `ov772x_probe': ov772x.c:(.text+0x1ee2): undefined reference to `v4l2_fwnode_endpoint_alloc_parse' ia64-linux-ld: ov772x.c:(.text+0x1f12): undefined reference to `v4l2_fwnode_endpoint_free' ia64-linux-ld: ov772x.c:(.text+0x2212): undefined reference to `v4l2_fwnode_endpoint_alloc_parse' Fixes: 8a10b4e3601e ("media: i2c: ov772x: Parse endpoint properties") Reported-by: kernel test robot Signed-off-by: Lad Prabhakar Acked-by: Jacopo Mondi Reviewed-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index eddb10220953..bb1b5a340431 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1013,6 +1013,7 @@ config VIDEO_OV772X tristate "OmniVision OV772x sensor support" depends on I2C && VIDEO_V4L2 select REGMAP_SCCB + select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV772x camera. -- cgit v1.2.3 From 25a6436002d3bf2cc7483d0bfcf4373c3552f375 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 7 Jan 2021 23:54:58 +0100 Subject: media: v4l2-async: Safely unregister an non-registered async subdev Make the V4L2 async framework a bit more robust by allowing to unregister a non-registered async subdev. Otherwise the v4l2_async_cleanup() will attempt to delete the async subdev from the subdev_list with the corresponding list_head not initialized. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 3db8e2a92982..64643b169783 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -813,6 +813,9 @@ EXPORT_SYMBOL(v4l2_async_register_subdev); void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) { + if (!sd->async_list.next) + return; + mutex_lock(&list_lock); __v4l2_async_notifier_unregister(sd->subdev_notifier); -- cgit v1.2.3 From 35cb6aa82656f11196b13cd94cdbc5816d12b5a0 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 15 Jan 2021 11:16:52 +0100 Subject: media: dt-bindings: media: ov5647: Fix filename Commit 1b5071af8240 ("media: dt-bindings: media: i2c: Rename ov5647.yaml") renamed the bindings file but did not update the Id: field there. Fix it by using the new filename. Fixes: 1b5071af8240 ("media: dt-bindings: media: i2c: Rename ov5647.yaml") Signed-off-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml index 280c62afae13..429566c9ee1d 100644 --- a/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/media/i2c/ov5647.yaml# +$id: http://devicetree.org/schemas/media/i2c/ovti,ov5647.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Omnivision OV5647 raw image sensor -- cgit v1.2.3 From 256442881b1726a671befa47b7f92cd5d11b7ad5 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:00:49 +0100 Subject: media: allegro: extract RBSP handler from H.264 NAL generator The RBSP structure is the same for HEVC and H.264. In order to be able to reuse the RBSP handler for generating HEVC NAL units, extract the functions from the H.264 generator. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/Makefile | 3 +- drivers/media/platform/allegro-dvt/nal-h264.c | 336 +------------------------- drivers/media/platform/allegro-dvt/nal-rbsp.c | 305 +++++++++++++++++++++++ drivers/media/platform/allegro-dvt/nal-rbsp.h | 60 +++++ 4 files changed, 374 insertions(+), 330 deletions(-) create mode 100644 drivers/media/platform/allegro-dvt/nal-rbsp.c create mode 100644 drivers/media/platform/allegro-dvt/nal-rbsp.h diff --git a/drivers/media/platform/allegro-dvt/Makefile b/drivers/media/platform/allegro-dvt/Makefile index 8e306dcdc55c..10119e84a60f 100644 --- a/drivers/media/platform/allegro-dvt/Makefile +++ b/drivers/media/platform/allegro-dvt/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -allegro-objs := allegro-core.o nal-h264.o allegro-mail.o +allegro-objs := allegro-core.o allegro-mail.o +allegro-objs += nal-rbsp.o nal-h264.o obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro.o diff --git a/drivers/media/platform/allegro-dvt/nal-h264.c b/drivers/media/platform/allegro-dvt/nal-h264.c index bd48b8883572..94dd9266d850 100644 --- a/drivers/media/platform/allegro-dvt/nal-h264.c +++ b/drivers/media/platform/allegro-dvt/nal-h264.c @@ -22,6 +22,7 @@ #include #include "nal-h264.h" +#include "nal-rbsp.h" /* * See Rec. ITU-T H.264 (04/2017) Table 7-1 – NAL unit type codes, syntax @@ -33,54 +34,6 @@ enum nal_unit_type { FILLER_DATA = 12, }; -struct rbsp; - -struct nal_h264_ops { - int (*rbsp_bit)(struct rbsp *rbsp, int *val); - int (*rbsp_bits)(struct rbsp *rbsp, int n, unsigned int *val); - int (*rbsp_uev)(struct rbsp *rbsp, unsigned int *val); - int (*rbsp_sev)(struct rbsp *rbsp, int *val); -}; - -/** - * struct rbsp - State object for handling a raw byte sequence payload - * @data: pointer to the data of the rbsp - * @size: maximum size of the data of the rbsp - * @pos: current bit position inside the rbsp - * @num_consecutive_zeros: number of zeros before @pos - * @ops: per datatype functions for interacting with the rbsp - * @error: an error occurred while handling the rbsp - * - * This struct is passed around the various parsing functions and tracks the - * current position within the raw byte sequence payload. - * - * The @ops field allows to separate the operation, i.e., reading/writing a - * value from/to that rbsp, from the structure of the NAL unit. This allows to - * have a single function for iterating the NAL unit, while @ops has function - * pointers for handling each type in the rbsp. - */ -struct rbsp { - u8 *data; - size_t size; - unsigned int pos; - unsigned int num_consecutive_zeros; - struct nal_h264_ops *ops; - int error; -}; - -static void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, - struct nal_h264_ops *ops) -{ - if (!rbsp) - return; - - rbsp->data = addr; - rbsp->size = size; - rbsp->pos = 0; - rbsp->ops = ops; - rbsp->error = 0; -} - /** * nal_h264_profile_from_v4l2() - Get profile_idc for v4l2 h264 profile * @profile: the profile as &enum v4l2_mpeg_video_h264_profile @@ -155,281 +108,6 @@ int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level) } } -static int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value); -static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value); - -/* - * When reading or writing, the emulation_prevention_three_byte is detected - * only when the 2 one bits need to be inserted. Therefore, we are not - * actually adding the 0x3 byte, but the 2 one bits and the six 0 bits of the - * next byte. - */ -#define EMULATION_PREVENTION_THREE_BYTE (0x3 << 6) - -static int add_emulation_prevention_three_byte(struct rbsp *rbsp) -{ - rbsp->num_consecutive_zeros = 0; - rbsp_write_bits(rbsp, 8, EMULATION_PREVENTION_THREE_BYTE); - - return 0; -} - -static int discard_emulation_prevention_three_byte(struct rbsp *rbsp) -{ - unsigned int tmp = 0; - - rbsp->num_consecutive_zeros = 0; - rbsp_read_bits(rbsp, 8, &tmp); - if (tmp != EMULATION_PREVENTION_THREE_BYTE) - return -EINVAL; - - return 0; -} - -static inline int rbsp_read_bit(struct rbsp *rbsp) -{ - int shift; - int ofs; - int bit; - int err; - - if (rbsp->num_consecutive_zeros == 22) { - err = discard_emulation_prevention_three_byte(rbsp); - if (err) - return err; - } - - shift = 7 - (rbsp->pos % 8); - ofs = rbsp->pos / 8; - if (ofs >= rbsp->size) - return -EINVAL; - - bit = (rbsp->data[ofs] >> shift) & 1; - - rbsp->pos++; - - if (bit == 1 || - (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) - rbsp->num_consecutive_zeros = 0; - else - rbsp->num_consecutive_zeros++; - - return bit; -} - -static inline int rbsp_write_bit(struct rbsp *rbsp, bool value) -{ - int shift; - int ofs; - - if (rbsp->num_consecutive_zeros == 22) - add_emulation_prevention_three_byte(rbsp); - - shift = 7 - (rbsp->pos % 8); - ofs = rbsp->pos / 8; - if (ofs >= rbsp->size) - return -EINVAL; - - rbsp->data[ofs] &= ~(1 << shift); - rbsp->data[ofs] |= value << shift; - - rbsp->pos++; - - if (value || - (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) { - rbsp->num_consecutive_zeros = 0; - } else { - rbsp->num_consecutive_zeros++; - } - - return 0; -} - -static inline int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value) -{ - int i; - int bit; - unsigned int tmp = 0; - - if (n > 8 * sizeof(*value)) - return -EINVAL; - - for (i = n; i > 0; i--) { - bit = rbsp_read_bit(rbsp); - if (bit < 0) - return bit; - tmp |= bit << (i - 1); - } - - if (value) - *value = tmp; - - return 0; -} - -static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value) -{ - int ret; - - if (n > 8 * sizeof(value)) - return -EINVAL; - - while (n--) { - ret = rbsp_write_bit(rbsp, (value >> n) & 1); - if (ret) - return ret; - } - - return 0; -} - -static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *value) -{ - int leading_zero_bits = 0; - unsigned int tmp = 0; - int ret; - - while ((ret = rbsp_read_bit(rbsp)) == 0) - leading_zero_bits++; - if (ret < 0) - return ret; - - if (leading_zero_bits > 0) { - ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); - if (ret) - return ret; - } - - if (value) - *value = (1 << leading_zero_bits) - 1 + tmp; - - return 0; -} - -static int rbsp_write_uev(struct rbsp *rbsp, unsigned int *value) -{ - int ret; - int leading_zero_bits; - - if (!value) - return -EINVAL; - - leading_zero_bits = ilog2(*value + 1); - - ret = rbsp_write_bits(rbsp, leading_zero_bits, 0); - if (ret) - return ret; - - return rbsp_write_bits(rbsp, leading_zero_bits + 1, *value + 1); -} - -static int rbsp_read_sev(struct rbsp *rbsp, int *value) -{ - int ret; - unsigned int tmp; - - ret = rbsp_read_uev(rbsp, &tmp); - if (ret) - return ret; - - if (value) { - if (tmp & 1) - *value = (tmp + 1) / 2; - else - *value = -(tmp / 2); - } - - return 0; -} - -static int rbsp_write_sev(struct rbsp *rbsp, int *value) -{ - unsigned int tmp; - - if (!value) - return -EINVAL; - - if (*value > 0) - tmp = (2 * (*value)) | 1; - else - tmp = -2 * (*value); - - return rbsp_write_uev(rbsp, &tmp); -} - -static int __rbsp_write_bit(struct rbsp *rbsp, int *value) -{ - return rbsp_write_bit(rbsp, *value); -} - -static int __rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int *value) -{ - return rbsp_write_bits(rbsp, n, *value); -} - -static struct nal_h264_ops write = { - .rbsp_bit = __rbsp_write_bit, - .rbsp_bits = __rbsp_write_bits, - .rbsp_uev = rbsp_write_uev, - .rbsp_sev = rbsp_write_sev, -}; - -static int __rbsp_read_bit(struct rbsp *rbsp, int *value) -{ - int tmp = rbsp_read_bit(rbsp); - - if (tmp < 0) - return tmp; - *value = tmp; - - return 0; -} - -static struct nal_h264_ops read = { - .rbsp_bit = __rbsp_read_bit, - .rbsp_bits = rbsp_read_bits, - .rbsp_uev = rbsp_read_uev, - .rbsp_sev = rbsp_read_sev, -}; - -static inline void rbsp_bit(struct rbsp *rbsp, int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_bit(rbsp, value); -} - -static inline void rbsp_bits(struct rbsp *rbsp, int n, int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_bits(rbsp, n, value); -} - -static inline void rbsp_uev(struct rbsp *rbsp, unsigned int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_uev(rbsp, value); -} - -static inline void rbsp_sev(struct rbsp *rbsp, int *value) -{ - if (rbsp->error) - return; - rbsp->error = rbsp->ops->rbsp_sev(rbsp, value); -} - -static void nal_h264_rbsp_trailing_bits(struct rbsp *rbsp) -{ - unsigned int rbsp_stop_one_bit = 1; - unsigned int rbsp_alignment_zero_bit = 0; - - rbsp_bit(rbsp, &rbsp_stop_one_bit); - rbsp_bits(rbsp, round_up(rbsp->pos, 8) - rbsp->pos, - &rbsp_alignment_zero_bit); -} - static void nal_h264_write_start_code_prefix(struct rbsp *rbsp) { u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); @@ -767,7 +445,7 @@ ssize_t nal_h264_write_sps(const struct device *dev, nal_h264_rbsp_sps(&rbsp, sps); - nal_h264_rbsp_trailing_bits(&rbsp); + rbsp_trailing_bits(&rbsp); if (rbsp.error) return rbsp.error; @@ -814,7 +492,7 @@ ssize_t nal_h264_read_sps(const struct device *dev, nal_h264_rbsp_sps(&rbsp, sps); - nal_h264_rbsp_trailing_bits(&rbsp); + rbsp_trailing_bits(&rbsp); if (rbsp.error) return rbsp.error; @@ -859,7 +537,7 @@ ssize_t nal_h264_write_pps(const struct device *dev, nal_h264_rbsp_pps(&rbsp, pps); - nal_h264_rbsp_trailing_bits(&rbsp); + rbsp_trailing_bits(&rbsp); if (rbsp.error) return rbsp.error; @@ -896,7 +574,7 @@ ssize_t nal_h264_read_pps(const struct device *dev, nal_h264_rbsp_pps(&rbsp, pps); - nal_h264_rbsp_trailing_bits(&rbsp); + rbsp_trailing_bits(&rbsp); if (rbsp.error) return rbsp.error; @@ -942,7 +620,7 @@ ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n) nal_h264_write_filler_data(&rbsp); - nal_h264_rbsp_trailing_bits(&rbsp); + rbsp_trailing_bits(&rbsp); return DIV_ROUND_UP(rbsp.pos, 8); } @@ -991,7 +669,7 @@ ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n) return -EINVAL; nal_h264_read_filler_data(&rbsp); - nal_h264_rbsp_trailing_bits(&rbsp); + rbsp_trailing_bits(&rbsp); if (rbsp.error) return rbsp.error; diff --git a/drivers/media/platform/allegro-dvt/nal-rbsp.c b/drivers/media/platform/allegro-dvt/nal-rbsp.c new file mode 100644 index 000000000000..935ba23844f2 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-rbsp.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019-2020 Pengutronix, Michael Tretter + * + * Helper functions to generate a raw byte sequence payload from values. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "nal-rbsp.h" + +void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, + struct nal_rbsp_ops *ops) +{ + if (!rbsp) + return; + + rbsp->data = addr; + rbsp->size = size; + rbsp->pos = 0; + rbsp->ops = ops; + rbsp->error = 0; +} + +static int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value); +static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value); + +/* + * When reading or writing, the emulation_prevention_three_byte is detected + * only when the 2 one bits need to be inserted. Therefore, we are not + * actually adding the 0x3 byte, but the 2 one bits and the six 0 bits of the + * next byte. + */ +#define EMULATION_PREVENTION_THREE_BYTE (0x3 << 6) + +static int add_emulation_prevention_three_byte(struct rbsp *rbsp) +{ + rbsp->num_consecutive_zeros = 0; + rbsp_write_bits(rbsp, 8, EMULATION_PREVENTION_THREE_BYTE); + + return 0; +} + +static int discard_emulation_prevention_three_byte(struct rbsp *rbsp) +{ + unsigned int tmp = 0; + + rbsp->num_consecutive_zeros = 0; + rbsp_read_bits(rbsp, 8, &tmp); + if (tmp != EMULATION_PREVENTION_THREE_BYTE) + return -EINVAL; + + return 0; +} + +static inline int rbsp_read_bit(struct rbsp *rbsp) +{ + int shift; + int ofs; + int bit; + int err; + + if (rbsp->num_consecutive_zeros == 22) { + err = discard_emulation_prevention_three_byte(rbsp); + if (err) + return err; + } + + shift = 7 - (rbsp->pos % 8); + ofs = rbsp->pos / 8; + if (ofs >= rbsp->size) + return -EINVAL; + + bit = (rbsp->data[ofs] >> shift) & 1; + + rbsp->pos++; + + if (bit == 1 || + (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) + rbsp->num_consecutive_zeros = 0; + else + rbsp->num_consecutive_zeros++; + + return bit; +} + +static inline int rbsp_write_bit(struct rbsp *rbsp, bool value) +{ + int shift; + int ofs; + + if (rbsp->num_consecutive_zeros == 22) + add_emulation_prevention_three_byte(rbsp); + + shift = 7 - (rbsp->pos % 8); + ofs = rbsp->pos / 8; + if (ofs >= rbsp->size) + return -EINVAL; + + rbsp->data[ofs] &= ~(1 << shift); + rbsp->data[ofs] |= value << shift; + + rbsp->pos++; + + if (value || + (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) { + rbsp->num_consecutive_zeros = 0; + } else { + rbsp->num_consecutive_zeros++; + } + + return 0; +} + +static inline int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value) +{ + int i; + int bit; + unsigned int tmp = 0; + + if (n > 8 * sizeof(*value)) + return -EINVAL; + + for (i = n; i > 0; i--) { + bit = rbsp_read_bit(rbsp); + if (bit < 0) + return bit; + tmp |= bit << (i - 1); + } + + if (value) + *value = tmp; + + return 0; +} + +static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value) +{ + int ret; + + if (n > 8 * sizeof(value)) + return -EINVAL; + + while (n--) { + ret = rbsp_write_bit(rbsp, (value >> n) & 1); + if (ret) + return ret; + } + + return 0; +} + +static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *value) +{ + int leading_zero_bits = 0; + unsigned int tmp = 0; + int ret; + + while ((ret = rbsp_read_bit(rbsp)) == 0) + leading_zero_bits++; + if (ret < 0) + return ret; + + if (leading_zero_bits > 0) { + ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); + if (ret) + return ret; + } + + if (value) + *value = (1 << leading_zero_bits) - 1 + tmp; + + return 0; +} + +static int rbsp_write_uev(struct rbsp *rbsp, unsigned int *value) +{ + int ret; + int leading_zero_bits; + + if (!value) + return -EINVAL; + + leading_zero_bits = ilog2(*value + 1); + + ret = rbsp_write_bits(rbsp, leading_zero_bits, 0); + if (ret) + return ret; + + return rbsp_write_bits(rbsp, leading_zero_bits + 1, *value + 1); +} + +static int rbsp_read_sev(struct rbsp *rbsp, int *value) +{ + int ret; + unsigned int tmp; + + ret = rbsp_read_uev(rbsp, &tmp); + if (ret) + return ret; + + if (value) { + if (tmp & 1) + *value = (tmp + 1) / 2; + else + *value = -(tmp / 2); + } + + return 0; +} + +static int rbsp_write_sev(struct rbsp *rbsp, int *value) +{ + unsigned int tmp; + + if (!value) + return -EINVAL; + + if (*value > 0) + tmp = (2 * (*value)) | 1; + else + tmp = -2 * (*value); + + return rbsp_write_uev(rbsp, &tmp); +} + +static int __rbsp_write_bit(struct rbsp *rbsp, int *value) +{ + return rbsp_write_bit(rbsp, *value); +} + +static int __rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int *value) +{ + return rbsp_write_bits(rbsp, n, *value); +} + +struct nal_rbsp_ops write = { + .rbsp_bit = __rbsp_write_bit, + .rbsp_bits = __rbsp_write_bits, + .rbsp_uev = rbsp_write_uev, + .rbsp_sev = rbsp_write_sev, +}; + +static int __rbsp_read_bit(struct rbsp *rbsp, int *value) +{ + int tmp = rbsp_read_bit(rbsp); + + if (tmp < 0) + return tmp; + *value = tmp; + + return 0; +} + +struct nal_rbsp_ops read = { + .rbsp_bit = __rbsp_read_bit, + .rbsp_bits = rbsp_read_bits, + .rbsp_uev = rbsp_read_uev, + .rbsp_sev = rbsp_read_sev, +}; + +void rbsp_bit(struct rbsp *rbsp, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_bit(rbsp, value); +} + +void rbsp_bits(struct rbsp *rbsp, int n, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_bits(rbsp, n, value); +} + +void rbsp_uev(struct rbsp *rbsp, unsigned int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_uev(rbsp, value); +} + +void rbsp_sev(struct rbsp *rbsp, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_sev(rbsp, value); +} + +void rbsp_trailing_bits(struct rbsp *rbsp) +{ + unsigned int rbsp_stop_one_bit = 1; + unsigned int rbsp_alignment_zero_bit = 0; + + rbsp_bit(rbsp, &rbsp_stop_one_bit); + rbsp_bits(rbsp, round_up(rbsp->pos, 8) - rbsp->pos, + &rbsp_alignment_zero_bit); +} diff --git a/drivers/media/platform/allegro-dvt/nal-rbsp.h b/drivers/media/platform/allegro-dvt/nal-rbsp.h new file mode 100644 index 000000000000..90cc1a4f716d --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-rbsp.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019-2020 Pengutronix, Michael Tretter + */ + +#ifndef __NAL_RBSP_H__ +#define __NAL_RBSP_H__ + +#include +#include + +struct rbsp; + +struct nal_rbsp_ops { + int (*rbsp_bit)(struct rbsp *rbsp, int *val); + int (*rbsp_bits)(struct rbsp *rbsp, int n, unsigned int *val); + int (*rbsp_uev)(struct rbsp *rbsp, unsigned int *val); + int (*rbsp_sev)(struct rbsp *rbsp, int *val); +}; + +/** + * struct rbsp - State object for handling a raw byte sequence payload + * @data: pointer to the data of the rbsp + * @size: maximum size of the data of the rbsp + * @pos: current bit position inside the rbsp + * @num_consecutive_zeros: number of zeros before @pos + * @ops: per datatype functions for interacting with the rbsp + * @error: an error occurred while handling the rbsp + * + * This struct is passed around the various parsing functions and tracks the + * current position within the raw byte sequence payload. + * + * The @ops field allows to separate the operation, i.e., reading/writing a + * value from/to that rbsp, from the structure of the NAL unit. This allows to + * have a single function for iterating the NAL unit, while @ops has function + * pointers for handling each type in the rbsp. + */ +struct rbsp { + u8 *data; + size_t size; + unsigned int pos; + unsigned int num_consecutive_zeros; + struct nal_rbsp_ops *ops; + int error; +}; + +extern struct nal_rbsp_ops write; +extern struct nal_rbsp_ops read; + +void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, + struct nal_rbsp_ops *ops); + +void rbsp_bit(struct rbsp *rbsp, int *value); +void rbsp_bits(struct rbsp *rbsp, int n, int *value); +void rbsp_uev(struct rbsp *rbsp, unsigned int *value); +void rbsp_sev(struct rbsp *rbsp, int *value); + +void rbsp_trailing_bits(struct rbsp *rbsp); + +#endif /* __NAL_RBSP_H__ */ -- cgit v1.2.3 From 98c588b6d27c04460a590f55f1e2474129abe87c Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:00:50 +0100 Subject: media: allegro: add helper to report unsupported fields Allow generators to explicitly signal an error if the C structs contain unsupported or invalid fields. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/nal-rbsp.c | 5 +++++ drivers/media/platform/allegro-dvt/nal-rbsp.h | 1 + 2 files changed, 6 insertions(+) diff --git a/drivers/media/platform/allegro-dvt/nal-rbsp.c b/drivers/media/platform/allegro-dvt/nal-rbsp.c index 935ba23844f2..d911322d0efa 100644 --- a/drivers/media/platform/allegro-dvt/nal-rbsp.c +++ b/drivers/media/platform/allegro-dvt/nal-rbsp.c @@ -29,6 +29,11 @@ void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, rbsp->error = 0; } +void rbsp_unsupported(struct rbsp *rbsp) +{ + rbsp->error = -EINVAL; +} + static int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value); static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value); diff --git a/drivers/media/platform/allegro-dvt/nal-rbsp.h b/drivers/media/platform/allegro-dvt/nal-rbsp.h index 90cc1a4f716d..c72f49fed8d3 100644 --- a/drivers/media/platform/allegro-dvt/nal-rbsp.h +++ b/drivers/media/platform/allegro-dvt/nal-rbsp.h @@ -49,6 +49,7 @@ extern struct nal_rbsp_ops read; void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, struct nal_rbsp_ops *ops); +void rbsp_unsupported(struct rbsp *rbsp); void rbsp_bit(struct rbsp *rbsp, int *value); void rbsp_bits(struct rbsp *rbsp, int n, int *value); -- cgit v1.2.3 From 7f8e438b90c91541d784feb5f71be1a0b01a6010 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:00:51 +0100 Subject: media: allegro: add HEVC NAL unit generator When encoding a video as HEVC, the allegro driver needs to generate the Non-VCL NAL units for HEVC. Do the same as for H.264 and add a module that takes C structs for the VPS/SPS/PPS and encodes the fields as RBPS as specified by "ITU-T Rec. H.265 (02/2018) high efficiency video coding". [hverkuil: add missing static for nal_hevc_write/read_start_code_prefix] [hverkuil: fix typo: pps -> vps in nal_hevc_write_vps] Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/Makefile | 2 +- drivers/media/platform/allegro-dvt/nal-hevc.c | 824 ++++++++++++++++++++++++++ drivers/media/platform/allegro-dvt/nal-hevc.h | 350 +++++++++++ 3 files changed, 1175 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/allegro-dvt/nal-hevc.c create mode 100644 drivers/media/platform/allegro-dvt/nal-hevc.h diff --git a/drivers/media/platform/allegro-dvt/Makefile b/drivers/media/platform/allegro-dvt/Makefile index 10119e84a60f..66108a303774 100644 --- a/drivers/media/platform/allegro-dvt/Makefile +++ b/drivers/media/platform/allegro-dvt/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 allegro-objs := allegro-core.o allegro-mail.o -allegro-objs += nal-rbsp.o nal-h264.o +allegro-objs += nal-rbsp.o nal-h264.o nal-hevc.o obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro.o diff --git a/drivers/media/platform/allegro-dvt/nal-hevc.c b/drivers/media/platform/allegro-dvt/nal-hevc.c new file mode 100644 index 000000000000..5db540c69bfe --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-hevc.c @@ -0,0 +1,824 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019-2020 Pengutronix, Michael Tretter + * + * Convert NAL units between raw byte sequence payloads (RBSP) and C structs. + * + * The conversion is defined in "ITU-T Rec. H.265 (02/2018) high efficiency + * video coding". Decoder drivers may use the parser to parse RBSP from + * encoded streams and configure the hardware, if the hardware is not able to + * parse RBSP itself. Encoder drivers may use the generator to generate the + * RBSP for VPS/SPS/PPS nal units and add them to the encoded stream if the + * hardware does not generate the units. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "nal-hevc.h" +#include "nal-rbsp.h" + +/* + * See Rec. ITU-T H.265 (02/2018) Table 7-1 – NAL unit type codes and NAL unit + * type classes + */ +enum nal_unit_type { + VPS_NUT = 32, + SPS_NUT = 33, + PPS_NUT = 34, + FD_NUT = 38, +}; + +int nal_hevc_profile_from_v4l2(enum v4l2_mpeg_video_hevc_profile profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + return 1; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: + return 2; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: + return 3; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(nal_hevc_profile_from_v4l2); + +int nal_hevc_tier_from_v4l2(enum v4l2_mpeg_video_hevc_tier tier) +{ + switch (tier) { + case V4L2_MPEG_VIDEO_HEVC_TIER_MAIN: + return 0; + case V4L2_MPEG_VIDEO_HEVC_TIER_HIGH: + return 1; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(nal_hevc_tier_from_v4l2); + +int nal_hevc_level_from_v4l2(enum v4l2_mpeg_video_hevc_level level) +{ + /* + * T-Rec-H.265 p. 280: general_level_idc and sub_layer_level_idc[ i ] + * shall be set equal to a value of 30 times the level number + * specified in Table A.6. + */ + int factor = 30 / 10; + + switch (level) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + return factor * 10; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + return factor * 20; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + return factor * 21; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + return factor * 30; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + return factor * 31; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + return factor * 40; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + return factor * 41; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + return factor * 50; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + return factor * 51; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2: + return factor * 52; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6: + return factor * 60; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1: + return factor * 61; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2: + return factor * 62; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(nal_hevc_level_from_v4l2); + +static void nal_hevc_write_start_code_prefix(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i = 4; + + if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + p[0] = 0x00; + p[1] = 0x00; + p[2] = 0x00; + p[3] = 0x01; + + rbsp->pos += i * 8; +} + +static void nal_hevc_read_start_code_prefix(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i = 4; + + if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x00 || p[3] != 0x01) { + rbsp->error = -EINVAL; + return; + } + + rbsp->pos += i * 8; +} + +static void nal_hevc_write_filler_data(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i; + + /* Keep 1 byte extra for terminating the NAL unit */ + i = rbsp->size - DIV_ROUND_UP(rbsp->pos, 8) - 1; + memset(p, 0xff, i); + rbsp->pos += i * 8; +} + +static void nal_hevc_read_filler_data(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + + while (*p == 0xff) { + if (DIV_ROUND_UP(rbsp->pos, 8) > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + p++; + rbsp->pos += 8; + } +} + +static void nal_hevc_rbsp_profile_tier_level(struct rbsp *rbsp, + struct nal_hevc_profile_tier_level *ptl) +{ + unsigned int i; + unsigned int max_num_sub_layers_minus_1 = 0; + + rbsp_bits(rbsp, 2, &ptl->general_profile_space); + rbsp_bit(rbsp, &ptl->general_tier_flag); + rbsp_bits(rbsp, 5, &ptl->general_profile_idc); + for (i = 0; i < 32; i++) + rbsp_bit(rbsp, &ptl->general_profile_compatibility_flag[i]); + rbsp_bit(rbsp, &ptl->general_progressive_source_flag); + rbsp_bit(rbsp, &ptl->general_interlaced_source_flag); + rbsp_bit(rbsp, &ptl->general_non_packed_constraint_flag); + rbsp_bit(rbsp, &ptl->general_frame_only_constraint_flag); + if (ptl->general_profile_idc == 4 || + ptl->general_profile_compatibility_flag[4] || + ptl->general_profile_idc == 5 || + ptl->general_profile_compatibility_flag[5] || + ptl->general_profile_idc == 6 || + ptl->general_profile_compatibility_flag[6] || + ptl->general_profile_idc == 7 || + ptl->general_profile_compatibility_flag[7] || + ptl->general_profile_idc == 8 || + ptl->general_profile_compatibility_flag[8] || + ptl->general_profile_idc == 9 || + ptl->general_profile_compatibility_flag[9] || + ptl->general_profile_idc == 10 || + ptl->general_profile_compatibility_flag[10]) { + rbsp_bit(rbsp, &ptl->general_max_12bit_constraint_flag); + rbsp_bit(rbsp, &ptl->general_max_10bit_constraint_flag); + rbsp_bit(rbsp, &ptl->general_max_8bit_constraint_flag); + rbsp_bit(rbsp, &ptl->general_max_422chroma_constraint_flag); + rbsp_bit(rbsp, &ptl->general_max_420chroma_constraint_flag); + rbsp_bit(rbsp, &ptl->general_max_monochrome_constraint_flag); + rbsp_bit(rbsp, &ptl->general_intra_constraint_flag); + rbsp_bit(rbsp, &ptl->general_one_picture_only_constraint_flag); + rbsp_bit(rbsp, &ptl->general_lower_bit_rate_constraint_flag); + if (ptl->general_profile_idc == 5 || + ptl->general_profile_compatibility_flag[5] || + ptl->general_profile_idc == 9 || + ptl->general_profile_compatibility_flag[9] || + ptl->general_profile_idc == 10 || + ptl->general_profile_compatibility_flag[10]) { + rbsp_bit(rbsp, &ptl->general_max_14bit_constraint_flag); + rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_33bits); + rbsp_bits(rbsp, 33 - 32, &ptl->general_reserved_zero_33bits); + } else { + rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_34bits); + rbsp_bits(rbsp, 34 - 2, &ptl->general_reserved_zero_34bits); + } + } else if (ptl->general_profile_idc == 2 || + ptl->general_profile_compatibility_flag[2]) { + rbsp_bits(rbsp, 7, &ptl->general_reserved_zero_7bits); + rbsp_bit(rbsp, &ptl->general_one_picture_only_constraint_flag); + rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_35bits); + rbsp_bits(rbsp, 35 - 32, &ptl->general_reserved_zero_35bits); + } else { + rbsp_bits(rbsp, 32, &ptl->general_reserved_zero_43bits); + rbsp_bits(rbsp, 43 - 32, &ptl->general_reserved_zero_43bits); + } + if ((ptl->general_profile_idc >= 1 && ptl->general_profile_idc <= 5) || + ptl->general_profile_idc == 9 || + ptl->general_profile_compatibility_flag[1] || + ptl->general_profile_compatibility_flag[2] || + ptl->general_profile_compatibility_flag[3] || + ptl->general_profile_compatibility_flag[4] || + ptl->general_profile_compatibility_flag[5] || + ptl->general_profile_compatibility_flag[9]) + rbsp_bit(rbsp, &ptl->general_inbld_flag); + else + rbsp_bit(rbsp, &ptl->general_reserved_zero_bit); + rbsp_bits(rbsp, 8, &ptl->general_level_idc); + if (max_num_sub_layers_minus_1 > 0) + rbsp_unsupported(rbsp); +} + +static void nal_hevc_rbsp_vps(struct rbsp *rbsp, struct nal_hevc_vps *vps) +{ + unsigned int i, j; + unsigned int reserved_0xffff_16bits = 0xffff; + + rbsp_bits(rbsp, 4, &vps->video_parameter_set_id); + rbsp_bit(rbsp, &vps->base_layer_internal_flag); + rbsp_bit(rbsp, &vps->base_layer_available_flag); + rbsp_bits(rbsp, 6, &vps->max_layers_minus1); + rbsp_bits(rbsp, 3, &vps->max_sub_layers_minus1); + rbsp_bits(rbsp, 1, &vps->temporal_id_nesting_flag); + rbsp_bits(rbsp, 16, &reserved_0xffff_16bits); + nal_hevc_rbsp_profile_tier_level(rbsp, &vps->profile_tier_level); + rbsp_bit(rbsp, &vps->sub_layer_ordering_info_present_flag); + for (i = vps->sub_layer_ordering_info_present_flag ? 0 : vps->max_sub_layers_minus1; + i <= vps->max_sub_layers_minus1; i++) { + rbsp_uev(rbsp, &vps->max_dec_pic_buffering_minus1[i]); + rbsp_uev(rbsp, &vps->max_num_reorder_pics[i]); + rbsp_uev(rbsp, &vps->max_latency_increase_plus1[i]); + } + rbsp_bits(rbsp, 6, &vps->max_layer_id); + rbsp_uev(rbsp, &vps->num_layer_sets_minus1); + for (i = 0; i <= vps->num_layer_sets_minus1; i++) + for (j = 0; j <= vps->max_layer_id; j++) + rbsp_bit(rbsp, &vps->layer_id_included_flag[i][j]); + rbsp_bit(rbsp, &vps->timing_info_present_flag); + if (vps->timing_info_present_flag) + rbsp_unsupported(rbsp); + rbsp_bit(rbsp, &vps->extension_flag); + if (vps->extension_flag) + rbsp_unsupported(rbsp); +} + +static void nal_hevc_rbsp_sps(struct rbsp *rbsp, struct nal_hevc_sps *sps) +{ + unsigned int i; + + rbsp_bits(rbsp, 4, &sps->video_parameter_set_id); + rbsp_bits(rbsp, 3, &sps->max_sub_layers_minus1); + rbsp_bit(rbsp, &sps->temporal_id_nesting_flag); + nal_hevc_rbsp_profile_tier_level(rbsp, &sps->profile_tier_level); + rbsp_uev(rbsp, &sps->seq_parameter_set_id); + + rbsp_uev(rbsp, &sps->chroma_format_idc); + if (sps->chroma_format_idc == 3) + rbsp_bit(rbsp, &sps->separate_colour_plane_flag); + rbsp_uev(rbsp, &sps->pic_width_in_luma_samples); + rbsp_uev(rbsp, &sps->pic_height_in_luma_samples); + rbsp_bit(rbsp, &sps->conformance_window_flag); + if (sps->conformance_window_flag) { + rbsp_uev(rbsp, &sps->conf_win_left_offset); + rbsp_uev(rbsp, &sps->conf_win_right_offset); + rbsp_uev(rbsp, &sps->conf_win_top_offset); + rbsp_uev(rbsp, &sps->conf_win_bottom_offset); + } + rbsp_uev(rbsp, &sps->bit_depth_luma_minus8); + rbsp_uev(rbsp, &sps->bit_depth_chroma_minus8); + + rbsp_uev(rbsp, &sps->log2_max_pic_order_cnt_lsb_minus4); + + rbsp_bit(rbsp, &sps->sub_layer_ordering_info_present_flag); + for (i = (sps->sub_layer_ordering_info_present_flag ? 0 : sps->max_sub_layers_minus1); + i <= sps->max_sub_layers_minus1; i++) { + rbsp_uev(rbsp, &sps->max_dec_pic_buffering_minus1[i]); + rbsp_uev(rbsp, &sps->max_num_reorder_pics[i]); + rbsp_uev(rbsp, &sps->max_latency_increase_plus1[i]); + } + rbsp_uev(rbsp, &sps->log2_min_luma_coding_block_size_minus3); + rbsp_uev(rbsp, &sps->log2_diff_max_min_luma_coding_block_size); + rbsp_uev(rbsp, &sps->log2_min_luma_transform_block_size_minus2); + rbsp_uev(rbsp, &sps->log2_diff_max_min_luma_transform_block_size); + rbsp_uev(rbsp, &sps->max_transform_hierarchy_depth_inter); + rbsp_uev(rbsp, &sps->max_transform_hierarchy_depth_intra); + + rbsp_bit(rbsp, &sps->scaling_list_enabled_flag); + if (sps->scaling_list_enabled_flag) + rbsp_unsupported(rbsp); + + rbsp_bit(rbsp, &sps->amp_enabled_flag); + rbsp_bit(rbsp, &sps->sample_adaptive_offset_enabled_flag); + rbsp_bit(rbsp, &sps->pcm_enabled_flag); + if (sps->pcm_enabled_flag) { + rbsp_bits(rbsp, 4, &sps->pcm_sample_bit_depth_luma_minus1); + rbsp_bits(rbsp, 4, &sps->pcm_sample_bit_depth_chroma_minus1); + rbsp_uev(rbsp, &sps->log2_min_pcm_luma_coding_block_size_minus3); + rbsp_uev(rbsp, &sps->log2_diff_max_min_pcm_luma_coding_block_size); + rbsp_bit(rbsp, &sps->pcm_loop_filter_disabled_flag); + } + + rbsp_uev(rbsp, &sps->num_short_term_ref_pic_sets); + if (sps->num_short_term_ref_pic_sets > 0) + rbsp_unsupported(rbsp); + + rbsp_bit(rbsp, &sps->long_term_ref_pics_present_flag); + if (sps->long_term_ref_pics_present_flag) + rbsp_unsupported(rbsp); + + rbsp_bit(rbsp, &sps->sps_temporal_mvp_enabled_flag); + rbsp_bit(rbsp, &sps->strong_intra_smoothing_enabled_flag); + rbsp_bit(rbsp, &sps->vui_parameters_present_flag); + if (sps->vui_parameters_present_flag) + rbsp_unsupported(rbsp); + + rbsp_bit(rbsp, &sps->extension_present_flag); + if (sps->extension_present_flag) { + rbsp_bit(rbsp, &sps->sps_range_extension_flag); + rbsp_bit(rbsp, &sps->sps_multilayer_extension_flag); + rbsp_bit(rbsp, &sps->sps_3d_extension_flag); + rbsp_bit(rbsp, &sps->sps_scc_extension_flag); + rbsp_bits(rbsp, 5, &sps->sps_extension_4bits); + } + if (sps->sps_range_extension_flag) + rbsp_unsupported(rbsp); + if (sps->sps_multilayer_extension_flag) + rbsp_unsupported(rbsp); + if (sps->sps_3d_extension_flag) + rbsp_unsupported(rbsp); + if (sps->sps_scc_extension_flag) + rbsp_unsupported(rbsp); + if (sps->sps_extension_4bits) + rbsp_unsupported(rbsp); +} + +static void nal_hevc_rbsp_pps(struct rbsp *rbsp, struct nal_hevc_pps *pps) +{ + unsigned int i; + + rbsp_uev(rbsp, &pps->pps_pic_parameter_set_id); + rbsp_uev(rbsp, &pps->pps_seq_parameter_set_id); + rbsp_bit(rbsp, &pps->dependent_slice_segments_enabled_flag); + rbsp_bit(rbsp, &pps->output_flag_present_flag); + rbsp_bits(rbsp, 3, &pps->num_extra_slice_header_bits); + rbsp_bit(rbsp, &pps->sign_data_hiding_enabled_flag); + rbsp_bit(rbsp, &pps->cabac_init_present_flag); + rbsp_uev(rbsp, &pps->num_ref_idx_l0_default_active_minus1); + rbsp_uev(rbsp, &pps->num_ref_idx_l1_default_active_minus1); + rbsp_sev(rbsp, &pps->init_qp_minus26); + rbsp_bit(rbsp, &pps->constrained_intra_pred_flag); + rbsp_bit(rbsp, &pps->transform_skip_enabled_flag); + rbsp_bit(rbsp, &pps->cu_qp_delta_enabled_flag); + if (pps->cu_qp_delta_enabled_flag) + rbsp_uev(rbsp, &pps->diff_cu_qp_delta_depth); + rbsp_sev(rbsp, &pps->pps_cb_qp_offset); + rbsp_sev(rbsp, &pps->pps_cr_qp_offset); + rbsp_bit(rbsp, &pps->pps_slice_chroma_qp_offsets_present_flag); + rbsp_bit(rbsp, &pps->weighted_pred_flag); + rbsp_bit(rbsp, &pps->weighted_bipred_flag); + rbsp_bit(rbsp, &pps->transquant_bypass_enabled_flag); + rbsp_bit(rbsp, &pps->tiles_enabled_flag); + rbsp_bit(rbsp, &pps->entropy_coding_sync_enabled_flag); + if (pps->tiles_enabled_flag) { + rbsp_uev(rbsp, &pps->num_tile_columns_minus1); + rbsp_uev(rbsp, &pps->num_tile_rows_minus1); + rbsp_bit(rbsp, &pps->uniform_spacing_flag); + if (!pps->uniform_spacing_flag) { + for (i = 0; i < pps->num_tile_columns_minus1; i++) + rbsp_uev(rbsp, &pps->column_width_minus1[i]); + for (i = 0; i < pps->num_tile_rows_minus1; i++) + rbsp_uev(rbsp, &pps->row_height_minus1[i]); + } + rbsp_bit(rbsp, &pps->loop_filter_across_tiles_enabled_flag); + } + rbsp_bit(rbsp, &pps->pps_loop_filter_across_slices_enabled_flag); + rbsp_bit(rbsp, &pps->deblocking_filter_control_present_flag); + if (pps->deblocking_filter_control_present_flag) { + rbsp_bit(rbsp, &pps->deblocking_filter_override_enabled_flag); + rbsp_bit(rbsp, &pps->pps_deblocking_filter_disabled_flag); + if (!pps->pps_deblocking_filter_disabled_flag) { + rbsp_sev(rbsp, &pps->pps_beta_offset_div2); + rbsp_sev(rbsp, &pps->pps_tc_offset_div2); + } + } + rbsp_bit(rbsp, &pps->pps_scaling_list_data_present_flag); + if (pps->pps_scaling_list_data_present_flag) + rbsp_unsupported(rbsp); + rbsp_bit(rbsp, &pps->lists_modification_present_flag); + rbsp_uev(rbsp, &pps->log2_parallel_merge_level_minus2); + rbsp_bit(rbsp, &pps->slice_segment_header_extension_present_flag); + rbsp_bit(rbsp, &pps->pps_extension_present_flag); + if (pps->pps_extension_present_flag) { + rbsp_bit(rbsp, &pps->pps_range_extension_flag); + rbsp_bit(rbsp, &pps->pps_multilayer_extension_flag); + rbsp_bit(rbsp, &pps->pps_3d_extension_flag); + rbsp_bit(rbsp, &pps->pps_scc_extension_flag); + rbsp_bits(rbsp, 4, &pps->pps_extension_4bits); + } + if (pps->pps_range_extension_flag) + rbsp_unsupported(rbsp); + if (pps->pps_multilayer_extension_flag) + rbsp_unsupported(rbsp); + if (pps->pps_3d_extension_flag) + rbsp_unsupported(rbsp); + if (pps->pps_scc_extension_flag) + rbsp_unsupported(rbsp); + if (pps->pps_extension_4bits) + rbsp_unsupported(rbsp); +} + +/** + * nal_hevc_write_vps() - Write PPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @vps: &struct nal_hevc_vps to convert to RBSP + * + * Convert @vps to RBSP data and write it into @dest. + * + * The size of the VPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the VPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_hevc_write_vps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_vps *vps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_unit_type = VPS_NUT; + unsigned int nuh_layer_id = 0; + unsigned int nuh_temporal_id_plus1 = 1; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_hevc_write_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + nal_hevc_rbsp_vps(&rbsp, vps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_write_vps); + +/** + * nal_hevc_read_vps() - Read VPS NAL unit from RBSP format + * @dev: device pointer + * @vps: the &struct nal_hevc_vps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @vps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_hevc_read_vps(const struct device *dev, + struct nal_hevc_vps *vps, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_unit_type; + unsigned int nuh_layer_id; + unsigned int nuh_temporal_id_plus1; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_hevc_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + if (rbsp.error || + forbidden_zero_bit != 0 || + nal_unit_type != VPS_NUT) + return -EINVAL; + + nal_hevc_rbsp_vps(&rbsp, vps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_read_vps); + +/** + * nal_hevc_write_sps() - Write SPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @sps: &struct nal_hevc_sps to convert to RBSP + * + * Convert @sps to RBSP data and write it into @dest. + * + * The size of the SPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the SPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_hevc_write_sps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_sps *sps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_unit_type = SPS_NUT; + unsigned int nuh_layer_id = 0; + unsigned int nuh_temporal_id_plus1 = 1; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_hevc_write_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + nal_hevc_rbsp_sps(&rbsp, sps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_write_sps); + +/** + * nal_hevc_read_sps() - Read SPS NAL unit from RBSP format + * @dev: device pointer + * @sps: the &struct nal_hevc_sps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @sps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_hevc_read_sps(const struct device *dev, + struct nal_hevc_sps *sps, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_unit_type; + unsigned int nuh_layer_id; + unsigned int nuh_temporal_id_plus1; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_hevc_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + if (rbsp.error || + forbidden_zero_bit != 0 || + nal_unit_type != SPS_NUT) + return -EINVAL; + + nal_hevc_rbsp_sps(&rbsp, sps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_read_sps); + +/** + * nal_hevc_write_pps() - Write PPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @pps: &struct nal_hevc_pps to convert to RBSP + * + * Convert @pps to RBSP data and write it into @dest. + * + * The size of the PPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the PPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_hevc_write_pps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_pps *pps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_unit_type = PPS_NUT; + unsigned int nuh_layer_id = 0; + unsigned int nuh_temporal_id_plus1 = 1; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_hevc_write_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + nal_hevc_rbsp_pps(&rbsp, pps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_write_pps); + +/** + * nal_hevc_read_pps() - Read PPS NAL unit from RBSP format + * @dev: device pointer + * @pps: the &struct nal_hevc_pps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @pps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_hevc_read_pps(const struct device *dev, + struct nal_hevc_pps *pps, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_unit_type; + unsigned int nuh_layer_id; + unsigned int nuh_temporal_id_plus1; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_hevc_read_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + nal_hevc_rbsp_pps(&rbsp, pps); + + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_read_pps); + +/** + * nal_hevc_write_filler() - Write filler data RBSP + * @dev: device pointer + * @dest: buffer to fill with filler data + * @n: size of the buffer to fill with filler data + * + * Write a filler data RBSP to @dest with a size of @n bytes and return the + * number of written filler data bytes. + * + * Use this function to generate dummy data in an RBSP data stream that can be + * safely ignored by hevc decoders. + * + * The RBSP format of the filler data is specified in Rec. ITU-T H.265 + * (02/2018) 7.3.2.8 Filler data RBSP syntax. + * + * Return: number of filler data bytes (including marker) or negative error + */ +ssize_t nal_hevc_write_filler(const struct device *dev, void *dest, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_unit_type = FD_NUT; + unsigned int nuh_layer_id = 0; + unsigned int nuh_temporal_id_plus1 = 1; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_hevc_write_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + nal_hevc_write_filler_data(&rbsp); + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_write_filler); + +/** + * nal_hevc_read_filler() - Read filler data RBSP + * @dev: device pointer + * @src: buffer with RBSP data that is read + * @n: maximum size of src that shall be read + * + * Read a filler data RBSP from @src up to a maximum size of @n bytes and + * return the size of the filler data in bytes including the marker. + * + * This function is used to parse filler data and skip the respective bytes in + * the RBSP data. + * + * The RBSP format of the filler data is specified in Rec. ITU-T H.265 + * (02/2018) 7.3.2.8 Filler data RBSP syntax. + * + * Return: number of filler data bytes (including marker) or negative error + */ +ssize_t nal_hevc_read_filler(const struct device *dev, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_unit_type; + unsigned int nuh_layer_id; + unsigned int nuh_temporal_id_plus1; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_hevc_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 6, &nal_unit_type); + rbsp_bits(&rbsp, 6, &nuh_layer_id); + rbsp_bits(&rbsp, 3, &nuh_temporal_id_plus1); + + if (rbsp.error) + return rbsp.error; + if (forbidden_zero_bit != 0 || + nal_unit_type != FD_NUT) + return -EINVAL; + + nal_hevc_read_filler_data(&rbsp); + rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_hevc_read_filler); diff --git a/drivers/media/platform/allegro-dvt/nal-hevc.h b/drivers/media/platform/allegro-dvt/nal-hevc.h new file mode 100644 index 000000000000..fc994d4242d8 --- /dev/null +++ b/drivers/media/platform/allegro-dvt/nal-hevc.h @@ -0,0 +1,350 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + * + * Convert NAL units between raw byte sequence payloads (RBSP) and C structs. + */ + +#ifndef __NAL_HEVC_H__ +#define __NAL_HEVC_H__ + +#include +#include +#include + +struct nal_hevc_profile_tier_level { + unsigned int general_profile_space; + unsigned int general_tier_flag; + unsigned int general_profile_idc; + unsigned int general_profile_compatibility_flag[32]; + unsigned int general_progressive_source_flag; + unsigned int general_interlaced_source_flag; + unsigned int general_non_packed_constraint_flag; + unsigned int general_frame_only_constraint_flag; + union { + struct { + unsigned int general_max_12bit_constraint_flag; + unsigned int general_max_10bit_constraint_flag; + unsigned int general_max_8bit_constraint_flag; + unsigned int general_max_422chroma_constraint_flag; + unsigned int general_max_420chroma_constraint_flag; + unsigned int general_max_monochrome_constraint_flag; + unsigned int general_intra_constraint_flag; + unsigned int general_one_picture_only_constraint_flag; + unsigned int general_lower_bit_rate_constraint_flag; + union { + struct { + unsigned int general_max_14bit_constraint_flag; + unsigned int general_reserved_zero_33bits; + }; + unsigned int general_reserved_zero_34bits; + }; + }; + struct { + unsigned int general_reserved_zero_7bits; + /* unsigned int general_one_picture_only_constraint_flag; */ + unsigned int general_reserved_zero_35bits; + }; + unsigned int general_reserved_zero_43bits; + }; + union { + unsigned int general_inbld_flag; + unsigned int general_reserved_zero_bit; + }; + unsigned int general_level_idc; +}; + +/** + * struct nal_hevc_vps - Video parameter set + * + * C struct representation of the video parameter set NAL unit as defined by + * Rec. ITU-T H.265 (02/2018) 7.3.2.1 Video parameter set RBSP syntax + */ +struct nal_hevc_vps { + unsigned int video_parameter_set_id; + unsigned int base_layer_internal_flag; + unsigned int base_layer_available_flag; + unsigned int max_layers_minus1; + unsigned int max_sub_layers_minus1; + unsigned int temporal_id_nesting_flag; + struct nal_hevc_profile_tier_level profile_tier_level; + unsigned int sub_layer_ordering_info_present_flag; + struct { + unsigned int max_dec_pic_buffering_minus1[7]; + unsigned int max_num_reorder_pics[7]; + unsigned int max_latency_increase_plus1[7]; + }; + unsigned int max_layer_id; + unsigned int num_layer_sets_minus1; + unsigned int layer_id_included_flag[1024][64]; + unsigned int timing_info_present_flag; + struct { + unsigned int num_units_in_tick; + unsigned int time_scale; + unsigned int poc_proportional_to_timing_flag; + unsigned int num_ticks_poc_diff_one_minus1; + unsigned int num_hrd_parameters; + struct { + unsigned int hrd_layer_set_idx[0]; + unsigned int cprms_present_flag[0]; + }; + /* hrd_parameters( cprms_present_flag[ i ], max_sub_layers_minus1 ) */ + }; + unsigned int extension_flag; + unsigned int extension_data_flag; +}; + +struct nal_hevc_sub_layer_hrd_parameters { + unsigned int bit_rate_value_minus1[1]; + unsigned int cpb_size_value_minus1[1]; + unsigned int cbr_flag[1]; +}; + +struct nal_hevc_hrd_parameters { + unsigned int nal_hrd_parameters_present_flag; + unsigned int vcl_hrd_parameters_present_flag; + struct { + unsigned int sub_pic_hrd_params_present_flag; + struct { + unsigned int tick_divisor_minus2; + unsigned int du_cpb_removal_delay_increment_length_minus1; + unsigned int sub_pic_cpb_params_in_pic_timing_sei_flag; + unsigned int dpb_output_delay_du_length_minus1; + }; + unsigned int bit_rate_scale; + unsigned int cpb_size_scale; + unsigned int cpb_size_du_scale; + unsigned int initial_cpb_removal_delay_length_minus1; + unsigned int au_cpb_removal_delay_length_minus1; + unsigned int dpb_output_delay_length_minus1; + }; + struct { + unsigned int fixed_pic_rate_general_flag[1]; + unsigned int fixed_pic_rate_within_cvs_flag[1]; + unsigned int elemental_duration_in_tc_minus1[1]; + unsigned int low_delay_hrd_flag[1]; + unsigned int cpb_cnt_minus1[1]; + struct nal_hevc_sub_layer_hrd_parameters nal_hrd[1]; + struct nal_hevc_sub_layer_hrd_parameters vcl_hrd[1]; + }; +}; + +/** + * struct nal_hevc_vui_parameters - VUI parameters + * + * C struct representation of the VUI parameters as defined by Rec. ITU-T + * H.265 (02/2018) E.2.1 VUI parameters syntax. + */ +struct nal_hevc_vui_parameters { + unsigned int aspect_ratio_info_present_flag; + struct { + unsigned int aspect_ratio_idc; + unsigned int sar_width; + unsigned int sar_height; + }; + unsigned int overscan_info_present_flag; + unsigned int overscan_appropriate_flag; + unsigned int video_signal_type_present_flag; + struct { + unsigned int video_format; + unsigned int video_full_range_flag; + unsigned int colour_description_present_flag; + struct { + unsigned int colour_primaries; + unsigned int transfer_characteristics; + unsigned int matrix_coeffs; + }; + }; + unsigned int chroma_loc_info_present_flag; + struct { + unsigned int chroma_sample_loc_type_top_field; + unsigned int chroma_sample_loc_type_bottom_field; + }; + unsigned int neutral_chroma_indication_flag; + unsigned int field_seq_flag; + unsigned int frame_field_info_present_flag; + unsigned int default_display_window_flag; + struct { + unsigned int def_disp_win_left_offset; + unsigned int def_disp_win_right_offset; + unsigned int def_disp_win_top_offset; + unsigned int def_disp_win_bottom_offset; + }; + unsigned int vui_timing_info_present_flag; + struct { + unsigned int vui_num_units_in_tick; + unsigned int vui_time_scale; + unsigned int vui_poc_proportional_to_timing_flag; + unsigned int vui_num_ticks_poc_diff_one_minus1; + unsigned int vui_hrd_parameters_present_flag; + struct nal_hevc_hrd_parameters nal_hrd_parameters; + }; + unsigned int bitstream_restriction_flag; + struct { + unsigned int tiles_fixed_structure_flag; + unsigned int motion_vectors_over_pic_boundaries_flag; + unsigned int restricted_ref_pic_lists_flag; + unsigned int min_spatial_segmentation_idc; + unsigned int max_bytes_per_pic_denom; + unsigned int max_bits_per_min_cu_denom; + unsigned int log2_max_mv_length_horizontal; + unsigned int log2_max_mv_length_vertical; + }; +}; + +/** + * struct nal_hevc_sps - Sequence parameter set + * + * C struct representation of the video parameter set NAL unit as defined by + * Rec. ITU-T H.265 (02/2018) 7.3.2.2 Sequence parameter set RBSP syntax + */ +struct nal_hevc_sps { + unsigned int video_parameter_set_id; + unsigned int max_sub_layers_minus1; + unsigned int temporal_id_nesting_flag; + struct nal_hevc_profile_tier_level profile_tier_level; + unsigned int seq_parameter_set_id; + unsigned int chroma_format_idc; + unsigned int separate_colour_plane_flag; + unsigned int pic_width_in_luma_samples; + unsigned int pic_height_in_luma_samples; + unsigned int conformance_window_flag; + struct { + unsigned int conf_win_left_offset; + unsigned int conf_win_right_offset; + unsigned int conf_win_top_offset; + unsigned int conf_win_bottom_offset; + }; + + unsigned int bit_depth_luma_minus8; + unsigned int bit_depth_chroma_minus8; + unsigned int log2_max_pic_order_cnt_lsb_minus4; + unsigned int sub_layer_ordering_info_present_flag; + struct { + unsigned int max_dec_pic_buffering_minus1[7]; + unsigned int max_num_reorder_pics[7]; + unsigned int max_latency_increase_plus1[7]; + }; + unsigned int log2_min_luma_coding_block_size_minus3; + unsigned int log2_diff_max_min_luma_coding_block_size; + unsigned int log2_min_luma_transform_block_size_minus2; + unsigned int log2_diff_max_min_luma_transform_block_size; + unsigned int max_transform_hierarchy_depth_inter; + unsigned int max_transform_hierarchy_depth_intra; + + unsigned int scaling_list_enabled_flag; + unsigned int scaling_list_data_present_flag; + unsigned int amp_enabled_flag; + unsigned int sample_adaptive_offset_enabled_flag; + unsigned int pcm_enabled_flag; + struct { + unsigned int pcm_sample_bit_depth_luma_minus1; + unsigned int pcm_sample_bit_depth_chroma_minus1; + unsigned int log2_min_pcm_luma_coding_block_size_minus3; + unsigned int log2_diff_max_min_pcm_luma_coding_block_size; + unsigned int pcm_loop_filter_disabled_flag; + }; + + unsigned int num_short_term_ref_pic_sets; + unsigned int long_term_ref_pics_present_flag; + unsigned int sps_temporal_mvp_enabled_flag; + unsigned int strong_intra_smoothing_enabled_flag; + unsigned int vui_parameters_present_flag; + struct nal_hevc_vui_parameters vui; + unsigned int extension_present_flag; + struct { + unsigned int sps_range_extension_flag; + unsigned int sps_multilayer_extension_flag; + unsigned int sps_3d_extension_flag; + unsigned int sps_scc_extension_flag; + unsigned int sps_extension_4bits; + }; +}; + +struct nal_hevc_pps { + unsigned int pps_pic_parameter_set_id; + unsigned int pps_seq_parameter_set_id; + unsigned int dependent_slice_segments_enabled_flag; + unsigned int output_flag_present_flag; + unsigned int num_extra_slice_header_bits; + unsigned int sign_data_hiding_enabled_flag; + unsigned int cabac_init_present_flag; + unsigned int num_ref_idx_l0_default_active_minus1; + unsigned int num_ref_idx_l1_default_active_minus1; + int init_qp_minus26; + unsigned int constrained_intra_pred_flag; + unsigned int transform_skip_enabled_flag; + unsigned int cu_qp_delta_enabled_flag; + unsigned int diff_cu_qp_delta_depth; + int pps_cb_qp_offset; + int pps_cr_qp_offset; + unsigned int pps_slice_chroma_qp_offsets_present_flag; + unsigned int weighted_pred_flag; + unsigned int weighted_bipred_flag; + unsigned int transquant_bypass_enabled_flag; + unsigned int tiles_enabled_flag; + unsigned int entropy_coding_sync_enabled_flag; + struct { + unsigned int num_tile_columns_minus1; + unsigned int num_tile_rows_minus1; + unsigned int uniform_spacing_flag; + struct { + unsigned int column_width_minus1[1]; + unsigned int row_height_minus1[1]; + }; + unsigned int loop_filter_across_tiles_enabled_flag; + }; + unsigned int pps_loop_filter_across_slices_enabled_flag; + unsigned int deblocking_filter_control_present_flag; + struct { + unsigned int deblocking_filter_override_enabled_flag; + unsigned int pps_deblocking_filter_disabled_flag; + struct { + int pps_beta_offset_div2; + int pps_tc_offset_div2; + }; + }; + unsigned int pps_scaling_list_data_present_flag; + unsigned int lists_modification_present_flag; + unsigned int log2_parallel_merge_level_minus2; + unsigned int slice_segment_header_extension_present_flag; + unsigned int pps_extension_present_flag; + struct { + unsigned int pps_range_extension_flag; + unsigned int pps_multilayer_extension_flag; + unsigned int pps_3d_extension_flag; + unsigned int pps_scc_extension_flag; + unsigned int pps_extension_4bits; + }; +}; + +int nal_hevc_profile_from_v4l2(enum v4l2_mpeg_video_hevc_profile profile); +int nal_hevc_tier_from_v4l2(enum v4l2_mpeg_video_hevc_tier tier); +int nal_hevc_level_from_v4l2(enum v4l2_mpeg_video_hevc_level level); + +int nal_range_from_v4l2(enum v4l2_quantization quantization); +int nal_color_primaries_from_v4l2(enum v4l2_colorspace colorspace); +int nal_transfer_characteristics_from_v4l2(enum v4l2_colorspace colorspace, + enum v4l2_xfer_func xfer_func); +int nal_matrix_coeffs_from_v4l2(enum v4l2_colorspace colorspace, + enum v4l2_ycbcr_encoding ycbcr_encoding); + +ssize_t nal_hevc_write_vps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_vps *vps); +ssize_t nal_hevc_read_vps(const struct device *dev, + struct nal_hevc_vps *vps, void *src, size_t n); + +ssize_t nal_hevc_write_sps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_sps *sps); +ssize_t nal_hevc_read_sps(const struct device *dev, + struct nal_hevc_sps *sps, void *src, size_t n); + +ssize_t nal_hevc_write_pps(const struct device *dev, + void *dest, size_t n, struct nal_hevc_pps *pps); +ssize_t nal_hevc_read_pps(const struct device *dev, + struct nal_hevc_pps *pps, void *src, size_t n); + +ssize_t nal_hevc_write_filler(const struct device *dev, void *dest, size_t n); +ssize_t nal_hevc_read_filler(const struct device *dev, void *src, size_t n); + +#endif /* __NAL_HEVC_H__ */ -- cgit v1.2.3 From 8e64f00846bb19cb1910363c1acad64aa92020d4 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Fri, 15 Jan 2021 10:34:59 +0100 Subject: media: allegro: implement S_FMT for CAPTURE In order to support different codecs, the driver must support changing the format on CAPTURE. Therefore, the driver needs to handle S_FMT on CAPTURE. As the driver will have a different number of formats for OUTPUT and CAPTURE, split the check for the format index in ENUM_FMT into CAPTURE and OUTPUT. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 30 ++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index dd02ca5a584b..4d8e27464018 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -2502,13 +2502,15 @@ static int allegro_querycap(struct file *file, void *fh, static int allegro_enum_fmt_vid(struct file *file, void *fh, struct v4l2_fmtdesc *f) { - if (f->index) - return -EINVAL; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (f->index >= 1) + return -EINVAL; f->pixelformat = V4L2_PIX_FMT_NV12; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (f->index >= 1) + return -EINVAL; f->pixelformat = V4L2_PIX_FMT_H264; break; default: @@ -2556,6 +2558,28 @@ static int allegro_try_fmt_vid_cap(struct file *file, void *fh, return 0; } +static int allegro_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct allegro_channel *channel = fh_to_channel(fh); + struct vb2_queue *vq; + int err; + + err = allegro_try_fmt_vid_cap(file, fh, f); + if (err) + return err; + + vq = v4l2_m2m_get_vq(channel->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + if (vb2_is_busy(vq)) + return -EBUSY; + + channel->codec = f->fmt.pix.pixelformat; + + return 0; +} + static int allegro_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *f) { @@ -2768,7 +2792,7 @@ static const struct v4l2_ioctl_ops allegro_ioctl_ops = { .vidioc_enum_fmt_vid_out = allegro_enum_fmt_vid, .vidioc_g_fmt_vid_cap = allegro_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = allegro_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = allegro_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = allegro_s_fmt_vid_cap, .vidioc_g_fmt_vid_out = allegro_g_fmt_vid_out, .vidioc_try_fmt_vid_out = allegro_try_fmt_vid_out, .vidioc_s_fmt_vid_out = allegro_s_fmt_vid_out, -- cgit v1.2.3 From d2a1b58fd2df21d811872e4635087a91b6ba2d45 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:00:53 +0100 Subject: media: allegro: adjust channel after format change A format change (i.e. the frame size or the codec) has multiple effects on a channel: - The available controls - The limits of the available controls - The default encoding options To avoid scattering the changes all over the driver, add a new function that goes over the channel and does all required adjustments. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 69 +++++++++++++++++------ 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index 4d8e27464018..4be596d96d45 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -1963,7 +1963,6 @@ static int allegro_create_channel(struct allegro_channel *channel) { struct allegro_dev *dev = channel->dev; unsigned long timeout; - enum v4l2_mpeg_video_h264_level min_level; if (channel_exists(channel)) { v4l2_warn(&dev->v4l2_dev, @@ -1986,16 +1985,6 @@ static int allegro_create_channel(struct allegro_channel *channel) DIV_ROUND_UP(channel->framerate.numerator, channel->framerate.denominator)); - min_level = select_minimum_h264_level(channel->width, channel->height); - if (channel->level < min_level) { - v4l2_warn(&dev->v4l2_dev, - "user %d: selected Level %s too low: increasing to Level %s\n", - channel->user_id, - v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[channel->level], - v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[min_level]); - channel->level = min_level; - } - v4l2_ctrl_grab(channel->mpeg_video_h264_profile, true); v4l2_ctrl_grab(channel->mpeg_video_h264_level, true); v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, true); @@ -2031,6 +2020,53 @@ err: return channel->error; } +/** + * allegro_channel_adjust() - Adjust channel parameters to current format + * @channel: the channel to adjust + * + * Various parameters of a channel and their limits depend on the currently + * set format. Adjust the parameters after a format change in one go. + */ +static void allegro_channel_adjust(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + struct v4l2_ctrl *ctrl; + s64 min; + s64 max; + + channel->sizeimage_encoded = + estimate_stream_size(channel->width, channel->height); + + ctrl = channel->mpeg_video_h264_level; + min = select_minimum_h264_level(channel->width, channel->height); + if (ctrl->minimum > min) + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s.minimum: %lld -> %lld\n", + v4l2_ctrl_get_name(ctrl->id), ctrl->minimum, min); + v4l2_ctrl_lock(ctrl); + __v4l2_ctrl_modify_range(ctrl, min, ctrl->maximum, + ctrl->step, ctrl->default_value); + v4l2_ctrl_unlock(ctrl); + channel->level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level); + + ctrl = channel->mpeg_video_bitrate; + max = maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level)); + if (ctrl->maximum < max) + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s: maximum: %lld -> %lld\n", + v4l2_ctrl_get_name(ctrl->id), ctrl->maximum, max); + v4l2_ctrl_lock(ctrl); + __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, + ctrl->step, ctrl->default_value); + v4l2_ctrl_unlock(ctrl); + + ctrl = channel->mpeg_video_bitrate_peak; + v4l2_ctrl_lock(ctrl); + __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, + ctrl->step, ctrl->default_value); + v4l2_ctrl_unlock(ctrl); +} + static void allegro_set_default_params(struct allegro_channel *channel) { channel->width = ALLEGRO_WIDTH_DEFAULT; @@ -2050,8 +2086,6 @@ static void allegro_set_default_params(struct allegro_channel *channel) channel->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; channel->level = select_minimum_h264_level(channel->width, channel->height); - channel->sizeimage_encoded = - estimate_stream_size(channel->width, channel->height); channel->bitrate = maximum_bitrate(channel->level); channel->bitrate_peak = maximum_bitrate(channel->level); @@ -2459,6 +2493,8 @@ static int allegro_open(struct file *file) file->private_data = &channel->fh; v4l2_fh_add(&channel->fh); + allegro_channel_adjust(channel); + return 0; error: @@ -2577,6 +2613,8 @@ static int allegro_s_fmt_vid_cap(struct file *file, void *fh, channel->codec = f->fmt.pix.pixelformat; + allegro_channel_adjust(channel); + return 0; } @@ -2647,10 +2685,7 @@ static int allegro_s_fmt_vid_out(struct file *file, void *fh, channel->quantization = f->fmt.pix.quantization; channel->xfer_func = f->fmt.pix.xfer_func; - channel->level = - select_minimum_h264_level(channel->width, channel->height); - channel->sizeimage_encoded = - estimate_stream_size(channel->width, channel->height); + allegro_channel_adjust(channel); return 0; } -- cgit v1.2.3 From 83a4b7fd7f6f95da2905b7751cec4ad4dc6c222f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:00:54 +0100 Subject: media: allegro: move encoding options to channel There are several encoding options that are hard coded in the parameter that is used to configure the hardware codec. However, some of the options must be written to the SPS/PPS by the driver. Furthermore, some of the options depend on the codec that is used (i.e. H.264 or HEVC). Therefore, move options that depend on the codec to the channel and add constants for options that are independent of the codec but must be written to the SPS/PPS. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 85 +++++++++++++++++------ 1 file changed, 65 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index 4be596d96d45..d7b1cc2b9e6e 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -94,6 +94,12 @@ #define SIZE_MACROBLOCK 16 +/* Encoding options */ +#define LOG2_MAX_FRAME_NUM 4 +#define LOG2_MAX_PIC_ORDER_CNT 10 +#define BETA_OFFSET_DIV_2 -1 +#define TC_OFFSET_DIV_2 -1 + static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-2)"); @@ -202,9 +208,30 @@ struct allegro_channel { struct allegro_buffer config_blob; + unsigned int log2_max_frame_num; + bool temporal_mvp_enable; + + bool enable_loop_filter_across_tiles; + bool enable_loop_filter_across_slices; + bool dbf_ovr_en; + unsigned int num_ref_idx_l0; unsigned int num_ref_idx_l1; + /* Maximum range for motion estimation */ + int b_hrz_me_range; + int b_vrt_me_range; + int p_hrz_me_range; + int p_vrt_me_range; + /* Size limits of coding unit */ + int min_cu_size; + int max_cu_size; + /* Size limits of transform unit */ + int min_tu_size; + int max_tu_size; + int max_transfo_depth_intra; + int max_transfo_depth_inter; + struct v4l2_ctrl *mpeg_video_h264_profile; struct v4l2_ctrl *mpeg_video_h264_level; struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; @@ -911,32 +938,32 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->level = v4l2_level_to_mcu_level(channel->level); param->tier = 0; - param->log2_max_poc = 10; - param->log2_max_frame_num = 4; - param->temporal_mvp_enable = 1; + param->log2_max_poc = LOG2_MAX_PIC_ORDER_CNT; + param->log2_max_frame_num = channel->log2_max_frame_num; + param->temporal_mvp_enable = channel->temporal_mvp_enable; - param->dbf_ovr_en = 1; + param->dbf_ovr_en = channel->dbf_ovr_en; param->rdo_cost_mode = 1; param->custom_lda = 1; param->lf = 1; - param->lf_x_tile = 1; - param->lf_x_slice = 1; + param->lf_x_tile = channel->enable_loop_filter_across_tiles; + param->lf_x_slice = channel->enable_loop_filter_across_slices; param->src_bit_depth = 8; - param->beta_offset = -1; - param->tc_offset = -1; + param->beta_offset = BETA_OFFSET_DIV_2; + param->tc_offset = TC_OFFSET_DIV_2; param->num_slices = 1; - param->me_range[0] = 8; - param->me_range[1] = 8; - param->me_range[2] = 16; - param->me_range[3] = 16; - param->max_cu_size = ilog2(SIZE_MACROBLOCK); - param->min_cu_size = ilog2(8); - param->max_tu_size = 2; - param->min_tu_size = 2; - param->max_transfo_depth_intra = 1; - param->max_transfo_depth_inter = 1; + param->me_range[0] = channel->b_hrz_me_range; + param->me_range[1] = channel->b_vrt_me_range; + param->me_range[2] = channel->p_hrz_me_range; + param->me_range[3] = channel->p_vrt_me_range; + param->max_cu_size = channel->max_cu_size; + param->min_cu_size = channel->min_cu_size; + param->max_tu_size = channel->max_tu_size; + param->min_tu_size = channel->min_tu_size; + param->max_transfo_depth_intra = channel->max_transfo_depth_intra; + param->max_transfo_depth_inter = channel->max_transfo_depth_inter; param->prefetch_auto = 0; param->prefetch_mem_offset = 0; @@ -1266,9 +1293,9 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, sps->constraint_set5_flag = 0; sps->level_idc = nal_h264_level_from_v4l2(channel->level); sps->seq_parameter_set_id = 0; - sps->log2_max_frame_num_minus4 = 0; + sps->log2_max_frame_num_minus4 = LOG2_MAX_FRAME_NUM - 4; sps->pic_order_cnt_type = 0; - sps->log2_max_pic_order_cnt_lsb_minus4 = 6; + sps->log2_max_pic_order_cnt_lsb_minus4 = LOG2_MAX_PIC_ORDER_CNT - 4; sps->max_num_ref_frames = 3; sps->gaps_in_frame_num_value_allowed_flag = 0; sps->pic_width_in_mbs_minus1 = @@ -2065,6 +2092,24 @@ static void allegro_channel_adjust(struct allegro_channel *channel) __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, ctrl->step, ctrl->default_value); v4l2_ctrl_unlock(ctrl); + + channel->log2_max_frame_num = LOG2_MAX_FRAME_NUM; + channel->temporal_mvp_enable = true; + + channel->dbf_ovr_en = true; + channel->enable_loop_filter_across_tiles = true; + channel->enable_loop_filter_across_slices = true; + + channel->b_hrz_me_range = 8; + channel->b_vrt_me_range = 8; + channel->p_hrz_me_range = 16; + channel->p_vrt_me_range = 16; + channel->max_cu_size = ilog2(16); + channel->min_cu_size = ilog2(8); + channel->max_tu_size = ilog2(4); + channel->min_tu_size = ilog2(4); + channel->max_transfo_depth_intra = 1; + channel->max_transfo_depth_inter = 1; } static void allegro_set_default_params(struct allegro_channel *channel) -- cgit v1.2.3 From e7cd90988b9d03c3a886a23f015c41c71371bc4f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:00:55 +0100 Subject: media: allegro: fix log2_max_poc in firmware 2019.1 The log2_max_poc field is used to set log2_max_pic_order_cnt_lsb_minus4 for the coded stream. It has an allowed range of 0 to 12. param contains the value without the minus4, but since firmware version 2019.1, the value has to be log2_max_pic_order_cnt_lsb - 1, presumably to fit the maximum value of 16 into a 4 bit field. The driver does not support firmware version 2019.1. Thus, change the behaviour starting from firmware version 2019.2. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-mail.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.c b/drivers/media/platform/allegro-dvt/allegro-mail.c index 993e16f06305..5dbc1c029020 100644 --- a/drivers/media/platform/allegro-dvt/allegro-mail.c +++ b/drivers/media/platform/allegro-dvt/allegro-mail.c @@ -109,8 +109,11 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) val = 0; val |= param->temporal_mvp_enable ? BIT(20) : 0; - val |= FIELD_PREP(GENMASK(7, 4), param->log2_max_frame_num) | - FIELD_PREP(GENMASK(3, 0), param->log2_max_poc); + val |= FIELD_PREP(GENMASK(7, 4), param->log2_max_frame_num); + if (version >= MCU_MSG_VERSION_2019_2) + val |= FIELD_PREP(GENMASK(3, 0), param->log2_max_poc - 1); + else + val |= FIELD_PREP(GENMASK(3, 0), param->log2_max_poc); dst[i++] = val; val = 0; -- cgit v1.2.3 From 21de56fa0789cf1c7e9a6d946f45c79ead04ffd4 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:00:56 +0100 Subject: media: allegro: use handler_setup to configure channel v4l2_ctrl_handler_setup() calls s_ctrl for all controls of the handler. This ensures that the channel is initialized using the default values of the v4l2-ctrls. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index d7b1cc2b9e6e..a9a9e9bb0401 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -2520,6 +2520,8 @@ static int allegro_open(struct file *file) v4l2_ctrl_cluster(3, &channel->mpeg_video_bitrate_mode); + v4l2_ctrl_handler_setup(handler); + channel->mcu_channel_id = -1; channel->user_id = -1; -- cgit v1.2.3 From 2e3917e6bd226b2d69f903392592d701a388e4f0 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:00:57 +0100 Subject: media: allegro: initialize bitrate using v4l2_ctrl As the driver now uses the v4l2-ctrls to setup the channel, there is no need to explicitly set the bitrate. The initial bitrate is now set via the same path as if it is set from userspace using the v4l2-ctrl. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index a9a9e9bb0401..64cbd4f2c371 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -2132,8 +2132,6 @@ static void allegro_set_default_params(struct allegro_channel *channel) channel->level = select_minimum_h264_level(channel->width, channel->height); - channel->bitrate = maximum_bitrate(channel->level); - channel->bitrate_peak = maximum_bitrate(channel->level); channel->cpb_size = maximum_cpb_size(channel->level); channel->gop_size = ALLEGRO_GOP_SIZE_DEFAULT; } @@ -2421,6 +2419,8 @@ static int allegro_open(struct file *file) struct v4l2_ctrl_handler *handler; u64 mask; int ret; + unsigned int bitrate_max; + unsigned int bitrate_def; channel = kzalloc(sizeof(*channel), GFP_KERNEL); if (!channel) @@ -2486,16 +2486,17 @@ static int allegro_open(struct file *file) V4L2_CID_MPEG_VIDEO_BITRATE_MODE, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + + bitrate_max = maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + bitrate_def = maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE, - 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->bitrate); + 0, bitrate_max, 1, bitrate_def); channel->mpeg_video_bitrate_peak = v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, - 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->bitrate_peak); + 0, bitrate_max, 1, bitrate_def); channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, -- cgit v1.2.3 From d4a881c1b9f61978f05412487877b6745f58478d Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:00:58 +0100 Subject: media: allegro: implement scaling of cpb size in SPS The cbp_size_value_minus1 in the SPS can scaled using cpb_size_scale to reduce the number of bits necessary to encode the value. For simplicity, the scaling was set to 1. Restructure to the code to make it easier to drop the cbp_size from the channel and as we are at it, also properly implement the scaling. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index 64cbd4f2c371..9aaf8891e49d 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -1279,6 +1279,8 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, /* Calculation of crop units in Rec. ITU-T H.264 (04/2017) p. 76 */ unsigned int crop_unit_x = 2; unsigned int crop_unit_y = 2; + unsigned int cpb_size; + unsigned int cpb_size_scale; sps = kzalloc(sizeof(*sps), GFP_KERNEL); if (!sps) @@ -1336,13 +1338,15 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, sps->vui.vcl_hrd_parameters_present_flag = 1; sps->vui.vcl_hrd_parameters.cpb_cnt_minus1 = 0; sps->vui.vcl_hrd_parameters.bit_rate_scale = 0; - sps->vui.vcl_hrd_parameters.cpb_size_scale = 1; /* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */ sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] = channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1; /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */ + cpb_size = channel->cpb_size; + cpb_size_scale = ffs(cpb_size) - 4; + sps->vui.vcl_hrd_parameters.cpb_size_scale = cpb_size_scale; sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] = - (channel->cpb_size * 1000) / (1 << (4 + sps->vui.vcl_hrd_parameters.cpb_size_scale)) - 1; + (cpb_size * 1000) / (1 << (4 + cpb_size_scale)) - 1; sps->vui.vcl_hrd_parameters.cbr_flag[0] = !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable); sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 = 31; -- cgit v1.2.3 From 7f046e4b05b382d6e1578b1963d79a1b68ee5be3 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:00:59 +0100 Subject: media: allegro: remove cpb_size and gop_size from channel The cpb_size and the gop_size are straight copies of the values in the v4l2-ctrls. To avoid this duplication, directly get the values from the v4l2-ctrls and remove the copies. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 30 +++++++++-------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index 9aaf8891e49d..ea3042640c9f 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -203,8 +203,6 @@ struct allegro_channel { bool frame_rc_enable; unsigned int bitrate; unsigned int bitrate_peak; - unsigned int cpb_size; - unsigned int gop_size; struct allegro_buffer config_blob; @@ -925,6 +923,7 @@ static int fill_create_channel_param(struct allegro_channel *channel, int p_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); int b_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode); + unsigned int cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size); param->width = channel->width; param->height = channel->height; @@ -972,8 +971,7 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->rate_control_mode = channel->frame_rc_enable ? v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0; - param->cpb_size = v4l2_cpb_size_to_mcu(channel->cpb_size, - channel->bitrate_peak); + param->cpb_size = v4l2_cpb_size_to_mcu(cpb_size, channel->bitrate_peak); /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */ param->initial_rem_delay = param->cpb_size; param->framerate = DIV_ROUND_UP(channel->framerate.numerator, @@ -996,10 +994,10 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->max_pixel_value = 255; param->gop_ctrl_mode = 0x00000002; - param->freq_idr = channel->gop_size; + param->freq_idr = v4l2_ctrl_g_ctrl(channel->mpeg_video_gop_size); param->freq_lt = 0; param->gdr_mode = 0x00000000; - param->gop_length = channel->gop_size; + param->gop_length = v4l2_ctrl_g_ctrl(channel->mpeg_video_gop_size); param->subframe_latency = 0x00000000; param->lda_factors[0] = 51; @@ -1342,7 +1340,7 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] = channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1; /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */ - cpb_size = channel->cpb_size; + cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size); cpb_size_scale = ffs(cpb_size) - 4; sps->vui.vcl_hrd_parameters.cpb_size_scale = cpb_size_scale; sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] = @@ -2135,9 +2133,6 @@ static void allegro_set_default_params(struct allegro_channel *channel) channel->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; channel->level = select_minimum_h264_level(channel->width, channel->height); - - channel->cpb_size = maximum_cpb_size(channel->level); - channel->gop_size = ALLEGRO_GOP_SIZE_DEFAULT; } static int allegro_queue_setup(struct vb2_queue *vq, @@ -2394,12 +2389,6 @@ static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) v4l2_ctrl_activate(channel->mpeg_video_bitrate_peak, ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); break; - case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: - channel->cpb_size = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - channel->gop_size = ctrl->val; - break; case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: @@ -2425,6 +2414,8 @@ static int allegro_open(struct file *file) int ret; unsigned int bitrate_max; unsigned int bitrate_def; + unsigned int cpb_size_max; + unsigned int cpb_size_def; channel = kzalloc(sizeof(*channel), GFP_KERNEL); if (!channel) @@ -2493,6 +2484,8 @@ static int allegro_open(struct file *file) bitrate_max = maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); bitrate_def = maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + cpb_size_max = maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + cpb_size_def = maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE, @@ -2504,13 +2497,12 @@ static int allegro_open(struct file *file) channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, - 0, maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), - 1, channel->cpb_size); + 0, cpb_size_max, 1, cpb_size_def); channel->mpeg_video_gop_size = v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, ALLEGRO_GOP_SIZE_MAX, - 1, channel->gop_size); + 1, ALLEGRO_GOP_SIZE_DEFAULT); v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, -- cgit v1.2.3 From 608341075c2d5ead508962e837fb53a25ef3ffba Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:01:00 +0100 Subject: media: allegro: remove profile and level from channel The profile and level are straight copies from the v4l2-ctrls. Avoid duplication and directly read the value of the v4l2-ctrl. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index ea3042640c9f..de6571251a07 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -195,8 +195,6 @@ struct allegro_channel { unsigned int osequence; u32 codec; - enum v4l2_mpeg_video_h264_profile profile; - enum v4l2_mpeg_video_h264_level level; unsigned int sizeimage_encoded; unsigned int csequence; @@ -924,6 +922,8 @@ static int fill_create_channel_param(struct allegro_channel *channel, int b_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode); unsigned int cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size); + enum v4l2_mpeg_video_h264_profile profile; + enum v4l2_mpeg_video_h264_level level; param->width = channel->width; param->height = channel->height; @@ -931,11 +931,13 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->colorspace = v4l2_colorspace_to_mcu_colorspace(channel->colorspace); param->src_mode = 0x0; - param->profile = v4l2_profile_to_mcu_profile(channel->profile); - param->constraint_set_flags = BIT(1); + param->codec = channel->codec; - param->level = v4l2_level_to_mcu_level(channel->level); - param->tier = 0; + profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_profile); + level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level); + param->profile = v4l2_profile_to_mcu_profile(profile); + param->constraint_set_flags = BIT(1); + param->level = v4l2_level_to_mcu_level(level); param->log2_max_poc = LOG2_MAX_PIC_ORDER_CNT; param->log2_max_frame_num = channel->log2_max_frame_num; @@ -1277,6 +1279,8 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, /* Calculation of crop units in Rec. ITU-T H.264 (04/2017) p. 76 */ unsigned int crop_unit_x = 2; unsigned int crop_unit_y = 2; + enum v4l2_mpeg_video_h264_profile profile; + enum v4l2_mpeg_video_h264_level level; unsigned int cpb_size; unsigned int cpb_size_scale; @@ -1284,14 +1288,17 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, if (!sps) return -ENOMEM; - sps->profile_idc = nal_h264_profile_from_v4l2(channel->profile); + profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_profile); + level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level); + + sps->profile_idc = nal_h264_profile_from_v4l2(profile); sps->constraint_set0_flag = 0; sps->constraint_set1_flag = 1; sps->constraint_set2_flag = 0; sps->constraint_set3_flag = 0; sps->constraint_set4_flag = 0; sps->constraint_set5_flag = 0; - sps->level_idc = nal_h264_level_from_v4l2(channel->level); + sps->level_idc = nal_h264_level_from_v4l2(level); sps->seq_parameter_set_id = 0; sps->log2_max_frame_num_minus4 = LOG2_MAX_FRAME_NUM - 4; sps->pic_order_cnt_type = 0; @@ -2076,7 +2083,6 @@ static void allegro_channel_adjust(struct allegro_channel *channel) __v4l2_ctrl_modify_range(ctrl, min, ctrl->maximum, ctrl->step, ctrl->default_value); v4l2_ctrl_unlock(ctrl); - channel->level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level); ctrl = channel->mpeg_video_bitrate; max = maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level)); @@ -2130,9 +2136,6 @@ static void allegro_set_default_params(struct allegro_channel *channel) channel->sizeimage_raw = channel->stride * channel->height * 3 / 2; channel->codec = V4L2_PIX_FMT_H264; - channel->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; - channel->level = - select_minimum_h264_level(channel->width, channel->height); } static int allegro_queue_setup(struct vb2_queue *vq, @@ -2377,9 +2380,6 @@ static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) "s_ctrl: %s = %d\n", v4l2_ctrl_get_name(ctrl->id), ctrl->val); switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - channel->level = ctrl->val; - break; case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: channel->frame_rc_enable = ctrl->val; break; -- cgit v1.2.3 From 2b6e6e5b26cf556c576887d8d4e617d3020b6ed2 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:01:01 +0100 Subject: media: allegro: use accessor functions for QP values V4L2 specifies different controls for the QP values of different codecs. Simplify users that just care for the QP values by providing accessor function that return the correct control based on the currently set codec. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 40 ++++++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index de6571251a07..ffb989b46bf9 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -264,6 +264,36 @@ struct allegro_channel { unsigned int error; }; +static inline int +allegro_channel_get_i_frame_qp(struct allegro_channel *channel) +{ + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); +} + +static inline int +allegro_channel_get_p_frame_qp(struct allegro_channel *channel) +{ + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); +} + +static inline int +allegro_channel_get_b_frame_qp(struct allegro_channel *channel) +{ + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); +} + +static inline int +allegro_channel_get_min_qp(struct allegro_channel *channel) +{ + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); +} + +static inline int +allegro_channel_get_max_qp(struct allegro_channel *channel) +{ + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); +} + struct allegro_m2m_buffer { struct v4l2_m2m_buffer buf; struct list_head head; @@ -917,9 +947,9 @@ static s16 get_qp_delta(int minuend, int subtrahend) static int fill_create_channel_param(struct allegro_channel *channel, struct create_channel_param *param) { - int i_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); - int p_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); - int b_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); + int i_frame_qp = allegro_channel_get_i_frame_qp(channel); + int p_frame_qp = allegro_channel_get_p_frame_qp(channel); + int b_frame_qp = allegro_channel_get_b_frame_qp(channel); int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode); unsigned int cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size); enum v4l2_mpeg_video_h264_profile profile; @@ -982,8 +1012,8 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->target_bitrate = channel->bitrate; param->max_bitrate = channel->bitrate_peak; param->initial_qp = i_frame_qp; - param->min_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); - param->max_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); + param->min_qp = allegro_channel_get_min_qp(channel); + param->max_qp = allegro_channel_get_max_qp(channel); param->ip_delta = get_qp_delta(i_frame_qp, p_frame_qp); param->pb_delta = get_qp_delta(p_frame_qp, b_frame_qp); param->golden_ref = 0; -- cgit v1.2.3 From 655ef9f9e87568aff031391712669b74d1b362f1 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:01:02 +0100 Subject: media: allegro: add helper to get entropy mode The driver only supports CAVLC for H.264, but HEVC only uses CABAC. As the driver has to explicitly tell the MCU to use CABAC for HEVC, add a helper function to get the entropy mode. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index ffb989b46bf9..93559ffe9fd2 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -944,6 +944,14 @@ static s16 get_qp_delta(int minuend, int subtrahend) return minuend - subtrahend; } +static u32 allegro_channel_get_entropy_mode(struct allegro_channel *channel) +{ +#define ALLEGRO_ENTROPY_MODE_CAVLC 0 +#define ALLEGRO_ENTROPY_MODE_CABAC 1 + + return ALLEGRO_ENTROPY_MODE_CAVLC; +} + static int fill_create_channel_param(struct allegro_channel *channel, struct create_channel_param *param) { @@ -974,6 +982,7 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->temporal_mvp_enable = channel->temporal_mvp_enable; param->dbf_ovr_en = channel->dbf_ovr_en; + param->entropy_mode = allegro_channel_get_entropy_mode(channel); param->rdo_cost_mode = 1; param->custom_lda = 1; param->lf = 1; -- cgit v1.2.3 From 4132dcbf5ccd8857d0be3ef80d2a8b70662fbc40 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:01:03 +0100 Subject: media: allegro: rename codec specific functions The maximum bitrate and the size of the CPB are dependent on the level. As the levels differ between the different codecs, the functions to get the maximum bitrate and CPB must be codec specific, too. Rename the functions to make it obvious that the function are only valid for H.264. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index 93559ffe9fd2..81f68b67016f 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -507,7 +507,7 @@ select_minimum_h264_level(unsigned int width, unsigned int height) return level; } -static unsigned int maximum_bitrate(enum v4l2_mpeg_video_h264_level level) +static unsigned int h264_maximum_bitrate(enum v4l2_mpeg_video_h264_level level) { switch (level) { case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: @@ -546,7 +546,7 @@ static unsigned int maximum_bitrate(enum v4l2_mpeg_video_h264_level level) } } -static unsigned int maximum_cpb_size(enum v4l2_mpeg_video_h264_level level) +static unsigned int h264_maximum_cpb_size(enum v4l2_mpeg_video_h264_level level) { switch (level) { case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: @@ -2124,7 +2124,7 @@ static void allegro_channel_adjust(struct allegro_channel *channel) v4l2_ctrl_unlock(ctrl); ctrl = channel->mpeg_video_bitrate; - max = maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level)); + max = h264_maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level)); if (ctrl->maximum < max) v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: maximum: %lld -> %lld\n", @@ -2521,10 +2521,10 @@ static int allegro_open(struct file *file) V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); - bitrate_max = maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); - bitrate_def = maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); - cpb_size_max = maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); - cpb_size_def = maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + bitrate_max = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + bitrate_def = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + cpb_size_max = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + cpb_size_def = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE, -- cgit v1.2.3 From be7f41f2f10c8605ba4b9a5515cf449006470b90 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:01:04 +0100 Subject: media: allegro: increase offset in CAPTURE buffer The HEVC Non-VCL NAL units require more space than the H.264 Non-VCL NAL units. Therefore, the driver needs to reserve more space in front of the actual coded data that is written by the hardware codec. Increase the offset that shall be used by the hardware codec from 64 bytes to 128 bytes which is as arbitrary as before. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index 81f68b67016f..bb024cff4c7c 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -90,7 +90,7 @@ * because it needs to write SPS/PPS NAL units. The encoder writes the actual * frame data after the offset. */ -#define ENCODER_STREAM_OFFSET SZ_64 +#define ENCODER_STREAM_OFFSET SZ_128 #define SIZE_MACROBLOCK 16 -- cgit v1.2.3 From 99b05ce74ceeb474ff4db37a0861b135063b7c7f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:01:05 +0100 Subject: media: allegro: activate v4l2-ctrls only for current codec These controls are specific to H.264 and shall only be activated, if the coded format is H.264. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index bb024cff4c7c..f3a656ab2b06 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -2105,6 +2105,7 @@ err: static void allegro_channel_adjust(struct allegro_channel *channel) { struct allegro_dev *dev = channel->dev; + u32 codec = channel->codec; struct v4l2_ctrl *ctrl; s64 min; s64 max; @@ -2140,6 +2141,21 @@ static void allegro_channel_adjust(struct allegro_channel *channel) ctrl->step, ctrl->default_value); v4l2_ctrl_unlock(ctrl); + v4l2_ctrl_activate(channel->mpeg_video_h264_profile, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_level, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_i_frame_qp, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_max_qp, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_min_qp, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_p_frame_qp, + codec == V4L2_PIX_FMT_H264); + v4l2_ctrl_activate(channel->mpeg_video_h264_b_frame_qp, + codec == V4L2_PIX_FMT_H264); + channel->log2_max_frame_num = LOG2_MAX_FRAME_NUM; channel->temporal_mvp_enable = true; -- cgit v1.2.3 From b08797d1b2ec25165768c841a2ac484fecd72be1 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 3 Dec 2020 12:01:06 +0100 Subject: media: allegro: add support for HEVC encoding The Allegro Codec supports HEVC encoding. The messages to the MCU are the same for H.264 and HEVC, but some options have to be changed. These are actually only a few options. The driver, however, must add the HEVC VPS/SPS/PPS NAL Units to the coded stream and must properly provide the HEVC format and controls to user space. [hverkuil: fix warning for wrong enum type (h264 instead of hevc)] Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/allegro-dvt/allegro-core.c | 545 ++++++++++++++++++++-- drivers/media/platform/allegro-dvt/allegro-mail.c | 6 + drivers/media/platform/allegro-dvt/allegro-mail.h | 1 + 3 files changed, 516 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index f3a656ab2b06..887b492e4ad1 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -30,6 +30,7 @@ #include "allegro-mail.h" #include "nal-h264.h" +#include "nal-hevc.h" /* * Support up to 4k video streams. The hardware actually supports higher @@ -209,6 +210,8 @@ struct allegro_channel { bool enable_loop_filter_across_tiles; bool enable_loop_filter_across_slices; + bool enable_deblocking_filter_override; + bool enable_reordering; bool dbf_ovr_en; unsigned int num_ref_idx_l0; @@ -235,6 +238,16 @@ struct allegro_channel { struct v4l2_ctrl *mpeg_video_h264_min_qp; struct v4l2_ctrl *mpeg_video_h264_p_frame_qp; struct v4l2_ctrl *mpeg_video_h264_b_frame_qp; + + struct v4l2_ctrl *mpeg_video_hevc_profile; + struct v4l2_ctrl *mpeg_video_hevc_level; + struct v4l2_ctrl *mpeg_video_hevc_tier; + struct v4l2_ctrl *mpeg_video_hevc_i_frame_qp; + struct v4l2_ctrl *mpeg_video_hevc_max_qp; + struct v4l2_ctrl *mpeg_video_hevc_min_qp; + struct v4l2_ctrl *mpeg_video_hevc_p_frame_qp; + struct v4l2_ctrl *mpeg_video_hevc_b_frame_qp; + struct v4l2_ctrl *mpeg_video_frame_rc_enable; struct { /* video bitrate mode control cluster */ struct v4l2_ctrl *mpeg_video_bitrate_mode; @@ -267,31 +280,46 @@ struct allegro_channel { static inline int allegro_channel_get_i_frame_qp(struct allegro_channel *channel) { - return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); + if (channel->codec == V4L2_PIX_FMT_HEVC) + return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_i_frame_qp); + else + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); } static inline int allegro_channel_get_p_frame_qp(struct allegro_channel *channel) { - return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); + if (channel->codec == V4L2_PIX_FMT_HEVC) + return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_p_frame_qp); + else + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); } static inline int allegro_channel_get_b_frame_qp(struct allegro_channel *channel) { - return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); + if (channel->codec == V4L2_PIX_FMT_HEVC) + return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_b_frame_qp); + else + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); } static inline int allegro_channel_get_min_qp(struct allegro_channel *channel) { - return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); + if (channel->codec == V4L2_PIX_FMT_HEVC) + return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_min_qp); + else + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); } static inline int allegro_channel_get_max_qp(struct allegro_channel *channel) { - return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); + if (channel->codec == V4L2_PIX_FMT_HEVC) + return v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_max_qp); + else + return v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); } struct allegro_m2m_buffer { @@ -585,6 +613,86 @@ static unsigned int h264_maximum_cpb_size(enum v4l2_mpeg_video_h264_level level) } } +static enum v4l2_mpeg_video_hevc_level +select_minimum_hevc_level(unsigned int width, unsigned int height) +{ + unsigned int luma_picture_size = width * height; + enum v4l2_mpeg_video_hevc_level level; + + if (luma_picture_size <= 36864) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_1; + else if (luma_picture_size <= 122880) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_2; + else if (luma_picture_size <= 245760) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1; + else if (luma_picture_size <= 552960) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_3; + else if (luma_picture_size <= 983040) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1; + else if (luma_picture_size <= 2228224) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_4; + else if (luma_picture_size <= 8912896) + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_5; + else + level = V4L2_MPEG_VIDEO_HEVC_LEVEL_6; + + return level; +} + +static unsigned int hevc_maximum_bitrate(enum v4l2_mpeg_video_hevc_level level) +{ + /* + * See Rec. ITU-T H.265 v5 (02/2018), A.4.2 Profile-specific level + * limits for the video profiles. + */ + switch (level) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + return 128; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + return 1500; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + return 3000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + return 6000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + return 10000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + return 12000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + return 20000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + return 25000; + default: + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + return 40000; + } +} + +static unsigned int hevc_maximum_cpb_size(enum v4l2_mpeg_video_hevc_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + return 350; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + return 1500; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + return 3000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + return 6000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + return 10000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + return 12000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + return 20000; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + return 25000; + default: + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + return 40000; + } +} + static const struct fw_info * allegro_get_firmware_info(struct allegro_dev *dev, const struct firmware *fw, @@ -908,6 +1016,55 @@ static u16 v4l2_level_to_mcu_level(enum v4l2_mpeg_video_h264_level level) } } +static u8 hevc_profile_to_mcu_profile(enum v4l2_mpeg_video_hevc_profile profile) +{ + switch (profile) { + default: + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + return 1; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: + return 2; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: + return 3; + } +} + +static u16 hevc_level_to_mcu_level(enum v4l2_mpeg_video_hevc_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + return 10; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + return 20; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + return 21; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + return 30; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + return 31; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + return 40; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + return 41; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + return 50; + default: + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + return 51; + } +} + +static u8 hevc_tier_to_mcu_tier(enum v4l2_mpeg_video_hevc_tier tier) +{ + switch (tier) { + default: + case V4L2_MPEG_VIDEO_HEVC_TIER_MAIN: + return 0; + case V4L2_MPEG_VIDEO_HEVC_TIER_HIGH: + return 1; + } +} + static u32 v4l2_bitrate_mode_to_mcu_mode(enum v4l2_mpeg_video_bitrate_mode mode) { @@ -949,6 +1106,10 @@ static u32 allegro_channel_get_entropy_mode(struct allegro_channel *channel) #define ALLEGRO_ENTROPY_MODE_CAVLC 0 #define ALLEGRO_ENTROPY_MODE_CABAC 1 + /* HEVC always uses CABAC, but this has to be explicitly set */ + if (channel->codec == V4L2_PIX_FMT_HEVC) + return ALLEGRO_ENTROPY_MODE_CABAC; + return ALLEGRO_ENTROPY_MODE_CAVLC; } @@ -960,8 +1121,6 @@ static int fill_create_channel_param(struct allegro_channel *channel, int b_frame_qp = allegro_channel_get_b_frame_qp(channel); int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode); unsigned int cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size); - enum v4l2_mpeg_video_h264_profile profile; - enum v4l2_mpeg_video_h264_level level; param->width = channel->width; param->height = channel->height; @@ -971,17 +1130,37 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->src_mode = 0x0; param->codec = channel->codec; - profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_profile); - level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level); - param->profile = v4l2_profile_to_mcu_profile(profile); - param->constraint_set_flags = BIT(1); - param->level = v4l2_level_to_mcu_level(level); + if (channel->codec == V4L2_PIX_FMT_H264) { + enum v4l2_mpeg_video_h264_profile profile; + enum v4l2_mpeg_video_h264_level level; + + profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_profile); + level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level); + + param->profile = v4l2_profile_to_mcu_profile(profile); + param->constraint_set_flags = BIT(1); + param->level = v4l2_level_to_mcu_level(level); + } else { + enum v4l2_mpeg_video_hevc_profile profile; + enum v4l2_mpeg_video_hevc_level level; + enum v4l2_mpeg_video_hevc_tier tier; + + profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_profile); + level = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level); + tier = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_tier); + + param->profile = hevc_profile_to_mcu_profile(profile); + param->level = hevc_level_to_mcu_level(level); + param->tier = hevc_tier_to_mcu_tier(tier); + } param->log2_max_poc = LOG2_MAX_PIC_ORDER_CNT; param->log2_max_frame_num = channel->log2_max_frame_num; param->temporal_mvp_enable = channel->temporal_mvp_enable; param->dbf_ovr_en = channel->dbf_ovr_en; + param->override_lf = channel->enable_deblocking_filter_override; + param->enable_reordering = channel->enable_reordering; param->entropy_mode = allegro_channel_get_entropy_mode(channel); param->rdo_cost_mode = 1; param->custom_lda = 1; @@ -1454,6 +1633,158 @@ static void allegro_channel_eos_event(struct allegro_channel *channel) v4l2_event_queue_fh(&channel->fh, &eos_event); } +static ssize_t allegro_hevc_write_vps(struct allegro_channel *channel, + void *dest, size_t n) +{ + struct allegro_dev *dev = channel->dev; + struct nal_hevc_vps *vps; + struct nal_hevc_profile_tier_level *ptl; + ssize_t size; + unsigned int num_ref_frames = channel->num_ref_idx_l0; + s32 profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_profile); + s32 level = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level); + s32 tier = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_tier); + + vps = kzalloc(sizeof(*vps), GFP_KERNEL); + if (!vps) + return -ENOMEM; + + vps->base_layer_internal_flag = 1; + vps->base_layer_available_flag = 1; + vps->temporal_id_nesting_flag = 1; + + ptl = &vps->profile_tier_level; + ptl->general_profile_idc = nal_hevc_profile_from_v4l2(profile); + ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1; + ptl->general_tier_flag = nal_hevc_tier_from_v4l2(tier); + ptl->general_progressive_source_flag = 1; + ptl->general_frame_only_constraint_flag = 1; + ptl->general_level_idc = nal_hevc_level_from_v4l2(level); + + vps->sub_layer_ordering_info_present_flag = 0; + vps->max_dec_pic_buffering_minus1[0] = num_ref_frames; + vps->max_num_reorder_pics[0] = num_ref_frames; + + size = nal_hevc_write_vps(&dev->plat_dev->dev, dest, n, vps); + + kfree(vps); + + return size; +} + +static ssize_t allegro_hevc_write_sps(struct allegro_channel *channel, + void *dest, size_t n) +{ + struct allegro_dev *dev = channel->dev; + struct nal_hevc_sps *sps; + struct nal_hevc_profile_tier_level *ptl; + ssize_t size; + unsigned int num_ref_frames = channel->num_ref_idx_l0; + s32 profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_profile); + s32 level = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level); + s32 tier = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_tier); + + sps = kzalloc(sizeof(*sps), GFP_KERNEL); + if (!sps) + return -ENOMEM; + + sps->temporal_id_nesting_flag = 1; + + ptl = &sps->profile_tier_level; + ptl->general_profile_idc = nal_hevc_profile_from_v4l2(profile); + ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1; + ptl->general_tier_flag = nal_hevc_tier_from_v4l2(tier); + ptl->general_progressive_source_flag = 1; + ptl->general_frame_only_constraint_flag = 1; + ptl->general_level_idc = nal_hevc_level_from_v4l2(level); + + sps->seq_parameter_set_id = 0; + sps->chroma_format_idc = 1; /* Only 4:2:0 sampling supported */ + sps->pic_width_in_luma_samples = round_up(channel->width, 8); + sps->pic_height_in_luma_samples = round_up(channel->height, 8); + sps->conf_win_right_offset = + sps->pic_width_in_luma_samples - channel->width; + sps->conf_win_bottom_offset = + sps->pic_height_in_luma_samples - channel->height; + sps->conformance_window_flag = + sps->conf_win_right_offset || sps->conf_win_bottom_offset; + + sps->log2_max_pic_order_cnt_lsb_minus4 = LOG2_MAX_PIC_ORDER_CNT - 4; + + sps->sub_layer_ordering_info_present_flag = 1; + sps->max_dec_pic_buffering_minus1[0] = num_ref_frames; + sps->max_num_reorder_pics[0] = num_ref_frames; + + sps->log2_min_luma_coding_block_size_minus3 = + channel->min_cu_size - 3; + sps->log2_diff_max_min_luma_coding_block_size = + channel->max_cu_size - channel->min_cu_size; + sps->log2_min_luma_transform_block_size_minus2 = + channel->min_tu_size - 2; + sps->log2_diff_max_min_luma_transform_block_size = + channel->max_tu_size - channel->min_tu_size; + sps->max_transform_hierarchy_depth_intra = + channel->max_transfo_depth_intra; + sps->max_transform_hierarchy_depth_inter = + channel->max_transfo_depth_inter; + + sps->sps_temporal_mvp_enabled_flag = channel->temporal_mvp_enable; + sps->strong_intra_smoothing_enabled_flag = channel->max_cu_size > 4; + + size = nal_hevc_write_sps(&dev->plat_dev->dev, dest, n, sps); + + kfree(sps); + + return size; +} + +static ssize_t allegro_hevc_write_pps(struct allegro_channel *channel, + struct mcu_msg_encode_frame_response *msg, + void *dest, size_t n) +{ + struct allegro_dev *dev = channel->dev; + struct nal_hevc_pps *pps; + ssize_t size; + int i; + + pps = kzalloc(sizeof(*pps), GFP_KERNEL); + if (!pps) + return -ENOMEM; + + pps->pps_pic_parameter_set_id = 0; + pps->pps_seq_parameter_set_id = 0; + + if (msg->num_column > 1 || msg->num_row > 1) { + pps->tiles_enabled_flag = 1; + pps->num_tile_columns_minus1 = msg->num_column - 1; + pps->num_tile_rows_minus1 = msg->num_row - 1; + + for (i = 0; i < msg->num_column; i++) + pps->column_width_minus1[i] = msg->tile_width[i] - 1; + + for (i = 0; i < msg->num_row; i++) + pps->row_height_minus1[i] = msg->tile_height[i] - 1; + } + + pps->loop_filter_across_tiles_enabled_flag = + channel->enable_loop_filter_across_tiles; + pps->pps_loop_filter_across_slices_enabled_flag = + channel->enable_loop_filter_across_slices; + pps->deblocking_filter_control_present_flag = 1; + pps->deblocking_filter_override_enabled_flag = + channel->enable_deblocking_filter_override; + pps->pps_beta_offset_div2 = BETA_OFFSET_DIV_2; + pps->pps_tc_offset_div2 = TC_OFFSET_DIV_2; + + pps->lists_modification_present_flag = channel->enable_reordering; + + size = nal_hevc_write_pps(&dev->plat_dev->dev, dest, n, pps); + + kfree(pps); + + return size; +} + static u64 allegro_put_buffer(struct allegro_channel *channel, struct list_head *list, struct vb2_v4l2_buffer *buffer) @@ -1577,8 +1908,27 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, curr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); free = partition->offset; + + if (channel->codec == V4L2_PIX_FMT_HEVC && msg->is_idr) { + len = allegro_hevc_write_vps(channel, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "not enough space for video parameter set: %zd left\n", + free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: wrote %zd byte VPS nal unit\n", + channel->mcu_channel_id, len); + } + if (msg->is_idr) { - len = allegro_h264_write_sps(channel, curr, free); + if (channel->codec == V4L2_PIX_FMT_H264) + len = allegro_h264_write_sps(channel, curr, free); + else + len = allegro_hevc_write_sps(channel, curr, free); if (len < 0) { v4l2_err(&dev->v4l2_dev, "not enough space for sequence parameter set: %zd left\n", @@ -1593,7 +1943,10 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, } if (msg->slice_type == AL_ENC_SLICE_TYPE_I) { - len = allegro_h264_write_pps(channel, curr, free); + if (channel->codec == V4L2_PIX_FMT_H264) + len = allegro_h264_write_pps(channel, curr, free); + else + len = allegro_hevc_write_pps(channel, msg, curr, free); if (len < 0) { v4l2_err(&dev->v4l2_dev, "not enough space for picture parameter set: %zd left\n", @@ -1611,7 +1964,10 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, dst_buf->vb2_buf.planes[0].data_offset = free; free = 0; } else { - len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free); + if (channel->codec == V4L2_PIX_FMT_H264) + len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free); + else + len = nal_hevc_write_filler(&dev->plat_dev->dev, curr, free); if (len < 0) { v4l2_err(&dev->v4l2_dev, "failed to write %zd filler data\n", free); @@ -2011,6 +2367,16 @@ static void allegro_destroy_channel(struct allegro_channel *channel) v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false); v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false); v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false); + + v4l2_ctrl_grab(channel->mpeg_video_hevc_profile, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_level, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_tier, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_i_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_max_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_min_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_p_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_hevc_b_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false); v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false); v4l2_ctrl_grab(channel->mpeg_video_bitrate, false); @@ -2067,6 +2433,16 @@ static int allegro_create_channel(struct allegro_channel *channel) v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, true); v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, true); v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, true); + + v4l2_ctrl_grab(channel->mpeg_video_hevc_profile, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_level, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_tier, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_i_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_max_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_min_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_p_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_hevc_b_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, true); v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true); v4l2_ctrl_grab(channel->mpeg_video_bitrate, true); @@ -2113,8 +2489,13 @@ static void allegro_channel_adjust(struct allegro_channel *channel) channel->sizeimage_encoded = estimate_stream_size(channel->width, channel->height); - ctrl = channel->mpeg_video_h264_level; - min = select_minimum_h264_level(channel->width, channel->height); + if (codec == V4L2_PIX_FMT_H264) { + ctrl = channel->mpeg_video_h264_level; + min = select_minimum_h264_level(channel->width, channel->height); + } else { + ctrl = channel->mpeg_video_hevc_level; + min = select_minimum_hevc_level(channel->width, channel->height); + } if (ctrl->minimum > min) v4l2_dbg(1, debug, &dev->v4l2_dev, "%s.minimum: %lld -> %lld\n", @@ -2125,7 +2506,10 @@ static void allegro_channel_adjust(struct allegro_channel *channel) v4l2_ctrl_unlock(ctrl); ctrl = channel->mpeg_video_bitrate; - max = h264_maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level)); + if (codec == V4L2_PIX_FMT_H264) + max = h264_maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level)); + else + max = hevc_maximum_bitrate(v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level)); if (ctrl->maximum < max) v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: maximum: %lld -> %lld\n", @@ -2156,21 +2540,51 @@ static void allegro_channel_adjust(struct allegro_channel *channel) v4l2_ctrl_activate(channel->mpeg_video_h264_b_frame_qp, codec == V4L2_PIX_FMT_H264); - channel->log2_max_frame_num = LOG2_MAX_FRAME_NUM; + v4l2_ctrl_activate(channel->mpeg_video_hevc_profile, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_level, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_tier, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_i_frame_qp, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_max_qp, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_min_qp, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_p_frame_qp, + codec == V4L2_PIX_FMT_HEVC); + v4l2_ctrl_activate(channel->mpeg_video_hevc_b_frame_qp, + codec == V4L2_PIX_FMT_HEVC); + + if (codec == V4L2_PIX_FMT_H264) + channel->log2_max_frame_num = LOG2_MAX_FRAME_NUM; channel->temporal_mvp_enable = true; - - channel->dbf_ovr_en = true; + channel->dbf_ovr_en = (codec == V4L2_PIX_FMT_H264); + channel->enable_deblocking_filter_override = (codec == V4L2_PIX_FMT_HEVC); + channel->enable_reordering = (codec == V4L2_PIX_FMT_HEVC); channel->enable_loop_filter_across_tiles = true; channel->enable_loop_filter_across_slices = true; - channel->b_hrz_me_range = 8; - channel->b_vrt_me_range = 8; - channel->p_hrz_me_range = 16; - channel->p_vrt_me_range = 16; - channel->max_cu_size = ilog2(16); - channel->min_cu_size = ilog2(8); - channel->max_tu_size = ilog2(4); - channel->min_tu_size = ilog2(4); + if (codec == V4L2_PIX_FMT_H264) { + channel->b_hrz_me_range = 8; + channel->b_vrt_me_range = 8; + channel->p_hrz_me_range = 16; + channel->p_vrt_me_range = 16; + channel->max_cu_size = ilog2(16); + channel->min_cu_size = ilog2(8); + channel->max_tu_size = ilog2(4); + channel->min_tu_size = ilog2(4); + } else { + channel->b_hrz_me_range = 16; + channel->b_vrt_me_range = 16; + channel->p_hrz_me_range = 32; + channel->p_vrt_me_range = 32; + channel->max_cu_size = ilog2(32); + channel->min_cu_size = ilog2(8); + channel->max_tu_size = ilog2(32); + channel->min_tu_size = ilog2(4); + } channel->max_transfo_depth_intra = 1; channel->max_transfo_depth_inter = 1; } @@ -2525,6 +2939,51 @@ static int allegro_open(struct file *file) &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 0, 51, 1, 30); + + channel->mpeg_video_hevc_profile = + v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, 0x0, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN); + channel->mpeg_video_hevc_level = + v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 0x0, + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1); + channel->mpeg_video_hevc_tier = + v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_TIER, + V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, 0x0, + V4L2_MPEG_VIDEO_HEVC_TIER_MAIN); + channel->mpeg_video_hevc_i_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_hevc_max_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, + 0, 51, 1, 51); + channel->mpeg_video_hevc_min_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + 0, 51, 1, 0); + channel->mpeg_video_hevc_p_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_hevc_b_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_frame_rc_enable = v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, @@ -2537,10 +2996,17 @@ static int allegro_open(struct file *file) V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); - bitrate_max = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); - bitrate_def = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); - cpb_size_max = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); - cpb_size_def = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + if (channel->codec == V4L2_PIX_FMT_H264) { + bitrate_max = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + bitrate_def = h264_maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + cpb_size_max = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + cpb_size_def = h264_maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + } else { + bitrate_max = hevc_maximum_bitrate(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1); + bitrate_def = hevc_maximum_bitrate(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1); + cpb_size_max = hevc_maximum_cpb_size(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1); + cpb_size_def = hevc_maximum_cpb_size(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1); + } channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE, @@ -2644,9 +3110,12 @@ static int allegro_enum_fmt_vid(struct file *file, void *fh, f->pixelformat = V4L2_PIX_FMT_NV12; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (f->index >= 1) + if (f->index >= 2) return -EINVAL; - f->pixelformat = V4L2_PIX_FMT_H264; + if (f->index == 0) + f->pixelformat = V4L2_PIX_FMT_H264; + if (f->index == 1) + f->pixelformat = V4L2_PIX_FMT_HEVC; break; default: return -EINVAL; @@ -2685,7 +3154,10 @@ static int allegro_try_fmt_vid_cap(struct file *file, void *fh, f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height, ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_HEVC && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_H264) + f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; + f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = estimate_stream_size(f->fmt.pix.width, f->fmt.pix.height); @@ -2832,6 +3304,7 @@ static int allegro_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { switch (fsize->pixel_format) { + case V4L2_PIX_FMT_HEVC: case V4L2_PIX_FMT_H264: case V4L2_PIX_FMT_NV12: break; diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.c b/drivers/media/platform/allegro-dvt/allegro-mail.c index 5dbc1c029020..7e08c5050f2e 100644 --- a/drivers/media/platform/allegro-dvt/allegro-mail.c +++ b/drivers/media/platform/allegro-dvt/allegro-mail.c @@ -67,12 +67,16 @@ static inline u32 settings_get_mcu_codec(struct create_channel_param *param) if (version < MCU_MSG_VERSION_2019_2) { switch (pixelformat) { + case V4L2_PIX_FMT_HEVC: + return 2; case V4L2_PIX_FMT_H264: default: return 1; } } else { switch (pixelformat) { + case V4L2_PIX_FMT_HEVC: + return 1; case V4L2_PIX_FMT_H264: default: return 0; @@ -117,7 +121,9 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) dst[i++] = val; val = 0; + val |= param->enable_reordering ? BIT(0) : 0; val |= param->dbf_ovr_en ? BIT(2) : 0; + val |= param->override_lf ? BIT(12) : 0; dst[i++] = val; if (version >= MCU_MSG_VERSION_2019_2) { diff --git a/drivers/media/platform/allegro-dvt/allegro-mail.h b/drivers/media/platform/allegro-dvt/allegro-mail.h index f7485cf78c4f..2c7bc509eac3 100644 --- a/drivers/media/platform/allegro-dvt/allegro-mail.h +++ b/drivers/media/platform/allegro-dvt/allegro-mail.h @@ -65,6 +65,7 @@ struct create_channel_param { u32 temporal_mvp_enable; u32 enable_reordering; u32 dbf_ovr_en; + u32 override_lf; u32 num_ref_idx_l0; u32 num_ref_idx_l1; u32 custom_lda; -- cgit v1.2.3 From c5b14df7a80acadbbd184808dc3f519fbb2ab96c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 15 Jan 2021 11:11:26 +0100 Subject: media: venus: Fix uninitialized variable count being checked for zero In the case where plat->codecs is NULL the variable count is uninitialized but is being checked to see if it is 0. Fix this by initializing count to 0. Addresses-Coverity: ("Uninitialized scalar variable") Fixes: e29929266be1 ("media: venus: Get codecs and capabilities from hfi platform") Signed-off-by: Colin Ian King Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 2c63988cb321..7263c0c32695 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -232,7 +232,7 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) { const struct hfi_platform *plat; const struct hfi_plat_caps *caps = NULL; - u32 enc_codecs, dec_codecs, count; + u32 enc_codecs, dec_codecs, count = 0; unsigned int entries; if (inst) -- cgit v1.2.3 From 0f9774dddf6ce17b134582147d54221548ff06fc Mon Sep 17 00:00:00 2001 From: Zou Wei Date: Thu, 14 Jan 2021 10:05:21 +0100 Subject: media: venus: Mark bufreq_enc with static keyword Fix the following sparse warning: hfi_plat_bufs_v6.c:1242:5: warning: symbol 'bufreq_enc' was not declared. Should it be static? Signed-off-by: Zou Wei Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c index 072e349dd46c..d43d1a53e72d 100644 --- a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c @@ -1239,8 +1239,8 @@ static int bufreq_dec(struct hfi_plat_buffers_params *params, u32 buftype, return 0; } -int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype, - struct hfi_buffer_requirements *bufreq) +static int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype, + struct hfi_buffer_requirements *bufreq) { enum hfi_version version = params->version; struct enc_bufsize_ops *enc_ops; -- cgit v1.2.3 From 0ca0ca9805055bb0efc16890f9d6433c65bd07cc Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Tue, 15 Dec 2020 19:08:36 +0100 Subject: media: venus: core: add support to dump FW region Add support to dump video FW region during FW crash using devcoredump helpers. Signed-off-by: Dikshita Agarwal Reviewed-by: Stephen Boyd Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.c | 30 ++++++++++++++++++++++++++++ drivers/media/platform/qcom/venus/core.h | 2 ++ drivers/media/platform/qcom/venus/firmware.c | 3 +++ 3 files changed, 35 insertions(+) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 6bcd5659ffcc..1471c7f9c89d 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,33 @@ #include "firmware.h" #include "pm_helpers.h" +static void venus_coredump(struct venus_core *core) +{ + struct device *dev; + phys_addr_t mem_phys; + size_t mem_size; + void *mem_va; + void *data; + + dev = core->dev; + mem_phys = core->fw.mem_phys; + mem_size = core->fw.mem_size; + + mem_va = memremap(mem_phys, mem_size, MEMREMAP_WC); + if (!mem_va) + return; + + data = vmalloc(mem_size); + if (!data) { + memunmap(mem_va); + return; + } + + memcpy(data, mem_va, mem_size); + memunmap(mem_va); + dev_coredumpv(dev, data, mem_size, GFP_KERNEL); +} + static void venus_event_notify(struct venus_core *core, u32 event) { struct venus_inst *inst; @@ -67,6 +95,8 @@ static void venus_sys_error_handler(struct work_struct *work) venus_shutdown(core); + venus_coredump(core); + pm_runtime_put_sync(core->dev); while (core->pmdomains[0] && pm_runtime_active(core->pmdomains[0])) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index b984d508ed71..45da0307a1b8 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -144,6 +144,8 @@ struct venus_core { struct device *dev; struct iommu_domain *iommu_domain; size_t mapped_mem_size; + phys_addr_t mem_phys; + size_t mem_size; } fw; struct mutex lock; struct list_head instances; diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index d03e2dd5808c..89defc21ea81 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -201,6 +201,9 @@ int venus_boot(struct venus_core *core) return -EINVAL; } + core->fw.mem_size = mem_size; + core->fw.mem_phys = mem_phys; + if (core->use_tz) ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID); else -- cgit v1.2.3 From 74c357fc2c4bfe877eb351a06fc6778fc5c25e81 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Wed, 16 Sep 2020 13:09:47 +0200 Subject: media: platform: Remove depends on interconnect The dependency on interconnect in the Kconfig was introduced to avoid the case of interconnect=m and driver=y, but the interconnect framework has been converted from tristate to bool now. Remove the dependency as the framework can't be a module anymore. Signed-off-by: Georgi Djakov Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index f0c6295a6d50..fd1831e97b22 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -545,7 +545,6 @@ config VIDEO_QCOM_VENUS tristate "Qualcomm Venus V4L2 encoder/decoder driver" depends on VIDEO_DEV && VIDEO_V4L2 depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST - depends on INTERCONNECT || !INTERCONNECT select QCOM_MDT_LOADER if ARCH_QCOM select QCOM_SCM if ARCH_QCOM select VIDEOBUF2_DMA_CONTIG -- cgit v1.2.3 From a76f43a490542ecb8c57176730b6eb665d716139 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Sat, 9 Jan 2021 08:21:30 +0100 Subject: media: venus: pm_helpers: Control core power domain manually Presently we use device_link to control core power domain. But this leads to issues because the genpd doesn't guarantee synchronous on/off for supplier devices. Switch to manually control by pmruntime calls. Tested-by: Fritz Koenig Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 2 -- drivers/media/platform/qcom/venus/pm_helpers.c | 36 ++++++++++++++------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 45da0307a1b8..a252ed32cc14 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -91,7 +91,6 @@ struct venus_format { * @clks: an array of struct clk pointers * @vcodec0_clks: an array of vcodec0 struct clk pointers * @vcodec1_clks: an array of vcodec1 struct clk pointers - * @pd_dl_venus: pmdomain device-link for venus domain * @pmdomains: an array of pmdomains struct device pointers * @vdev_dec: a reference to video device structure for decoder instances * @vdev_enc: a reference to video device structure for encoder instances @@ -128,7 +127,6 @@ struct venus_core { struct icc_path *cpucfg_path; struct opp_table *opp_table; bool has_opp_table; - struct device_link *pd_dl_venus; struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX]; struct device_link *opp_dl_venus; struct device *opp_pmdomain; diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 0011c3aa3a73..43c4e3d9e281 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -774,13 +774,6 @@ static int vcodec_domains_get(struct device *dev) core->pmdomains[i] = pd; } - core->pd_dl_venus = device_link_add(dev, core->pmdomains[0], - DL_FLAG_PM_RUNTIME | - DL_FLAG_STATELESS | - DL_FLAG_RPM_ACTIVE); - if (!core->pd_dl_venus) - return -ENODEV; - skip_pmdomains: if (!core->has_opp_table) return 0; @@ -807,14 +800,12 @@ skip_pmdomains: opp_dl_add_err: dev_pm_opp_detach_genpd(core->opp_table); opp_attach_err: - if (core->pd_dl_venus) { - device_link_del(core->pd_dl_venus); - for (i = 0; i < res->vcodec_pmdomains_num; i++) { - if (IS_ERR_OR_NULL(core->pmdomains[i])) - continue; - dev_pm_domain_detach(core->pmdomains[i], true); - } + for (i = 0; i < res->vcodec_pmdomains_num; i++) { + if (IS_ERR_OR_NULL(core->pmdomains[i])) + continue; + dev_pm_domain_detach(core->pmdomains[i], true); } + return ret; } @@ -827,9 +818,6 @@ static void vcodec_domains_put(struct device *dev) if (!res->vcodec_pmdomains_num) goto skip_pmdomains; - if (core->pd_dl_venus) - device_link_del(core->pd_dl_venus); - for (i = 0; i < res->vcodec_pmdomains_num; i++) { if (IS_ERR_OR_NULL(core->pmdomains[i])) continue; @@ -916,16 +904,30 @@ static void core_put_v4(struct device *dev) static int core_power_v4(struct device *dev, int on) { struct venus_core *core = dev_get_drvdata(dev); + struct device *pmctrl = core->pmdomains[0]; int ret = 0; if (on == POWER_ON) { + if (pmctrl) { + ret = pm_runtime_get_sync(pmctrl); + if (ret < 0) { + pm_runtime_put_noidle(pmctrl); + return ret; + } + } + ret = core_clks_enable(core); + if (ret < 0 && pmctrl) + pm_runtime_put_sync(pmctrl); } else { /* Drop the performance state vote */ if (core->opp_pmdomain) dev_pm_opp_set_rate(dev, 0); core_clks_disable(core); + + if (pmctrl) + pm_runtime_put_sync(pmctrl); } return ret; -- cgit v1.2.3 From 01e869e787568a16bd8c77afc7171ef81e559dfa Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Thu, 5 Nov 2020 06:24:56 +0100 Subject: media: venus: venc: fix handlig of S_SELECTION and G_SELECTION - return correct width and height for G_SELECTION - update capture port wxh with rectangle wxh. - add support for HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO to set stride info and chroma offset to FW. Signed-off-by: Dikshita Agarwal Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 18 +++++++++++++ drivers/media/platform/qcom/venus/helpers.h | 2 ++ drivers/media/platform/qcom/venus/hfi_cmds.c | 12 +++++++++ drivers/media/platform/qcom/venus/hfi_helper.h | 4 +-- drivers/media/platform/qcom/venus/venc.c | 36 ++++++++++++++++++-------- 5 files changed, 59 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index a20b45f35f3d..76ece2ff8d39 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -1662,3 +1662,21 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, return -EINVAL; } EXPORT_SYMBOL_GPL(venus_helper_get_out_fmts); + +int venus_helper_set_stride(struct venus_inst *inst, + unsigned int width, unsigned int height) +{ + const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO; + + struct hfi_uncompressed_plane_actual_info plane_actual_info; + + plane_actual_info.buffer_type = HFI_BUFFER_INPUT; + plane_actual_info.num_planes = 2; + plane_actual_info.plane_format[0].actual_stride = width; + plane_actual_info.plane_format[0].actual_plane_buffer_height = height; + plane_actual_info.plane_format[1].actual_stride = width; + plane_actual_info.plane_format[1].actual_plane_buffer_height = height / 2; + + return hfi_session_set_property(inst, ptype, &plane_actual_info); +} +EXPORT_SYMBOL_GPL(venus_helper_set_stride); diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 5407979bf234..351093845499 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -63,4 +63,6 @@ void venus_helper_get_ts_metadata(struct venus_inst *inst, u64 timestamp_us, struct vb2_v4l2_buffer *vbuf); int venus_helper_get_profile_level(struct venus_inst *inst, u32 *profile, u32 *level); int venus_helper_set_profile_level(struct venus_inst *inst, u32 profile, u32 level); +int venus_helper_set_stride(struct venus_inst *inst, unsigned int aligned_width, + unsigned int aligned_height); #endif diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 7022368c1e63..4f7565834469 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1205,6 +1205,18 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cu); break; } + case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: { + struct hfi_uncompressed_plane_actual_info *in = pdata; + struct hfi_uncompressed_plane_actual_info *info = prop_data; + + info->buffer_type = in->buffer_type; + info->num_planes = in->num_planes; + info->plane_format[0] = in->plane_format[0]; + if (in->num_planes > 1) + info->plane_format[1] = in->plane_format[1]; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*info); + break; + } case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 1f1c3faa4631..6b524c7cde5f 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -926,13 +926,13 @@ struct hfi_uncompressed_plane_actual { struct hfi_uncompressed_plane_actual_info { u32 buffer_type; u32 num_planes; - struct hfi_uncompressed_plane_actual plane_format[1]; + struct hfi_uncompressed_plane_actual plane_format[2]; }; struct hfi_uncompressed_plane_actual_constraints_info { u32 buffer_type; u32 num_planes; - struct hfi_uncompressed_plane_constraints plane_format[1]; + struct hfi_uncompressed_plane_constraints plane_format[2]; }; struct hfi_codec_supported { diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index e4775ec97a87..d01add6eb449 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -190,8 +190,10 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) pixmp->height = clamp(pixmp->height, frame_height_min(inst), frame_height_max(inst)); - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pixmp->width = ALIGN(pixmp->width, 128); pixmp->height = ALIGN(pixmp->height, 32); + } pixmp->width = ALIGN(pixmp->width, 2); pixmp->height = ALIGN(pixmp->height, 2); @@ -335,13 +337,13 @@ venc_g_selection(struct file *file, void *fh, struct v4l2_selection *s) switch (s->target) { case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - s->r.width = inst->width; - s->r.height = inst->height; - break; - case V4L2_SEL_TGT_CROP: s->r.width = inst->out_width; s->r.height = inst->out_height; break; + case V4L2_SEL_TGT_CROP: + s->r.width = inst->width; + s->r.height = inst->height; + break; default: return -EINVAL; } @@ -360,12 +362,19 @@ venc_s_selection(struct file *file, void *fh, struct v4l2_selection *s) if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; + if (s->r.width > inst->out_width || + s->r.height > inst->out_height) + return -EINVAL; + + s->r.width = ALIGN(s->r.width, 2); + s->r.height = ALIGN(s->r.height, 2); + switch (s->target) { case V4L2_SEL_TGT_CROP: - if (s->r.width != inst->out_width || - s->r.height != inst->out_height || - s->r.top != 0 || s->r.left != 0) - return -EINVAL; + s->r.top = 0; + s->r.left = 0; + inst->width = s->r.width; + inst->height = s->r.height; break; default: return -EINVAL; @@ -741,6 +750,11 @@ static int venc_init_session(struct venus_inst *inst) else if (ret) return ret; + ret = venus_helper_set_stride(inst, inst->out_width, + inst->out_height); + if (ret) + goto deinit; + ret = venus_helper_set_input_resolution(inst, inst->width, inst->height); if (ret) @@ -828,8 +842,8 @@ static int venc_queue_setup(struct vb2_queue *q, inst->num_input_bufs = *num_buffers; sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt, - inst->width, - inst->height); + inst->out_width, + inst->out_height); inst->input_buf_size = sizes[0]; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: -- cgit v1.2.3 From 002c22bd360e07b6ae01e3b3d928e9c568ec3d6b Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Fri, 8 Jan 2021 08:23:37 +0100 Subject: media: venus: venc: set inband mode property to FW. Set HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER to FW to support inband sequence header mode. Reviewed-by: Fritz Koenig Signed-off-by: Dikshita Agarwal Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/venc.c | 14 ++++++++++++++ drivers/media/platform/qcom/venus/venc_ctrls.c | 17 ++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index d01add6eb449..c04474afd812 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -545,6 +545,7 @@ static int venc_set_properties(struct venus_inst *inst) struct hfi_idr_period idrp; struct hfi_quantization quant; struct hfi_quantization_range quant_range; + struct hfi_enable en; u32 ptype, rate_control, bitrate; u32 profile, level; int ret; @@ -664,6 +665,19 @@ static int venc_set_properties(struct venus_inst *inst) if (ret) return ret; + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 || + inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + ptype = HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER; + if (ctr->header_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) + en.enable = 0; + else + en.enable = 1; + + ret = hfi_session_set_property(inst, ptype, &en); + if (ret) + return ret; + } + if (!ctr->bitrate_peak) bitrate *= 2; else diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 9fbe8388a938..a52b80055173 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -209,6 +209,20 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_MPEG_VIDEO_HEADER_MODE: ctr->header_mode = ctrl->val; + mutex_lock(&inst->lock); + if (inst->streamon_out && inst->streamon_cap) { + if (ctrl->val == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) + en.enable = 0; + else + en.enable = 1; + ptype = HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER; + ret = hfi_session_set_property(inst, ptype, &en); + if (ret) { + mutex_unlock(&inst->lock); + return ret; + } + } + mutex_unlock(&inst->lock); break; case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: break; @@ -343,7 +357,8 @@ int venc_ctrl_init(struct venus_inst *inst) v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_HEADER_MODE, V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, - 1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + ~((1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) | + (1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME)), V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE); v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, -- cgit v1.2.3 From f0ddb4e9911665b9ad68fe94e0faaaff5953902e Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Wed, 16 Dec 2020 08:06:50 +0100 Subject: media: venus: venc: set IDR period to FW only for H264 & HEVC HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD is supported for only H264 & HEVC codec. There is no need to set it for VP8 since all key frames are treated as IDR frames for VP8. Signed-off-by: Dikshita Agarwal Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/venc.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index c04474afd812..6976ed553647 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -598,16 +598,19 @@ static int venc_set_properties(struct venus_inst *inst) return ret; } - /* IDR periodicity, n: - * n = 0 - only the first I-frame is IDR frame - * n = 1 - all I-frames will be IDR frames - * n > 1 - every n-th I-frame will be IDR frame - */ - ptype = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD; - idrp.idr_period = 0; - ret = hfi_session_set_property(inst, ptype, &idrp); - if (ret) - return ret; + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 || + inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + /* IDR periodicity, n: + * n = 0 - only the first I-frame is IDR frame + * n = 1 - all I-frames will be IDR frames + * n > 1 - every n-th I-frame will be IDR frame + */ + ptype = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD; + idrp.idr_period = 0; + ret = hfi_session_set_property(inst, ptype, &idrp); + if (ret) + return ret; + } if (ctr->num_b_frames) { u32 max_num_b_frames = NUM_B_FRAMES_MAX; -- cgit v1.2.3 From b53f2fa136b1b20f03184f4a98fea75eb2705eac Mon Sep 17 00:00:00 2001 From: Brad Love Date: Tue, 26 Jan 2021 02:54:13 +0100 Subject: media: mxl692: MaxLinear 692 ATSC demod/tuner driver MaxLinear 692 is a combo demod/tuner which has ATSC and QAM capabilities. Only ATSC is currently advertised via DVB properties. QAM still has issues. Confirmed working on both big and little endian. Big endian verified on PowerPC Mac mini. Signed-off-by: Brad Love Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/Kconfig | 9 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/mxl692.c | 1378 +++++++++++++++++++++++++++++ drivers/media/dvb-frontends/mxl692.h | 38 + drivers/media/dvb-frontends/mxl692_defs.h | 548 ++++++++++++ 5 files changed, 1974 insertions(+) create mode 100644 drivers/media/dvb-frontends/mxl692.c create mode 100644 drivers/media/dvb-frontends/mxl692.h create mode 100644 drivers/media/dvb-frontends/mxl692_defs.h diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 6a1ef277fea4..3468b07b62fe 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -708,6 +708,15 @@ config DVB_S5H1411 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. +config DVB_MXL692 + tristate "MaxLinear MXL692 based" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + MaxLinear MxL692 is a combo tuner-demodulator that + supports ATSC 8VSB and QAM modes. Say Y when you want to + support this frontend. + comment "ISDB-T (terrestrial) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index e9179162658c..b9f47d68e14e 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_DVB_S5H1420) += s5h1420.o obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o +obj-$(CONFIG_DVB_MXL692) += mxl692.o obj-$(CONFIG_DVB_LG2160) += lg2160.o obj-$(CONFIG_DVB_CX24123) += cx24123.o obj-$(CONFIG_DVB_LNBH25) += lnbh25.o diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c new file mode 100644 index 000000000000..86af831c0176 --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692.c @@ -0,0 +1,1378 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the MaxLinear MxL69x family of combo tuners/demods + * + * Copyright (C) 2020 Brad Love + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "mxl692.h" +#include "mxl692_defs.h" + +static const struct dvb_frontend_ops mxl692_ops; + +struct mxl692_dev { + struct dvb_frontend fe; + struct i2c_client *i2c_client; + struct mutex i2c_lock; /* i2c command mutex */ + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; + enum MXL_EAGLE_POWER_MODE_E power_mode; + u32 current_frequency; + int device_type; + int seqnum; + int init_done; +}; + +static int mxl692_i2c_write(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) +{ + int ret = 0; + struct i2c_msg msg = { + .addr = dev->i2c_client->addr, + .flags = 0, + .buf = buffer, + .len = buf_len + }; + + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); + if (ret != 1) + dev_dbg(&dev->i2c_client->dev, "i2c write error!\n"); + + return ret; +} + +static int mxl692_i2c_read(struct mxl692_dev *dev, u8 *buffer, u16 buf_len) +{ + int ret = 0; + struct i2c_msg msg = { + .addr = dev->i2c_client->addr, + .flags = I2C_M_RD, + .buf = buffer, + .len = buf_len + }; + + ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1); + if (ret != 1) + dev_dbg(&dev->i2c_client->dev, "i2c read error!\n"); + + return ret; +} + +static int convert_endian(u32 size, u8 *d) +{ + u32 i; + + for (i = 0; i < (size & ~3); i += 4) { + d[i + 0] ^= d[i + 3]; + d[i + 3] ^= d[i + 0]; + d[i + 0] ^= d[i + 3]; + + d[i + 1] ^= d[i + 2]; + d[i + 2] ^= d[i + 1]; + d[i + 1] ^= d[i + 2]; + } + + switch (size & 3) { + case 0: + case 1: + /* do nothing */ + break; + case 2: + d[i + 0] ^= d[i + 1]; + d[i + 1] ^= d[i + 0]; + d[i + 0] ^= d[i + 1]; + break; + + case 3: + d[i + 0] ^= d[i + 2]; + d[i + 2] ^= d[i + 0]; + d[i + 0] ^= d[i + 2]; + break; + } + return size; +} + +static int convert_endian_n(int n, u32 size, u8 *d) +{ + int i, count = 0; + + for (i = 0; i < n; i += size) + count += convert_endian(size, d + i); + return count; +} + +static void mxl692_tx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) +{ +#ifdef __BIG_ENDIAN + return; +#endif + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ + + switch (opcode) { + case MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET: + case MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET: + case MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET: + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_PARAMS_SET: + buffer += 5; + buffer += convert_endian(2 * sizeof(u32), buffer); + break; + default: + /* no swapping - all get opcodes */ + /* ATSC/OOB no swapping */ + break; + } +} + +static void mxl692_rx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer) +{ +#ifdef __BIG_ENDIAN + return; +#endif + buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */ + + switch (opcode) { + case MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET: + buffer++; + buffer += convert_endian(2 * sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_STATUS_GET: + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET: + buffer += convert_endian(3 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET: + buffer += convert_endian_n(24, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_STATUS_GET: + buffer += 8; + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET: + buffer += convert_endian(7 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET: + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET: + buffer += convert_endian_n(24, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET: + buffer += convert_endian_n(8, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET: + buffer += convert_endian_n(17, sizeof(u16), buffer); + break; + case MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET: + buffer += convert_endian(3 * sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_OOB_STATUS_GET: + buffer += convert_endian_n(2, sizeof(u16), buffer); + buffer += convert_endian(sizeof(u32), buffer); + break; + case MXL_EAGLE_OPCODE_SMA_RECEIVE_GET: + buffer += convert_endian(sizeof(u32), buffer); + break; + default: + /* no swapping - all set opcodes */ + break; + } +} + +static u32 mxl692_checksum(u8 *buffer, u32 size) +{ + u32 ix, div_size; + u32 cur_cksum = 0; + __be32 *buf; + + div_size = DIV_ROUND_UP(size, 4); + + buf = (__be32 *)buffer; + for (ix = 0; ix < div_size; ix++) + cur_cksum += be32_to_cpu(buf[ix]); + + cur_cksum ^= 0xDEADBEEF; + + return cur_cksum; +} + +static int mxl692_validate_fw_header(struct mxl692_dev *dev, + const u8 *buffer, u32 buf_len) +{ + int status = 0; + u32 ix, temp; + __be32 *local_buf = NULL; + u8 temp_cksum = 0; + const u8 fw_hdr[] = { 0x4D, 0x31, 0x10, 0x02, 0x40, 0x00, 0x00, 0x80 }; + + if (memcmp(buffer, fw_hdr, 8) != 0) { + status = -EINVAL; + goto err_finish; + } + + local_buf = (__be32 *)(buffer + 8); + temp = be32_to_cpu(*local_buf); + + if ((buf_len - 16) != temp >> 8) { + status = -EINVAL; + goto err_finish; + } + + for (ix = 16; ix < buf_len; ix++) + temp_cksum += buffer[ix]; + + if (temp_cksum != buffer[11]) + status = -EINVAL; + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "failed\n"); + return status; +} + +static int mxl692_write_fw_block(struct mxl692_dev *dev, const u8 *buffer, + u32 buf_len, u32 *index) +{ + int status = 0; + u32 ix = 0, total_len = 0, addr = 0, chunk_len = 0, prevchunk_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + int payload_max = MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE; + + ix = *index; + + if (buffer[ix] == 0x53) { + total_len = buffer[ix + 1] << 16 | buffer[ix + 2] << 8 | buffer[ix + 3]; + total_len = (total_len + 3) & ~3; + addr = buffer[ix + 4] << 24 | buffer[ix + 5] << 16 | + buffer[ix + 6] << 8 | buffer[ix + 7]; + ix += MXL_EAGLE_FW_SEGMENT_HEADER_SIZE; + + while ((total_len > 0) && (status == 0)) { + plocal_buf = local_buf; + chunk_len = (total_len < payload_max) ? total_len : payload_max; + + *plocal_buf++ = 0xFC; + *plocal_buf++ = chunk_len + sizeof(u32); + + *(u32 *)plocal_buf = addr + prevchunk_len; +#ifdef __BIG_ENDIAN + convert_endian(sizeof(u32), plocal_buf); +#endif + plocal_buf += sizeof(u32); + + memcpy(plocal_buf, &buffer[ix], chunk_len); + convert_endian(chunk_len, plocal_buf); + if (mxl692_i2c_write(dev, local_buf, + (chunk_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + break; + } + + prevchunk_len += chunk_len; + total_len -= chunk_len; + ix += chunk_len; + } + *index = ix; + } else { + status = -EINVAL; + } + + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + + return status; +} + +static int mxl692_memwrite(struct mxl692_dev *dev, u32 addr, + u8 *buffer, u32 size) +{ + int status = 0, total_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + + total_len = size; + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ + + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE)) + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFC; + *plocal_buf++ = total_len + sizeof(u32); + + *(u32 *)plocal_buf = addr; + plocal_buf += sizeof(u32); + + memcpy(plocal_buf, buffer, total_len); +#ifdef __BIG_ENDIAN + convert_endian(sizeof(u32) + total_len, local_buf + 2); +#endif + if (mxl692_i2c_write(dev, local_buf, + (total_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + goto err_finish; + } + + return status; +err_finish: + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_memread(struct mxl692_dev *dev, u32 addr, + u8 *buffer, u32 size) +{ + int status = 0; + u8 local_buf[MXL_EAGLE_I2C_MHEADER_SIZE] = {}, *plocal_buf = NULL; + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFB; + *plocal_buf++ = sizeof(u32); + *(u32 *)plocal_buf = addr; +#ifdef __BIG_ENDIAN + convert_endian(sizeof(u32), plocal_buf); +#endif + mutex_lock(&dev->i2c_lock); + + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_MHEADER_SIZE) > 0) { + size = (size + 3) & ~3; /* 4 byte alignment */ + status = mxl692_i2c_read(dev, buffer, (u16)size) < 0 ? -EREMOTEIO : 0; +#ifdef __BIG_ENDIAN + if (status == 0) + convert_endian(size, buffer); +#endif + } else { + status = -EREMOTEIO; + } + + mutex_unlock(&dev->i2c_lock); + + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + + return status; +} + +static const char *mxl692_opcode_string(u8 opcode) +{ + if (opcode >= 0 && opcode <= MXL_EAGLE_OPCODE_INTERNAL) + return MXL_EAGLE_OPCODE_STRING[opcode]; + + return "invalid opcode"; +} + +static int mxl692_opwrite(struct mxl692_dev *dev, u8 *buffer, + u32 size) +{ + int status = 0, total_len = 0; + u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL; + struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_hdr = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)buffer; + + total_len = size; + total_len = (total_len + 3) & ~3; /* 4 byte alignment */ + + if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) + dev_dbg(&dev->i2c_client->dev, "hrmph?\n"); + + plocal_buf = local_buf; + + *plocal_buf++ = 0xFE; + *plocal_buf++ = (u8)total_len; + + memcpy(plocal_buf, buffer, total_len); + convert_endian(total_len, plocal_buf); + + if (mxl692_i2c_write(dev, local_buf, + (total_len + MXL_EAGLE_I2C_PHEADER_SIZE)) < 0) { + status = -EREMOTEIO; + goto err_finish; + } +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "opcode %s err %d\n", + mxl692_opcode_string(tx_hdr->opcode), status); + return status; +} + +static int mxl692_opread(struct mxl692_dev *dev, u8 *buffer, + u32 size) +{ + int status = 0; + u32 ix = 0; + u8 local_buf[MXL_EAGLE_I2C_PHEADER_SIZE] = {}; + + local_buf[0] = 0xFD; + local_buf[1] = 0; + + if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_PHEADER_SIZE) > 0) { + size = (size + 3) & ~3; /* 4 byte alignment */ + + /* Read in 4 byte chunks */ + for (ix = 0; ix < size; ix += 4) { + if (mxl692_i2c_read(dev, buffer + ix, 4) < 0) { + dev_dbg(&dev->i2c_client->dev, "ix=%d size=%d\n", ix, size); + status = -EREMOTEIO; + goto err_finish; + } + } + convert_endian(size, buffer); + } else { + status = -EREMOTEIO; + } +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_i2c_writeread(struct mxl692_dev *dev, + u8 opcode, + u8 *tx_payload, + u8 tx_payload_size, + u8 *rx_payload, + u8 rx_payload_expected) +{ + int status = 0, timeout = 40; + u8 tx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + u32 resp_checksum = 0, resp_checksum_tmp = 0; + struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_header; + struct MXL_EAGLE_HOST_MSG_HEADER_T *rx_header; + + mutex_lock(&dev->i2c_lock); + + if ((tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE) > + (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) { + status = -EINVAL; + goto err_finish; + } + + tx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)tx_buf; + tx_header->opcode = opcode; + tx_header->seqnum = dev->seqnum++; + tx_header->payload_size = tx_payload_size; + tx_header->checksum = 0; + + if (dev->seqnum == 0) + dev->seqnum = 1; + + if (tx_payload && tx_payload_size > 0) + memcpy(&tx_buf[MXL_EAGLE_HOST_MSG_HEADER_SIZE], tx_payload, tx_payload_size); + + mxl692_tx_swap(opcode, tx_buf); + + tx_header->checksum = 0; + tx_header->checksum = mxl692_checksum(tx_buf, + MXL_EAGLE_HOST_MSG_HEADER_SIZE + tx_payload_size); +#ifdef __LITTLE_ENDIAN + convert_endian(4, (u8 *)&tx_header->checksum); /* cksum is big endian */ +#endif + /* send Tx message */ + status = mxl692_opwrite(dev, tx_buf, + tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE); + if (status) { + status = -EREMOTEIO; + goto err_finish; + } + + /* receive Rx message (polling) */ + rx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)rx_buf; + + do { + status = mxl692_opread(dev, rx_buf, + rx_payload_expected + MXL_EAGLE_HOST_MSG_HEADER_SIZE); + usleep_range(1000, 2000); + timeout--; + } while ((timeout > 0) && (status == 0) && + (rx_header->seqnum == 0) && + (rx_header->checksum == 0)); + + if (timeout == 0 || status) { + dev_dbg(&dev->i2c_client->dev, "timeout=%d status=%d\n", + timeout, status); + status = -ETIMEDOUT; + goto err_finish; + } + + if (rx_header->status) { + dev_dbg(&dev->i2c_client->dev, "rx header status code: %d\n", rx_header->status); + status = -EREMOTEIO; + goto err_finish; + } + + if (rx_header->seqnum != tx_header->seqnum || + rx_header->opcode != tx_header->opcode || + rx_header->payload_size != rx_payload_expected) { + dev_dbg(&dev->i2c_client->dev, "Something failed seq=%s opcode=%s pSize=%s\n", + rx_header->seqnum != tx_header->seqnum ? "X" : "0", + rx_header->opcode != tx_header->opcode ? "X" : "0", + rx_header->payload_size != rx_payload_expected ? "X" : "0"); + if (rx_header->payload_size != rx_payload_expected) + dev_dbg(&dev->i2c_client->dev, + "rx_header->payloadSize=%d rx_payload_expected=%d\n", + rx_header->payload_size, rx_payload_expected); + status = -EREMOTEIO; + goto err_finish; + } + + resp_checksum = rx_header->checksum; + rx_header->checksum = 0; + + resp_checksum_tmp = mxl692_checksum(rx_buf, + MXL_EAGLE_HOST_MSG_HEADER_SIZE + rx_header->payload_size); +#ifdef __LITTLE_ENDIAN + convert_endian(4, (u8 *)&resp_checksum_tmp); /* cksum is big endian */ +#endif + if (resp_checksum != resp_checksum_tmp) { + dev_dbg(&dev->i2c_client->dev, "rx checksum failure\n"); + status = -EREMOTEIO; + goto err_finish; + } + + mxl692_rx_swap(rx_header->opcode, rx_buf); + + if (rx_header->payload_size > 0) { + if (!rx_payload) { + dev_dbg(&dev->i2c_client->dev, "no rx payload?!?\n"); + status = -EREMOTEIO; + goto err_finish; + } + memcpy(rx_payload, rx_buf + MXL_EAGLE_HOST_MSG_HEADER_SIZE, + rx_header->payload_size); + } +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + + mutex_unlock(&dev->i2c_lock); + return status; +} + +static int mxl692_fwdownload(struct mxl692_dev *dev, + const u8 *firmware_buf, u32 buf_len) +{ + int status = 0; + u32 ix, reg_val = 0x1; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_DEV_STATUS_T *dev_status; + + if (buf_len < MXL_EAGLE_FW_HEADER_SIZE || + buf_len > MXL_EAGLE_FW_MAX_SIZE_IN_KB * 1000) + return -EINVAL; + + mutex_lock(&dev->i2c_lock); + + dev_dbg(&dev->i2c_client->dev, "\n"); + + status = mxl692_validate_fw_header(dev, firmware_buf, buf_len); + if (status) + goto err_finish; + + ix = 16; + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* DRAM */ + if (status) + goto err_finish; + + status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* IRAM */ + if (status) + goto err_finish; + + /* release CPU from reset */ + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + mutex_unlock(&dev->i2c_lock); + + if (status == 0) { + /* verify FW is alive */ + usleep_range(MXL_EAGLE_FW_LOAD_TIME * 1000, (MXL_EAGLE_FW_LOAD_TIME + 5) * 1000); + dev_status = (struct MXL_EAGLE_DEV_STATUS_T *)&rx_buf; + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, + NULL, + 0, + (u8 *)dev_status, + sizeof(struct MXL_EAGLE_DEV_STATUS_T)); + } + + return status; +err_finish: + mutex_unlock(&dev->i2c_lock); + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_get_versions(struct mxl692_dev *dev) +{ + int status = 0; + struct MXL_EAGLE_DEV_VER_T dev_ver = {}; + static const char * const chip_id[] = {"N/A", "691", "248", "692"}; + + status = mxl692_i2c_writeread(dev, MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, + NULL, + 0, + (u8 *)&dev_ver, + sizeof(struct MXL_EAGLE_DEV_VER_T)); + if (status) + return status; + + dev_info(&dev->i2c_client->dev, "MxL692_DEMOD Chip ID: %s\n", + chip_id[dev_ver.chip_id]); + + dev_info(&dev->i2c_client->dev, + "MxL692_DEMOD FW Version: %d.%d.%d.%d_RC%d\n", + dev_ver.firmware_ver[0], + dev_ver.firmware_ver[1], + dev_ver.firmware_ver[2], + dev_ver.firmware_ver[3], + dev_ver.firmware_ver[4]); + + return status; +} + +static int mxl692_reset(struct mxl692_dev *dev) +{ + int status = 0; + u32 dev_type = MXL_EAGLE_DEVICE_MAX, reg_val = 0x2; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + /* legacy i2c override */ + status = mxl692_memwrite(dev, 0x80000100, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* verify sku */ + status = mxl692_memread(dev, 0x70000188, (u8 *)&dev_type, sizeof(u32)); + if (status) + goto err_finish; + + if (dev_type != dev->device_type) + goto err_finish; + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_config_regulators(struct mxl692_dev *dev, + enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E power_supply) +{ + int status = 0; + u32 reg_val; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + /* configure main regulator according to the power supply source */ + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0x00FFFFFF; + reg_val |= (power_supply == MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE) ? + 0x14000000 : 0x10000000; + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* configure digital regulator to high current mode */ + status = mxl692_memread(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x800; + + status = mxl692_memwrite(dev, 0x90000018, (u8 *)®_val, sizeof(u32)); + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_config_xtal(struct mxl692_dev *dev, + struct MXL_EAGLE_DEV_XTAL_T *dev_xtal) +{ + int status = 0; + u32 reg_val, reg_val1; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + status = mxl692_memread(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL capacitance */ + reg_val &= 0xFFFFFFE0; + reg_val |= dev_xtal->xtal_cap; + + /* set CLK OUT */ + reg_val = dev_xtal->clk_out_enable ? (reg_val | 0x0100) : (reg_val & 0xFFFFFEFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set CLK OUT divider */ + reg_val = dev_xtal->clk_out_div_enable ? (reg_val | 0x0200) : (reg_val & 0xFFFFFDFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL sharing */ + reg_val = dev_xtal->xtal_sharing_enable ? (reg_val | 0x010400) : (reg_val & 0xFFFEFBFF); + + status = mxl692_memwrite(dev, 0x90000000, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + /* enable/disable XTAL calibration, based on master/slave device */ + status = mxl692_memread(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + + if (dev_xtal->xtal_calibration_enable) { + /* enable XTAL calibration and set XTAL amplitude to a higher value */ + reg_val1 &= 0xFFFFFFFD; + reg_val1 |= 0x30; + + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + } else { + /* disable XTAL calibration */ + reg_val1 |= 0x2; + + status = mxl692_memwrite(dev, 0x90000030, (u8 *)®_val1, sizeof(u32)); + if (status) + goto err_finish; + + /* set XTAL bias value */ + status = mxl692_memread(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0xC0FFFFFF; + reg_val |= 0xA000000; + + status = mxl692_memwrite(dev, 0x9000002c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + } + + /* start XTAL calibration */ + status = mxl692_memread(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x8; + + status = mxl692_memwrite(dev, 0x70000010, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + status = mxl692_memread(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x10; + + status = mxl692_memwrite(dev, 0x70000018, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + status = mxl692_memread(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val &= 0xFFFFEFFF; + + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + reg_val |= 0x1000; + + status = mxl692_memwrite(dev, 0x9001014c, (u8 *)®_val, sizeof(u32)); + if (status) + goto err_finish; + + usleep_range(45000, 55000); + +err_finish: + if (status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_powermode(struct mxl692_dev *dev, + enum MXL_EAGLE_POWER_MODE_E power_mode) +{ + int status = 0; + u8 mode = power_mode; + + dev_dbg(&dev->i2c_client->dev, "%s\n", + power_mode == MXL_EAGLE_POWER_MODE_SLEEP ? "sleep" : "active"); + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, + &mode, + sizeof(u8), + NULL, + 0); + if (status) { + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; + } + + dev->power_mode = power_mode; + + return status; +} + +static int mxl692_init(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->i2c_client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int status = 0; + const struct firmware *firmware; + struct MXL_EAGLE_DEV_XTAL_T xtal_config = {}; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + if (dev->init_done) + goto warm; + + dev->seqnum = 1; + + status = mxl692_reset(dev); + if (status) + goto err; + + usleep_range(50 * 1000, 60 * 1000); /* was 1000! */ + + status = mxl692_config_regulators(dev, MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL); + if (status) + goto err; + + xtal_config.xtal_cap = 26; + xtal_config.clk_out_div_enable = 0; + xtal_config.clk_out_enable = 0; + xtal_config.xtal_calibration_enable = 0; + xtal_config.xtal_sharing_enable = 1; + status = mxl692_config_xtal(dev, &xtal_config); + if (status) + goto err; + + status = request_firmware(&firmware, MXL692_FIRMWARE, &client->dev); + if (status) { + dev_dbg(&dev->i2c_client->dev, "firmware missing? %s\n", + MXL692_FIRMWARE); + goto err; + } + + status = mxl692_fwdownload(dev, firmware->data, firmware->size); + if (status) + goto err_release_firmware; + + release_firmware(firmware); + + status = mxl692_get_versions(dev); + if (status) + goto err; + + dev->power_mode = MXL_EAGLE_POWER_MODE_SLEEP; +warm: + /* Config Device Power Mode */ + if (dev->power_mode != MXL_EAGLE_POWER_MODE_ACTIVE) { + status = mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_ACTIVE); + if (status) + goto err; + + usleep_range(50 * 1000, 60 * 1000); /* was 500! */ + } + + /* Init stats here to indicate which stats are supported */ + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + dev->init_done = 1; + return 0; +err_release_firmware: + release_firmware(firmware); +err: + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_sleep(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + + if (dev->power_mode != MXL_EAGLE_POWER_MODE_SLEEP) + mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_SLEEP); + + return 0; +} + +static int mxl692_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct mxl692_dev *dev = fe->demodulator_priv; + + int status = 0; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type; + struct MXL_EAGLE_MPEGOUT_PARAMS_T mpeg_params = {}; + enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E qam_annex = MXL_EAGLE_QAM_DEMOD_ANNEX_B; + struct MXL_EAGLE_QAM_DEMOD_PARAMS_T qam_params = {}; + struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T tuner_params = {}; + u8 op_param = 0; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + switch (p->modulation) { + case VSB_8: + demod_type = MXL_EAGLE_DEMOD_TYPE_ATSC; + break; + case QAM_AUTO: + case QAM_64: + case QAM_128: + case QAM_256: + demod_type = MXL_EAGLE_DEMOD_TYPE_QAM; + break; + default: + return -EINVAL; + } + + if (dev->current_frequency == p->frequency && dev->demod_type == demod_type) { + dev_dbg(&dev->i2c_client->dev, "already set up\n"); + return 0; + } + + dev->current_frequency = -1; + dev->demod_type = -1; + + op_param = demod_type; + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, + &op_param, + sizeof(u8), + NULL, + 0); + if (status) { + dev_dbg(&dev->i2c_client->dev, + "DEVICE_DEMODULATOR_TYPE_SET...FAIL err 0x%x\n", status); + goto err; + } + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + mpeg_params.mpeg_parallel = 0; + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_MSB_1ST; + mpeg_params.mpeg_sync_pulse_width = MXL_EAGLE_DATA_SYNC_WIDTH_BIT; + mpeg_params.mpeg_valid_pol = MXL_EAGLE_CLOCK_POSITIVE; + mpeg_params.mpeg_sync_pol = MXL_EAGLE_CLOCK_POSITIVE; + mpeg_params.mpeg_clk_pol = MXL_EAGLE_CLOCK_NEGATIVE; + mpeg_params.mpeg3wire_mode_enable = 0; + mpeg_params.mpeg_clk_freq = MXL_EAGLE_MPEG_CLOCK_27MHZ; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + (u8 *)&mpeg_params, + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), + NULL, + 0); + if (status) + goto err; + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + if (qam_annex == MXL_EAGLE_QAM_DEMOD_ANNEX_A) + mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_LSB_1ST; + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + (u8 *)&mpeg_params, + sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T), + NULL, + 0); + if (status) + goto err; + + qam_params.annex_type = qam_annex; + qam_params.qam_type = MXL_EAGLE_QAM_DEMOD_AUTO; + qam_params.iq_flip = MXL_EAGLE_DEMOD_IQ_AUTO; + if (p->modulation == QAM_64) + qam_params.symbol_rate_hz = 5057000; + else + qam_params.symbol_rate_hz = 5361000; + + qam_params.symbol_rate_256qam_hz = 5361000; + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, + (u8 *)&qam_params, + sizeof(struct MXL_EAGLE_QAM_DEMOD_PARAMS_T), + NULL, 0); + if (status) + goto err; + + break; + default: + break; + } + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + tuner_params.freq_hz = p->frequency; + tuner_params.bandwidth = MXL_EAGLE_TUNER_BW_6MHZ; + tuner_params.tune_mode = MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW; + + dev_dbg(&dev->i2c_client->dev, " Tuning Freq: %d %s\n", tuner_params.freq_hz, + demod_type == MXL_EAGLE_DEMOD_TYPE_ATSC ? "ATSC" : "QAM"); + + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, + (u8 *)&tuner_params, + sizeof(struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T), + NULL, + 0); + if (status) + goto err; + + usleep_range(20 * 1000, 30 * 1000); /* was 500! */ + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_INIT_SET, + NULL, 0, NULL, 0); + if (status) + goto err; + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_RESTART_SET, + NULL, 0, NULL, 0); + if (status) + goto err; + break; + default: + break; + } + + dev->demod_type = demod_type; + dev->current_frequency = p->frequency; + + return 0; +err: + dev_dbg(&dev->i2c_client->dev, "err %d\n", status); + return status; +} + +static int mxl692_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + p->modulation = c->modulation; + p->frequency = c->frequency; + + return 0; +} + +static int mxl692_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + + *snr = 0; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); + if (!mxl_status) { + *snr = (u16)(atsc_status->snr_db_tenths / 10); + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = *snr; + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); + if (!mxl_status) + *snr = (u16)(qam_status->snr_db_tenths / 10); + break; + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + + if (mxl_status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status); + return mxl_status; +} + +static int mxl692_read_ber_ucb(struct dvb_frontend *fe) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *atsc_errors; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + u32 utmp; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + atsc_errors = (struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T)); + if (!mxl_status) { + if (atsc_errors->error_packets == 0) + utmp = 0; + else + utmp = ((atsc_errors->error_bytes / atsc_errors->error_packets) * + atsc_errors->total_packets); + /* ber */ + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue += atsc_errors->error_bytes; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue += utmp; + /* ucb */ + c->block_error.stat[0].scale = FE_SCALE_COUNTER; + c->block_error.stat[0].uvalue += atsc_errors->error_packets; + + dev_dbg(&dev->i2c_client->dev, "%llu %llu\n", + c->post_bit_count.stat[0].uvalue, c->block_error.stat[0].uvalue); + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + + if (mxl_status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status); + + return mxl_status; +} + +static int mxl692_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct mxl692_dev *dev = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}; + struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status; + struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status; + enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type; + int mxl_status = 0; + *status = 0; + + dev_dbg(&dev->i2c_client->dev, "\n"); + + atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf; + qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf; + + switch (demod_type) { + case MXL_EAGLE_DEMOD_TYPE_ATSC: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T)); + if (!mxl_status && atsc_status->atsc_lock) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = atsc_status->snr_db_tenths / 10; + } + break; + case MXL_EAGLE_DEMOD_TYPE_QAM: + mxl_status = mxl692_i2c_writeread(dev, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + NULL, + 0, + rx_buf, + sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T)); + if (!mxl_status && qam_status->qam_locked) { + *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_LOCK; + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = qam_status->snr_db_tenths / 10; + } + break; + case MXL_EAGLE_DEMOD_TYPE_OOB: + default: + break; + } + + if ((*status & FE_HAS_LOCK) == 0) { + /* No lock, reset all statistics */ + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + if (mxl_status) + dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status); + else + mxl_status = mxl692_read_ber_ucb(fe); + + return mxl_status; +} + +static const struct dvb_frontend_ops mxl692_ops = { + .delsys = { SYS_ATSC }, + .info = { + .name = "MaxLinear MxL692 VSB tuner-demodulator", + .frequency_min_hz = 54000000, + .frequency_max_hz = 858000000, + .frequency_stepsize_hz = 62500, + .caps = FE_CAN_8VSB + }, + + .init = mxl692_init, + .sleep = mxl692_sleep, + .set_frontend = mxl692_set_frontend, + .get_frontend = mxl692_get_frontend, + + .read_status = mxl692_read_status, + .read_snr = mxl692_read_snr, +}; + +static int mxl692_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxl692_config *config = client->dev.platform_data; + struct mxl692_dev *dev; + int ret = 0; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + dev_dbg(&client->dev, "kzalloc() failed\n"); + goto err; + } + + memcpy(&dev->fe.ops, &mxl692_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = dev; + dev->i2c_client = client; + *config->fe = &dev->fe; + mutex_init(&dev->i2c_lock); + i2c_set_clientdata(client, dev); + + dev_info(&client->dev, "MaxLinear mxl692 successfully attached\n"); + + return 0; +err: + dev_dbg(&client->dev, "failed %d\n", ret); + return -ENODEV; +} + +static int mxl692_remove(struct i2c_client *client) +{ + struct mxl692_dev *dev = i2c_get_clientdata(client); + + dev->fe.demodulator_priv = NULL; + i2c_set_clientdata(client, NULL); + kfree(dev); + + return 0; +} + +static const struct i2c_device_id mxl692_id_table[] = { + {"mxl692", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mxl692_id_table); + +static struct i2c_driver mxl692_driver = { + .driver = { + .name = "mxl692", + }, + .probe = mxl692_probe, + .remove = mxl692_remove, + .id_table = mxl692_id_table, +}; + +module_i2c_driver(mxl692_driver); + +MODULE_AUTHOR("Brad Love "); +MODULE_DESCRIPTION("MaxLinear MxL692 demodulator/tuner driver"); +MODULE_FIRMWARE(MXL692_FIRMWARE); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/mxl692.h b/drivers/media/dvb-frontends/mxl692.h new file mode 100644 index 000000000000..45bc48f1f12f --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the MaxLinear MxL69x family of tuners/demods + * + * Copyright (C) 2020 Brad Love + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MXL692_H_ +#define _MXL692_H_ + +#include + +#define MXL692_FIRMWARE "dvb-demod-mxl692.fw" + +struct mxl692_config { + unsigned char id; + u8 i2c_addr; + /* + * frontend + * returned by driver + */ + struct dvb_frontend **fe; +}; + +#endif /* _MXL692_H_ */ diff --git a/drivers/media/dvb-frontends/mxl692_defs.h b/drivers/media/dvb-frontends/mxl692_defs.h new file mode 100644 index 000000000000..776ac407b4e7 --- /dev/null +++ b/drivers/media/dvb-frontends/mxl692_defs.h @@ -0,0 +1,548 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the MaxLinear MxL69x family of combo tuners/demods + * + * Copyright (C) 2020 Brad Love + * + * based on code: + * Copyright (c) 2016 MaxLinear, Inc. All rights reserved + * which was released under GPL V2 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/***************************************************************************** + * Defines + ***************************************************************************** + */ +#define MXL_EAGLE_HOST_MSG_HEADER_SIZE 8 +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 +#define MXL_EAGLE_QAM_FFE_TAPS_LENGTH 16 +#define MXL_EAGLE_QAM_SPUR_TAPS_LENGTH 32 +#define MXL_EAGLE_QAM_DFE_TAPS_LENGTH 72 +#define MXL_EAGLE_ATSC_FFE_TAPS_LENGTH 4096 +#define MXL_EAGLE_ATSC_DFE_TAPS_LENGTH 384 +#define MXL_EAGLE_VERSION_SIZE 5 /* A.B.C.D-RCx */ +#define MXL_EAGLE_FW_LOAD_TIME 50 + +#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76 +#define MXL_EAGLE_FW_HEADER_SIZE 16 +#define MXL_EAGLE_FW_SEGMENT_HEADER_SIZE 8 +#define MXL_EAGLE_MAX_I2C_PACKET_SIZE 58 +#define MXL_EAGLE_I2C_MHEADER_SIZE 6 +#define MXL_EAGLE_I2C_PHEADER_SIZE 2 + +/* Enum of Eagle family devices */ +enum MXL_EAGLE_DEVICE_E { + MXL_EAGLE_DEVICE_691 = 1, /* Device Mxl691 */ + MXL_EAGLE_DEVICE_248 = 2, /* Device Mxl248 */ + MXL_EAGLE_DEVICE_692 = 3, /* Device Mxl692 */ + MXL_EAGLE_DEVICE_MAX, /* No such device */ +}; + +#define VER_A 1 +#define VER_B 1 +#define VER_C 1 +#define VER_D 3 +#define VER_E 6 + +/* Enum of Host to Eagle I2C protocol opcodes */ +enum MXL_EAGLE_OPCODE_E { + /* DEVICE */ + MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET, + MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET, + MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET, + MXL_EAGLE_OPCODE_DEVICE_GPIO_DIRECTION_SET, + MXL_EAGLE_OPCODE_DEVICE_GPO_LEVEL_SET, + MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET, + MXL_EAGLE_OPCODE_DEVICE_IO_MUX_SET, + MXL_EAGLE_OPCODE_DEVICE_VERSION_GET, + MXL_EAGLE_OPCODE_DEVICE_STATUS_GET, + MXL_EAGLE_OPCODE_DEVICE_GPI_LEVEL_GET, + + /* TUNER */ + MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET, + MXL_EAGLE_OPCODE_TUNER_LOCK_STATUS_GET, + MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET, + + /* ATSC */ + MXL_EAGLE_OPCODE_ATSC_INIT_SET, + MXL_EAGLE_OPCODE_ATSC_ACQUIRE_CARRIER_SET, + MXL_EAGLE_OPCODE_ATSC_STATUS_GET, + MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_DFE_TAPS_GET, + MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET, + + /* QAM */ + MXL_EAGLE_OPCODE_QAM_PARAMS_SET, + MXL_EAGLE_OPCODE_QAM_RESTART_SET, + MXL_EAGLE_OPCODE_QAM_STATUS_GET, + MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET, + MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET, + + /* OOB */ + MXL_EAGLE_OPCODE_OOB_PARAMS_SET, + MXL_EAGLE_OPCODE_OOB_RESTART_SET, + MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET, + MXL_EAGLE_OPCODE_OOB_STATUS_GET, + + /* SMA */ + MXL_EAGLE_OPCODE_SMA_INIT_SET, + MXL_EAGLE_OPCODE_SMA_PARAMS_SET, + MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET, + MXL_EAGLE_OPCODE_SMA_RECEIVE_GET, + + /* DEBUG */ + MXL_EAGLE_OPCODE_INTERNAL, + + MXL_EAGLE_OPCODE_MAX = 70, +}; + +/* Enum of Host to Eagle I2C protocol opcodes */ +static const char * const MXL_EAGLE_OPCODE_STRING[] = { + /* DEVICE */ + "DEVICE_DEMODULATOR_TYPE_SET", + "DEVICE_MPEG_OUT_PARAMS_SET", + "DEVICE_POWERMODE_SET", + "DEVICE_GPIO_DIRECTION_SET", + "DEVICE_GPO_LEVEL_SET", + "DEVICE_INTR_MASK_SET", + "DEVICE_IO_MUX_SET", + "DEVICE_VERSION_GET", + "DEVICE_STATUS_GET", + "DEVICE_GPI_LEVEL_GET", + + /* TUNER */ + "TUNER_CHANNEL_TUNE_SET", + "TUNER_LOCK_STATUS_GET", + "TUNER_AGC_STATUS_GET", + + /* ATSC */ + "ATSC_INIT_SET", + "ATSC_ACQUIRE_CARRIER_SET", + "ATSC_STATUS_GET", + "ATSC_ERROR_COUNTERS_GET", + "ATSC_EQUALIZER_FILTER_DFE_TAPS_GET", + "ATSC_EQUALIZER_FILTER_FFE_TAPS_GET", + + /* QAM */ + "QAM_PARAMS_SET", + "QAM_RESTART_SET", + "QAM_STATUS_GET", + "QAM_ERROR_COUNTERS_GET", + "QAM_CONSTELLATION_VALUE_GET", + "QAM_EQUALIZER_FILTER_FFE_GET", + "QAM_EQUALIZER_FILTER_SPUR_START_GET", + "QAM_EQUALIZER_FILTER_SPUR_END_GET", + "QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET", + "QAM_EQUALIZER_FILTER_DFE_START_GET", + "QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET", + "QAM_EQUALIZER_FILTER_DFE_END_GET", + + /* OOB */ + "OOB_PARAMS_SET", + "OOB_RESTART_SET", + "OOB_ERROR_COUNTERS_GET", + "OOB_STATUS_GET", + + /* SMA */ + "SMA_INIT_SET", + "SMA_PARAMS_SET", + "SMA_TRANSMIT_SET", + "SMA_RECEIVE_GET", + + /* DEBUG */ + "INTERNAL", +}; + +/* Enum of Callabck function types */ +enum MXL_EAGLE_CB_TYPE_E { + MXL_EAGLE_CB_FW_DOWNLOAD = 0, +}; + +/* Enum of power supply types */ +enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E { + MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE, /* Single supply of 3.3V */ + MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL, /* Dual supply, 1.8V & 3.3V */ +}; + +/* Enum of I/O pad drive modes */ +enum MXL_EAGLE_IO_MUX_DRIVE_MODE_E { + MXL_EAGLE_IO_MUX_DRIVE_MODE_1X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_2X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_3X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_4X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_5X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_6X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_7X, + MXL_EAGLE_IO_MUX_DRIVE_MODE_8X, +}; + +/* Enum of demodulator types. Used for selection of demodulator + * type in relevant devices, e.g. ATSC vs. QAM in Mxl691 + */ +enum MXL_EAGLE_DEMOD_TYPE_E { + MXL_EAGLE_DEMOD_TYPE_QAM, /* Mxl248 or Mxl692 */ + MXL_EAGLE_DEMOD_TYPE_OOB, /* Mxl248 only */ + MXL_EAGLE_DEMOD_TYPE_ATSC /* Mxl691 or Mxl692 */ +}; + +/* Enum of power modes. Used for initial + * activation, or for activating sleep mode + */ +enum MXL_EAGLE_POWER_MODE_E { + MXL_EAGLE_POWER_MODE_SLEEP, + MXL_EAGLE_POWER_MODE_ACTIVE +}; + +/* Enum of GPIOs, used in device GPIO APIs */ +enum MXL_EAGLE_GPIO_NUMBER_E { + MXL_EAGLE_GPIO_NUMBER_0, + MXL_EAGLE_GPIO_NUMBER_1, + MXL_EAGLE_GPIO_NUMBER_2, + MXL_EAGLE_GPIO_NUMBER_3, + MXL_EAGLE_GPIO_NUMBER_4, + MXL_EAGLE_GPIO_NUMBER_5, + MXL_EAGLE_GPIO_NUMBER_6 +}; + +/* Enum of GPIO directions, used in GPIO direction configuration API */ +enum MXL_EAGLE_GPIO_DIRECTION_E { + MXL_EAGLE_GPIO_DIRECTION_INPUT, + MXL_EAGLE_GPIO_DIRECTION_OUTPUT +}; + +/* Enum of GPIO level, used in device GPIO APIs */ +enum MXL_EAGLE_GPIO_LEVEL_E { + MXL_EAGLE_GPIO_LEVEL_LOW, + MXL_EAGLE_GPIO_LEVEL_HIGH, +}; + +/* Enum of I/O Mux function, used in device I/O mux configuration API */ +enum MXL_EAGLE_IOMUX_FUNCTION_E { + MXL_EAGLE_IOMUX_FUNC_FEC_LOCK, + MXL_EAGLE_IOMUX_FUNC_MERR, +}; + +/* Enum of MPEG Data format, used in MPEG and OOB output configuration */ +enum MXL_EAGLE_MPEG_DATA_FORMAT_E { + MXL_EAGLE_DATA_SERIAL_LSB_1ST = 0, + MXL_EAGLE_DATA_SERIAL_MSB_1ST, + + MXL_EAGLE_DATA_SYNC_WIDTH_BIT = 0, + MXL_EAGLE_DATA_SYNC_WIDTH_BYTE +}; + +/* Enum of MPEG Clock format, used in MPEG and OOB output configuration */ +enum MXL_EAGLE_MPEG_CLOCK_FORMAT_E { + MXL_EAGLE_CLOCK_ACTIVE_HIGH = 0, + MXL_EAGLE_CLOCK_ACTIVE_LOW, + + MXL_EAGLE_CLOCK_POSITIVE = 0, + MXL_EAGLE_CLOCK_NEGATIVE, + + MXL_EAGLE_CLOCK_IN_PHASE = 0, + MXL_EAGLE_CLOCK_INVERTED, +}; + +/* Enum of MPEG Clock speeds, used in MPEG output configuration */ +enum MXL_EAGLE_MPEG_CLOCK_RATE_E { + MXL_EAGLE_MPEG_CLOCK_54MHZ, + MXL_EAGLE_MPEG_CLOCK_40_5MHZ, + MXL_EAGLE_MPEG_CLOCK_27MHZ, + MXL_EAGLE_MPEG_CLOCK_13_5MHZ, +}; + +/* Enum of Interrupt mask bit, used in host interrupt configuration */ +enum MXL_EAGLE_INTR_MASK_BITS_E { + MXL_EAGLE_INTR_MASK_DEMOD = 0, + MXL_EAGLE_INTR_MASK_SMA_RX = 1, + MXL_EAGLE_INTR_MASK_WDOG = 31 +}; + +/* Enum of QAM Demodulator type, used in QAM configuration */ +enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E { + MXL_EAGLE_QAM_DEMOD_ANNEX_B, /* J.83B */ + MXL_EAGLE_QAM_DEMOD_ANNEX_A, /* DVB-C */ +}; + +/* Enum of QAM Demodulator modulation, used in QAM configuration and status */ +enum MXL_EAGLE_QAM_DEMOD_QAM_TYPE_E { + MXL_EAGLE_QAM_DEMOD_QAM16, + MXL_EAGLE_QAM_DEMOD_QAM64, + MXL_EAGLE_QAM_DEMOD_QAM256, + MXL_EAGLE_QAM_DEMOD_QAM1024, + MXL_EAGLE_QAM_DEMOD_QAM32, + MXL_EAGLE_QAM_DEMOD_QAM128, + MXL_EAGLE_QAM_DEMOD_QPSK, + MXL_EAGLE_QAM_DEMOD_AUTO, +}; + +/* Enum of Demodulator IQ setup, used in QAM, OOB configuration and status */ +enum MXL_EAGLE_IQ_FLIP_E { + MXL_EAGLE_DEMOD_IQ_NORMAL, + MXL_EAGLE_DEMOD_IQ_FLIPPED, + MXL_EAGLE_DEMOD_IQ_AUTO, +}; + +/* Enum of OOB Demodulator symbol rates, used in OOB configuration */ +enum MXL_EAGLE_OOB_DEMOD_SYMB_RATE_E { + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_0_772MHZ, /* ANSI/SCTE 55-2 0.772 MHz */ + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_024MHZ, /* ANSI/SCTE 55-1 1.024 MHz */ + MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_544MHZ, /* ANSI/SCTE 55-2 1.544 MHz */ +}; + +/* Enum of tuner channel tuning mode */ +enum MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_E { + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW, /* Normal "view" mode */ + MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_SCAN, /* Fast "scan" mode */ +}; + +/* Enum of tuner bandwidth */ +enum MXL_EAGLE_TUNER_BW_E { + MXL_EAGLE_TUNER_BW_6MHZ, + MXL_EAGLE_TUNER_BW_7MHZ, + MXL_EAGLE_TUNER_BW_8MHZ, +}; + +/* Enum of tuner bandwidth */ +enum MXL_EAGLE_JUNCTION_TEMPERATURE_E { + MXL_EAGLE_JUNCTION_TEMPERATURE_BELOW_0_CELSIUS = 0, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_0_TO_14_CELSIUS = 1, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_14_TO_28_CELSIUS = 3, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_28_TO_42_CELSIUS = 2, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_42_TO_57_CELSIUS = 6, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_57_TO_71_CELSIUS = 7, + MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_71_TO_85_CELSIUS = 5, + MXL_EAGLE_JUNCTION_TEMPERATURE_ABOVE_85_CELSIUS = 4, +}; + +/* Struct passed in optional callback used during FW download */ +struct MXL_EAGLE_FW_DOWNLOAD_CB_PAYLOAD_T { + u32 total_len; + u32 downloaded_len; +}; + +/* Struct used of I2C protocol between host and Eagle, internal use only */ +struct __packed MXL_EAGLE_HOST_MSG_HEADER_T { + u8 opcode; + u8 seqnum; + u8 payload_size; + u8 status; + u32 checksum; +}; + +/* Device version information struct */ +struct __packed MXL_EAGLE_DEV_VER_T { + u8 chip_id; + u8 firmware_ver[MXL_EAGLE_VERSION_SIZE]; + u8 mxlware_ver[MXL_EAGLE_VERSION_SIZE]; +}; + +/* Xtal configuration struct */ +struct __packed MXL_EAGLE_DEV_XTAL_T { + u8 xtal_cap; /* accepted range is 1..31 pF. Default is 26 */ + u8 clk_out_enable; + u8 clk_out_div_enable; /* clock out freq is xtal freq / 6 */ + u8 xtal_sharing_enable; /* if enabled set xtal_cap to 25 pF */ + u8 xtal_calibration_enable; /* enable for master, disable for slave */ +}; + +/* GPIO direction struct, internally used in GPIO configuration API */ +struct __packed MXL_EAGLE_DEV_GPIO_DIRECTION_T { + u8 gpio_number; + u8 gpio_direction; +}; + +/* GPO level struct, internally used in GPIO configuration API */ +struct __packed MXL_EAGLE_DEV_GPO_LEVEL_T { + u8 gpio_number; + u8 gpo_level; +}; + +/* Device Status struct */ +struct MXL_EAGLE_DEV_STATUS_T { + u8 temperature; + u8 demod_type; + u8 power_mode; + u8 cpu_utilization_percent; +}; + +/* Device interrupt configuration struct */ +struct __packed MXL_EAGLE_DEV_INTR_CFG_T { + u32 intr_mask; + u8 edge_trigger; + u8 positive_trigger; + u8 global_enable_interrupt; +}; + +/* MPEG pad drive parameters, used on MPEG output configuration */ +/* See MXL_EAGLE_IO_MUX_DRIVE_MODE_E */ +struct MXL_EAGLE_MPEG_PAD_DRIVE_T { + u8 pad_drv_mpeg_syn; + u8 pad_drv_mpeg_dat; + u8 pad_drv_mpeg_val; + u8 pad_drv_mpeg_clk; +}; + +/* MPEGOUT parameter struct, used in MPEG output configuration */ +struct MXL_EAGLE_MPEGOUT_PARAMS_T { + u8 mpeg_parallel; + u8 msb_first; + u8 mpeg_sync_pulse_width; /* See MXL_EAGLE_MPEG_DATA_FORMAT_E */ + u8 mpeg_valid_pol; + u8 mpeg_sync_pol; + u8 mpeg_clk_pol; + u8 mpeg3wire_mode_enable; + u8 mpeg_clk_freq; + struct MXL_EAGLE_MPEG_PAD_DRIVE_T mpeg_pad_drv; +}; + +/* QAM Demodulator parameters struct, used in QAM params configuration */ +struct __packed MXL_EAGLE_QAM_DEMOD_PARAMS_T { + u8 annex_type; + u8 qam_type; + u8 iq_flip; + u8 search_range_idx; + u8 spur_canceller_enable; + u32 symbol_rate_hz; + u32 symbol_rate_256qam_hz; +}; + +/* QAM Demodulator status */ +struct MXL_EAGLE_QAM_DEMOD_STATUS_T { + u8 annex_type; + u8 qam_type; + u8 iq_flip; + u8 interleaver_depth_i; + u8 interleaver_depth_j; + u8 qam_locked; + u8 fec_locked; + u8 mpeg_locked; + u16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offset_hz; +}; + +/* QAM Demodulator error counters */ +struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T { + u32 corrected_code_words; + u32 uncorrected_code_words; + u32 total_code_words_received; + u32 corrected_bits; + u32 error_mpeg_frames; + u32 mpeg_frames_received; + u32 erasures; +}; + +/* QAM Demodulator constellation point */ +struct MXL_EAGLE_QAM_DEMOD_CONSTELLATION_VAL_T { + s16 i_value[12]; + s16 q_value[12]; +}; + +/* QAM Demodulator equalizer filter taps */ +struct MXL_EAGLE_QAM_DEMOD_EQU_FILTER_T { + s16 ffe_taps[MXL_EAGLE_QAM_FFE_TAPS_LENGTH]; + s16 spur_taps[MXL_EAGLE_QAM_SPUR_TAPS_LENGTH]; + s16 dfe_taps[MXL_EAGLE_QAM_DFE_TAPS_LENGTH]; + u8 ffe_leading_tap_index; + u8 dfe_taps_number; +}; + +/* OOB Demodulator parameters struct, used in OOB params configuration */ +struct __packed MXL_EAGLE_OOB_DEMOD_PARAMS_T { + u8 symbol_rate; + u8 iq_flip; + u8 clk_pol; +}; + +/* OOB Demodulator error counters */ +struct MXL_EAGLE_OOB_DEMOD_ERROR_COUNTERS_T { + u32 corrected_packets; + u32 uncorrected_packets; + u32 total_packets_received; +}; + +/* OOB status */ +struct __packed MXL_EAGLE_OOB_DEMOD_STATUS_T { + u16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offsetHz; + u8 qam_locked; + u8 fec_locked; + u8 mpeg_locked; + u8 retune_required; + u8 iq_flip; +}; + +/* ATSC Demodulator status */ +struct __packed MXL_EAGLE_ATSC_DEMOD_STATUS_T { + s16 snr_db_tenths; + s16 timing_offset; + s32 carrier_offset_hz; + u8 frame_lock; + u8 atsc_lock; + u8 fec_lock; +}; + +/* ATSC Demodulator error counters */ +struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T { + u32 error_packets; + u32 total_packets; + u32 error_bytes; +}; + +/* ATSC Demodulator equalizers filter taps */ +struct __packed MXL_EAGLE_ATSC_DEMOD_EQU_FILTER_T { + s16 ffe_taps[MXL_EAGLE_ATSC_FFE_TAPS_LENGTH]; + s8 dfe_taps[MXL_EAGLE_ATSC_DFE_TAPS_LENGTH]; +}; + +/* Tuner AGC Status */ +struct __packed MXL_EAGLE_TUNER_AGC_STATUS_T { + u8 locked; + u16 raw_agc_gain; /* AGC gain [dB] = rawAgcGain / 2^6 */ + s16 rx_power_db_hundredths; +}; + +/* Tuner channel tune parameters */ +struct __packed MXL_EAGLE_TUNER_CHANNEL_PARAMS_T { + u32 freq_hz; + u8 tune_mode; + u8 bandwidth; +}; + +/* Tuner channel lock indications */ +struct __packed MXL_EAGLE_TUNER_LOCK_STATUS_T { + u8 rf_pll_locked; + u8 ref_pll_locked; +}; + +/* Smart antenna parameters used in Smart antenna params configuration */ +struct __packed MXL_EAGLE_SMA_PARAMS_T { + u8 full_duplex_enable; + u8 rx_disable; + u8 idle_logic_high; +}; + +/* Smart antenna message format */ +struct __packed MXL_EAGLE_SMA_MESSAGE_T { + u32 payload_bits; + u8 total_num_bits; +}; + -- cgit v1.2.3 From 688e2dd468ace07b6c6d54f37d50ad03b5ec6cf7 Mon Sep 17 00:00:00 2001 From: Brad Love Date: Tue, 26 Jan 2021 02:54:14 +0100 Subject: media: em28xx-core: Fix TS2 active led TS2 active led is completely igored, fix that. Signed-off-by: Brad Love Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 3daa64bb1e1d..584fa400cd7d 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -698,8 +698,10 @@ int em28xx_capture_start(struct em28xx *dev, int start) if (dev->mode == EM28XX_ANALOG_MODE) led = em28xx_find_led(dev, EM28XX_LED_ANALOG_CAPTURING); - else + else if (dev->ts == PRIMARY_TS) led = em28xx_find_led(dev, EM28XX_LED_DIGITAL_CAPTURING); + else + led = em28xx_find_led(dev, EM28XX_LED_DIGITAL_CAPTURING_TS2); if (led) em28xx_write_reg_bits(dev, led->gpio_reg, -- cgit v1.2.3 From 1970105af093f2fbbc5578640075ca843575c934 Mon Sep 17 00:00:00 2001 From: Brad Love Date: Tue, 26 Jan 2021 02:54:15 +0100 Subject: media: em28xx-core: Fix i2c error debug Read errors are currently reported as write errors. An incorrectly received read operation is never reported at all. Add a debug statement indicating the request mismatch. Signed-off-by: Brad Love Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 592b98b3643a..255395959255 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -294,6 +294,10 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) "reading from i2c device at 0x%x failed (error=%i)\n", addr, ret); return ret; + } else if (ret != len) { + dev_dbg(&dev->intf->dev, + "%i bytes read from i2c device at 0x%x requested, but %i bytes written\n", + ret, addr, len); } /* * NOTE: some devices with two i2c buses have the bad habit to return 0 @@ -329,7 +333,7 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) } dev_warn(&dev->intf->dev, - "write to i2c device at 0x%x failed with unknown error (status=%i)\n", + "read from i2c device at 0x%x failed with unknown error (status=%i)\n", addr, ret); return -EIO; } -- cgit v1.2.3 From 2e74a01fb073900c8cc5df6b5e4bcbf575ac6c26 Mon Sep 17 00:00:00 2001 From: Brad Love Date: Tue, 26 Jan 2021 02:54:16 +0100 Subject: media: em28xx: Add support for Hauppauge USB QuadHD Hauppauge USB QuadHD contains two two-tuner em28xx devices behind a usb hub. Each of the four dvb adapters contains a MaxLinear 692 combo ATSC demod/tuner. Bus 003 Device 006: ID 2040:846d Hauppauge Bus 003 Device 005: ID 2040:846d Hauppauge Bus 003 Device 004: ID 04e2:0404 Exar Corp. Signed-off-by: Brad Love Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/Kconfig | 1 + drivers/media/usb/em28xx/em28xx-cards.c | 46 +++++++++++++++++++++++++++++++++ drivers/media/usb/em28xx/em28xx-dvb.c | 26 +++++++++++++++++++ drivers/media/usb/em28xx/em28xx.h | 1 + 4 files changed, 74 insertions(+) diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index 8a24731b373a..b3c472b8c5a9 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -67,6 +67,7 @@ config VIDEO_EM28XX_DVB select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MXL692 if MEDIA_SUBDRV_AUTOSELECT help This adds support for DVB cards based on the Empiatech em28xx chips. diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 5144888ae36f..d6c8ae213914 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -549,6 +549,21 @@ static const struct em28xx_reg_seq hauppauge_dualhd_dvb[] = { {-1, -1, -1, -1}, }; +/* Hauppauge USB QuadHD */ +static struct em28xx_reg_seq hauppauge_usb_quadhd_atsc_reg_seq[] = { + {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0}, + {0x0d, 0xff, 0xff, 200}, + {0x50, 0x04, 0xff, 300}, + {EM2874_R80_GPIO_P0_CTRL, 0xb0, 0xf0, 100}, /* demod 1 reset */ + {EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xf0, 100}, + {EM2874_R80_GPIO_P0_CTRL, 0xd0, 0xf0, 100}, /* demod 2 reset */ + {EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xf0, 100}, + {EM2874_R5F_TS_ENABLE, 0x44, 0xff, 50}, + {EM2874_R5D_TS1_PKT_SIZE, 0x05, 0xff, 50}, + {EM2874_R5E_TS2_PKT_SIZE, 0x05, 0xff, 50}, + {-1, -1, -1, -1}, +}; + /* * Button definitions */ @@ -644,6 +659,22 @@ static struct em28xx_led hauppauge_dualhd_leds[] = { {-1, 0, 0, 0}, }; +static struct em28xx_led hauppauge_usb_quadhd_leds[] = { + { + .role = EM28XX_LED_DIGITAL_CAPTURING, + .gpio_reg = EM2874_R80_GPIO_P0_CTRL, + .gpio_mask = EM_GPIO_2, + .inverted = 1, + }, + { + .role = EM28XX_LED_DIGITAL_CAPTURING_TS2, + .gpio_reg = EM2874_R80_GPIO_P0_CTRL, + .gpio_mask = EM_GPIO_0, + .inverted = 1, + }, + {-1, 0, 0, 0}, +}; + /* * Board definitions */ @@ -2539,6 +2570,19 @@ const struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + /* 2040:826d Hauppauge USB QuadHD + * Empia 28274, Max Linear 692 ATSC combo demod/tuner + */ + [EM2874_BOARD_HAUPPAUGE_USB_QUADHD] = { + .name = "Hauppauge USB QuadHD ATSC", + .def_i2c_bus = 1, + .has_dual_ts = 1, + .has_dvb = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ, + .tuner_type = TUNER_ABSENT, + .tuner_gpio = hauppauge_usb_quadhd_atsc_reg_seq, + .leds = hauppauge_usb_quadhd_leds, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2672,6 +2716,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, { USB_DEVICE(0x2040, 0x826d), .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, + { USB_DEVICE(0x2040, 0x846d), + .driver_info = EM2874_BOARD_HAUPPAUGE_USB_QUADHD }, { USB_DEVICE(0x0438, 0xb002), .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, { USB_DEVICE(0x2001, 0xf112), diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index fb9cbfa81a84..526424279637 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -62,6 +62,7 @@ #include "si2157.h" #include "tc90522.h" #include "qm1d1c0042.h" +#include "mxl692.h" MODULE_AUTHOR("Mauro Carvalho Chehab "); MODULE_LICENSE("GPL v2"); @@ -1459,6 +1460,26 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev) return 0; } +static int em2874_dvb_init_hauppauge_usb_quadhd(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct mxl692_config mxl692_config = {}; + unsigned char addr; + + /* attach demod/tuner combo */ + mxl692_config.id = (dev->ts == PRIMARY_TS) ? 0 : 1; + mxl692_config.fe = &dvb->fe[0]; + addr = (dev->ts == PRIMARY_TS) ? 0x60 : 0x63; + + dvb->i2c_client_demod = dvb_module_probe("mxl692", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + addr, &mxl692_config); + if (!dvb->i2c_client_demod) + return -ENODEV; + + return 0; +} + static int em28xx_dvb_init(struct em28xx *dev) { int result = 0, dvb_alt = 0; @@ -1945,6 +1966,11 @@ static int em28xx_dvb_init(struct em28xx *dev) if (result) goto out_free; break; + case EM2874_BOARD_HAUPPAUGE_USB_QUADHD: + result = em2874_dvb_init_hauppauge_usb_quadhd(dev); + if (result) + goto out_free; + break; default: dev_err(&dev->intf->dev, "The frontend of your DVB/ATSC card isn't supported yet\n"); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 55a46faaf7b7..6648e11f1271 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -152,6 +152,7 @@ #define EM2861_BOARD_MAGIX_VIDEOWANDLER2 103 #define EM28178_BOARD_PCTV_461E_V2 104 #define EM2860_BOARD_MYGICA_IGRABBER 105 +#define EM2874_BOARD_HAUPPAUGE_USB_QUADHD 106 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit v1.2.3 From 6532923237b427ed30cc7b4486f6f1ccdee3c647 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Fri, 29 Jan 2021 11:54:53 +0100 Subject: media: smipcie: fix interrupt handling and IR timeout After the first IR message, interrupts are no longer received. In addition, the code generates a timeout IR message of 10ms but sets the timeout value to 100ms, so no timeout was ever generated. Link: https://bugzilla.kernel.org/show_bug.cgi?id=204317 Fixes: a49a7a4635de ("media: smipcie: add universal ir capability") Tested-by: Laz Lev Cc: stable@vger.kernel.org # v5.1+ Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/smipcie/smipcie-ir.c | 46 +++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c index e6b74e161a05..c0604d9c7011 100644 --- a/drivers/media/pci/smipcie/smipcie-ir.c +++ b/drivers/media/pci/smipcie/smipcie-ir.c @@ -60,38 +60,44 @@ static void smi_ir_decode(struct smi_rc *ir) { struct smi_dev *dev = ir->dev; struct rc_dev *rc_dev = ir->rc_dev; - u32 dwIRControl, dwIRData; - u8 index, ucIRCount, readLoop; + u32 control, data; + u8 index, ir_count, read_loop; - dwIRControl = smi_read(IR_Init_Reg); + control = smi_read(IR_Init_Reg); - if (dwIRControl & rbIRVld) { - ucIRCount = (u8) smi_read(IR_Data_Cnt); + dev_dbg(&rc_dev->dev, "ircontrol: 0x%08x\n", control); - readLoop = ucIRCount/4; - if (ucIRCount % 4) - readLoop += 1; - for (index = 0; index < readLoop; index++) { - dwIRData = smi_read(IR_DATA_BUFFER_BASE + (index * 4)); + if (control & rbIRVld) { + ir_count = (u8)smi_read(IR_Data_Cnt); - ir->irData[index*4 + 0] = (u8)(dwIRData); - ir->irData[index*4 + 1] = (u8)(dwIRData >> 8); - ir->irData[index*4 + 2] = (u8)(dwIRData >> 16); - ir->irData[index*4 + 3] = (u8)(dwIRData >> 24); + dev_dbg(&rc_dev->dev, "ircount %d\n", ir_count); + + read_loop = ir_count / 4; + if (ir_count % 4) + read_loop += 1; + for (index = 0; index < read_loop; index++) { + data = smi_read(IR_DATA_BUFFER_BASE + (index * 4)); + dev_dbg(&rc_dev->dev, "IRData 0x%08x\n", data); + + ir->irData[index * 4 + 0] = (u8)(data); + ir->irData[index * 4 + 1] = (u8)(data >> 8); + ir->irData[index * 4 + 2] = (u8)(data >> 16); + ir->irData[index * 4 + 3] = (u8)(data >> 24); } - smi_raw_process(rc_dev, ir->irData, ucIRCount); - smi_set(IR_Init_Reg, rbIRVld); + smi_raw_process(rc_dev, ir->irData, ir_count); } - if (dwIRControl & rbIRhighidle) { + if (control & rbIRhighidle) { struct ir_raw_event rawir = {}; + dev_dbg(&rc_dev->dev, "high idle\n"); + rawir.pulse = 0; rawir.duration = SMI_SAMPLE_PERIOD * SMI_SAMPLE_IDLEMIN; ir_raw_event_store_with_filter(rc_dev, &rawir); - smi_set(IR_Init_Reg, rbIRhighidle); } + smi_set(IR_Init_Reg, rbIRVld); ir_raw_event_handle(rc_dev); } @@ -150,7 +156,7 @@ int smi_ir_init(struct smi_dev *dev) rc_dev->dev.parent = &dev->pci_dev->dev; rc_dev->map_name = dev->info->rc_map; - rc_dev->timeout = MS_TO_US(100); + rc_dev->timeout = SMI_SAMPLE_PERIOD * SMI_SAMPLE_IDLEMIN; rc_dev->rx_resolution = SMI_SAMPLE_PERIOD; ir->rc_dev = rc_dev; @@ -173,7 +179,7 @@ void smi_ir_exit(struct smi_dev *dev) struct smi_rc *ir = &dev->ir; struct rc_dev *rc_dev = ir->rc_dev; - smi_ir_stop(ir); rc_unregister_device(rc_dev); + smi_ir_stop(ir); ir->rc_dev = NULL; } -- cgit v1.2.3 From c0133e9dbac84bc6189ef5896b937201934a4ec5 Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Mon, 25 Jan 2021 15:10:29 +0100 Subject: media: doc: pixfmt-yuv: Fix 4:4:4 subsampling info YUV 4:4:4 is not subsampled, fix this in the docs. Fixes: da785536e007 ("media: doc: pixfmt-yuv: Move all semi-planar YUV formats to common file") Signed-off-by: Helen Koike Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst index 7d4d39201a3f..1e0db602cc1b 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst @@ -396,9 +396,9 @@ number of lines as the luma plane. NV24 and NV42 ------------- -Semi-planar YUV 4:4:4 formats. The chroma plane is subsampled by 2 in the -horizontal direction. Chroma lines contain half the number of pixels and the -same number of bytes as luma lines, and the chroma plane contains the same +Semi-planar YUV 4:4:4 formats. The chroma plane is not subsampled. +Chroma lines contain the same number of pixels and twice the +number of bytes as luma lines, and the chroma plane contains the same number of lines as the luma plane. .. flat-table:: Sample 4x4 NV24 Image -- cgit v1.2.3 From fee20eb5ceaba5a475b9cd735777b48d07d1a3b9 Mon Sep 17 00:00:00 2001 From: dingsenjie Date: Thu, 28 Jan 2021 07:53:29 +0100 Subject: media: media/pci: fix spelling typo of frimware frimware -> firmware Signed-off-by: dingsenjie Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7164/saa7164.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h index 2801a2b03fa0..4b4eb156e214 100644 --- a/drivers/media/pci/saa7164/saa7164.h +++ b/drivers/media/pci/saa7164/saa7164.h @@ -24,7 +24,7 @@ saa7164_bus..() : Manage a read/write memory ring buffer in the | : PCIe Address space. | - | saa7164_fw...() : Load any frimware + | saa7164_fw...() : Load any firmware | | : direct into the device V V <- ----------------- PCIe address space -------------------- -> -- cgit v1.2.3 From 08979f160eb96120354cbc6a815e8296f52cdc0d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 30 Jan 2021 10:08:42 +0100 Subject: media: media/dvb/dvbstb.svg: Antena -> Antenna Fix typo. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/dvb/dvbstb.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/userspace-api/media/dvb/dvbstb.svg b/Documentation/userspace-api/media/dvb/dvbstb.svg index 87e68baa056b..6f0ba98f9bf9 100644 --- a/Documentation/userspace-api/media/dvb/dvbstb.svg +++ b/Documentation/userspace-api/media/dvb/dvbstb.svg @@ -2,7 +2,7 @@ image/svg+xmlAntena +x="1013.1317" y="5435.1221">Antenna Frontend CA Demux -- cgit v1.2.3 From c90c103c8dc42a6d236491cf9f80145a5aaf968d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 1 Feb 2021 09:37:40 +0100 Subject: media: vidtv: adapter->mdev was set too late The media device has to be initialized and assigned to adapter->mdev before the dvb devices are created, since that will trigger the automatic creation of the topology. Rework this code to achieve this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vidtv/vidtv_bridge.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index 6ab17a83bced..75617709c8ce 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -416,6 +416,7 @@ static int vidtv_bridge_dvb_init(struct vidtv_dvb *dvb) ret = vidtv_bridge_register_adap(dvb); if (ret < 0) goto fail_adapter; + dvb_register_media_controller(&dvb->adapter, &dvb->mdev); for (i = 0; i < NUM_FE; ++i) { ret = vidtv_bridge_probe_demod(dvb, i); @@ -495,6 +496,15 @@ static int vidtv_bridge_probe(struct platform_device *pdev) dvb->pdev = pdev; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + dvb->mdev.dev = &pdev->dev; + + strscpy(dvb->mdev.model, "vidtv", sizeof(dvb->mdev.model)); + strscpy(dvb->mdev.bus_info, "platform:vidtv", sizeof(dvb->mdev.bus_info)); + + media_device_init(&dvb->mdev); +#endif + ret = vidtv_bridge_dvb_init(dvb); if (ret < 0) goto err_dvb; @@ -504,20 +514,12 @@ static int vidtv_bridge_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dvb); #ifdef CONFIG_MEDIA_CONTROLLER_DVB - dvb->mdev.dev = &pdev->dev; - - strscpy(dvb->mdev.model, "vidtv", sizeof(dvb->mdev.model)); - strscpy(dvb->mdev.bus_info, "platform:vidtv", sizeof(dvb->mdev.bus_info)); - - media_device_init(&dvb->mdev); ret = media_device_register(&dvb->mdev); if (ret) { dev_err(dvb->mdev.dev, "media device register failed (err=%d)\n", ret); goto err_media_device_register; } - - dvb_register_media_controller(&dvb->adapter, &dvb->mdev); #endif /* CONFIG_MEDIA_CONTROLLER_DVB */ dev_info(&pdev->dev, "Successfully initialized vidtv!\n"); -- cgit v1.2.3 From 063b811f34650bf88e24998eb9c094607cb3b53e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 1 Feb 2021 09:40:56 +0100 Subject: media: uvc: strncpy -> strscpy The use of strncpy is discouraged, use strscpy instead. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 1abc122a0977..30ef2a3110f7 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1553,7 +1553,7 @@ static int uvc_gpio_parse(struct uvc_device *dev) unit->gpio.bmControls[0] = 1; unit->get_cur = uvc_gpio_get_cur; unit->get_info = uvc_gpio_get_info; - strncpy(unit->name, "GPIO", sizeof(unit->name) - 1); + strscpy(unit->name, "GPIO", sizeof(unit->name)); list_add_tail(&unit->list, &dev->entities); -- cgit v1.2.3 From ea12d248b0966d80f478a08e6138c6cb9b3883aa Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 13 Jan 2021 11:05:17 +0100 Subject: media: i2c: fix spelling mistakes: "enpoint" -> "endpoint" There are two spelling mistakes in dev_err messages. Fix these. Signed-off-by: Colin Ian King Reviewed-by: Paul Kocialkowski Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5648.c | 2 +- drivers/media/i2c/ov8865.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c index 110190b322e2..eab7839ea093 100644 --- a/drivers/media/i2c/ov5648.c +++ b/drivers/media/i2c/ov5648.c @@ -2454,7 +2454,7 @@ static int ov5648_probe(struct i2c_client *client) handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); if (!handle) { - dev_err(dev, "unable to find enpoint node\n"); + dev_err(dev, "unable to find endpoint node\n"); return -EINVAL; } diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index b2099e38d0af..36a60fbc211d 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -2799,7 +2799,7 @@ static int ov8865_probe(struct i2c_client *client) handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); if (!handle) { - dev_err(dev, "unable to find enpoint node\n"); + dev_err(dev, "unable to find endpoint node\n"); return -EINVAL; } -- cgit v1.2.3 From 7e7618579dcde5b2e533a6f53ae7e26d3ddcc679 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 15 Jan 2021 21:23:57 +0100 Subject: media: v4l2-async: Remove V4L2_ASYNC_MATCH_DEVNAME The last user for this type of match was the soc-camera/sh_mobile_csi2 driver, which was removed in v4.9. If the support is ever needed, it can always be restored. [Sakari Ailus: Also drop DEVNAME from debug prints recently added.] Signed-off-by: Ezequiel Garcia Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 41 ------------------------------------ include/media/v4l2-async.h | 25 ---------------------- 2 files changed, 66 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 64643b169783..221feb9a8f7b 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -66,12 +66,6 @@ static bool match_i2c(struct v4l2_async_notifier *notifier, #endif } -static bool match_devname(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) -{ - return !strcmp(asd->match.device_name, dev_name(sd->dev)); -} - static bool match_fwnode(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { @@ -164,9 +158,6 @@ v4l2_async_find_match(struct v4l2_async_notifier *notifier, list_for_each_entry(asd, ¬ifier->waiting, list) { /* bus_type has been verified valid before */ switch (asd->match_type) { - case V4L2_ASYNC_MATCH_DEVNAME: - match = match_devname; - break; case V4L2_ASYNC_MATCH_I2C: match = match_i2c; break; @@ -195,9 +186,6 @@ static bool asd_equal(struct v4l2_async_subdev *asd_x, return false; switch (asd_x->match_type) { - case V4L2_ASYNC_MATCH_DEVNAME: - return strcmp(asd_x->match.device_name, - asd_y->match.device_name) == 0; case V4L2_ASYNC_MATCH_I2C: return asd_x->match.i2c.adapter_id == asd_y->match.i2c.adapter_id && @@ -464,7 +452,6 @@ static int v4l2_async_notifier_asd_valid(struct v4l2_async_notifier *notifier, return -EINVAL; switch (asd->match_type) { - case V4L2_ASYNC_MATCH_DEVNAME: case V4L2_ASYNC_MATCH_I2C: case V4L2_ASYNC_MATCH_FWNODE: if (v4l2_async_notifier_has_async_subdev(notifier, asd, @@ -718,31 +705,6 @@ v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, } EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_i2c_subdev); -struct v4l2_async_subdev * -v4l2_async_notifier_add_devname_subdev(struct v4l2_async_notifier *notifier, - const char *device_name, - unsigned int asd_struct_size) -{ - struct v4l2_async_subdev *asd; - int ret; - - asd = kzalloc(asd_struct_size, GFP_KERNEL); - if (!asd) - return ERR_PTR(-ENOMEM); - - asd->match_type = V4L2_ASYNC_MATCH_DEVNAME; - asd->match.device_name = device_name; - - ret = v4l2_async_notifier_add_subdev(notifier, asd); - if (ret) { - kfree(asd); - return ERR_PTR(ret); - } - - return asd; -} -EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_devname_subdev); - int v4l2_async_register_subdev(struct v4l2_subdev *sd) { struct v4l2_async_notifier *subdev_notifier; @@ -841,9 +803,6 @@ static void print_waiting_subdev(struct seq_file *s, struct v4l2_async_subdev *asd) { switch (asd->match_type) { - case V4L2_ASYNC_MATCH_DEVNAME: - seq_printf(s, " [devname] dev=%s\n", asd->match.device_name); - break; case V4L2_ASYNC_MATCH_I2C: seq_printf(s, " [i2c] dev=%d-%04x\n", asd->match.i2c.adapter_id, asd->match.i2c.address); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 26296d4cb660..0ddc06e36c08 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -22,7 +22,6 @@ struct v4l2_async_notifier; * enum v4l2_async_match_type - type of asynchronous subdevice logic to be used * in order to identify a match * - * @V4L2_ASYNC_MATCH_DEVNAME: Match will use the device name * @V4L2_ASYNC_MATCH_I2C: Match will check for I2C adapter ID and address * @V4L2_ASYNC_MATCH_FWNODE: Match will use firmware node * @@ -30,7 +29,6 @@ struct v4l2_async_notifier; * algorithm that will be used to match an asynchronous device. */ enum v4l2_async_match_type { - V4L2_ASYNC_MATCH_DEVNAME, V4L2_ASYNC_MATCH_I2C, V4L2_ASYNC_MATCH_FWNODE, }; @@ -43,9 +41,6 @@ enum v4l2_async_match_type { * @match.fwnode: * pointer to &struct fwnode_handle to be matched. * Used if @match_type is %V4L2_ASYNC_MATCH_FWNODE. - * @match.device_name: - * string containing the device name to be matched. - * Used if @match_type is %V4L2_ASYNC_MATCH_DEVNAME. * @match.i2c: embedded struct with I2C parameters to be matched. * Both @match.i2c.adapter_id and @match.i2c.address * should be matched. @@ -69,7 +64,6 @@ struct v4l2_async_subdev { enum v4l2_async_match_type match_type; union { struct fwnode_handle *fwnode; - const char *device_name; struct { int adapter_id; unsigned short address; @@ -218,25 +212,6 @@ v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, int adapter_id, unsigned short address, unsigned int asd_struct_size); -/** - * v4l2_async_notifier_add_devname_subdev - Allocate and add a device-name - * async subdev to the notifier's master asd_list. - * - * @notifier: pointer to &struct v4l2_async_notifier - * @device_name: device name string to be matched - * @asd_struct_size: size of the driver's async sub-device struct, including - * sizeof(struct v4l2_async_subdev). The &struct - * v4l2_async_subdev shall be the first member of - * the driver's async sub-device struct, i.e. both - * begin at the same memory address. - * - * Same as above but for device-name matched sub-devices. - */ -struct v4l2_async_subdev * -v4l2_async_notifier_add_devname_subdev(struct v4l2_async_notifier *notifier, - const char *device_name, - unsigned int asd_struct_size); - /** * v4l2_async_notifier_register - registers a subdevice asynchronous notifier * -- cgit v1.2.3 From 0e34fe5beacef8d6cbec5c8486309871bb18e7d5 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 11 Jan 2021 15:54:36 +0100 Subject: media: ipu3-cio2: Do not zero reserved fields Core code already clears reserved fields of struct v4l2_pix_format_mplane, check commit 4e1e0eb0e074 ("media: v4l2-ioctl: Zero v4l2_plane_pix_format reserved fields"). Signed-off-by: Ricardo Ribalda Reviewed-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c index d13d64ae342e..a99ed5b7cd80 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c @@ -1094,12 +1094,9 @@ static int cio2_v4l2_try_fmt(struct file *file, void *fh, struct v4l2_format *f) mpix->pixelformat = fmt->fourcc; mpix->colorspace = V4L2_COLORSPACE_RAW; mpix->field = V4L2_FIELD_NONE; - memset(mpix->reserved, 0, sizeof(mpix->reserved)); mpix->plane_fmt[0].bytesperline = cio2_bytesperline(mpix->width); mpix->plane_fmt[0].sizeimage = mpix->plane_fmt[0].bytesperline * mpix->height; - memset(mpix->plane_fmt[0].reserved, 0, - sizeof(mpix->plane_fmt[0].reserved)); /* use default */ mpix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; -- cgit v1.2.3 From c432147c771d5efe00d0e96ceb7779636cd31300 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 11 Jan 2021 15:54:45 +0100 Subject: media: staging/intel-ipu3: Do not zero reserved fields Core code already clears reserved fields of struct v4l2_pix_format_mplane, check commit 4e1e0eb0e074 ("media: v4l2-ioctl: Zero v4l2_plane_pix_format reserved fields"). Signed-off-by: Ricardo Ribalda Reviewed-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/ipu3-v4l2.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index 4dc8d9165f63..60aa02eb7d2a 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -773,9 +773,6 @@ static int imgu_try_fmt(struct file *file, void *fh, struct v4l2_format *f) pixm->pixelformat = fmt->fourcc; - memset(pixm->plane_fmt[0].reserved, 0, - sizeof(pixm->plane_fmt[0].reserved)); - return 0; } -- cgit v1.2.3 From e88ccf09e79cf33cac40316ba69c820d9eebc82b Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 18 Jan 2021 09:14:46 +0100 Subject: media: i2c: max9286: fix access to unallocated memory The asd allocated with v4l2_async_notifier_add_fwnode_subdev() must be of size max9286_asd, otherwise access to max9286_asd->source will go to unallocated memory. Fixes: 86d37bf31af6 ("media: i2c: max9286: Allocate v4l2_async_subdev dynamically") Signed-off-by: Tomi Valkeinen Cc: stable@vger.kernel.org # v5.10+ Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham Tested-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/max9286.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index c82c1493e099..b1e2476d3c9e 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -580,7 +580,7 @@ static int max9286_v4l2_notifier_register(struct max9286_priv *priv) asd = v4l2_async_notifier_add_fwnode_subdev(&priv->notifier, source->fwnode, - sizeof(*asd)); + sizeof(struct max9286_asd)); if (IS_ERR(asd)) { dev_err(dev, "Failed to add subdev for source %u: %ld", i, PTR_ERR(asd)); -- cgit v1.2.3 From 38a50230292f232852f4e648d5e0a1cfaf37081b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 Jan 2021 09:46:03 +0100 Subject: media: i2c: ov5648: remove unnecessary NULL check The "mode_index == ARRAY_SIZE(ov5648_modes)" check ensures that we exited the loop via a break statement so we know that "mode" must be valid. Delete this unnecessary NULL check. Signed-off-by: Dan Carpenter Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5648.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c index eab7839ea093..dfe38ab8224d 100644 --- a/drivers/media/i2c/ov5648.c +++ b/drivers/media/i2c/ov5648.c @@ -2338,7 +2338,7 @@ static int ov5648_enum_frame_interval(struct v4l2_subdev *subdev, } } - if (mode_index == ARRAY_SIZE(ov5648_modes) || !mode) + if (mode_index == ARRAY_SIZE(ov5648_modes)) return -EINVAL; switch (interval_enum->code) { -- cgit v1.2.3 From c1cf3d896d124e3e00794f9bfbde49f0fc279e3f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:45 +0100 Subject: media: v4l2-async: Clean v4l2_async_notifier_add_fwnode_remote_subdev Change v4l2_async_notifier_add_fwnode_remote_subdev semantics so it allocates the struct v4l2_async_subdev pointer. This makes the API consistent: the v4l2-async subdevice addition functions have now a unified usage model. This model is simpler, as it makes v4l2-async responsible for the allocation and release of the subdevice descriptor, and no longer something the driver has to worry about. On the user side, the change makes the API simpler for the drivers to use and less error-prone. Signed-off-by: Ezequiel Garcia Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Reviewed-by: Helen Koike Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 17 ++--- drivers/media/platform/omap3isp/isp.c | 79 +++++++++------------- .../media/platform/rockchip/rkisp1/rkisp1-dev.c | 15 ++-- drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c | 9 ++- drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h | 1 - drivers/media/platform/video-mux.c | 14 ++-- drivers/media/v4l2-core/v4l2-async.c | 24 +++---- drivers/staging/media/imx/imx-media-csi.c | 14 ++-- drivers/staging/media/imx/imx6-mipi-csi2.c | 19 ++---- drivers/staging/media/imx/imx7-media-csi.c | 16 ++--- drivers/staging/media/imx/imx7-mipi-csis.c | 15 ++-- include/media/v4l2-async.h | 15 ++-- 12 files changed, 96 insertions(+), 142 deletions(-) diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c index a99ed5b7cd80..0895c199de3e 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c @@ -1464,7 +1464,8 @@ static int cio2_parse_firmware(struct cio2_device *cio2) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - struct sensor_async_subdev *s_asd = NULL; + struct sensor_async_subdev *s_asd; + struct v4l2_async_subdev *asd; struct fwnode_handle *ep; ep = fwnode_graph_get_endpoint_by_id( @@ -1478,27 +1479,23 @@ static int cio2_parse_firmware(struct cio2_device *cio2) if (ret) goto err_parse; - s_asd = kzalloc(sizeof(*s_asd), GFP_KERNEL); - if (!s_asd) { - ret = -ENOMEM; + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &cio2->notifier, ep, sizeof(*s_asd)); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); goto err_parse; } + s_asd = container_of(asd, struct sensor_async_subdev, asd); s_asd->csi2.port = vep.base.port; s_asd->csi2.lanes = vep.bus.mipi_csi2.num_data_lanes; - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &cio2->notifier, ep, &s_asd->asd); - if (ret) - goto err_parse; - fwnode_handle_put(ep); continue; err_parse: fwnode_handle_put(ep); - kfree(s_asd); return ret; } diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index b1fc4518e275..1311b4996ece 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -2126,21 +2126,6 @@ static void isp_parse_of_csi1_endpoint(struct device *dev, buscfg->bus.ccp2.crc = 1; } -static int isp_alloc_isd(struct isp_async_subdev **isd, - struct isp_bus_cfg **buscfg) -{ - struct isp_async_subdev *__isd; - - __isd = kzalloc(sizeof(*__isd), GFP_KERNEL); - if (!__isd) - return -ENOMEM; - - *isd = __isd; - *buscfg = &__isd->bus; - - return 0; -} - static struct { u32 phy; u32 csi2_if; @@ -2156,7 +2141,7 @@ static int isp_parse_of_endpoints(struct isp_device *isp) { struct fwnode_handle *ep; struct isp_async_subdev *isd = NULL; - struct isp_bus_cfg *buscfg; + struct v4l2_async_subdev *asd; unsigned int i; ep = fwnode_graph_get_endpoint_by_id( @@ -2174,20 +2159,15 @@ static int isp_parse_of_endpoints(struct isp_device *isp) ret = v4l2_fwnode_endpoint_parse(ep, &vep); if (!ret) { - ret = isp_alloc_isd(&isd, &buscfg); - if (ret) - return ret; - } - - if (!ret) { - isp_parse_of_parallel_endpoint(isp->dev, &vep, buscfg); - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &isp->notifier, ep, &isd->asd); + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &isp->notifier, ep, sizeof(*isd)); + if (!IS_ERR(asd)) { + isd = container_of(asd, struct isp_async_subdev, asd); + isp_parse_of_parallel_endpoint(isp->dev, &vep, &isd->bus); + } } fwnode_handle_put(ep); - if (ret) - kfree(isd); } for (i = 0; i < ARRAY_SIZE(isp_bus_interfaces); i++) { @@ -2206,15 +2186,8 @@ static int isp_parse_of_endpoints(struct isp_device *isp) dev_dbg(isp->dev, "parsing serial interface %u, node %pOF\n", i, to_of_node(ep)); - ret = isp_alloc_isd(&isd, &buscfg); - if (ret) - return ret; - ret = v4l2_fwnode_endpoint_parse(ep, &vep); - if (!ret) { - buscfg->interface = isp_bus_interfaces[i].csi2_if; - isp_parse_of_csi2_endpoint(isp->dev, &vep, buscfg); - } else if (ret == -ENXIO) { + if (ret == -ENXIO) { vep = (struct v4l2_fwnode_endpoint) { .bus_type = V4L2_MBUS_CSI1 }; ret = v4l2_fwnode_endpoint_parse(ep, &vep); @@ -2224,21 +2197,35 @@ static int isp_parse_of_endpoints(struct isp_device *isp) { .bus_type = V4L2_MBUS_CCP2 }; ret = v4l2_fwnode_endpoint_parse(ep, &vep); } - if (!ret) { - buscfg->interface = - isp_bus_interfaces[i].csi1_if; - isp_parse_of_csi1_endpoint(isp->dev, &vep, - buscfg); - } } - if (!ret) - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &isp->notifier, ep, &isd->asd); + if (!ret) { + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &isp->notifier, ep, sizeof(*isd)); + + if (!IS_ERR(asd)) { + isd = container_of(asd, struct isp_async_subdev, asd); + + switch (vep.bus_type) { + case V4L2_MBUS_CSI2_DPHY: + isd->bus.interface = + isp_bus_interfaces[i].csi2_if; + isp_parse_of_csi2_endpoint(isp->dev, &vep, &isd->bus); + break; + case V4L2_MBUS_CSI1: + case V4L2_MBUS_CCP2: + isd->bus.interface = + isp_bus_interfaces[i].csi1_if; + isp_parse_of_csi1_endpoint(isp->dev, &vep, + &isd->bus); + break; + default: + break; + } + } + } fwnode_handle_put(ep); - if (ret) - kfree(isd); } return 0; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index 68da1eed753d..daa1b6d22436 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -252,6 +252,7 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1) .bus_type = V4L2_MBUS_CSI2_DPHY }; struct rkisp1_sensor_async *rk_asd = NULL; + struct v4l2_async_subdev *asd; struct fwnode_handle *ep; ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(rkisp1->dev), @@ -264,21 +265,18 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1) if (ret) goto err_parse; - rk_asd = kzalloc(sizeof(*rk_asd), GFP_KERNEL); - if (!rk_asd) { - ret = -ENOMEM; + asd = v4l2_async_notifier_add_fwnode_remote_subdev(ntf, ep, + sizeof(*rk_asd)); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); goto err_parse; } + rk_asd = container_of(asd, struct rkisp1_sensor_async, asd); rk_asd->mbus_type = vep.bus_type; rk_asd->mbus_flags = vep.bus.mipi_csi2.flags; rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes; - ret = v4l2_async_notifier_add_fwnode_remote_subdev(ntf, ep, - &rk_asd->asd); - if (ret) - goto err_parse; - dev_dbg(rkisp1->dev, "registered ep id %d with %d lanes\n", vep.base.id, rk_asd->lanes); @@ -289,7 +287,6 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1) continue; err_parse: fwnode_handle_put(ep); - kfree(rk_asd); v4l2_async_notifier_cleanup(ntf); return ret; } diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index ec46cff80fdb..3f94b8c966f3 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -118,6 +118,7 @@ static int sun4i_csi_notifier_init(struct sun4i_csi *csi) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL, }; + struct v4l2_async_subdev *asd; struct fwnode_handle *ep; int ret; @@ -134,10 +135,12 @@ static int sun4i_csi_notifier_init(struct sun4i_csi *csi) csi->bus = vep.bus.parallel; - ret = v4l2_async_notifier_add_fwnode_remote_subdev(&csi->notifier, - ep, &csi->asd); - if (ret) + asd = v4l2_async_notifier_add_fwnode_remote_subdev(&csi->notifier, + ep, sizeof(*asd)); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); goto out; + } csi->notifier.ops = &sun4i_csi_notify_ops; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h index 0f67ff652c2e..a5f61ee0ec4d 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h @@ -139,7 +139,6 @@ struct sun4i_csi { struct v4l2_mbus_framefmt subdev_fmt; /* V4L2 Async variables */ - struct v4l2_async_subdev asd; struct v4l2_async_notifier notifier; struct v4l2_subdev *src_subdev; int src_pad; diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index 53570250a25d..7b280dfca727 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -370,19 +370,13 @@ static int video_mux_async_register(struct video_mux *vmux, if (!ep) continue; - asd = kzalloc(sizeof(*asd), GFP_KERNEL); - if (!asd) { - fwnode_handle_put(ep); - return -ENOMEM; - } - - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &vmux->notifier, ep, asd); + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &vmux->notifier, ep, sizeof(*asd)); fwnode_handle_put(ep); - if (ret) { - kfree(asd); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); /* OK if asd already exists */ if (ret != -EEXIST) return ret; diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 221feb9a8f7b..ed603ae63cad 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -656,26 +656,26 @@ v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, } EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_fwnode_subdev); -int +struct v4l2_async_subdev * v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, struct fwnode_handle *endpoint, - struct v4l2_async_subdev *asd) + unsigned int asd_struct_size) { + struct v4l2_async_subdev *asd; struct fwnode_handle *remote; - int ret; remote = fwnode_graph_get_remote_port_parent(endpoint); if (!remote) - return -ENOTCONN; + return ERR_PTR(-ENOTCONN); - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode = remote; - - ret = v4l2_async_notifier_add_subdev(notif, asd); - if (ret) - fwnode_handle_put(remote); - - return ret; + asd = v4l2_async_notifier_add_fwnode_subdev(notif, remote, + asd_struct_size); + /* + * Calling v4l2_async_notifier_add_fwnode_subdev grabs a refcount, + * so drop the one we got in fwnode_graph_get_remote_port_parent. + */ + fwnode_handle_put(remote); + return asd; } EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_fwnode_remote_subdev); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index db77fef07654..6344389e6afa 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1922,19 +1922,13 @@ static int imx_csi_async_register(struct csi_priv *priv) port, 0, FWNODE_GRAPH_ENDPOINT_NEXT); if (ep) { - asd = kzalloc(sizeof(*asd), GFP_KERNEL); - if (!asd) { - fwnode_handle_put(ep); - return -ENOMEM; - } - - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &priv->notifier, ep, asd); + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &priv->notifier, ep, sizeof(*asd)); fwnode_handle_put(ep); - if (ret) { - kfree(asd); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); /* OK if asd already exists */ if (ret != -EEXIST) return ret; diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index 6b58899dcefe..a79ab38158e2 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -641,7 +641,7 @@ static int csi2_async_register(struct csi2_dev *csi2) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY, }; - struct v4l2_async_subdev *asd = NULL; + struct v4l2_async_subdev *asd; struct fwnode_handle *ep; int ret; @@ -661,19 +661,13 @@ static int csi2_async_register(struct csi2_dev *csi2) dev_dbg(csi2->dev, "data lanes: %d\n", vep.bus.mipi_csi2.num_data_lanes); dev_dbg(csi2->dev, "flags: 0x%08x\n", vep.bus.mipi_csi2.flags); - asd = kzalloc(sizeof(*asd), GFP_KERNEL); - if (!asd) { - ret = -ENOMEM; - goto err_parse; - } - - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &csi2->notifier, ep, asd); - if (ret) - goto err_parse; - + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &csi2->notifier, ep, sizeof(*asd)); fwnode_handle_put(ep); + if (IS_ERR(asd)) + return PTR_ERR(asd); + csi2->notifier.ops = &csi2_notify_ops; ret = v4l2_async_subdev_notifier_register(&csi2->sd, @@ -685,7 +679,6 @@ static int csi2_async_register(struct csi2_dev *csi2) err_parse: fwnode_handle_put(ep); - kfree(asd); return ret; } diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c index ac52b1daf991..6c59485291ca 100644 --- a/drivers/staging/media/imx/imx7-media-csi.c +++ b/drivers/staging/media/imx/imx7-media-csi.c @@ -1191,7 +1191,7 @@ static const struct v4l2_async_notifier_operations imx7_csi_notify_ops = { static int imx7_csi_async_register(struct imx7_csi *csi) { - struct v4l2_async_subdev *asd = NULL; + struct v4l2_async_subdev *asd; struct fwnode_handle *ep; int ret; @@ -1200,19 +1200,13 @@ static int imx7_csi_async_register(struct imx7_csi *csi) ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0, FWNODE_GRAPH_ENDPOINT_NEXT); if (ep) { - asd = kzalloc(sizeof(*asd), GFP_KERNEL); - if (!asd) { - fwnode_handle_put(ep); - return -ENOMEM; - } - - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &csi->notifier, ep, asd); + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &csi->notifier, ep, sizeof(*asd)); fwnode_handle_put(ep); - if (ret) { - kfree(asd); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); /* OK if asd already exists */ if (ret != -EEXIST) return ret; diff --git a/drivers/staging/media/imx/imx7-mipi-csis.c b/drivers/staging/media/imx/imx7-mipi-csis.c index 7612993cc1d6..1ba77e629e5b 100644 --- a/drivers/staging/media/imx/imx7-mipi-csis.c +++ b/drivers/staging/media/imx/imx7-mipi-csis.c @@ -1004,7 +1004,7 @@ static int mipi_csis_async_register(struct csi_state *state) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY, }; - struct v4l2_async_subdev *asd = NULL; + struct v4l2_async_subdev *asd; struct fwnode_handle *ep; int ret; @@ -1024,17 +1024,13 @@ static int mipi_csis_async_register(struct csi_state *state) dev_dbg(state->dev, "data lanes: %d\n", state->bus.num_data_lanes); dev_dbg(state->dev, "flags: 0x%08x\n", state->bus.flags); - asd = kzalloc(sizeof(*asd), GFP_KERNEL); - if (!asd) { - ret = -ENOMEM; + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &state->notifier, ep, sizeof(*asd)); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); goto err_parse; } - ret = v4l2_async_notifier_add_fwnode_remote_subdev( - &state->notifier, ep, asd); - if (ret) - goto err_parse; - fwnode_handle_put(ep); state->notifier.ops = &mipi_csis_notify_ops; @@ -1048,7 +1044,6 @@ static int mipi_csis_async_register(struct csi_state *state) err_parse: fwnode_handle_put(ep); - kfree(asd); return ret; } diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 0ddc06e36c08..8815e233677e 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -174,9 +174,11 @@ v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, * * @notif: pointer to &struct v4l2_async_notifier * @endpoint: local endpoint pointing to the remote sub-device to be matched - * @asd: Async sub-device struct allocated by the caller. The &struct - * v4l2_async_subdev shall be the first member of the driver's async - * sub-device struct, i.e. both begin at the same memory address. + * @asd_struct_size: size of the driver's async sub-device struct, including + * sizeof(struct v4l2_async_subdev). The &struct + * v4l2_async_subdev shall be the first member of + * the driver's async sub-device struct, i.e. both + * begin at the same memory address. * * Gets the remote endpoint of a given local endpoint, set it up for fwnode * matching and adds the async sub-device to the notifier's @asd_list. The @@ -184,13 +186,12 @@ v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, * notifier cleanup time. * * This is just like @v4l2_async_notifier_add_fwnode_subdev, but with the - * exception that the fwnode refers to a local endpoint, not the remote one, and - * the function relies on the caller to allocate the async sub-device struct. + * exception that the fwnode refers to a local endpoint, not the remote one. */ -int +struct v4l2_async_subdev * v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, struct fwnode_handle *endpoint, - struct v4l2_async_subdev *asd); + unsigned int asd_struct_size); /** * v4l2_async_notifier_add_i2c_subdev - Allocate and add an i2c async -- cgit v1.2.3 From d6701f13bd0747a78bb0b78dd45344e475afd512 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:46 +0100 Subject: media: atmel: Use v4l2_async_notifier_add_fwnode_remote_subdev The use of v4l2_async_notifier_add_subdev will be discouraged. Drivers are instead encouraged to use a helper such as v4l2_async_notifier_add_fwnode_remote_subdev. This fixes a misuse of the API, as v4l2_async_notifier_add_subdev should get a kmalloc'ed struct v4l2_async_subdev, removing some boilerplate code while at it. Signed-off-by: Ezequiel Garcia Reviewed-by: Jacopo Mondi Reviewed-by: Helen Koike Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/atmel/atmel-isc.h | 1 + drivers/media/platform/atmel/atmel-isi.c | 46 +++++++----------------- drivers/media/platform/atmel/atmel-sama5d2-isc.c | 44 ++++++++--------------- 3 files changed, 29 insertions(+), 62 deletions(-) diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h index 24b784b893d6..fab8eca58d93 100644 --- a/drivers/media/platform/atmel/atmel-isc.h +++ b/drivers/media/platform/atmel/atmel-isc.h @@ -41,6 +41,7 @@ struct isc_buffer { struct isc_subdev_entity { struct v4l2_subdev *sd; struct v4l2_async_subdev *asd; + struct device_node *epn; struct v4l2_async_notifier notifier; u32 pfe_cfg0; diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index d74aa73f26be..c1a6dd7af002 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -70,7 +70,6 @@ struct frame_buffer { struct isi_graph_entity { struct device_node *node; - struct v4l2_async_subdev asd; struct v4l2_subdev *subdev; }; @@ -1136,45 +1135,26 @@ static const struct v4l2_async_notifier_operations isi_graph_notify_ops = { .complete = isi_graph_notify_complete, }; -static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node) -{ - struct device_node *ep = NULL; - struct device_node *remote; - - ep = of_graph_get_next_endpoint(node, ep); - if (!ep) - return -EINVAL; - - remote = of_graph_get_remote_port_parent(ep); - of_node_put(ep); - if (!remote) - return -EINVAL; - - /* Remote node to connect */ - isi->entity.node = remote; - isi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - isi->entity.asd.match.fwnode = of_fwnode_handle(remote); - return 0; -} - static int isi_graph_init(struct atmel_isi *isi) { + struct v4l2_async_subdev *asd; + struct device_node *ep; int ret; - /* Parse the graph to extract a list of subdevice DT nodes. */ - ret = isi_graph_parse(isi, isi->dev->of_node); - if (ret < 0) { - dev_err(isi->dev, "Graph parsing failed\n"); - return ret; - } + ep = of_graph_get_next_endpoint(isi->dev->of_node, NULL); + if (!ep) + return -EINVAL; v4l2_async_notifier_init(&isi->notifier); - ret = v4l2_async_notifier_add_subdev(&isi->notifier, &isi->entity.asd); - if (ret) { - of_node_put(isi->entity.node); - return ret; - } + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &isi->notifier, + of_fwnode_handle(ep), + sizeof(*asd)); + of_node_put(ep); + + if (IS_ERR(asd)) + return PTR_ERR(asd); isi->notifier.ops = &isi_graph_notify_ops; diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c index a3304f49e499..9ee2cd194f93 100644 --- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c +++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c @@ -57,7 +57,7 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) { struct device_node *np = dev->of_node; - struct device_node *epn = NULL, *rem; + struct device_node *epn = NULL; struct isc_subdev_entity *subdev_entity; unsigned int flags; int ret; @@ -71,17 +71,9 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) if (!epn) return 0; - rem = of_graph_get_remote_port_parent(epn); - if (!rem) { - dev_notice(dev, "Remote device at %pOF not found\n", - epn); - continue; - } - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), &v4l2_epn); if (ret) { - of_node_put(rem); ret = -EINVAL; dev_err(dev, "Could not parse the endpoint\n"); break; @@ -90,21 +82,10 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), GFP_KERNEL); if (!subdev_entity) { - of_node_put(rem); - ret = -ENOMEM; - break; - } - - /* asd will be freed by the subsystem once it's added to the - * notifier list - */ - subdev_entity->asd = kzalloc(sizeof(*subdev_entity->asd), - GFP_KERNEL); - if (!subdev_entity->asd) { - of_node_put(rem); ret = -ENOMEM; break; } + subdev_entity->epn = epn; flags = v4l2_epn.bus.parallel.flags; @@ -121,12 +102,10 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | ISC_PFE_CFG0_CCIR656; - subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - subdev_entity->asd->match.fwnode = of_fwnode_handle(rem); list_add_tail(&subdev_entity->list, &isc->subdev_entities); } - of_node_put(epn); + return ret; } @@ -228,13 +207,20 @@ static int atmel_isc_probe(struct platform_device *pdev) } list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { + struct v4l2_async_subdev *asd; + v4l2_async_notifier_init(&subdev_entity->notifier); - ret = v4l2_async_notifier_add_subdev(&subdev_entity->notifier, - subdev_entity->asd); - if (ret) { - fwnode_handle_put(subdev_entity->asd->match.fwnode); - kfree(subdev_entity->asd); + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &subdev_entity->notifier, + of_fwnode_handle(subdev_entity->epn), + sizeof(*asd)); + + of_node_put(subdev_entity->epn); + subdev_entity->epn = NULL; + + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); goto cleanup_subdev; } -- cgit v1.2.3 From 49cff8db6c220e85092363c231ee609d3cd147c9 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:47 +0100 Subject: media: stm32: Use v4l2_async_notifier_add_fwnode_remote_subdev The use of v4l2_async_notifier_add_subdev will be discouraged. Drivers are instead encouraged to use a helper such as v4l2_async_notifier_add_fwnode_remote_subdev. This fixes a misuse of the API, as v4l2_async_notifier_add_subdev should get a kmalloc'ed struct v4l2_async_subdev, removing some boilerplate code while at it. Use the appropriate helper v4l2_async_notifier_add_fwnode_remote_subdev, which handles the needed setup, instead of open-coding it. This results in removal of the now unneeded driver-specific state struct dcmi_graph_entity, keeping track of just the source subdevice. Signed-off-by: Ezequiel Garcia Reviewed-by: Jacopo Mondi Reviewed-by: Helen Koike Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 86 +++++++++++-------------------- 1 file changed, 30 insertions(+), 56 deletions(-) diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index b745f1342c2e..142f63d07dcd 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -99,13 +99,6 @@ enum state { #define OVERRUN_ERROR_THRESHOLD 3 -struct dcmi_graph_entity { - struct v4l2_async_subdev asd; - - struct device_node *remote_node; - struct v4l2_subdev *source; -}; - struct dcmi_format { u32 fourcc; u32 mbus_code; @@ -139,7 +132,7 @@ struct stm32_dcmi { struct v4l2_device v4l2_dev; struct video_device *vdev; struct v4l2_async_notifier notifier; - struct dcmi_graph_entity entity; + struct v4l2_subdev *source; struct v4l2_format fmt; struct v4l2_rect crop; bool do_crop; @@ -610,7 +603,7 @@ static int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi, struct v4l2_subdev_pad_config *pad_cfg, struct v4l2_subdev_format *format) { - struct media_entity *entity = &dcmi->entity.source->entity; + struct media_entity *entity = &dcmi->source->entity; struct v4l2_subdev *subdev; struct media_pad *sink_pad = NULL; struct media_pad *src_pad = NULL; @@ -1018,7 +1011,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, } v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); - ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt, + ret = v4l2_subdev_call(dcmi->source, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; @@ -1152,7 +1145,7 @@ static int dcmi_get_sensor_format(struct stm32_dcmi *dcmi, }; int ret; - ret = v4l2_subdev_call(dcmi->entity.source, pad, get_fmt, NULL, &fmt); + ret = v4l2_subdev_call(dcmi->source, pad, get_fmt, NULL, &fmt); if (ret) return ret; @@ -1181,7 +1174,7 @@ static int dcmi_set_sensor_format(struct stm32_dcmi *dcmi, } v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); - ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt, + ret = v4l2_subdev_call(dcmi->source, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; @@ -1204,7 +1197,7 @@ static int dcmi_get_sensor_bounds(struct stm32_dcmi *dcmi, /* * Get sensor bounds first */ - ret = v4l2_subdev_call(dcmi->entity.source, pad, get_selection, + ret = v4l2_subdev_call(dcmi->source, pad, get_selection, NULL, &bounds); if (!ret) *r = bounds.r; @@ -1385,7 +1378,7 @@ static int dcmi_enum_framesizes(struct file *file, void *fh, fse.code = sd_fmt->mbus_code; - ret = v4l2_subdev_call(dcmi->entity.source, pad, enum_frame_size, + ret = v4l2_subdev_call(dcmi->source, pad, enum_frame_size, NULL, &fse); if (ret) return ret; @@ -1402,7 +1395,7 @@ static int dcmi_g_parm(struct file *file, void *priv, { struct stm32_dcmi *dcmi = video_drvdata(file); - return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.source, p); + return v4l2_g_parm_cap(video_devdata(file), dcmi->source, p); } static int dcmi_s_parm(struct file *file, void *priv, @@ -1410,7 +1403,7 @@ static int dcmi_s_parm(struct file *file, void *priv, { struct stm32_dcmi *dcmi = video_drvdata(file); - return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.source, p); + return v4l2_s_parm_cap(video_devdata(file), dcmi->source, p); } static int dcmi_enum_frameintervals(struct file *file, void *fh, @@ -1432,7 +1425,7 @@ static int dcmi_enum_frameintervals(struct file *file, void *fh, fie.code = sd_fmt->mbus_code; - ret = v4l2_subdev_call(dcmi->entity.source, pad, + ret = v4l2_subdev_call(dcmi->source, pad, enum_frame_interval, NULL, &fie); if (ret) return ret; @@ -1452,7 +1445,7 @@ MODULE_DEVICE_TABLE(of, stm32_dcmi_of_match); static int dcmi_open(struct file *file) { struct stm32_dcmi *dcmi = video_drvdata(file); - struct v4l2_subdev *sd = dcmi->entity.source; + struct v4l2_subdev *sd = dcmi->source; int ret; if (mutex_lock_interruptible(&dcmi->lock)) @@ -1483,7 +1476,7 @@ unlock: static int dcmi_release(struct file *file) { struct stm32_dcmi *dcmi = video_drvdata(file); - struct v4l2_subdev *sd = dcmi->entity.source; + struct v4l2_subdev *sd = dcmi->source; bool fh_singular; int ret; @@ -1616,7 +1609,7 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi) { const struct dcmi_format *sd_fmts[ARRAY_SIZE(dcmi_formats)]; unsigned int num_fmts = 0, i, j; - struct v4l2_subdev *subdev = dcmi->entity.source; + struct v4l2_subdev *subdev = dcmi->source; struct v4l2_subdev_mbus_code_enum mbus_code = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; @@ -1675,7 +1668,7 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi) static int dcmi_framesizes_init(struct stm32_dcmi *dcmi) { unsigned int num_fsize = 0; - struct v4l2_subdev *subdev = dcmi->entity.source; + struct v4l2_subdev *subdev = dcmi->source; struct v4l2_subdev_frame_size_enum fse = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, .code = dcmi->sd_format->mbus_code, @@ -1727,14 +1720,13 @@ static int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier) * we search for the source subdevice * in order to expose it through V4L2 interface */ - dcmi->entity.source = - media_entity_to_v4l2_subdev(dcmi_find_source(dcmi)); - if (!dcmi->entity.source) { + dcmi->source = media_entity_to_v4l2_subdev(dcmi_find_source(dcmi)); + if (!dcmi->source) { dev_err(dcmi->dev, "Source subdevice not found\n"); return -ENODEV; } - dcmi->vdev->ctrl_handler = dcmi->entity.source->ctrl_handler; + dcmi->vdev->ctrl_handler = dcmi->source->ctrl_handler; ret = dcmi_formats_init(dcmi); if (ret) { @@ -1813,46 +1805,28 @@ static const struct v4l2_async_notifier_operations dcmi_graph_notify_ops = { .complete = dcmi_graph_notify_complete, }; -static int dcmi_graph_parse(struct stm32_dcmi *dcmi, struct device_node *node) -{ - struct device_node *ep = NULL; - struct device_node *remote; - - ep = of_graph_get_next_endpoint(node, ep); - if (!ep) - return -EINVAL; - - remote = of_graph_get_remote_port_parent(ep); - of_node_put(ep); - if (!remote) - return -EINVAL; - - /* Remote node to connect */ - dcmi->entity.remote_node = remote; - dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - dcmi->entity.asd.match.fwnode = of_fwnode_handle(remote); - return 0; -} - static int dcmi_graph_init(struct stm32_dcmi *dcmi) { + struct v4l2_async_subdev *asd; + struct device_node *ep; int ret; - /* Parse the graph to extract a list of subdevice DT nodes. */ - ret = dcmi_graph_parse(dcmi, dcmi->dev->of_node); - if (ret < 0) { - dev_err(dcmi->dev, "Failed to parse graph\n"); - return ret; + ep = of_graph_get_next_endpoint(dcmi->dev->of_node, NULL); + if (!ep) { + dev_err(dcmi->dev, "Failed to get next endpoint\n"); + return -EINVAL; } v4l2_async_notifier_init(&dcmi->notifier); - ret = v4l2_async_notifier_add_subdev(&dcmi->notifier, - &dcmi->entity.asd); - if (ret) { + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &dcmi->notifier, of_fwnode_handle(ep), sizeof(*asd)); + + of_node_put(ep); + + if (IS_ERR(asd)) { dev_err(dcmi->dev, "Failed to add subdev notifier\n"); - of_node_put(dcmi->entity.remote_node); - return ret; + return PTR_ERR(asd); } dcmi->notifier.ops = &dcmi_graph_notify_ops; -- cgit v1.2.3 From 3a2822bfe45c50abd9f76a8547a77a1f6a0e8c8d Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:48 +0100 Subject: media: exynos4-is: Use v4l2_async_notifier_add_fwnode_remote_subdev The use of v4l2_async_notifier_add_subdev will be discouraged. Drivers are instead encouraged to use a helper such as v4l2_async_notifier_add_fwnode_remote_subdev. This fixes a misuse of the API, as v4l2_async_notifier_add_subdev should get a kmalloc'ed struct v4l2_async_subdev, removing some boilerplate code while at it. Use the appropriate helper v4l2_async_notifier_add_fwnode_remote_subdev, which handles the needed setup, instead of open-coding it. Signed-off-by: Ezequiel Garcia Reviewed-by: Jacopo Mondi Reviewed-by: Helen Koike Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/media-dev.c | 24 +++++++++++++----------- drivers/media/platform/exynos4-is/media-dev.h | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index e636c33e847b..f4687b0cbd65 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -401,6 +401,7 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, int index = fmd->num_sensors; struct fimc_source_info *pd = &fmd->sensor[index].pdata; struct device_node *rem, *np; + struct v4l2_async_subdev *asd; struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; int ret; @@ -418,10 +419,10 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, pd->mux_id = (endpoint.base.port - 1) & 0x1; rem = of_graph_get_remote_port_parent(ep); - of_node_put(ep); if (rem == NULL) { v4l2_info(&fmd->v4l2_dev, "Remote device at %pOF not found\n", ep); + of_node_put(ep); return 0; } @@ -450,6 +451,7 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, * checking parent's node name. */ np = of_get_parent(rem); + of_node_put(rem); if (of_node_name_eq(np, "i2c-isp")) pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; @@ -458,20 +460,19 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, of_node_put(np); if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) { - of_node_put(rem); + of_node_put(ep); return -EINVAL; } - fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - fmd->sensor[index].asd.match.fwnode = of_fwnode_handle(rem); + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &fmd->subdev_notifier, of_fwnode_handle(ep), sizeof(*asd)); - ret = v4l2_async_notifier_add_subdev(&fmd->subdev_notifier, - &fmd->sensor[index].asd); - if (ret) { - of_node_put(rem); - return ret; - } + of_node_put(ep); + + if (IS_ERR(asd)) + return PTR_ERR(asd); + fmd->sensor[index].asd = asd; fmd->num_sensors++; return 0; @@ -1381,7 +1382,8 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier, /* Find platform data for this sensor subdev */ for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++) - if (fmd->sensor[i].asd.match.fwnode == + if (fmd->sensor[i].asd && + fmd->sensor[i].asd->match.fwnode == of_fwnode_handle(subdev->dev->of_node)) si = &fmd->sensor[i]; diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index 9447fafe23c6..a3876d668ea6 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -83,7 +83,7 @@ struct fimc_camclk_info { */ struct fimc_sensor_info { struct fimc_source_info pdata; - struct v4l2_async_subdev asd; + struct v4l2_async_subdev *asd; struct v4l2_subdev *subdev; struct fimc_dev *host; }; -- cgit v1.2.3 From 5bbefdefe8a6927e0cdf68bf02f677e06ccc32b6 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:49 +0100 Subject: media: st-mipid02: Use v4l2_async_notifier_add_fwnode_remote_subdev The use of v4l2_async_notifier_add_subdev will be discouraged. Drivers are instead encouraged to use a helper such as v4l2_async_notifier_add_fwnode_remote_subdev. This fixes a misuse of the API, as v4l2_async_notifier_add_subdev should get a kmalloc'ed struct v4l2_async_subdev, removing some boilerplate code while at it. Use the appropriate helper v4l2_async_notifier_add_fwnode_remote_subdev, which handles the needed setup, instead of open-coding it. Signed-off-by: Ezequiel Garcia Reviewed-by: Jacopo Mondi Reviewed-by: Helen Koike Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/st-mipid02.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index 003ba22334cd..bb32a5278a4e 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -92,7 +92,6 @@ struct mipid02_dev { u64 link_frequency; struct v4l2_fwnode_endpoint tx; /* remote source */ - struct v4l2_async_subdev asd; struct v4l2_async_notifier notifier; struct v4l2_subdev *s_subdev; /* registers */ @@ -844,6 +843,7 @@ static int mipid02_parse_rx_ep(struct mipid02_dev *bridge) { struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; struct i2c_client *client = bridge->i2c_client; + struct v4l2_async_subdev *asd; struct device_node *ep_node; int ret; @@ -875,18 +875,17 @@ static int mipid02_parse_rx_ep(struct mipid02_dev *bridge) bridge->rx = ep; /* register async notifier so we get noticed when sensor is connected */ - bridge->asd.match.fwnode = - fwnode_graph_get_remote_port_parent(of_fwnode_handle(ep_node)); - bridge->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + v4l2_async_notifier_init(&bridge->notifier); + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &bridge->notifier, + of_fwnode_handle(ep_node), + sizeof(*asd)); of_node_put(ep_node); - v4l2_async_notifier_init(&bridge->notifier); - ret = v4l2_async_notifier_add_subdev(&bridge->notifier, &bridge->asd); - if (ret) { - dev_err(&client->dev, "fail to register asd to notifier %d", - ret); - fwnode_handle_put(bridge->asd.match.fwnode); - return ret; + if (IS_ERR(asd)) { + dev_err(&client->dev, "fail to register asd to notifier %ld", + PTR_ERR(asd)); + return PTR_ERR(asd); } bridge->notifier.ops = &mipid02_notifier_ops; -- cgit v1.2.3 From 88367b1522bf7e777ed86bb9396c645296ee4ecc Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:50 +0100 Subject: media: cadence: Use v4l2_async_notifier_add_fwnode_remote_subdev The use of v4l2_async_notifier_add_subdev will be discouraged. Drivers are instead encouraged to use a helper such as v4l2_async_notifier_add_fwnode_remote_subdev. This fixes a misuse of the API, as v4l2_async_notifier_add_subdev should get a kmalloc'ed struct v4l2_async_subdev, removing some boilerplate code while at it. Use the appropriate helper v4l2_async_notifier_add_fwnode_remote_subdev, which handles the needed setup, instead of open-coding it. Signed-off-by: Ezequiel Garcia Reviewed-by: Jacopo Mondi Reviewed-by: Helen Koike Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/cadence/cdns-csi2rx.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index be9ec59774d6..7d299cacef8c 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -81,7 +81,6 @@ struct csi2rx_priv { struct media_pad pads[CSI2RX_PAD_MAX]; /* Remote source */ - struct v4l2_async_subdev asd; struct v4l2_subdev *source_subdev; int source_pad; }; @@ -362,6 +361,7 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx) { struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; + struct v4l2_async_subdev *asd; struct fwnode_handle *fwh; struct device_node *ep; int ret; @@ -395,17 +395,13 @@ static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx) return -EINVAL; } - csi2rx->asd.match.fwnode = fwnode_graph_get_remote_port_parent(fwh); - csi2rx->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - of_node_put(ep); - v4l2_async_notifier_init(&csi2rx->notifier); - ret = v4l2_async_notifier_add_subdev(&csi2rx->notifier, &csi2rx->asd); - if (ret) { - fwnode_handle_put(csi2rx->asd.match.fwnode); - return ret; - } + asd = v4l2_async_notifier_add_fwnode_remote_subdev(&csi2rx->notifier, + fwh, sizeof(*asd)); + of_node_put(ep); + if (IS_ERR(asd)) + return PTR_ERR(asd); csi2rx->notifier.ops = &csi2rx_notifier_ops; -- cgit v1.2.3 From 50fe0de0fedbc259eb79951f6912b78795dcaeaa Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:51 +0100 Subject: media: marvell-ccic: Use v4l2_async_notifier_add_*_subdev The use of v4l2_async_notifier_add_subdev will be discouraged. Drivers are instead encouraged to use a helper such as v4l2_async_notifier_add_fwnode_remote_subdev. This fixes a misuse of the API, as v4l2_async_notifier_add_subdev should get a kmalloc'ed struct v4l2_async_subdev, removing some boilerplate code while at it. Use the appropriate helper: v4l2_async_notifier_add_i2c_subdev or v4l2_async_notifier_add_fwnode_remote_subdev, which handles the needed setup, instead of open-coding it. Signed-off-by: Ezequiel Garcia Reviewed-by: Jacopo Mondi Reviewed-by: Helen Koike Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/cafe-driver.c | 14 +++++++++++--- drivers/media/platform/marvell-ccic/mcam-core.c | 10 ---------- drivers/media/platform/marvell-ccic/mcam-core.h | 1 - drivers/media/platform/marvell-ccic/mmp-driver.c | 11 ++++++++--- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index 00f623d62c96..91d65f71be96 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -489,6 +489,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, int ret; struct cafe_camera *cam; struct mcam_camera *mcam; + struct v4l2_async_subdev *asd; /* * Start putting together one of our big camera structures. @@ -546,9 +547,16 @@ static int cafe_pci_probe(struct pci_dev *pdev, if (ret) goto out_pdown; - mcam->asd.match_type = V4L2_ASYNC_MATCH_I2C; - mcam->asd.match.i2c.adapter_id = i2c_adapter_id(cam->i2c_adapter); - mcam->asd.match.i2c.address = ov7670_info.addr; + v4l2_async_notifier_init(&mcam->notifier); + + asd = v4l2_async_notifier_add_i2c_subdev(&mcam->notifier, + i2c_adapter_id(cam->i2c_adapter), + ov7670_info.addr, + sizeof(*asd)); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + goto out_smbus_shutdown; + } ret = mccic_register(mcam); if (ret) diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index c012fd2e1d29..153277e4fe80 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1866,16 +1866,6 @@ int mccic_register(struct mcam_camera *cam) cam->pix_format = mcam_def_pix_format; cam->mbus_code = mcam_def_mbus_code; - /* - * Register sensor notifier. - */ - v4l2_async_notifier_init(&cam->notifier); - ret = v4l2_async_notifier_add_subdev(&cam->notifier, &cam->asd); - if (ret) { - cam_warn(cam, "failed to add subdev to a notifier"); - goto out; - } - cam->notifier.ops = &mccic_notify_ops; ret = v4l2_async_notifier_register(&cam->v4l2_dev, &cam->notifier); if (ret < 0) { diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index b55545822fd2..f324d808d737 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -151,7 +151,6 @@ struct mcam_camera { */ struct video_device vdev; struct v4l2_async_notifier notifier; - struct v4l2_async_subdev asd; struct v4l2_subdev *sensor; /* Videobuf2 stuff */ diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 032fdddbbecc..40d9fc4a731a 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -180,6 +180,7 @@ static int mmpcam_probe(struct platform_device *pdev) struct resource *res; struct fwnode_handle *ep; struct mmp_camera_platform_data *pdata; + struct v4l2_async_subdev *asd; int ret; cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL); @@ -238,10 +239,15 @@ static int mmpcam_probe(struct platform_device *pdev) if (!ep) return -ENODEV; - mcam->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - mcam->asd.match.fwnode = fwnode_graph_get_remote_port_parent(ep); + v4l2_async_notifier_init(&mcam->notifier); + asd = v4l2_async_notifier_add_fwnode_remote_subdev(&mcam->notifier, + ep, sizeof(*asd)); fwnode_handle_put(ep); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + goto out; + } /* * Register the device with the core. @@ -278,7 +284,6 @@ static int mmpcam_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); return 0; out: - fwnode_handle_put(mcam->asd.match.fwnode); mccic_shutdown(mcam); return ret; -- cgit v1.2.3 From 5fd934d70215a7b7290af96f6d7e894e5957cdbb Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:52 +0100 Subject: media: renesas-ceu: Use v4l2_async_notifier_add_*_subdev The use of v4l2_async_notifier_add_subdev will be discouraged. Drivers are instead encouraged to use a helper such as v4l2_async_notifier_add_i2c_subdev. This fixes a misuse of the API, as v4l2_async_notifier_add_subdev should get a kmalloc'ed struct v4l2_async_subdev, removing some boilerplate code while at it. Use the appropriate helper: v4l2_async_notifier_add_i2c_subdev or v4l2_async_notifier_add_fwnode_remote_subdev, which handles the needed setup, instead of open-coding it. Using v4l2-async to allocate the driver-specific structs, requires to change struct ceu_subdev so the embedded struct v4l2_async_subdev is now the first element. Signed-off-by: Ezequiel Garcia Reviewed-by: Jacopo Mondi Reviewed-by: Helen Koike Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/renesas-ceu.c | 60 ++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c index 4a633ad0e8fa..0298d08b39e4 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas-ceu.c @@ -152,8 +152,8 @@ static inline struct ceu_buffer *vb2_to_ceu(struct vb2_v4l2_buffer *vbuf) * ceu_subdev - Wraps v4l2 sub-device and provides async subdevice. */ struct ceu_subdev { - struct v4l2_subdev *v4l2_sd; struct v4l2_async_subdev asd; + struct v4l2_subdev *v4l2_sd; /* per-subdevice mbus configuration options */ unsigned int mbus_flags; @@ -174,7 +174,7 @@ struct ceu_device { struct v4l2_device v4l2_dev; /* subdevices descriptors */ - struct ceu_subdev *subdevs; + struct ceu_subdev **subdevs; /* the subdevice currently in use */ struct ceu_subdev *sd; unsigned int sd_index; @@ -1195,7 +1195,7 @@ static int ceu_enum_input(struct file *file, void *priv, if (inp->index >= ceudev->num_sd) return -EINVAL; - ceusd = &ceudev->subdevs[inp->index]; + ceusd = ceudev->subdevs[inp->index]; inp->type = V4L2_INPUT_TYPE_CAMERA; inp->std = 0; @@ -1230,7 +1230,7 @@ static int ceu_s_input(struct file *file, void *priv, unsigned int i) return 0; ceu_sd_old = ceudev->sd; - ceudev->sd = &ceudev->subdevs[i]; + ceudev->sd = ceudev->subdevs[i]; /* * Make sure we can generate output image formats and apply @@ -1423,7 +1423,7 @@ static int ceu_notify_complete(struct v4l2_async_notifier *notifier) * ceu formats. */ if (!ceudev->sd) { - ceudev->sd = &ceudev->subdevs[0]; + ceudev->sd = ceudev->subdevs[0]; ceudev->sd_index = 0; } @@ -1467,8 +1467,8 @@ static const struct v4l2_async_notifier_operations ceu_notify_ops = { /* * ceu_init_async_subdevs() - Initialize CEU subdevices and async_subdevs in - * ceu device. Both DT and platform data parsing use - * this routine. + * ceu device. Both DT and platform data parsing use + * this routine. * * Returns 0 for success, -ENOMEM for failure. */ @@ -1495,6 +1495,7 @@ static int ceu_parse_platform_data(struct ceu_device *ceudev, const struct ceu_platform_data *pdata) { const struct ceu_async_subdev *async_sd; + struct v4l2_async_subdev *asd; struct ceu_subdev *ceu_sd; unsigned int i; int ret; @@ -1510,21 +1511,17 @@ static int ceu_parse_platform_data(struct ceu_device *ceudev, /* Setup the ceu subdevice and the async subdevice. */ async_sd = &pdata->subdevs[i]; - ceu_sd = &ceudev->subdevs[i]; - - INIT_LIST_HEAD(&ceu_sd->asd.list); - - ceu_sd->mbus_flags = async_sd->flags; - ceu_sd->asd.match_type = V4L2_ASYNC_MATCH_I2C; - ceu_sd->asd.match.i2c.adapter_id = async_sd->i2c_adapter_id; - ceu_sd->asd.match.i2c.address = async_sd->i2c_address; - - ret = v4l2_async_notifier_add_subdev(&ceudev->notifier, - &ceu_sd->asd); - if (ret) { + asd = v4l2_async_notifier_add_i2c_subdev(&ceudev->notifier, + async_sd->i2c_adapter_id, + async_sd->i2c_address, + sizeof(*ceu_sd)); + if (IS_ERR(asd)) { v4l2_async_notifier_cleanup(&ceudev->notifier); - return ret; + return PTR_ERR(asd); } + ceu_sd = to_ceu_subdev(asd); + ceu_sd->mbus_flags = async_sd->flags; + ceudev->subdevs[i] = ceu_sd; } return pdata->num_subdevs; @@ -1536,7 +1533,8 @@ static int ceu_parse_platform_data(struct ceu_device *ceudev, static int ceu_parse_dt(struct ceu_device *ceudev) { struct device_node *of = ceudev->dev->of_node; - struct device_node *ep, *remote; + struct device_node *ep; + struct v4l2_async_subdev *asd; struct ceu_subdev *ceu_sd; unsigned int i; int num_ep; @@ -1578,20 +1576,16 @@ static int ceu_parse_dt(struct ceu_device *ceudev) } /* Setup the ceu subdevice and the async subdevice. */ - ceu_sd = &ceudev->subdevs[i]; - INIT_LIST_HEAD(&ceu_sd->asd.list); - - remote = of_graph_get_remote_port_parent(ep); - ceu_sd->mbus_flags = fw_ep.bus.parallel.flags; - ceu_sd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - ceu_sd->asd.match.fwnode = of_fwnode_handle(remote); - - ret = v4l2_async_notifier_add_subdev(&ceudev->notifier, - &ceu_sd->asd); - if (ret) { - of_node_put(remote); + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &ceudev->notifier, of_fwnode_handle(ep), + sizeof(*ceu_sd)); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); goto error_cleanup; } + ceu_sd = to_ceu_subdev(asd); + ceu_sd->mbus_flags = fw_ep.bus.parallel.flags; + ceudev->subdevs[i] = ceu_sd; of_node_put(ep); } -- cgit v1.2.3 From c89502c84b15836d204c442dac221f7ddfb80eb2 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:53 +0100 Subject: media: pxa-camera: Use v4l2_async_notifier_add_*_subdev The use of v4l2_async_notifier_add_subdev will be discouraged. Drivers are instead encouraged to use a helper such as v4l2_async_notifier_add_fwnode_remote_subdev. This fixes a misuse of the API, as v4l2_async_notifier_add_subdev should get a kmalloc'ed struct v4l2_async_subdev. Use the appropriate helper: v4l2_async_notifier_add_i2c_subdev or v4l2_async_notifier_add_fwnode_remote_subdev, which handles the needed setup, instead of open-coding it. Signed-off-by: Ezequiel Garcia Reviewed-by: Helen Koike Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 57 +++++++++++++++---------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 75fad9689c90..b579ce2e93b6 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -656,8 +656,6 @@ struct pxa_camera_dev { const struct pxa_camera_format_xlate *current_fmt; struct v4l2_pix_format current_pix; - struct v4l2_async_subdev asd; - /* * PXA27x is only supposed to handle one camera on its Quick Capture * interface. If anyone ever builds hardware to enable more than @@ -2202,11 +2200,11 @@ static int pxa_camera_resume(struct device *dev) } static int pxa_camera_pdata_from_dt(struct device *dev, - struct pxa_camera_dev *pcdev, - struct v4l2_async_subdev *asd) + struct pxa_camera_dev *pcdev) { u32 mclk_rate; - struct device_node *remote, *np = dev->of_node; + struct v4l2_async_subdev *asd; + struct device_node *np = dev->of_node; struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; int err = of_property_read_u32(np, "clock-frequency", &mclk_rate); @@ -2258,13 +2256,12 @@ static int pxa_camera_pdata_from_dt(struct device *dev, if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) pcdev->platform_flags |= PXA_CAMERA_PCLK_EN; - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - remote = of_graph_get_remote_port_parent(np); - if (remote) - asd->match.fwnode = of_fwnode_handle(remote); - else - dev_notice(dev, "no remote for %pOF\n", np); - + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &pcdev->notifier, + of_fwnode_handle(np), + sizeof(*asd)); + if (IS_ERR(asd)) + err = PTR_ERR(asd); out: of_node_put(np); @@ -2300,18 +2297,23 @@ static int pxa_camera_probe(struct platform_device *pdev) if (IS_ERR(pcdev->clk)) return PTR_ERR(pcdev->clk); + v4l2_async_notifier_init(&pcdev->notifier); pcdev->res = res; - pcdev->pdata = pdev->dev.platform_data; if (pcdev->pdata) { + struct v4l2_async_subdev *asd; + pcdev->platform_flags = pcdev->pdata->flags; pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; - pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C; - pcdev->asd.match.i2c.adapter_id = - pcdev->pdata->sensor_i2c_adapter_id; - pcdev->asd.match.i2c.address = pcdev->pdata->sensor_i2c_address; + asd = v4l2_async_notifier_add_i2c_subdev( + &pcdev->notifier, + pcdev->pdata->sensor_i2c_adapter_id, + pcdev->pdata->sensor_i2c_address, + sizeof(*asd)); + if (IS_ERR(asd)) + err = PTR_ERR(asd); } else if (pdev->dev.of_node) { - err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd); + err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev); } else { return -ENODEV; } @@ -2403,27 +2405,15 @@ static int pxa_camera_probe(struct platform_device *pdev) if (err) goto exit_deactivate; - v4l2_async_notifier_init(&pcdev->notifier); - - err = v4l2_async_notifier_add_subdev(&pcdev->notifier, &pcdev->asd); - if (err) { - fwnode_handle_put(pcdev->asd.match.fwnode); - goto exit_free_v4l2dev; - } - - pcdev->notifier.ops = &pxa_camera_sensor_ops; - - if (!of_have_populated_dt()) - pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C; - err = pxa_camera_init_videobuf2(pcdev); if (err) goto exit_notifier_cleanup; v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - pcdev->asd.match.i2c.adapter_id, - pcdev->asd.match.i2c.address); + pcdev->pdata->sensor_i2c_adapter_id, + pcdev->pdata->sensor_i2c_address); + pcdev->notifier.ops = &pxa_camera_sensor_ops; pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, clk_name, NULL); if (IS_ERR(pcdev->mclk_clk)) { err = PTR_ERR(pcdev->mclk_clk); @@ -2439,7 +2429,6 @@ exit_free_clk: v4l2_clk_unregister(pcdev->mclk_clk); exit_notifier_cleanup: v4l2_async_notifier_cleanup(&pcdev->notifier); -exit_free_v4l2dev: v4l2_device_unregister(&pcdev->v4l2_dev); exit_deactivate: pxa_camera_deactivate(pcdev); -- cgit v1.2.3 From be5ec392bb76d9e1b380deb6b390f6e9fb210172 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:54 +0100 Subject: media: davinci: vpif_display: Remove unused v4l2-async code There are no users for vpif_display_config.asd_sizes and vpif_display_config.asd members, which means the v4l2-async subdevices aren't being defined anywhere. Remove the v4l2-async, leaving only the synchronous setup. Signed-off-by: Ezequiel Garcia Reviewed-by: Helen Koike Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_display.c | 86 +++++---------------------- drivers/media/platform/davinci/vpif_display.h | 1 - include/media/davinci/vpif_types.h | 2 - 3 files changed, 15 insertions(+), 74 deletions(-) diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 46afc029138f..e5f61d9b221d 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1117,23 +1117,6 @@ static void free_vpif_objs(void) kfree(vpif_obj.dev[i]); } -static int vpif_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) -{ - int i; - - for (i = 0; i < vpif_obj.config->subdev_count; i++) - if (!strcmp(vpif_obj.config->subdevinfo[i].name, - subdev->name)) { - vpif_obj.sd[i] = subdev; - vpif_obj.sd[i]->grp_id = 1 << i; - return 0; - } - - return -EINVAL; -} - static int vpif_probe_complete(void) { struct common_obj *common; @@ -1230,16 +1213,6 @@ probe_out: return err; } -static int vpif_async_complete(struct v4l2_async_notifier *notifier) -{ - return vpif_probe_complete(); -} - -static const struct v4l2_async_notifier_operations vpif_async_ops = { - .bound = vpif_async_bound, - .complete = vpif_async_complete, -}; - /* * vpif_probe: This function creates device entries by register itself to the * V4L2 driver and initializes fields of each channel objects @@ -1294,52 +1267,28 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_unregister; } - v4l2_async_notifier_init(&vpif_obj.notifier); - - if (!vpif_obj.config->asd_sizes) { - i2c_adap = i2c_get_adapter(vpif_obj.config->i2c_adapter_id); - for (i = 0; i < subdev_count; i++) { - vpif_obj.sd[i] = - v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, - i2c_adap, - &subdevdata[i]. - board_info, - NULL); - if (!vpif_obj.sd[i]) { - vpif_err("Error registering v4l2 subdevice\n"); - err = -ENODEV; - goto probe_subdev_out; - } - - if (vpif_obj.sd[i]) - vpif_obj.sd[i]->grp_id = 1 << i; - } - err = vpif_probe_complete(); - if (err) { + i2c_adap = i2c_get_adapter(vpif_obj.config->i2c_adapter_id); + for (i = 0; i < subdev_count; i++) { + vpif_obj.sd[i] = + v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, + &subdevdata[i].board_info, + NULL); + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + err = -ENODEV; goto probe_subdev_out; } - } else { - for (i = 0; i < vpif_obj.config->asd_sizes[0]; i++) { - err = v4l2_async_notifier_add_subdev( - &vpif_obj.notifier, vpif_obj.config->asd[i]); - if (err) - goto probe_cleanup; - } - vpif_obj.notifier.ops = &vpif_async_ops; - err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev, - &vpif_obj.notifier); - if (err) { - vpif_err("Error registering async notifier\n"); - err = -EINVAL; - goto probe_cleanup; - } + if (vpif_obj.sd[i]) + vpif_obj.sd[i]->grp_id = 1 << i; } + err = vpif_probe_complete(); + if (err) + goto probe_subdev_out; return 0; -probe_cleanup: - v4l2_async_notifier_cleanup(&vpif_obj.notifier); probe_subdev_out: kfree(vpif_obj.sd); vpif_unregister: @@ -1358,11 +1307,6 @@ static int vpif_remove(struct platform_device *device) struct channel_obj *ch; int i; - if (vpif_obj.config->asd_sizes) { - v4l2_async_notifier_unregister(&vpif_obj.notifier); - v4l2_async_notifier_cleanup(&vpif_obj.notifier); - } - v4l2_device_unregister(&vpif_obj.v4l2_dev); kfree(vpif_obj.sd); diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index f731a65eefd6..f98062e79167 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -118,7 +118,6 @@ struct vpif_device { struct v4l2_device v4l2_dev; struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS]; struct v4l2_subdev **sd; - struct v4l2_async_notifier notifier; struct vpif_display_config *config; }; diff --git a/include/media/davinci/vpif_types.h b/include/media/davinci/vpif_types.h index 8439e46fb993..d03e5c54347a 100644 --- a/include/media/davinci/vpif_types.h +++ b/include/media/davinci/vpif_types.h @@ -48,8 +48,6 @@ struct vpif_display_config { int i2c_adapter_id; struct vpif_display_chan_config chan_config[VPIF_DISPLAY_MAX_CHANNELS]; const char *card_name; - struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ - int *asd_sizes; /* 0-terminated array of asd group sizes */ }; struct vpif_input { -- cgit v1.2.3 From c1cc236250629f3181e2b98c16db2642e295278a Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:57 +0100 Subject: media: v4l2-async: Discourage use of v4l2_async_notifier_add_subdev Most -if not all- use-cases are expected to be covered by one of: v4l2_async_notifier_add_fwnode_subdev, v4l2_async_notifier_add_fwnode_remote_subdev or v4l2_async_notifier_add_i2c_subdev. We'd like to discourage drivers from using v4l2_async_notifier_add_subdev, so rename it as __v4l2_async_notifier_add_subdev. This is typically a good hint for drivers to avoid using the function. Signed-off-by: Ezequiel Garcia Reviewed-by: Laurent Pinchart Reviewed-by: Helen Koike Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 8 ++++---- drivers/media/v4l2-core/v4l2-fwnode.c | 2 +- include/media/v4l2-async.h | 9 +++++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index ed603ae63cad..d848db962dc7 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -611,7 +611,7 @@ void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier) } EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup); -int v4l2_async_notifier_add_subdev(struct v4l2_async_notifier *notifier, +int __v4l2_async_notifier_add_subdev(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { int ret; @@ -628,7 +628,7 @@ unlock: mutex_unlock(&list_lock); return ret; } -EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_subdev); +EXPORT_SYMBOL_GPL(__v4l2_async_notifier_add_subdev); struct v4l2_async_subdev * v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, @@ -645,7 +645,7 @@ v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, asd->match_type = V4L2_ASYNC_MATCH_FWNODE; asd->match.fwnode = fwnode_handle_get(fwnode); - ret = v4l2_async_notifier_add_subdev(notifier, asd); + ret = __v4l2_async_notifier_add_subdev(notifier, asd); if (ret) { fwnode_handle_put(fwnode); kfree(asd); @@ -695,7 +695,7 @@ v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, asd->match.i2c.adapter_id = adapter_id; asd->match.i2c.address = address; - ret = v4l2_async_notifier_add_subdev(notifier, asd); + ret = __v4l2_async_notifier_add_subdev(notifier, asd); if (ret) { kfree(asd); return ERR_PTR(ret); diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index c1c2b3060532..63be16c8eb83 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -822,7 +822,7 @@ v4l2_async_notifier_fwnode_parse_endpoint(struct device *dev, if (ret < 0) goto out_err; - ret = v4l2_async_notifier_add_subdev(notifier, asd); + ret = __v4l2_async_notifier_add_subdev(notifier, asd); if (ret < 0) { /* not an error if asd already exists */ if (ret == -EEXIST) diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 8815e233677e..b113329582ff 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -133,17 +133,22 @@ void v4l2_async_debug_init(struct dentry *debugfs_dir); void v4l2_async_notifier_init(struct v4l2_async_notifier *notifier); /** - * v4l2_async_notifier_add_subdev - Add an async subdev to the + * __v4l2_async_notifier_add_subdev - Add an async subdev to the * notifier's master asd list. * * @notifier: pointer to &struct v4l2_async_notifier * @asd: pointer to &struct v4l2_async_subdev * + * \warning: Drivers should avoid using this function and instead use one of: + * @v4l2_async_notifier_add_fwnode_subdev, + * @v4l2_async_notifier_add_fwnode_remote_subdev or + * @v4l2_async_notifier_add_i2c_subdev. + * * Call this function before registering a notifier to link the provided @asd to * the notifiers master @asd_list. The @asd must be allocated with k*alloc() as * it will be freed by the framework when the notifier is destroyed. */ -int v4l2_async_notifier_add_subdev(struct v4l2_async_notifier *notifier, +int __v4l2_async_notifier_add_subdev(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd); /** -- cgit v1.2.3 From b01edcbd409cf713ff4516c6e1e057834b4b43d6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 18 Jan 2021 02:52:58 +0100 Subject: media: v4l2-async: Improve v4l2_async_notifier_add_*_subdev() API The functions that add an async subdev to an async subdev notifier take as an argument the size of the container structure they need to allocate. This is error prone, as passing an invalid size will not be caught by the compiler. Wrap those functions in macros that take a container type instead of a size, and cast the returned pointer to the desired type. The compiler will catch mistakes if the incorrect type is passed to the macro, as the assignment types won't match. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Signed-off-by: Ezequiel Garcia Signed-off-by: Sakari Ailus Reviewed-by: Helen Koike Reviewed-by: Tomi Valkeinen (core+ti-cal) Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/max9286.c | 14 +++++----- drivers/media/i2c/st-mipid02.c | 2 +- drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 10 +++----- drivers/media/platform/am437x/am437x-vpfe.c | 2 +- drivers/media/platform/atmel/atmel-isi.c | 2 +- drivers/media/platform/atmel/atmel-sama5d2-isc.c | 2 +- drivers/media/platform/cadence/cdns-csi2rx.c | 3 ++- drivers/media/platform/davinci/vpif_capture.c | 2 +- drivers/media/platform/exynos4-is/media-dev.c | 3 ++- drivers/media/platform/marvell-ccic/cafe-driver.c | 2 +- drivers/media/platform/marvell-ccic/mmp-driver.c | 4 +-- drivers/media/platform/omap3isp/isp.c | 17 +++++------- drivers/media/platform/pxa_camera.c | 4 +-- drivers/media/platform/qcom/camss/camss.c | 11 +++----- drivers/media/platform/rcar-vin/rcar-core.c | 5 ++-- drivers/media/platform/rcar-vin/rcar-csi2.c | 2 +- drivers/media/platform/rcar_drif.c | 2 +- drivers/media/platform/renesas-ceu.c | 20 ++++++--------- .../media/platform/rockchip/rkisp1/rkisp1-dev.c | 12 ++++----- drivers/media/platform/stm32/stm32-dcmi.c | 3 ++- drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c | 4 +-- drivers/media/platform/ti-vpe/cal.c | 12 ++++----- drivers/media/platform/video-mux.c | 2 +- drivers/media/platform/xilinx/xilinx-vipp.c | 10 ++++---- drivers/media/v4l2-core/v4l2-async.c | 30 +++++++++++----------- drivers/media/v4l2-core/v4l2-fwnode.c | 4 +-- drivers/staging/media/imx/imx-media-csi.c | 2 +- drivers/staging/media/imx/imx-media-of.c | 2 +- drivers/staging/media/imx/imx6-mipi-csi2.c | 2 +- drivers/staging/media/imx/imx7-media-csi.c | 2 +- drivers/staging/media/imx/imx7-mipi-csis.c | 2 +- drivers/staging/media/tegra-video/vi.c | 10 ++++---- include/media/v4l2-async.h | 27 ++++++++++++------- 33 files changed, 113 insertions(+), 118 deletions(-) diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index b1e2476d3c9e..fd2151f2fa36 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -576,19 +576,19 @@ static int max9286_v4l2_notifier_register(struct max9286_priv *priv) for_each_source(priv, source) { unsigned int i = to_index(priv, source); - struct v4l2_async_subdev *asd; + struct max9286_asd *mas; - asd = v4l2_async_notifier_add_fwnode_subdev(&priv->notifier, + mas = v4l2_async_notifier_add_fwnode_subdev(&priv->notifier, source->fwnode, - sizeof(struct max9286_asd)); - if (IS_ERR(asd)) { + struct max9286_asd); + if (IS_ERR(mas)) { dev_err(dev, "Failed to add subdev for source %u: %ld", - i, PTR_ERR(asd)); + i, PTR_ERR(mas)); v4l2_async_notifier_cleanup(&priv->notifier); - return PTR_ERR(asd); + return PTR_ERR(mas); } - to_max9286_asd(asd)->source = source; + mas->source = source; } priv->notifier.ops = &max9286_notify_ops; diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index bb32a5278a4e..7f07ef56fbbd 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -879,7 +879,7 @@ static int mipid02_parse_rx_ep(struct mipid02_dev *bridge) asd = v4l2_async_notifier_add_fwnode_remote_subdev( &bridge->notifier, of_fwnode_handle(ep_node), - sizeof(*asd)); + struct v4l2_async_subdev); of_node_put(ep_node); if (IS_ERR(asd)) { diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c index 0895c199de3e..6e8c0c230e11 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c @@ -1465,7 +1465,6 @@ static int cio2_parse_firmware(struct cio2_device *cio2) .bus_type = V4L2_MBUS_CSI2_DPHY }; struct sensor_async_subdev *s_asd; - struct v4l2_async_subdev *asd; struct fwnode_handle *ep; ep = fwnode_graph_get_endpoint_by_id( @@ -1479,14 +1478,13 @@ static int cio2_parse_firmware(struct cio2_device *cio2) if (ret) goto err_parse; - asd = v4l2_async_notifier_add_fwnode_remote_subdev( - &cio2->notifier, ep, sizeof(*s_asd)); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); + s_asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &cio2->notifier, ep, struct sensor_async_subdev); + if (IS_ERR(s_asd)) { + ret = PTR_ERR(s_asd); goto err_parse; } - s_asd = container_of(asd, struct sensor_async_subdev, asd); s_asd->csi2.port = vep.base.port; s_asd->csi2.lanes = vep.bus.mipi_csi2.num_data_lanes; diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 0fb9f9ba1219..6cdc77dda0e4 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -2365,7 +2365,7 @@ vpfe_get_pdata(struct vpfe_device *vpfe) pdata->asd[i] = v4l2_async_notifier_add_fwnode_subdev( &vpfe->notifier, of_fwnode_handle(rem), - sizeof(struct v4l2_async_subdev)); + struct v4l2_async_subdev); of_node_put(rem); if (IS_ERR(pdata->asd[i])) goto cleanup; diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index c1a6dd7af002..0514be6153df 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -1150,7 +1150,7 @@ static int isi_graph_init(struct atmel_isi *isi) asd = v4l2_async_notifier_add_fwnode_remote_subdev( &isi->notifier, of_fwnode_handle(ep), - sizeof(*asd)); + struct v4l2_async_subdev); of_node_put(ep); if (IS_ERR(asd)) diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c index 9ee2cd194f93..0b78fecfd2a8 100644 --- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c +++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c @@ -214,7 +214,7 @@ static int atmel_isc_probe(struct platform_device *pdev) asd = v4l2_async_notifier_add_fwnode_remote_subdev( &subdev_entity->notifier, of_fwnode_handle(subdev_entity->epn), - sizeof(*asd)); + struct v4l2_async_subdev); of_node_put(subdev_entity->epn); subdev_entity->epn = NULL; diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 7d299cacef8c..c68a3eac62cd 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -398,7 +398,8 @@ static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx) v4l2_async_notifier_init(&csi2rx->notifier); asd = v4l2_async_notifier_add_fwnode_remote_subdev(&csi2rx->notifier, - fwh, sizeof(*asd)); + fwh, + struct v4l2_async_subdev); of_node_put(ep); if (IS_ERR(asd)) return PTR_ERR(asd); diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 72a0e94e2e21..8d2e165bf7de 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1584,7 +1584,7 @@ vpif_capture_get_pdata(struct platform_device *pdev) pdata->asd[i] = v4l2_async_notifier_add_fwnode_subdev( &vpif_obj.notifier, of_fwnode_handle(rem), - sizeof(struct v4l2_async_subdev)); + struct v4l2_async_subdev); if (IS_ERR(pdata->asd[i])) goto err_cleanup; diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index f4687b0cbd65..8e1e892085ec 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -465,7 +465,8 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, } asd = v4l2_async_notifier_add_fwnode_remote_subdev( - &fmd->subdev_notifier, of_fwnode_handle(ep), sizeof(*asd)); + &fmd->subdev_notifier, of_fwnode_handle(ep), + struct v4l2_async_subdev); of_node_put(ep); diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index 91d65f71be96..9c94a8b58b7c 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -552,7 +552,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, asd = v4l2_async_notifier_add_i2c_subdev(&mcam->notifier, i2c_adapter_id(cam->i2c_adapter), ov7670_info.addr, - sizeof(*asd)); + struct v4l2_async_subdev); if (IS_ERR(asd)) { ret = PTR_ERR(asd); goto out_smbus_shutdown; diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 40d9fc4a731a..f2f09cea751d 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -241,8 +241,8 @@ static int mmpcam_probe(struct platform_device *pdev) v4l2_async_notifier_init(&mcam->notifier); - asd = v4l2_async_notifier_add_fwnode_remote_subdev(&mcam->notifier, - ep, sizeof(*asd)); + asd = v4l2_async_notifier_add_fwnode_remote_subdev(&mcam->notifier, ep, + struct v4l2_async_subdev); fwnode_handle_put(ep); if (IS_ERR(asd)) { ret = PTR_ERR(asd); diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 1311b4996ece..a6bb7d9bf75f 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -2141,7 +2141,6 @@ static int isp_parse_of_endpoints(struct isp_device *isp) { struct fwnode_handle *ep; struct isp_async_subdev *isd = NULL; - struct v4l2_async_subdev *asd; unsigned int i; ep = fwnode_graph_get_endpoint_by_id( @@ -2159,12 +2158,10 @@ static int isp_parse_of_endpoints(struct isp_device *isp) ret = v4l2_fwnode_endpoint_parse(ep, &vep); if (!ret) { - asd = v4l2_async_notifier_add_fwnode_remote_subdev( - &isp->notifier, ep, sizeof(*isd)); - if (!IS_ERR(asd)) { - isd = container_of(asd, struct isp_async_subdev, asd); + isd = v4l2_async_notifier_add_fwnode_remote_subdev( + &isp->notifier, ep, struct isp_async_subdev); + if (!IS_ERR(isd)) isp_parse_of_parallel_endpoint(isp->dev, &vep, &isd->bus); - } } fwnode_handle_put(ep); @@ -2200,12 +2197,10 @@ static int isp_parse_of_endpoints(struct isp_device *isp) } if (!ret) { - asd = v4l2_async_notifier_add_fwnode_remote_subdev( - &isp->notifier, ep, sizeof(*isd)); - - if (!IS_ERR(asd)) { - isd = container_of(asd, struct isp_async_subdev, asd); + isd = v4l2_async_notifier_add_fwnode_remote_subdev( + &isp->notifier, ep, struct isp_async_subdev); + if (!IS_ERR(isd)) { switch (vep.bus_type) { case V4L2_MBUS_CSI2_DPHY: isd->bus.interface = diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index b579ce2e93b6..7f92de351c52 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2259,7 +2259,7 @@ static int pxa_camera_pdata_from_dt(struct device *dev, asd = v4l2_async_notifier_add_fwnode_remote_subdev( &pcdev->notifier, of_fwnode_handle(np), - sizeof(*asd)); + struct v4l2_async_subdev); if (IS_ERR(asd)) err = PTR_ERR(asd); out: @@ -2309,7 +2309,7 @@ static int pxa_camera_probe(struct platform_device *pdev) &pcdev->notifier, pcdev->pdata->sensor_i2c_adapter_id, pcdev->pdata->sensor_i2c_address, - sizeof(*asd)); + struct v4l2_async_subdev); if (IS_ERR(asd)) err = PTR_ERR(asd); } else if (pdev->dev.of_node) { diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 8fefce57bc49..7c0f669f8aa6 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -655,7 +655,6 @@ static int camss_of_parse_ports(struct camss *camss) for_each_endpoint_of_node(dev->of_node, node) { struct camss_async_subdev *csd; - struct v4l2_async_subdev *asd; if (!of_device_is_available(node)) continue; @@ -667,17 +666,15 @@ static int camss_of_parse_ports(struct camss *camss) goto err_cleanup; } - asd = v4l2_async_notifier_add_fwnode_subdev( + csd = v4l2_async_notifier_add_fwnode_subdev( &camss->notifier, of_fwnode_handle(remote), - sizeof(*csd)); + struct camss_async_subdev); of_node_put(remote); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); + if (IS_ERR(csd)) { + ret = PTR_ERR(csd); goto err_cleanup; } - csd = container_of(asd, struct camss_async_subdev, asd); - ret = camss_of_parse_endpoint_node(dev, node, csd); if (ret < 0) goto err_cleanup; diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index e48d666f2c63..cb3025992817 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -642,7 +642,7 @@ static int rvin_parallel_parse_of(struct rvin_dev *vin) } asd = v4l2_async_notifier_add_fwnode_subdev(&vin->notifier, fwnode, - sizeof(*asd)); + struct v4l2_async_subdev); if (IS_ERR(asd)) { ret = PTR_ERR(asd); goto out; @@ -842,7 +842,8 @@ static int rvin_mc_parse_of(struct rvin_dev *vin, unsigned int id) } asd = v4l2_async_notifier_add_fwnode_subdev(&vin->group->notifier, - fwnode, sizeof(*asd)); + fwnode, + struct v4l2_async_subdev); if (IS_ERR(asd)) { ret = PTR_ERR(asd); goto out; diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 945d2eb87233..e06cd512aba2 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -910,7 +910,7 @@ static int rcsi2_parse_dt(struct rcar_csi2 *priv) priv->notifier.ops = &rcar_csi2_notify_ops; asd = v4l2_async_notifier_add_fwnode_subdev(&priv->notifier, fwnode, - sizeof(*asd)); + struct v4l2_async_subdev); fwnode_handle_put(fwnode); if (IS_ERR(asd)) return PTR_ERR(asd); diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c index f318cd4b8086..83bd9a412a56 100644 --- a/drivers/media/platform/rcar_drif.c +++ b/drivers/media/platform/rcar_drif.c @@ -1231,7 +1231,7 @@ static int rcar_drif_parse_subdevs(struct rcar_drif_sdr *sdr) } asd = v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode, - sizeof(*asd)); + struct v4l2_async_subdev); fwnode_handle_put(fwnode); if (IS_ERR(asd)) return PTR_ERR(asd); diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c index 0298d08b39e4..1678175c49bd 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas-ceu.c @@ -1495,7 +1495,6 @@ static int ceu_parse_platform_data(struct ceu_device *ceudev, const struct ceu_platform_data *pdata) { const struct ceu_async_subdev *async_sd; - struct v4l2_async_subdev *asd; struct ceu_subdev *ceu_sd; unsigned int i; int ret; @@ -1511,15 +1510,14 @@ static int ceu_parse_platform_data(struct ceu_device *ceudev, /* Setup the ceu subdevice and the async subdevice. */ async_sd = &pdata->subdevs[i]; - asd = v4l2_async_notifier_add_i2c_subdev(&ceudev->notifier, + ceu_sd = v4l2_async_notifier_add_i2c_subdev(&ceudev->notifier, async_sd->i2c_adapter_id, async_sd->i2c_address, - sizeof(*ceu_sd)); - if (IS_ERR(asd)) { + struct ceu_subdev); + if (IS_ERR(ceu_sd)) { v4l2_async_notifier_cleanup(&ceudev->notifier); - return PTR_ERR(asd); + return PTR_ERR(ceu_sd); } - ceu_sd = to_ceu_subdev(asd); ceu_sd->mbus_flags = async_sd->flags; ceudev->subdevs[i] = ceu_sd; } @@ -1534,7 +1532,6 @@ static int ceu_parse_dt(struct ceu_device *ceudev) { struct device_node *of = ceudev->dev->of_node; struct device_node *ep; - struct v4l2_async_subdev *asd; struct ceu_subdev *ceu_sd; unsigned int i; int num_ep; @@ -1576,14 +1573,13 @@ static int ceu_parse_dt(struct ceu_device *ceudev) } /* Setup the ceu subdevice and the async subdevice. */ - asd = v4l2_async_notifier_add_fwnode_remote_subdev( + ceu_sd = v4l2_async_notifier_add_fwnode_remote_subdev( &ceudev->notifier, of_fwnode_handle(ep), - sizeof(*ceu_sd)); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); + struct ceu_subdev); + if (IS_ERR(ceu_sd)) { + ret = PTR_ERR(ceu_sd); goto error_cleanup; } - ceu_sd = to_ceu_subdev(asd); ceu_sd->mbus_flags = fw_ep.bus.parallel.flags; ceudev->subdevs[i] = ceu_sd; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index daa1b6d22436..02cde70a9d74 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -251,8 +251,7 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - struct rkisp1_sensor_async *rk_asd = NULL; - struct v4l2_async_subdev *asd; + struct rkisp1_sensor_async *rk_asd; struct fwnode_handle *ep; ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(rkisp1->dev), @@ -265,14 +264,13 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1) if (ret) goto err_parse; - asd = v4l2_async_notifier_add_fwnode_remote_subdev(ntf, ep, - sizeof(*rk_asd)); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); + rk_asd = v4l2_async_notifier_add_fwnode_remote_subdev(ntf, ep, + struct rkisp1_sensor_async); + if (IS_ERR(rk_asd)) { + ret = PTR_ERR(rk_asd); goto err_parse; } - rk_asd = container_of(asd, struct rkisp1_sensor_async, asd); rk_asd->mbus_type = vep.bus_type; rk_asd->mbus_flags = vep.bus.mipi_csi2.flags; rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes; diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 142f63d07dcd..bbcc2254fa2e 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1820,7 +1820,8 @@ static int dcmi_graph_init(struct stm32_dcmi *dcmi) v4l2_async_notifier_init(&dcmi->notifier); asd = v4l2_async_notifier_add_fwnode_remote_subdev( - &dcmi->notifier, of_fwnode_handle(ep), sizeof(*asd)); + &dcmi->notifier, of_fwnode_handle(ep), + struct v4l2_async_subdev); of_node_put(ep); diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index 3f94b8c966f3..8d40a7acba9c 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -135,8 +135,8 @@ static int sun4i_csi_notifier_init(struct sun4i_csi *csi) csi->bus = vep.bus.parallel; - asd = v4l2_async_notifier_add_fwnode_remote_subdev(&csi->notifier, - ep, sizeof(*asd)); + asd = v4l2_async_notifier_add_fwnode_remote_subdev(&csi->notifier, ep, + struct v4l2_async_subdev); if (IS_ERR(asd)) { ret = PTR_ERR(asd); goto out; diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 5fb627811c6b..fa0931788040 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -685,23 +685,21 @@ static int cal_async_notifier_register(struct cal_dev *cal) for (i = 0; i < cal->data->num_csi2_phy; ++i) { struct cal_camerarx *phy = cal->phy[i]; struct cal_v4l2_async_subdev *casd; - struct v4l2_async_subdev *asd; struct fwnode_handle *fwnode; if (!phy->sensor_node) continue; fwnode = of_fwnode_handle(phy->sensor_node); - asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, - fwnode, - sizeof(*casd)); - if (IS_ERR(asd)) { + casd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, + fwnode, + struct cal_v4l2_async_subdev); + if (IS_ERR(casd)) { phy_err(phy, "Failed to add subdev to notifier\n"); - ret = PTR_ERR(asd); + ret = PTR_ERR(casd); goto error; } - casd = to_cal_asd(asd); casd->phy = phy; } diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index 7b280dfca727..133122e38515 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -371,7 +371,7 @@ static int video_mux_async_register(struct video_mux *vmux, continue; asd = v4l2_async_notifier_add_fwnode_remote_subdev( - &vmux->notifier, ep, sizeof(*asd)); + &vmux->notifier, ep, struct v4l2_async_subdev); fwnode_handle_put(ep); diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index cc2856efea59..bf4015d852e3 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -359,7 +359,7 @@ static int xvip_graph_parse_one(struct xvip_composite_device *xdev, dev_dbg(xdev->dev, "parsing node %p\n", fwnode); while (1) { - struct v4l2_async_subdev *asd; + struct xvip_graph_entity *xge; ep = fwnode_graph_get_next_endpoint(fwnode, ep); if (ep == NULL) @@ -382,12 +382,12 @@ static int xvip_graph_parse_one(struct xvip_composite_device *xdev, continue; } - asd = v4l2_async_notifier_add_fwnode_subdev( + xge = v4l2_async_notifier_add_fwnode_subdev( &xdev->notifier, remote, - sizeof(struct xvip_graph_entity)); + struct xvip_graph_entity); fwnode_handle_put(remote); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); + if (IS_ERR(xge)) { + ret = PTR_ERR(xge); goto err_notifier_cleanup; } } diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index d848db962dc7..e638aa8aecb7 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -631,9 +631,9 @@ unlock: EXPORT_SYMBOL_GPL(__v4l2_async_notifier_add_subdev); struct v4l2_async_subdev * -v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, - struct fwnode_handle *fwnode, - unsigned int asd_struct_size) +__v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, + struct fwnode_handle *fwnode, + unsigned int asd_struct_size) { struct v4l2_async_subdev *asd; int ret; @@ -654,12 +654,12 @@ v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, return asd; } -EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_fwnode_subdev); +EXPORT_SYMBOL_GPL(__v4l2_async_notifier_add_fwnode_subdev); struct v4l2_async_subdev * -v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, - struct fwnode_handle *endpoint, - unsigned int asd_struct_size) +__v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, + struct fwnode_handle *endpoint, + unsigned int asd_struct_size) { struct v4l2_async_subdev *asd; struct fwnode_handle *remote; @@ -668,21 +668,21 @@ v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, if (!remote) return ERR_PTR(-ENOTCONN); - asd = v4l2_async_notifier_add_fwnode_subdev(notif, remote, - asd_struct_size); + asd = __v4l2_async_notifier_add_fwnode_subdev(notif, remote, + asd_struct_size); /* - * Calling v4l2_async_notifier_add_fwnode_subdev grabs a refcount, + * Calling __v4l2_async_notifier_add_fwnode_subdev grabs a refcount, * so drop the one we got in fwnode_graph_get_remote_port_parent. */ fwnode_handle_put(remote); return asd; } -EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_fwnode_remote_subdev); +EXPORT_SYMBOL_GPL(__v4l2_async_notifier_add_fwnode_remote_subdev); struct v4l2_async_subdev * -v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, - int adapter_id, unsigned short address, - unsigned int asd_struct_size) +__v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, + int adapter_id, unsigned short address, + unsigned int asd_struct_size) { struct v4l2_async_subdev *asd; int ret; @@ -703,7 +703,7 @@ v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, return asd; } -EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_i2c_subdev); +EXPORT_SYMBOL_GPL(__v4l2_async_notifier_add_i2c_subdev); int v4l2_async_register_subdev(struct v4l2_subdev *sd) { diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 63be16c8eb83..2283ff3b8e1d 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -944,7 +944,7 @@ static int v4l2_fwnode_reference_parse(struct device *dev, asd = v4l2_async_notifier_add_fwnode_subdev(notifier, args.fwnode, - sizeof(*asd)); + struct v4l2_async_subdev); fwnode_handle_put(args.fwnode); if (IS_ERR(asd)) { /* not an error if asd already exists */ @@ -1244,7 +1244,7 @@ v4l2_fwnode_reference_parse_int_props(struct device *dev, struct v4l2_async_subdev *asd; asd = v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode, - sizeof(*asd)); + struct v4l2_async_subdev); fwnode_handle_put(fwnode); if (IS_ERR(asd)) { ret = PTR_ERR(asd); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 6344389e6afa..ef5add079774 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1923,7 +1923,7 @@ static int imx_csi_async_register(struct csi_priv *priv) FWNODE_GRAPH_ENDPOINT_NEXT); if (ep) { asd = v4l2_async_notifier_add_fwnode_remote_subdev( - &priv->notifier, ep, sizeof(*asd)); + &priv->notifier, ep, struct v4l2_async_subdev); fwnode_handle_put(ep); diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index 82e13e972e23..b677cf0e0c84 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -31,7 +31,7 @@ int imx_media_of_add_csi(struct imx_media_dev *imxmd, /* add CSI fwnode to async notifier */ asd = v4l2_async_notifier_add_fwnode_subdev(&imxmd->notifier, of_fwnode_handle(csi_np), - sizeof(*asd)); + struct v4l2_async_subdev); if (IS_ERR(asd)) { ret = PTR_ERR(asd); if (ret == -EEXIST) diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index a79ab38158e2..4f8fcc91aaae 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -662,7 +662,7 @@ static int csi2_async_register(struct csi2_dev *csi2) dev_dbg(csi2->dev, "flags: 0x%08x\n", vep.bus.mipi_csi2.flags); asd = v4l2_async_notifier_add_fwnode_remote_subdev( - &csi2->notifier, ep, sizeof(*asd)); + &csi2->notifier, ep, struct v4l2_async_subdev); fwnode_handle_put(ep); if (IS_ERR(asd)) diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c index 6c59485291ca..3046f880c014 100644 --- a/drivers/staging/media/imx/imx7-media-csi.c +++ b/drivers/staging/media/imx/imx7-media-csi.c @@ -1201,7 +1201,7 @@ static int imx7_csi_async_register(struct imx7_csi *csi) FWNODE_GRAPH_ENDPOINT_NEXT); if (ep) { asd = v4l2_async_notifier_add_fwnode_remote_subdev( - &csi->notifier, ep, sizeof(*asd)); + &csi->notifier, ep, struct v4l2_async_subdev); fwnode_handle_put(ep); diff --git a/drivers/staging/media/imx/imx7-mipi-csis.c b/drivers/staging/media/imx/imx7-mipi-csis.c index 1ba77e629e5b..a01a7364b4b9 100644 --- a/drivers/staging/media/imx/imx7-mipi-csis.c +++ b/drivers/staging/media/imx/imx7-mipi-csis.c @@ -1025,7 +1025,7 @@ static int mipi_csis_async_register(struct csi_state *state) dev_dbg(state->dev, "flags: 0x%08x\n", state->bus.flags); asd = v4l2_async_notifier_add_fwnode_remote_subdev( - &state->notifier, ep, sizeof(*asd)); + &state->notifier, ep, struct v4l2_async_subdev); if (IS_ERR(asd)) { ret = PTR_ERR(asd); goto err_parse; diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 70e1e18644b2..7a09061cda57 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -1788,7 +1788,7 @@ static int tegra_vi_graph_parse_one(struct tegra_vi_channel *chan, struct tegra_vi *vi = chan->vi; struct fwnode_handle *ep = NULL; struct fwnode_handle *remote = NULL; - struct v4l2_async_subdev *asd; + struct tegra_vi_graph_entity *tvge; struct device_node *node = NULL; int ret; @@ -1812,10 +1812,10 @@ static int tegra_vi_graph_parse_one(struct tegra_vi_channel *chan, continue; } - asd = v4l2_async_notifier_add_fwnode_subdev(&chan->notifier, - remote, sizeof(struct tegra_vi_graph_entity)); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); + tvge = v4l2_async_notifier_add_fwnode_subdev(&chan->notifier, + remote, struct tegra_vi_graph_entity); + if (IS_ERR(tvge)) { + ret = PTR_ERR(tvge); dev_err(vi->dev, "failed to add subdev to notifier: %d\n", ret); fwnode_handle_put(remote); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index b113329582ff..192a11bdc4ad 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -168,9 +168,12 @@ int __v4l2_async_notifier_add_subdev(struct v4l2_async_notifier *notifier, * is released later at notifier cleanup time. */ struct v4l2_async_subdev * -v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, - struct fwnode_handle *fwnode, - unsigned int asd_struct_size); +__v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, + struct fwnode_handle *fwnode, + unsigned int asd_struct_size); +#define v4l2_async_notifier_add_fwnode_subdev(__notifier, __fwnode, __type) \ +((__type *)__v4l2_async_notifier_add_fwnode_subdev(__notifier, __fwnode, \ + sizeof(__type))) /** * v4l2_async_notifier_add_fwnode_remote_subdev - Allocate and add a fwnode @@ -194,9 +197,12 @@ v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, * exception that the fwnode refers to a local endpoint, not the remote one. */ struct v4l2_async_subdev * -v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, - struct fwnode_handle *endpoint, - unsigned int asd_struct_size); +__v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, + struct fwnode_handle *endpoint, + unsigned int asd_struct_size); +#define v4l2_async_notifier_add_fwnode_remote_subdev(__notifier, __ep, __type) \ +((__type *)__v4l2_async_notifier_add_fwnode_remote_subdev(__notifier, __ep, \ + sizeof(__type))) /** * v4l2_async_notifier_add_i2c_subdev - Allocate and add an i2c async @@ -214,9 +220,12 @@ v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, * Same as above but for I2C matched sub-devices. */ struct v4l2_async_subdev * -v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, - int adapter_id, unsigned short address, - unsigned int asd_struct_size); +__v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, + int adapter_id, unsigned short address, + unsigned int asd_struct_size); +#define v4l2_async_notifier_add_i2c_subdev(__notifier, __adap, __addr, __type) \ +((__type *)__v4l2_async_notifier_add_i2c_subdev(__notifier, __adap, __addr, \ + sizeof(__type))) /** * v4l2_async_notifier_register - registers a subdevice asynchronous notifier -- cgit v1.2.3 From 3e90e5ad9497d993852e638f75e9e154787bdd61 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jan 2021 02:52:55 +0100 Subject: media: Clarify v4l2-async subdevice addition API Now that most users of v4l2_async_notifier_add_subdev have been converted, let's fix the documentation so it's more clear how the v4l2-async API should be used. Document functions that drivers should use, and their purpose. Signed-off-by: Ezequiel Garcia Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Reviewed-by: Helen Koike Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/media/v4l2-subdev.rst | 48 +++++++++++++++++++++----- include/media/v4l2-async.h | 15 +++++--- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/Documentation/driver-api/media/v4l2-subdev.rst b/Documentation/driver-api/media/v4l2-subdev.rst index 0e82c77cf3e2..8b53da2f9c74 100644 --- a/Documentation/driver-api/media/v4l2-subdev.rst +++ b/Documentation/driver-api/media/v4l2-subdev.rst @@ -197,15 +197,45 @@ unregister the notifier the driver has to call takes two arguments: a pointer to struct :c:type:`v4l2_device` and a pointer to struct :c:type:`v4l2_async_notifier`. -Before registering the notifier, bridge drivers must do two things: -first, the notifier must be initialized using the -:c:func:`v4l2_async_notifier_init`. Second, bridge drivers can then -begin to form a list of subdevice descriptors that the bridge device -needs for its operation. Subdevice descriptors are added to the notifier -using the :c:func:`v4l2_async_notifier_add_subdev` call. This function -takes two arguments: a pointer to struct :c:type:`v4l2_async_notifier`, -and a pointer to the subdevice descripter, which is of type struct -:c:type:`v4l2_async_subdev`. +Before registering the notifier, bridge drivers must do two things: first, the +notifier must be initialized using the :c:func:`v4l2_async_notifier_init`. +Second, bridge drivers can then begin to form a list of subdevice descriptors +that the bridge device needs for its operation. Several functions are available +to add subdevice descriptors to a notifier, depending on the type of device and +the needs of the driver. + +:c:func:`v4l2_async_notifier_add_fwnode_remote_subdev` and +:c:func:`v4l2_async_notifier_add_i2c_subdev` are for bridge and ISP drivers for +registering their async sub-devices with the notifier. + +:c:func:`v4l2_async_register_subdev_sensor_common` is a helper function for +sensor drivers registering their own async sub-device, but it also registers a +notifier and further registers async sub-devices for lens and flash devices +found in firmware. The notifier for the sub-device is unregistered with the +async sub-device. + +These functions allocate an async sub-device descriptor which is of type struct +:c:type:`v4l2_async_subdev` embedded in a driver-specific struct. The &struct +:c:type:`v4l2_async_subdev` shall be the first member of this struct: + +.. code-block:: c + + struct my_async_subdev { + struct v4l2_async_subdev asd; + ... + }; + + struct my_async_subdev *my_asd; + struct fwnode_handle *ep; + + ... + + my_asd = v4l2_async_notifier_add_fwnode_remote_subdev(¬ifier, ep, + struct my_async_subdev); + fwnode_handle_put(ep); + + if (IS_ERR(asd)) + return PTR_ERR(asd); The V4L2 core will then use these descriptors to match asynchronously registered subdevices to them. If a match is detected the ``.bound()`` diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 192a11bdc4ad..6f22daa6f067 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -128,7 +128,12 @@ void v4l2_async_debug_init(struct dentry *debugfs_dir); * @notifier: pointer to &struct v4l2_async_notifier * * This function initializes the notifier @asd_list. It must be called - * before the first call to @v4l2_async_notifier_add_subdev. + * before adding a subdevice to a notifier, using one of: + * @v4l2_async_notifier_add_fwnode_remote_subdev, + * @v4l2_async_notifier_add_fwnode_subdev, + * @v4l2_async_notifier_add_i2c_subdev, + * @__v4l2_async_notifier_add_subdev or + * @v4l2_async_notifier_parse_fwnode_endpoints. */ void v4l2_async_notifier_init(struct v4l2_async_notifier *notifier); @@ -262,9 +267,11 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier); * sub-devices allocated for the purposes of the notifier but not the notifier * itself. The user is responsible for calling this function to clean up the * notifier after calling - * @v4l2_async_notifier_add_subdev, - * @v4l2_async_notifier_parse_fwnode_endpoints or - * @v4l2_fwnode_reference_parse_sensor_common. + * @v4l2_async_notifier_add_fwnode_remote_subdev, + * @v4l2_async_notifier_add_fwnode_subdev, + * @v4l2_async_notifier_add_i2c_subdev, + * @__v4l2_async_notifier_add_subdev or + * @v4l2_async_notifier_parse_fwnode_endpoints. * * There is no harm from calling v4l2_async_notifier_cleanup in other * cases as long as its memory has been zeroed after it has been -- cgit v1.2.3 From 918b866edfec39e22ccb9528bc11a0dd6ca62c2b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 28 Jan 2021 10:44:24 +0100 Subject: media: dt-bindings: Remove old ov5647.yaml file, update ovti,ov5647.yaml Recently ov5647.yaml got renamed as ovti,ov5647.yaml. As part of the video-interfaces DT schema conversion that was unintentionally brought back. Fix this by applying the schema changes to the new file and removing the old one. Fixes: 066a94e28a23 ("media: dt-bindings: media: Use graph and video-interfaces schemas") Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/ov5647.yaml | 76 ---------------------- .../devicetree/bindings/media/i2c/ovti,ov5647.yaml | 20 ++---- 2 files changed, 4 insertions(+), 92 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/i2c/ov5647.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/ov5647.yaml b/Documentation/devicetree/bindings/media/i2c/ov5647.yaml deleted file mode 100644 index 3b1ea9da437a..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov5647.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/media/i2c/ov5647.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Omnivision OV5647 raw image sensor - -maintainers: - - Dave Stevenson - - Jacopo Mondi - -description: |- - The OV5647 is a raw image sensor with MIPI CSI-2 and CCP2 image data - interfaces and CCI (I2C compatible) control bus. - -properties: - compatible: - const: ovti,ov5647 - - reg: - description: I2C device address. - maxItems: 1 - - clocks: - description: Reference to the xclk clock. - maxItems: 1 - - pwdn-gpios: - description: Reference to the GPIO connected to the pwdn pin. Active high. - maxItems: 1 - - port: - $ref: /schemas/graph.yaml#/$defs/port-base - - properties: - endpoint: - $ref: /schemas/media/video-interfaces.yaml# - unevaluatedProperties: false - - properties: - clock-noncontinuous: true - - additionalProperties: false - -required: - - compatible - - reg - - clocks - - port - -additionalProperties: false - -examples: - - | - #include - - i2c { - #address-cells = <1>; - #size-cells = <0>; - - ov5647: camera@36 { - compatible = "ovti,ov5647"; - reg = <0x36>; - clocks = <&camera_clk>; - pwdn-gpios = <&pioE 29 GPIO_ACTIVE_HIGH>; - - port { - camera_out: endpoint { - remote-endpoint = <&csi1_ep1>; - }; - }; - }; - }; - -... diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml index 429566c9ee1d..1ab22e75d3c6 100644 --- a/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml @@ -31,27 +31,15 @@ properties: maxItems: 1 port: - type: object - description: |- - Should contain one endpoint sub-node used to model connection to the - video receiver according to the specification defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. + $ref: /schemas/graph.yaml#/$defs/port-base properties: endpoint: - type: object + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false properties: - remote-endpoint: - description: |- - phandle to the video receiver input port. - - clock-noncontinuous: - type: boolean - description: |- - Set to true to allow MIPI CSI-2 non-continuous clock operations. - - additionalProperties: false + clock-noncontinuous: true additionalProperties: false -- cgit v1.2.3 From 655ae29da72a693cf294bba3c3322e662ff75bd3 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Wed, 27 Jan 2021 19:01:43 +0100 Subject: media: marvell-ccic: power up the device on mclk enable Writing to REG_CLKCTRL with the power off causes a hang. Enable the device first. Cc: stable@vger.kernel.org # 5.10+ Signed-off-by: Lubomir Rintel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/mcam-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 153277e4fe80..141bf5d97a04 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -931,6 +931,7 @@ static int mclk_enable(struct clk_hw *hw) mclk_div = 2; } + pm_runtime_get_sync(cam->dev); clk_enable(cam->clk[0]); mcam_reg_write(cam, REG_CLKCTRL, (mclk_src << 29) | mclk_div); mcam_ctlr_power_up(cam); @@ -944,6 +945,7 @@ static void mclk_disable(struct clk_hw *hw) mcam_ctlr_power_down(cam); clk_disable(cam->clk[0]); + pm_runtime_put(cam->dev); } static unsigned long mclk_recalc_rate(struct clk_hw *hw, -- cgit v1.2.3 From a59f853b3b4bce1471ad164357c3f51bdd0e6ba9 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 14 Jan 2021 18:04:25 +0100 Subject: media: i2c: Add driver for RDACM21 camera module The RDACM21 is a GMSL camera supporting 1280x1080 resolution images developed by IMI based on an Omnivision OV10640 sensor, an Omnivision OV490 ISP and a Maxim MAX9271 GMSL serializer. The driver uses the max9271 library module, to maximize code reuse with other camera module drivers using the same serializer, such as rdacm20. Reviewed-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 12 + drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 2 + drivers/media/i2c/rdacm21.c | 623 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 650 insertions(+) create mode 100644 drivers/media/i2c/rdacm21.c diff --git a/MAINTAINERS b/MAINTAINERS index 45c70f101e61..af8f06213f52 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14971,6 +14971,18 @@ F: drivers/media/i2c/max9271.c F: drivers/media/i2c/max9271.h F: drivers/media/i2c/rdacm20.c +RDACM21 Camera Sensor +M: Jacopo Mondi +M: Kieran Bingham +M: Laurent Pinchart +M: Niklas Söderlund +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/rdacm2x-gmsl.yaml +F: drivers/media/i2c/max9271.c +F: drivers/media/i2c/max9271.h +F: drivers/media/i2c/rdacm21.c + RDC R-321X SoC M: Florian Fainelli S: Maintained diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index bb1b5a340431..ad92f4789b83 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1239,6 +1239,19 @@ config VIDEO_RDACM20 This camera should be used in conjunction with a GMSL deserialiser such as the MAX9286. +config VIDEO_RDACM21 + tristate "IMI RDACM21 camera support" + depends on I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + help + This driver supports the IMI RDACM21 GMSL camera, used in + ADAS systems. + + This camera should be used in conjunction with a GMSL + deserialiser such as the MAX9286. + config VIDEO_RJ54N1 tristate "Sharp RJ54N1CB0C sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 5b1dcfa3ce76..f5cd92bc285d 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -126,6 +126,8 @@ obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o rdacm20-camera_module-objs := rdacm20.o max9271.o obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o +rdacm21-camera_module-objs := rdacm21.o max9271.o +obj-$(CONFIG_VIDEO_RDACM21) += rdacm21-camera_module.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c new file mode 100644 index 000000000000..dcc21515e5a4 --- /dev/null +++ b/drivers/media/i2c/rdacm21.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IMI RDACM21 GMSL Camera Driver + * + * Copyright (C) 2017-2020 Jacopo Mondi + * Copyright (C) 2017-2019 Kieran Bingham + * Copyright (C) 2017-2019 Laurent Pinchart + * Copyright (C) 2017-2019 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "max9271.h" + +#define MAX9271_RESET_CYCLES 10 + +#define OV490_I2C_ADDRESS 0x24 + +#define OV490_PAGE_HIGH_REG 0xfffd +#define OV490_PAGE_LOW_REG 0xfffe + +/* + * The SCCB slave handling is undocumented; the registers naming scheme is + * totally arbitrary. + */ +#define OV490_SCCB_SLAVE_WRITE 0x00 +#define OV490_SCCB_SLAVE_READ 0x01 +#define OV490_SCCB_SLAVE0_DIR 0x80195000 +#define OV490_SCCB_SLAVE0_ADDR_HIGH 0x80195001 +#define OV490_SCCB_SLAVE0_ADDR_LOW 0x80195002 + +#define OV490_DVP_CTRL3 0x80286009 + +#define OV490_ODS_CTRL_FRAME_OUTPUT_EN 0x0c +#define OV490_ODS_CTRL 0x8029d000 + +#define OV490_HOST_CMD 0x808000c0 +#define OV490_HOST_CMD_TRIGGER 0xc1 + +#define OV490_ID_VAL 0x0490 +#define OV490_ID(_p, _v) ((((_p) & 0xff) << 8) | ((_v) & 0xff)) +#define OV490_PID 0x8080300a +#define OV490_VER 0x8080300b +#define OV490_PID_TIMEOUT 20 +#define OV490_OUTPUT_EN_TIMEOUT 300 + +#define OV490_GPIO0 BIT(0) +#define OV490_SPWDN0 BIT(0) +#define OV490_GPIO_SEL0 0x80800050 +#define OV490_GPIO_SEL1 0x80800051 +#define OV490_GPIO_DIRECTION0 0x80800054 +#define OV490_GPIO_DIRECTION1 0x80800055 +#define OV490_GPIO_OUTPUT_VALUE0 0x80800058 +#define OV490_GPIO_OUTPUT_VALUE1 0x80800059 + +#define OV490_ISP_HSIZE_LOW 0x80820060 +#define OV490_ISP_HSIZE_HIGH 0x80820061 +#define OV490_ISP_VSIZE_LOW 0x80820062 +#define OV490_ISP_VSIZE_HIGH 0x80820063 + +#define OV10640_ID_HIGH 0xa6 +#define OV10640_CHIP_ID 0x300a +#define OV10640_PIXEL_RATE 55000000 + +struct rdacm21_device { + struct device *dev; + struct max9271_device serializer; + struct i2c_client *isp; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_mbus_framefmt fmt; + struct v4l2_ctrl_handler ctrls; + u32 addrs[2]; + u16 last_page; +}; + +static inline struct rdacm21_device *sd_to_rdacm21(struct v4l2_subdev *sd) +{ + return container_of(sd, struct rdacm21_device, sd); +} + +static const struct ov490_reg { + u16 reg; + u8 val; +} ov490_regs_wizard[] = { + {0xfffd, 0x80}, + {0xfffe, 0x82}, + {0x0071, 0x11}, + {0x0075, 0x11}, + {0xfffe, 0x29}, + {0x6010, 0x01}, + /* + * OV490 EMB line disable in YUV and RAW data, + * NOTE: EMB line is still used in ISP and sensor + */ + {0xe000, 0x14}, + {0xfffe, 0x28}, + {0x6000, 0x04}, + {0x6004, 0x00}, + /* + * PCLK polarity - useless due to silicon bug. + * Use 0x808000bb register instead. + */ + {0x6008, 0x00}, + {0xfffe, 0x80}, + {0x0091, 0x00}, + /* bit[3]=0 - PCLK polarity workaround. */ + {0x00bb, 0x1d}, + /* Ov490 FSIN: app_fsin_from_fsync */ + {0xfffe, 0x85}, + {0x0008, 0x00}, + {0x0009, 0x01}, + /* FSIN0 source. */ + {0x000A, 0x05}, + {0x000B, 0x00}, + /* FSIN0 delay. */ + {0x0030, 0x02}, + {0x0031, 0x00}, + {0x0032, 0x00}, + {0x0033, 0x00}, + /* FSIN1 delay. */ + {0x0038, 0x02}, + {0x0039, 0x00}, + {0x003A, 0x00}, + {0x003B, 0x00}, + /* FSIN0 length. */ + {0x0070, 0x2C}, + {0x0071, 0x01}, + {0x0072, 0x00}, + {0x0073, 0x00}, + /* FSIN1 length. */ + {0x0074, 0x64}, + {0x0075, 0x00}, + {0x0076, 0x00}, + {0x0077, 0x00}, + {0x0000, 0x14}, + {0x0001, 0x00}, + {0x0002, 0x00}, + {0x0003, 0x00}, + /* + * Load fsin0,load fsin1,load other, + * It will be cleared automatically. + */ + {0x0004, 0x32}, + {0x0005, 0x00}, + {0x0006, 0x00}, + {0x0007, 0x00}, + {0xfffe, 0x80}, + /* Sensor FSIN. */ + {0x0081, 0x00}, + /* ov10640 FSIN enable */ + {0xfffe, 0x19}, + {0x5000, 0x00}, + {0x5001, 0x30}, + {0x5002, 0x8c}, + {0x5003, 0xb2}, + {0xfffe, 0x80}, + {0x00c0, 0xc1}, + /* ov10640 HFLIP=1 by default */ + {0xfffe, 0x19}, + {0x5000, 0x01}, + {0x5001, 0x00}, + {0xfffe, 0x80}, + {0x00c0, 0xdc}, +}; + +static int ov490_read(struct rdacm21_device *dev, u16 reg, u8 *val) +{ + u8 buf[2] = { reg >> 8, reg }; + int ret; + + ret = i2c_master_send(dev->isp, buf, 2); + if (ret == 2) + ret = i2c_master_recv(dev->isp, val, 1); + + if (ret < 0) { + dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n", + __func__, reg, ret); + return ret; + } + + return 0; +} + +static int ov490_write(struct rdacm21_device *dev, u16 reg, u8 val) +{ + u8 buf[3] = { reg >> 8, reg, val }; + int ret; + + ret = i2c_master_send(dev->isp, buf, 3); + if (ret < 0) { + dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n", + __func__, reg, ret); + return ret; + } + + return 0; +} + +static int ov490_set_page(struct rdacm21_device *dev, u16 page) +{ + u8 page_high = page >> 8; + u8 page_low = page; + int ret; + + if (page == dev->last_page) + return 0; + + if (page_high != (dev->last_page >> 8)) { + ret = ov490_write(dev, OV490_PAGE_HIGH_REG, page_high); + if (ret) + return ret; + } + + if (page_low != (u8)dev->last_page) { + ret = ov490_write(dev, OV490_PAGE_LOW_REG, page_low); + if (ret) + return ret; + } + + dev->last_page = page; + usleep_range(100, 150); + + return 0; +} + +static int ov490_read_reg(struct rdacm21_device *dev, u32 reg, u8 *val) +{ + int ret; + + ret = ov490_set_page(dev, reg >> 16); + if (ret) + return ret; + + ret = ov490_read(dev, (u16)reg, val); + if (ret) + return ret; + + dev_dbg(dev->dev, "%s: 0x%08x = 0x%02x\n", __func__, reg, *val); + + return 0; +} + +static int ov490_write_reg(struct rdacm21_device *dev, u32 reg, u8 val) +{ + int ret; + + ret = ov490_set_page(dev, reg >> 16); + if (ret) + return ret; + + ret = ov490_write(dev, (u16)reg, val); + if (ret) + return ret; + + dev_dbg(dev->dev, "%s: 0x%08x = 0x%02x\n", __func__, reg, val); + + return 0; +} + +static int rdacm21_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct rdacm21_device *dev = sd_to_rdacm21(sd); + + /* + * Enable serial link now that the ISP provides a valid pixel clock + * to start serializing video data on the GMSL link. + */ + return max9271_set_serial_link(&dev->serializer, enable); +} + +static int rdacm21_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_YUYV8_1X16; + + return 0; +} + +static int rdacm21_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct rdacm21_device *dev = sd_to_rdacm21(sd); + + if (format->pad) + return -EINVAL; + + mf->width = dev->fmt.width; + mf->height = dev->fmt.height; + mf->code = MEDIA_BUS_FMT_YUYV8_1X16; + mf->colorspace = V4L2_COLORSPACE_SRGB; + mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_601; + mf->quantization = V4L2_QUANTIZATION_FULL_RANGE; + mf->xfer_func = V4L2_XFER_FUNC_NONE; + + return 0; +} + +static const struct v4l2_subdev_video_ops rdacm21_video_ops = { + .s_stream = rdacm21_s_stream, +}; + +static const struct v4l2_subdev_pad_ops rdacm21_subdev_pad_ops = { + .enum_mbus_code = rdacm21_enum_mbus_code, + .get_fmt = rdacm21_get_fmt, + .set_fmt = rdacm21_get_fmt, +}; + +static const struct v4l2_subdev_ops rdacm21_subdev_ops = { + .video = &rdacm21_video_ops, + .pad = &rdacm21_subdev_pad_ops, +}; + +static int ov10640_initialize(struct rdacm21_device *dev) +{ + u8 val; + + /* Power-up OV10640 by setting RESETB and PWDNB pins high. */ + ov490_write_reg(dev, OV490_GPIO_SEL0, OV490_GPIO0); + ov490_write_reg(dev, OV490_GPIO_SEL1, OV490_SPWDN0); + ov490_write_reg(dev, OV490_GPIO_DIRECTION0, OV490_GPIO0); + ov490_write_reg(dev, OV490_GPIO_DIRECTION1, OV490_SPWDN0); + ov490_write_reg(dev, OV490_GPIO_OUTPUT_VALUE0, OV490_GPIO0); + ov490_write_reg(dev, OV490_GPIO_OUTPUT_VALUE0, OV490_SPWDN0); + usleep_range(3000, 5000); + + /* Read OV10640 ID to test communications. */ + ov490_write_reg(dev, OV490_SCCB_SLAVE0_DIR, OV490_SCCB_SLAVE_READ); + ov490_write_reg(dev, OV490_SCCB_SLAVE0_ADDR_HIGH, OV10640_CHIP_ID >> 8); + ov490_write_reg(dev, OV490_SCCB_SLAVE0_ADDR_LOW, (u8)OV10640_CHIP_ID); + + /* Trigger SCCB slave transaction and give it some time to complete. */ + ov490_write_reg(dev, OV490_HOST_CMD, OV490_HOST_CMD_TRIGGER); + usleep_range(1000, 1500); + + ov490_read_reg(dev, OV490_SCCB_SLAVE0_DIR, &val); + if (val != OV10640_ID_HIGH) { + dev_err(dev->dev, "OV10640 ID mismatch: (0x%02x)\n", val); + return -ENODEV; + } + + dev_dbg(dev->dev, "OV10640 ID = 0x%2x\n", val); + + return 0; +} + +static int ov490_initialize(struct rdacm21_device *dev) +{ + u8 pid, ver, val; + unsigned int i; + int ret; + + /* + * Read OV490 Id to test communications. Give it up to 40msec to + * exit from reset. + */ + for (i = 0; i < OV490_PID_TIMEOUT; ++i) { + ret = ov490_read_reg(dev, OV490_PID, &pid); + if (ret == 0) + break; + usleep_range(1000, 2000); + } + if (i == OV490_PID_TIMEOUT) { + dev_err(dev->dev, "OV490 PID read failed (%d)\n", ret); + return ret; + } + + ret = ov490_read_reg(dev, OV490_VER, &ver); + if (ret < 0) + return ret; + + if (OV490_ID(pid, ver) != OV490_ID_VAL) { + dev_err(dev->dev, "OV490 ID mismatch (0x%04x)\n", + OV490_ID(pid, ver)); + return -ENODEV; + } + + /* Wait for firmware boot by reading streamon status. */ + for (i = 0; i < OV490_OUTPUT_EN_TIMEOUT; ++i) { + ov490_read_reg(dev, OV490_ODS_CTRL, &val); + if (val == OV490_ODS_CTRL_FRAME_OUTPUT_EN) + break; + usleep_range(1000, 2000); + } + if (i == OV490_OUTPUT_EN_TIMEOUT) { + dev_err(dev->dev, "Timeout waiting for firmware boot\n"); + return -ENODEV; + } + + ret = ov10640_initialize(dev); + if (ret) + return ret; + + /* Program OV490 with register-value table. */ + for (i = 0; i < ARRAY_SIZE(ov490_regs_wizard); ++i) { + ret = ov490_write(dev, ov490_regs_wizard[i].reg, + ov490_regs_wizard[i].val); + if (ret < 0) { + dev_err(dev->dev, + "%s: register %u (0x%04x) write failed (%d)\n", + __func__, i, ov490_regs_wizard[i].reg, ret); + + return -EIO; + } + + usleep_range(100, 150); + } + + /* + * The ISP is programmed with the content of a serial flash memory. + * Read the firmware configuration to reflect it through the V4L2 APIs. + */ + ov490_read_reg(dev, OV490_ISP_HSIZE_HIGH, &val); + dev->fmt.width = (val & 0xf) << 8; + ov490_read_reg(dev, OV490_ISP_HSIZE_LOW, &val); + dev->fmt.width |= (val & 0xff); + + ov490_read_reg(dev, OV490_ISP_VSIZE_HIGH, &val); + dev->fmt.height = (val & 0xf) << 8; + ov490_read_reg(dev, OV490_ISP_VSIZE_LOW, &val); + dev->fmt.height |= val & 0xff; + + /* Set bus width to 12 bits with [0:11] ordering. */ + ov490_write_reg(dev, OV490_DVP_CTRL3, 0x10); + + dev_info(dev->dev, "Identified RDACM21 camera module\n"); + + return 0; +} + +static int rdacm21_initialize(struct rdacm21_device *dev) +{ + int ret; + + /* Verify communication with the MAX9271: ping to wakeup. */ + dev->serializer.client->addr = MAX9271_DEFAULT_ADDR; + i2c_smbus_read_byte(dev->serializer.client); + usleep_range(3000, 5000); + + /* Enable reverse channel and disable the serial link. */ + ret = max9271_set_serial_link(&dev->serializer, false); + if (ret) + return ret; + + /* Configure I2C bus at 105Kbps speed and configure GMSL. */ + ret = max9271_configure_i2c(&dev->serializer, + MAX9271_I2CSLVSH_469NS_234NS | + MAX9271_I2CSLVTO_1024US | + MAX9271_I2CMSTBT_105KBPS); + if (ret) + return ret; + + ret = max9271_verify_id(&dev->serializer); + if (ret) + return ret; + + /* Enable GPIO1 and hold OV490 in reset during max9271 configuration. */ + ret = max9271_enable_gpios(&dev->serializer, MAX9271_GPIO1OUT); + if (ret) + return ret; + + ret = max9271_clear_gpios(&dev->serializer, MAX9271_GPIO1OUT); + if (ret) + return ret; + + ret = max9271_configure_gmsl_link(&dev->serializer); + if (ret) + return ret; + + ret = max9271_set_address(&dev->serializer, dev->addrs[0]); + if (ret) + return ret; + dev->serializer.client->addr = dev->addrs[0]; + + ret = max9271_set_translation(&dev->serializer, dev->addrs[1], + OV490_I2C_ADDRESS); + if (ret) + return ret; + dev->isp->addr = dev->addrs[1]; + + /* Release OV490 from reset and initialize it. */ + ret = max9271_set_gpios(&dev->serializer, MAX9271_GPIO1OUT); + if (ret) + return ret; + usleep_range(3000, 5000); + + ret = ov490_initialize(dev); + if (ret) + return ret; + + /* + * Set reverse channel high threshold to increase noise immunity. + * + * This should be compensated by increasing the reverse channel + * amplitude on the remote deserializer side. + */ + return max9271_set_high_threshold(&dev->serializer, true); +} + +static int rdacm21_probe(struct i2c_client *client) +{ + struct rdacm21_device *dev; + struct fwnode_handle *ep; + int ret; + + dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->dev = &client->dev; + dev->serializer.client = client; + + ret = of_property_read_u32_array(client->dev.of_node, "reg", + dev->addrs, 2); + if (ret < 0) { + dev_err(dev->dev, "Invalid DT reg property: %d\n", ret); + return -EINVAL; + } + + /* Create the dummy I2C client for the sensor. */ + dev->isp = i2c_new_dummy_device(client->adapter, OV490_I2C_ADDRESS); + if (IS_ERR(dev->isp)) + return PTR_ERR(dev->isp); + + ret = rdacm21_initialize(dev); + if (ret < 0) + goto error; + + /* Initialize and register the subdevice. */ + v4l2_i2c_subdev_init(&dev->sd, client, &rdacm21_subdev_ops); + dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + v4l2_ctrl_handler_init(&dev->ctrls, 1); + v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, + OV10640_PIXEL_RATE, OV10640_PIXEL_RATE, 1, + OV10640_PIXEL_RATE); + dev->sd.ctrl_handler = &dev->ctrls; + + ret = dev->ctrls.error; + if (ret) + goto error_free_ctrls; + + dev->pad.flags = MEDIA_PAD_FL_SOURCE; + dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); + if (ret < 0) + goto error_free_ctrls; + + ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + if (!ep) { + dev_err(&client->dev, + "Unable to get endpoint in node %pOF\n", + client->dev.of_node); + ret = -ENOENT; + goto error_free_ctrls; + } + dev->sd.fwnode = ep; + + ret = v4l2_async_register_subdev(&dev->sd); + if (ret) + goto error_put_node; + + return 0; + +error_put_node: + fwnode_handle_put(dev->sd.fwnode); +error_free_ctrls: + v4l2_ctrl_handler_free(&dev->ctrls); +error: + i2c_unregister_device(dev->isp); + + return ret; +} + +static int rdacm21_remove(struct i2c_client *client) +{ + struct rdacm21_device *dev = sd_to_rdacm21(i2c_get_clientdata(client)); + + v4l2_async_unregister_subdev(&dev->sd); + v4l2_ctrl_handler_free(&dev->ctrls); + i2c_unregister_device(dev->isp); + fwnode_handle_put(dev->sd.fwnode); + + return 0; +} + +static const struct of_device_id rdacm21_of_ids[] = { + { .compatible = "imi,rdacm21" }, + { } +}; +MODULE_DEVICE_TABLE(of, rdacm21_of_ids); + +static struct i2c_driver rdacm21_i2c_driver = { + .driver = { + .name = "rdacm21", + .of_match_table = rdacm21_of_ids, + }, + .probe_new = rdacm21_probe, + .remove = rdacm21_remove, +}; + +module_i2c_driver(rdacm21_i2c_driver); + +MODULE_DESCRIPTION("GMSL Camera driver for RDACM21"); +MODULE_AUTHOR("Jacopo Mondi"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From c00b72491366529be722de20679b169c4f479f39 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 14 Jan 2021 18:04:26 +0100 Subject: media: dt-bindings: media: max9286: Document 'maxim,reverse-channel-microvolt' Document the 'maxim,reverse-channel-microvolt' vendor property in the bindings document of the max9286 driver. The newly introduced property allows to specify the initial configuration of the GMSL reverse control channel to accommodate remote serializers pre-programmed with the high threshold power supply noise immunity enabled. Reviewed-by: Rob Herring Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/i2c/maxim,max9286.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml index bf4fa56ffcc7..ee16102fdfe7 100644 --- a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml @@ -50,6 +50,26 @@ properties: '#gpio-cells': const: 2 + maxim,reverse-channel-microvolt: + minimum: 30000 + maximum: 200000 + default: 170000 + description: | + Initial amplitude of the reverse control channel, in micro volts. + + The initial amplitude shall be adjusted to a value compatible with the + configuration of the connected remote serializer. + + Some camera modules (for example RDACM20) include an on-board MCU that + pre-programs the embedded serializer with power supply noise immunity + (high-threshold) enabled. A typical value of the deserializer's reverse + channel amplitude to communicate with pre-programmed serializers is + 170000 micro volts. + + A typical value for the reverse channel amplitude to communicate with + a remote serializer whose high-threshold noise immunity is not enabled + is 100000 micro volts + ports: $ref: /schemas/graph.yaml#/properties/ports @@ -185,6 +205,8 @@ examples: gpio-controller; #gpio-cells = <2>; + maxim,reverse-channel-microvolt = <170000>; + ports { #address-cells = <1>; #size-cells = <0>; -- cgit v1.2.3 From 02b57eb3b5760520154f6c1b7db4e58a1780f3b6 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 14 Jan 2021 18:04:27 +0100 Subject: media: i2c: max9286: Break-out reverse channel setup Break out the reverse channel setup configuration procedure to its own function. This change prepares for configuring the reverse channel conditionally to the remote side high threshold configuration. No functional changes intended. Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/max9286.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index fd2151f2fa36..ebe9c2215b48 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -336,6 +336,22 @@ static void max9286_configure_i2c(struct max9286_priv *priv, bool localack) usleep_range(3000, 5000); } +static void max9286_reverse_channel_setup(struct max9286_priv *priv) +{ + /* + * Reverse channel setup. + * + * - Enable custom reverse channel configuration (through register 0x3f) + * and set the first pulse length to 35 clock cycles. + * - Increase the reverse channel amplitude to 170mV to accommodate the + * high threshold enabled by the serializer driver. + */ + max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35)); + max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) | + MAX9286_REV_AMP_X); + usleep_range(2000, 2500); +} + /* * max9286_check_video_links() - Make sure video links are detected and locked * @@ -941,19 +957,7 @@ static int max9286_setup(struct max9286_priv *priv) * only. This should be disabled after the mux is initialised. */ max9286_configure_i2c(priv, true); - - /* - * Reverse channel setup. - * - * - Enable custom reverse channel configuration (through register 0x3f) - * and set the first pulse length to 35 clock cycles. - * - Increase the reverse channel amplitude to 170mV to accommodate the - * high threshold enabled by the serializer driver. - */ - max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35)); - max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) | - MAX9286_REV_AMP_X); - usleep_range(2000, 2500); + max9286_reverse_channel_setup(priv); /* * Enable GMSL links, mask unused ones and autodetect link -- cgit v1.2.3 From 5a386b1ff7710dec76c586a3e79f862fc9fc9d73 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 14 Jan 2021 18:04:28 +0100 Subject: media: i2c: max9286: Make channel amplitude programmable Instrument the function that configures the reverse channel with a programmable amplitude value. This change serves to prepare to adjust the reverse channel amplitude depending on the remote end high-threshold configuration. Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/max9286.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index ebe9c2215b48..4d7587dfb5aa 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -336,19 +336,28 @@ static void max9286_configure_i2c(struct max9286_priv *priv, bool localack) usleep_range(3000, 5000); } -static void max9286_reverse_channel_setup(struct max9286_priv *priv) +static void max9286_reverse_channel_setup(struct max9286_priv *priv, + unsigned int chan_amplitude) { + /* Reverse channel transmission time: default to 1. */ + u8 chan_config = MAX9286_REV_TRF(1); + /* * Reverse channel setup. * * - Enable custom reverse channel configuration (through register 0x3f) * and set the first pulse length to 35 clock cycles. - * - Increase the reverse channel amplitude to 170mV to accommodate the - * high threshold enabled by the serializer driver. + * - Adjust reverse channel amplitude: values > 130 are programmed + * using the additional +100mV REV_AMP_X boost flag */ max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35)); - max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) | - MAX9286_REV_AMP_X); + + if (chan_amplitude > 100) { + /* It is not possible to express values (100 < x < 130) */ + chan_amplitude = max(30U, chan_amplitude - 100); + chan_config |= MAX9286_REV_AMP_X; + } + max9286_write(priv, 0x3b, chan_config | MAX9286_REV_AMP(chan_amplitude)); usleep_range(2000, 2500); } @@ -957,7 +966,7 @@ static int max9286_setup(struct max9286_priv *priv) * only. This should be disabled after the mux is initialised. */ max9286_configure_i2c(priv, true); - max9286_reverse_channel_setup(priv); + max9286_reverse_channel_setup(priv, 170); /* * Enable GMSL links, mask unused ones and autodetect link -- cgit v1.2.3 From 85cb767cbfcd89d9949fca91bda8e7330f33e12a Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 14 Jan 2021 18:04:29 +0100 Subject: media: i2c: max9286: Configure reverse channel amplitude Adjust the initial reverse channel amplitude parsing from firmware interface the 'maxim,reverse-channel-microvolt' property. This change is required for both rdacm20 and rdacm21 camera modules to be correctly probed when used in combination with the max9286 deserializer. Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/max9286.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index 4d7587dfb5aa..6fd4d59fcc72 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -163,6 +163,8 @@ struct max9286_priv { unsigned int mux_channel; bool mux_open; + u32 reverse_channel_mv; + struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *pixelrate; @@ -556,10 +558,14 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier, * All enabled sources have probed and enabled their reverse control * channels: * + * - Increase the reverse channel amplitude to compensate for the + * remote ends high threshold, if not done already * - Verify all configuration links are properly detected * - Disable auto-ack as communication on the control channel are now * stable. */ + if (priv->reverse_channel_mv < 170) + max9286_reverse_channel_setup(priv, 170); max9286_check_config_link(priv, priv->source_mask); /* @@ -966,7 +972,7 @@ static int max9286_setup(struct max9286_priv *priv) * only. This should be disabled after the mux is initialised. */ max9286_configure_i2c(priv, true); - max9286_reverse_channel_setup(priv, 170); + max9286_reverse_channel_setup(priv, priv->reverse_channel_mv); /* * Enable GMSL links, mask unused ones and autodetect link @@ -1130,6 +1136,7 @@ static int max9286_parse_dt(struct max9286_priv *priv) struct device_node *i2c_mux; struct device_node *node = NULL; unsigned int i2c_mux_mask = 0; + u32 reverse_channel_microvolt; /* Balance the of_node_put() performed by of_find_node_by_name(). */ of_node_get(dev->of_node); @@ -1220,6 +1227,20 @@ static int max9286_parse_dt(struct max9286_priv *priv) } of_node_put(node); + /* + * Parse the initial value of the reverse channel amplitude from + * the firmware interface and convert it to millivolts. + * + * Default it to 170mV for backward compatibility with DTBs that do not + * provide the property. + */ + if (of_property_read_u32(dev->of_node, + "maxim,reverse-channel-microvolt", + &reverse_channel_microvolt)) + priv->reverse_channel_mv = 170; + else + priv->reverse_channel_mv = reverse_channel_microvolt / 1000U; + priv->route_mask = priv->source_mask; return 0; -- cgit v1.2.3 From ded8f0355a8821262a27a398d3f5587c29665dae Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Thu, 19 Nov 2020 23:22:48 +0100 Subject: media: i2c: rdacm20: Constify static structs The only usage of rdacm20_video_ops is to assign it to the video field in the v4l2_subdev_ops struct which is a pointer to const, and the only usage of rdacm20_subdev_ops is to pass its address to v4l2_i2c_subdev_init() which accepts a pointer to const. Make them const to allow the compiler to put them in read-only memory. Signed-off-by: Rikard Falkeborn Acked-by: Jacopo Mondi Reviewed-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/rdacm20.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c index 16bcb764b0e0..90eb73f0e6e9 100644 --- a/drivers/media/i2c/rdacm20.c +++ b/drivers/media/i2c/rdacm20.c @@ -435,7 +435,7 @@ static int rdacm20_get_fmt(struct v4l2_subdev *sd, return 0; } -static struct v4l2_subdev_video_ops rdacm20_video_ops = { +static const struct v4l2_subdev_video_ops rdacm20_video_ops = { .s_stream = rdacm20_s_stream, }; @@ -445,7 +445,7 @@ static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = { .set_fmt = rdacm20_get_fmt, }; -static struct v4l2_subdev_ops rdacm20_subdev_ops = { +static const struct v4l2_subdev_ops rdacm20_subdev_ops = { .video = &rdacm20_video_ops, .pad = &rdacm20_subdev_pad_ops, }; -- cgit v1.2.3 From d899e5f1db7aa67cad01f4835286cb985890a3da Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Jan 2021 18:46:32 +0100 Subject: media: dt-bindings: media: imx258: add bindings for IMX258 sensor Add bindings for the IMX258 camera sensor. The bindings, just like the driver, are quite limited, e.g. do not support regulator supplies. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/imx258.yaml | 134 +++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 135 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/imx258.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/imx258.yaml b/Documentation/devicetree/bindings/media/i2c/imx258.yaml new file mode 100644 index 000000000000..eaf77866ed9b --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/imx258.yaml @@ -0,0 +1,134 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/imx258.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony IMX258 13 Mpixel CMOS Digital Image Sensor + +maintainers: + - Krzysztof Kozlowski + +description: |- + IMX258 is a diagonal 5.867mm (Type 1/3.06) 13 Mega-pixel CMOS active pixel + type stacked image sensor with a square pixel array of size 4208 x 3120. It + is programmable through I2C interface. Image data is sent through MIPI + CSI-2. + +properties: + compatible: + const: sony,imx258 + + assigned-clocks: true + assigned-clock-parents: true + assigned-clock-rates: true + + clocks: + description: + Clock frequency from 6 to 27 MHz. + maxItems: 1 + + reg: + maxItems: 1 + + reset-gpios: + description: |- + Reference to the GPIO connected to the XCLR pin, if any. + + vana-supply: + description: + Analog voltage (VANA) supply, 2.7 V + + vdig-supply: + description: + Digital I/O voltage (VDIG) supply, 1.2 V + + vif-supply: + description: + Interface voltage (VIF) supply, 1.8 V + + # See ../video-interfaces.txt for more details + port: + type: object + properties: + endpoint: + type: object + properties: + data-lanes: + oneOf: + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + - items: + - const: 1 + - const: 2 + + link-frequencies: + allOf: + - $ref: /schemas/types.yaml#/definitions/uint64-array + description: + Allowed data bus frequencies. + + required: + - data-lanes + - link-frequencies + +required: + - compatible + - reg + - port + +additionalProperties: false + +examples: + - | + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + sensor@6c { + compatible = "sony,imx258"; + reg = <0x6c>; + clocks = <&imx258_clk>; + + port { + endpoint { + remote-endpoint = <&csi1_ep>; + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <320000000>; + }; + }; + }; + }; + + /* Oscillator on the camera board */ + imx258_clk: clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <19200000>; + }; + + - | + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + sensor@6c { + compatible = "sony,imx258"; + reg = <0x6c>; + clocks = <&imx258_clk>; + + assigned-clocks = <&imx258_clk>; + assigned-clock-rates = <19200000>; + + port { + endpoint { + remote-endpoint = <&csi1_ep>; + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <633600000>; + }; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index af8f06213f52..e6ebb2ff5d1c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16559,6 +16559,7 @@ M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/imx258.yaml F: drivers/media/i2c/imx258.c SONY IMX274 SENSOR DRIVER -- cgit v1.2.3 From 9d14932d3eb0644684aa85b4f2dd5a90c455b2be Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Jan 2021 18:46:33 +0100 Subject: media: i2c: imx258: add support for binding via device tree The IMX258 can be used also on embedded designs using device tree so allow the sensor to bind to a device tree node. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx258.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index df62c69a48c0..ba7f29622974 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -1291,11 +1291,18 @@ static const struct acpi_device_id imx258_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, imx258_acpi_ids); #endif +static const struct of_device_id imx258_dt_ids[] = { + { .compatible = "sony,imx258" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx258_dt_ids); + static struct i2c_driver imx258_i2c_driver = { .driver = { .name = "imx258", .pm = &imx258_pm_ops, .acpi_match_table = ACPI_PTR(imx258_acpi_ids), + .of_match_table = imx258_dt_ids, }, .probe_new = imx258_probe, .remove = imx258_remove, -- cgit v1.2.3 From 2b585242b8619799fab2d0840f35cc936ed88749 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Jan 2021 18:46:34 +0100 Subject: media: i2c: imx258: simplify getting state container The pointer to 'struct v4l2_subdev' is stored in drvdata via v4l2_i2c_subdev_init() so there is no point of a dance like: struct i2c_client *client = to_i2c_client(struct device *dev) struct v4l2_subdev *sd = i2c_get_clientdata(client); This allows to remove local variable 'client' and few pointer dereferences. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx258.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index ba7f29622974..505981e02cff 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -1018,8 +1018,7 @@ err_unlock: static int __maybe_unused imx258_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx258 *imx258 = to_imx258(sd); if (imx258->streaming) @@ -1030,8 +1029,7 @@ static int __maybe_unused imx258_suspend(struct device *dev) static int __maybe_unused imx258_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx258 *imx258 = to_imx258(sd); int ret; -- cgit v1.2.3 From 9fda25332c4b9e193f11f34130b6f3fdbafc11dc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Jan 2021 18:46:35 +0100 Subject: media: i2c: imx258: get clock from device properties and enable it via runtime PM The IMX258 sensor driver checked in device properties for a clock-frequency property which actually does not mean that the clock is really running such frequency or is it even enabled. Get the provided clock and check it frequency. If none is provided, fall back to old property. Enable the clock when accessing the IMX258 registers and when streaming starts with runtime PM. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx258.c | 69 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 505981e02cff..61d74b794582 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -2,6 +2,7 @@ // Copyright (C) 2018 Intel Corporation #include +#include #include #include #include @@ -68,6 +69,9 @@ #define REG_CONFIG_MIRROR_FLIP 0x03 #define REG_CONFIG_FLIP_TEST_PATTERN 0x02 +/* Input clock frequency in Hz */ +#define IMX258_INPUT_CLOCK_FREQ 19200000 + struct imx258_reg { u16 address; u8 val; @@ -610,6 +614,8 @@ struct imx258 { /* Streaming on/off */ bool streaming; + + struct clk *clk; }; static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd) @@ -972,6 +978,29 @@ static int imx258_stop_streaming(struct imx258 *imx258) return 0; } +static int imx258_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx258 *imx258 = to_imx258(sd); + int ret; + + ret = clk_prepare_enable(imx258->clk); + if (ret) + dev_err(dev, "failed to enable clock\n"); + + return ret; +} + +static int imx258_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx258 *imx258 = to_imx258(sd); + + clk_disable_unprepare(imx258->clk); + + return 0; +} + static int imx258_set_stream(struct v4l2_subdev *sd, int enable) { struct imx258 *imx258 = to_imx258(sd); @@ -1199,9 +1228,26 @@ static int imx258_probe(struct i2c_client *client) int ret; u32 val = 0; - device_property_read_u32(&client->dev, "clock-frequency", &val); - if (val != 19200000) + imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL); + if (!imx258) + return -ENOMEM; + + imx258->clk = devm_clk_get_optional(&client->dev, NULL); + if (!imx258->clk) { + dev_dbg(&client->dev, + "no clock provided, using clock-frequency property\n"); + + device_property_read_u32(&client->dev, "clock-frequency", &val); + if (val != IMX258_INPUT_CLOCK_FREQ) + return -EINVAL; + } else if (IS_ERR(imx258->clk)) { + return dev_err_probe(&client->dev, PTR_ERR(imx258->clk), + "error getting clock\n"); + } + if (clk_get_rate(imx258->clk) != IMX258_INPUT_CLOCK_FREQ) { + dev_err(&client->dev, "input clock frequency not supported\n"); return -EINVAL; + } /* * Check that the device is mounted upside down. The driver only @@ -1211,24 +1257,25 @@ static int imx258_probe(struct i2c_client *client) if (ret || val != 180) return -EINVAL; - imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL); - if (!imx258) - return -ENOMEM; - /* Initialize subdev */ v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); + /* Will be powered off via pm_runtime_idle */ + ret = imx258_power_on(&client->dev); + if (ret) + return ret; + /* Check module identity */ ret = imx258_identify_module(imx258); if (ret) - return ret; + goto error_identify; /* Set default mode to max resolution */ imx258->cur_mode = &supported_modes[0]; ret = imx258_init_controls(imx258); if (ret) - return ret; + goto error_identify; /* Initialize subdev */ imx258->sd.internal_ops = &imx258_internal_ops; @@ -1258,6 +1305,9 @@ error_media_entity: error_handler_free: imx258_free_controls(imx258); +error_identify: + imx258_power_off(&client->dev); + return ret; } @@ -1271,6 +1321,8 @@ static int imx258_remove(struct i2c_client *client) imx258_free_controls(imx258); pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + imx258_power_off(&client->dev); pm_runtime_set_suspended(&client->dev); return 0; @@ -1278,6 +1330,7 @@ static int imx258_remove(struct i2c_client *client) static const struct dev_pm_ops imx258_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(imx258_suspend, imx258_resume) + SET_RUNTIME_PM_OPS(imx258_power_off, imx258_power_on, NULL) }; #ifdef CONFIG_ACPI -- cgit v1.2.3 From a52e17361987782ecea7cc6ed0dc1f37a11949a8 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 12 Jan 2021 20:49:14 +0100 Subject: media: mach-pxa: Register the camera sensor fixed-rate clock The pxa-camera capture driver currently registers a v4l2-clk clock, named "mclk", to represent the mt9m111 sensor clock. Register a proper fixed-rate clock using the generic clock framework, which will allow to remove the v4l2-clk clock in the pxa-camera driver in a follow-up commit. Signed-off-by: Ezequiel Garcia Acked-by: Arnd Bergmann (for arch/arm/mach-*/) Acked-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-pxa/devices.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/mach-pxa/devices.c b/arch/arm/mach-pxa/devices.c index 524d6093e0c7..09b8495f3fd9 100644 --- a/arch/arm/mach-pxa/devices.c +++ b/arch/arm/mach-pxa/devices.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -634,6 +635,13 @@ static struct platform_device pxa27x_device_camera = { void __init pxa_set_camera_info(struct pxacamera_platform_data *info) { + struct clk *mclk; + + /* Register a fixed-rate clock for camera sensors. */ + mclk = clk_register_fixed_rate(NULL, "pxa_camera_clk", NULL, 0, + info->mclk_10khz * 10000); + if (!IS_ERR(mclk)) + clkdev_create(mclk, "mclk", NULL); pxa_register_device(&pxa27x_device_camera, info); } -- cgit v1.2.3 From 832e6609f790bb7e6185200a31460741d161788f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 12 Jan 2021 20:49:15 +0100 Subject: media: pxa_camera: Drop the v4l2-clk clock register Now that mach-based PXA platforms are registering proper fixed-rate clocks through the CCF, the v4l2-clk clock is no longer required. Drop this clock, so the driver no longer depends on the legacy v4l2-clk API. Signed-off-by: Ezequiel Garcia Acked-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 7f92de351c52..14077797f5e1 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -31,7 +31,6 @@ #include #include -#include #include #include #include @@ -675,7 +674,6 @@ struct pxa_camera_dev { unsigned long ciclk; unsigned long mclk; u32 mclk_divisor; - struct v4l2_clk *mclk_clk; u16 width_flags; /* max 10 bits */ struct list_head capture; @@ -2031,9 +2029,6 @@ static const struct v4l2_ioctl_ops pxa_camera_ioctl_ops = { .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -static const struct v4l2_clk_ops pxa_camera_mclk_ops = { -}; - static const struct video_device pxa_camera_videodev_template = { .name = "pxa-camera", .minor = -1, @@ -2141,11 +2136,6 @@ static void pxa_camera_sensor_unbind(struct v4l2_async_notifier *notifier, pxa_camera_destroy_formats(pcdev); - if (pcdev->mclk_clk) { - v4l2_clk_unregister(pcdev->mclk_clk); - pcdev->mclk_clk = NULL; - } - video_unregister_device(&pcdev->vdev); pcdev->sensor = NULL; @@ -2278,7 +2268,6 @@ static int pxa_camera_probe(struct platform_device *pdev) .src_maxburst = 8, .direction = DMA_DEV_TO_MEM, }; - char clk_name[V4L2_CLK_NAME_SIZE]; int irq; int err = 0, i; @@ -2409,24 +2398,12 @@ static int pxa_camera_probe(struct platform_device *pdev) if (err) goto exit_notifier_cleanup; - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - pcdev->pdata->sensor_i2c_adapter_id, - pcdev->pdata->sensor_i2c_address); - pcdev->notifier.ops = &pxa_camera_sensor_ops; - pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, clk_name, NULL); - if (IS_ERR(pcdev->mclk_clk)) { - err = PTR_ERR(pcdev->mclk_clk); - goto exit_notifier_cleanup; - } - err = v4l2_async_notifier_register(&pcdev->v4l2_dev, &pcdev->notifier); if (err) - goto exit_free_clk; + goto exit_notifier_cleanup; return 0; -exit_free_clk: - v4l2_clk_unregister(pcdev->mclk_clk); exit_notifier_cleanup: v4l2_async_notifier_cleanup(&pcdev->notifier); v4l2_device_unregister(&pcdev->v4l2_dev); @@ -2455,11 +2432,6 @@ static int pxa_camera_remove(struct platform_device *pdev) v4l2_async_notifier_unregister(&pcdev->notifier); v4l2_async_notifier_cleanup(&pcdev->notifier); - if (pcdev->mclk_clk) { - v4l2_clk_unregister(pcdev->mclk_clk); - pcdev->mclk_clk = NULL; - } - v4l2_device_unregister(&pcdev->v4l2_dev); dev_info(&pdev->dev, "PXA Camera driver unloaded\n"); -- cgit v1.2.3 From 8de14b3acb9c7f4bcb8565a036dd579e5b7aaa7a Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 12 Jan 2021 20:49:16 +0100 Subject: media: ov9640: Use the generic clock framework Commit 63839882c597 ("media: mach-pxa: palmz72/pcm990: remove soc_camera dependencies") removed the last in-tree user of this sensor. New users will be required to use the generic clock framework, so it's possible to convert the driver to use it. Convert the driver to use the CCF, and drop the legacy v4l2-clk API. Signed-off-by: Ezequiel Garcia Acked-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 15 ++++++--------- drivers/media/i2c/ov9640.h | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index e2a25240fc85..d36b04c49628 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -17,6 +17,7 @@ * Copyright (C) 2008, Guennadi Liakhovetski */ +#include #include #include #include @@ -26,7 +27,6 @@ #include #include -#include #include #include #include @@ -333,13 +333,13 @@ static int ov9640_s_power(struct v4l2_subdev *sd, int on) if (on) { gpiod_set_value(priv->gpio_power, 1); usleep_range(1000, 2000); - ret = v4l2_clk_enable(priv->clk); + ret = clk_prepare_enable(priv->clk); usleep_range(1000, 2000); gpiod_set_value(priv->gpio_reset, 0); } else { gpiod_set_value(priv->gpio_reset, 1); usleep_range(1000, 2000); - v4l2_clk_disable(priv->clk); + clk_disable_unprepare(priv->clk); usleep_range(1000, 2000); gpiod_set_value(priv->gpio_power, 0); } @@ -719,7 +719,7 @@ static int ov9640_probe(struct i2c_client *client, priv->subdev.ctrl_handler = &priv->hdl; - priv->clk = v4l2_clk_get(&client->dev, "mclk"); + priv->clk = devm_clk_get(&client->dev, "mclk"); if (IS_ERR(priv->clk)) { ret = PTR_ERR(priv->clk); goto ectrlinit; @@ -727,17 +727,15 @@ static int ov9640_probe(struct i2c_client *client, ret = ov9640_video_probe(client); if (ret) - goto eprobe; + goto ectrlinit; priv->subdev.dev = &client->dev; ret = v4l2_async_register_subdev(&priv->subdev); if (ret) - goto eprobe; + goto ectrlinit; return 0; -eprobe: - v4l2_clk_put(priv->clk); ectrlinit: v4l2_ctrl_handler_free(&priv->hdl); @@ -749,7 +747,6 @@ static int ov9640_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov9640_priv *priv = to_ov9640_sensor(sd); - v4l2_clk_put(priv->clk); v4l2_async_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); diff --git a/drivers/media/i2c/ov9640.h b/drivers/media/i2c/ov9640.h index a8ed6992c1a8..c105594b2472 100644 --- a/drivers/media/i2c/ov9640.h +++ b/drivers/media/i2c/ov9640.h @@ -196,7 +196,7 @@ struct ov9640_reg { struct ov9640_priv { struct v4l2_subdev subdev; struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; + struct clk *clk; struct gpio_desc *gpio_power; struct gpio_desc *gpio_reset; -- cgit v1.2.3 From ea6300cdf181c9e9f169aad423381fc77022caf3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 12 Jan 2021 20:49:17 +0100 Subject: media: mt9m111: Use the generic clock framework This sensor driver has a proper device-tree binding, and also all its platform-data based in-tree users have been converted to use the generic clock framework. Convert the driver to use the CCF, and drop the legacy v4l2-clk API. Signed-off-by: Ezequiel Garcia Reviewed-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m111.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 69697386ffcd..0e11734f75aa 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -4,6 +4,7 @@ * * Copyright (C) 2008, Robert Jarzmik */ +#include #include #include #include @@ -16,7 +17,6 @@ #include #include -#include #include #include #include @@ -232,7 +232,7 @@ struct mt9m111 { struct v4l2_ctrl *gain; struct mt9m111_context *ctx; struct v4l2_rect rect; /* cropping rectangle */ - struct v4l2_clk *clk; + struct clk *clk; unsigned int width; /* output */ unsigned int height; /* sizes */ struct v4l2_fract frame_interval; @@ -977,7 +977,7 @@ static int mt9m111_power_on(struct mt9m111 *mt9m111) struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); int ret; - ret = v4l2_clk_enable(mt9m111->clk); + ret = clk_prepare_enable(mt9m111->clk); if (ret < 0) return ret; @@ -995,7 +995,7 @@ out_regulator_disable: regulator_disable(mt9m111->regulator); out_clk_disable: - v4l2_clk_disable(mt9m111->clk); + clk_disable_unprepare(mt9m111->clk); dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret); @@ -1006,7 +1006,7 @@ static void mt9m111_power_off(struct mt9m111 *mt9m111) { mt9m111_suspend(mt9m111); regulator_disable(mt9m111->regulator); - v4l2_clk_disable(mt9m111->clk); + clk_disable_unprepare(mt9m111->clk); } static int mt9m111_s_power(struct v4l2_subdev *sd, int on) @@ -1266,7 +1266,7 @@ static int mt9m111_probe(struct i2c_client *client) return ret; } - mt9m111->clk = v4l2_clk_get(&client->dev, "mclk"); + mt9m111->clk = devm_clk_get(&client->dev, "mclk"); if (IS_ERR(mt9m111->clk)) return PTR_ERR(mt9m111->clk); @@ -1311,7 +1311,7 @@ static int mt9m111_probe(struct i2c_client *client) mt9m111->subdev.ctrl_handler = &mt9m111->hdl; if (mt9m111->hdl.error) { ret = mt9m111->hdl.error; - goto out_clkput; + return ret; } #ifdef CONFIG_MEDIA_CONTROLLER @@ -1354,8 +1354,6 @@ out_entityclean: out_hdlfree: #endif v4l2_ctrl_handler_free(&mt9m111->hdl); -out_clkput: - v4l2_clk_put(mt9m111->clk); return ret; } @@ -1366,7 +1364,6 @@ static int mt9m111_remove(struct i2c_client *client) v4l2_async_unregister_subdev(&mt9m111->subdev); media_entity_cleanup(&mt9m111->subdev.entity); - v4l2_clk_put(mt9m111->clk); v4l2_ctrl_handler_free(&mt9m111->hdl); return 0; -- cgit v1.2.3 From 01747ab9ca07c086f253bb99ba261def9dfb41d0 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 12 Jan 2021 20:49:18 +0100 Subject: media: ov6650: Use the generic clock framework Commit ce548396a433 ("media: mach-omap1: board-ams-delta.c: remove soc_camera dependencies") removed the last in-tree user of this sensor. New users will be required to use the generic clock framework, so it's possible to convert the driver to use it. Convert the driver to use the CCF, and drop the legacy v4l2-clk API. Signed-off-by: Ezequiel Garcia Reviewed-by: Janusz Krzysztofik Reviewed-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index d73f9f540932..85dd13694bd2 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -22,13 +22,13 @@ */ #include +#include #include #include #include #include #include -#include #include #include @@ -194,7 +194,7 @@ struct ov6650 { struct v4l2_ctrl *blue; struct v4l2_ctrl *red; }; - struct v4l2_clk *clk; + struct clk *clk; bool half_scale; /* scale down output by 2 */ struct v4l2_rect rect; /* sensor cropping window */ struct v4l2_fract tpf; /* as requested with s_frame_interval */ @@ -459,9 +459,9 @@ static int ov6650_s_power(struct v4l2_subdev *sd, int on) int ret = 0; if (on) - ret = v4l2_clk_enable(priv->clk); + ret = clk_prepare_enable(priv->clk); else - v4l2_clk_disable(priv->clk); + clk_disable_unprepare(priv->clk); return ret; } @@ -821,14 +821,14 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) u8 pidh, pidl, midh, midl; int i, ret = 0; - priv->clk = v4l2_clk_get(&client->dev, NULL); + priv->clk = devm_clk_get(&client->dev, NULL); if (IS_ERR(priv->clk)) { ret = PTR_ERR(priv->clk); - dev_err(&client->dev, "v4l2_clk request err: %d\n", ret); + dev_err(&client->dev, "clk request err: %d\n", ret); return ret; } - rate = v4l2_clk_get_rate(priv->clk); + rate = clk_get_rate(priv->clk); for (i = 0; rate && i < ARRAY_SIZE(ov6650_xclk); i++) { if (rate != ov6650_xclk[i].rate) continue; @@ -839,8 +839,8 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) break; } for (i = 0; !xclk && i < ARRAY_SIZE(ov6650_xclk); i++) { - ret = v4l2_clk_set_rate(priv->clk, ov6650_xclk[i].rate); - if (ret || v4l2_clk_get_rate(priv->clk) != ov6650_xclk[i].rate) + ret = clk_set_rate(priv->clk, ov6650_xclk[i].rate); + if (ret || clk_get_rate(priv->clk) != ov6650_xclk[i].rate) continue; xclk = &ov6650_xclk[i]; @@ -852,12 +852,12 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) dev_err(&client->dev, "unable to get supported clock rate\n"); if (!ret) ret = -EINVAL; - goto eclkput; + return ret; } ret = ov6650_s_power(sd, 1); if (ret < 0) - goto eclkput; + return ret; msleep(20); @@ -899,11 +899,6 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) done: ov6650_s_power(sd, 0); - if (!ret) - return 0; -eclkput: - v4l2_clk_put(priv->clk); - return ret; } @@ -1089,7 +1084,6 @@ static int ov6650_remove(struct i2c_client *client) { struct ov6650 *priv = to_ov6650(client); - v4l2_clk_put(priv->clk); v4l2_async_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); return 0; -- cgit v1.2.3 From 85db876b08f10705e992bc924530f74de3859f22 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 12 Jan 2021 20:49:19 +0100 Subject: media: Remove the legacy v4l2-clk API The V4L2 temporary clock helper API, was introduced in late 2012 and, as mentioned in the documentation, meant to be replaced by the generic clock API, once the generic clock framework became available on all relevant architectures. The generic clock API is a well-established API (since a few years now). The last few media capture drivers and sensors using v4l2-clk have been converted to the generic clock framework. We can now remove the v4l2-clk API. Signed-off-by: Ezequiel Garcia Acked-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/media/v4l2-clocks.rst | 31 --- Documentation/driver-api/media/v4l2-core.rst | 1 - drivers/media/v4l2-core/Makefile | 2 +- drivers/media/v4l2-core/v4l2-clk.c | 321 ------------------------- include/media/v4l2-clk.h | 73 ------ 5 files changed, 1 insertion(+), 427 deletions(-) delete mode 100644 Documentation/driver-api/media/v4l2-clocks.rst delete mode 100644 drivers/media/v4l2-core/v4l2-clk.c delete mode 100644 include/media/v4l2-clk.h diff --git a/Documentation/driver-api/media/v4l2-clocks.rst b/Documentation/driver-api/media/v4l2-clocks.rst deleted file mode 100644 index 5c22eecab7ba..000000000000 --- a/Documentation/driver-api/media/v4l2-clocks.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -V4L2 clocks ------------ - -.. attention:: - - This is a temporary API and it shall be replaced by the generic - clock API, when the latter becomes widely available. - -Many subdevices, like camera sensors, TV decoders and encoders, need a clock -signal to be supplied by the system. Often this clock is supplied by the -respective bridge device. The Linux kernel provides a Common Clock Framework for -this purpose. However, it is not (yet) available on all architectures. Besides, -the nature of the multi-functional (clock, data + synchronisation, I2C control) -connection of subdevices to the system might impose special requirements on the -clock API usage. E.g. V4L2 has to support clock provider driver unregistration -while a subdevice driver is holding a reference to the clock. For these reasons -a V4L2 clock helper API has been developed and is provided to bridge and -subdevice drivers. - -The API consists of two parts: two functions to register and unregister a V4L2 -clock source: v4l2_clk_register() and v4l2_clk_unregister() and calls to control -a clock object, similar to the respective generic clock API calls: -v4l2_clk_get(), v4l2_clk_put(), v4l2_clk_enable(), v4l2_clk_disable(), -v4l2_clk_get_rate(), and v4l2_clk_set_rate(). Clock suppliers have to provide -clock operations that will be called when clock users invoke respective API -methods. - -It is expected that once the CCF becomes available on all relevant -architectures this API will be removed. diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst index 0dcad7a23141..1a8c4a5f256b 100644 --- a/Documentation/driver-api/media/v4l2-core.rst +++ b/Documentation/driver-api/media/v4l2-core.rst @@ -15,7 +15,6 @@ Video4Linux devices v4l2-controls v4l2-videobuf v4l2-videobuf2 - v4l2-clocks v4l2-dv-timings v4l2-flash-led-class v4l2-mc diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 2ef0c7c958a2..e4cd589b99a5 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -6,7 +6,7 @@ tuner-objs := tuner-core.o videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ - v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o \ + v4l2-event.o v4l2-ctrls.o v4l2-subdev.o \ v4l2-async.o v4l2-common.o videodev-$(CONFIG_COMPAT) += v4l2-compat-ioctl32.o videodev-$(CONFIG_TRACEPOINTS) += v4l2-trace.o diff --git a/drivers/media/v4l2-core/v4l2-clk.c b/drivers/media/v4l2-core/v4l2-clk.c deleted file mode 100644 index 91274eee6977..000000000000 --- a/drivers/media/v4l2-core/v4l2-clk.c +++ /dev/null @@ -1,321 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * V4L2 clock service - * - * Copyright (C) 2012-2013, Guennadi Liakhovetski - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static DEFINE_MUTEX(clk_lock); -static LIST_HEAD(clk_list); - -static struct v4l2_clk *v4l2_clk_find(const char *dev_id) -{ - struct v4l2_clk *clk; - - list_for_each_entry(clk, &clk_list, list) - if (!strcmp(dev_id, clk->dev_id)) - return clk; - - return ERR_PTR(-ENODEV); -} - -struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id) -{ - struct v4l2_clk *clk; - struct clk *ccf_clk = clk_get(dev, id); - char clk_name[V4L2_CLK_NAME_SIZE]; - - if (PTR_ERR(ccf_clk) == -EPROBE_DEFER) - return ERR_PTR(-EPROBE_DEFER); - - if (!IS_ERR_OR_NULL(ccf_clk)) { - clk = kzalloc(sizeof(*clk), GFP_KERNEL); - if (!clk) { - clk_put(ccf_clk); - return ERR_PTR(-ENOMEM); - } - clk->clk = ccf_clk; - - return clk; - } - - mutex_lock(&clk_lock); - clk = v4l2_clk_find(dev_name(dev)); - - /* if dev_name is not found, try use the OF name to find again */ - if (PTR_ERR(clk) == -ENODEV && dev->of_node) { - v4l2_clk_name_of(clk_name, sizeof(clk_name), dev->of_node); - clk = v4l2_clk_find(clk_name); - } - - if (!IS_ERR(clk)) - atomic_inc(&clk->use_count); - mutex_unlock(&clk_lock); - - return clk; -} -EXPORT_SYMBOL(v4l2_clk_get); - -void v4l2_clk_put(struct v4l2_clk *clk) -{ - struct v4l2_clk *tmp; - - if (IS_ERR(clk)) - return; - - if (clk->clk) { - clk_put(clk->clk); - kfree(clk); - return; - } - - mutex_lock(&clk_lock); - - list_for_each_entry(tmp, &clk_list, list) - if (tmp == clk) - atomic_dec(&clk->use_count); - - mutex_unlock(&clk_lock); -} -EXPORT_SYMBOL(v4l2_clk_put); - -static int v4l2_clk_lock_driver(struct v4l2_clk *clk) -{ - struct v4l2_clk *tmp; - int ret = -ENODEV; - - mutex_lock(&clk_lock); - - list_for_each_entry(tmp, &clk_list, list) - if (tmp == clk) { - ret = !try_module_get(clk->ops->owner); - if (ret) - ret = -EFAULT; - break; - } - - mutex_unlock(&clk_lock); - - return ret; -} - -static void v4l2_clk_unlock_driver(struct v4l2_clk *clk) -{ - module_put(clk->ops->owner); -} - -int v4l2_clk_enable(struct v4l2_clk *clk) -{ - int ret; - - if (clk->clk) - return clk_prepare_enable(clk->clk); - - ret = v4l2_clk_lock_driver(clk); - if (ret < 0) - return ret; - - mutex_lock(&clk->lock); - - if (++clk->enable == 1 && clk->ops->enable) { - ret = clk->ops->enable(clk); - if (ret < 0) - clk->enable--; - } - - mutex_unlock(&clk->lock); - - return ret; -} -EXPORT_SYMBOL(v4l2_clk_enable); - -/* - * You might Oops if you try to disabled a disabled clock, because then the - * driver isn't locked and could have been unloaded by now, so, don't do that - */ -void v4l2_clk_disable(struct v4l2_clk *clk) -{ - int enable; - - if (clk->clk) - return clk_disable_unprepare(clk->clk); - - mutex_lock(&clk->lock); - - enable = --clk->enable; - if (WARN(enable < 0, "Unbalanced %s() on %s!\n", __func__, - clk->dev_id)) - clk->enable++; - else if (!enable && clk->ops->disable) - clk->ops->disable(clk); - - mutex_unlock(&clk->lock); - - v4l2_clk_unlock_driver(clk); -} -EXPORT_SYMBOL(v4l2_clk_disable); - -unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk) -{ - int ret; - - if (clk->clk) - return clk_get_rate(clk->clk); - - ret = v4l2_clk_lock_driver(clk); - if (ret < 0) - return ret; - - mutex_lock(&clk->lock); - if (!clk->ops->get_rate) - ret = -ENOSYS; - else - ret = clk->ops->get_rate(clk); - mutex_unlock(&clk->lock); - - v4l2_clk_unlock_driver(clk); - - return ret; -} -EXPORT_SYMBOL(v4l2_clk_get_rate); - -int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate) -{ - int ret; - - if (clk->clk) { - long r = clk_round_rate(clk->clk, rate); - if (r < 0) - return r; - return clk_set_rate(clk->clk, r); - } - - ret = v4l2_clk_lock_driver(clk); - - if (ret < 0) - return ret; - - mutex_lock(&clk->lock); - if (!clk->ops->set_rate) - ret = -ENOSYS; - else - ret = clk->ops->set_rate(clk, rate); - mutex_unlock(&clk->lock); - - v4l2_clk_unlock_driver(clk); - - return ret; -} -EXPORT_SYMBOL(v4l2_clk_set_rate); - -struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops, - const char *dev_id, - void *priv) -{ - struct v4l2_clk *clk; - int ret; - - if (!ops || !dev_id) - return ERR_PTR(-EINVAL); - - clk = kzalloc(sizeof(struct v4l2_clk), GFP_KERNEL); - if (!clk) - return ERR_PTR(-ENOMEM); - - clk->dev_id = kstrdup(dev_id, GFP_KERNEL); - if (!clk->dev_id) { - ret = -ENOMEM; - goto ealloc; - } - clk->ops = ops; - clk->priv = priv; - atomic_set(&clk->use_count, 0); - mutex_init(&clk->lock); - - mutex_lock(&clk_lock); - if (!IS_ERR(v4l2_clk_find(dev_id))) { - mutex_unlock(&clk_lock); - ret = -EEXIST; - goto eexist; - } - list_add_tail(&clk->list, &clk_list); - mutex_unlock(&clk_lock); - - return clk; - -eexist: -ealloc: - kfree(clk->dev_id); - kfree(clk); - return ERR_PTR(ret); -} -EXPORT_SYMBOL(v4l2_clk_register); - -void v4l2_clk_unregister(struct v4l2_clk *clk) -{ - if (WARN(atomic_read(&clk->use_count), - "%s(): Refusing to unregister ref-counted %s clock!\n", - __func__, clk->dev_id)) - return; - - mutex_lock(&clk_lock); - list_del(&clk->list); - mutex_unlock(&clk_lock); - - kfree(clk->dev_id); - kfree(clk); -} -EXPORT_SYMBOL(v4l2_clk_unregister); - -struct v4l2_clk_fixed { - unsigned long rate; - struct v4l2_clk_ops ops; -}; - -static unsigned long fixed_get_rate(struct v4l2_clk *clk) -{ - struct v4l2_clk_fixed *priv = clk->priv; - return priv->rate; -} - -struct v4l2_clk *__v4l2_clk_register_fixed(const char *dev_id, - unsigned long rate, struct module *owner) -{ - struct v4l2_clk *clk; - struct v4l2_clk_fixed *priv = kzalloc(sizeof(*priv), GFP_KERNEL); - - if (!priv) - return ERR_PTR(-ENOMEM); - - priv->rate = rate; - priv->ops.get_rate = fixed_get_rate; - priv->ops.owner = owner; - - clk = v4l2_clk_register(&priv->ops, dev_id, priv); - if (IS_ERR(clk)) - kfree(priv); - - return clk; -} -EXPORT_SYMBOL(__v4l2_clk_register_fixed); - -void v4l2_clk_unregister_fixed(struct v4l2_clk *clk) -{ - kfree(clk->priv); - v4l2_clk_unregister(clk); -} -EXPORT_SYMBOL(v4l2_clk_unregister_fixed); diff --git a/include/media/v4l2-clk.h b/include/media/v4l2-clk.h deleted file mode 100644 index d9d21a43a834..000000000000 --- a/include/media/v4l2-clk.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * V4L2 clock service - * - * Copyright (C) 2012-2013, Guennadi Liakhovetski - * - * ATTENTION: This is a temporary API and it shall be replaced by the generic - * clock API, when the latter becomes widely available. - */ - -#ifndef MEDIA_V4L2_CLK_H -#define MEDIA_V4L2_CLK_H - -#include -#include -#include -#include - -struct module; -struct device; - -struct clk; -struct v4l2_clk { - struct list_head list; - const struct v4l2_clk_ops *ops; - const char *dev_id; - int enable; - struct mutex lock; /* Protect the enable count */ - atomic_t use_count; - struct clk *clk; - void *priv; -}; - -struct v4l2_clk_ops { - struct module *owner; - int (*enable)(struct v4l2_clk *clk); - void (*disable)(struct v4l2_clk *clk); - unsigned long (*get_rate)(struct v4l2_clk *clk); - int (*set_rate)(struct v4l2_clk *clk, unsigned long); -}; - -struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops, - const char *dev_name, - void *priv); -void v4l2_clk_unregister(struct v4l2_clk *clk); -struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id); -void v4l2_clk_put(struct v4l2_clk *clk); -int v4l2_clk_enable(struct v4l2_clk *clk); -void v4l2_clk_disable(struct v4l2_clk *clk); -unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk); -int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate); - -struct module; - -struct v4l2_clk *__v4l2_clk_register_fixed(const char *dev_id, - unsigned long rate, struct module *owner); -void v4l2_clk_unregister_fixed(struct v4l2_clk *clk); - -static inline struct v4l2_clk *v4l2_clk_register_fixed(const char *dev_id, - unsigned long rate) -{ - return __v4l2_clk_register_fixed(dev_id, rate, THIS_MODULE); -} - -#define V4L2_CLK_NAME_SIZE 64 - -#define v4l2_clk_name_i2c(name, size, adap, client) snprintf(name, size, \ - "%d-%04x", adap, client) - -#define v4l2_clk_name_of(name, size, node) snprintf(name, size, \ - "of-%pOF", node) - -#endif -- cgit v1.2.3 From d12783207ae8f687e1bf9d79803a705041ff2679 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 2 Feb 2021 21:07:20 +0100 Subject: media: ipu3-cio2: Build bridge only if ACPI is enabled ipu3-cio2-bridge uses several features of the ACPI framework that have no meaningful replacement when ACPI is disabled. Instead of adding #ifdefs to the affected places, only build the bridge code if CONFIG_ACPI is enabled. Fixes: 803abec64ef9 ("media: ipu3-cio2: Add cio2-bridge to ipu3-cio2 driver") Signed-off-by: Sakari Ailus Reported-by: Randy Dunlap Acked-by: Randy Dunlap # build-tested Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/intel/ipu3/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig index 24f4e79fe0cb..dce8274c81e6 100644 --- a/drivers/media/pci/intel/ipu3/Kconfig +++ b/drivers/media/pci/intel/ipu3/Kconfig @@ -20,7 +20,7 @@ config VIDEO_IPU3_CIO2 config CIO2_BRIDGE bool "IPU3 CIO2 Sensors Bridge" - depends on VIDEO_IPU3_CIO2 + depends on VIDEO_IPU3_CIO2 && ACPI help This extension provides an API for the ipu3-cio2 driver to create connections to cameras that are hidden in the SSDB buffer in ACPI. -- cgit v1.2.3 From 6f862f8488306f116c3f2cb907ed0975aa539c13 Mon Sep 17 00:00:00 2001 From: Shawn Tu Date: Tue, 2 Feb 2021 05:35:01 +0100 Subject: media: ov5675: fix vflip/hflip control Set/clear the bits to configure the register to expected value to assume the v/hflip state. Signed-off-by: Shawn Tu Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5675.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index 5e35808037ad..ae00d717e599 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -624,7 +624,7 @@ static int ov5675_set_ctrl_hflip(struct ov5675 *ov5675, u32 ctrl_val) return ov5675_write_reg(ov5675, OV5675_REG_FORMAT1, OV5675_REG_VALUE_08BIT, - ctrl_val ? val & ~BIT(3) : val); + ctrl_val ? val & ~BIT(3) : val | BIT(3)); } static int ov5675_set_ctrl_vflip(struct ov5675 *ov5675, u8 ctrl_val) @@ -639,7 +639,7 @@ static int ov5675_set_ctrl_vflip(struct ov5675 *ov5675, u8 ctrl_val) ret = ov5675_write_reg(ov5675, OV5675_REG_FORMAT1, OV5675_REG_VALUE_08BIT, - ctrl_val ? val | BIT(4) | BIT(5) : val); + ctrl_val ? val | BIT(4) | BIT(5) : val & ~BIT(4) & ~BIT(5)); if (ret) return ret; @@ -652,7 +652,7 @@ static int ov5675_set_ctrl_vflip(struct ov5675 *ov5675, u8 ctrl_val) return ov5675_write_reg(ov5675, OV5675_REG_FORMAT2, OV5675_REG_VALUE_08BIT, - ctrl_val ? val | BIT(1) : val); + ctrl_val ? val | BIT(1) : val & ~BIT(1)); } static int ov5675_set_ctrl(struct v4l2_ctrl *ctrl) -- cgit v1.2.3 From 49b94d580abc47ef96b2adef0ef3ac0582f2c0c1 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Tue, 26 Jan 2021 08:49:34 +0100 Subject: media: i2c: imx219: Implement V4L2_CID_LINK_FREQ control This control is needed for imx219 driver, as the link frequency is independent from the pixel rate in this case, and can't be calculated from the pixel rate. Signed-off-by: Andrey Konovalov Reviewed-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx219.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 92a8d52776b8..6e3382b85a90 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -390,6 +390,10 @@ static const struct imx219_reg raw10_framefmt_regs[] = { {0x0309, 0x0a}, }; +static const s64 imx219_link_freq_menu[] = { + IMX219_DEFAULT_LINK_FREQ, +}; + static const char * const imx219_test_pattern_menu[] = { "Disabled", "Color Bars", @@ -547,6 +551,7 @@ struct imx219 { struct v4l2_ctrl_handler ctrl_handler; /* V4L2 Controls */ struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; struct v4l2_ctrl *exposure; struct v4l2_ctrl *vflip; struct v4l2_ctrl *hflip; @@ -1269,7 +1274,7 @@ static int imx219_init_controls(struct imx219 *imx219) int i, ret; ctrl_hdlr = &imx219->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 11); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); if (ret) return ret; @@ -1283,6 +1288,14 @@ static int imx219_init_controls(struct imx219 *imx219) IMX219_PIXEL_RATE, 1, IMX219_PIXEL_RATE); + imx219->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(imx219_link_freq_menu) - 1, 0, + imx219_link_freq_menu); + if (imx219->link_freq) + imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + /* Initial vblank/hblank/exposure parameters based on current mode */ imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_VBLANK, IMX219_VBLANK_MIN, -- cgit v1.2.3 From 2984b0ddd557c42224d1a1d2aefa25f24664c7f2 Mon Sep 17 00:00:00 2001 From: Robert Foss Date: Wed, 20 Jan 2021 13:08:47 +0100 Subject: media: ov8856: Configure sensor for GRBG Bayer for all modes The previously added modes 3264x2448 & 1632x1224 are actually configuring the sensor for BGGR mode, this is an issue since the mode that is exposed through V4L incorrectly is set as GRBG. This patch fixes the issue by moving the output crop window of internal sensor ISP uses by one row, which means that the Bayer pattern of the output is changed. row 1: B G B G B G ... row 2: G R G R G R ... row 3: B G B G B G ... ... row 2: G R G R G R ... row 3: B G B G B G ... ... Signed-off-by: Robert Foss Suggested-by: Andrey Konovalov Reviewed-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov8856.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index d8cefd3d4045..b337f729d5e3 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -428,7 +428,7 @@ static const struct ov8856_reg mode_3264x2448_regs[] = { {0x3810, 0x00}, {0x3811, 0x04}, {0x3812, 0x00}, - {0x3813, 0x02}, + {0x3813, 0x01}, {0x3814, 0x01}, {0x3815, 0x01}, {0x3816, 0x00}, @@ -821,7 +821,7 @@ static const struct ov8856_reg mode_1632x1224_regs[] = { {0x3810, 0x00}, {0x3811, 0x02}, {0x3812, 0x00}, - {0x3813, 0x02}, + {0x3813, 0x01}, {0x3814, 0x03}, {0x3815, 0x01}, {0x3816, 0x00}, -- cgit v1.2.3 From 41b3e23376e9c316e6bf509ab9983fc8b0c0fc25 Mon Sep 17 00:00:00 2001 From: Martina Krasteva Date: Wed, 3 Feb 2021 14:54:40 +0100 Subject: media: dt-bindings: media: Add bindings for imx334 - Add dt-bindings documentation for Sony imx334 sensor driver. - Add MAINTAINERS entry for Sony imx334 binding documentation. Signed-off-by: Martina Krasteva Reviewed-by: Gjorgji Rosikopulos Acked-by: Daniele Alessandrelli Acked-by: Paul J. Murphy Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/sony,imx334.yaml | 91 ++++++++++++++++++++++ MAINTAINERS | 8 ++ 2 files changed, 99 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx334.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx334.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx334.yaml new file mode 100644 index 000000000000..24e689314bde --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx334.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2021 Intel Corporation +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/sony,imx334.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony IMX334 Sensor + +maintainers: + - Paul J. Murphy + - Daniele Alessandrelli + +description: + IMX334 sensor is a Sony CMOS active pixel digital image sensor with an active + array size of 3864H x 2202V. It is programmable through I2C interface. The + I2C client address is fixed to 0x1a as per sensor data sheet. Image data is + sent through MIPI CSI-2. + +properties: + compatible: + const: sony,imx334 + reg: + description: I2C address + maxItems: 1 + + assigned-clocks: true + assigned-clock-parents: true + assigned-clock-rates: true + + clocks: + description: Clock frequency from 6 to 27 MHz, 37.125MHz, 74.25MHz + maxItems: 1 + + reset-gpios: + description: Reference to the GPIO connected to the XCLR pin, if any. + + port: + type: object + additionalProperties: false + $ref: /schemas/graph.yaml#/properties/port + + properties: + endpoint: + type: object + properties: + data-lanes: + $ref: ../video-interfaces.yaml#/properties/data-lanes + link-frequencies: + $ref: ../video-interfaces.yaml#/properties/link-frequencies + + required: + - data-lanes + - link-frequencies + + required: + - endpoint + +required: + - compatible + - reg + - clocks + - port + +additionalProperties: false + +examples: + - | + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + camera@1a { + compatible = "sony,imx334"; + reg = <0x1a>; + clocks = <&imx334_clk>; + + assigned-clocks = <&imx334_clk>; + assigned-clock-parents = <&imx334_clk_parent>; + assigned-clock-rates = <24000000>; + + port { + imx334: endpoint { + remote-endpoint = <&cam>; + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <891000000>; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index e6ebb2ff5d1c..36be45824a43 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16585,6 +16585,14 @@ S: Maintained T: git git://linuxtv.org/media_tree.git F: drivers/media/i2c/imx319.c +SONY IMX334 SENSOR DRIVER +M: Paul J. Murphy +M: Daniele Alessandrelli +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/sony,imx334.yaml + SONY IMX355 SENSOR DRIVER M: Tianshu Qiu L: linux-media@vger.kernel.org -- cgit v1.2.3 From 9746b11715c3949241e2d88cb9057da4adab7e02 Mon Sep 17 00:00:00 2001 From: Martina Krasteva Date: Wed, 3 Feb 2021 14:54:41 +0100 Subject: media: i2c: Add imx334 camera sensor driver Add a v4l2 sub-device driver for the Sony imx334 image sensor. This is a camera sensor using the i2c bus for control and the csi-2 bus for data. The following features are supported: - manual exposure and analog gain control support - vblank/hblank/pixel rate/link freq control support - supported resolution: - 3840x2160 @ 60fps - supported bayer order output: - SRGGB12 Signed-off-by: Martina Krasteva Reviewed-by: Gjorgji Rosikopulos Acked-by: Daniele Alessandrelli Acked-by: Paul J. Murphy Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + drivers/media/i2c/Kconfig | 14 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx334.c | 1131 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1147 insertions(+) create mode 100644 drivers/media/i2c/imx334.c diff --git a/MAINTAINERS b/MAINTAINERS index 36be45824a43..0da3255abb91 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16592,6 +16592,7 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/sony,imx334.yaml +F: drivers/media/i2c/imx334.c SONY IMX355 SENSOR DRIVER M: Tianshu Qiu diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index ad92f4789b83..2d3dc0d82f9e 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -813,6 +813,20 @@ config VIDEO_IMX319 To compile this driver as a module, choose M here: the module will be called imx319. +config VIDEO_IMX334 + tristate "Sony IMX334 sensor support" + depends on OF_GPIO + depends on I2C && VIDEO_V4L2 + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Sony + IMX334 camera. + + To compile this driver as a module, choose M here: the + module will be called imx334. + config VIDEO_IMX355 tristate "Sony IMX355 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index f5cd92bc285d..6bd22d63e1a7 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -122,6 +122,7 @@ obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o +obj-$(CONFIG_VIDEO_IMX334) += imx334.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o rdacm20-camera_module-objs := rdacm20.o max9271.o diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c new file mode 100644 index 000000000000..07e31bc2ef18 --- /dev/null +++ b/drivers/media/i2c/imx334.c @@ -0,0 +1,1131 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Sony imx334 sensor driver + * + * Copyright (C) 2021 Intel Corporation + */ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Streaming Mode */ +#define IMX334_REG_MODE_SELECT 0x3000 +#define IMX334_MODE_STANDBY 0x01 +#define IMX334_MODE_STREAMING 0x00 + +/* Lines per frame */ +#define IMX334_REG_LPFR 0x3030 + +/* Chip ID */ +#define IMX334_REG_ID 0x3044 +#define IMX334_ID 0x1e + +/* Exposure control */ +#define IMX334_REG_SHUTTER 0x3058 +#define IMX334_EXPOSURE_MIN 1 +#define IMX334_EXPOSURE_OFFSET 5 +#define IMX334_EXPOSURE_STEP 1 +#define IMX334_EXPOSURE_DEFAULT 0x0648 + +/* Analog gain control */ +#define IMX334_REG_AGAIN 0x30e8 +#define IMX334_AGAIN_MIN 0 +#define IMX334_AGAIN_MAX 240 +#define IMX334_AGAIN_STEP 1 +#define IMX334_AGAIN_DEFAULT 0 + +/* Group hold register */ +#define IMX334_REG_HOLD 0x3001 + +/* Input clock rate */ +#define IMX334_INCLK_RATE 24000000 + +/* CSI2 HW configuration */ +#define IMX334_LINK_FREQ 891000000 +#define IMX334_NUM_DATA_LANES 4 + +#define IMX334_REG_MIN 0x00 +#define IMX334_REG_MAX 0xfffff + +/** + * struct imx334_reg - imx334 sensor register + * @address: Register address + * @val: Register value + */ +struct imx334_reg { + u16 address; + u8 val; +}; + +/** + * struct imx334_reg_list - imx334 sensor register list + * @num_of_regs: Number of registers in the list + * @regs: Pointer to register list + */ +struct imx334_reg_list { + u32 num_of_regs; + const struct imx334_reg *regs; +}; + +/** + * struct imx334_mode - imx334 sensor mode structure + * @width: Frame width + * @height: Frame height + * @code: Format code + * @hblank: Horizontal blanking in lines + * @vblank: Vertical blanking in lines + * @vblank_min: Minimal vertical blanking in lines + * @vblank_max: Maximum vertical blanking in lines + * @pclk: Sensor pixel clock + * @link_freq_idx: Link frequency index + * @reg_list: Register list for sensor mode + */ +struct imx334_mode { + u32 width; + u32 height; + u32 code; + u32 hblank; + u32 vblank; + u32 vblank_min; + u32 vblank_max; + u64 pclk; + u32 link_freq_idx; + struct imx334_reg_list reg_list; +}; + +/** + * struct imx334 - imx334 sensor device structure + * @dev: Pointer to generic device + * @client: Pointer to i2c client + * @sd: V4L2 sub-device + * @pad: Media pad. Only one pad supported + * @reset_gpio: Sensor reset gpio + * @inclk: Sensor input clock + * @ctrl_handler: V4L2 control handler + * @link_freq_ctrl: Pointer to link frequency control + * @pclk_ctrl: Pointer to pixel clock control + * @hblank_ctrl: Pointer to horizontal blanking control + * @vblank_ctrl: Pointer to vertical blanking control + * @exp_ctrl: Pointer to exposure control + * @again_ctrl: Pointer to analog gain control + * @vblank: Vertical blanking in lines + * @cur_mode: Pointer to current selected sensor mode + * @mutex: Mutex for serializing sensor controls + * @streaming: Flag indicating streaming state + */ +struct imx334 { + struct device *dev; + struct i2c_client *client; + struct v4l2_subdev sd; + struct media_pad pad; + struct gpio_desc *reset_gpio; + struct clk *inclk; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *link_freq_ctrl; + struct v4l2_ctrl *pclk_ctrl; + struct v4l2_ctrl *hblank_ctrl; + struct v4l2_ctrl *vblank_ctrl; + struct { + struct v4l2_ctrl *exp_ctrl; + struct v4l2_ctrl *again_ctrl; + }; + u32 vblank; + const struct imx334_mode *cur_mode; + struct mutex mutex; + bool streaming; +}; + +static const s64 link_freq[] = { + IMX334_LINK_FREQ, +}; + +/* Sensor mode registers */ +static const struct imx334_reg mode_3840x2160_regs[] = { + {0x3000, 0x01}, + {0x3002, 0x00}, + {0x3018, 0x04}, + {0x37b0, 0x36}, + {0x304c, 0x00}, + {0x300c, 0x3b}, + {0x300d, 0x2a}, + {0x3034, 0x26}, + {0x3035, 0x02}, + {0x314c, 0x29}, + {0x314d, 0x01}, + {0x315a, 0x02}, + {0x3168, 0xa0}, + {0x316a, 0x7e}, + {0x3288, 0x21}, + {0x328a, 0x02}, + {0x302c, 0x3c}, + {0x302e, 0x00}, + {0x302f, 0x0f}, + {0x3076, 0x70}, + {0x3077, 0x08}, + {0x3090, 0x70}, + {0x3091, 0x08}, + {0x30d8, 0x20}, + {0x30d9, 0x12}, + {0x3308, 0x70}, + {0x3309, 0x08}, + {0x3414, 0x05}, + {0x3416, 0x18}, + {0x35ac, 0x0e}, + {0x3648, 0x01}, + {0x364a, 0x04}, + {0x364c, 0x04}, + {0x3678, 0x01}, + {0x367c, 0x31}, + {0x367e, 0x31}, + {0x3708, 0x02}, + {0x3714, 0x01}, + {0x3715, 0x02}, + {0x3716, 0x02}, + {0x3717, 0x02}, + {0x371c, 0x3d}, + {0x371d, 0x3f}, + {0x372c, 0x00}, + {0x372d, 0x00}, + {0x372e, 0x46}, + {0x372f, 0x00}, + {0x3730, 0x89}, + {0x3731, 0x00}, + {0x3732, 0x08}, + {0x3733, 0x01}, + {0x3734, 0xfe}, + {0x3735, 0x05}, + {0x375d, 0x00}, + {0x375e, 0x00}, + {0x375f, 0x61}, + {0x3760, 0x06}, + {0x3768, 0x1b}, + {0x3769, 0x1b}, + {0x376a, 0x1a}, + {0x376b, 0x19}, + {0x376c, 0x18}, + {0x376d, 0x14}, + {0x376e, 0x0f}, + {0x3776, 0x00}, + {0x3777, 0x00}, + {0x3778, 0x46}, + {0x3779, 0x00}, + {0x377a, 0x08}, + {0x377b, 0x01}, + {0x377c, 0x45}, + {0x377d, 0x01}, + {0x377e, 0x23}, + {0x377f, 0x02}, + {0x3780, 0xd9}, + {0x3781, 0x03}, + {0x3782, 0xf5}, + {0x3783, 0x06}, + {0x3784, 0xa5}, + {0x3788, 0x0f}, + {0x378a, 0xd9}, + {0x378b, 0x03}, + {0x378c, 0xeb}, + {0x378d, 0x05}, + {0x378e, 0x87}, + {0x378f, 0x06}, + {0x3790, 0xf5}, + {0x3792, 0x43}, + {0x3794, 0x7a}, + {0x3796, 0xa1}, + {0x3e04, 0x0e}, + {0x3a00, 0x01}, +}; + +/* Supported sensor mode configurations */ +static const struct imx334_mode supported_mode = { + .width = 3840, + .height = 2160, + .hblank = 560, + .vblank = 2340, + .vblank_min = 90, + .vblank_max = 132840, + .pclk = 594000000, + .link_freq_idx = 0, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3840x2160_regs), + .regs = mode_3840x2160_regs, + }, +}; + +/** + * to_imx334() - imv334 V4L2 sub-device to imx334 device. + * @subdev: pointer to imx334 V4L2 sub-device + * + * Return: pointer to imx334 device + */ +static inline struct imx334 *to_imx334(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct imx334, sd); +} + +/** + * imx334_read_reg() - Read registers. + * @imx334: pointer to imx334 device + * @reg: register address + * @len: length of bytes to read. Max supported bytes is 4 + * @val: pointer to register value to be filled. + * + * Big endian register addresses with little endian values. + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_read_reg(struct imx334 *imx334, u16 reg, u32 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx334->sd); + struct i2c_msg msgs[2] = {0}; + u8 addr_buf[2] = {0}; + u8 data_buf[4] = {0}; + int ret; + + if (WARN_ON(len > 4)) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = data_buf; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_le32(data_buf); + + return 0; +} + +/** + * imx334_write_reg() - Write register + * @imx334: pointer to imx334 device + * @reg: register address + * @len: length of bytes. Max supported bytes is 4 + * @val: register value + * + * Big endian register addresses with little endian values. + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_write_reg(struct imx334 *imx334, u16 reg, u32 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx334->sd); + u8 buf[6] = {0}; + + if (WARN_ON(len > 4)) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_le32(val, buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/** + * imx334_write_regs() - Write a list of registers + * @imx334: pointer to imx334 device + * @regs: list of registers to be written + * @len: length of registers array + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_write_regs(struct imx334 *imx334, + const struct imx334_reg *regs, u32 len) +{ + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = imx334_write_reg(imx334, regs[i].address, 1, regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +/** + * imx334_update_controls() - Update control ranges based on streaming mode + * @imx334: pointer to imx334 device + * @mode: pointer to imx334_mode sensor mode + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_update_controls(struct imx334 *imx334, + const struct imx334_mode *mode) +{ + int ret; + + ret = __v4l2_ctrl_s_ctrl(imx334->link_freq_ctrl, mode->link_freq_idx); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(imx334->hblank_ctrl, mode->hblank); + if (ret) + return ret; + + return __v4l2_ctrl_modify_range(imx334->vblank_ctrl, mode->vblank_min, + mode->vblank_max, 1, mode->vblank); +} + +/** + * imx334_update_exp_gain() - Set updated exposure and gain + * @imx334: pointer to imx334 device + * @exposure: updated exposure value + * @gain: updated analog gain value + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_update_exp_gain(struct imx334 *imx334, u32 exposure, u32 gain) +{ + u32 lpfr, shutter; + int ret; + + lpfr = imx334->vblank + imx334->cur_mode->height; + shutter = lpfr - exposure; + + dev_dbg(imx334->dev, "Set long exp %u analog gain %u sh0 %u lpfr %u", + exposure, gain, shutter, lpfr); + + ret = imx334_write_reg(imx334, IMX334_REG_HOLD, 1, 1); + if (ret) + return ret; + + ret = imx334_write_reg(imx334, IMX334_REG_LPFR, 3, lpfr); + if (ret) + goto error_release_group_hold; + + ret = imx334_write_reg(imx334, IMX334_REG_SHUTTER, 3, shutter); + if (ret) + goto error_release_group_hold; + + ret = imx334_write_reg(imx334, IMX334_REG_AGAIN, 1, gain); + +error_release_group_hold: + imx334_write_reg(imx334, IMX334_REG_HOLD, 1, 0); + + return ret; +} + +/** + * imx334_set_ctrl() - Set subdevice control + * @ctrl: pointer to v4l2_ctrl structure + * + * Supported controls: + * - V4L2_CID_VBLANK + * - cluster controls: + * - V4L2_CID_ANALOGUE_GAIN + * - V4L2_CID_EXPOSURE + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx334 *imx334 = + container_of(ctrl->handler, struct imx334, ctrl_handler); + u32 analog_gain; + u32 exposure; + int ret; + + switch (ctrl->id) { + case V4L2_CID_VBLANK: + imx334->vblank = imx334->vblank_ctrl->val; + + dev_dbg(imx334->dev, "Received vblank %u, new lpfr %u", + imx334->vblank, + imx334->vblank + imx334->cur_mode->height); + + ret = __v4l2_ctrl_modify_range(imx334->exp_ctrl, + IMX334_EXPOSURE_MIN, + imx334->vblank + + imx334->cur_mode->height - + IMX334_EXPOSURE_OFFSET, + 1, IMX334_EXPOSURE_DEFAULT); + break; + case V4L2_CID_EXPOSURE: + + /* Set controls only if sensor is in power on state */ + if (!pm_runtime_get_if_in_use(imx334->dev)) + return 0; + + exposure = ctrl->val; + analog_gain = imx334->again_ctrl->val; + + dev_dbg(imx334->dev, "Received exp %u analog gain %u", + exposure, analog_gain); + + ret = imx334_update_exp_gain(imx334, exposure, analog_gain); + + pm_runtime_put(imx334->dev); + + break; + default: + dev_err(imx334->dev, "Invalid control %d", ctrl->id); + ret = -EINVAL; + } + + return ret; +} + +/* V4l2 subdevice control ops*/ +static const struct v4l2_ctrl_ops imx334_ctrl_ops = { + .s_ctrl = imx334_set_ctrl, +}; + +/** + * imx334_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes + * @sd: pointer to imx334 V4L2 sub-device structure + * @cfg: V4L2 sub-device pad configuration + * @code: V4L2 sub-device code enumeration need to be filled + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = supported_mode.code; + + return 0; +} + +/** + * imx334_enum_frame_size() - Enumerate V4L2 sub-device frame sizes + * @sd: pointer to imx334 V4L2 sub-device structure + * @cfg: V4L2 sub-device pad configuration + * @fsize: V4L2 sub-device size enumeration need to be filled + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fsize) +{ + if (fsize->index > 0) + return -EINVAL; + + if (fsize->code != supported_mode.code) + return -EINVAL; + + fsize->min_width = supported_mode.width; + fsize->max_width = fsize->min_width; + fsize->min_height = supported_mode.height; + fsize->max_height = fsize->min_height; + + return 0; +} + +/** + * imx334_fill_pad_format() - Fill subdevice pad format + * from selected sensor mode + * @imx334: pointer to imx334 device + * @mode: pointer to imx334_mode sensor mode + * @fmt: V4L2 sub-device format need to be filled + */ +static void imx334_fill_pad_format(struct imx334 *imx334, + const struct imx334_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->code; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; +} + +/** + * imx334_get_pad_format() - Get subdevice pad format + * @sd: pointer to imx334 V4L2 sub-device structure + * @cfg: V4L2 sub-device pad configuration + * @fmt: V4L2 sub-device format need to be set + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx334 *imx334 = to_imx334(sd); + + mutex_lock(&imx334->mutex); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *framefmt; + + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + fmt->format = *framefmt; + } else { + imx334_fill_pad_format(imx334, imx334->cur_mode, fmt); + } + + mutex_unlock(&imx334->mutex); + + return 0; +} + +/** + * imx334_set_pad_format() - Set subdevice pad format + * @sd: pointer to imx334 V4L2 sub-device structure + * @cfg: V4L2 sub-device pad configuration + * @fmt: V4L2 sub-device format need to be set + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx334 *imx334 = to_imx334(sd); + const struct imx334_mode *mode; + int ret = 0; + + mutex_lock(&imx334->mutex); + + mode = &supported_mode; + imx334_fill_pad_format(imx334, mode, fmt); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *framefmt; + + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + *framefmt = fmt->format; + } else { + ret = imx334_update_controls(imx334, mode); + if (!ret) + imx334->cur_mode = mode; + } + + mutex_unlock(&imx334->mutex); + + return ret; +} + +/** + * imx334_init_pad_cfg() - Initialize sub-device pad configuration + * @sd: pointer to imx334 V4L2 sub-device structure + * @cfg: V4L2 sub-device pad configuration + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_init_pad_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct imx334 *imx334 = to_imx334(sd); + struct v4l2_subdev_format fmt = { 0 }; + + fmt.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + imx334_fill_pad_format(imx334, &supported_mode, &fmt); + + return imx334_set_pad_format(sd, cfg, &fmt); +} + +/** + * imx334_start_streaming() - Start sensor stream + * @imx334: pointer to imx334 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_start_streaming(struct imx334 *imx334) +{ + const struct imx334_reg_list *reg_list; + int ret; + + /* Write sensor mode registers */ + reg_list = &imx334->cur_mode->reg_list; + ret = imx334_write_regs(imx334, reg_list->regs, + reg_list->num_of_regs); + if (ret) { + dev_err(imx334->dev, "fail to write initial registers"); + return ret; + } + + /* Setup handler will write actual exposure and gain */ + ret = __v4l2_ctrl_handler_setup(imx334->sd.ctrl_handler); + if (ret) { + dev_err(imx334->dev, "fail to setup handler"); + return ret; + } + + /* Start streaming */ + ret = imx334_write_reg(imx334, IMX334_REG_MODE_SELECT, + 1, IMX334_MODE_STREAMING); + if (ret) { + dev_err(imx334->dev, "fail to start streaming"); + return ret; + } + + return 0; +} + +/** + * imx334_stop_streaming() - Stop sensor stream + * @imx334: pointer to imx334 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_stop_streaming(struct imx334 *imx334) +{ + return imx334_write_reg(imx334, IMX334_REG_MODE_SELECT, + 1, IMX334_MODE_STANDBY); +} + +/** + * imx334_set_stream() - Enable sensor streaming + * @sd: pointer to imx334 subdevice + * @enable: set to enable sensor streaming + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx334 *imx334 = to_imx334(sd); + int ret; + + mutex_lock(&imx334->mutex); + + if (imx334->streaming == enable) { + mutex_unlock(&imx334->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_get_sync(imx334->dev); + if (ret) + goto error_power_off; + + ret = imx334_start_streaming(imx334); + if (ret) + goto error_power_off; + } else { + imx334_stop_streaming(imx334); + pm_runtime_put(imx334->dev); + } + + imx334->streaming = enable; + + mutex_unlock(&imx334->mutex); + + return 0; + +error_power_off: + pm_runtime_put(imx334->dev); + mutex_unlock(&imx334->mutex); + + return ret; +} + +/** + * imx334_detect() - Detect imx334 sensor + * @imx334: pointer to imx334 device + * + * Return: 0 if successful, -EIO if sensor id does not match + */ +static int imx334_detect(struct imx334 *imx334) +{ + int ret; + u32 val; + + ret = imx334_read_reg(imx334, IMX334_REG_ID, 2, &val); + if (ret) + return ret; + + if (val != IMX334_ID) { + dev_err(imx334->dev, "chip id mismatch: %x!=%x", + IMX334_ID, val); + return -ENXIO; + } + + return 0; +} + +/** + * imx334_parse_hw_config() - Parse HW configuration and check if supported + * @imx334: pointer to imx334 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_parse_hw_config(struct imx334 *imx334) +{ + struct fwnode_handle *fwnode = dev_fwnode(imx334->dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + unsigned long rate; + int ret; + int i; + + if (!fwnode) + return -ENXIO; + + /* Request optional reset pin */ + imx334->reset_gpio = devm_gpiod_get_optional(imx334->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(imx334->reset_gpio)) { + dev_err(imx334->dev, "failed to get reset gpio %d", ret); + return PTR_ERR(imx334->reset_gpio); + } + + /* Get sensor input clock */ + imx334->inclk = devm_clk_get(imx334->dev, NULL); + if (IS_ERR(imx334->inclk)) { + dev_err(imx334->dev, "could not get inclk"); + return PTR_ERR(imx334->inclk); + } + + rate = clk_get_rate(imx334->inclk); + if (rate != IMX334_INCLK_RATE) { + dev_err(imx334->dev, "inclk frequency mismatch"); + return -EINVAL; + } + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENXIO; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX334_NUM_DATA_LANES) { + dev_err(imx334->dev, + "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto done_endpoint_free; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(imx334->dev, "no link frequencies defined"); + ret = -EINVAL; + goto done_endpoint_free; + } + + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) + if (bus_cfg.link_frequencies[i] == IMX334_LINK_FREQ) + goto done_endpoint_free; + + ret = -EINVAL; + +done_endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +/* V4l2 subdevice ops */ +static const struct v4l2_subdev_video_ops imx334_video_ops = { + .s_stream = imx334_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx334_pad_ops = { + .init_cfg = imx334_init_pad_cfg, + .enum_mbus_code = imx334_enum_mbus_code, + .enum_frame_size = imx334_enum_frame_size, + .get_fmt = imx334_get_pad_format, + .set_fmt = imx334_set_pad_format, +}; + +static const struct v4l2_subdev_ops imx334_subdev_ops = { + .video = &imx334_video_ops, + .pad = &imx334_pad_ops, +}; + +/** + * imx334_power_on() - Sensor power on sequence + * @dev: pointer to i2c device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx334 *imx334 = to_imx334(sd); + int ret; + + gpiod_set_value_cansleep(imx334->reset_gpio, 1); + + ret = clk_prepare_enable(imx334->inclk); + if (ret) { + dev_err(imx334->dev, "fail to enable inclk"); + goto error_reset; + } + + usleep_range(18000, 20000); + + return 0; + +error_reset: + gpiod_set_value_cansleep(imx334->reset_gpio, 0); + + return ret; +} + +/** + * imx334_power_off() - Sensor power off sequence + * @dev: pointer to i2c device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx334 *imx334 = to_imx334(sd); + + gpiod_set_value_cansleep(imx334->reset_gpio, 0); + + clk_disable_unprepare(imx334->inclk); + + return 0; +} + +/** + * imx334_init_controls() - Initialize sensor subdevice controls + * @imx334: pointer to imx334 device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_init_controls(struct imx334 *imx334) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &imx334->ctrl_handler; + const struct imx334_mode *mode = imx334->cur_mode; + u32 lpfr; + int ret; + + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 6); + if (ret) + return ret; + + /* Serialize controls with sensor device */ + ctrl_hdlr->lock = &imx334->mutex; + + /* Initialize exposure and gain */ + lpfr = mode->vblank + mode->height; + imx334->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx334_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX334_EXPOSURE_MIN, + lpfr - IMX334_EXPOSURE_OFFSET, + IMX334_EXPOSURE_STEP, + IMX334_EXPOSURE_DEFAULT); + + imx334->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx334_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + IMX334_AGAIN_MIN, + IMX334_AGAIN_MAX, + IMX334_AGAIN_STEP, + IMX334_AGAIN_DEFAULT); + + v4l2_ctrl_cluster(2, &imx334->exp_ctrl); + + imx334->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx334_ctrl_ops, + V4L2_CID_VBLANK, + mode->vblank_min, + mode->vblank_max, + 1, mode->vblank); + + /* Read only controls */ + imx334->pclk_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx334_ctrl_ops, + V4L2_CID_PIXEL_RATE, + mode->pclk, mode->pclk, + 1, mode->pclk); + + imx334->link_freq_ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &imx334_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq) - + 1, + mode->link_freq_idx, + link_freq); + if (imx334->link_freq_ctrl) + imx334->link_freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + imx334->hblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &imx334_ctrl_ops, + V4L2_CID_HBLANK, + IMX334_REG_MIN, + IMX334_REG_MAX, + 1, mode->hblank); + if (imx334->hblank_ctrl) + imx334->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctrl_hdlr->error) { + dev_err(imx334->dev, "control init failed: %d", + ctrl_hdlr->error); + v4l2_ctrl_handler_free(ctrl_hdlr); + return ctrl_hdlr->error; + } + + imx334->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +/** + * imx334_probe() - I2C client device binding + * @client: pointer to i2c client device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_probe(struct i2c_client *client) +{ + struct imx334 *imx334; + int ret; + + imx334 = devm_kzalloc(&client->dev, sizeof(*imx334), GFP_KERNEL); + if (!imx334) + return -ENOMEM; + + imx334->dev = &client->dev; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx334->sd, client, &imx334_subdev_ops); + + ret = imx334_parse_hw_config(imx334); + if (ret) { + dev_err(imx334->dev, "HW configuration is not supported"); + return ret; + } + + mutex_init(&imx334->mutex); + + ret = imx334_power_on(imx334->dev); + if (ret) { + dev_err(imx334->dev, "failed to power-on the sensor"); + goto error_mutex_destroy; + } + + /* Check module identity */ + ret = imx334_detect(imx334); + if (ret) { + dev_err(imx334->dev, "failed to find sensor: %d", ret); + goto error_power_off; + } + + /* Set default mode to max resolution */ + imx334->cur_mode = &supported_mode; + imx334->vblank = imx334->cur_mode->vblank; + + ret = imx334_init_controls(imx334); + if (ret) { + dev_err(imx334->dev, "failed to init controls: %d", ret); + goto error_power_off; + } + + /* Initialize subdev */ + imx334->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx334->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + imx334->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&imx334->sd.entity, 1, &imx334->pad); + if (ret) { + dev_err(imx334->dev, "failed to init entity pads: %d", ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor_common(&imx334->sd); + if (ret < 0) { + dev_err(imx334->dev, + "failed to register async subdev: %d", ret); + goto error_media_entity; + } + + pm_runtime_set_active(imx334->dev); + pm_runtime_enable(imx334->dev); + pm_runtime_idle(imx334->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&imx334->sd.entity); +error_handler_free: + v4l2_ctrl_handler_free(imx334->sd.ctrl_handler); +error_power_off: + imx334_power_off(imx334->dev); +error_mutex_destroy: + mutex_destroy(&imx334->mutex); + + return ret; +} + +/** + * imx334_remove() - I2C client device unbinding + * @client: pointer to I2C client device + * + * Return: 0 if successful, error code otherwise. + */ +static int imx334_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx334 *imx334 = to_imx334(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + pm_runtime_disable(&client->dev); + pm_runtime_suspended(&client->dev); + + mutex_destroy(&imx334->mutex); + + return 0; +} + +static const struct dev_pm_ops imx334_pm_ops = { + SET_RUNTIME_PM_OPS(imx334_power_off, imx334_power_on, NULL) +}; + +static const struct of_device_id imx334_of_match[] = { + { .compatible = "sony,imx334" }, + { } +}; + +MODULE_DEVICE_TABLE(of, imx334_of_match); + +static struct i2c_driver imx334_driver = { + .probe_new = imx334_probe, + .remove = imx334_remove, + .driver = { + .name = "imx334", + .pm = &imx334_pm_ops, + .of_match_table = imx334_of_match, + }, +}; + +module_i2c_driver(imx334_driver); + +MODULE_DESCRIPTION("Sony imx334 sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From c702e2f70275dbc5373aef50c450cf9c5730636c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 8 Feb 2021 12:32:29 +0100 Subject: media: imx334: 'ret' is uninitialized, should have been PTR_ERR() Fix this compiler warning: drivers/media/i2c/imx334.c: In function 'imx334_parse_hw_config': include/linux/dev_printk.h:112:2: warning: 'ret' may be used uninitialized in this function [-Wmaybe-uninitialized] 112 | _dev_err(dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~~ drivers/media/i2c/imx334.c:783:6: note: 'ret' was declared here 783 | int ret; | ^~~ Signed-off-by: Hans Verkuil Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx334.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 07e31bc2ef18..ad530f0d338a 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -790,7 +790,8 @@ static int imx334_parse_hw_config(struct imx334 *imx334) imx334->reset_gpio = devm_gpiod_get_optional(imx334->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(imx334->reset_gpio)) { - dev_err(imx334->dev, "failed to get reset gpio %d", ret); + dev_err(imx334->dev, "failed to get reset gpio %ld", + PTR_ERR(imx334->reset_gpio)); return PTR_ERR(imx334->reset_gpio); } -- cgit v1.2.3 From 7f03d9fefcc55ed4882338126ef1f6b6778ea21f Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 9 Feb 2021 12:09:21 +0100 Subject: media: i2c: Kconfig: Make MAX9271 a module With the introduction of the RDACM21 camera module support in commit a59f853b3b4b ("media: i2c: Add driver for RDACM21 camera module") the symbols defined by the max9271 library were exported twice if multiple users of the library were compiled in at the same time. In example: WARNING: modpost: drivers/media/i2c/rdacm21-camera_module: 'max9271_set_serial_link' exported twice. Previous export was in drivers/media/i2c/rdacm20-camera_module.ko Fix this by making the max9271 file a module and have the driver using its functions select it. Fixes: a59f853b3b4b ("media: i2c: Add driver for RDACM21 camera module") Reported-by: Stephen Rothwell Reviewed-by: Geert Uytterhoeven Acked-by: Sakari Ailus Reviewed-by: Laurent Pinchart Suggested-by: Mauro Carvalho Chehab Signed-off-by: Jacopo Mondi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 5 +++++ drivers/media/i2c/Makefile | 7 +++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 2d3dc0d82f9e..462c0e059754 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1240,12 +1240,16 @@ config VIDEO_NOON010PC30 source "drivers/media/i2c/m5mols/Kconfig" +config VIDEO_MAX9271_LIB + tristate + config VIDEO_RDACM20 tristate "IMI RDACM20 camera support" depends on I2C select V4L2_FWNODE select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER + select VIDEO_MAX9271_LIB help This driver supports the IMI RDACM20 GMSL camera, used in ADAS systems. @@ -1259,6 +1263,7 @@ config VIDEO_RDACM21 select V4L2_FWNODE select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER + select VIDEO_MAX9271_LIB help This driver supports the IMI RDACM21 GMSL camera, used in ADAS systems. diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 6bd22d63e1a7..0c067beca066 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -125,10 +125,9 @@ obj-$(CONFIG_VIDEO_IMX319) += imx319.o obj-$(CONFIG_VIDEO_IMX334) += imx334.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o -rdacm20-camera_module-objs := rdacm20.o max9271.o -obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o -rdacm21-camera_module-objs := rdacm21.o max9271.o -obj-$(CONFIG_VIDEO_RDACM21) += rdacm21-camera_module.o +obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o +obj-$(CONFIG_VIDEO_RDACM20) += rdacm20.o +obj-$(CONFIG_VIDEO_RDACM21) += rdacm21.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o obj-$(CONFIG_SDR_MAX2175) += max2175.o -- cgit v1.2.3 From ce79aecf608469b8b8e422928e6fca50b6ca7133 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 10 Feb 2021 16:49:08 +0100 Subject: media: i2c: max9271: Add MODULE_* macros Since commit 7f03d9fefcc5 ("media: i2c: Kconfig: Make MAX9271 a module") the max9271 library is built as a module but no MODULE_*() attributes were specified, causing a build error due to missing license information. ERROR: modpost: missing MODULE_LICENSE() in drivers/media/i2c/max9271.o Fix this by adding MODULE attributes to the driver. Fixes: 7f03d9fefcc5 ("media: i2c: Kconfig: Make MAX9271 a module") Reported-by: Stephen Rothwell Reported-by: Nathan Chancellor Signed-off-by: Jacopo Mondi Acked-by: Sakari Ailus Reviewed-by: Laurent Pinchart Reviewed-by: Nathan Chancellor Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/max9271.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c index c247db569bab..c495582dcff6 100644 --- a/drivers/media/i2c/max9271.c +++ b/drivers/media/i2c/max9271.c @@ -18,6 +18,7 @@ #include #include +#include #include "max9271.h" @@ -339,3 +340,7 @@ int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest) return 0; } EXPORT_SYMBOL_GPL(max9271_set_translation); + +MODULE_DESCRIPTION("Maxim MAX9271 GMSL Serializer"); +MODULE_AUTHOR("Jacopo Mondi"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8f202f8e9ff38e29694a4bc0a519b4e03c1726ee Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 15 Feb 2021 13:37:28 +0100 Subject: media: v4l: async: Fix kerneldoc documentation for async functions Fix kerneldoc documentation for functions that add async sub-devices to notifiers. The functions themselves were improved recently but that left issues with the kerneldoc documentation. Fix them now. Also remove underscores from macro argument names. [mchehab: fix a build breakage] Reported-by: Stephen Rothwell Fixes: b01edcbd409c ("media: v4l2-async: Improve v4l2_async_notifier_add_*_subdev() API") Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-async.h | 80 ++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 6f22daa6f067..f572e1279182 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -156,42 +156,44 @@ void v4l2_async_notifier_init(struct v4l2_async_notifier *notifier); int __v4l2_async_notifier_add_subdev(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd); +struct v4l2_async_subdev * +__v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, + struct fwnode_handle *fwnode, + unsigned int asd_struct_size); /** * v4l2_async_notifier_add_fwnode_subdev - Allocate and add a fwnode async * subdev to the notifier's master asd_list. * * @notifier: pointer to &struct v4l2_async_notifier - * @fwnode: fwnode handle of the sub-device to be matched - * @asd_struct_size: size of the driver's async sub-device struct, including - * sizeof(struct v4l2_async_subdev). The &struct - * v4l2_async_subdev shall be the first member of - * the driver's async sub-device struct, i.e. both - * begin at the same memory address. + * @fwnode: fwnode handle of the sub-device to be matched, pointer to + * &struct fwnode_handle + * @type: Type of the driver's async sub-device struct. The &struct + * v4l2_async_subdev shall be the first member of the driver's async + * sub-device struct, i.e. both begin at the same memory address. * * Allocate a fwnode-matched asd of size asd_struct_size, and add it to the * notifiers @asd_list. The function also gets a reference of the fwnode which * is released later at notifier cleanup time. */ -struct v4l2_async_subdev * -__v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, - struct fwnode_handle *fwnode, - unsigned int asd_struct_size); -#define v4l2_async_notifier_add_fwnode_subdev(__notifier, __fwnode, __type) \ -((__type *)__v4l2_async_notifier_add_fwnode_subdev(__notifier, __fwnode, \ - sizeof(__type))) +#define v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode, type) \ + ((type *)__v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode, \ + sizeof(type))) +struct v4l2_async_subdev * +__v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, + struct fwnode_handle *endpoint, + unsigned int asd_struct_size); /** * v4l2_async_notifier_add_fwnode_remote_subdev - Allocate and add a fwnode * remote async subdev to the * notifier's master asd_list. * - * @notif: pointer to &struct v4l2_async_notifier - * @endpoint: local endpoint pointing to the remote sub-device to be matched - * @asd_struct_size: size of the driver's async sub-device struct, including - * sizeof(struct v4l2_async_subdev). The &struct - * v4l2_async_subdev shall be the first member of - * the driver's async sub-device struct, i.e. both - * begin at the same memory address. + * @notifier: pointer to &struct v4l2_async_notifier + * @ep: local endpoint pointing to the remote sub-device to be matched, + * pointer to &struct fwnode_handle + * @type: Type of the driver's async sub-device struct. The &struct + * v4l2_async_subdev shall be the first member of the driver's async + * sub-device struct, i.e. both begin at the same memory address. * * Gets the remote endpoint of a given local endpoint, set it up for fwnode * matching and adds the async sub-device to the notifier's @asd_list. The @@ -201,36 +203,32 @@ __v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, * This is just like @v4l2_async_notifier_add_fwnode_subdev, but with the * exception that the fwnode refers to a local endpoint, not the remote one. */ -struct v4l2_async_subdev * -__v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, - struct fwnode_handle *endpoint, - unsigned int asd_struct_size); -#define v4l2_async_notifier_add_fwnode_remote_subdev(__notifier, __ep, __type) \ -((__type *)__v4l2_async_notifier_add_fwnode_remote_subdev(__notifier, __ep, \ - sizeof(__type))) +#define v4l2_async_notifier_add_fwnode_remote_subdev(notifier, ep, type) \ + ((type *) \ + __v4l2_async_notifier_add_fwnode_remote_subdev(notifier, ep, \ + sizeof(type))) +struct v4l2_async_subdev * +__v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, + int adapter_id, unsigned short address, + unsigned int asd_struct_size); /** * v4l2_async_notifier_add_i2c_subdev - Allocate and add an i2c async * subdev to the notifier's master asd_list. * * @notifier: pointer to &struct v4l2_async_notifier - * @adapter_id: I2C adapter ID to be matched + * @adapter: I2C adapter ID to be matched * @address: I2C address of sub-device to be matched - * @asd_struct_size: size of the driver's async sub-device struct, including - * sizeof(struct v4l2_async_subdev). The &struct - * v4l2_async_subdev shall be the first member of - * the driver's async sub-device struct, i.e. both - * begin at the same memory address. + * @type: Type of the driver's async sub-device struct. The &struct + * v4l2_async_subdev shall be the first member of the driver's async + * sub-device struct, i.e. both begin at the same memory address. * - * Same as above but for I2C matched sub-devices. + * Same as v4l2_async_notifier_add_fwnode_subdev() but for I2C matched + * sub-devices. */ -struct v4l2_async_subdev * -__v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, - int adapter_id, unsigned short address, - unsigned int asd_struct_size); -#define v4l2_async_notifier_add_i2c_subdev(__notifier, __adap, __addr, __type) \ -((__type *)__v4l2_async_notifier_add_i2c_subdev(__notifier, __adap, __addr, \ - sizeof(__type))) +#define v4l2_async_notifier_add_i2c_subdev(notifier, adapter, address, type) \ + ((type *)__v4l2_async_notifier_add_i2c_subdev(notifier, adapter, \ + address, sizeof(type))) /** * v4l2_async_notifier_register - registers a subdevice asynchronous notifier -- cgit v1.2.3