* [PATCH v4 5/7] [media] coda: fix frame index to returned error
From: Michael Tretter @ 2017-01-20 14:00 UTC (permalink / raw)
To: linux-media-u79uwXL29TY76Z2rM5mHXA
Cc: Philipp Zabel, devicetree-u79uwXL29TY76Z2rM5mHXA, Hans Verkuil,
Mauro Carvalho Chehab, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
Michael Tretter
In-Reply-To: <20170120140025.3338-1-m.tretter-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
display_idx refers to the frame that will be returned in the next round.
The currently processed frame is ctx->display_idx and errors should be
reported for this frame.
Signed-off-by: Michael Tretter <m.tretter-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Acked-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
drivers/media/platform/coda/coda-bit.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index b6625047250d..309eb4eb5ad1 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -2057,7 +2057,7 @@ static void coda_finish_decode(struct coda_ctx *ctx)
}
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload);
- coda_m2m_buf_done(ctx, dst_buf, ctx->frame_errors[display_idx] ?
+ coda_m2m_buf_done(ctx, dst_buf, ctx->frame_errors[ctx->display_idx] ?
VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 6/7] [media] coda: use VDOA for un-tiling custom macroblock format
From: Michael Tretter @ 2017-01-20 14:00 UTC (permalink / raw)
To: linux-media-u79uwXL29TY76Z2rM5mHXA
Cc: Philipp Zabel, devicetree-u79uwXL29TY76Z2rM5mHXA, Hans Verkuil,
Mauro Carvalho Chehab, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
Michael Tretter
In-Reply-To: <20170120140025.3338-1-m.tretter-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
If the CODA driver is configured to produce NV12 output and the VDOA is
available, the VDOA can be used to transform the custom macroblock tiled
format to a raster-ordered format for scanout.
In this case, set the output format of the CODA to the custom macroblock
tiled format, disable the rotator, and use the VDOA to write to the v4l2
buffer. The VDOA is synchronized with the CODA to always un-tile the
frame that the CODA finished in the previous run.
Signed-off-by: Michael Tretter <m.tretter-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
drivers/media/platform/coda/coda-bit.c | 86 +++++++++++++++++-------
drivers/media/platform/coda/coda-common.c | 104 ++++++++++++++++++++++++++++--
drivers/media/platform/coda/coda.h | 3 +
3 files changed, 163 insertions(+), 30 deletions(-)
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index 309eb4eb5ad1..f608de4c52ac 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -30,6 +30,7 @@
#include <media/videobuf2-vmalloc.h>
#include "coda.h"
+#include "imx-vdoa.h"
#define CREATE_TRACE_POINTS
#include "trace.h"
@@ -1517,6 +1518,10 @@ static int __coda_start_decoding(struct coda_ctx *ctx)
u32 val;
int ret;
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "Video Data Order Adapter: %s\n",
+ ctx->use_vdoa ? "Enabled" : "Disabled");
+
/* Start decoding */
q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
@@ -1535,7 +1540,8 @@ static int __coda_start_decoding(struct coda_ctx *ctx)
if (dst_fourcc == V4L2_PIX_FMT_NV12)
ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE;
if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP)
- ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR;
+ ctx->frame_mem_ctrl |= (0x3 << 9) |
+ ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR);
coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL);
ctx->display_idx = -1;
@@ -1618,6 +1624,15 @@ static int __coda_start_decoding(struct coda_ctx *ctx)
__func__, ctx->idx, width, height);
ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED);
+ /*
+ * If the VDOA is used, the decoder needs one additional frame,
+ * because the frames are freed when the next frame is decoded.
+ * Otherwise there are visible errors in the decoded frames (green
+ * regions in displayed frames) and a broken order of frames (earlier
+ * frames are sporadically displayed after later frames).
+ */
+ if (ctx->use_vdoa)
+ ctx->num_internal_frames += 1;
if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) {
v4l2_err(&dev->v4l2_dev,
"not enough framebuffers to decode (%d < %d)\n",
@@ -1724,6 +1739,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx)
struct coda_q_data *q_data_dst;
struct coda_buffer_meta *meta;
unsigned long flags;
+ u32 rot_mode = 0;
u32 reg_addr, reg_stride;
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
@@ -1759,27 +1775,40 @@ static int coda_prepare_decode(struct coda_ctx *ctx)
if (dev->devtype->product == CODA_960)
coda_set_gdi_regs(ctx);
- if (dev->devtype->product == CODA_960) {
- /*
- * The CODA960 seems to have an internal list of buffers with
- * 64 entries that includes the registered frame buffers as
- * well as the rotator buffer output.
- * ROT_INDEX needs to be < 0x40, but > ctx->num_internal_frames.
- */
- coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->vb2_buf.index,
- CODA9_CMD_DEC_PIC_ROT_INDEX);
-
- reg_addr = CODA9_CMD_DEC_PIC_ROT_ADDR_Y;
- reg_stride = CODA9_CMD_DEC_PIC_ROT_STRIDE;
+ if (ctx->use_vdoa &&
+ ctx->display_idx >= 0 &&
+ ctx->display_idx < ctx->num_internal_frames) {
+ vdoa_device_run(ctx->vdoa,
+ vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0),
+ ctx->internal_frames[ctx->display_idx].paddr);
} else {
- reg_addr = CODA_CMD_DEC_PIC_ROT_ADDR_Y;
- reg_stride = CODA_CMD_DEC_PIC_ROT_STRIDE;
+ if (dev->devtype->product == CODA_960) {
+ /*
+ * The CODA960 seems to have an internal list of
+ * buffers with 64 entries that includes the
+ * registered frame buffers as well as the rotator
+ * buffer output.
+ *
+ * ROT_INDEX needs to be < 0x40, but >
+ * ctx->num_internal_frames.
+ */
+ coda_write(dev,
+ CODA_MAX_FRAMEBUFFERS + dst_buf->vb2_buf.index,
+ CODA9_CMD_DEC_PIC_ROT_INDEX);
+
+ reg_addr = CODA9_CMD_DEC_PIC_ROT_ADDR_Y;
+ reg_stride = CODA9_CMD_DEC_PIC_ROT_STRIDE;
+ } else {
+ reg_addr = CODA_CMD_DEC_PIC_ROT_ADDR_Y;
+ reg_stride = CODA_CMD_DEC_PIC_ROT_STRIDE;
+ }
+ coda_write_base(ctx, q_data_dst, dst_buf, reg_addr);
+ coda_write(dev, q_data_dst->bytesperline, reg_stride);
+
+ rot_mode = CODA_ROT_MIR_ENABLE | ctx->params.rot_mode;
}
- coda_write_base(ctx, q_data_dst, dst_buf, reg_addr);
- coda_write(dev, q_data_dst->bytesperline, reg_stride);
- coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
- CODA_CMD_DEC_PIC_ROT_MODE);
+ coda_write(dev, rot_mode, CODA_CMD_DEC_PIC_ROT_MODE);
switch (dev->devtype->product) {
case CODA_DX6:
@@ -1851,6 +1880,7 @@ static void coda_finish_decode(struct coda_ctx *ctx)
u32 src_fourcc;
int success;
u32 err_mb;
+ int err_vdoa = 0;
u32 val;
/* Update kfifo out pointer from coda bitstream read pointer */
@@ -1934,13 +1964,17 @@ static void coda_finish_decode(struct coda_ctx *ctx)
}
}
+ /* Wait until the VDOA finished writing the previous display frame */
+ if (ctx->use_vdoa &&
+ ctx->display_idx >= 0 &&
+ ctx->display_idx < ctx->num_internal_frames) {
+ err_vdoa = vdoa_wait_for_completion(ctx->vdoa);
+ }
+
ctx->frm_dis_flg = coda_read(dev,
CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
- /*
- * The previous display frame was copied out by the rotator,
- * now it can be overwritten again
- */
+ /* The previous display frame was copied out and can be overwritten */
if (ctx->display_idx >= 0 &&
ctx->display_idx < ctx->num_internal_frames) {
ctx->frm_dis_flg &= ~(1 << ctx->display_idx);
@@ -2057,8 +2091,10 @@ static void coda_finish_decode(struct coda_ctx *ctx)
}
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload);
- coda_m2m_buf_done(ctx, dst_buf, ctx->frame_errors[ctx->display_idx] ?
- VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ if (ctx->frame_errors[ctx->display_idx] || err_vdoa)
+ coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR);
+ else
+ coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE);
v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
"job finished: decoding frame (%d) (%s)\n",
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index b23fe0f0fb56..13ee6cba6847 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -41,6 +41,7 @@
#include <media/videobuf2-vmalloc.h>
#include "coda.h"
+#include "imx-vdoa.h"
#define CODA_NAME "coda"
@@ -66,6 +67,10 @@ static int disable_tiling;
module_param(disable_tiling, int, 0644);
MODULE_PARM_DESC(disable_tiling, "Disable tiled frame buffers");
+static int disable_vdoa;
+module_param(disable_vdoa, int, 0644);
+MODULE_PARM_DESC(disable_vdoa, "Disable Video Data Order Adapter tiled to raster-scan conversion");
+
void coda_write(struct coda_dev *dev, u32 data, u32 reg)
{
v4l2_dbg(2, coda_debug, &dev->v4l2_dev,
@@ -325,6 +330,31 @@ const char *coda_product_name(int product)
}
}
+static struct vdoa_data *coda_get_vdoa_data(void)
+{
+ struct device_node *vdoa_node;
+ struct platform_device *vdoa_pdev;
+ struct vdoa_data *vdoa_data = NULL;
+
+ vdoa_node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-vdoa");
+ if (!vdoa_node)
+ return NULL;
+
+ vdoa_pdev = of_find_device_by_node(vdoa_node);
+ if (!vdoa_pdev)
+ goto out;
+
+ vdoa_data = platform_get_drvdata(vdoa_pdev);
+ if (!vdoa_data)
+ vdoa_data = ERR_PTR(-EPROBE_DEFER);
+
+out:
+ if (vdoa_node)
+ of_node_put(vdoa_node);
+
+ return vdoa_data;
+}
+
/*
* V4L2 ioctl() operations.
*/
@@ -417,6 +447,33 @@ static int coda_try_pixelformat(struct coda_ctx *ctx, struct v4l2_format *f)
return 0;
}
+static int coda_try_fmt_vdoa(struct coda_ctx *ctx, struct v4l2_format *f,
+ bool *use_vdoa)
+{
+ int err;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (!use_vdoa)
+ return -EINVAL;
+
+ if (!ctx->vdoa) {
+ *use_vdoa = false;
+ return 0;
+ }
+
+ err = vdoa_context_configure(NULL, f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.pixelformat);
+ if (err) {
+ *use_vdoa = false;
+ return 0;
+ }
+
+ *use_vdoa = true;
+ return 0;
+}
+
static unsigned int coda_estimate_sizeimage(struct coda_ctx *ctx, u32 sizeimage,
u32 width, u32 height)
{
@@ -495,6 +552,7 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv,
const struct coda_codec *codec;
struct vb2_queue *src_vq;
int ret;
+ bool use_vdoa;
ret = coda_try_pixelformat(ctx, f);
if (ret < 0)
@@ -531,6 +589,10 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
f->fmt.pix.height * 3 / 2;
+
+ ret = coda_try_fmt_vdoa(ctx, f, &use_vdoa);
+ if (ret < 0)
+ return ret;
}
return 0;
@@ -601,11 +663,9 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f,
switch (f->fmt.pix.pixelformat) {
case V4L2_PIX_FMT_NV12:
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP;
- if (!disable_tiling)
- break;
- }
+ ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP;
+ if (!disable_tiling)
+ break;
/* else fall through */
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
@@ -615,6 +675,15 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f,
break;
}
+ if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP &&
+ !coda_try_fmt_vdoa(ctx, f, &ctx->use_vdoa) &&
+ ctx->use_vdoa)
+ vdoa_context_configure(ctx->vdoa, f->fmt.pix.width,
+ f->fmt.pix.height,
+ f->fmt.pix.pixelformat);
+ else
+ ctx->use_vdoa = false;
+
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"Setting format for type %d, wxh: %dx%d, fmt: %4.4s %c\n",
f->type, q_data->width, q_data->height,
@@ -1041,6 +1110,16 @@ static int coda_job_ready(void *m2m_priv)
bool stream_end = ctx->bit_stream_param &
CODA_BIT_STREAM_END_FLAG;
int num_metas = ctx->num_metas;
+ unsigned int count;
+
+ count = hweight32(ctx->frm_dis_flg);
+ if (ctx->use_vdoa && count >= (ctx->num_internal_frames - 1)) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "%d: not ready: all internal buffers in use: %d/%d (0x%x)",
+ ctx->idx, count, ctx->num_internal_frames,
+ ctx->frm_dis_flg);
+ return 0;
+ }
if (ctx->hold && !src_bufs) {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
@@ -1731,6 +1810,13 @@ static int coda_open(struct file *file)
default:
ctx->reg_idx = idx;
}
+ if (ctx->dev->vdoa && !disable_vdoa) {
+ ctx->vdoa = vdoa_context_create(dev->vdoa);
+ if (!ctx->vdoa)
+ v4l2_warn(&dev->v4l2_dev,
+ "Failed to create vdoa context: not using vdoa");
+ }
+ ctx->use_vdoa = false;
/* Power up and upload firmware if necessary */
ret = pm_runtime_get_sync(&dev->plat_dev->dev);
@@ -1812,6 +1898,9 @@ static int coda_release(struct file *file)
/* If this instance is running, call .job_abort and wait for it to end */
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ if (ctx->vdoa)
+ vdoa_context_destroy(ctx->vdoa);
+
/* In case the instance was not running, we still need to call SEQ_END */
if (ctx->ops->seq_end_work) {
queue_work(dev->workqueue, &ctx->seq_end_work);
@@ -2258,6 +2347,11 @@ static int coda_probe(struct platform_device *pdev)
}
dev->iram_pool = pool;
+ /* Get vdoa_data if supported by the platform */
+ dev->vdoa = coda_get_vdoa_data();
+ if (PTR_ERR(dev->vdoa) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret)
return ret;
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
index 53f96661683c..7ed79eb774e7 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/coda/coda.h
@@ -75,6 +75,7 @@ struct coda_dev {
struct platform_device *plat_dev;
const struct coda_devtype *devtype;
int firmware;
+ struct vdoa_data *vdoa;
void __iomem *regs_base;
struct clk *clk_per;
@@ -236,6 +237,8 @@ struct coda_ctx {
int display_idx;
struct dentry *debugfs_entry;
bool use_bit;
+ bool use_vdoa;
+ struct vdoa_ctx *vdoa;
};
extern int coda_debug;
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 7/7] [media] coda: support YUYV output if VDOA is used
From: Michael Tretter @ 2017-01-20 14:00 UTC (permalink / raw)
To: linux-media
Cc: Philipp Zabel, devicetree, Hans Verkuil, Mauro Carvalho Chehab,
kernel, Michael Tretter
In-Reply-To: <20170120140025.3338-1-m.tretter@pengutronix.de>
The VDOA is able to transform the NV12 custom macroblock tiled format of
the CODA to YUYV format. If and only if the VDOA is available, the
driver can also provide YUYV support.
While the driver is configured to produce YUYV output, the CODA must be
configured to produce NV12 macroblock tiled frames and the VDOA must
transform the intermediate result into the final YUYV output.
Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
---
drivers/media/platform/coda/coda-bit.c | 7 +++++--
drivers/media/platform/coda/coda-common.c | 30 ++++++++++++++++++++++++++++++
2 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index f608de4c52ac..466a44e4549e 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -759,7 +759,7 @@ static void coda9_set_frame_cache(struct coda_ctx *ctx, u32 fourcc)
cache_config = 1 << CODA9_CACHE_PAGEMERGE_OFFSET;
}
coda_write(ctx->dev, cache_size, CODA9_CMD_SET_FRAME_CACHE_SIZE);
- if (fourcc == V4L2_PIX_FMT_NV12) {
+ if (fourcc == V4L2_PIX_FMT_NV12 || fourcc == V4L2_PIX_FMT_YUYV) {
cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET |
16 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET |
0 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET;
@@ -1537,7 +1537,7 @@ static int __coda_start_decoding(struct coda_ctx *ctx)
ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) |
CODA9_FRAME_TILED2LINEAR);
- if (dst_fourcc == V4L2_PIX_FMT_NV12)
+ if (dst_fourcc == V4L2_PIX_FMT_NV12 || dst_fourcc == V4L2_PIX_FMT_YUYV)
ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE;
if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP)
ctx->frame_mem_ctrl |= (0x3 << 9) |
@@ -2079,6 +2079,9 @@ static void coda_finish_decode(struct coda_ctx *ctx)
trace_coda_dec_rot_done(ctx, dst_buf, meta);
switch (q_data_dst->fourcc) {
+ case V4L2_PIX_FMT_YUYV:
+ payload = width * height * 2;
+ break;
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_NV12:
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index 13ee6cba6847..a918b294adef 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -95,6 +95,8 @@ void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data,
u32 base_cb, base_cr;
switch (q_data->fourcc) {
+ case V4L2_PIX_FMT_YUYV:
+ /* Fallthrough: IN -H264-> CODA -NV12 MB-> VDOA -YUYV-> OUT */
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_YUV420:
default:
@@ -201,6 +203,11 @@ static const struct coda_video_device coda_bit_decoder = {
V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_YUV420,
V4L2_PIX_FMT_YVU420,
+ /*
+ * If V4L2_PIX_FMT_YUYV should be default,
+ * set_default_params() must be adjusted.
+ */
+ V4L2_PIX_FMT_YUYV,
},
};
@@ -246,6 +253,7 @@ static u32 coda_format_normalize_yuv(u32 fourcc)
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_YUYV:
return V4L2_PIX_FMT_YUV420;
default:
return fourcc;
@@ -434,6 +442,11 @@ static int coda_try_pixelformat(struct coda_ctx *ctx, struct v4l2_format *f)
return -EINVAL;
for (i = 0; i < CODA_MAX_FORMATS; i++) {
+ /* Skip YUYV if the vdoa is not available */
+ if (!ctx->vdoa && f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ formats[i] == V4L2_PIX_FMT_YUYV)
+ continue;
+
if (formats[i] == f->fmt.pix.pixelformat) {
f->fmt.pix.pixelformat = formats[i];
return 0;
@@ -520,6 +533,11 @@ static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec,
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
f->fmt.pix.height * 3 / 2;
break;
+ case V4L2_PIX_FMT_YUYV:
+ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+ f->fmt.pix.height;
+ break;
case V4L2_PIX_FMT_YUV422P:
f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
@@ -593,6 +611,15 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv,
ret = coda_try_fmt_vdoa(ctx, f, &use_vdoa);
if (ret < 0)
return ret;
+
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {
+ if (!use_vdoa)
+ return -EINVAL;
+
+ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16) * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+ f->fmt.pix.height;
+ }
}
return 0;
@@ -662,6 +689,9 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f,
}
switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP;
+ break;
case V4L2_PIX_FMT_NV12:
ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP;
if (!disable_tiling)
--
2.11.0
^ permalink raw reply related
* Re: [PATCH v5 00/14] ARM: da850-lcdk: add SATA support
From: Bartosz Golaszewski @ 2017-01-20 14:02 UTC (permalink / raw)
To: Sekhar Nori
Cc: Mark Rutland, linux-devicetree, David Lechner, Kevin Hilman,
Michael Turquette, Russell King, LKML, linux-ide, Rob Herring,
Patrick Titiano, Tejun Heo, arm-soc
In-Reply-To: <86bcbb3c-87e4-3508-f4a6-26529f39d374@ti.com>
2017-01-20 14:56 GMT+01:00 Sekhar Nori <nsekhar@ti.com>:
> Hi Tejun,
>
> On Friday 20 January 2017 06:58 PM, Tejun Heo wrote:
>> On Fri, Jan 20, 2017 at 12:21:51PM +0100, Bartosz Golaszewski wrote:
>>> This series contains all the changes necessary to make SATA work on
>>> the da850-lcdk board.
>>>
>>> The first patch adds DT bindings for the ahci-da850 driver.
>>>
>>> The second enables relevant modules in davinci_all_defconfig.
>>>
>>> Patches 03/14-06/14 modify the way the clocks are handled regarding
>>> SATA on the da850 platform. We modify the ahci driver to retrieve
>>> the clock via con_id and model the external SATA oscillator as
>>> a real clock.
>>>
>>> Patches 07/14-11/14 extend the ahci-da850 driver. Add DT support,
>>> implement workarounds necessary to make SATA work on the da850-lcdk
>>> board and un-hardcode the external clock multiplier.
>>
>> Please feel free to add
>>
>> Acked-by: Tejun Heo <tj@kernel.org>
>>
>> to the all libata patches. Please let me know how the patches should
>> be routed once other parts are settled.
>
> I believe you can queue the libata patches independently (patches 1, 4,
> 7, 8, 9, 10, 11). It looks like they have been written such that driver
> continues to work with existing platform code (Bartosz, please disagree
> if I am wrong). 1/14 still needs the ack from DT maintainers.
>
Patch 11/14 depends on 06/14. Other than that I think it should work
independently.
Thanks,
Bartosz
^ permalink raw reply
* Re: [PATCH v3 13/24] platform: add video-multiplexer subdevice driver
From: Hans Verkuil @ 2017-01-20 14:03 UTC (permalink / raw)
To: Steve Longerbeam, robh+dt, mark.rutland, shawnguo, kernel,
fabio.estevam, linux, mchehab, nick, markus.heiser, p.zabel,
laurent.pinchart+renesas, bparrot, geert, arnd, sudipm.mukherjee,
minghsiu.tsai, tiffany.lin, jean-christophe.trotin, horms+renesas,
niklas.soderlund+renesas, robert.jarzmik, songjun.wu,
andrew-ct.chen, gregkh
Cc: devel, devicetree, Steve Longerbeam, Sascha Hauer, linux-kernel,
linux-arm-kernel, linux-media
In-Reply-To: <1483755102-24785-14-git-send-email-steve_longerbeam@mentor.com>
On 01/07/2017 03:11 AM, Steve Longerbeam wrote:
> From: Philipp Zabel <p.zabel@pengutronix.de>
>
> This driver can handle SoC internal and external video bus multiplexers,
> controlled either by register bit fields or by a GPIO. The subdevice
> passes through frame interval and mbus configuration of the active input
> to the output side.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
>
> --
>
> - fixed a cut&paste error in vidsw_remove(): v4l2_async_register_subdev()
> should be unregister.
>
> - added media_entity_cleanup() and v4l2_device_unregister_subdev()
> to vidsw_remove().
>
> - there was a line left over from a previous iteration that negated
> the new way of determining the pad count just before it which
> has been removed (num_pads = of_get_child_count(np)).
>
> - Philipp Zabel has developed a set of patches that allow adding
> to the subdev async notifier waiting list using a chaining method
> from the async registered callbacks (v4l2_of_subdev_registered()
> and the prep patches for that). For now, I've removed the use of
> v4l2_of_subdev_registered() for the vidmux driver's registered
> callback. This doesn't affect the functionality of this driver,
> but allows for it to be merged now, before adding the chaining
> support.
>
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
> .../bindings/media/video-multiplexer.txt | 59 +++
> drivers/media/platform/Kconfig | 8 +
> drivers/media/platform/Makefile | 2 +
> drivers/media/platform/video-multiplexer.c | 472 +++++++++++++++++++++
> 4 files changed, 541 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/video-multiplexer.txt
> create mode 100644 drivers/media/platform/video-multiplexer.c
>
> diff --git a/Documentation/devicetree/bindings/media/video-multiplexer.txt b/Documentation/devicetree/bindings/media/video-multiplexer.txt
> new file mode 100644
> index 0000000..9d133d9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/video-multiplexer.txt
> @@ -0,0 +1,59 @@
> +Video Multiplexer
> +=================
> +
> +Video multiplexers allow to select between multiple input ports. Video received
> +on the active input port is passed through to the output port. Muxes described
> +by this binding may be controlled by a syscon register bitfield or by a GPIO.
> +
> +Required properties:
> +- compatible : should be "video-multiplexer"
> +- reg: should be register base of the register containing the control bitfield
> +- bit-mask: bitmask of the control bitfield in the control register
> +- bit-shift: bit offset of the control bitfield in the control register
> +- gpios: alternatively to reg, bit-mask, and bit-shift, a single GPIO phandle
> + may be given to switch between two inputs
> +- #address-cells: should be <1>
> +- #size-cells: should be <0>
> +- port@*: at least three port nodes containing endpoints connecting to the
> + source and sink devices according to of_graph bindings. The last port is
> + the output port, all others are inputs.
> +
> +Example:
> +
> +syscon {
> + compatible = "syscon", "simple-mfd";
> +
> + mux {
> + compatible = "video-multiplexer";
> + /* Single bit (1 << 19) in syscon register 0x04: */
> + reg = <0x04>;
> + bit-mask = <1>;
> + bit-shift = <19>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + reg = <0>;
> +
> + mux_in0: endpoint {
> + remote-endpoint = <&video_source0_out>;
> + };
> + };
> +
> + port@1 {
> + reg = <1>;
> +
> + mux_in1: endpoint {
> + remote-endpoint = <&video_source1_out>;
> + };
> + };
> +
> + port@2 {
> + reg = <2>;
> +
> + mux_out: endpoint {
> + remote-endpoint = <&capture_interface_in>;
> + };
> + };
> + };
> +};
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index d944421..65614b5 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -74,6 +74,14 @@ config VIDEO_M32R_AR_M64278
> To compile this driver as a module, choose M here: the
> module will be called arv.
>
> +config VIDEO_MULTIPLEXER
> + tristate "Video Multiplexer"
> + depends on VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
> + help
> + This driver provides support for SoC internal N:1 video bus
> + multiplexers controlled by register bitfields as well as external
> + 2:1 video multiplexers controlled by a single GPIO.
> +
> config VIDEO_OMAP3
> tristate "OMAP 3 Camera support"
> depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 5b3cb27..7cf0ee5 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -27,6 +27,8 @@ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o
>
> obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
>
> +obj-$(CONFIG_VIDEO_MULTIPLEXER) += video-multiplexer.o
> +
> obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/
> obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/
> obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
> diff --git a/drivers/media/platform/video-multiplexer.c b/drivers/media/platform/video-multiplexer.c
> new file mode 100644
> index 0000000..48980c4
> --- /dev/null
> +++ b/drivers/media/platform/video-multiplexer.c
> @@ -0,0 +1,472 @@
> +/*
> + * video stream multiplexer controlled via gpio or syscon
> + *
> + * Copyright (C) 2013 Pengutronix, Sascha Hauer <kernel@pengutronix.de>
> + * Copyright (C) 2016 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
> + *
> + * 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; either version 2
> + * of the License, or (at your option) any later version.
> + * 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 <linux/err.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-of.h>
> +
> +struct vidsw {
> + struct v4l2_subdev subdev;
> + unsigned int num_pads;
> + struct media_pad *pads;
> + struct v4l2_mbus_framefmt *format_mbus;
> + struct v4l2_fract timeperframe;
> + struct v4l2_of_endpoint *endpoint;
> + struct regmap_field *field;
> + struct gpio_desc *gpio;
> + int active;
> +};
> +
> +static inline struct vidsw *v4l2_subdev_to_vidsw(struct v4l2_subdev *sd)
> +{
> + return container_of(sd, struct vidsw, subdev);
> +}
> +
> +static void vidsw_set_active(struct vidsw *vidsw, int active)
> +{
> + vidsw->active = active;
> + if (active < 0)
> + return;
> +
> + dev_dbg(vidsw->subdev.dev, "setting %d active\n", active);
> +
> + if (vidsw->field)
> + regmap_field_write(vidsw->field, active);
> + else if (vidsw->gpio)
> + gpiod_set_value(vidsw->gpio, active);
> +}
> +
> +static int vidsw_link_setup(struct media_entity *entity,
> + const struct media_pad *local,
> + const struct media_pad *remote, u32 flags)
> +{
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> +
> + /* We have no limitations on enabling or disabling our output link */
> + if (local->index == vidsw->num_pads - 1)
> + return 0;
> +
> + dev_dbg(sd->dev, "link setup %s -> %s", remote->entity->name,
> + local->entity->name);
> +
> + if (!(flags & MEDIA_LNK_FL_ENABLED)) {
> + if (local->index == vidsw->active) {
> + dev_dbg(sd->dev, "going inactive\n");
> + vidsw->active = -1;
> + }
> + return 0;
> + }
> +
> + if (vidsw->active >= 0) {
> + struct media_pad *pad;
> +
> + if (vidsw->active == local->index)
> + return 0;
> +
> + pad = media_entity_remote_pad(&vidsw->pads[vidsw->active]);
> + if (pad) {
> + struct media_link *link;
> + int ret;
> +
> + link = media_entity_find_link(pad,
> + &vidsw->pads[vidsw->active]);
> + if (link) {
> + ret = __media_entity_setup_link(link, 0);
> + if (ret)
> + return ret;
> + }
> + }
> + }
> +
> + vidsw_set_active(vidsw, local->index);
> +
> + return 0;
> +}
> +
> +static struct media_entity_operations vidsw_ops = {
> + .link_setup = vidsw_link_setup,
> +};
> +
> +static bool vidsw_endpoint_disabled(struct device_node *ep)
> +{
> + struct device_node *rpp;
> +
> + if (!of_device_is_available(ep))
> + return true;
> +
> + rpp = of_graph_get_remote_port_parent(ep);
> + if (!rpp)
> + return true;
> +
> + return !of_device_is_available(rpp);
> +}
> +
> +static int vidsw_async_init(struct vidsw *vidsw, struct device_node *node)
> +{
> + struct device_node *ep;
> + u32 portno;
> + int numports;
> + int ret;
> + int i;
> + bool active_link = false;
> +
> + numports = vidsw->num_pads;
> +
> + for (i = 0; i < numports - 1; i++)
> + vidsw->pads[i].flags = MEDIA_PAD_FL_SINK;
> + vidsw->pads[numports - 1].flags = MEDIA_PAD_FL_SOURCE;
> +
> + vidsw->subdev.entity.function = MEDIA_ENT_F_MUX;
> + ret = media_entity_pads_init(&vidsw->subdev.entity, numports,
> + vidsw->pads);
> + if (ret < 0)
> + return ret;
> +
> + vidsw->subdev.entity.ops = &vidsw_ops;
> +
> + for_each_endpoint_of_node(node, ep) {
> + struct v4l2_of_endpoint endpoint;
> +
> + v4l2_of_parse_endpoint(ep, &endpoint);
> +
> + portno = endpoint.base.port;
> + if (portno >= numports - 1)
> + continue;
> +
> + if (vidsw_endpoint_disabled(ep)) {
> + dev_dbg(vidsw->subdev.dev, "port %d disabled\n", portno);
> + continue;
> + }
> +
> + vidsw->endpoint[portno] = endpoint;
> +
> + if (portno == vidsw->active)
> + active_link = true;
> + }
> +
> + for (portno = 0; portno < numports - 1; portno++) {
> + if (!vidsw->endpoint[portno].base.local_node)
> + continue;
> +
> + /* If the active input is not connected, use another */
> + if (!active_link) {
> + vidsw_set_active(vidsw, portno);
> + active_link = true;
> + }
> + }
> +
> + return v4l2_async_register_subdev(&vidsw->subdev);
> +}
> +
> +int vidsw_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg)
> +{
> + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> + struct media_pad *pad;
> + int ret;
> +
> + if (vidsw->active == -1) {
> + dev_err(sd->dev, "no configuration for inactive mux\n");
> + return -EINVAL;
> + }
> +
> + /*
> + * Retrieve media bus configuration from the entity connected to the
> + * active input
> + */
> + pad = media_entity_remote_pad(&vidsw->pads[vidsw->active]);
> + if (pad) {
> + sd = media_entity_to_v4l2_subdev(pad->entity);
> + ret = v4l2_subdev_call(sd, video, g_mbus_config, cfg);
> + if (ret == -ENOIOCTLCMD)
> + pad = NULL;
> + else if (ret < 0) {
> + dev_err(sd->dev, "failed to get source configuration\n");
> + return ret;
> + }
> + }
> + if (!pad) {
> + /* Mirror the input side on the output side */
> + cfg->type = vidsw->endpoint[vidsw->active].bus_type;
> + if (cfg->type == V4L2_MBUS_PARALLEL ||
> + cfg->type == V4L2_MBUS_BT656)
> + cfg->flags = vidsw->endpoint[vidsw->active].bus.parallel.flags;
> + }
> +
> + return 0;
> +}
I am not certain this op is needed at all. In the current kernel this op is only
used by soc_camera, pxa_camera and omap3isp (somewhat dubious). Normally this
information should come from the device tree and there should be no need for this op.
My (tentative) long-term plan was to get rid of this op.
If you don't need it, then I recommend it is removed.
> +
> +static int vidsw_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> + struct v4l2_subdev *upstream_sd;
> + struct media_pad *pad;
> +
> + if (vidsw->active == -1) {
> + dev_err(sd->dev, "Can not start streaming on inactive mux\n");
> + return -EINVAL;
> + }
> +
> + pad = media_entity_remote_pad(&sd->entity.pads[vidsw->active]);
> + if (!pad) {
> + dev_err(sd->dev, "Failed to find remote source pad\n");
> + return -ENOLINK;
> + }
> +
> + if (!is_media_entity_v4l2_subdev(pad->entity)) {
> + dev_err(sd->dev, "Upstream entity is not a v4l2 subdev\n");
> + return -ENODEV;
> + }
> +
> + upstream_sd = media_entity_to_v4l2_subdev(pad->entity);
> +
> + return v4l2_subdev_call(upstream_sd, video, s_stream, enable);
> +}
> +
> +static int vidsw_g_frame_interval(struct v4l2_subdev *sd,
> + struct v4l2_subdev_frame_interval *fi)
> +{
> + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> +
> + fi->interval = vidsw->timeperframe;
> +
> + return 0;
> +}
> +
> +static int vidsw_s_frame_interval(struct v4l2_subdev *sd,
> + struct v4l2_subdev_frame_interval *fi)
> +{
> + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> +
> + vidsw->timeperframe = fi->interval;
> +
> + return 0;
> +}
> +
> +static const struct v4l2_subdev_video_ops vidsw_subdev_video_ops = {
> + .g_mbus_config = vidsw_g_mbus_config,
> + .s_stream = vidsw_s_stream,
> + .g_frame_interval = vidsw_g_frame_interval,
> + .s_frame_interval = vidsw_s_frame_interval,
> +};
> +
> +static struct v4l2_mbus_framefmt *
> +__vidsw_get_pad_format(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + unsigned int pad, u32 which)
> +{
> + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> +
> + switch (which) {
> + case V4L2_SUBDEV_FORMAT_TRY:
> + return v4l2_subdev_get_try_format(sd, cfg, pad);
> + case V4L2_SUBDEV_FORMAT_ACTIVE:
> + return &vidsw->format_mbus[pad];
> + default:
> + return NULL;
> + }
> +}
> +
> +static int vidsw_get_format(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *sdformat)
> +{
> + sdformat->format = *__vidsw_get_pad_format(sd, cfg, sdformat->pad,
> + sdformat->which);
> + return 0;
> +}
> +
> +static int vidsw_set_format(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *sdformat)
> +{
> + struct vidsw *vidsw = v4l2_subdev_to_vidsw(sd);
> + struct v4l2_mbus_framefmt *mbusformat;
> +
> + if (sdformat->pad >= vidsw->num_pads)
> + return -EINVAL;
> +
> + mbusformat = __vidsw_get_pad_format(sd, cfg, sdformat->pad,
> + sdformat->which);
> + if (!mbusformat)
> + return -EINVAL;
> +
> + /* Output pad mirrors active input pad, no limitations on input pads */
> + if (sdformat->pad == (vidsw->num_pads - 1) && vidsw->active >= 0)
> + sdformat->format = vidsw->format_mbus[vidsw->active];
> +
> + *mbusformat = sdformat->format;
> +
> + return 0;
> +}
> +
> +static struct v4l2_subdev_pad_ops vidsw_pad_ops = {
> + .get_fmt = vidsw_get_format,
> + .set_fmt = vidsw_set_format,
> +};
> +
> +static struct v4l2_subdev_ops vidsw_subdev_ops = {
> + .pad = &vidsw_pad_ops,
> + .video = &vidsw_subdev_video_ops,
> +};
> +
> +static int of_get_reg_field(struct device_node *node, struct reg_field *field)
> +{
> + u32 bit_mask;
> + int ret;
> +
> + ret = of_property_read_u32(node, "reg", &field->reg);
> + if (ret < 0)
> + return ret;
> +
> + ret = of_property_read_u32(node, "bit-mask", &bit_mask);
> + if (ret < 0)
> + return ret;
> +
> + ret = of_property_read_u32(node, "bit-shift", &field->lsb);
> + if (ret < 0)
> + return ret;
> +
> + field->msb = field->lsb + fls(bit_mask) - 1;
> +
> + return 0;
> +}
> +
> +static int vidsw_probe(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct of_endpoint endpoint;
> + struct device_node *ep;
> + struct reg_field field;
> + struct vidsw *vidsw;
> + struct regmap *map;
> + unsigned int num_pads;
> + int ret;
> +
> + vidsw = devm_kzalloc(&pdev->dev, sizeof(*vidsw), GFP_KERNEL);
> + if (!vidsw)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, vidsw);
> +
> + v4l2_subdev_init(&vidsw->subdev, &vidsw_subdev_ops);
> + snprintf(vidsw->subdev.name, sizeof(vidsw->subdev.name), "%s",
> + np->name);
> + vidsw->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + vidsw->subdev.dev = &pdev->dev;
> +
> + /*
> + * The largest numbered port is the output port. It determines
> + * total number of pads
> + */
> + num_pads = 0;
> + for_each_endpoint_of_node(np, ep) {
> + of_graph_parse_endpoint(ep, &endpoint);
> + num_pads = max(num_pads, endpoint.port + 1);
> + }
> +
> + if (num_pads < 2) {
> + dev_err(&pdev->dev, "Not enough ports %d\n", num_pads);
> + return -EINVAL;
> + }
> +
> + ret = of_get_reg_field(np, &field);
> + if (ret == 0) {
> + map = syscon_node_to_regmap(np->parent);
> + if (!map) {
> + dev_err(&pdev->dev, "Failed to get syscon register map\n");
> + return PTR_ERR(map);
> + }
> +
> + vidsw->field = devm_regmap_field_alloc(&pdev->dev, map, field);
> + if (IS_ERR(vidsw->field)) {
> + dev_err(&pdev->dev, "Failed to allocate regmap field\n");
> + return PTR_ERR(vidsw->field);
> + }
> +
> + regmap_field_read(vidsw->field, &vidsw->active);
> + } else {
> + if (num_pads > 3) {
> + dev_err(&pdev->dev, "Too many ports %d\n", num_pads);
> + return -EINVAL;
> + }
> +
> + vidsw->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
> + if (IS_ERR(vidsw->gpio)) {
> + dev_warn(&pdev->dev,
> + "could not request control gpio: %d\n", ret);
> + vidsw->gpio = NULL;
> + }
> +
> + vidsw->active = gpiod_get_value(vidsw->gpio) ? 1 : 0;
> + }
> +
> + vidsw->num_pads = num_pads;
> + vidsw->pads = devm_kzalloc(&pdev->dev, sizeof(*vidsw->pads) * num_pads,
> + GFP_KERNEL);
> + vidsw->format_mbus = devm_kzalloc(&pdev->dev,
> + sizeof(*vidsw->format_mbus) * num_pads, GFP_KERNEL);
> + vidsw->endpoint = devm_kzalloc(&pdev->dev,
> + sizeof(*vidsw->endpoint) * (num_pads - 1), GFP_KERNEL);
> +
> + ret = vidsw_async_init(vidsw, np);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int vidsw_remove(struct platform_device *pdev)
> +{
> + struct vidsw *vidsw = platform_get_drvdata(pdev);
> + struct v4l2_subdev *sd = &vidsw->subdev;
> +
> + v4l2_async_unregister_subdev(sd);
> + media_entity_cleanup(&sd->entity);
> + v4l2_device_unregister_subdev(sd);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id vidsw_dt_ids[] = {
> + { .compatible = "video-multiplexer", },
> + { /* sentinel */ }
> +};
> +
> +static struct platform_driver vidsw_driver = {
> + .probe = vidsw_probe,
> + .remove = vidsw_remove,
> + .driver = {
> + .of_match_table = vidsw_dt_ids,
> + .name = "video-multiplexer",
> + },
> +};
> +
> +module_platform_driver(vidsw_driver);
> +
> +MODULE_DESCRIPTION("video stream multiplexer");
> +MODULE_AUTHOR("Sascha Hauer, Pengutronix");
> +MODULE_AUTHOR("Philipp Zabel, Pengutronix");
> +MODULE_LICENSE("GPL");
>
Regards,
Hans
^ permalink raw reply
* [PATCH 0/8] Renesas CPG/MSSR Reset Control Support
From: Geert Uytterhoeven @ 2017-01-20 14:08 UTC (permalink / raw)
To: Philipp Zabel, Simon Horman, Magnus Damm, Michael Turquette,
Stephen Boyd, Rob Herring, Mark Rutland
Cc: linux-clk, devicetree, linux-renesas-soc, linux-kernel,
linux-arm-kernel, Geert Uytterhoeven
Hi all,
This patch series adds reset control support to the Renesas Clock Pulse
Generator / Module Standby and Software Reset module, on the R-Car H3
and M3-W, RZ/G1M, and RZ/G1E SoCs.
- Patch 1 amends the Renesas CPG/MSSR DT bindings for reset control,
- Patches 2-4 add reset control to the Renesas CPG/MSSR driver,
- Patches 5-8 add reset control properties to the R-Car H3 and M3-W,
RZ/G1M, and RZ/G1E DTSes.
Note that this patch series implements reset functionality only.
Actual reset policy is to be defined and implemented separately.
This is an optional feature, to be enabled explicitly using
CONFIG_RESET_CONTROLLER=y. When enabled, an on-SoC device can be reset
easily using device_reset(), or by using the reset_control_*() API when
more fine-grained control is desired.
Possible use cases are (not exhaustive):
- Reset a device before use, to make sure it's in a predefined state, and
doesn't depend on earlier configuration by e.g. the boot loader,
- Reset a device after detecting an anomaly,
- Reset a device to verify suspend/resume is handled correctly by the
driver in case the device would be part of a power domain on a
different/future SoC.
Dependencies and upstreaming:
- As patches 1-4 touch a driver under drivers/clk/renesas/, I think
it's best if they go in through a pull request to the clock
maintainers, like other Renesas clock driver changes,
- Patches 5-8 have no dependencies nor impact as long as
CONFIG_RESET_CONTROLLER=n.
However, if CONFIG_RESET_CONTROLLER=y and resets properties are
prsesent in DTS, the EHCI and OHCI drivers already deassert reset as
part of their initialization sequences, and put the devices back
into reset state in case initialization failed, or on unbind.
Hence it's best to apply the DTS patches after the driver support
has landed upstream. Else USB on R-Car H3 will fail to initialize
when booting a kernel with CONFIG_RESET_CONTROLLER=y.
I'm not aware of other relevant drivers already using reset control.
For your convenience, the driver and DTS changes (incl. dependencies) are
available in the topic/renesas-cpg-mssr-reset-driver-v1 resp.
topic/renesas-cpg-mssr-reset-dts-v1 branches of my topic/rcar-rst-v4 branch of
my renesas-drivers git repository at
git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git
This has been tested on the R-Car Gen3 Salvator-X (H3 and M3-W) and the
R-Car M2-W (using out-of-tree driver modifications) Koelsch development
boards, by inspecting device register contents before and after reset,
and by comparing them with their documented reset values.
Thanks for your comments!
Geert Uytterhoeven (8):
clk: renesas: cpg-mssr: Document reset control support
clk: renesas: cpg-mssr: Document suitability for RZ/G1
clk: renesas: cpg-mssr: Rename cpg_mssr_priv.mstp_lock
clk: renesas: cpg-mssr: Add support for reset control
arm64: dts: r8a7795: Add reset control properties
arm64: dts: r8a7796: Add reset control properties
ARM: dts: r8a7743: Add reset control properties
ARM: dts: r8a7745: Add reset control properties
.../devicetree/bindings/clock/renesas,cpg-mssr.txt | 6 +
arch/arm/boot/dts/r8a7743.dtsi | 24 ++++
arch/arm/boot/dts/r8a7745.dtsi | 24 ++++
arch/arm64/boot/dts/renesas/r8a7795.dtsi | 103 ++++++++++++++++
arch/arm64/boot/dts/renesas/r8a7796.dtsi | 34 ++++++
drivers/clk/renesas/renesas-cpg-mssr.c | 134 ++++++++++++++++++++-
6 files changed, 319 insertions(+), 6 deletions(-)
--
1.9.1
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCH 1/8] clk: renesas: cpg-mssr: Document reset control support
From: Geert Uytterhoeven @ 2017-01-20 14:08 UTC (permalink / raw)
To: Philipp Zabel, Simon Horman, Magnus Damm, Michael Turquette,
Stephen Boyd, Rob Herring, Mark Rutland
Cc: linux-clk, devicetree, linux-renesas-soc, linux-kernel,
linux-arm-kernel, Geert Uytterhoeven
In-Reply-To: <1484921306-9967-1-git-send-email-geert+renesas@glider.be>
Document properties needed to use the Reset Control feature of the
Renesas Clock Pulse Generator / Module Standby and Software Reset
module.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt
index c469194129536332..f4f944d813081857 100644
--- a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt
+++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt
@@ -42,6 +42,10 @@ Required Properties:
Domain bindings in
Documentation/devicetree/bindings/power/power_domain.txt.
+ - #reset-cells: Must be 1
+ - The single reset specifier cell must be the module number, as defined
+ in the datasheet.
+
Examples
--------
@@ -55,6 +59,7 @@ Examples
clock-names = "extal", "extalr";
#clock-cells = <2>;
#power-domain-cells = <0>;
+ #reset-cells = <1>;
};
@@ -69,5 +74,6 @@ Examples
dmas = <&dmac1 0x13>, <&dmac1 0x12>;
dma-names = "tx", "rx";
power-domains = <&cpg>;
+ resets = <&cpg 310>;
status = "disabled";
};
--
1.9.1
^ permalink raw reply related
* [PATCH 2/8] clk: renesas: cpg-mssr: Document suitability for RZ/G1
From: Geert Uytterhoeven @ 2017-01-20 14:08 UTC (permalink / raw)
To: Philipp Zabel, Simon Horman, Magnus Damm, Michael Turquette,
Stephen Boyd, Rob Herring, Mark Rutland
Cc: linux-clk, devicetree, linux-renesas-soc, linux-kernel,
linux-arm-kernel, Geert Uytterhoeven
In-Reply-To: <1484921306-9967-1-git-send-email-geert+renesas@glider.be>
The Renesas CPG/MSSR driver is already in active use for RZ/G1 since
commits c0b2d75d2a4bf6a3 ("clk: renesas: cpg-mssr: Add R8A7743 support")
and 9127d54bb8947159 ("clk: renesas: cpg-mssr: Add R8A7745 support").
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
drivers/clk/renesas/renesas-cpg-mssr.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index 6947482d48a55094..eb8534e5ebf3007d 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -43,7 +43,7 @@
* Module Standby and Software Reset register offets.
*
* If the registers exist, these are valid for SH-Mobile, R-Mobile,
- * R-Car Gen 2, and R-Car Gen 3.
+ * R-Car Gen2, R-Car Gen3, and RZ/G1.
* These are NOT valid for R-Car Gen1 and RZ/A1!
*/
--
1.9.1
^ permalink raw reply related
* [PATCH 3/8] clk: renesas: cpg-mssr: Rename cpg_mssr_priv.mstp_lock
From: Geert Uytterhoeven @ 2017-01-20 14:08 UTC (permalink / raw)
To: Philipp Zabel, Simon Horman, Magnus Damm, Michael Turquette,
Stephen Boyd, Rob Herring, Mark Rutland
Cc: linux-clk, devicetree, linux-renesas-soc, linux-kernel,
linux-arm-kernel, Geert Uytterhoeven
In-Reply-To: <1484921306-9967-1-git-send-email-geert+renesas@glider.be>
The spinlock is used to protect Read-Modify-Write register accesses,
which won't be limited to SMSTPCR register accesses.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
drivers/clk/renesas/renesas-cpg-mssr.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index eb8534e5ebf3007d..f1161a585c57e433 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -98,7 +98,7 @@
*
* @dev: CPG/MSSR device
* @base: CPG/MSSR register block base address
- * @mstp_lock: protects writes to SMSTPCR
+ * @rmw_lock: protects RMW register accesses
* @clks: Array containing all Core and Module Clocks
* @num_core_clks: Number of Core Clocks in clks[]
* @num_mod_clks: Number of Module Clocks in clks[]
@@ -107,7 +107,7 @@
struct cpg_mssr_priv {
struct device *dev;
void __iomem *base;
- spinlock_t mstp_lock;
+ spinlock_t rmw_lock;
struct clk **clks;
unsigned int num_core_clks;
@@ -144,7 +144,7 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
dev_dbg(dev, "MSTP %u%02u/%pC %s\n", reg, bit, hw->clk,
enable ? "ON" : "OFF");
- spin_lock_irqsave(&priv->mstp_lock, flags);
+ spin_lock_irqsave(&priv->rmw_lock, flags);
value = readl(priv->base + SMSTPCR(reg));
if (enable)
@@ -153,7 +153,7 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
value |= bitmask;
writel(value, priv->base + SMSTPCR(reg));
- spin_unlock_irqrestore(&priv->mstp_lock, flags);
+ spin_unlock_irqrestore(&priv->rmw_lock, flags);
if (!enable)
return 0;
@@ -550,7 +550,7 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
return -ENOMEM;
priv->dev = dev;
- spin_lock_init(&priv->mstp_lock);
+ spin_lock_init(&priv->rmw_lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(dev, res);
--
1.9.1
^ permalink raw reply related
* [PATCH 4/8] clk: renesas: cpg-mssr: Add support for reset control
From: Geert Uytterhoeven @ 2017-01-20 14:08 UTC (permalink / raw)
To: Philipp Zabel, Simon Horman, Magnus Damm, Michael Turquette,
Stephen Boyd, Rob Herring, Mark Rutland
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Geert Uytterhoeven
In-Reply-To: <1484921306-9967-1-git-send-email-geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
Add optional support for the Reset Control feature of the Renesas Clock
Pulse Generator / Module Standby and Software Reset module on R-Car
Gen2, R-Car Gen3, and RZ/G1 SoCs.
This allows to reset SoC devices using the Reset Controller API.
Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
drivers/clk/renesas/renesas-cpg-mssr.c | 122 +++++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+)
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index f1161a585c57e433..ea4af714ac14603a 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -16,6 +16,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clk/renesas.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
@@ -25,6 +26,7 @@
#include <linux/platform_device.h>
#include <linux/pm_clock.h>
#include <linux/pm_domain.h>
+#include <linux/reset-controller.h>
#include <linux/slab.h>
#include <dt-bindings/clock/renesas-cpg-mssr.h>
@@ -96,6 +98,7 @@
/**
* Clock Pulse Generator / Module Standby and Software Reset Private Data
*
+ * @rcdev: Optional reset controller entity
* @dev: CPG/MSSR device
* @base: CPG/MSSR register block base address
* @rmw_lock: protects RMW register accesses
@@ -105,6 +108,9 @@
* @last_dt_core_clk: ID of the last Core Clock exported to DT
*/
struct cpg_mssr_priv {
+#ifdef CONFIG_RESET_CONTROLLER
+ struct reset_controller_dev rcdev;
+#endif
struct device *dev;
void __iomem *base;
spinlock_t rmw_lock;
@@ -494,6 +500,118 @@ static int __init cpg_mssr_add_clk_domain(struct device *dev,
return 0;
}
+#ifdef CONFIG_RESET_CONTROLLER
+
+#define rcdev_to_priv(x) container_of(x, struct cpg_mssr_priv, rcdev)
+
+static int cpg_mssr_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev);
+ unsigned int reg = id / 32;
+ unsigned int bit = id % 32;
+ u32 bitmask = BIT(bit);
+ unsigned long flags;
+ u32 value;
+
+ dev_dbg(priv->dev, "reset %u%02u\n", reg, bit);
+
+ /* Reset module */
+ spin_lock_irqsave(&priv->rmw_lock, flags);
+ value = readl(priv->base + SRCR(reg));
+ value |= bitmask;
+ writel(value, priv->base + SRCR(reg));
+ spin_unlock_irqrestore(&priv->rmw_lock, flags);
+
+ /* Wait for at least one cycle of the RCLK clock (@ ca. 32 kHz) */
+ udelay(35);
+
+ /* Release module from reset state */
+ writel(bitmask, priv->base + SRSTCLR(reg));
+
+ return 0;
+}
+
+static int cpg_mssr_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev);
+ unsigned int reg = id / 32;
+ unsigned int bit = id % 32;
+ unsigned long flags;
+ u32 value;
+
+ dev_dbg(priv->dev, "assert %u%02u\n", reg, bit);
+
+ spin_lock_irqsave(&priv->rmw_lock, flags);
+ value = readl(priv->base + SRCR(reg));
+ writel(value | BIT(bit), priv->base + SRCR(reg));
+ spin_unlock_irqrestore(&priv->rmw_lock, flags);
+ return 0;
+}
+
+static int cpg_mssr_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev);
+ unsigned int reg = id / 32;
+ unsigned int bit = id % 32;
+
+ dev_dbg(priv->dev, "deassert %u%02u\n", reg, bit);
+
+ writel(BIT(bit), priv->base + SRSTCLR(reg));
+ return 0;
+}
+
+static int cpg_mssr_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev);
+ unsigned int reg = id / 32;
+ unsigned int bit = id % 32;
+
+ return !!(readl(priv->base + SRCR(reg)) & BIT(bit));
+}
+
+static const struct reset_control_ops cpg_mssr_reset_ops = {
+ .reset = cpg_mssr_reset,
+ .assert = cpg_mssr_assert,
+ .deassert = cpg_mssr_deassert,
+ .status = cpg_mssr_status,
+};
+
+static int cpg_mssr_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev);
+ unsigned int unpacked = reset_spec->args[0];
+ unsigned int idx = MOD_CLK_PACK(unpacked);
+
+ if (unpacked % 100 > 31 || idx >= rcdev->nr_resets) {
+ dev_err(priv->dev, "Invalid reset index %u\n", unpacked);
+ return -EINVAL;
+ }
+
+ return idx;
+}
+
+static int cpg_mssr_reset_controller_register(struct cpg_mssr_priv *priv)
+{
+ priv->rcdev.ops = &cpg_mssr_reset_ops;
+ priv->rcdev.of_node = priv->dev->of_node;
+ priv->rcdev.of_reset_n_cells = 1;
+ priv->rcdev.of_xlate = cpg_mssr_reset_xlate;
+ priv->rcdev.nr_resets = priv->num_mod_clks;
+ return devm_reset_controller_register(priv->dev, &priv->rcdev);
+}
+
+#else /* !CONFIG_RESET_CONTROLLER */
+static inline int cpg_mssr_reset_controller_register(struct cpg_mssr_priv *priv)
+{
+ return 0;
+}
+#endif /* !CONFIG_RESET_CONTROLLER */
+
+
static const struct of_device_id cpg_mssr_match[] = {
#ifdef CONFIG_ARCH_R8A7743
{
@@ -591,6 +709,10 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
if (error)
return error;
+ error = cpg_mssr_reset_controller_register(priv);
+ if (error)
+ return error;
+
return 0;
}
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 5/8] arm64: dts: r8a7795: Add reset control properties
From: Geert Uytterhoeven @ 2017-01-20 14:08 UTC (permalink / raw)
To: Philipp Zabel, Simon Horman, Magnus Damm, Michael Turquette,
Stephen Boyd, Rob Herring, Mark Rutland
Cc: linux-clk, devicetree, linux-renesas-soc, linux-kernel,
linux-arm-kernel, Geert Uytterhoeven
In-Reply-To: <1484921306-9967-1-git-send-email-geert+renesas@glider.be>
Add properties to describe the reset topology for on-SoC devices:
- Add the "#reset-cells" property to the CPG/MSSR device node,
- Add resets and reset-names properties to the various device nodes.
This allows to reset SoC devices using the Reset Controller API.
Note that resets usually match the corresponding module clocks.
Exceptions are:
- The audio module has resets for the Serial Sound Interfaces only,
- The DU has only 2 resets, one per channel pair.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
arch/arm64/boot/dts/renesas/r8a7795.dtsi | 103 +++++++++++++++++++++++++++++++
1 file changed, 103 insertions(+)
diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
index 0627df2a0b810c86..cdb67944c34166bd 100644
--- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
@@ -169,6 +169,7 @@
clocks = <&cpg CPG_MOD 408>;
clock-names = "clk";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 408>;
};
wdt0: watchdog@e6020000 {
@@ -176,6 +177,7 @@
reg = <0 0xe6020000 0 0x0c>;
clocks = <&cpg CPG_MOD 402>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 402>;
status = "disabled";
};
@@ -191,6 +193,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 912>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 912>;
};
gpio1: gpio@e6051000 {
@@ -205,6 +208,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 911>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 911>;
};
gpio2: gpio@e6052000 {
@@ -219,6 +223,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 910>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 910>;
};
gpio3: gpio@e6053000 {
@@ -233,6 +238,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 909>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 909>;
};
gpio4: gpio@e6054000 {
@@ -247,6 +253,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 908>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 908>;
};
gpio5: gpio@e6055000 {
@@ -261,6 +268,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 907>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 907>;
};
gpio6: gpio@e6055400 {
@@ -275,6 +283,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 906>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 906>;
};
gpio7: gpio@e6055800 {
@@ -289,6 +298,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 905>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 905>;
};
pmu_a57 {
@@ -322,6 +332,7 @@
clock-names = "extal", "extalr";
#clock-cells = <2>;
#power-domain-cells = <0>;
+ #reset-cells = <1>;
};
rst: reset-controller@e6160000 {
@@ -369,6 +380,7 @@
clocks = <&cpg CPG_MOD 502>;
clock-names = "fck";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 502>;
#dma-cells = <1>;
dma-channels = <16>;
};
@@ -402,6 +414,7 @@
clocks = <&cpg CPG_MOD 501>;
clock-names = "fck";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 501>;
#dma-cells = <1>;
dma-channels = <16>;
};
@@ -424,6 +437,7 @@
GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 407>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 407>;
};
dmac0: dma-controller@e6700000 {
@@ -455,6 +469,7 @@
clocks = <&cpg CPG_MOD 219>;
clock-names = "fck";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 219>;
#dma-cells = <1>;
dma-channels = <16>;
};
@@ -488,6 +503,7 @@
clocks = <&cpg CPG_MOD 218>;
clock-names = "fck";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 218>;
#dma-cells = <1>;
dma-channels = <16>;
};
@@ -521,6 +537,7 @@
clocks = <&cpg CPG_MOD 217>;
clock-names = "fck";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 217>;
#dma-cells = <1>;
dma-channels = <16>;
};
@@ -563,6 +580,7 @@
"ch24";
clocks = <&cpg CPG_MOD 812>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 812>;
phy-mode = "rgmii-id";
#address-cells = <1>;
#size-cells = <0>;
@@ -580,6 +598,7 @@
assigned-clocks = <&cpg CPG_CORE R8A7795_CLK_CANFD>;
assigned-clock-rates = <40000000>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 916>;
status = "disabled";
};
@@ -595,6 +614,7 @@
assigned-clocks = <&cpg CPG_CORE R8A7795_CLK_CANFD>;
assigned-clock-rates = <40000000>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 915>;
status = "disabled";
};
@@ -611,6 +631,7 @@
assigned-clocks = <&cpg CPG_CORE R8A7795_CLK_CANFD>;
assigned-clock-rates = <40000000>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 914>;
status = "disabled";
channel0 {
@@ -635,6 +656,7 @@
dmas = <&dmac1 0x31>, <&dmac1 0x30>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 520>;
status = "disabled";
};
@@ -651,6 +673,7 @@
dmas = <&dmac1 0x33>, <&dmac1 0x32>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 519>;
status = "disabled";
};
@@ -667,6 +690,7 @@
dmas = <&dmac1 0x35>, <&dmac1 0x34>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 518>;
status = "disabled";
};
@@ -683,6 +707,7 @@
dmas = <&dmac0 0x37>, <&dmac0 0x36>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 517>;
status = "disabled";
};
@@ -699,6 +724,7 @@
dmas = <&dmac0 0x39>, <&dmac0 0x38>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 516>;
status = "disabled";
};
@@ -714,6 +740,7 @@
dmas = <&dmac1 0x51>, <&dmac1 0x50>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 207>;
status = "disabled";
};
@@ -729,6 +756,7 @@
dmas = <&dmac1 0x53>, <&dmac1 0x52>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 206>;
status = "disabled";
};
@@ -744,6 +772,7 @@
dmas = <&dmac1 0x13>, <&dmac1 0x12>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 310>;
status = "disabled";
};
@@ -759,6 +788,7 @@
dmas = <&dmac0 0x57>, <&dmac0 0x56>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 204>;
status = "disabled";
};
@@ -774,6 +804,7 @@
dmas = <&dmac0 0x59>, <&dmac0 0x58>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 203>;
status = "disabled";
};
@@ -789,6 +820,7 @@
dmas = <&dmac1 0x5b>, <&dmac1 0x5a>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 202>;
status = "disabled";
};
@@ -803,6 +835,7 @@
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
dmas = <&dmac1 0x91>, <&dmac1 0x90>;
dma-names = "tx", "rx";
+ resets = <&cpg 931>;
i2c-scl-internal-delay-ns = <110>;
status = "disabled";
};
@@ -818,6 +851,7 @@
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
dmas = <&dmac1 0x93>, <&dmac1 0x92>;
dma-names = "tx", "rx";
+ resets = <&cpg 930>;
i2c-scl-internal-delay-ns = <6>;
status = "disabled";
};
@@ -833,6 +867,7 @@
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
dmas = <&dmac1 0x95>, <&dmac1 0x94>;
dma-names = "tx", "rx";
+ resets = <&cpg 929>;
i2c-scl-internal-delay-ns = <6>;
status = "disabled";
};
@@ -848,6 +883,7 @@
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
dmas = <&dmac0 0x97>, <&dmac0 0x96>;
dma-names = "tx", "rx";
+ resets = <&cpg 928>;
i2c-scl-internal-delay-ns = <110>;
status = "disabled";
};
@@ -863,6 +899,7 @@
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
dmas = <&dmac0 0x99>, <&dmac0 0x98>;
dma-names = "tx", "rx";
+ resets = <&cpg 927>;
i2c-scl-internal-delay-ns = <110>;
status = "disabled";
};
@@ -878,6 +915,7 @@
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
dmas = <&dmac0 0x9b>, <&dmac0 0x9a>;
dma-names = "tx", "rx";
+ resets = <&cpg 919>;
i2c-scl-internal-delay-ns = <110>;
status = "disabled";
};
@@ -893,6 +931,7 @@
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
dmas = <&dmac0 0x9d>, <&dmac0 0x9c>;
dma-names = "tx", "rx";
+ resets = <&cpg 918>;
i2c-scl-internal-delay-ns = <6>;
status = "disabled";
};
@@ -902,6 +941,7 @@
reg = <0 0xe6e30000 0 0x8>;
clocks = <&cpg CPG_MOD 523>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 523>;
#pwm-cells = <2>;
status = "disabled";
};
@@ -911,6 +951,7 @@
reg = <0 0xe6e31000 0 0x8>;
clocks = <&cpg CPG_MOD 523>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 523>;
#pwm-cells = <2>;
status = "disabled";
};
@@ -920,6 +961,7 @@
reg = <0 0xe6e32000 0 0x8>;
clocks = <&cpg CPG_MOD 523>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 523>;
#pwm-cells = <2>;
status = "disabled";
};
@@ -929,6 +971,7 @@
reg = <0 0xe6e33000 0 0x8>;
clocks = <&cpg CPG_MOD 523>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 523>;
#pwm-cells = <2>;
status = "disabled";
};
@@ -938,6 +981,7 @@
reg = <0 0xe6e34000 0 0x8>;
clocks = <&cpg CPG_MOD 523>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 523>;
#pwm-cells = <2>;
status = "disabled";
};
@@ -947,6 +991,7 @@
reg = <0 0xe6e35000 0 0x8>;
clocks = <&cpg CPG_MOD 523>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 523>;
#pwm-cells = <2>;
status = "disabled";
};
@@ -956,6 +1001,7 @@
reg = <0 0xe6e36000 0 0x8>;
clocks = <&cpg CPG_MOD 523>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 523>;
#pwm-cells = <2>;
status = "disabled";
};
@@ -1010,6 +1056,16 @@
"dvc.0", "dvc.1",
"clk_a", "clk_b", "clk_c", "clk_i";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 1005>,
+ <&cpg 1006>, <&cpg 1007>,
+ <&cpg 1008>, <&cpg 1009>,
+ <&cpg 1010>, <&cpg 1011>,
+ <&cpg 1012>, <&cpg 1013>,
+ <&cpg 1014>, <&cpg 1015>;
+ reset-names = "ssi-all",
+ "ssi.9", "ssi.8", "ssi.7", "ssi.6",
+ "ssi.5", "ssi.4", "ssi.3", "ssi.2",
+ "ssi.1", "ssi.0";
status = "disabled";
rcar_sound,dvc {
@@ -1152,6 +1208,7 @@
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 815>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 815>;
status = "disabled";
};
@@ -1161,6 +1218,7 @@
interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 328>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 328>;
status = "disabled";
};
@@ -1170,6 +1228,7 @@
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 327>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 327>;
status = "disabled";
};
@@ -1182,6 +1241,7 @@
interrupt-names = "ch0", "ch1";
clocks = <&cpg CPG_MOD 330>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 330>;
#dma-cells = <1>;
dma-channels = <2>;
};
@@ -1195,6 +1255,7 @@
interrupt-names = "ch0", "ch1";
clocks = <&cpg CPG_MOD 331>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 331>;
#dma-cells = <1>;
dma-channels = <2>;
};
@@ -1206,6 +1267,7 @@
clocks = <&cpg CPG_MOD 314>;
max-frequency = <200000000>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 314>;
status = "disabled";
};
@@ -1216,6 +1278,7 @@
clocks = <&cpg CPG_MOD 313>;
max-frequency = <200000000>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 313>;
status = "disabled";
};
@@ -1226,6 +1289,7 @@
clocks = <&cpg CPG_MOD 312>;
max-frequency = <200000000>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 312>;
status = "disabled";
};
@@ -1236,6 +1300,7 @@
clocks = <&cpg CPG_MOD 311>;
max-frequency = <200000000>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 311>;
status = "disabled";
};
@@ -1246,6 +1311,7 @@
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 703>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 703>;
#phy-cells = <0>;
status = "disabled";
};
@@ -1256,6 +1322,7 @@
reg = <0 0xee0a0200 0 0x700>;
clocks = <&cpg CPG_MOD 702>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 702>;
#phy-cells = <0>;
status = "disabled";
};
@@ -1266,6 +1333,7 @@
reg = <0 0xee0c0200 0 0x700>;
clocks = <&cpg CPG_MOD 701>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 701>;
#phy-cells = <0>;
status = "disabled";
};
@@ -1278,6 +1346,7 @@
phys = <&usb2_phy0>;
phy-names = "usb";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 703>;
status = "disabled";
};
@@ -1289,6 +1358,7 @@
phys = <&usb2_phy1>;
phy-names = "usb";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 702>;
status = "disabled";
};
@@ -1300,6 +1370,7 @@
phys = <&usb2_phy2>;
phy-names = "usb";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 701>;
status = "disabled";
};
@@ -1311,6 +1382,7 @@
phys = <&usb2_phy0>;
phy-names = "usb";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 703>;
status = "disabled";
};
@@ -1322,6 +1394,7 @@
phys = <&usb2_phy1>;
phy-names = "usb";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 702>;
status = "disabled";
};
@@ -1333,6 +1406,7 @@
phys = <&usb2_phy2>;
phy-names = "usb";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 701>;
status = "disabled";
};
@@ -1349,6 +1423,7 @@
phys = <&usb2_phy0>;
phy-names = "usb";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 704>;
status = "disabled";
};
@@ -1375,6 +1450,7 @@
clocks = <&cpg CPG_MOD 319>, <&pcie_bus_clk>;
clock-names = "pcie", "pcie_bus";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 319>;
status = "disabled";
};
@@ -1401,6 +1477,7 @@
clocks = <&cpg CPG_MOD 318>, <&pcie_bus_clk>;
clock-names = "pcie", "pcie_bus";
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 318>;
status = "disabled";
};
@@ -1410,6 +1487,7 @@
interrupts = <GIC_SPI 465 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 624>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 624>;
renesas,fcp = <&fcpvb1>;
};
@@ -1419,6 +1497,7 @@
reg = <0 0xfe92f000 0 0x200>;
clocks = <&cpg CPG_MOD 606>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 606>;
};
fcpf0: fcp@fe950000 {
@@ -1426,6 +1505,7 @@
reg = <0 0xfe950000 0 0x200>;
clocks = <&cpg CPG_MOD 615>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 615>;
};
fcpf1: fcp@fe951000 {
@@ -1433,6 +1513,7 @@
reg = <0 0xfe951000 0 0x200>;
clocks = <&cpg CPG_MOD 614>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 614>;
};
fcpf2: fcp@fe952000 {
@@ -1440,6 +1521,7 @@
reg = <0 0xfe952000 0 0x200>;
clocks = <&cpg CPG_MOD 613>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 613>;
};
vspbd: vsp@fe960000 {
@@ -1448,6 +1530,7 @@
interrupts = <GIC_SPI 266 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 626>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 626>;
renesas,fcp = <&fcpvb0>;
};
@@ -1457,6 +1540,7 @@
reg = <0 0xfe96f000 0 0x200>;
clocks = <&cpg CPG_MOD 607>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 607>;
};
vspi0: vsp@fe9a0000 {
@@ -1465,6 +1549,7 @@
interrupts = <GIC_SPI 444 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 631>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 631>;
renesas,fcp = <&fcpvi0>;
};
@@ -1474,6 +1559,7 @@
reg = <0 0xfe9af000 0 0x200>;
clocks = <&cpg CPG_MOD 611>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 611>;
};
vspi1: vsp@fe9b0000 {
@@ -1482,6 +1568,7 @@
interrupts = <GIC_SPI 445 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 630>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 630>;
renesas,fcp = <&fcpvi1>;
};
@@ -1491,6 +1578,7 @@
reg = <0 0xfe9bf000 0 0x200>;
clocks = <&cpg CPG_MOD 610>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 610>;
};
vspi2: vsp@fe9c0000 {
@@ -1499,6 +1587,7 @@
interrupts = <GIC_SPI 446 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 629>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 629>;
renesas,fcp = <&fcpvi2>;
};
@@ -1508,6 +1597,7 @@
reg = <0 0xfe9cf000 0 0x200>;
clocks = <&cpg CPG_MOD 609>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 609>;
};
vspd0: vsp@fea20000 {
@@ -1516,6 +1606,7 @@
interrupts = <GIC_SPI 466 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 623>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 623>;
renesas,fcp = <&fcpvd0>;
};
@@ -1525,6 +1616,7 @@
reg = <0 0xfea27000 0 0x200>;
clocks = <&cpg CPG_MOD 603>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 603>;
};
vspd1: vsp@fea28000 {
@@ -1533,6 +1625,7 @@
interrupts = <GIC_SPI 467 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 622>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 622>;
renesas,fcp = <&fcpvd1>;
};
@@ -1542,6 +1635,7 @@
reg = <0 0xfea2f000 0 0x200>;
clocks = <&cpg CPG_MOD 602>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 602>;
};
vspd2: vsp@fea30000 {
@@ -1550,6 +1644,7 @@
interrupts = <GIC_SPI 468 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 621>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 621>;
renesas,fcp = <&fcpvd2>;
};
@@ -1559,6 +1654,7 @@
reg = <0 0xfea37000 0 0x200>;
clocks = <&cpg CPG_MOD 601>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 601>;
};
vspd3: vsp@fea38000 {
@@ -1567,6 +1663,7 @@
interrupts = <GIC_SPI 469 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 620>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 620>;
renesas,fcp = <&fcpvd3>;
};
@@ -1576,6 +1673,7 @@
reg = <0 0xfea3f000 0 0x200>;
clocks = <&cpg CPG_MOD 600>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ resets = <&cpg 600>;
};
fdp1@fe940000 {
@@ -1584,6 +1682,7 @@
interrupts = <GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 119>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 119>;
renesas,fcp = <&fcpf0>;
};
@@ -1593,6 +1692,7 @@
interrupts = <GIC_SPI 263 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 118>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 118>;
renesas,fcp = <&fcpf1>;
};
@@ -1602,6 +1702,7 @@
interrupts = <GIC_SPI 264 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 117>;
power-domains = <&sysc R8A7795_PD_A3VP>;
+ resets = <&cpg 117>;
renesas,fcp = <&fcpf2>;
};
@@ -1620,6 +1721,8 @@
<&cpg CPG_MOD 721>,
<&cpg CPG_MOD 727>;
clock-names = "du.0", "du.1", "du.2", "du.3", "lvds.0";
+ resets = <&cpg 724>, <&cpg 722>, <&cpg 727>;
+ reset-names = "du.0-1", "du.2-3", "lvds.0";
status = "disabled";
vsps = <&vspd0 &vspd1 &vspd2 &vspd3>;
--
1.9.1
^ permalink raw reply related
* [PATCH 6/8] arm64: dts: r8a7796: Add reset control properties
From: Geert Uytterhoeven @ 2017-01-20 14:08 UTC (permalink / raw)
To: Philipp Zabel, Simon Horman, Magnus Damm, Michael Turquette,
Stephen Boyd, Rob Herring, Mark Rutland
Cc: linux-clk, devicetree, linux-renesas-soc, linux-kernel,
linux-arm-kernel, Geert Uytterhoeven
In-Reply-To: <1484921306-9967-1-git-send-email-geert+renesas@glider.be>
Add properties to describe the reset topology for on-SoC devices:
- Add the "#reset-cells" property to the CPG/MSSR device node,
- Add resets and reset-names properties to the various device nodes.
This allows to reset SoC devices using the Reset Controller API.
Note that all resets added match the corresponding module clocks.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
arch/arm64/boot/dts/renesas/r8a7796.dtsi | 34 ++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
index 6591c76613687b40..e3286106a038e936 100644
--- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
@@ -104,6 +104,7 @@
clocks = <&cpg CPG_MOD 408>;
clock-names = "clk";
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 408>;
};
timer {
@@ -124,6 +125,7 @@
reg = <0 0xe6020000 0 0x0c>;
clocks = <&cpg CPG_MOD 402>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 402>;
status = "disabled";
};
@@ -139,6 +141,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 912>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 912>;
};
gpio1: gpio@e6051000 {
@@ -153,6 +156,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 911>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 911>;
};
gpio2: gpio@e6052000 {
@@ -167,6 +171,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 910>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 910>;
};
gpio3: gpio@e6053000 {
@@ -181,6 +186,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 909>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 909>;
};
gpio4: gpio@e6054000 {
@@ -195,6 +201,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 908>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 908>;
};
gpio5: gpio@e6055000 {
@@ -209,6 +216,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 907>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 907>;
};
gpio6: gpio@e6055400 {
@@ -223,6 +231,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 906>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 906>;
};
gpio7: gpio@e6055800 {
@@ -237,6 +246,7 @@
interrupt-controller;
clocks = <&cpg CPG_MOD 905>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 905>;
};
pfc: pin-controller@e6060000 {
@@ -251,6 +261,7 @@
clock-names = "extal", "extalr";
#clock-cells = <2>;
#power-domain-cells = <0>;
+ #reset-cells = <1>;
};
rst: reset-controller@e6160000 {
@@ -278,6 +289,7 @@
interrupts = <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 931>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 931>;
dmas = <&dmac1 0x91>, <&dmac1 0x90>,
<&dmac2 0x91>, <&dmac2 0x90>;
dma-names = "tx", "rx", "tx", "rx";
@@ -294,6 +306,7 @@
interrupts = <GIC_SPI 288 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 930>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 930>;
dmas = <&dmac1 0x93>, <&dmac1 0x92>,
<&dmac2 0x93>, <&dmac2 0x92>;
dma-names = "tx", "rx", "tx", "rx";
@@ -310,6 +323,7 @@
interrupts = <GIC_SPI 286 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 929>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 929>;
dmas = <&dmac1 0x95>, <&dmac1 0x94>,
<&dmac2 0x95>, <&dmac2 0x94>;
dma-names = "tx", "rx", "tx", "rx";
@@ -326,6 +340,7 @@
interrupts = <GIC_SPI 290 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 928>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 928>;
dmas = <&dmac0 0x97>, <&dmac0 0x96>;
dma-names = "tx", "rx";
i2c-scl-internal-delay-ns = <110>;
@@ -341,6 +356,7 @@
interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 927>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 927>;
dmas = <&dmac0 0x99>, <&dmac0 0x98>;
dma-names = "tx", "rx";
i2c-scl-internal-delay-ns = <110>;
@@ -356,6 +372,7 @@
interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 919>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 919>;
dmas = <&dmac0 0x9b>, <&dmac0 0x9a>;
dma-names = "tx", "rx";
i2c-scl-internal-delay-ns = <110>;
@@ -371,6 +388,7 @@
interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 918>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 918>;
dmas = <&dmac0 0x9d>, <&dmac0 0x9c>;
dma-names = "tx", "rx";
i2c-scl-internal-delay-ns = <6>;
@@ -389,6 +407,7 @@
assigned-clocks = <&cpg CPG_CORE R8A7796_CLK_CANFD>;
assigned-clock-rates = <40000000>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 916>;
status = "disabled";
};
@@ -404,6 +423,7 @@
assigned-clocks = <&cpg CPG_CORE R8A7796_CLK_CANFD>;
assigned-clock-rates = <40000000>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 915>;
status = "disabled";
};
@@ -420,6 +440,7 @@
assigned-clocks = <&cpg CPG_CORE R8A7796_CLK_CANFD>;
assigned-clock-rates = <40000000>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 914>;
status = "disabled";
channel0 {
@@ -469,6 +490,7 @@
"ch24";
clocks = <&cpg CPG_MOD 812>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 812>;
phy-mode = "rgmii-id";
#address-cells = <1>;
#size-cells = <0>;
@@ -484,6 +506,7 @@
<&scif_clk>;
clock-names = "fck", "brg_int", "scif_clk";
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 310>;
status = "disabled";
};
@@ -497,6 +520,7 @@
<&dmac2 0x41>, <&dmac2 0x40>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 211>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
@@ -512,6 +536,7 @@
<&dmac2 0x43>, <&dmac2 0x42>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 210>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
@@ -526,6 +551,7 @@
dmas = <&dmac0 0x45>, <&dmac0 0x44>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 209>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
@@ -540,6 +566,7 @@
dmas = <&dmac0 0x47>, <&dmac0 0x46>;
dma-names = "tx", "rx";
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 208>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
@@ -574,6 +601,7 @@
clocks = <&cpg CPG_MOD 219>;
clock-names = "fck";
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 219>;
#dma-cells = <1>;
dma-channels = <16>;
};
@@ -607,6 +635,7 @@
clocks = <&cpg CPG_MOD 218>;
clock-names = "fck";
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 218>;
#dma-cells = <1>;
dma-channels = <16>;
};
@@ -640,6 +669,7 @@
clocks = <&cpg CPG_MOD 217>;
clock-names = "fck";
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 217>;
#dma-cells = <1>;
dma-channels = <16>;
};
@@ -651,6 +681,7 @@
clocks = <&cpg CPG_MOD 314>;
max-frequency = <200000000>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 314>;
status = "disabled";
};
@@ -661,6 +692,7 @@
clocks = <&cpg CPG_MOD 313>;
max-frequency = <200000000>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 313>;
status = "disabled";
};
@@ -671,6 +703,7 @@
clocks = <&cpg CPG_MOD 312>;
max-frequency = <200000000>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 312>;
status = "disabled";
};
@@ -681,6 +714,7 @@
clocks = <&cpg CPG_MOD 311>;
max-frequency = <200000000>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
+ resets = <&cpg 311>;
status = "disabled";
};
};
--
1.9.1
^ permalink raw reply related
* [PATCH 7/8] ARM: dts: r8a7743: Add reset control properties
From: Geert Uytterhoeven @ 2017-01-20 14:08 UTC (permalink / raw)
To: Philipp Zabel, Simon Horman, Magnus Damm, Michael Turquette,
Stephen Boyd, Rob Herring, Mark Rutland
Cc: linux-clk, devicetree, linux-renesas-soc, linux-kernel,
linux-arm-kernel, Geert Uytterhoeven
In-Reply-To: <1484921306-9967-1-git-send-email-geert+renesas@glider.be>
Add properties to describe the reset topology for on-SoC devices:
- Add the "#reset-cells" property to the CPG/MSSR device node,
- Add resets and reset-names properties to the various device nodes.
This allows to reset SoC devices using the Reset Controller API.
Note that all resets added match the corresponding module clocks.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
arch/arm/boot/dts/r8a7743.dtsi | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/arch/arm/boot/dts/r8a7743.dtsi b/arch/arm/boot/dts/r8a7743.dtsi
index 40d2cdede7022ff6..5f2287ea00922ee0 100644
--- a/arch/arm/boot/dts/r8a7743.dtsi
+++ b/arch/arm/boot/dts/r8a7743.dtsi
@@ -63,6 +63,7 @@
clocks = <&cpg CPG_MOD 408>;
clock-names = "clk";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 408>;
};
irqc: interrupt-controller@e61c0000 {
@@ -82,6 +83,7 @@
<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 407>;
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 407>;
};
timer {
@@ -103,6 +105,7 @@
clock-names = "extal", "usb_extal";
#clock-cells = <2>;
#power-domain-cells = <0>;
+ #reset-cells = <1>;
};
prr: chipid@ff000044 {
@@ -149,6 +152,7 @@
clocks = <&cpg CPG_MOD 219>;
clock-names = "fck";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 219>;
#dma-cells = <1>;
dma-channels = <15>;
};
@@ -181,6 +185,7 @@
clocks = <&cpg CPG_MOD 218>;
clock-names = "fck";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 218>;
#dma-cells = <1>;
dma-channels = <15>;
};
@@ -196,6 +201,7 @@
<&dmac1 0x21>, <&dmac1 0x22>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 204>;
status = "disabled";
};
@@ -210,6 +216,7 @@
<&dmac1 0x25>, <&dmac1 0x26>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 203>;
status = "disabled";
};
@@ -224,6 +231,7 @@
<&dmac1 0x27>, <&dmac1 0x28>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 202>;
status = "disabled";
};
@@ -238,6 +246,7 @@
<&dmac1 0x1b>, <&dmac1 0x1c>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 1106>;
status = "disabled";
};
@@ -252,6 +261,7 @@
<&dmac1 0x1f>, <&dmac1 0x20>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 1107>;
status = "disabled";
};
@@ -266,6 +276,7 @@
<&dmac1 0x23>, <&dmac1 0x24>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 1108>;
status = "disabled";
};
@@ -280,6 +291,7 @@
<&dmac1 0x3d>, <&dmac1 0x3e>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 206>;
status = "disabled";
};
@@ -294,6 +306,7 @@
<&dmac1 0x19>, <&dmac1 0x1a>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 207>;
status = "disabled";
};
@@ -308,6 +321,7 @@
<&dmac1 0x1d>, <&dmac1 0x1e>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 216>;
status = "disabled";
};
@@ -323,6 +337,7 @@
<&dmac1 0x29>, <&dmac1 0x2a>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 721>;
status = "disabled";
};
@@ -338,6 +353,7 @@
<&dmac1 0x2d>, <&dmac1 0x2e>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 720>;
status = "disabled";
};
@@ -353,6 +369,7 @@
<&dmac1 0x2b>, <&dmac1 0x2c>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 719>;
status = "disabled";
};
@@ -368,6 +385,7 @@
<&dmac1 0x2f>, <&dmac1 0x30>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 718>;
status = "disabled";
};
@@ -383,6 +401,7 @@
<&dmac1 0xfb>, <&dmac1 0xfc>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 715>;
status = "disabled";
};
@@ -398,6 +417,7 @@
<&dmac1 0xfd>, <&dmac1 0xfe>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 714>;
status = "disabled";
};
@@ -413,6 +433,7 @@
<&dmac1 0x39>, <&dmac1 0x3a>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 717>;
status = "disabled";
};
@@ -428,6 +449,7 @@
<&dmac1 0x4d>, <&dmac1 0x4e>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 716>;
status = "disabled";
};
@@ -443,6 +465,7 @@
<&dmac1 0x3b>, <&dmac1 0x3c>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 713>;
status = "disabled";
};
@@ -452,6 +475,7 @@
interrupts = <GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 813>;
power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+ resets = <&cpg 813>;
phy-mode = "rmii";
#address-cells = <1>;
#size-cells = <0>;
--
1.9.1
^ permalink raw reply related
* [PATCH 8/8] ARM: dts: r8a7745: Add reset control properties
From: Geert Uytterhoeven @ 2017-01-20 14:08 UTC (permalink / raw)
To: Philipp Zabel, Simon Horman, Magnus Damm, Michael Turquette,
Stephen Boyd, Rob Herring, Mark Rutland
Cc: linux-clk, devicetree, linux-renesas-soc, linux-kernel,
linux-arm-kernel, Geert Uytterhoeven
In-Reply-To: <1484921306-9967-1-git-send-email-geert+renesas@glider.be>
Add properties to describe the reset topology for on-SoC devices:
- Add the "#reset-cells" property to the CPG/MSSR device node,
- Add resets and reset-names properties to the various device nodes.
This allows to reset SoC devices using the Reset Controller API.
Note that all resets added match the corresponding module clocks.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
arch/arm/boot/dts/r8a7745.dtsi | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/arch/arm/boot/dts/r8a7745.dtsi b/arch/arm/boot/dts/r8a7745.dtsi
index a81dcc82e2eadd09..ac62c14af764dd1a 100644
--- a/arch/arm/boot/dts/r8a7745.dtsi
+++ b/arch/arm/boot/dts/r8a7745.dtsi
@@ -63,6 +63,7 @@
clocks = <&cpg CPG_MOD 408>;
clock-names = "clk";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 408>;
};
irqc: interrupt-controller@e61c0000 {
@@ -82,6 +83,7 @@
<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 407>;
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 407>;
};
timer {
@@ -103,6 +105,7 @@
clock-names = "extal", "usb_extal";
#clock-cells = <2>;
#power-domain-cells = <0>;
+ #reset-cells = <1>;
};
prr: chipid@ff000044 {
@@ -149,6 +152,7 @@
clocks = <&cpg CPG_MOD 219>;
clock-names = "fck";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 219>;
#dma-cells = <1>;
dma-channels = <15>;
};
@@ -181,6 +185,7 @@
clocks = <&cpg CPG_MOD 218>;
clock-names = "fck";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 218>;
#dma-cells = <1>;
dma-channels = <15>;
};
@@ -196,6 +201,7 @@
<&dmac1 0x21>, <&dmac1 0x22>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 204>;
status = "disabled";
};
@@ -210,6 +216,7 @@
<&dmac1 0x25>, <&dmac1 0x26>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 203>;
status = "disabled";
};
@@ -224,6 +231,7 @@
<&dmac1 0x27>, <&dmac1 0x28>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 202>;
status = "disabled";
};
@@ -238,6 +246,7 @@
<&dmac1 0x1b>, <&dmac1 0x1c>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 1106>;
status = "disabled";
};
@@ -252,6 +261,7 @@
<&dmac1 0x1f>, <&dmac1 0x20>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 1107>;
status = "disabled";
};
@@ -266,6 +276,7 @@
<&dmac1 0x23>, <&dmac1 0x24>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 1108>;
status = "disabled";
};
@@ -280,6 +291,7 @@
<&dmac1 0x3d>, <&dmac1 0x3e>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 206>;
status = "disabled";
};
@@ -294,6 +306,7 @@
<&dmac1 0x19>, <&dmac1 0x1a>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 207>;
status = "disabled";
};
@@ -308,6 +321,7 @@
<&dmac1 0x1d>, <&dmac1 0x1e>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 216>;
status = "disabled";
};
@@ -323,6 +337,7 @@
<&dmac1 0x29>, <&dmac1 0x2a>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 721>;
status = "disabled";
};
@@ -338,6 +353,7 @@
<&dmac1 0x2d>, <&dmac1 0x2e>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 720>;
status = "disabled";
};
@@ -353,6 +369,7 @@
<&dmac1 0x2b>, <&dmac1 0x2c>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 719>;
status = "disabled";
};
@@ -368,6 +385,7 @@
<&dmac1 0x2f>, <&dmac1 0x30>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 718>;
status = "disabled";
};
@@ -383,6 +401,7 @@
<&dmac1 0xfb>, <&dmac1 0xfc>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 715>;
status = "disabled";
};
@@ -398,6 +417,7 @@
<&dmac1 0xfd>, <&dmac1 0xfe>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 714>;
status = "disabled";
};
@@ -413,6 +433,7 @@
<&dmac1 0x39>, <&dmac1 0x3a>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 717>;
status = "disabled";
};
@@ -428,6 +449,7 @@
<&dmac1 0x4d>, <&dmac1 0x4e>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 716>;
status = "disabled";
};
@@ -443,6 +465,7 @@
<&dmac1 0x3b>, <&dmac1 0x3c>;
dma-names = "tx", "rx", "tx", "rx";
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 713>;
status = "disabled";
};
@@ -452,6 +475,7 @@
interrupts = <GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 813>;
power-domains = <&sysc R8A7745_PD_ALWAYS_ON>;
+ resets = <&cpg 813>;
phy-mode = "rmii";
#address-cells = <1>;
#size-cells = <0>;
--
1.9.1
^ permalink raw reply related
* Re: [PATCH 1/2] dt-bindings: gpu: Add Mali Utgard bindings
From: Rob Herring @ 2017-01-20 14:10 UTC (permalink / raw)
To: John Stultz
Cc: Maxime Ripard, Mark Rutland, Thomas Petazzoni,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Heiko Stuebner, Javier Martinez Canillas, Kevin Hilman,
Linus Walleij, Krzysztof Kozlowski, Matthias Brugger,
Chen-Yu Tsai, Kukjin Kim, Alexandre Belloni, Boris Brezillon,
Carlo Caione, Antoine Ténart, linux-arm-kernel
In-Reply-To: <CALAqxLV=5ZCu8=rDUFHS9148DxzrqeTipsueA7F4i3t-mcWr=A@mail.gmail.com>
On Thu, Jan 19, 2017 at 1:24 PM, John Stultz <john.stultz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> On Mon, Jan 16, 2017 at 5:24 AM, Maxime Ripard
> <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:
>> The ARM Mali Utgard GPU family is embedded into a number of SoCs from
>> Allwinner, Amlogic, Mediatek or Rockchip.
>>
>> Add a binding for the GPU of that family.
>>
>> Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>> ---
>> .../devicetree/bindings/gpu/arm,mali-utgard.txt | 76 ++++++++++++++++++++++
>> 1 file changed, 76 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt
>>
>> diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt b/Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt
>> new file mode 100644
>> index 000000000000..df05ba0ec357
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt
>> @@ -0,0 +1,76 @@
>> +ARM Mali Utgard GPU
>> +===================
>> +
>> +Required properties:
>> + - compatible:
>> + * "arm,mali-utgard" and one of the following:
>> + + "arm,mali-300"
>> + + "arm,mali-400"
>> + + "arm,mali-450"
>> +
>> + - reg: Physical base address and length of the GPU registers
>> +
>> + - interrupts: an entry for each entry in interrupt-names.
>> + See ../interrupt-controller/interrupts.txt for details.
>> +
>> + - interrupt-names:
>> + * ppX: Pixel Processor X interrupt (X from 0 to 7)
>> + * ppmmuX: Pixel Processor X MMU interrupt (X from 0 to 7)
>> + * pp: Pixel Processor broadcast interrupt (mali-450 only)
>> + * gp: Geometry Processor interrupt
>> + * gpmmu: Geometry Processor MMU interrupt
>> +
>> +
>> +Optional properties:
>> + - interrupt-names:
>> + * pmu: Power Management Unit interrupt, if implemented in hardware
>> +
>> +Vendor-specific bindings
>> +------------------------
>> +
>> +The Mali GPU is integrated very differently from one SoC to
>> +another. In order to accommodate those differences, you have the option
>> +to specify one more vendor-specific compatible, among:
>> +
>> + - allwinner,sun4i-a10-mali
>> + Required properties:
>> + * clocks: an entry for each entry in clock-names
>> + * clock-names:
>> + + bus: bus clock for the GPU
>> + + core: clock driving the GPU itself
>> + * resets: phandle to the reset line for the GPU
>> +
>> + - allwinner,sun7i-a20-mali
>> + Required properties:
>> + * clocks: an entry for each entry in clock-names
>> + * clock-names:
>> + + bus: bus clock for the GPU
>> + + core: clock driving the GPU itself
>> + * resets: phandle to the reset line for the GPU
>> +
>> +Example:
>> +
>> +mali: gpu@01c40000 {
>> + compatible = "allwinner,sun7i-a20-mali", "arm,mali-400",
>> + "arm,mali-utgard";
>> + reg = <0x01c40000 0x10000>;
>> + interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>,
>> + <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>,
>> + <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>,
>> + <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
>> + <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
>> + <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>,
>> + <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
>> + interrupt-names = "gp",
>> + "gpmmu",
>> + "pp0",
>> + "ppmmu0",
>> + "pp1",
>> + "ppmmu1",
>> + "pmu";
>> + clocks = <&ccu CLK_BUS_GPU>, <&ccu CLK_GPU>;
>> + clock-names = "bus", "core";
>> + resets = <&ccu RST_BUS_GPU>;
>> +};
>> +
>> +
>
>
> Having a mali utgard binding upstream would be great. However I'm a
> little worried that the mali driver I've used sort of only half way
> uses DT, and still requires a custom built in platform driver to setup
> numerous other things. Curious if you have a pointer to the kernel
> driver you've been using with the vendor specific bindings above? I'd
> like to try to adapt what we have to your method to validate the above
> as generic.
>
> And, just for context, here's the node we've been using with hikey:
>
> mali:mali@f4080000 {
> compatible = "arm,mali-450", "arm,mali-utgard";
> reg = <0x0 0x3f100000 0x0 0x00708000>;
Why's the size 7MB?
> clocks = <&media_ctrl HI6220_G3D_CLK>,
> <&media_ctrl HI6220_G3D_PCLK>;
> clock-names = "clk_g3d", "pclk_g3d";
Seems like these are swapped from Maxime's order. Based on the pclk
frequency, that's the bus clock.
> mali_def_freq = <500>;
> pclk_freq = <144>;
There's a standard property for these with assigned-clocks.
> dfs_steps = <2>;
> dfs_lockprf = <1>;
> dfs_limit_max_prf = <1>;
> dfs_profile_num = <2>;
> dfs_profiles = <250 3 0>, <500 1 0>;
This looks like a OPP table. It can all be driver data associated with
the compatible string for now.
> mali_type = <2>;
Not sure about this one.
Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: gpu: Add Mali Utgard bindings
From: Rob Herring @ 2017-01-20 14:15 UTC (permalink / raw)
To: Maxime Ripard
Cc: Mark Rutland, Thomas Petazzoni, Heiko Stuebner,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Kevin Hilman, Linus Walleij, John Reitan, Krzysztof Kozlowski,
Javier Martinez Canillas, Chen-Yu Tsai, Kukjin Kim, John Stultz,
Boris Brezillon, Antoine Ténart, Matthias Brugger,
Alexandre Belloni, Carlo Caione,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <20170120091608.ijdhpeq2t5no75rc@lukather>
On Fri, Jan 20, 2017 at 3:16 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Hi John,
>
> On Thu, Jan 19, 2017 at 11:24:38AM -0800, John Stultz wrote:
>> On Mon, Jan 16, 2017 at 5:24 AM, Maxime Ripard
>> <maxime.ripard@free-electrons.com> wrote:
>> > The ARM Mali Utgard GPU family is embedded into a number of SoCs from
>> > Allwinner, Amlogic, Mediatek or Rockchip.
>> >
>> > Add a binding for the GPU of that family.
>> >
>> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> > ---
>> > .../devicetree/bindings/gpu/arm,mali-utgard.txt | 76 ++++++++++++++++++++++
>> > 1 file changed, 76 insertions(+)
>> > create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt
>> >
>> > diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt b/Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt
>> > new file mode 100644
>> > index 000000000000..df05ba0ec357
>> > --- /dev/null
>> > +++ b/Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt
>> > @@ -0,0 +1,76 @@
>> > +ARM Mali Utgard GPU
>> > +===================
>> > +
>> > +Required properties:
>> > + - compatible:
>> > + * "arm,mali-utgard" and one of the following:
>> > + + "arm,mali-300"
>> > + + "arm,mali-400"
>> > + + "arm,mali-450"
>> > +
>> > + - reg: Physical base address and length of the GPU registers
>> > +
>> > + - interrupts: an entry for each entry in interrupt-names.
>> > + See ../interrupt-controller/interrupts.txt for details.
>> > +
>> > + - interrupt-names:
>> > + * ppX: Pixel Processor X interrupt (X from 0 to 7)
>> > + * ppmmuX: Pixel Processor X MMU interrupt (X from 0 to 7)
>> > + * pp: Pixel Processor broadcast interrupt (mali-450 only)
>> > + * gp: Geometry Processor interrupt
>> > + * gpmmu: Geometry Processor MMU interrupt
>> > +
>> > +
>> > +Optional properties:
>> > + - interrupt-names:
>> > + * pmu: Power Management Unit interrupt, if implemented in hardware
>> > +
>> > +Vendor-specific bindings
>> > +------------------------
>> > +
>> > +The Mali GPU is integrated very differently from one SoC to
>> > +another. In order to accommodate those differences, you have the option
>> > +to specify one more vendor-specific compatible, among:
>> > +
>> > + - allwinner,sun4i-a10-mali
>> > + Required properties:
>> > + * clocks: an entry for each entry in clock-names
>> > + * clock-names:
>> > + + bus: bus clock for the GPU
>> > + + core: clock driving the GPU itself
>> > + * resets: phandle to the reset line for the GPU
>> > +
>> > + - allwinner,sun7i-a20-mali
>> > + Required properties:
>> > + * clocks: an entry for each entry in clock-names
>> > + * clock-names:
>> > + + bus: bus clock for the GPU
>> > + + core: clock driving the GPU itself
>> > + * resets: phandle to the reset line for the GPU
>> > +
>> > +Example:
>> > +
>> > +mali: gpu@01c40000 {
>> > + compatible = "allwinner,sun7i-a20-mali", "arm,mali-400",
>> > + "arm,mali-utgard";
>> > + reg = <0x01c40000 0x10000>;
>> > + interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>,
>> > + <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>,
>> > + <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>,
>> > + <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
>> > + <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
>> > + <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>,
>> > + <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
>> > + interrupt-names = "gp",
>> > + "gpmmu",
>> > + "pp0",
>> > + "ppmmu0",
>> > + "pp1",
>> > + "ppmmu1",
>> > + "pmu";
>> > + clocks = <&ccu CLK_BUS_GPU>, <&ccu CLK_GPU>;
>> > + clock-names = "bus", "core";
>> > + resets = <&ccu RST_BUS_GPU>;
>> > +};
>>
>> Having a mali utgard binding upstream would be great. However I'm a
>> little worried that the mali driver I've used sort of only half way
>> uses DT, and still requires a custom built in platform driver to setup
>> numerous other things. Curious if you have a pointer to the kernel
>> driver you've been using with the vendor specific bindings above? I'd
>> like to try to adapt what we have to your method to validate the above
>> as generic.
>
> I've created a custom platform driver, so just like you it seems,
> because I've not managed to make ARM's DT support work.
>
> https://github.com/mripard/sunxi-mali/blob/master/driver/src/devicedrv/mali/platform/sunxi/sunxi.c
>
> I haven't updated it yet with the bindings suggested above, but only
> the interrupt and clock names have changed. The rest very much
> applies.
>
> The only thing that might be vendor specific would be the (optional)
> declaration of the mali_gpu_device_data structure. As far as I know,
> there's two things of importance there:
> - the list of the valid OPPs in order to do DVFS, but that could be
> made generic too using the operating-points binding
>
> - And the valid area for the buffers for the fbdev blobs (fb_start,
> fb_size and shared_mem_size). I'm not entirely happy with this one
> so far (which is also why I've not pushed it). We'd need to come
> up with a way to get the base address and size of the CMA region
> backing the framebuffer allocation, but I haven't find any trivial
> way to do so, so for now I just hardcoded it. Worst case scenario,
> we can hardcode values based on the compatible.
memory-region property?
>
>> And, just for context, here's the node we've been using with hikey:
>>
>> mali:mali@f4080000 {
>> compatible = "arm,mali-450", "arm,mali-utgard";
>> reg = <0x0 0x3f100000 0x0 0x00708000>;
>
> This is because the hikey is using a 64 bits CPU, right?
Having 2 cells for address and size is generally entirely pointless.
The peripheral regions generally don't need 64-bits of address space
or size. ranges should be used.
Rob
^ permalink raw reply
* Re: [PATCH v3 2/4] dt-bindings: Add TI SCI PM Domains
From: Sudeep Holla @ 2017-01-20 14:18 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Kevin Hilman
Cc: Nishanth Menon, devicetree@vger.kernel.org, Santosh Shilimkar,
Dave Gerlach, Lokesh Vutla, Keerthy, linux-pm@vger.kernel.org,
Rafael J . Wysocki, linux-kernel@vger.kernel.org, Tero Kristo,
Russell King, Sudeep Holla, linux-arm-kernel@lists.infradead.org
In-Reply-To: <CAPDyKFqzs59N5bc89X-96Fgy9b=_+s8zYmHHshQeDrJCnaqprA@mail.gmail.com>
On 20/01/17 14:00, Ulf Hansson wrote:
> + Sudeep
>
> On 19 January 2017 at 00:03, Rob Herring <robh@kernel.org> wrote:
>>
>> We could continue to use the power domain binding (maybe we already
>> are and that ship has sailed). I'm not totally against the idea even
>> if there is no power domain, but I'm not sold on it either. If we do
>> go this route, then I still say the id should be a cell in the
>> power-domain phandle.
>>
>> Another option is create something new either common or TI SCI
>> specific. It could be just a table of ids and phandles in the SCI
>> node. I'm much more comfortable with an isolated property in one node
>> than something scattered throughout the DT.
>
> To me, this seems like the best possible solution.
>
> However, perhaps we should also consider the SCPI Generic power domain
> (drivers/firmware/scpi_pm_domain.c), because I believe it's closely
> related.
> To change the power state of a device, this PM domain calls
> scpi_device_set|get_power_state() (drivers/firmware/arm_scpi.c), which
> also needs a device id as a parameter. Very similar to our case with
> the TI SCI domain.
>
> Currently these SCPI device ids lacks corresponding DT bindings, so
> the scpi_pm_domain tries to work around it by assigning ids
> dynamically at genpd creation time.
>
IIUC do you mean the binding for the power domain provider to have a
list of domain ids ? If so yes, we don't have one.
But the idea was to have the range to be continuous and create genpd for
the complete range. Though the SCPI specification lacked a command to
get the max. no. of domains supported. That's the reason we had to
introduce the num-domains(*) which may be optional if we have firmware
interface to obtain that information.
--
Regards,
Sudeep
(*) P.S: but it has been considered for SCMI(which is an improvement and
more flexible/extensible replacement/upgrade to SCPI) which will be
released soon.
^ permalink raw reply
* Re: [PATCH] usb: dwc3: add quirk to handle DWC_USB3_NUM == DWC_USB3_NUM_IN_EPS
From: Bryan O'Donoghue @ 2017-01-20 14:19 UTC (permalink / raw)
To: Felipe Balbi, John Youn,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
mark.rutland-5wv7dgnIgG8@public.gmane.org,
linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <871svy9bl1.fsf-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
On 20/01/17 12:10, Felipe Balbi wrote:
>>> And unfortunately, whether this is set or not is not visible to the
>>> software so it will require a quirk.
>>
>> but arrived at this conclusion because I couldn't think of a reasonable
>> guess value for IN/OUT endpoint numbers that would work if
>> DWC_USB3_NUM_EPS == DWC_USB3_NUM_IN_EPS
>
> Well, we can, for now at least, take the simple approach of half IN,
> half OUT. If DWC3_USB3_NUM_EPS is odd, then OUT should take the extra
> endpoint. If anybody complains, we can fix it later ;-)
Sure, works just as well for me either way.
---
bod
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 2/4] dt-bindings: Add TI SCI PM Domains
From: Dave Gerlach @ 2017-01-20 14:20 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Kevin Hilman
Cc: Nishanth Menon, devicetree@vger.kernel.org,
linux-pm@vger.kernel.org, Lokesh Vutla, Keerthy,
Santosh Shilimkar, Rafael J . Wysocki,
linux-kernel@vger.kernel.org, Tero Kristo, Russell King,
Sudeep Holla, linux-arm-kernel@lists.infradead.org
In-Reply-To: <CAPDyKFqzs59N5bc89X-96Fgy9b=_+s8zYmHHshQeDrJCnaqprA@mail.gmail.com>
On 01/20/2017 08:00 AM, Ulf Hansson wrote:
> + Sudeep
>
> On 19 January 2017 at 00:03, Rob Herring <robh@kernel.org> wrote:
>> On Tue, Jan 17, 2017 at 6:07 PM, Kevin Hilman <khilman@baylibre.com> wrote:
>>> Tero Kristo <t-kristo@ti.com> writes:
>>>> On 17/01/17 00:12, Dave Gerlach wrote:
>>>>> On 01/13/2017 08:40 PM, Rob Herring wrote:
>>>>>> On Fri, Jan 13, 2017 at 2:28 PM, Dave Gerlach <d-gerlach@ti.com> wrote:
>>
>> [...]
>>
>>>>>>> My ti,sci-id is not an index into a list of power domains, so it
>>>>>>> should not
>>>>>>> go in the power-domains cells and go against what the power-domains
>>>>>>> binding
>>>>>>> says that the cell expects. We have one single power domain, and the new
>>>>>>> ti,sci-id binding is not something the genpd framework itself is
>>>>>>> concerned
>>>>>>> with as it's our property to identify a device inside a power domain,
>>>>>>> not to
>>>>>>> identify which power domain it is associated with.
>>>>>>
>>>>>> What is the id used for? I can understand why you need to know what
>>>>>> power domain a device is in (as power-domains identifies), but not
>>>>>> what devices are in a power domain.
>>>>>
>>>>> We have a system control processor that provides power management
>>>>> services to the OS and it responsible for handling the power state of
>>>>> each device. This control happens over a communication interface we have
>>>>> called TI SCI (implemented at drivers/firmware/ti-sci.c). The
>>>>> communication protocol uses these ids to identify each device within the
>>>>> power domain so that the control processor can do what is necessary to
>>>>> enable that device.
>>>>
>>>> I think a minor detail here that Rob might be missing right now is,
>>>> that the ti,sci-id is only controlling the PM runtime handling, and
>>>> providing the ID per-device for this purpose only. AFAIK, it is not
>>>> really connected to the power domain anymore as such, as we don't have
>>>> power-domains / per device anymore as was the case in some earlier
>>>> revision of this work.
>>>
>>> I think this gets to the heart of things. IMO The confusion arises
>>> because we're throwing around the term "power domain" when there isn't
>>> an actual hardware power domain here.
>>
>> I thought there was 1.
>>
>>> Unfortunately, the genpd bindings have used the terminology power-domain
>>> when in fact genpd is more generic than that and can be used not just
>>> for hardware power domains, but for arbitrary grouping of devices that
>>> have common PM properties. That's why genpd actually stands for generic
>>> PM domain, not power domain. Unfortunately, the bindings have grown
>>> primarily out of the usage for hardware power domains.
>>
>> Now it makes some sense.
>>
>> So the question is does this PM domain grouping need to be described
>> in DT or not, and if so what does that look like?
>
> Yes, it's needed and already being done. For example, we have clock
> domains for several Renesas platforms.
>
>>
>> We could continue to use the power domain binding (maybe we already
>> are and that ship has sailed). I'm not totally against the idea even
>> if there is no power domain, but I'm not sold on it either. If we do
>> go this route, then I still say the id should be a cell in the
>> power-domain phandle.
>>
>> Another option is create something new either common or TI SCI
>> specific. It could be just a table of ids and phandles in the SCI
>> node. I'm much more comfortable with an isolated property in one node
>> than something scattered throughout the DT.
>
> To me, this seems like the best possible solution.
>
> However, perhaps we should also consider the SCPI Generic power domain
> (drivers/firmware/scpi_pm_domain.c), because I believe it's closely
> related.
> To change the power state of a device, this PM domain calls
> scpi_device_set|get_power_state() (drivers/firmware/arm_scpi.c), which
> also needs a device id as a parameter. Very similar to our case with
> the TI SCI domain.
>
> Currently these SCPI device ids lacks corresponding DT bindings, so
> the scpi_pm_domain tries to work around it by assigning ids
> dynamically at genpd creation time.
>
> That makes me wonder, whether we should think of something common/generic?
When you say something common/generic, do you mean a better binding for genpd,
or something bigger than that like a new driver? Because I do think a phandle
cell left open for the genpd provider to interpret solves both the scpi and
ti-sci problem we are facing here in the best way. Using generic PM domains lets
us do exactly what we want apart from interpreting the phandle cell with our
driver, and I feel like anything else we try at this point is just going to be
to work around that. Is bringing back genpd xlate something we can discuss?
Regards,
Dave
>
> [...]
>
> Kind regards
> Uffe
>
^ permalink raw reply
* Re: Question about OF-graph ports
From: Rob Herring @ 2017-01-20 14:22 UTC (permalink / raw)
To: Kuninori Morimoto; +Cc: Mark Brown, Linux-DT, Linux-ALSA
In-Reply-To: <871svymrnt.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
On Thu, Jan 19, 2017 at 7:46 PM, Kuninori Morimoto
<kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org> wrote:
>
> Hi Rob
>
> Thank you for your feedback
>
>> > SoC.0 Codec0
>> > SoC.1 <-> Card <-> Codec1
>> > SoC.2 Codec2
> (snip)
>> The card should only have the entry (or exit) points of the graph.
>> That may not even need to be graph nodes. It could just be a list of
>> CPU DAI phandles (which then have graph nodes).
>>
>> Look at the display side use of OF graph. We have display subsystem
>> nodes (like a card) with a poorly named ports property (nothing to do
>> with OF graph) listing display channels. Each phandle there is the
>> entry point to the graph and is typically the front end display
>> controller (perhaps a CSC and scaling unit). The graph then goes to a
>> backend controller (for display timing), then to interface PHY
>> (DSI/HDMI), then possibly and external bridge chip, then to a
>> connector or panel node. That's a complex example. In simple cases, we
>> just have a display controller connected to a panel.
>
> If my understanding here was correct, do you mean like this ?
I need a drawing of what the hw looks like to really tell you.
>
> sound_dai0 {
> ports {
> port { /* Card.0 */ }
> port { /* SoC.0 */ }
> port { /* Codec0 */ }
This tells me that the DAI0 h/w block has 2 inputs and 1 output or has
1 input and 2 outputs. I assume that is not what you meant.
> };
> };
>
> sound_dai1 {
> ports {
> port { /* Card.1 */ }
> port { /* SoC.1 */ }
> port { /* Codec1 */ }
> };
> };
>
> sound_dai2 {
> ports {
> port { /* Card.2 */ }
> port { /* SoC.2 */ }
> port { /* Codec2 */ }
> };
> };
>
> card {
> ports {
> port { /* sound_dai0 */ }
> port { /* sound_dai1 */ }
> port { /* sound_dai2 */ }
> };
> };
>
>
> Or this ?
>
> sound_dai0 {
> ports {
> port { /* SoC.0 */ }
> port { /* Codec0 */ }
This is probably closer, but I don't know what SoC.0 is. The input
should be some audio processor I guess.
> };
> };
>
> sound_dai1 {
> ports {
> port { /* SoC.1 */ }
> port { /* Codec1 */ }
> };
> };
>
> sound_dai2 {
> ports {
> port { /* SoC.2 */ }
> port { /* Codec2 */ }
> };
> };
>
> card {
> dais = <&sound_dai0
> &sound_dai1
> &sound_dai2>;
> };
>
>
> Best regards
> ---
> Kuninori Morimoto
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v5 00/14] ARM: da850-lcdk: add SATA support
From: Sekhar Nori @ 2017-01-20 14:22 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Tejun Heo, Kevin Hilman, Patrick Titiano, Michael Turquette,
Rob Herring, Mark Rutland, Russell King, David Lechner, linux-ide,
linux-devicetree, LKML, arm-soc, Olof Johansson, Arnd Bergmann
In-Reply-To: <CAMpxmJUPh9MZKwG6k6p_mo6niXNNT02JCDgXi4DPRH2Fk3yxng@mail.gmail.com>
On Friday 20 January 2017 07:32 PM, Bartosz Golaszewski wrote:
> 2017-01-20 14:56 GMT+01:00 Sekhar Nori <nsekhar@ti.com>:
>> Hi Tejun,
>>
>> On Friday 20 January 2017 06:58 PM, Tejun Heo wrote:
>>> On Fri, Jan 20, 2017 at 12:21:51PM +0100, Bartosz Golaszewski wrote:
>>>> This series contains all the changes necessary to make SATA work on
>>>> the da850-lcdk board.
>>>>
>>>> The first patch adds DT bindings for the ahci-da850 driver.
>>>>
>>>> The second enables relevant modules in davinci_all_defconfig.
>>>>
>>>> Patches 03/14-06/14 modify the way the clocks are handled regarding
>>>> SATA on the da850 platform. We modify the ahci driver to retrieve
>>>> the clock via con_id and model the external SATA oscillator as
>>>> a real clock.
>>>>
>>>> Patches 07/14-11/14 extend the ahci-da850 driver. Add DT support,
>>>> implement workarounds necessary to make SATA work on the da850-lcdk
>>>> board and un-hardcode the external clock multiplier.
>>>
>>> Please feel free to add
>>>
>>> Acked-by: Tejun Heo <tj@kernel.org>
>>>
>>> to the all libata patches. Please let me know how the patches should
>>> be routed once other parts are settled.
>>
>> I believe you can queue the libata patches independently (patches 1, 4,
>> 7, 8, 9, 10, 11). It looks like they have been written such that driver
>> continues to work with existing platform code (Bartosz, please disagree
>> if I am wrong). 1/14 still needs the ack from DT maintainers.
>>
>
> Patch 11/14 depends on 06/14. Other than that I think it should work
> independently.
Ah, right. You recently changed 11/14 to error out in case refclkpn is
not found instead of using a default value (do agree that current
version is better).
If there are no build time dependencies still, it might be okay to queue
these patches through their respective maintainer trees.
Tejun, I am open to queuing the driver changes through ARM-SoC. I guess
with that there is little chance that SATA will be broken on linux-next
for a longish period of time.
I have Cced the ARM-SoC maintainers too.
Thanks,
Sekhar
^ permalink raw reply
* Re: [PATCH 3/3] PCI: imx6: Add code to support i.MX7D
From: Andrey Smirnov @ 2017-01-20 14:24 UTC (permalink / raw)
To: Lucas Stach
Cc: linux-pci, Andrey Yurovsky, Richard Zhu, Bjorn Helgaas,
Fabio Estevam, Shawn Guo, Rob Herring, Mark Rutland, Lee Jones,
linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <1484844727.24339.50.camel@pengutronix.de>
On Thu, Jan 19, 2017 at 8:52 AM, Lucas Stach <l.stach@pengutronix.de> wrote:
> Am Donnerstag, den 19.01.2017, 08:36 -0800 schrieb Andrey Smirnov:
>> Add various bits of code needed to support i.MX7D variant of the IP.
>>
>> Cc: yurovsky@gmail.com
>> Cc: Richard Zhu <hongxing.zhu@nxp.com>
>> Cc: Lucas Stach <l.stach@pengutronix.de>
>> Cc: Bjorn Helgaas <bhelgaas@google.com>
>> Cc: Fabio Estevam <festevam@gmail.com>
>> Cc: Shawn Guo <shawnguo@kernel.org>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Lee Jones <lee.jones@linaro.org>
>> Cc: linux-arm-kernel@lists.infradead.org
>> Cc: devicetree@vger.kernel.org
>> Cc: linux-kernel@vger.kernel.org
>> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>> ---
>> .../devicetree/bindings/pci/fsl,imx6q-pcie.txt | 6 +-
>> drivers/pci/host/pci-imx6.c | 188 ++++++++++++++++++---
>> include/linux/mfd/syscon/imx7-gpc.h | 18 ++
>> include/linux/mfd/syscon/imx7-iomuxc-gpr.h | 4 +
>> include/linux/mfd/syscon/imx7-src.h | 18 ++
>> 5 files changed, 208 insertions(+), 26 deletions(-)
>> create mode 100644 include/linux/mfd/syscon/imx7-gpc.h
>> create mode 100644 include/linux/mfd/syscon/imx7-src.h
>>
>> diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
>> index 83aeb1f..20b9382 100644
>> --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
>> +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
>> @@ -4,7 +4,8 @@ This PCIe host controller is based on the Synopsis Designware PCIe IP
>> and thus inherits all the common properties defined in designware-pcie.txt.
>>
>> Required properties:
>> -- compatible: "fsl,imx6q-pcie", "fsl,imx6sx-pcie", "fsl,imx6qp-pcie"
>> +- compatible: "fsl,imx6q-pcie", "fsl,imx6sx-pcie",
>> + "fsl,imx6qp-pcie", "fsl,imx7d-pcie"
>> - reg: base address and length of the PCIe controller
>> - interrupts: A list of interrupt outputs of the controller. Must contain an
>> entry for each entry in the interrupt-names property.
>> @@ -34,6 +35,9 @@ Additional required properties for imx6sx-pcie:
>> - clock names: Must include the following additional entries:
>> - "pcie_inbound_axi"
>>
>> +Additional required properties for imx7d-pcie:
>> +- pcie-phy-supply: Should specify the regulator supplying PCIe PHY
>> +
> This isn't a PHY regulator, but a regulator powering the GPC domain
> where the PHY is located. This should not be handled in the PCIe driver.
> See the previous discussion with i.MX6SX, that is in the same boat.
>
Good to know, thanks for clarification.
> [...]
>> static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
>> {
>> - if (imx6_pcie->variant == IMX6SX)
>> + switch (imx6_pcie->variant) {
>> + case IMX7D: {
>> + int ret;
>> + unsigned int reg, mapping;
>> + struct pcie_port *pp = &imx6_pcie->pp;
>> + struct device *dev = pp->dev;
>> +
>> + regulator_set_voltage(imx6_pcie->pcie_phy_regulator,
>> + 1000000, 1000000);
>> + ret = regulator_enable(imx6_pcie->pcie_phy_regulator);
>> + if (ret) {
>> + dev_err(dev, "failed to enable pcie regulator.\n");
>> + return;
>> + }
>> +
>> + /*
>> + * Now that PHY regulator is enabled, do sofware
>> + * power-up request by mapping PHY in A7 domain and
>> + * setting power-up request bit
>> + */
>> + regmap_read(imx6_pcie->gpc, GPC_PGC_CPU_MAPPING, &mapping);
>> + regmap_write(imx6_pcie->gpc, GPC_PGC_CPU_MAPPING,
>> + mapping | IMX7D_PCIE_PHY_A7_DOMAIN);
>> +
>> + regmap_update_bits(imx6_pcie->gpc, GPC_PU_PGC_SW_PUP_REQ,
>> + IMX7D_PCIE_PHY_SW_PUP_REQ,
>> + IMX7D_PCIE_PHY_SW_PUP_REQ);
>> + /*
>> + * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
>> + * for PUP_REQ bit to be cleared
>> + */
>> + while (!regmap_read(imx6_pcie->gpc,
>> + GPC_PU_PGC_SW_PUP_REQ, ®) &&
>> + reg & IMX7D_PCIE_PHY_SW_PUP_REQ)
>> + ;
>> + regmap_write(imx6_pcie->gpc, GPC_PGC_CPU_MAPPING, mapping);
>
> I won't allow code touching the GPC registers to sneak into the PCIe
> driver. I know this is the downstream solution, but this really needs a
> proper GPC power domain driver, instead of this hack.
>
OK, will change in v2.
> Regards,
> Lucas
>
^ permalink raw reply
* Re: [PATCH v3 19/24] media: imx: Add IC subdev drivers
From: Hans Verkuil @ 2017-01-20 14:29 UTC (permalink / raw)
To: Steve Longerbeam, robh+dt, mark.rutland, shawnguo, kernel,
fabio.estevam, linux, mchehab, nick, markus.heiser, p.zabel,
laurent.pinchart+renesas, bparrot, geert, arnd, sudipm.mukherjee,
minghsiu.tsai, tiffany.lin, jean-christophe.trotin, horms+renesas,
niklas.soderlund+renesas, robert.jarzmik, songjun.wu,
andrew-ct.chen, gregkh
Cc: devel, devicetree, Steve Longerbeam, linux-kernel,
linux-arm-kernel, linux-media
In-Reply-To: <1483755102-24785-20-git-send-email-steve_longerbeam@mentor.com>
On 01/07/2017 03:11 AM, Steve Longerbeam wrote:
> This is a set of three media entity subdevice drivers for the i.MX
> Image Converter. The i.MX IC module contains three independent
> "tasks":
>
> - Pre-processing Encode task: video frames are routed directly from
> the CSI and can be scaled, color-space converted, and rotated.
> Scaled output is limited to 1024x1024 resolution. Output frames
> are routed to the camera interface entities (camif).
>
> - Pre-processing Viewfinder task: this task can perform the same
> conversions as the pre-process encode task, but in addition can
> be used for hardware motion compensated deinterlacing. Frames can
> come either directly from the CSI or from the SMFC entities (memory
> buffers via IDMAC channels). Scaled output is limited to 1024x1024
> resolution. Output frames can be routed to various sinks including
> the post-processing task entities.
>
> - Post-processing task: same conversions as pre-process encode. However
> this entity sends frames to the i.MX IPU image converter which supports
> image tiling, which allows scaled output up to 4096x4096 resolution.
> Output frames can be routed to the camera interfaces.
>
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
> drivers/staging/media/imx/Makefile | 2 +
> drivers/staging/media/imx/imx-ic-common.c | 109 +++
> drivers/staging/media/imx/imx-ic-pp.c | 636 ++++++++++++++++
> drivers/staging/media/imx/imx-ic-prpenc.c | 1033 +++++++++++++++++++++++++
> drivers/staging/media/imx/imx-ic-prpvf.c | 1179 +++++++++++++++++++++++++++++
> drivers/staging/media/imx/imx-ic.h | 38 +
> 6 files changed, 2997 insertions(+)
> create mode 100644 drivers/staging/media/imx/imx-ic-common.c
> create mode 100644 drivers/staging/media/imx/imx-ic-pp.c
> create mode 100644 drivers/staging/media/imx/imx-ic-prpenc.c
> create mode 100644 drivers/staging/media/imx/imx-ic-prpvf.c
> create mode 100644 drivers/staging/media/imx/imx-ic.h
>
> diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
> index 3559d7b..d2a962c 100644
> --- a/drivers/staging/media/imx/Makefile
> +++ b/drivers/staging/media/imx/Makefile
> @@ -1,8 +1,10 @@
> imx-media-objs := imx-media-dev.o imx-media-fim.o imx-media-internal-sd.o \
> imx-media-of.o
> +imx-ic-objs := imx-ic-common.o imx-ic-prpenc.o imx-ic-prpvf.o imx-ic-pp.o
>
> obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media.o
> obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-common.o
> +obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-ic.o
>
> obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
> obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-smfc.o
> diff --git a/drivers/staging/media/imx/imx-ic-common.c b/drivers/staging/media/imx/imx-ic-common.c
> new file mode 100644
> index 0000000..45706ca
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-ic-common.c
> @@ -0,0 +1,109 @@
> +/*
> + * V4L2 Image Converter Subdev for Freescale i.MX5/6 SOC
> + *
> + * Copyright (c) 2014-2016 Mentor Graphics 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; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +#include "imx-media.h"
> +#include "imx-ic.h"
> +
> +static struct imx_ic_ops *ic_ops[IC_NUM_TASKS] = {
> + [IC_TASK_ENCODER] = &imx_ic_prpenc_ops,
> + [IC_TASK_VIEWFINDER] = &imx_ic_prpvf_ops,
> + [IC_TASK_POST_PROCESSOR] = &imx_ic_pp_ops,
> +};
> +
> +static int imx_ic_probe(struct platform_device *pdev)
> +{
> + struct imx_media_internal_sd_platformdata *pdata;
> + struct imx_ic_priv *priv;
> + int ret;
> +
> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, &priv->sd);
> + priv->dev = &pdev->dev;
> +
> + /* get our ipu_id, grp_id and IC task id */
> + pdata = priv->dev->platform_data;
> + priv->ipu_id = pdata->ipu_id;
> + switch (pdata->grp_id) {
> + case IMX_MEDIA_GRP_ID_IC_PRPENC:
> + priv->task_id = IC_TASK_ENCODER;
> + break;
> + case IMX_MEDIA_GRP_ID_IC_PRPVF:
> + priv->task_id = IC_TASK_VIEWFINDER;
> + break;
> + case IMX_MEDIA_GRP_ID_IC_PP0...IMX_MEDIA_GRP_ID_IC_PP3:
> + priv->task_id = IC_TASK_POST_PROCESSOR;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + v4l2_subdev_init(&priv->sd, ic_ops[priv->task_id]->subdev_ops);
> + v4l2_set_subdevdata(&priv->sd, priv);
> + priv->sd.internal_ops = ic_ops[priv->task_id]->internal_ops;
> + priv->sd.entity.ops = ic_ops[priv->task_id]->entity_ops;
> + priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
> + priv->sd.dev = &pdev->dev;
> + priv->sd.owner = THIS_MODULE;
> + priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
> + priv->sd.grp_id = pdata->grp_id;
> + strncpy(priv->sd.name, pdata->sd_name, sizeof(priv->sd.name));
> +
> + ret = ic_ops[priv->task_id]->init(priv);
> + if (ret)
> + return ret;
> +
> + ret = v4l2_async_register_subdev(&priv->sd);
> + if (ret)
> + ic_ops[priv->task_id]->remove(priv);
> +
> + return ret;
> +}
> +
> +static int imx_ic_remove(struct platform_device *pdev)
> +{
> + struct v4l2_subdev *sd = platform_get_drvdata(pdev);
> + struct imx_ic_priv *priv = container_of(sd, struct imx_ic_priv, sd);
> +
> + ic_ops[priv->task_id]->remove(priv);
> +
> + v4l2_async_unregister_subdev(&priv->sd);
> + media_entity_cleanup(&priv->sd.entity);
> + v4l2_device_unregister_subdev(sd);
> +
> + return 0;
> +}
> +
> +static const struct platform_device_id imx_ic_ids[] = {
> + { .name = "imx-ipuv3-ic" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(platform, imx_ic_ids);
> +
> +static struct platform_driver imx_ic_driver = {
> + .probe = imx_ic_probe,
> + .remove = imx_ic_remove,
> + .id_table = imx_ic_ids,
> + .driver = {
> + .name = "imx-ipuv3-ic",
> + },
> +};
> +module_platform_driver(imx_ic_driver);
> +
> +MODULE_DESCRIPTION("i.MX IC subdev driver");
> +MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:imx-ipuv3-ic");
> diff --git a/drivers/staging/media/imx/imx-ic-pp.c b/drivers/staging/media/imx/imx-ic-pp.c
> new file mode 100644
> index 0000000..1f75616
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-ic-pp.c
> @@ -0,0 +1,636 @@
> +/*
> + * V4L2 IC Post-Processor Subdev for Freescale i.MX5/6 SOC
> + *
> + * Copyright (c) 2014-2016 Mentor Graphics 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; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/timer.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-of.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include <video/imx-ipu-image-convert.h>
> +#include <media/imx.h>
> +#include "imx-media.h"
> +#include "imx-ic.h"
> +
> +#define PP_NUM_PADS 2
> +
> +struct pp_priv {
> + struct imx_media_dev *md;
> + struct imx_ic_priv *ic_priv;
> + int pp_id;
> +
> + struct ipu_soc *ipu;
> + struct ipu_image_convert_ctx *ic_ctx;
> +
> + struct media_pad pad[PP_NUM_PADS];
> + int input_pad;
> + int output_pad;
> +
> + /* our dma buffer sink ring */
> + struct imx_media_dma_buf_ring *in_ring;
> + /* the dma buffer ring we send to sink */
> + struct imx_media_dma_buf_ring *out_ring;
> + struct ipu_image_convert_run *out_run;
> +
> + struct imx_media_dma_buf *inbuf; /* last input buffer */
> +
> + bool stream_on; /* streaming is on */
> + bool stop; /* streaming is stopping */
> + spinlock_t irqlock;
> +
> + struct v4l2_subdev *src_sd;
> + struct v4l2_subdev *sink_sd;
> +
> + struct v4l2_mbus_framefmt format_mbus[PP_NUM_PADS];
> + const struct imx_media_pixfmt *cc[PP_NUM_PADS];
> +
> + /* motion select control */
> + struct v4l2_ctrl_handler ctrl_hdlr;
> + int rotation; /* degrees */
> + bool hflip;
> + bool vflip;
> +
> + /* derived from rotation, hflip, vflip controls */
> + enum ipu_rotate_mode rot_mode;
> +};
> +
> +static inline struct pp_priv *sd_to_priv(struct v4l2_subdev *sd)
> +{
> + struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
> +
> + return ic_priv->task_priv;
> +}
> +
> +static void pp_convert_complete(struct ipu_image_convert_run *run,
> + void *data)
> +{
> + struct pp_priv *priv = data;
> + struct imx_media_dma_buf *done;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&priv->irqlock, flags);
> +
> + done = imx_media_dma_buf_get_active(priv->out_ring);
> + /* give the completed buffer to the sink */
> + if (!WARN_ON(!done))
> + imx_media_dma_buf_done(done, run->status ?
> + IMX_MEDIA_BUF_STATUS_ERROR :
> + IMX_MEDIA_BUF_STATUS_DONE);
> +
> + /* we're done with the inbuf, queue it back */
> + imx_media_dma_buf_queue(priv->in_ring, priv->inbuf->index);
> +
> + spin_unlock_irqrestore(&priv->irqlock, flags);
> +}
> +
> +static void pp_queue_conversion(struct pp_priv *priv,
> + struct imx_media_dma_buf *inbuf)
> +{
> + struct ipu_image_convert_run *run;
> + struct imx_media_dma_buf *outbuf;
> +
> + /* get next queued buffer and make it active */
> + outbuf = imx_media_dma_buf_get_next_queued(priv->out_ring);
> + imx_media_dma_buf_set_active(outbuf);
> + priv->inbuf = inbuf;
> +
> + run = &priv->out_run[outbuf->index];
> + run->ctx = priv->ic_ctx;
> + run->in_phys = inbuf->phys;
> + run->out_phys = outbuf->phys;
> + ipu_image_convert_queue(run);
> +}
> +
> +static long pp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
> +{
> + struct pp_priv *priv = sd_to_priv(sd);
> + struct imx_media_dma_buf_ring **ring;
> + struct imx_media_dma_buf *buf;
> + unsigned long flags;
> +
> + switch (cmd) {
> + case IMX_MEDIA_REQ_DMA_BUF_SINK_RING:
> + /* src asks for a buffer ring */
> + if (!priv->in_ring)
> + return -EINVAL;
> + ring = (struct imx_media_dma_buf_ring **)arg;
> + *ring = priv->in_ring;
> + break;
> + case IMX_MEDIA_NEW_DMA_BUF:
> + /* src hands us a new buffer */
> + spin_lock_irqsave(&priv->irqlock, flags);
> + if (!priv->stop &&
> + !imx_media_dma_buf_get_active(priv->out_ring)) {
> + buf = imx_media_dma_buf_dequeue(priv->in_ring);
> + if (buf)
> + pp_queue_conversion(priv, buf);
> + }
> + spin_unlock_irqrestore(&priv->irqlock, flags);
> + break;
> + case IMX_MEDIA_REL_DMA_BUF_SINK_RING:
> + /* src indicates sink buffer ring can be freed */
> + if (!priv->in_ring)
> + return 0;
> + v4l2_info(sd, "%s: freeing sink ring\n", __func__);
> + imx_media_free_dma_buf_ring(priv->in_ring);
> + priv->in_ring = NULL;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int pp_start(struct pp_priv *priv)
> +{
> + struct imx_ic_priv *ic_priv = priv->ic_priv;
> + struct ipu_image image_in, image_out;
> + const struct imx_media_pixfmt *incc;
> + struct v4l2_mbus_framefmt *infmt;
> + int i, in_size, ret;
> +
> + /* ask the sink for the buffer ring */
> + ret = v4l2_subdev_call(priv->sink_sd, core, ioctl,
> + IMX_MEDIA_REQ_DMA_BUF_SINK_RING,
> + &priv->out_ring);
> + if (ret)
> + return ret;
> +
> + imx_media_mbus_fmt_to_ipu_image(&image_in,
> + &priv->format_mbus[priv->input_pad]);
> + imx_media_mbus_fmt_to_ipu_image(&image_out,
> + &priv->format_mbus[priv->output_pad]);
> +
> + priv->ipu = priv->md->ipu[ic_priv->ipu_id];
> + priv->ic_ctx = ipu_image_convert_prepare(priv->ipu,
> + IC_TASK_POST_PROCESSOR,
> + &image_in, &image_out,
> + priv->rot_mode,
> + pp_convert_complete, priv);
> + if (IS_ERR(priv->ic_ctx))
> + return PTR_ERR(priv->ic_ctx);
> +
> + infmt = &priv->format_mbus[priv->input_pad];
> + incc = priv->cc[priv->input_pad];
> + in_size = (infmt->width * incc->bpp * infmt->height) >> 3;
> +
> + if (priv->in_ring) {
> + v4l2_warn(&ic_priv->sd, "%s: dma-buf ring was not freed\n",
> + __func__);
> + imx_media_free_dma_buf_ring(priv->in_ring);
> + }
> +
> + priv->in_ring = imx_media_alloc_dma_buf_ring(priv->md,
> + &priv->src_sd->entity,
> + &ic_priv->sd.entity,
> + in_size,
> + IMX_MEDIA_MIN_RING_BUFS,
> + true);
> + if (IS_ERR(priv->in_ring)) {
> + v4l2_err(&ic_priv->sd,
> + "failed to alloc dma-buf ring\n");
> + ret = PTR_ERR(priv->in_ring);
> + priv->in_ring = NULL;
> + goto out_unprep;
> + }
> +
> + for (i = 0; i < IMX_MEDIA_MIN_RING_BUFS; i++)
> + imx_media_dma_buf_queue(priv->in_ring, i);
> +
> + priv->out_run = kzalloc(IMX_MEDIA_MAX_RING_BUFS *
> + sizeof(*priv->out_run), GFP_KERNEL);
> + if (!priv->out_run) {
> + v4l2_err(&ic_priv->sd, "failed to alloc src ring runs\n");
> + ret = -ENOMEM;
> + goto out_free_ring;
> + }
> +
> + priv->stop = false;
> +
> + return 0;
> +
> +out_free_ring:
> + imx_media_free_dma_buf_ring(priv->in_ring);
> + priv->in_ring = NULL;
> +out_unprep:
> + ipu_image_convert_unprepare(priv->ic_ctx);
> + return ret;
> +}
> +
> +static void pp_stop(struct pp_priv *priv)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&priv->irqlock, flags);
> + priv->stop = true;
> + spin_unlock_irqrestore(&priv->irqlock, flags);
> +
> + ipu_image_convert_unprepare(priv->ic_ctx);
> + kfree(priv->out_run);
> +
> + priv->out_ring = NULL;
> +
> + /* inform sink that its sink buffer ring can now be freed */
> + v4l2_subdev_call(priv->sink_sd, core, ioctl,
> + IMX_MEDIA_REL_DMA_BUF_SINK_RING, 0);
> +}
> +
> +static int pp_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> + struct pp_priv *priv = sd_to_priv(sd);
> + int ret = 0;
> +
> + if (!priv->src_sd || !priv->sink_sd)
> + return -EPIPE;
> +
> + v4l2_info(sd, "stream %s\n", enable ? "ON" : "OFF");
> +
> + if (enable && !priv->stream_on)
> + ret = pp_start(priv);
> + else if (!enable && priv->stream_on)
> + pp_stop(priv);
> +
> + if (!ret)
> + priv->stream_on = enable;
> + return ret;
> +}
> +
> +static int pp_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + const struct imx_media_pixfmt *cc;
> + u32 fourcc;
> + int ret;
> +
> + if (code->pad >= PP_NUM_PADS)
> + return -EINVAL;
> +
> + ret = ipu_image_convert_enum_format(code->index, &fourcc);
> + if (ret)
> + return ret;
> +
> + /* convert returned fourcc to mbus code */
> + cc = imx_media_find_format(fourcc, 0, true, true);
> + if (WARN_ON(!cc))
> + return -EINVAL;
> +
> + code->code = cc->codes[0];
> + return 0;
> +}
> +
> +static int pp_get_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *sdformat)
> +{
> + struct pp_priv *priv = sd_to_priv(sd);
> +
> + if (sdformat->pad >= PP_NUM_PADS)
> + return -EINVAL;
> +
> + sdformat->format = priv->format_mbus[sdformat->pad];
> +
> + return 0;
> +}
> +
> +static int pp_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *sdformat)
> +{
> + struct pp_priv *priv = sd_to_priv(sd);
> + struct v4l2_mbus_framefmt *infmt, *outfmt;
> + const struct imx_media_pixfmt *cc;
> + struct ipu_image test_in, test_out;
> + u32 code;
> +
> + if (sdformat->pad >= PP_NUM_PADS)
> + return -EINVAL;
> +
> + if (priv->stream_on)
> + return -EBUSY;
> +
> + infmt = &priv->format_mbus[priv->input_pad];
> + outfmt = &priv->format_mbus[priv->output_pad];
> +
> + cc = imx_media_find_format(0, sdformat->format.code, true, true);
> + if (!cc) {
> + imx_media_enum_format(&code, 0, true, true);
> + cc = imx_media_find_format(0, code, true, true);
> + sdformat->format.code = cc->codes[0];
> + }
> +
> + if (sdformat->pad == priv->output_pad) {
> + imx_media_mbus_fmt_to_ipu_image(&test_out, &sdformat->format);
> + imx_media_mbus_fmt_to_ipu_image(&test_in, infmt);
> + ipu_image_convert_adjust(&test_in, &test_out, priv->rot_mode);
> + imx_media_ipu_image_to_mbus_fmt(&sdformat->format, &test_out);
> + } else {
> + imx_media_mbus_fmt_to_ipu_image(&test_in, &sdformat->format);
> + imx_media_mbus_fmt_to_ipu_image(&test_out, outfmt);
> + ipu_image_convert_adjust(&test_in, &test_out, priv->rot_mode);
> + imx_media_ipu_image_to_mbus_fmt(&sdformat->format, &test_in);
> + }
> +
> + if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) {
> + cfg->try_fmt = sdformat->format;
> + } else {
> + if (sdformat->pad == priv->output_pad) {
> + *outfmt = sdformat->format;
> + imx_media_ipu_image_to_mbus_fmt(infmt, &test_in);
> + } else {
> + *infmt = sdformat->format;
> + imx_media_ipu_image_to_mbus_fmt(outfmt, &test_out);
> + }
> + priv->cc[sdformat->pad] = cc;
> + }
> +
> + return 0;
> +}
> +
> +static int pp_link_setup(struct media_entity *entity,
> + const struct media_pad *local,
> + const struct media_pad *remote, u32 flags)
> +{
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> + struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
> + struct pp_priv *priv = ic_priv->task_priv;
> + struct v4l2_subdev *remote_sd;
> +
> + dev_dbg(ic_priv->dev, "link setup %s -> %s", remote->entity->name,
> + local->entity->name);
> +
> + remote_sd = media_entity_to_v4l2_subdev(remote->entity);
> +
> + if (local->flags & MEDIA_PAD_FL_SOURCE) {
> + if (flags & MEDIA_LNK_FL_ENABLED) {
> + if (priv->sink_sd)
> + return -EBUSY;
> + priv->sink_sd = remote_sd;
> + } else {
> + priv->sink_sd = NULL;
> + }
> + } else {
> + if (flags & MEDIA_LNK_FL_ENABLED) {
> + if (priv->src_sd)
> + return -EBUSY;
> + priv->src_sd = remote_sd;
> + } else {
> + priv->src_sd = NULL;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int pp_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct pp_priv *priv = container_of(ctrl->handler,
> + struct pp_priv, ctrl_hdlr);
> + struct imx_ic_priv *ic_priv = priv->ic_priv;
> + enum ipu_rotate_mode rot_mode;
> + bool hflip, vflip;
> + int rotation, ret;
> +
> + rotation = priv->rotation;
> + hflip = priv->hflip;
> + vflip = priv->vflip;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_HFLIP:
> + hflip = (ctrl->val == 1);
> + break;
> + case V4L2_CID_VFLIP:
> + vflip = (ctrl->val == 1);
> + break;
> + case V4L2_CID_ROTATE:
> + rotation = ctrl->val;
> + break;
> + default:
> + v4l2_err(&ic_priv->sd, "Invalid control\n");
> + return -EINVAL;
> + }
> +
> + ret = ipu_degrees_to_rot_mode(&rot_mode, rotation, hflip, vflip);
> + if (ret)
> + return ret;
> +
> + if (rot_mode != priv->rot_mode) {
> + struct v4l2_mbus_framefmt *infmt, *outfmt;
> + struct ipu_image test_in, test_out;
> +
> + /* can't change rotation mid-streaming */
> + if (priv->stream_on)
> + return -EBUSY;
> +
> + /*
> + * make sure this rotation will work with current input/output
> + * formats before setting
> + */
> + infmt = &priv->format_mbus[priv->input_pad];
> + outfmt = &priv->format_mbus[priv->output_pad];
> + imx_media_mbus_fmt_to_ipu_image(&test_in, infmt);
> + imx_media_mbus_fmt_to_ipu_image(&test_out, outfmt);
> +
> + ret = ipu_image_convert_verify(&test_in, &test_out, rot_mode);
> + if (ret)
> + return ret;
> +
> + priv->rot_mode = rot_mode;
> + priv->rotation = rotation;
> + priv->hflip = hflip;
> + priv->vflip = vflip;
> + }
> +
> + return 0;
> +}
> +
> +static const struct v4l2_ctrl_ops pp_ctrl_ops = {
> + .s_ctrl = pp_s_ctrl,
> +};
> +
> +static const struct v4l2_ctrl_config pp_std_ctrl[] = {
> + {
> + .id = V4L2_CID_HFLIP,
> + .name = "Horizontal Flip",
> + .type = V4L2_CTRL_TYPE_BOOLEAN,
> + .def = 0,
> + .min = 0,
> + .max = 1,
> + .step = 1,
> + }, {
> + .id = V4L2_CID_VFLIP,
> + .name = "Vertical Flip",
> + .type = V4L2_CTRL_TYPE_BOOLEAN,
> + .def = 0,
> + .min = 0,
> + .max = 1,
> + .step = 1,
> + }, {
> + .id = V4L2_CID_ROTATE,
> + .name = "Rotation",
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + .def = 0,
> + .min = 0,
> + .max = 270,
> + .step = 90,
> + },
> +};
> +
> +#define PP_NUM_CONTROLS ARRAY_SIZE(pp_std_ctrl)
> +
> +static int pp_init_controls(struct pp_priv *priv)
> +{
> + struct imx_ic_priv *ic_priv = priv->ic_priv;
> + struct v4l2_ctrl_handler *hdlr = &priv->ctrl_hdlr;
> + const struct v4l2_ctrl_config *c;
> + int i, ret;
> +
> + v4l2_ctrl_handler_init(hdlr, PP_NUM_CONTROLS);
> +
> + for (i = 0; i < PP_NUM_CONTROLS; i++) {
> + c = &pp_std_ctrl[i];
> + v4l2_ctrl_new_std(hdlr, &pp_ctrl_ops,
> + c->id, c->min, c->max, c->step, c->def);
> + }
> +
> + ic_priv->sd.ctrl_handler = hdlr;
> +
> + if (hdlr->error) {
> + ret = hdlr->error;
> + v4l2_ctrl_handler_free(hdlr);
> + return ret;
> + }
> +
> + v4l2_ctrl_handler_setup(hdlr);
> +
> + return 0;
> +}
> +
> +/*
> + * retrieve our pads parsed from the OF graph by the media device
> + */
> +static int pp_registered(struct v4l2_subdev *sd)
> +{
> + struct pp_priv *priv = sd_to_priv(sd);
> + struct imx_media_subdev *imxsd;
> + struct imx_media_pad *pad;
> + int i, ret;
> +
> + /* get media device */
> + priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
> +
> + imxsd = imx_media_find_subdev_by_sd(priv->md, sd);
> + if (IS_ERR(imxsd))
> + return PTR_ERR(imxsd);
> +
> + if (imxsd->num_sink_pads != 1 || imxsd->num_src_pads != 1)
> + return -EINVAL;
> +
> + for (i = 0; i < PP_NUM_PADS; i++) {
> + pad = &imxsd->pad[i];
> + priv->pad[i] = pad->pad;
> + if (priv->pad[i].flags & MEDIA_PAD_FL_SINK)
> + priv->input_pad = i;
> + else
> + priv->output_pad = i;
> +
> + /* set a default mbus format */
> + ret = imx_media_init_mbus_fmt(&priv->format_mbus[i],
> + 640, 480, 0, V4L2_FIELD_NONE,
> + &priv->cc[i]);
> + if (ret)
> + return ret;
> + }
> +
> + ret = pp_init_controls(priv);
> + if (ret)
> + return ret;
> +
> + ret = media_entity_pads_init(&sd->entity, PP_NUM_PADS, priv->pad);
> + if (ret)
> + goto free_ctrls;
> +
> + return 0;
> +free_ctrls:
> + v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
> + return ret;
> +}
> +
> +static struct v4l2_subdev_pad_ops pp_pad_ops = {
> + .enum_mbus_code = pp_enum_mbus_code,
> + .get_fmt = pp_get_fmt,
> + .set_fmt = pp_set_fmt,
> +};
> +
> +static struct v4l2_subdev_video_ops pp_video_ops = {
> + .s_stream = pp_s_stream,
> +};
> +
> +static struct v4l2_subdev_core_ops pp_core_ops = {
> + .ioctl = pp_ioctl,
> +};
> +
> +static struct media_entity_operations pp_entity_ops = {
> + .link_setup = pp_link_setup,
> + .link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static struct v4l2_subdev_ops pp_subdev_ops = {
> + .video = &pp_video_ops,
> + .pad = &pp_pad_ops,
> + .core = &pp_core_ops,
> +};
> +
> +static struct v4l2_subdev_internal_ops pp_internal_ops = {
> + .registered = pp_registered,
> +};
> +
> +static int pp_init(struct imx_ic_priv *ic_priv)
> +{
> + struct pp_priv *priv;
> +
> + priv = devm_kzalloc(ic_priv->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + ic_priv->task_priv = priv;
> + priv->ic_priv = ic_priv;
> + spin_lock_init(&priv->irqlock);
> +
> + /* get our PP id */
> + priv->pp_id = (ic_priv->sd.grp_id >> IMX_MEDIA_GRP_ID_IC_PP_BIT) - 1;
> +
> + return 0;
> +}
> +
> +static void pp_remove(struct imx_ic_priv *ic_priv)
> +{
> + struct pp_priv *priv = ic_priv->task_priv;
> +
> + v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
> +}
> +
> +struct imx_ic_ops imx_ic_pp_ops = {
> + .subdev_ops = &pp_subdev_ops,
> + .internal_ops = &pp_internal_ops,
> + .entity_ops = &pp_entity_ops,
> + .init = pp_init,
> + .remove = pp_remove,
> +};
> diff --git a/drivers/staging/media/imx/imx-ic-prpenc.c b/drivers/staging/media/imx/imx-ic-prpenc.c
> new file mode 100644
> index 0000000..3d85a82
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-ic-prpenc.c
> @@ -0,0 +1,1033 @@
> +/*
> + * V4L2 Capture IC Encoder Subdev for Freescale i.MX5/6 SOC
> + *
> + * This subdevice handles capture of video frames from the CSI, which
> + * are routed directly to the Image Converter preprocess encode task,
> + * for resizing, colorspace conversion, and rotation.
> + *
> + * Copyright (c) 2012-2016 Mentor Graphics 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; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/timer.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-of.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include <media/imx.h>
> +#include "imx-media.h"
> +#include "imx-ic.h"
> +
> +#define PRPENC_NUM_PADS 2
> +
> +#define MAX_W_IC 1024
> +#define MAX_H_IC 1024
> +#define MAX_W_SINK 4096
> +#define MAX_H_SINK 4096
> +
> +struct prpenc_priv {
> + struct imx_media_dev *md;
> + struct imx_ic_priv *ic_priv;
> +
> + /* IPU units we require */
> + struct ipu_soc *ipu;
> + struct ipu_ic *ic_enc;
> +
> + struct media_pad pad[PRPENC_NUM_PADS];
> + int input_pad;
> + int output_pad;
> +
> + struct ipuv3_channel *enc_ch;
> + struct ipuv3_channel *enc_rot_in_ch;
> + struct ipuv3_channel *enc_rot_out_ch;
> +
> + /* the dma buffer ring to send to sink */
> + struct imx_media_dma_buf_ring *out_ring;
> + struct imx_media_dma_buf *next;
> +
> + int ipu_buf_num; /* ipu double buffer index: 0-1 */
> +
> + struct v4l2_subdev *src_sd;
> + struct v4l2_subdev *sink_sd;
> +
> + /* the CSI id at link validate */
> + int csi_id;
> +
> + /* the attached sensor at stream on */
> + struct imx_media_subdev *sensor;
> +
> + struct v4l2_mbus_framefmt format_mbus[PRPENC_NUM_PADS];
> + const struct imx_media_pixfmt *cc[PRPENC_NUM_PADS];
> +
> + struct imx_media_dma_buf rot_buf[2];
> +
> + /* controls */
> + struct v4l2_ctrl_handler ctrl_hdlr;
> + int rotation; /* degrees */
> + bool hflip;
> + bool vflip;
> +
> + /* derived from rotation, hflip, vflip controls */
> + enum ipu_rotate_mode rot_mode;
> +
> + spinlock_t irqlock;
> +
> + struct timer_list eof_timeout_timer;
> + int eof_irq;
> + int nfb4eof_irq;
> +
> + bool stream_on; /* streaming is on */
> + bool last_eof; /* waiting for last EOF at stream off */
> + struct completion last_eof_comp;
> +};
> +
> +static inline struct prpenc_priv *sd_to_priv(struct v4l2_subdev *sd)
> +{
> + struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
> +
> + return ic_priv->task_priv;
> +}
> +
> +static void prpenc_put_ipu_resources(struct prpenc_priv *priv)
> +{
> + if (!IS_ERR_OR_NULL(priv->ic_enc))
> + ipu_ic_put(priv->ic_enc);
> + priv->ic_enc = NULL;
> +
> + if (!IS_ERR_OR_NULL(priv->enc_ch))
> + ipu_idmac_put(priv->enc_ch);
> + priv->enc_ch = NULL;
> +
> + if (!IS_ERR_OR_NULL(priv->enc_rot_in_ch))
> + ipu_idmac_put(priv->enc_rot_in_ch);
> + priv->enc_rot_in_ch = NULL;
> +
> + if (!IS_ERR_OR_NULL(priv->enc_rot_out_ch))
> + ipu_idmac_put(priv->enc_rot_out_ch);
> + priv->enc_rot_out_ch = NULL;
> +}
> +
> +static int prpenc_get_ipu_resources(struct prpenc_priv *priv)
> +{
> + struct imx_ic_priv *ic_priv = priv->ic_priv;
> + int ret;
> +
> + priv->ipu = priv->md->ipu[ic_priv->ipu_id];
> +
> + priv->ic_enc = ipu_ic_get(priv->ipu, IC_TASK_ENCODER);
> + if (IS_ERR(priv->ic_enc)) {
> + v4l2_err(&ic_priv->sd, "failed to get IC ENC\n");
> + ret = PTR_ERR(priv->ic_enc);
> + goto out;
> + }
> +
> + priv->enc_ch = ipu_idmac_get(priv->ipu,
> + IPUV3_CHANNEL_IC_PRP_ENC_MEM);
> + if (IS_ERR(priv->enc_ch)) {
> + v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n",
> + IPUV3_CHANNEL_IC_PRP_ENC_MEM);
> + ret = PTR_ERR(priv->enc_ch);
> + goto out;
> + }
> +
> + priv->enc_rot_in_ch = ipu_idmac_get(priv->ipu,
> + IPUV3_CHANNEL_MEM_ROT_ENC);
> + if (IS_ERR(priv->enc_rot_in_ch)) {
> + v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n",
> + IPUV3_CHANNEL_MEM_ROT_ENC);
> + ret = PTR_ERR(priv->enc_rot_in_ch);
> + goto out;
> + }
> +
> + priv->enc_rot_out_ch = ipu_idmac_get(priv->ipu,
> + IPUV3_CHANNEL_ROT_ENC_MEM);
> + if (IS_ERR(priv->enc_rot_out_ch)) {
> + v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n",
> + IPUV3_CHANNEL_ROT_ENC_MEM);
> + ret = PTR_ERR(priv->enc_rot_out_ch);
> + goto out;
> + }
> +
> + return 0;
> +out:
> + prpenc_put_ipu_resources(priv);
> + return ret;
> +}
> +
> +static irqreturn_t prpenc_eof_interrupt(int irq, void *dev_id)
> +{
> + struct prpenc_priv *priv = dev_id;
> + struct imx_media_dma_buf *done, *next;
> + struct ipuv3_channel *channel;
> +
> + spin_lock(&priv->irqlock);
> +
> + if (priv->last_eof) {
> + complete(&priv->last_eof_comp);
> + priv->last_eof = false;
> + goto unlock;
> + }
> +
> + /* inform CSI of this EOF so it can monitor frame intervals */
> + v4l2_subdev_call(priv->src_sd, core, interrupt_service_routine,
> + 0, NULL);
> +
> + channel = (ipu_rot_mode_is_irt(priv->rot_mode)) ?
> + priv->enc_rot_out_ch : priv->enc_ch;
> +
> + done = imx_media_dma_buf_get_active(priv->out_ring);
> + /* give the completed buffer to the sink */
> + if (!WARN_ON(!done))
> + imx_media_dma_buf_done(done, IMX_MEDIA_BUF_STATUS_DONE);
> +
> + /* priv->next buffer is now the active one */
> + imx_media_dma_buf_set_active(priv->next);
> +
> + /* bump the EOF timeout timer */
> + mod_timer(&priv->eof_timeout_timer,
> + jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
> +
> + if (ipu_idmac_buffer_is_ready(channel, priv->ipu_buf_num))
> + ipu_idmac_clear_buffer(channel, priv->ipu_buf_num);
> +
> + /* get next queued buffer */
> + next = imx_media_dma_buf_get_next_queued(priv->out_ring);
> +
> + ipu_cpmem_set_buffer(channel, priv->ipu_buf_num, next->phys);
> + ipu_idmac_select_buffer(channel, priv->ipu_buf_num);
> +
> + /* toggle IPU double-buffer index */
> + priv->ipu_buf_num ^= 1;
> + priv->next = next;
> +
> +unlock:
> + spin_unlock(&priv->irqlock);
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t prpenc_nfb4eof_interrupt(int irq, void *dev_id)
> +{
> + struct prpenc_priv *priv = dev_id;
> + struct imx_ic_priv *ic_priv = priv->ic_priv;
> + static const struct v4l2_event ev = {
> + .type = V4L2_EVENT_IMX_NFB4EOF,
> + };
> +
> + v4l2_err(&ic_priv->sd, "NFB4EOF\n");
> +
> + v4l2_subdev_notify_event(&ic_priv->sd, &ev);
> +
> + return IRQ_HANDLED;
> +}
> +
> +/*
> + * EOF timeout timer function.
> + */
> +static void prpenc_eof_timeout(unsigned long data)
> +{
> + struct prpenc_priv *priv = (struct prpenc_priv *)data;
> + struct imx_ic_priv *ic_priv = priv->ic_priv;
> + static const struct v4l2_event ev = {
> + .type = V4L2_EVENT_IMX_EOF_TIMEOUT,
> + };
> +
> + v4l2_err(&ic_priv->sd, "EOF timeout\n");
> +
> + v4l2_subdev_notify_event(&ic_priv->sd, &ev);
> +}
> +
> +static void prpenc_setup_channel(struct prpenc_priv *priv,
> + struct ipuv3_channel *channel,
> + enum ipu_rotate_mode rot_mode,
> + dma_addr_t addr0, dma_addr_t addr1,
> + bool rot_swap_width_height)
> +{
> + struct v4l2_mbus_framefmt *infmt, *outfmt;
> + unsigned int burst_size;
> + struct ipu_image image;
> +
> + infmt = &priv->format_mbus[priv->input_pad];
> + outfmt = &priv->format_mbus[priv->output_pad];
> +
> + if (rot_swap_width_height)
> + swap(outfmt->width, outfmt->height);
> +
> + ipu_cpmem_zero(channel);
> +
> + imx_media_mbus_fmt_to_ipu_image(&image, outfmt);
> +
> + image.phys0 = addr0;
> + image.phys1 = addr1;
> + ipu_cpmem_set_image(channel, &image);
> +
> + if (channel == priv->enc_rot_in_ch ||
> + channel == priv->enc_rot_out_ch) {
> + burst_size = 8;
> + ipu_cpmem_set_block_mode(channel);
> + } else {
> + burst_size = (outfmt->width & 0xf) ? 8 : 16;
> + }
> +
> + ipu_cpmem_set_burstsize(channel, burst_size);
> +
> + if (rot_mode)
> + ipu_cpmem_set_rotation(channel, rot_mode);
> +
> + if (outfmt->field == V4L2_FIELD_NONE &&
> + (V4L2_FIELD_HAS_BOTH(infmt->field) ||
> + infmt->field == V4L2_FIELD_ALTERNATE) &&
> + channel == priv->enc_ch)
> + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline);
> +
> + ipu_ic_task_idma_init(priv->ic_enc, channel,
> + outfmt->width, outfmt->height,
> + burst_size, rot_mode);
> + ipu_cpmem_set_axi_id(channel, 1);
> +
> + ipu_idmac_set_double_buffer(channel, true);
> +
> + if (rot_swap_width_height)
> + swap(outfmt->width, outfmt->height);
> +}
> +
> +static int prpenc_setup_rotation(struct prpenc_priv *priv)
> +{
> + struct imx_ic_priv *ic_priv = priv->ic_priv;
> + struct v4l2_mbus_framefmt *infmt, *outfmt;
> + const struct imx_media_pixfmt *outcc, *incc;
> + struct imx_media_dma_buf *buf0, *buf1;
> + int out_size, ret;
> +
> + infmt = &priv->format_mbus[priv->input_pad];
> + outfmt = &priv->format_mbus[priv->output_pad];
> + incc = priv->cc[priv->input_pad];
> + outcc = priv->cc[priv->output_pad];
> +
> + out_size = (outfmt->width * outcc->bpp * outfmt->height) >> 3;
> +
> + ret = imx_media_alloc_dma_buf(priv->md, &priv->rot_buf[0], out_size);
> + if (ret) {
> + v4l2_err(&ic_priv->sd, "failed to alloc rot_buf[0], %d\n", ret);
> + return ret;
> + }
> + ret = imx_media_alloc_dma_buf(priv->md, &priv->rot_buf[1], out_size);
> + if (ret) {
> + v4l2_err(&ic_priv->sd, "failed to alloc rot_buf[1], %d\n", ret);
> + goto free_rot0;
> + }
> +
> + ret = ipu_ic_task_init(priv->ic_enc,
> + infmt->width, infmt->height,
> + outfmt->height, outfmt->width,
> + incc->cs, outcc->cs);
> + if (ret) {
> + v4l2_err(&ic_priv->sd, "ipu_ic_task_init failed, %d\n", ret);
> + goto free_rot1;
> + }
> +
> + /* init the IC ENC-->MEM IDMAC channel */
> + prpenc_setup_channel(priv, priv->enc_ch,
> + IPU_ROTATE_NONE,
> + priv->rot_buf[0].phys,
> + priv->rot_buf[1].phys,
> + true);
> +
> + /* init the MEM-->IC ENC ROT IDMAC channel */
> + prpenc_setup_channel(priv, priv->enc_rot_in_ch,
> + priv->rot_mode,
> + priv->rot_buf[0].phys,
> + priv->rot_buf[1].phys,
> + true);
> +
> + buf0 = imx_media_dma_buf_get_next_queued(priv->out_ring);
> + imx_media_dma_buf_set_active(buf0);
> + buf1 = imx_media_dma_buf_get_next_queued(priv->out_ring);
> + priv->next = buf1;
> +
> + /* init the destination IC ENC ROT-->MEM IDMAC channel */
> + prpenc_setup_channel(priv, priv->enc_rot_out_ch,
> + IPU_ROTATE_NONE,
> + buf0->phys, buf1->phys,
> + false);
> +
> + /* now link IC ENC-->MEM to MEM-->IC ENC ROT */
> + ipu_idmac_link(priv->enc_ch, priv->enc_rot_in_ch);
> +
> + /* enable the IC */
> + ipu_ic_enable(priv->ic_enc);
> +
> + /* set buffers ready */
> + ipu_idmac_select_buffer(priv->enc_ch, 0);
> + ipu_idmac_select_buffer(priv->enc_ch, 1);
> + ipu_idmac_select_buffer(priv->enc_rot_out_ch, 0);
> + ipu_idmac_select_buffer(priv->enc_rot_out_ch, 1);
> +
> + /* enable the channels */
> + ipu_idmac_enable_channel(priv->enc_ch);
> + ipu_idmac_enable_channel(priv->enc_rot_in_ch);
> + ipu_idmac_enable_channel(priv->enc_rot_out_ch);
> +
> + /* and finally enable the IC PRPENC task */
> + ipu_ic_task_enable(priv->ic_enc);
> +
> + return 0;
> +
> +free_rot1:
> + imx_media_free_dma_buf(priv->md, &priv->rot_buf[1]);
> +free_rot0:
> + imx_media_free_dma_buf(priv->md, &priv->rot_buf[0]);
> + return ret;
> +}
> +
> +static void prpenc_unsetup_rotation(struct prpenc_priv *priv)
> +{
> + ipu_ic_task_disable(priv->ic_enc);
> +
> + ipu_idmac_disable_channel(priv->enc_ch);
> + ipu_idmac_disable_channel(priv->enc_rot_in_ch);
> + ipu_idmac_disable_channel(priv->enc_rot_out_ch);
> +
> + ipu_idmac_unlink(priv->enc_ch, priv->enc_rot_in_ch);
> +
> + ipu_ic_disable(priv->ic_enc);
> +
> + imx_media_free_dma_buf(priv->md, &priv->rot_buf[0]);
> + imx_media_free_dma_buf(priv->md, &priv->rot_buf[1]);
> +}
> +
> +static int prpenc_setup_norotation(struct prpenc_priv *priv)
> +{
> + struct imx_ic_priv *ic_priv = priv->ic_priv;
> + struct v4l2_mbus_framefmt *infmt, *outfmt;
> + const struct imx_media_pixfmt *outcc, *incc;
> + struct imx_media_dma_buf *buf0, *buf1;
> + int ret;
> +
> + infmt = &priv->format_mbus[priv->input_pad];
> + outfmt = &priv->format_mbus[priv->output_pad];
> + incc = priv->cc[priv->input_pad];
> + outcc = priv->cc[priv->output_pad];
> +
> + ret = ipu_ic_task_init(priv->ic_enc,
> + infmt->width, infmt->height,
> + outfmt->width, outfmt->height,
> + incc->cs, outcc->cs);
> + if (ret) {
> + v4l2_err(&ic_priv->sd, "ipu_ic_task_init failed, %d\n", ret);
> + return ret;
> + }
> +
> + buf0 = imx_media_dma_buf_get_next_queued(priv->out_ring);
> + imx_media_dma_buf_set_active(buf0);
> + buf1 = imx_media_dma_buf_get_next_queued(priv->out_ring);
> + priv->next = buf1;
> +
> + /* init the IC PRP-->MEM IDMAC channel */
> + prpenc_setup_channel(priv, priv->enc_ch, priv->rot_mode,
> + buf0->phys, buf1->phys,
> + false);
> +
> + ipu_cpmem_dump(priv->enc_ch);
> + ipu_ic_dump(priv->ic_enc);
> + ipu_dump(priv->ipu);
> +
> + ipu_ic_enable(priv->ic_enc);
> +
> + /* set buffers ready */
> + ipu_idmac_select_buffer(priv->enc_ch, 0);
> + ipu_idmac_select_buffer(priv->enc_ch, 1);
> +
> + /* enable the channels */
> + ipu_idmac_enable_channel(priv->enc_ch);
> +
> + /* enable the IC ENCODE task */
> + ipu_ic_task_enable(priv->ic_enc);
> +
> + return 0;
> +}
> +
> +static void prpenc_unsetup_norotation(struct prpenc_priv *priv)
> +{
> + ipu_ic_task_disable(priv->ic_enc);
> + ipu_idmac_disable_channel(priv->enc_ch);
> + ipu_ic_disable(priv->ic_enc);
> +}
> +
> +static int prpenc_start(struct prpenc_priv *priv)
> +{
> + struct imx_ic_priv *ic_priv = priv->ic_priv;
> + int ret;
> +
> + if (!priv->sensor) {
> + v4l2_err(&ic_priv->sd, "no sensor attached\n");
> + return -EINVAL;
> + }
> +
> + ret = prpenc_get_ipu_resources(priv);
> + if (ret)
> + return ret;
> +
> + /* set IC to receive from CSI */
> + ipu_set_ic_src_mux(priv->ipu, priv->csi_id, false);
> +
> + /* ask the sink for the buffer ring */
> + ret = v4l2_subdev_call(priv->sink_sd, core, ioctl,
> + IMX_MEDIA_REQ_DMA_BUF_SINK_RING,
> + &priv->out_ring);
> + if (ret)
> + goto out_put_ipu;
> +
> + priv->ipu_buf_num = 0;
> +
> + /* init EOF completion waitq */
> + init_completion(&priv->last_eof_comp);
> + priv->last_eof = false;
> +
> + if (ipu_rot_mode_is_irt(priv->rot_mode))
> + ret = prpenc_setup_rotation(priv);
> + else
> + ret = prpenc_setup_norotation(priv);
> + if (ret)
> + goto out_put_ipu;
> +
> + priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu,
> + priv->enc_ch,
> + IPU_IRQ_NFB4EOF);
> + ret = devm_request_irq(ic_priv->dev, priv->nfb4eof_irq,
> + prpenc_nfb4eof_interrupt, 0,
> + "imx-ic-prpenc-nfb4eof", priv);
> + if (ret) {
> + v4l2_err(&ic_priv->sd,
> + "Error registering NFB4EOF irq: %d\n", ret);
> + goto out_unsetup;
> + }
> +
> + if (ipu_rot_mode_is_irt(priv->rot_mode))
> + priv->eof_irq = ipu_idmac_channel_irq(
> + priv->ipu, priv->enc_rot_out_ch, IPU_IRQ_EOF);
> + else
> + priv->eof_irq = ipu_idmac_channel_irq(
> + priv->ipu, priv->enc_ch, IPU_IRQ_EOF);
> +
> + ret = devm_request_irq(ic_priv->dev, priv->eof_irq,
> + prpenc_eof_interrupt, 0,
> + "imx-ic-prpenc-eof", priv);
> + if (ret) {
> + v4l2_err(&ic_priv->sd,
> + "Error registering eof irq: %d\n", ret);
> + goto out_free_nfb4eof_irq;
> + }
> +
> + /* start the EOF timeout timer */
> + mod_timer(&priv->eof_timeout_timer,
> + jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
> +
> + return 0;
> +
> +out_free_nfb4eof_irq:
> + devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv);
> +out_unsetup:
> + if (ipu_rot_mode_is_irt(priv->rot_mode))
> + prpenc_unsetup_rotation(priv);
> + else
> + prpenc_unsetup_norotation(priv);
> +out_put_ipu:
> + prpenc_put_ipu_resources(priv);
> + return ret;
> +}
> +
> +static void prpenc_stop(struct prpenc_priv *priv)
> +{
> + struct imx_ic_priv *ic_priv = priv->ic_priv;
> + unsigned long flags;
> + int ret;
> +
> + /* mark next EOF interrupt as the last before stream off */
> + spin_lock_irqsave(&priv->irqlock, flags);
> + priv->last_eof = true;
> + spin_unlock_irqrestore(&priv->irqlock, flags);
> +
> + /*
> + * and then wait for interrupt handler to mark completion.
> + */
> + ret = wait_for_completion_timeout(
> + &priv->last_eof_comp,
> + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
> + if (ret == 0)
> + v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n");
> +
> + devm_free_irq(ic_priv->dev, priv->eof_irq, priv);
> + devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv);
> +
> + if (ipu_rot_mode_is_irt(priv->rot_mode))
> + prpenc_unsetup_rotation(priv);
> + else
> + prpenc_unsetup_norotation(priv);
> +
> + prpenc_put_ipu_resources(priv);
> +
> + /* cancel the EOF timeout timer */
> + del_timer_sync(&priv->eof_timeout_timer);
> +
> + priv->out_ring = NULL;
> +
> + /* inform sink that the buffer ring can now be freed */
> + v4l2_subdev_call(priv->sink_sd, core, ioctl,
> + IMX_MEDIA_REL_DMA_BUF_SINK_RING, 0);
> +}
> +
> +static int prpenc_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + struct prpenc_priv *priv = sd_to_priv(sd);
> + bool allow_planar;
> +
> + if (code->pad >= PRPENC_NUM_PADS)
> + return -EINVAL;
> +
> + allow_planar = (code->pad == priv->output_pad);
> +
> + return imx_media_enum_format(&code->code, code->index,
> + true, allow_planar);
> +}
> +
> +static int prpenc_get_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *sdformat)
> +{
> + struct prpenc_priv *priv = sd_to_priv(sd);
> +
> + if (sdformat->pad >= PRPENC_NUM_PADS)
> + return -EINVAL;
> +
> + sdformat->format = priv->format_mbus[sdformat->pad];
> +
> + return 0;
> +}
> +
> +static int prpenc_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *sdformat)
> +{
> + struct prpenc_priv *priv = sd_to_priv(sd);
> + struct v4l2_mbus_framefmt *infmt, *outfmt;
> + const struct imx_media_pixfmt *cc;
> + bool allow_planar;
> + u32 code;
> +
> + if (sdformat->pad >= PRPENC_NUM_PADS)
> + return -EINVAL;
> +
> + if (priv->stream_on)
> + return -EBUSY;
> +
> + infmt = &priv->format_mbus[priv->input_pad];
> + outfmt = &priv->format_mbus[priv->output_pad];
> + allow_planar = (sdformat->pad == priv->output_pad);
> +
> + cc = imx_media_find_format(0, sdformat->format.code,
> + true, allow_planar);
> + if (!cc) {
> + imx_media_enum_format(&code, 0, true, false);
> + cc = imx_media_find_format(0, code, true, false);
> + sdformat->format.code = cc->codes[0];
> + }
> +
> + if (sdformat->pad == priv->output_pad) {
> + sdformat->format.width = min_t(__u32,
> + sdformat->format.width,
> + MAX_W_IC);
> + sdformat->format.height = min_t(__u32,
> + sdformat->format.height,
> + MAX_H_IC);
> +
> + if (sdformat->format.field != V4L2_FIELD_NONE)
> + sdformat->format.field = infmt->field;
> +
> + /* IC resizer cannot downsize more than 4:1 */
> + if (ipu_rot_mode_is_irt(priv->rot_mode)) {
> + sdformat->format.width = max_t(__u32,
> + sdformat->format.width,
> + infmt->height / 4);
> + sdformat->format.height = max_t(__u32,
> + sdformat->format.height,
> + infmt->width / 4);
> + } else {
> + sdformat->format.width = max_t(__u32,
> + sdformat->format.width,
> + infmt->width / 4);
> + sdformat->format.height = max_t(__u32,
> + sdformat->format.height,
> + infmt->height / 4);
> + }
> + } else {
> + sdformat->format.width = min_t(__u32,
> + sdformat->format.width,
> + MAX_W_SINK);
> + sdformat->format.height = min_t(__u32,
> + sdformat->format.height,
> + MAX_H_SINK);
> + }
> +
> + if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) {
> + cfg->try_fmt = sdformat->format;
> + } else {
> + priv->format_mbus[sdformat->pad] = sdformat->format;
> + priv->cc[sdformat->pad] = cc;
> + }
> +
> + return 0;
> +}
> +
> +static int prpenc_link_setup(struct media_entity *entity,
> + const struct media_pad *local,
> + const struct media_pad *remote, u32 flags)
> +{
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> + struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
> + struct prpenc_priv *priv = ic_priv->task_priv;
> + struct v4l2_subdev *remote_sd;
> +
> + dev_dbg(ic_priv->dev, "link setup %s -> %s", remote->entity->name,
> + local->entity->name);
> +
> + remote_sd = media_entity_to_v4l2_subdev(remote->entity);
> +
> + if (local->flags & MEDIA_PAD_FL_SOURCE) {
> + if (flags & MEDIA_LNK_FL_ENABLED) {
> + if (priv->sink_sd)
> + return -EBUSY;
> + priv->sink_sd = remote_sd;
> + } else {
> + priv->sink_sd = NULL;
> + }
> +
> + return 0;
> + }
> +
> + /* this is sink pad */
> + if (flags & MEDIA_LNK_FL_ENABLED) {
> + if (priv->src_sd)
> + return -EBUSY;
> + priv->src_sd = remote_sd;
> + } else {
> + priv->src_sd = NULL;
> + return 0;
> + }
> +
> + switch (remote_sd->grp_id) {
> + case IMX_MEDIA_GRP_ID_CSI0:
> + priv->csi_id = 0;
> + break;
> + case IMX_MEDIA_GRP_ID_CSI1:
> + priv->csi_id = 1;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int prpenc_link_validate(struct v4l2_subdev *sd,
> + struct media_link *link,
> + struct v4l2_subdev_format *source_fmt,
> + struct v4l2_subdev_format *sink_fmt)
> +{
> + struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
> + struct prpenc_priv *priv = ic_priv->task_priv;
> + struct v4l2_mbus_config sensor_mbus_cfg;
> + int ret;
> +
> + ret = v4l2_subdev_link_validate_default(sd, link,
> + source_fmt, sink_fmt);
> + if (ret)
> + return ret;
> +
> + priv->sensor = __imx_media_find_sensor(priv->md, &ic_priv->sd.entity);
> + if (IS_ERR(priv->sensor)) {
> + v4l2_err(&ic_priv->sd, "no sensor attached\n");
> + ret = PTR_ERR(priv->sensor);
> + priv->sensor = NULL;
> + return ret;
> + }
> +
> + ret = v4l2_subdev_call(priv->sensor->sd, video, g_mbus_config,
> + &sensor_mbus_cfg);
> + if (ret)
> + return ret;
> +
> + if (sensor_mbus_cfg.type == V4L2_MBUS_CSI2) {
> + int vc_num = 0;
> + /* see NOTE in imx-csi.c */
> +#if 0
> + vc_num = imx_media_find_mipi_csi2_channel(
> + priv->md, &ic_priv->sd.entity);
> + if (vc_num < 0)
> + return vc_num;
> +#endif
> + /* only virtual channel 0 can be sent to IC */
> + if (vc_num != 0)
> + return -EINVAL;
> + } else {
> + /*
> + * only 8-bit pixels can be sent to IC for parallel
> + * busses
> + */
> + if (priv->sensor->sensor_ep.bus.parallel.bus_width >= 16)
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int prpenc_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct prpenc_priv *priv = container_of(ctrl->handler,
> + struct prpenc_priv, ctrl_hdlr);
> + struct imx_ic_priv *ic_priv = priv->ic_priv;
> + enum ipu_rotate_mode rot_mode;
> + bool hflip, vflip;
> + int rotation, ret;
> +
> + rotation = priv->rotation;
> + hflip = priv->hflip;
> + vflip = priv->vflip;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_HFLIP:
> + hflip = (ctrl->val == 1);
> + break;
> + case V4L2_CID_VFLIP:
> + vflip = (ctrl->val == 1);
> + break;
> + case V4L2_CID_ROTATE:
> + rotation = ctrl->val;
> + break;
> + default:
> + v4l2_err(&ic_priv->sd, "Invalid control\n");
> + return -EINVAL;
> + }
> +
> + ret = ipu_degrees_to_rot_mode(&rot_mode, rotation, hflip, vflip);
> + if (ret)
> + return ret;
> +
> + if (rot_mode != priv->rot_mode) {
> + /* can't change rotation mid-streaming */
> + if (priv->stream_on)
> + return -EBUSY;
> +
> + priv->rot_mode = rot_mode;
> + priv->rotation = rotation;
> + priv->hflip = hflip;
> + priv->vflip = vflip;
> + }
> +
> + return 0;
> +}
> +
> +static const struct v4l2_ctrl_ops prpenc_ctrl_ops = {
> + .s_ctrl = prpenc_s_ctrl,
> +};
> +
> +static const struct v4l2_ctrl_config prpenc_std_ctrl[] = {
> + {
> + .id = V4L2_CID_HFLIP,
> + .name = "Horizontal Flip",
> + .type = V4L2_CTRL_TYPE_BOOLEAN,
> + .def = 0,
> + .min = 0,
> + .max = 1,
> + .step = 1,
> + }, {
> + .id = V4L2_CID_VFLIP,
> + .name = "Vertical Flip",
> + .type = V4L2_CTRL_TYPE_BOOLEAN,
> + .def = 0,
> + .min = 0,
> + .max = 1,
> + .step = 1,
> + }, {
> + .id = V4L2_CID_ROTATE,
> + .name = "Rotation",
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + .def = 0,
> + .min = 0,
> + .max = 270,
> + .step = 90,
> + },
> +};
Use v4l2_ctrl_new_std() instead of this array: this avoids duplicating information
like the name and type.
If this is also done elsewhere, then it should be changed there as well.
Regards,
Hans
^ permalink raw reply
* Re: [PATCH v3 20/24] media: imx: Add Camera Interface subdev driver
From: Hans Verkuil @ 2017-01-20 14:38 UTC (permalink / raw)
To: Steve Longerbeam, robh+dt, mark.rutland, shawnguo, kernel,
fabio.estevam, linux, mchehab, nick, markus.heiser, p.zabel,
laurent.pinchart+renesas, bparrot, geert, arnd, sudipm.mukherjee,
minghsiu.tsai, tiffany.lin, jean-christophe.trotin, horms+renesas,
niklas.soderlund+renesas, robert.jarzmik, songjun.wu,
andrew-ct.chen, gregkh
Cc: devel, devicetree, Steve Longerbeam, linux-kernel,
linux-arm-kernel, linux-media
In-Reply-To: <1483755102-24785-21-git-send-email-steve_longerbeam@mentor.com>
On 01/07/2017 03:11 AM, Steve Longerbeam wrote:
> This is the camera interface driver that provides the v4l2
> user interface. Frames can be received from various sources:
>
> - directly from SMFC for capturing unconverted images directly from
> camera sensors.
>
> - from the IC pre-process encode task.
>
> - from the IC pre-process viewfinder task.
>
> - from the IC post-process task.
>
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
> drivers/staging/media/imx/Makefile | 2 +-
> drivers/staging/media/imx/imx-camif.c | 1000 +++++++++++++++++++++++++++++++++
> 2 files changed, 1001 insertions(+), 1 deletion(-)
> create mode 100644 drivers/staging/media/imx/imx-camif.c
>
> diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
> index d2a962c..fe9e992 100644
> --- a/drivers/staging/media/imx/Makefile
> +++ b/drivers/staging/media/imx/Makefile
> @@ -8,4 +8,4 @@ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-ic.o
>
> obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
> obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-smfc.o
> -
> +obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-camif.o
> diff --git a/drivers/staging/media/imx/imx-camif.c b/drivers/staging/media/imx/imx-camif.c
> new file mode 100644
> index 0000000..404f724
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-camif.c
> @@ -0,0 +1,1000 @@
> +/*
> + * Video Camera Capture Subdev for Freescale i.MX5/6 SOC
> + *
> + * Copyright (c) 2012-2016 Mentor Graphics 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; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/timer.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-of.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include <video/imx-ipu-v3.h>
> +#include <media/imx.h>
> +#include "imx-media.h"
> +
> +#define CAMIF_NUM_PADS 2
> +
> +struct camif_priv {
> + struct device *dev;
> + struct video_device vfd;
> + struct media_pipeline mp;
> + struct imx_media_dev *md;
> + struct v4l2_subdev sd;
> + struct media_pad pad[CAMIF_NUM_PADS];
> + struct media_pad vd_pad;
> + int id;
> + int input_pad;
> + int output_pad;
> +
> + struct v4l2_mbus_framefmt format_mbus[CAMIF_NUM_PADS];
> + const struct imx_media_pixfmt *cc[CAMIF_NUM_PADS];
> +
> + /* dma buffer ring */
> + struct imx_media_dma_buf_ring *in_ring;
> + struct v4l2_subdev *src_sd;
> +
> + struct mutex mutex; /* capture device mutex */
> + spinlock_t q_lock; /* protect ready_q */
> +
> + /* buffer queue used in videobuf2 */
> + struct vb2_queue buffer_queue;
> +
> + /* streaming buffer queue */
> + struct list_head ready_q;
> +
> + /* controls inherited from subdevs */
> + struct v4l2_ctrl_handler ctrl_hdlr;
> +
> + /* misc status */
> + int current_input; /* the current input */
> + v4l2_std_id current_std; /* current standard */
> + bool stop; /* streaming is stopping */
> +};
> +
> +/* In bytes, per queue */
> +#define VID_MEM_LIMIT SZ_64M
> +
> +static struct vb2_ops camif_qops;
> +
> +/*
> + * Video ioctls follow
> + */
> +
> +static int vidioc_querycap(struct file *file, void *fh,
> + struct v4l2_capability *cap)
> +{
> + strncpy(cap->driver, "imx-media-camif", sizeof(cap->driver) - 1);
> + strncpy(cap->card, "imx-media-camif", sizeof(cap->card) - 1);
> + cap->bus_info[0] = 0;
Should be set to something like 'platform:imx-media-camif'. v4l2-compliance should
complain about this.
> + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
Set device_caps in struct video_device, then drop these two lines since
the core will set these up based on the device_caps field in struct video_device.
> +
> + return 0;
> +}
> +
> +static int camif_enum_fmt_vid_cap(struct file *file, void *fh,
> + struct v4l2_fmtdesc *f)
> +{
> + const struct imx_media_pixfmt *cc;
> + u32 code;
> + int ret;
> +
> + ret = imx_media_enum_format(&code, f->index, true, true);
> + if (ret)
> + return ret;
> + cc = imx_media_find_format(0, code, true, true);
> + if (!cc)
> + return -EINVAL;
> +
> + f->pixelformat = cc->fourcc;
> +
> + return 0;
> +}
> +
> +static int camif_g_fmt_vid_cap(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> + struct v4l2_mbus_framefmt *outfmt;
> +
> + /* user format is the same as the format from output pad */
> + outfmt = &priv->format_mbus[priv->output_pad];
> + return imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, outfmt);
> +}
> +
> +static int camif_try_fmt_vid_cap(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + return camif_g_fmt_vid_cap(file, fh, f);
> +}
> +
> +static int camif_s_fmt_vid_cap(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> +
> + if (vb2_is_busy(&priv->buffer_queue)) {
> + v4l2_err(&priv->sd, "%s queue busy\n", __func__);
> + return -EBUSY;
> + }
> +
> + return camif_try_fmt_vid_cap(file, priv, f);
> +}
> +
> +static int camif_querystd(struct file *file, void *fh, v4l2_std_id *std)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> + struct imx_media_subdev *sensor;
> +
> + sensor = imx_media_find_sensor(priv->md, &priv->sd.entity);
> + if (IS_ERR(sensor)) {
> + v4l2_err(&priv->sd, "no sensor attached\n");
> + return PTR_ERR(sensor);
> + }
> +
> + return v4l2_subdev_call(sensor->sd, video, querystd, std);
> +}
> +
> +static int camif_g_std(struct file *file, void *fh, v4l2_std_id *std)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> +
> + *std = priv->current_std;
> + return 0;
> +}
> +
> +static int camif_s_std(struct file *file, void *fh, v4l2_std_id std)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> + struct imx_media_subdev *sensor;
> + int ret;
> +
> + sensor = imx_media_find_sensor(priv->md, &priv->sd.entity);
> + if (IS_ERR(sensor)) {
> + v4l2_err(&priv->sd, "no sensor attached\n");
> + return PTR_ERR(sensor);
> + }
> +
> + if (vb2_is_busy(&priv->buffer_queue))
> + return -EBUSY;
> +
> + ret = v4l2_subdev_call(sensor->sd, video, s_std, std);
> + if (ret < 0)
> + return ret;
> +
> + priv->current_std = std;
> + return 0;
> +}
> +
> +static int camif_enum_input(struct file *file, void *fh,
> + struct v4l2_input *input)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> + struct imx_media_subdev *sensor;
> + int index = input->index;
> +
> + sensor = imx_media_find_sensor(priv->md, &priv->sd.entity);
> + if (IS_ERR(sensor)) {
> + v4l2_err(&priv->sd, "no sensor attached\n");
> + return PTR_ERR(sensor);
> + }
> +
> + if (index >= sensor->input.num)
> + return -EINVAL;
> +
> + input->type = V4L2_INPUT_TYPE_CAMERA;
> + strncpy(input->name, sensor->input.name[index], sizeof(input->name));
> +
> + if (index == priv->current_input) {
> + v4l2_subdev_call(sensor->sd, video, g_input_status,
> + &input->status);
> + v4l2_subdev_call(sensor->sd, video, querystd, &input->std);
Wrong op, use g_tvnorms instead.
> + } else {
> + input->status = V4L2_IN_ST_NO_SIGNAL;
> + input->std = V4L2_STD_UNKNOWN;
> + }
> +
> + return 0;
> +}
> +
> +static int camif_g_input(struct file *file, void *fh, unsigned int *index)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> +
> + *index = priv->current_input;
> + return 0;
> +}
> +
> +static int camif_s_input(struct file *file, void *fh, unsigned int index)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> + struct imx_media_subdev *sensor;
> + int ret;
> +
> + sensor = imx_media_find_sensor(priv->md, &priv->sd.entity);
> + if (IS_ERR(sensor)) {
> + v4l2_err(&priv->sd, "no sensor attached\n");
> + return PTR_ERR(sensor);
> + }
> +
> + if (index >= sensor->input.num)
> + return -EINVAL;
> +
> + if (index == priv->current_input)
> + return 0;
> +
> + /* select the sensor's input */
> + ret = v4l2_subdev_call(sensor->sd, video, s_routing,
> + sensor->input.value[index], 0, 0);
> + if (!ret)
> + priv->current_input = index;
> +
> + return ret;
> +}
> +
> +static int camif_g_parm(struct file *file, void *fh,
> + struct v4l2_streamparm *a)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> + struct imx_media_subdev *sensor;
> +
> + sensor = imx_media_find_sensor(priv->md, &priv->sd.entity);
> + if (IS_ERR(sensor)) {
> + v4l2_err(&priv->sd, "no sensor attached\n");
> + return PTR_ERR(sensor);
> + }
> +
> + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> + return -EINVAL;
> +
> + return v4l2_subdev_call(sensor->sd, video, g_parm, a);
> +}
> +
> +static int camif_s_parm(struct file *file, void *fh,
> + struct v4l2_streamparm *a)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> + struct imx_media_subdev *sensor;
> +
> + sensor = imx_media_find_sensor(priv->md, &priv->sd.entity);
> + if (IS_ERR(sensor)) {
> + v4l2_err(&priv->sd, "no sensor attached\n");
> + return PTR_ERR(sensor);
> + }
> +
> + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> + return -EINVAL;
> +
> + return v4l2_subdev_call(sensor->sd, video, s_parm, a);
> +}
> +
> +static const struct v4l2_ioctl_ops camif_ioctl_ops = {
> + .vidioc_querycap = vidioc_querycap,
> +
> + .vidioc_enum_fmt_vid_cap = camif_enum_fmt_vid_cap,
> + .vidioc_g_fmt_vid_cap = camif_g_fmt_vid_cap,
> + .vidioc_try_fmt_vid_cap = camif_try_fmt_vid_cap,
> + .vidioc_s_fmt_vid_cap = camif_s_fmt_vid_cap,
> +
> + .vidioc_querystd = camif_querystd,
> + .vidioc_g_std = camif_g_std,
> + .vidioc_s_std = camif_s_std,
> +
> + .vidioc_enum_input = camif_enum_input,
> + .vidioc_g_input = camif_g_input,
> + .vidioc_s_input = camif_s_input,
> +
> + .vidioc_g_parm = camif_g_parm,
> + .vidioc_s_parm = camif_s_parm,
> +
> + .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_expbuf = vb2_ioctl_expbuf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> +};
> +
> +/*
> + * Queue operations
> + */
> +
> +static u32 camif_get_sizeimage(struct camif_priv *priv)
> +{
> + struct v4l2_mbus_framefmt *outfmt;
> + const struct imx_media_pixfmt *outcc;
> +
> + outfmt = &priv->format_mbus[priv->output_pad];
> + outcc = priv->cc[priv->output_pad];
> + return (outfmt->width * outfmt->height * outcc->bpp) >> 3;
> +}
> +
> +static int camif_queue_setup(struct vb2_queue *vq,
> + unsigned int *nbuffers, unsigned int *nplanes,
> + unsigned int sizes[], struct device *alloc_devs[])
> +{
> + struct camif_priv *priv = vb2_get_drv_priv(vq);
> + unsigned int count = *nbuffers;
> + u32 sizeimage;
> +
> + if (!priv->src_sd)
> + return -EPIPE;
> +
> + if (vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> + return -EINVAL;
> +
> + sizeimage = camif_get_sizeimage(priv);
> +
> + count = min_t(unsigned int, count, IMX_MEDIA_MAX_RING_BUFS);
> + count = max_t(unsigned int, count, IMX_MEDIA_MIN_RING_BUFS);
> +
> + while (sizeimage * count > VID_MEM_LIMIT)
> + count--;
> +
> + if (count < IMX_MEDIA_MIN_RING_BUFS)
> + return -EINVAL;
> +
> + *nplanes = 1;
> + *nbuffers = count;
> + sizes[0] = sizeimage;
> +
> + return 0;
> +}
> +
> +static int camif_buf_init(struct vb2_buffer *vb)
> +{
> + struct vb2_queue *vq = vb->vb2_queue;
> + struct camif_priv *priv = vb2_get_drv_priv(vq);
> + struct imx_media_buffer *buf = to_imx_media_vb(vb);
> +
> + if (!priv->src_sd)
> + return -EPIPE;
> +
> + INIT_LIST_HEAD(&buf->list);
> +
> + return 0;
> +}
> +
> +static int camif_buf_prepare(struct vb2_buffer *vb)
> +{
> + struct vb2_queue *vq = vb->vb2_queue;
> + struct camif_priv *priv = vb2_get_drv_priv(vq);
> + u32 sizeimage = camif_get_sizeimage(priv);
> + int ret;
> +
> + if (!priv->src_sd)
> + return -EPIPE;
> +
> + if (vb2_plane_size(vb, 0) < sizeimage) {
> + v4l2_err(&priv->sd,
> + "data will not fit into plane (%lu < %lu)\n",
> + vb2_plane_size(vb, 0), (long)sizeimage);
> + return -EINVAL;
> + }
> +
> + vb2_set_plane_payload(vb, 0, sizeimage);
> +
> + if (!priv->in_ring) {
> + priv->in_ring = imx_media_alloc_dma_buf_ring(
> + priv->md, &priv->src_sd->entity, &priv->sd.entity,
> + sizeimage, vq->num_buffers, false);
> + if (IS_ERR(priv->in_ring)) {
> + v4l2_err(&priv->sd, "failed to alloc dma-buf ring\n");
> + ret = PTR_ERR(priv->in_ring);
> + priv->in_ring = NULL;
> + return ret;
> + }
> + }
> +
> + ret = imx_media_dma_buf_queue_from_vb(priv->in_ring, vb);
> + if (ret)
> + goto free_ring;
> +
> + return 0;
> +
> +free_ring:
> + imx_media_free_dma_buf_ring(priv->in_ring);
> + priv->in_ring = NULL;
> + return ret;
> +}
> +
> +static void camif_buf_queue(struct vb2_buffer *vb)
> +{
> + struct camif_priv *priv = vb2_get_drv_priv(vb->vb2_queue);
> + struct imx_media_buffer *buf = to_imx_media_vb(vb);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&priv->q_lock, flags);
> +
> + list_add_tail(&buf->list, &priv->ready_q);
> +
> + spin_unlock_irqrestore(&priv->q_lock, flags);
> +}
> +
> +static int camif_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> + struct camif_priv *priv = vb2_get_drv_priv(vq);
> + u32 sizeimage = camif_get_sizeimage(priv);
> + struct imx_media_buffer *buf, *tmp;
> + unsigned long flags;
> + int ret;
> +
> + if (vb2_is_streaming(vq))
> + return 0;
> +
> + if (!priv->src_sd)
> + return -EPIPE;
> +
> + if (!priv->in_ring) {
> + priv->in_ring = imx_media_alloc_dma_buf_ring(
> + priv->md, &priv->src_sd->entity, &priv->sd.entity,
> + sizeimage, vq->num_buffers, false);
> + if (IS_ERR(priv->in_ring)) {
> + v4l2_err(&priv->sd, "failed to alloc dma-buf ring\n");
> + ret = PTR_ERR(priv->in_ring);
> + priv->in_ring = NULL;
> + goto return_bufs;
> + }
> + }
> +
> + ret = imx_media_pipeline_set_stream(priv->md, &priv->sd.entity,
> + &priv->mp, true);
> + if (ret) {
> + v4l2_err(&priv->sd, "pipeline_set_stream failed with %d\n",
> + ret);
> + goto free_ring;
> + }
> +
> + priv->stop = false;
> +
> + return 0;
> +
> +free_ring:
> + imx_media_free_dma_buf_ring(priv->in_ring);
> + priv->in_ring = NULL;
> +return_bufs:
> + spin_lock_irqsave(&priv->q_lock, flags);
> + list_for_each_entry_safe(buf, tmp, &priv->ready_q, list) {
> + list_del(&buf->list);
> + vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_QUEUED);
> + }
> + spin_unlock_irqrestore(&priv->q_lock, flags);
> + return ret;
> +}
> +
> +static void camif_stop_streaming(struct vb2_queue *vq)
> +{
> + struct camif_priv *priv = vb2_get_drv_priv(vq);
> + struct imx_media_buffer *frame;
> + unsigned long flags;
> + int ret;
> +
> + if (!vb2_is_streaming(vq))
> + return;
> +
> + spin_lock_irqsave(&priv->q_lock, flags);
> + priv->stop = true;
> + spin_unlock_irqrestore(&priv->q_lock, flags);
> +
> + ret = imx_media_pipeline_set_stream(priv->md, &priv->sd.entity,
> + &priv->mp, false);
> + if (ret)
> + v4l2_warn(&priv->sd, "pipeline_set_stream failed with %d\n",
> + ret);
> +
> + if (priv->in_ring) {
> + v4l2_warn(&priv->sd, "%s: in_ring was not freed\n",
> + __func__);
> + imx_media_free_dma_buf_ring(priv->in_ring);
> + priv->in_ring = NULL;
> + }
> +
> + /* release all active buffers */
> + spin_lock_irqsave(&priv->q_lock, flags);
> + while (!list_empty(&priv->ready_q)) {
> + frame = list_entry(priv->ready_q.next,
> + struct imx_media_buffer, list);
> + list_del(&frame->list);
> + vb2_buffer_done(&frame->vbuf.vb2_buf, VB2_BUF_STATE_ERROR);
> + }
> + spin_unlock_irqrestore(&priv->q_lock, flags);
> +}
> +
> +static struct vb2_ops camif_qops = {
> + .queue_setup = camif_queue_setup,
> + .buf_init = camif_buf_init,
> + .buf_prepare = camif_buf_prepare,
> + .buf_queue = camif_buf_queue,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> + .start_streaming = camif_start_streaming,
> + .stop_streaming = camif_stop_streaming,
> +};
> +
> +/*
> + * File operations
> + */
> +static int camif_open(struct file *file)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> + int ret;
> +
> + if (mutex_lock_interruptible(&priv->mutex))
> + return -ERESTARTSYS;
> +
> + ret = v4l2_fh_open(file);
> + if (ret)
> + v4l2_err(&priv->sd, "v4l2_fh_open failed\n");
> +
> + mutex_unlock(&priv->mutex);
> + return ret;
> +}
> +
> +static int camif_release(struct file *file)
> +{
> + struct camif_priv *priv = video_drvdata(file);
> + struct vb2_queue *vq = &priv->buffer_queue;
> + int ret = 0;
> +
> + mutex_lock(&priv->mutex);
> +
> + if (file->private_data == vq->owner) {
> + vb2_queue_release(vq);
> + vq->owner = NULL;
> + }
> +
> + v4l2_fh_release(file);
> + mutex_unlock(&priv->mutex);
> + return ret;
> +}
> +
> +static const struct v4l2_file_operations camif_fops = {
> + .owner = THIS_MODULE,
> + .open = camif_open,
> + .release = camif_release,
> + .poll = vb2_fop_poll,
> + .unlocked_ioctl = video_ioctl2,
> + .mmap = vb2_fop_mmap,
> +};
> +
> +static struct video_device camif_videodev = {
> + .fops = &camif_fops,
> + .ioctl_ops = &camif_ioctl_ops,
> + .minor = -1,
> + .release = video_device_release,
> + .vfl_dir = VFL_DIR_RX,
> + .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
> +};
> +
> +/*
> + * Subdev and media entity operations
> + */
> +
> +static void camif_new_dma_buf(struct camif_priv *priv)
> +{
> + struct imx_media_dma_buf *dmabuf;
> + struct imx_media_buffer *buf;
> + enum vb2_buffer_state state;
> + struct vb2_buffer *vb;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&priv->q_lock, flags);
> +
> + if (priv->stop || list_empty(&priv->ready_q))
> + goto unlock;
> +
> + dmabuf = imx_media_dma_buf_dequeue(priv->in_ring);
> + if (!dmabuf)
> + goto unlock;
> +
> + vb = dmabuf->vb;
> + buf = to_imx_media_vb(vb);
> + if (list_empty(&buf->list)) {
> + dev_dbg(priv->dev, "%s: buf%d not queued\n", __func__,
> + vb->index);
> + goto unlock;
> + }
> +
> + dev_dbg(priv->dev, "%s: new buf%d\n", __func__, vb->index);
> + vb->timestamp = ktime_get_ns();
> + list_del_init(&buf->list);
> + state = dmabuf->status == IMX_MEDIA_BUF_STATUS_DONE ?
> + VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
> + vb2_buffer_done(vb, state);
> +unlock:
> + spin_unlock_irqrestore(&priv->q_lock, flags);
> +}
> +
> +static long camif_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
> +{
> + struct camif_priv *priv = v4l2_get_subdevdata(sd);
> + struct imx_media_dma_buf_ring **ring;
> +
> + switch (cmd) {
> + case IMX_MEDIA_REQ_DMA_BUF_SINK_RING:
> + if (!priv->in_ring)
> + return -EINVAL;
> + ring = (struct imx_media_dma_buf_ring **)arg;
> + *ring = priv->in_ring;
> + break;
> + case IMX_MEDIA_NEW_DMA_BUF:
> + camif_new_dma_buf(priv);
> + break;
> + case IMX_MEDIA_REL_DMA_BUF_SINK_RING:
> + /* src indicates sink buffer ring can be freed */
> + if (!priv->in_ring)
> + return 0;
> + v4l2_info(sd, "%s: freeing sink ring\n", __func__);
> + imx_media_free_dma_buf_ring(priv->in_ring);
> + priv->in_ring = NULL;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int camif_link_setup(struct media_entity *entity,
> + const struct media_pad *local,
> + const struct media_pad *remote, u32 flags)
> +{
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> + struct camif_priv *priv = v4l2_get_subdevdata(sd);
> + struct video_device *vfd = &priv->vfd;
> + struct v4l2_subdev *remote_sd;
> + int ret = 0;
> +
> + dev_dbg(priv->dev, "link setup %s -> %s", remote->entity->name,
> + local->entity->name);
> +
> + if (is_media_entity_v4l2_video_device(remote->entity))
> + return 0;
> +
> + WARN_ON(local->flags & MEDIA_PAD_FL_SOURCE);
> +
> + remote_sd = media_entity_to_v4l2_subdev(remote->entity);
> +
> + /* reset controls to refresh with inherited from subdevs */
> + v4l2_ctrl_handler_free(vfd->ctrl_handler);
> + v4l2_ctrl_handler_init(vfd->ctrl_handler, 0);
> +
> + if (flags & MEDIA_LNK_FL_ENABLED) {
> + if (priv->src_sd)
> + return -EBUSY;
> + priv->src_sd = remote_sd;
> + ret = imx_media_inherit_controls(priv->md, vfd,
> + &priv->src_sd->entity);
> + } else {
> + priv->src_sd = NULL;
> + }
> +
> + return ret;
> +}
> +
> +static int camif_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + if (code->pad >= CAMIF_NUM_PADS)
> + return -EINVAL;
> +
> + return imx_media_enum_format(&code->code, code->index, true, true);
> +}
> +
> +static int camif_get_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *sdformat)
> +{
> + struct camif_priv *priv = v4l2_get_subdevdata(sd);
> +
> + if (sdformat->pad >= CAMIF_NUM_PADS)
> + return -EINVAL;
> +
> + sdformat->format = priv->format_mbus[sdformat->pad];
> +
> + return 0;
> +}
> +
> +static int camif_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *sdformat)
> +{
> + struct camif_priv *priv = v4l2_get_subdevdata(sd);
> + const struct imx_media_pixfmt *cc;
> + u32 code;
> +
> + if (sdformat->pad >= CAMIF_NUM_PADS)
> + return -EINVAL;
> +
> + if (vb2_is_busy(&priv->buffer_queue)) {
> + v4l2_err(&priv->sd, "%s queue busy\n", __func__);
> + return -EBUSY;
> + }
> +
> + cc = imx_media_find_format(0, sdformat->format.code, true, true);
> + if (!cc) {
> + imx_media_enum_format(&code, 0, true, true);
> + cc = imx_media_find_format(0, code, true, true);
> + sdformat->format.code = cc->codes[0];
> + }
> +
> + /* Output pad mirrors input pad, no limitations on input pads */
> + if (sdformat->pad == priv->output_pad)
> + sdformat->format = priv->format_mbus[priv->input_pad];
> +
> + if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) {
> + cfg->try_fmt = sdformat->format;
> + } else {
> + priv->format_mbus[sdformat->pad] = sdformat->format;
> + priv->cc[sdformat->pad] = cc;
> + }
> +
> + return 0;
> +}
> +
> +static int camif_init_pads(struct camif_priv *priv)
> +{
> + struct video_device *vfd = &priv->vfd;
> + struct imx_media_subdev *imxsd;
> + struct imx_media_pad *pad;
> + int i, ret;
> +
> + imxsd = imx_media_find_subdev_by_sd(priv->md, &priv->sd);
> + if (IS_ERR(imxsd))
> + return PTR_ERR(imxsd);
> +
> + if (imxsd->num_sink_pads != 1 || imxsd->num_src_pads != 1) {
> + v4l2_err(&priv->sd, "invalid num pads %d/%d\n",
> + imxsd->num_sink_pads, imxsd->num_src_pads);
> + return -EINVAL;
> + }
> +
> + priv->vd_pad.flags = MEDIA_PAD_FL_SINK;
> + ret = media_entity_pads_init(&vfd->entity, 1, &priv->vd_pad);
> + if (ret) {
> + v4l2_err(&priv->sd, "failed to init device node pad\n");
> + return ret;
> + }
> +
> + for (i = 0; i < CAMIF_NUM_PADS; i++) {
> + pad = &imxsd->pad[i];
> + priv->pad[i] = pad->pad;
> + if (priv->pad[i].flags & MEDIA_PAD_FL_SINK)
> + priv->input_pad = i;
> + else
> + priv->output_pad = i;
> +
> + /* set a default mbus format */
> + ret = imx_media_init_mbus_fmt(&priv->format_mbus[i],
> + 640, 480, 0, V4L2_FIELD_NONE,
> + &priv->cc[i]);
> + if (ret)
> + return ret;
> + }
> +
> + return media_entity_pads_init(&priv->sd.entity, CAMIF_NUM_PADS,
> + priv->pad);
> +}
> +
> +static int camif_registered(struct v4l2_subdev *sd)
> +{
> + struct camif_priv *priv = v4l2_get_subdevdata(sd);
> + struct vb2_queue *vq = &priv->buffer_queue;
> + struct video_device *vfd = &priv->vfd;
> + struct v4l2_mbus_framefmt *infmt, *outfmt;
> + int ret;
> +
> + /* get media device */
> + priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
> +
> + vfd->v4l2_dev = sd->v4l2_dev;
> +
> + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> + if (ret) {
> + v4l2_err(sd, "Failed to register video device\n");
> + return ret;
> + }
> +
> + vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + vq->io_modes = VB2_MMAP | VB2_DMABUF;
> + vq->drv_priv = priv;
> + vq->buf_struct_size = sizeof(struct imx_media_buffer);
> + vq->ops = &camif_qops;
> + vq->mem_ops = &vb2_dma_contig_memops;
> + vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> + vq->lock = &priv->mutex;
> + vq->min_buffers_needed = 2;
> + vq->dev = priv->dev;
> +
> + ret = vb2_queue_init(vq);
> + if (ret) {
> + v4l2_err(sd, "vb2_queue_init failed\n");
> + goto unreg;
> + }
> +
> + INIT_LIST_HEAD(&priv->ready_q);
> +
> + ret = camif_init_pads(priv);
> + if (ret) {
> + v4l2_err(sd, "camif_init_pads failed\n");
> + goto unreg;
> + }
> +
> + /* create the link to our device node */
> + ret = media_create_pad_link(&sd->entity, priv->output_pad,
> + &vfd->entity, 0,
> + MEDIA_LNK_FL_IMMUTABLE |
> + MEDIA_LNK_FL_ENABLED);
> + if (ret) {
> + v4l2_err(sd, "failed to create link to device node\n");
> + goto unreg;
> + }
> +
> + /* setup default pad formats */
> + infmt = &priv->format_mbus[priv->input_pad];
> + outfmt = &priv->format_mbus[priv->output_pad];
> + ret = imx_media_init_mbus_fmt(outfmt, 640, 480, 0, V4L2_FIELD_NONE,
> + &priv->cc[priv->output_pad]);
> + if (ret)
> + goto unreg;
> +
> + *infmt = *outfmt;
> + priv->cc[priv->input_pad] = priv->cc[priv->output_pad];
> +
> + priv->current_std = V4L2_STD_UNKNOWN;
> +
> + v4l2_info(sd, "Registered %s as /dev/%s\n", vfd->name,
> + video_device_node_name(vfd));
> +
> + vfd->ctrl_handler = &priv->ctrl_hdlr;
> +
> + return 0;
> +unreg:
> + video_unregister_device(vfd);
> + return ret;
> +}
> +
> +static void camif_unregistered(struct v4l2_subdev *sd)
> +{
> + struct camif_priv *priv = v4l2_get_subdevdata(sd);
> + struct video_device *vfd = &priv->vfd;
> +
> + mutex_lock(&priv->mutex);
> +
> + if (video_is_registered(vfd)) {
> + video_unregister_device(vfd);
> + media_entity_cleanup(&vfd->entity);
> + }
> +
> + mutex_unlock(&priv->mutex);
> +}
> +
> +static const struct v4l2_subdev_internal_ops camif_internal_ops = {
> + .registered = camif_registered,
> + .unregistered = camif_unregistered,
> +};
> +
> +static struct v4l2_subdev_core_ops camif_core_ops = {
> + .ioctl = camif_ioctl,
> +};
> +
> +static struct media_entity_operations camif_entity_ops = {
> + .link_setup = camif_link_setup,
> + .link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static struct v4l2_subdev_pad_ops camif_pad_ops = {
> + .enum_mbus_code = camif_enum_mbus_code,
> + .get_fmt = camif_get_fmt,
> + .set_fmt = camif_set_fmt,
> +};
> +
> +static struct v4l2_subdev_ops camif_subdev_ops = {
> + .pad = &camif_pad_ops,
> + .core = &camif_core_ops,
> +};
> +
> +static int camif_probe(struct platform_device *pdev)
> +{
> + struct imx_media_internal_sd_platformdata *pdata;
> + struct camif_priv *priv;
> + struct video_device *vfd;
> + int ret;
> +
> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, priv);
> + priv->dev = &pdev->dev;
> +
> + pdata = priv->dev->platform_data;
> +
> + mutex_init(&priv->mutex);
> + spin_lock_init(&priv->q_lock);
> +
> + v4l2_subdev_init(&priv->sd, &camif_subdev_ops);
> + v4l2_set_subdevdata(&priv->sd, priv);
> + priv->sd.internal_ops = &camif_internal_ops;
> + priv->sd.entity.ops = &camif_entity_ops;
> + priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
> + priv->sd.dev = &pdev->dev;
> + priv->sd.owner = THIS_MODULE;
> + /* get our group id and camif id */
> + priv->sd.grp_id = pdata->grp_id;
> + priv->id = (pdata->grp_id >> IMX_MEDIA_GRP_ID_CAMIF_BIT) - 1;
> + strncpy(priv->sd.name, pdata->sd_name, sizeof(priv->sd.name));
> + snprintf(camif_videodev.name, sizeof(camif_videodev.name),
> + "%s devnode", pdata->sd_name);
> +
> + priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
> +
> + vfd = &priv->vfd;
> + *vfd = camif_videodev;
> + vfd->lock = &priv->mutex;
> + vfd->queue = &priv->buffer_queue;
> +
> + video_set_drvdata(vfd, priv);
> +
> + v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0);
> +
> + ret = v4l2_async_register_subdev(&priv->sd);
> + if (ret)
> + v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
> +
> + return ret;
> +}
> +
> +static int camif_remove(struct platform_device *pdev)
> +{
> + struct camif_priv *priv =
> + (struct camif_priv *)platform_get_drvdata(pdev);
> +
> + v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
> + v4l2_async_unregister_subdev(&priv->sd);
> + media_entity_cleanup(&priv->sd.entity);
> + v4l2_device_unregister_subdev(&priv->sd);
> +
> + return 0;
> +}
> +
> +static const struct platform_device_id camif_ids[] = {
> + { .name = "imx-media-camif" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(platform, camif_ids);
> +
> +static struct platform_driver imx_camif_driver = {
> + .probe = camif_probe,
> + .remove = camif_remove,
> + .driver = {
> + .name = "imx-media-camif",
> + },
> +};
> +
> +module_platform_driver(imx_camif_driver);
> +
> +MODULE_DESCRIPTION("i.MX camera interface subdev driver");
> +MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
> +MODULE_LICENSE("GPL");
>
Regards,
Hans
^ permalink raw reply
* Re: [PATCH v3 22/24] media: imx: Add MIPI CSI-2 OV5640 sensor subdev driver
From: Hans Verkuil @ 2017-01-20 14:48 UTC (permalink / raw)
To: Steve Longerbeam, robh+dt, mark.rutland, shawnguo, kernel,
fabio.estevam, linux, mchehab, nick, markus.heiser, p.zabel,
laurent.pinchart+renesas, bparrot, geert, arnd, sudipm.mukherjee,
minghsiu.tsai, tiffany.lin, jean-christophe.trotin, horms+renesas,
niklas.soderlund+renesas, robert.jarzmik, songjun.wu,
andrew-ct.chen, gregkh
Cc: devicetree, linux-kernel, linux-arm-kernel, linux-media, devel,
Steve Longerbeam
In-Reply-To: <1483755102-24785-23-git-send-email-steve_longerbeam@mentor.com>
On 01/07/2017 03:11 AM, Steve Longerbeam wrote:
> This driver is based on ov5640_mipi.c from Freescale imx_3.10.17_1.0.0_beta
> branch, modified heavily to bring forward to latest interfaces and code
> cleanup.
>
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
> drivers/staging/media/imx/Kconfig | 8 +
> drivers/staging/media/imx/Makefile | 2 +
> drivers/staging/media/imx/ov5640-mipi.c | 2348 +++++++++++++++++++++++++++++++
> 3 files changed, 2358 insertions(+)
> create mode 100644 drivers/staging/media/imx/ov5640-mipi.c
>
> diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig
> index ce2d2c8..09f373d 100644
> --- a/drivers/staging/media/imx/Kconfig
> +++ b/drivers/staging/media/imx/Kconfig
> @@ -17,5 +17,13 @@ config VIDEO_IMX_CAMERA
> ---help---
> A video4linux camera capture driver for i.MX5/6.
>
> +config IMX_OV5640_MIPI
> + tristate "OmniVision OV5640 MIPI CSI-2 camera support"
> + depends on GPIOLIB && VIDEO_IMX_CAMERA
> + select IMX_MIPI_CSI2
> + default y
> + ---help---
> + MIPI CSI-2 OV5640 Camera support.
> +
> endmenu
> endif
> diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
> index 0decef7..aa954c1 100644
> --- a/drivers/staging/media/imx/Makefile
> +++ b/drivers/staging/media/imx/Makefile
> @@ -10,3 +10,5 @@ obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
> obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-smfc.o
> obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-camif.o
> obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-mipi-csi2.o
> +
> +obj-$(CONFIG_IMX_OV5640_MIPI) += ov5640-mipi.o
> diff --git a/drivers/staging/media/imx/ov5640-mipi.c b/drivers/staging/media/imx/ov5640-mipi.c
> new file mode 100644
> index 0000000..54647a7
> --- /dev/null
> +++ b/drivers/staging/media/imx/ov5640-mipi.c
> @@ -0,0 +1,2348 @@
> +/*
> + * Copyright (c) 2014 Mentor Graphics Inc.
> + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
> + *
> + * 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; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/ctype.h>
> +#include <linux/types.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/i2c.h>
> +#include <linux/of_device.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-of.h>
> +#include <media/v4l2-ctrls.h>
> +
> +#define OV5640_VOLTAGE_ANALOG 2800000
> +#define OV5640_VOLTAGE_DIGITAL_CORE 1500000
> +#define OV5640_VOLTAGE_DIGITAL_IO 1800000
> +
> +#define MIN_FPS 15
> +#define MAX_FPS 30
> +#define DEFAULT_FPS 30
> +
> +/* min/typical/max system clock (xclk) frequencies */
> +#define OV5640_XCLK_MIN 6000000
> +#define OV5640_XCLK_TYP 24000000
> +#define OV5640_XCLK_MAX 54000000
> +
> +/* min/typical/max pixel clock (mclk) frequencies */
> +#define OV5640_MCLK_MIN 48000000
> +#define OV5640_MCLK_TYP 48000000
> +#define OV5640_MCLK_MAX 96000000
> +
> +#define OV5640_CHIP_ID 0x300A
> +#define OV5640_SLAVE_ID 0x3100
> +#define OV5640_DEFAULT_SLAVE_ID 0x3c
> +
> +#define OV5640_MAX_CONTROLS 64
> +
> +enum ov5640_mode {
> + ov5640_mode_MIN = 0,
> + ov5640_mode_QCIF_176_144 = 0,
> + ov5640_mode_QVGA_320_240,
> + ov5640_mode_VGA_640_480,
> + ov5640_mode_NTSC_720_480,
> + ov5640_mode_PAL_720_576,
> + ov5640_mode_XGA_1024_768,
> + ov5640_mode_720P_1280_720,
> + ov5640_mode_1080P_1920_1080,
> + ov5640_mode_QSXGA_2592_1944,
> + ov5640_num_modes,
> + ov5640_mode_INIT = 0xff, /*only for sensor init*/
> +};
> +
> +enum ov5640_frame_rate {
> + ov5640_15_fps,
> + ov5640_30_fps
> +};
> +
> +static int ov5640_framerates[] = {
> + [ov5640_15_fps] = 15,
> + [ov5640_30_fps] = 30,
> +};
> +#define ov5640_num_framerates ARRAY_SIZE(ov5640_framerates)
> +
> +/* image size under 1280 * 960 are SUBSAMPLING
> + * image size upper 1280 * 960 are SCALING
> + */
> +enum ov5640_downsize_mode {
> + SUBSAMPLING,
> + SCALING,
> +};
> +
> +struct reg_value {
> + u16 reg_addr;
> + u8 val;
> + u8 mask;
> + u32 delay_ms;
> +};
> +
> +struct ov5640_mode_info {
> + enum ov5640_mode mode;
> + enum ov5640_downsize_mode dn_mode;
> + u32 width;
> + u32 height;
> + struct reg_value *init_data_ptr;
> + u32 init_data_size;
> +};
> +
> +struct ov5640_dev {
> + struct i2c_client *i2c_client;
> + struct device *dev;
> + struct v4l2_subdev sd;
> + struct media_pad pad;
> + struct v4l2_ctrl_handler ctrl_hdl;
> + struct v4l2_of_endpoint ep; /* the parsed DT endpoint info */
> + struct v4l2_mbus_framefmt fmt;
> + struct v4l2_captureparm streamcap;
> + struct clk *xclk; /* system clock to OV5640 */
> + int xclk_freq; /* requested xclk freq from devicetree */
> +
> + enum ov5640_mode current_mode;
> + enum ov5640_frame_rate current_fr;
> +
> + bool on;
> + bool awb_on;
> + bool agc_on;
> +
> + /* cached control settings */
> + int ctrl_cache[OV5640_MAX_CONTROLS];
This is just duplicating the cached value in the control framework. I think this can be dropped.
> +
> + struct gpio_desc *reset_gpio;
> + struct gpio_desc *pwdn_gpio;
> + struct gpio_desc *gp_gpio;
> +
> + int prev_sysclk, prev_hts;
> + int ae_low, ae_high, ae_target;
> +
> + struct regulator *io_regulator;
> + struct regulator *core_regulator;
> + struct regulator *analog_regulator;
> + struct regulator *gpo_regulator;
> +};
> +
> +static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd)
> +{
> + return container_of(sd, struct ov5640_dev, sd);
> +}
> +
> +static inline struct ov5640_dev *ctrl_to_ov5640_dev(struct v4l2_ctrl *ctrl)
> +{
> + return container_of(ctrl->handler, struct ov5640_dev, ctrl_hdl);
> +}
> +
> +struct ov5640_control {
> + struct v4l2_queryctrl ctrl;
> + int (*set)(struct ov5640_dev *sensor, int value);
> +};
> +
> +static void ov5640_power(struct ov5640_dev *sensor, bool enable);
> +static void ov5640_reset(struct ov5640_dev *sensor);
> +static int ov5640_restore_ctrls(struct ov5640_dev *sensor);
> +static int ov5640_set_agc(struct ov5640_dev *sensor, int value);
> +static int ov5640_set_exposure(struct ov5640_dev *sensor, int value);
> +static int ov5640_get_exposure(struct ov5640_dev *sensor);
> +static int ov5640_set_gain(struct ov5640_dev *sensor, int value);
> +static int ov5640_get_gain(struct ov5640_dev *sensor);
> +
> +static struct reg_value ov5640_init_setting_30fps_VGA[] = {
> +
> + {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
> + {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
> + {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0},
> + {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0},
> + {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
> + {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
> + {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
> + {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
> + {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
> + {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
> + {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
> + {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
> + {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
> + {0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
> + {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
> + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
> + {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
> + {0x300e, 0x45, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0},
> + {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
> + {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x4837, 0x0a, 0, 0}, {0x4800, 0x04, 0, 0}, {0x3824, 0x02, 0, 0},
> + {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
> + {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
> + {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
> + {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0},
> + {0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0},
> + {0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0},
> + {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
> + {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
> + {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
> + {0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
> + {0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0},
> + {0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0},
> + {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
> + {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
> + {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
> + {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
> + {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
> + {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
> + {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
> + {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
> + {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
> + {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
> + {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
> + {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
> + {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
> + {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
> + {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0},
> + {0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0},
> + {0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
> + {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
> + {0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0},
> + {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
> + {0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
> + {0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0},
> + {0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0},
> + {0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0},
> + {0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0},
> + {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
> + {0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0},
> + {0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0},
> + {0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0},
> + {0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0},
> + {0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0},
> + {0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0},
> + {0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0},
> + {0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0},
> + {0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
> + {0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
> + {0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
> + {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
> +};
> +
> +static struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
> +
> + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
> + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
> + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
> + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
> +
> + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
> + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
> + {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0},
> + {0x380b, 0x00, 0, 0}, {0x3035, 0x12, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
> + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
> + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3808, 0x04, 0, 0},
> + {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
> + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
> + {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
> + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
> + {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
> + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
> + {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
> +};
> +static struct reg_value ov5640_setting_15fps_QCIF_176_144[] = {
> + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
> + {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
> + {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
> + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_15fps_NTSC_720_480[] = {
> + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
> + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
> + {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0},
> + {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_15fps_PAL_720_576[] = {
> + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0},
> + {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
> + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
> + {0x3008, 0x42, 0, 0},
> + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
> + {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
> + {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
> + {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
> + {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
> + {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
> + {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
> + {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0},
> + {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_15fps_720P_1280_720[] = {
> + {0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
> + {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
> + {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
> + {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
> + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
> + {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
> + {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
> + {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
> + {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
> + {0x3008, 0x42, 0, 0},
> + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
> + {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
> + {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
> + {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
> + {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
> + {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
> + {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0},
> + {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
> + {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
> + {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
> + {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
> + {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
> + {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
> + {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
> + {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
> + {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
> + {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
> + {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
> + {0x3503, 0, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
> + {0x3008, 0x42, 0, 0},
> + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
> + {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
> + {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
> + {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
> + {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
> + {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
> + {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x21, 0, 0},
> + {0x3036, 0x54, 0, 1}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
> + {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
> + {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
> + {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
> + {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
> + {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
> + {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
> + {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
> + {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
> + {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
> + {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
> + {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
> +};
> +
> +static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
> + {0x4202, 0x0f, 0, 0}, /* stream off the sensor */
> + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, /*disable flip*/
> + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
> + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
> + {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> + {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
> + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
> + {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
> + {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
> + {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
> + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
> + {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
> + {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
> + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
> + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> + {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
> + {0x4202, 0x00, 0, 0}, /* stream on the sensor */
> +};
> +
> +static struct ov5640_mode_info
> +ov5640_mode_info_data[ov5640_num_framerates][ov5640_num_modes] = {
> + {
> + {ov5640_mode_QCIF_176_144, SUBSAMPLING, 176, 144,
> + ov5640_setting_15fps_QCIF_176_144,
> + ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
> + {ov5640_mode_QVGA_320_240, SUBSAMPLING, 320, 240,
> + ov5640_setting_15fps_QVGA_320_240,
> + ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
> + {ov5640_mode_VGA_640_480, SUBSAMPLING, 640, 480,
> + ov5640_setting_15fps_VGA_640_480,
> + ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
> + {ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480,
> + ov5640_setting_15fps_NTSC_720_480,
> + ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
> + {ov5640_mode_PAL_720_576, SUBSAMPLING, 720, 576,
> + ov5640_setting_15fps_PAL_720_576,
> + ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
> + {ov5640_mode_XGA_1024_768, SUBSAMPLING, 1024, 768,
> + ov5640_setting_15fps_XGA_1024_768,
> + ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
> + {ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720,
> + ov5640_setting_15fps_720P_1280_720,
> + ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
> + {ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080,
> + ov5640_setting_15fps_1080P_1920_1080,
> + ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
> + {ov5640_mode_QSXGA_2592_1944, SCALING, 2592, 1944,
> + ov5640_setting_15fps_QSXGA_2592_1944,
> + ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
> + }, {
> + {ov5640_mode_QCIF_176_144, SUBSAMPLING, 176, 144,
> + ov5640_setting_30fps_QCIF_176_144,
> + ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
> + {ov5640_mode_QVGA_320_240, SUBSAMPLING, 320, 240,
> + ov5640_setting_30fps_QVGA_320_240,
> + ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
> + {ov5640_mode_VGA_640_480, SUBSAMPLING, 640, 480,
> + ov5640_setting_30fps_VGA_640_480,
> + ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
> + {ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480,
> + ov5640_setting_30fps_NTSC_720_480,
> + ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
> + {ov5640_mode_PAL_720_576, SUBSAMPLING, 720, 576,
> + ov5640_setting_30fps_PAL_720_576,
> + ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
> + {ov5640_mode_XGA_1024_768, SUBSAMPLING, 1024, 768,
> + ov5640_setting_30fps_XGA_1024_768,
> + ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
> + {ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720,
> + ov5640_setting_30fps_720P_1280_720,
> + ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
> + {ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080,
> + ov5640_setting_30fps_1080P_1920_1080,
> + ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
> + {ov5640_mode_QSXGA_2592_1944, -1, 0, 0, NULL, 0},
> + },
> +};
> +
> +static int ov5640_probe(struct i2c_client *adapter,
> + const struct i2c_device_id *device_id);
> +static int ov5640_remove(struct i2c_client *client);
> +
> +static int ov5640_init_slave_id(struct ov5640_dev *sensor)
> +{
> + struct i2c_msg msg;
> + u8 buf[4];
> + int ret;
> +
> + if (sensor->i2c_client->addr == OV5640_DEFAULT_SLAVE_ID)
> + return 0;
> +
> + buf[0] = OV5640_SLAVE_ID >> 8;
> + buf[1] = OV5640_SLAVE_ID & 0xff;
> + buf[2] = sensor->i2c_client->addr << 1;
> + msg.addr = OV5640_DEFAULT_SLAVE_ID;
> + msg.flags = 0;
> + msg.len = 3;
> + msg.buf = buf;
> +
> + ret = i2c_transfer(sensor->i2c_client->adapter, &msg, 1);
> + if (ret < 0) {
> + dev_err(sensor->dev, "%s: failed with %d\n", __func__, ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
> +{
> + u8 buf[3] = {0};
> + int ret;
> +
> + buf[0] = reg >> 8;
> + buf[1] = reg & 0xff;
> + buf[2] = val;
> +
> + ret = i2c_master_send(sensor->i2c_client, buf, 3);
> + if (ret < 0) {
> + v4l2_err(&sensor->sd, "%s: error: reg=%x, val=%x\n",
> + __func__, reg, val);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
> +{
> + u8 reg_buf[2] = {0};
> + u8 read_val = 0;
> +
> + reg_buf[0] = reg >> 8;
> + reg_buf[1] = reg & 0xff;
> +
> + if (2 != i2c_master_send(sensor->i2c_client, reg_buf, 2)) {
> + v4l2_err(&sensor->sd, "%s: write reg error: reg=%x\n",
> + __func__, reg);
> + return -EIO;
> + }
> +
> + if (1 != i2c_master_recv(sensor->i2c_client, &read_val, 1)) {
> + v4l2_err(&sensor->sd, "%s: read reg error: reg=%x, val=%x\n",
> + __func__, reg, read_val);
> + return -EIO;
> + }
> +
> + *val = read_val;
> + return 0;
> +}
> +
> +#define OV5640_READ_REG(s, r, v) { \
> + ret = ov5640_read_reg((s), (r), (v)); \
> + if (ret) \
> + return ret; \
> + }
> +#define OV5640_WRITE_REG(s, r, v) { \
> + ret = ov5640_write_reg((s), (r), (v)); \
> + if (ret) \
> + return ret; \
> + }
> +
> +static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val)
> +{
> + u8 hi, lo;
> + int ret;
> +
> + OV5640_READ_REG(sensor, reg, &hi);
> + OV5640_READ_REG(sensor, reg+1, &lo);
> +
> + *val = ((u16)hi << 8) | (u16)lo;
> + return 0;
> +}
> +#define OV5640_READ_REG16(s, r, v) { \
> + ret = ov5640_read_reg16((s), (r), (v)); \
> + if (ret) \
> + return ret; \
> + }
> +
> +static int ov5640_write_reg16(struct ov5640_dev *sensor, u16 reg, u16 val)
> +{
> + int ret;
> +
> + OV5640_WRITE_REG(sensor, reg, val >> 8);
> + OV5640_WRITE_REG(sensor, reg+1, val & 0xff);
> + return 0;
> +}
> +#define OV5640_WRITE_REG16(s, r, v) { \
> + ret = ov5640_write_reg16((s), (r), (v)); \
> + if (ret) \
> + return ret; \
> + }
> +
> +static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
> + u8 mask, u8 val)
> +{
> + u8 readval;
> + int ret;
> +
> + OV5640_READ_REG(sensor, reg, &readval);
> +
> + readval &= ~mask;
> + val &= mask;
> + val |= readval;
> +
> + OV5640_WRITE_REG(sensor, reg, val);
> + return 0;
> +}
> +#define OV5640_MOD_REG(s, r, m, v) { \
> + ret = ov5640_mod_reg((s), (r), (m), (v)); \
> + if (ret) \
> + return ret; \
> + }
> +
> +/* download ov5640 settings to sensor through i2c */
> +static int ov5640_load_regs(struct ov5640_dev *sensor,
> + struct reg_value *regs,
> + int size)
> +{
> + register u32 delay_ms = 0;
> + register u16 reg_addr = 0;
> + register u8 mask = 0;
> + register u8 val = 0;
> + int i, ret;
> +
> + for (i = 0; i < size; ++i, ++regs) {
> + delay_ms = regs->delay_ms;
> + reg_addr = regs->reg_addr;
> + val = regs->val;
> + mask = regs->mask;
> +
> + if (mask) {
> + OV5640_MOD_REG(sensor, reg_addr, mask, val);
> + } else {
> + OV5640_WRITE_REG(sensor, reg_addr, val);
> + }
> + if (delay_ms)
> + usleep_range(1000*delay_ms, 1000*delay_ms+100);
> + }
> +
> + return 0;
> +}
> +
> +static int ov5640_set_stream(struct ov5640_dev *sensor, bool on)
> +{
> + int ret;
> +
> + OV5640_WRITE_REG(sensor, 0x4202, on ? 0x00 : 0x0f);
> + return 0;
> +}
> +
> +static int ov5640_get_sysclk(struct ov5640_dev *sensor)
> +{
> + /* calculate sysclk */
> + int xvclk = sensor->xclk_freq / 10000;
> + int multiplier, prediv, VCO, sysdiv, pll_rdiv;
> + int sclk_rdiv_map[] = {1, 2, 4, 8};
> + int bit_div2x = 1, sclk_rdiv, sysclk;
> + u8 temp1, temp2;
> + int ret;
> +
> + OV5640_READ_REG(sensor, 0x3034, &temp1);
> + temp2 = temp1 & 0x0f;
> + if (temp2 == 8 || temp2 == 10)
> + bit_div2x = temp2 / 2;
> +
> + OV5640_READ_REG(sensor, 0x3035, &temp1);
> + sysdiv = temp1>>4;
> + if (sysdiv == 0)
> + sysdiv = 16;
> +
> + OV5640_READ_REG(sensor, 0x3036, &temp1);
> + multiplier = temp1;
> +
> + OV5640_READ_REG(sensor, 0x3037, &temp1);
> + prediv = temp1 & 0x0f;
> + pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
> +
> + OV5640_READ_REG(sensor, 0x3108, &temp1);
> + temp2 = temp1 & 0x03;
> + sclk_rdiv = sclk_rdiv_map[temp2];
> +
> + VCO = xvclk * multiplier / prediv;
> +
> + sysclk = VCO / sysdiv / pll_rdiv * 2 / bit_div2x / sclk_rdiv;
> +
> + return sysclk;
> +}
> +
> +static int ov5640_set_night_mode(struct ov5640_dev *sensor)
> +{
> + /* read HTS from register settings */
> + u8 mode;
> + int ret;
> +
> + OV5640_READ_REG(sensor, 0x3a00, &mode);
> + mode &= 0xfb;
> + OV5640_WRITE_REG(sensor, 0x3a00, mode);
> + return 0;
> +}
> +
> +static int ov5640_get_HTS(struct ov5640_dev *sensor)
> +{
> + /* read HTS from register settings */
> + u16 HTS;
> + int ret;
> +
> + OV5640_READ_REG16(sensor, 0x380c, &HTS);
> + return HTS;
> +}
> +
> +static int ov5640_get_VTS(struct ov5640_dev *sensor)
> +{
> + u16 VTS;
> + int ret;
> +
> + OV5640_READ_REG16(sensor, 0x380e, &VTS);
> + return VTS;
> +}
> +
> +static int ov5640_set_VTS(struct ov5640_dev *sensor, int VTS)
> +{
> + int ret;
> +
> + OV5640_WRITE_REG16(sensor, 0x380e, VTS);
> + return 0;
> +}
> +
> +static int ov5640_get_light_freq(struct ov5640_dev *sensor)
> +{
> + /* get banding filter value */
> + u8 temp, temp1;
> + int light_freq = 0;
> + int ret;
> +
> + OV5640_READ_REG(sensor, 0x3c01, &temp);
> +
> + if (temp & 0x80) {
> + /* manual */
> + OV5640_READ_REG(sensor, 0x3c00, &temp1);
> + if (temp1 & 0x04) {
> + /* 50Hz */
> + light_freq = 50;
> + } else {
> + /* 60Hz */
> + light_freq = 60;
> + }
> + } else {
> + /* auto */
> + OV5640_READ_REG(sensor, 0x3c0c, &temp1);
> + if (temp1 & 0x01) {
> + /* 50Hz */
> + light_freq = 50;
> + } else {
> + /* 60Hz */
> + }
> + }
> +
> + return light_freq;
> +}
> +
> +static int ov5640_set_bandingfilter(struct ov5640_dev *sensor)
> +{
> + int prev_vts;
> + int band_step60, max_band60, band_step50, max_band50;
> + int ret;
> +
> + /* read preview PCLK */
> + ret = ov5640_get_sysclk(sensor);
> + if (ret < 0)
> + return ret;
> + sensor->prev_sysclk = ret;
> + /* read preview HTS */
> + ret = ov5640_get_HTS(sensor);
> + if (ret < 0)
> + return ret;
> + sensor->prev_hts = ret;
> +
> + /* read preview VTS */
> + ret = ov5640_get_VTS(sensor);
> + if (ret < 0)
> + return ret;
> + prev_vts = ret;
> +
> + /* calculate banding filter */
> + /* 60Hz */
> + band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100/120;
> + OV5640_WRITE_REG16(sensor, 0x3a0a, band_step60);
> +
> + max_band60 = (int)((prev_vts-4)/band_step60);
> + OV5640_WRITE_REG(sensor, 0x3a0d, max_band60);
> +
> + /* 50Hz */
> + band_step50 = sensor->prev_sysclk * 100 / sensor->prev_hts;
> + OV5640_WRITE_REG16(sensor, 0x3a08, band_step50);
> +
> + max_band50 = (int)((prev_vts-4)/band_step50);
> + OV5640_WRITE_REG(sensor, 0x3a0e, max_band50);
> +
> + return 0;
> +}
> +
> +static int ov5640_set_AE_target(struct ov5640_dev *sensor, int target)
> +{
> + /* stable in high */
> + int fast_high, fast_low;
> + int ret;
> +
> + sensor->ae_low = target * 23 / 25; /* 0.92 */
> + sensor->ae_high = target * 27 / 25; /* 1.08 */
> +
> + fast_high = sensor->ae_high<<1;
> + if (fast_high > 255)
> + fast_high = 255;
> +
> + fast_low = sensor->ae_low >> 1;
> +
> + OV5640_WRITE_REG(sensor, 0x3a0f, sensor->ae_high);
> + OV5640_WRITE_REG(sensor, 0x3a10, sensor->ae_low);
> + OV5640_WRITE_REG(sensor, 0x3a1b, sensor->ae_high);
> + OV5640_WRITE_REG(sensor, 0x3a1e, sensor->ae_low);
> + OV5640_WRITE_REG(sensor, 0x3a11, fast_high);
> + OV5640_WRITE_REG(sensor, 0x3a1f, fast_low);
> +
> + return 0;
> +}
> +
> +static int ov5640_binning_on(struct ov5640_dev *sensor)
> +{
> + u8 temp;
> + int ret;
> +
> + OV5640_READ_REG(sensor, 0x3821, &temp);
> + temp &= 0xfe;
> +
> + return temp ? 1 : 0;
> +}
> +
> +static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
> +{
> + u8 temp, channel = sensor->ep.base.id;
> + int ret;
> +
> + OV5640_READ_REG(sensor, 0x4814, &temp);
> + temp &= ~(3 << 6);
> + temp |= (channel << 6);
> + OV5640_WRITE_REG(sensor, 0x4814, temp);
> +
> + return 0;
> +}
> +
> +static enum ov5640_mode
> +ov5640_find_nearest_mode(struct ov5640_dev *sensor,
> + int width, int height)
> +{
> + int i;
> +
> + for (i = ov5640_num_modes - 1; i >= 0; i--) {
> + if (ov5640_mode_info_data[0][i].width <= width &&
> + ov5640_mode_info_data[0][i].height <= height)
> + break;
> + }
> +
> + if (i < 0)
> + i = 0;
> +
> + return (enum ov5640_mode)i;
> +}
> +
> +/*
> + * sensor changes between scaling and subsampling, go through
> + * exposure calculation
> + */
> +static int ov5640_change_mode_exposure_calc(struct ov5640_dev *sensor,
> + enum ov5640_frame_rate frame_rate,
> + enum ov5640_mode mode)
> +{
> + struct reg_value *mode_data = NULL;
> + int mode_size = 0;
> + u8 average;
> + int prev_shutter, prev_gain16;
> + int cap_shutter, cap_gain16;
> + int cap_sysclk, cap_hts, cap_vts;
> + int light_freq, cap_bandfilt, cap_maxband;
> + long cap_gain16_shutter;
> + int ret = 0;
> +
> + /* check if the input mode and frame rate is valid */
> + mode_data = ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
> + mode_size = ov5640_mode_info_data[frame_rate][mode].init_data_size;
> +
> + sensor->fmt.width = ov5640_mode_info_data[frame_rate][mode].width;
> + sensor->fmt.height = ov5640_mode_info_data[frame_rate][mode].height;
> +
> + if (sensor->fmt.width == 0 || sensor->fmt.height == 0 ||
> + mode_data == NULL || mode_size == 0)
> + return -EINVAL;
> +
> + /* auto focus */
> + /* ov5640_auto_focus();//if no af function, just skip it */
> +
> + /* turn off AE/AG */
> + ret = ov5640_set_agc(sensor, false);
> + if (ret < 0)
> + return ret;
> +
> + /* read preview shutter */
> + ret = ov5640_get_exposure(sensor);
> + if (ret < 0)
> + return ret;
> + prev_shutter = ret;
> + ret = ov5640_binning_on(sensor);
> + if (ret < 0)
> + return ret;
> + if (ret && mode != ov5640_mode_720P_1280_720 &&
> + mode != ov5640_mode_1080P_1920_1080)
> + prev_shutter *= 2;
> +
> + /* read preview gain */
> + ret = ov5640_get_gain(sensor);
> + if (ret < 0)
> + return ret;
> + prev_gain16 = ret;
> +
> + /* get average */
> + OV5640_READ_REG(sensor, 0x56a1, &average);
> +
> + /* turn off night mode for capture */
> + ret = ov5640_set_night_mode(sensor);
> + if (ret < 0)
> + return ret;
> +
> + /* turn off overlay */
> + /* OV5640_WRITE_REG(0x3022, 0x06); //if no af function,
> + just skip it */
> +
> + ret = ov5640_set_stream(sensor, false);
> + if (ret < 0)
> + return ret;
> +
> + /* Write capture setting */
> + ret = ov5640_load_regs(sensor, mode_data, mode_size);
> + if (ret < 0)
> + return ret;
> +
> + /* read capture VTS */
> + ret = ov5640_get_VTS(sensor);
> + if (ret < 0)
> + return ret;
> + cap_vts = ret;
> + ret = ov5640_get_HTS(sensor);
> + if (ret < 0)
> + return ret;
> + cap_hts = ret;
> + ret = ov5640_get_sysclk(sensor);
> + if (ret < 0)
> + return ret;
> + cap_sysclk = ret;
> +
> + /* calculate capture banding filter */
> + ret = ov5640_get_light_freq(sensor);
> + if (ret < 0)
> + return ret;
> + light_freq = ret;
> +
> + if (light_freq == 60) {
> + /* 60Hz */
> + cap_bandfilt = cap_sysclk * 100 / cap_hts * 100 / 120;
> + } else {
> + /* 50Hz */
> + cap_bandfilt = cap_sysclk * 100 / cap_hts;
> + }
> + cap_maxband = (int)((cap_vts - 4) / cap_bandfilt);
> +
> + /* calculate capture shutter/gain16 */
> + if (average > sensor->ae_low && average < sensor->ae_high) {
> + /* in stable range */
> + cap_gain16_shutter =
> + prev_gain16 * prev_shutter *
> + cap_sysclk / sensor->prev_sysclk *
> + sensor->prev_hts / cap_hts *
> + sensor->ae_target / average;
> + } else {
> + cap_gain16_shutter =
> + prev_gain16 * prev_shutter *
> + cap_sysclk / sensor->prev_sysclk *
> + sensor->prev_hts / cap_hts;
> + }
> +
> + /* gain to shutter */
> + if (cap_gain16_shutter < (cap_bandfilt * 16)) {
> + /* shutter < 1/100 */
> + cap_shutter = cap_gain16_shutter / 16;
> + if (cap_shutter < 1)
> + cap_shutter = 1;
> +
> + cap_gain16 = cap_gain16_shutter / cap_shutter;
> + if (cap_gain16 < 16)
> + cap_gain16 = 16;
> + } else {
> + if (cap_gain16_shutter > (cap_bandfilt * cap_maxband * 16)) {
> + /* exposure reach max */
> + cap_shutter = cap_bandfilt * cap_maxband;
> + cap_gain16 = cap_gain16_shutter / cap_shutter;
> + } else {
> + /* 1/100 < (cap_shutter = n/100) =< max */
> + cap_shutter =
> + ((int)(cap_gain16_shutter / 16 / cap_bandfilt))
> + * cap_bandfilt;
> + cap_gain16 = cap_gain16_shutter / cap_shutter;
> + }
> + }
> +
> + /* write capture gain */
> + ret = ov5640_set_gain(sensor, cap_gain16);
> + if (ret < 0)
> + return ret;
> +
> + /* write capture shutter */
> + if (cap_shutter > (cap_vts - 4)) {
> + cap_vts = cap_shutter + 4;
> + ret = ov5640_set_VTS(sensor, cap_vts);
> + if (ret < 0)
> + return ret;
> + }
> +
> + ret = ov5640_set_exposure(sensor, cap_shutter);
> + if (ret < 0)
> + return ret;
> +
> + return ov5640_set_stream(sensor, true);
> +}
> +
> +/*
> + * if sensor changes inside scaling or subsampling
> + * change mode directly
> + */
> +static int ov5640_change_mode_direct(struct ov5640_dev *sensor,
> + enum ov5640_frame_rate frame_rate,
> + enum ov5640_mode mode)
> +{
> + struct reg_value *mode_data = NULL;
> + int mode_size = 0;
> + int ret = 0;
> +
> + /* check if the input mode and frame rate is valid */
> + mode_data = ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
> + mode_size = ov5640_mode_info_data[frame_rate][mode].init_data_size;
> +
> + sensor->fmt.width = ov5640_mode_info_data[frame_rate][mode].width;
> + sensor->fmt.height = ov5640_mode_info_data[frame_rate][mode].height;
> +
> + if (sensor->fmt.width == 0 || sensor->fmt.height == 0 ||
> + mode_data == NULL || mode_size == 0)
> + return -EINVAL;
> +
> + /* turn off AE/AG */
> + ret = ov5640_set_agc(sensor, false);
> + if (ret < 0)
> + return ret;
> +
> + ret = ov5640_set_stream(sensor, false);
> + if (ret < 0)
> + return ret;
> +
> + /* Write capture setting */
> + ret = ov5640_load_regs(sensor, mode_data, mode_size);
> + if (ret < 0)
> + return ret;
> +
> + ret = ov5640_set_stream(sensor, true);
> + if (ret < 0)
> + return ret;
> +
> + return ov5640_set_agc(sensor, true);
> +}
> +
> +static int ov5640_change_mode(struct ov5640_dev *sensor,
> + enum ov5640_frame_rate frame_rate,
> + enum ov5640_mode mode,
> + enum ov5640_mode orig_mode)
> +{
> + enum ov5640_downsize_mode dn_mode, orig_dn_mode;
> + struct reg_value *mode_data = NULL;
> + int mode_size = 0;
> + int ret = 0;
> +
> + if ((mode >= ov5640_num_modes || mode < ov5640_mode_MIN) &&
> + mode != ov5640_mode_INIT) {
> + v4l2_err(&sensor->sd, "Wrong ov5640 mode detected!\n");
> + return -EINVAL;
> + }
> +
> + dn_mode = ov5640_mode_info_data[frame_rate][mode].dn_mode;
> + orig_dn_mode = ov5640_mode_info_data[frame_rate][orig_mode].dn_mode;
> + if (mode == ov5640_mode_INIT) {
> + mode_data = ov5640_init_setting_30fps_VGA;
> + mode_size = ARRAY_SIZE(ov5640_init_setting_30fps_VGA);
> +
> + sensor->fmt.width = 640;
> + sensor->fmt.height = 480;
> + ret = ov5640_load_regs(sensor, mode_data, mode_size);
> + if (ret < 0)
> + return ret;
> +
> + mode_data = ov5640_setting_30fps_VGA_640_480;
> + mode_size = ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480);
> + ret = ov5640_load_regs(sensor, mode_data, mode_size);
> + } else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
> + (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
> + /* change between subsampling and scaling
> + * go through exposure calucation */
> + ret = ov5640_change_mode_exposure_calc(sensor, frame_rate,
> + mode);
> + } else {
> + /* change inside subsampling or scaling
> + * download firmware directly */
> + ret = ov5640_change_mode_direct(sensor, frame_rate, mode);
> + }
> +
> + if (ret < 0)
> + return ret;
> +
> + ret = ov5640_set_AE_target(sensor, sensor->ae_target);
> + if (ret < 0)
> + return ret;
> + ret = ov5640_get_light_freq(sensor);
> + if (ret < 0)
> + return ret;
> + ret = ov5640_set_bandingfilter(sensor);
> + if (ret < 0)
> + return ret;
> + ret = ov5640_set_virtual_channel(sensor);
> + if (ret < 0)
> + return ret;
> +
> + /* restore controls */
> + ov5640_restore_ctrls(sensor);
> +
> + if (ret >= 0 && mode != ov5640_mode_INIT) {
> + sensor->current_mode = mode;
> + sensor->current_fr = frame_rate;
> + }
> +
> + return 0;
> +}
> +
> +/* restore the last set video mode after chip power-on */
> +static int ov5640_restore_mode(struct ov5640_dev *sensor)
> +{
> + int ret = 0;
> +
> + /* first we need to set some initial register values */
> + ret = ov5640_change_mode(sensor, sensor->current_fr,
> + ov5640_mode_INIT, ov5640_mode_INIT);
> + if (ret < 0)
> + return ret;
> +
> + /* now restore the last capture mode */
> + return ov5640_change_mode(sensor,
> + sensor->current_fr,
> + sensor->current_mode,
> + ov5640_mode_VGA_640_480);
> +}
> +
> +static int ov5640_regulators_on(struct ov5640_dev *sensor)
> +{
> + int ret;
> +
> + if (sensor->io_regulator) {
> + ret = regulator_enable(sensor->io_regulator);
> + if (ret) {
> + v4l2_err(&sensor->sd, "io reg enable failed\n");
> + return ret;
> + }
> + }
> + if (sensor->core_regulator) {
> + ret = regulator_enable(sensor->core_regulator);
> + if (ret) {
> + v4l2_err(&sensor->sd, "core reg enable failed\n");
> + return ret;
> + }
> + }
> + if (sensor->gpo_regulator) {
> + ret = regulator_enable(sensor->gpo_regulator);
> + if (ret) {
> + v4l2_err(&sensor->sd, "gpo reg enable failed\n");
> + return ret;
> + }
> + }
> + if (sensor->analog_regulator) {
> + ret = regulator_enable(sensor->analog_regulator);
> + if (ret) {
> + v4l2_err(&sensor->sd, "analog reg enable failed\n");
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void ov5640_regulators_off(struct ov5640_dev *sensor)
> +{
> + if (sensor->analog_regulator)
> + regulator_disable(sensor->analog_regulator);
> + if (sensor->core_regulator)
> + regulator_disable(sensor->core_regulator);
> + if (sensor->io_regulator)
> + regulator_disable(sensor->io_regulator);
> + if (sensor->gpo_regulator)
> + regulator_disable(sensor->gpo_regulator);
> +}
> +
> +/* --------------- Subdev Operations --------------- */
> +
> +static int ov5640_s_power(struct v4l2_subdev *sd, int on)
> +{
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> + int ret;
> +
> + v4l2_info(sd, "power %s\n", on ? "ON" : "OFF");
> +
> + if (on && !sensor->on) {
> + if (sensor->xclk)
> + clk_prepare_enable(sensor->xclk);
> +
> + ret = ov5640_regulators_on(sensor);
> + if (ret)
> + return ret;
> +
> + ov5640_reset(sensor);
> + ov5640_power(sensor, true);
> +
> + ret = ov5640_init_slave_id(sensor);
> + if (ret)
> + return ret;
> +
> + ret = ov5640_restore_mode(sensor);
> + if (ret)
> + return ret;
> +
> + /*
> + * NOTE: Freescale adds a long delay (600 msec) after
> + * powering up and programming a mode on the ov5640-mipi
> + * camera (search for "msec_wait4stable" in FSL's
> + * ov5640_mipi.c), which equivalently would need to go
> + * right here. If we run into MIPI CSI-2 receiver dphy
> + * ready timeouts, it might be a clue to add that delay
> + * here.
> + */
> + } else if (!on && sensor->on) {
> + ov5640_power(sensor, false);
> +
> + ov5640_regulators_off(sensor);
> +
> + if (sensor->xclk)
> + clk_disable_unprepare(sensor->xclk);
> + }
> +
> + sensor->on = on;
> +
> + return 0;
> +}
> +
> +static int ov5640_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
> +{
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> + struct v4l2_captureparm *cparm = &a->parm.capture;
> +
> + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> + return -EINVAL;
> +
> + /* This is the only case currently handled. */
> + memset(a, 0, sizeof(*a));
> + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + cparm->capability = sensor->streamcap.capability;
> + cparm->timeperframe = sensor->streamcap.timeperframe;
> + cparm->capturemode = sensor->streamcap.capturemode;
> +
> + return 0;
> +}
> +
> +static int ov5640_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
> +{
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
> + enum ov5640_frame_rate frame_rate;
> + u32 tgt_fps; /* target frames per secound */
> + int ret = 0;
> +
> + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> + return -EINVAL;
> +
> + /* Check that the new frame rate is allowed. */
> + if ((timeperframe->numerator == 0) ||
> + (timeperframe->denominator == 0)) {
> + timeperframe->denominator = DEFAULT_FPS;
> + timeperframe->numerator = 1;
> + }
> +
> + tgt_fps = timeperframe->denominator / timeperframe->numerator;
> +
> + if (tgt_fps > MAX_FPS) {
> + timeperframe->denominator = MAX_FPS;
> + timeperframe->numerator = 1;
> + } else if (tgt_fps < MIN_FPS) {
> + timeperframe->denominator = MIN_FPS;
> + timeperframe->numerator = 1;
> + }
> +
> + /* Actual frame rate we use */
> + tgt_fps = timeperframe->denominator / timeperframe->numerator;
> +
> + if (tgt_fps == 15)
> + frame_rate = ov5640_15_fps;
> + else if (tgt_fps == 30)
> + frame_rate = ov5640_30_fps;
> + else {
> + v4l2_err(&sensor->sd, "frame rate %u not supported!\n",
> + tgt_fps);
> + return -EINVAL;
> + }
> +
> + ret = ov5640_change_mode(sensor, frame_rate,
> + sensor->current_mode,
> + sensor->current_mode);
> + if (ret < 0)
> + return ret;
> +
> + sensor->streamcap.timeperframe = *timeperframe;
> +
> + return 0;
> +}
> +
> +static int ov5640_get_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *format)
> +{
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> +
> + if (format->pad != 0)
> + return -EINVAL;
> +
> + format->format = sensor->fmt;
> +
> + return 0;
> +}
> +
> +static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
> + struct v4l2_mbus_framefmt *fmt,
> + enum ov5640_mode *new_mode)
> +{
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> + enum ov5640_mode mode;
> +
> + mode = ov5640_find_nearest_mode(sensor, fmt->width, fmt->height);
> +
> + fmt->width = ov5640_mode_info_data[0][mode].width;
> + fmt->height = ov5640_mode_info_data[0][mode].height;
> + fmt->code = sensor->fmt.code;
> +
> + if (new_mode)
> + *new_mode = mode;
> + return 0;
> +}
> +
> +static int ov5640_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *format)
> +{
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> + enum ov5640_mode new_mode;
> + int ret;
> +
> + if (format->pad != 0)
> + return -EINVAL;
> +
> + if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
> + ret = ov5640_try_fmt_internal(sd, &format->format, NULL);
> + if (ret)
> + return ret;
> + cfg->try_fmt = format->format;
> + return 0;
> + }
> +
> + ret = ov5640_try_fmt_internal(sd, &format->format, &new_mode);
> + if (ret)
> + return ret;
> +
> + ret = ov5640_change_mode(sensor, sensor->current_fr,
> + new_mode, sensor->current_mode);
> + if (ret >= 0)
> + sensor->fmt = format->format;
> +
> + return ret;
> +}
> +
> +
> +/*
> + * Sensor Controls.
> + */
> +
> +static int ov5640_set_hue(struct ov5640_dev *sensor, int value)
> +{
> + int ret;
> +
> + if (value) {
> + OV5640_MOD_REG(sensor, 0x5580, 1 << 0, 1 << 0);
> + OV5640_WRITE_REG16(sensor, 0x5581, value);
> + } else
> + OV5640_MOD_REG(sensor, 0x5580, 1 << 0, 0);
> +
> + return 0;
> +}
> +
> +static int ov5640_set_contrast(struct ov5640_dev *sensor, int value)
> +{
> + int ret;
> +
> + if (value) {
> + OV5640_MOD_REG(sensor, 0x5580, 1 << 2, 1 << 2);
> + OV5640_WRITE_REG(sensor, 0x5585, value & 0xff);
> + } else
> + OV5640_MOD_REG(sensor, 0x5580, 1 << 2, 0);
> +
> + return 0;
> +}
> +
> +static int ov5640_set_saturation(struct ov5640_dev *sensor, int value)
> +{
> + int ret;
> +
> + if (value) {
> + OV5640_MOD_REG(sensor, 0x5580, 1 << 1, 1 << 1);
> + OV5640_WRITE_REG(sensor, 0x5583, value & 0xff);
> + OV5640_WRITE_REG(sensor, 0x5584, value & 0xff);
> + } else
> + OV5640_MOD_REG(sensor, 0x5580, 1 << 1, 0);
> +
> + return 0;
> +}
> +
> +static int ov5640_set_awb(struct ov5640_dev *sensor, int value)
> +{
> + int ret;
> +
> + sensor->awb_on = value ? true : false;
> + OV5640_MOD_REG(sensor, 0x3406, 1 << 0, sensor->awb_on ? 0 : 1);
> + return 0;
> +}
> +
> +static int ov5640_set_red_balance(struct ov5640_dev *sensor, int value)
> +{
> + int ret;
> +
> + if (sensor->awb_on)
> + return -EINVAL;
> +
> + OV5640_WRITE_REG(sensor, 0x3401, value & 0xff);
> + OV5640_WRITE_REG(sensor, 0x3400, (value & 0xf00) >> 8);
> + return 0;
> +}
> +
> +#if 0
> +static int ov5640_set_green_balance(struct ov5640_dev *sensor, int value)
> +{
> + int ret;
> +
> + if (sensor->awb_on)
> + return -EINVAL;
> +
> + OV5640_WRITE_REG(sensor, 0x3403, value & 0xff);
> + OV5640_WRITE_REG(sensor, 0x3402, (value & 0xf00) >> 8);
> + return 0;
> +}
> +#endif
> +
> +static int ov5640_set_blue_balance(struct ov5640_dev *sensor, int value)
> +{
> + int ret;
> +
> + if (sensor->awb_on)
> + return -EINVAL;
> +
> + OV5640_WRITE_REG(sensor, 0x3405, value & 0xff);
> + OV5640_WRITE_REG(sensor, 0x3404, (value & 0xf00) >> 8);
> + return 0;
> +}
> +
> +static int ov5640_set_exposure(struct ov5640_dev *sensor, int value)
> +{
> + u16 max_exp = 0;
> + int ret;
> +
> + if (sensor->agc_on)
> + return -EINVAL;
> +
> + OV5640_READ_REG16(sensor, 0x350c, &max_exp);
> + if (value < max_exp) {
> + u32 exp = value << 4;
> +
> + OV5640_WRITE_REG(sensor, 0x3502, exp & 0xff);
> + OV5640_WRITE_REG(sensor, 0x3501, (exp >> 8) & 0xff);
> + OV5640_WRITE_REG(sensor, 0x3500, (exp >> 16) & 0x0f);
> + }
> +
> + return 0;
> +}
> +
> +/* read exposure, in number of line periods */
> +static int ov5640_get_exposure(struct ov5640_dev *sensor)
> +{
> + u8 temp;
> + int exp, ret;
> +
> + if (sensor->agc_on)
> + return -EINVAL;
> +
> + OV5640_READ_REG(sensor, 0x3500, &temp);
> + exp = ((int)temp & 0x0f) << 16;
> + OV5640_READ_REG(sensor, 0x3501, &temp);
> + exp |= ((int)temp << 8);
> + OV5640_READ_REG(sensor, 0x3502, &temp);
> + exp |= (int)temp;
> +
> + return exp >> 4;
> +}
> +
> +static int ov5640_set_agc(struct ov5640_dev *sensor, int value)
> +{
> + int ret;
> +
> + /* this enables/disables both AEC and AGC */
> + sensor->agc_on = value ? true : false;
> + OV5640_MOD_REG(sensor, 0x3503, 0x3, sensor->agc_on ? 0 : 0x3);
> +
> + return 0;
> +}
> +
> +static int ov5640_set_gain(struct ov5640_dev *sensor, int value)
> +{
> + int ret;
> +
> + if (sensor->agc_on)
> + return -EINVAL;
> +
> + OV5640_WRITE_REG16(sensor, 0x350a, value & 0x3ff);
> + return 0;
> +}
> +
> +static int ov5640_get_gain(struct ov5640_dev *sensor)
> +{
> + u16 gain;
> + int ret;
> +
> + if (sensor->agc_on)
> + return -EINVAL;
> +
> + OV5640_READ_REG16(sensor, 0x350a, &gain);
> +
> + return gain & 0x3ff;
> +}
> +
> +#if 0
> +static int ov5640_set_test_pattern(struct ov5640_dev *sensor, int value)
> +{
> + int ret;
> +
> + OV5640_MOD_REG(sensor, 0x503d, 0xa4, value ? 0xa4 : 0);
> + return 0;
> +}
> +#endif
> +
> +static struct ov5640_control ov5640_ctrls[] = {
> + {
> + .set = ov5640_set_agc,
> + .ctrl = {
> + .id = V4L2_CID_AUTOGAIN,
> + .name = "Auto Gain/Exposure Control",
> + .minimum = 0,
> + .maximum = 1,
> + .step = 1,
> + .default_value = 1,
> + .type = V4L2_CTRL_TYPE_BOOLEAN,
> + },
> + }, {
> + .set = ov5640_set_exposure,
> + .ctrl = {
> + .id = V4L2_CID_EXPOSURE,
> + .name = "Exposure",
> + .minimum = 0,
> + .maximum = 65535,
> + .step = 1,
> + .default_value = 0,
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + },
> + }, {
> + .set = ov5640_set_gain,
> + .ctrl = {
> + .id = V4L2_CID_GAIN,
> + .name = "Gain",
> + .minimum = 0,
> + .maximum = 1023,
> + .step = 1,
> + .default_value = 0,
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + },
> + }, {
> + .set = ov5640_set_hue,
> + .ctrl = {
> + .id = V4L2_CID_HUE,
> + .name = "Hue",
> + .minimum = 0,
> + .maximum = 359,
> + .step = 1,
> + .default_value = 0,
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + },
> + }, {
> + .set = ov5640_set_contrast,
> + .ctrl = {
> + .id = V4L2_CID_CONTRAST,
> + .name = "Contrast",
> + .minimum = 0,
> + .maximum = 255,
> + .step = 1,
> + .default_value = 0,
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + },
> + }, {
> + .set = ov5640_set_saturation,
> + .ctrl = {
> + .id = V4L2_CID_SATURATION,
> + .name = "Saturation",
> + .minimum = 0,
> + .maximum = 255,
> + .step = 1,
> + .default_value = 64,
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + },
> + }, {
> + .set = ov5640_set_awb,
> + .ctrl = {
> + .id = V4L2_CID_AUTO_WHITE_BALANCE,
> + .name = "Auto White Balance",
> + .minimum = 0,
> + .maximum = 1,
> + .step = 1,
> + .default_value = 1,
> + .type = V4L2_CTRL_TYPE_BOOLEAN,
> + },
> + }, {
> + .set = ov5640_set_red_balance,
> + .ctrl = {
> + .id = V4L2_CID_RED_BALANCE,
> + .name = "Red Balance",
> + .minimum = 0,
> + .maximum = 4095,
> + .step = 1,
> + .default_value = 0,
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + },
> + }, {
> + .set = ov5640_set_blue_balance,
> + .ctrl = {
> + .id = V4L2_CID_BLUE_BALANCE,
> + .name = "Blue Balance",
> + .minimum = 0,
> + .maximum = 4095,
> + .step = 1,
> + .default_value = 0,
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + },
> + },
> +};
> +#define OV5640_NUM_CONTROLS ARRAY_SIZE(ov5640_ctrls)
This should use v4l2_ctrl_new_std() instead of this array.
Just put a switch on ctrl->id in s_ctrl, and each case calls the corresponding
set function.
> +
> +static struct ov5640_control *ov5640_get_ctrl(int id, int *index)
> +{
> + struct ov5640_control *ret = NULL;
> + int i;
> +
> + for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
> + if (id == ov5640_ctrls[i].ctrl.id) {
> + ret = &ov5640_ctrls[i];
> + break;
> + }
> + }
> +
> + if (ret && index)
> + *index = i;
> + return ret;
> +}
> +
> +static int ov5640_restore_ctrls(struct ov5640_dev *sensor)
> +{
> + struct ov5640_control *c;
> + int i;
> +
> + for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
> + c = &ov5640_ctrls[i];
> + c->set(sensor, sensor->ctrl_cache[i]);
> + }
> +
> + return 0;
> +}
This does the same as v4l2_ctrl_handler_setup() if I understand the code correctly.
> +
> +static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct ov5640_dev *sensor = ctrl_to_ov5640_dev(ctrl);
> + struct ov5640_control *c;
> + int ret = 0;
> + int i;
> +
> + c = ov5640_get_ctrl(ctrl->id, &i);
> + if (!c)
> + return -EINVAL;
> +
> + ret = c->set(sensor, ctrl->val);
> + /* update cached value if no error */
> + if (!ret)
> + sensor->ctrl_cache[i] = ctrl->val;
> +
> + return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
> + .s_ctrl = ov5640_s_ctrl,
> +};
> +
> +static int ov5640_init_controls(struct ov5640_dev *sensor)
> +{
> + struct ov5640_control *c;
> + int i;
> +
> + v4l2_ctrl_handler_init(&sensor->ctrl_hdl, OV5640_NUM_CONTROLS);
> +
> + for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
> + c = &ov5640_ctrls[i];
> +
> + v4l2_ctrl_new_std(&sensor->ctrl_hdl, &ov5640_ctrl_ops,
> + c->ctrl.id, c->ctrl.minimum, c->ctrl.maximum,
> + c->ctrl.step, c->ctrl.default_value);
> + }
As mentioned, just drop the ov5640_ctrls array and call v4l2_ctr_new_std for each
control you're adding.
> +
> + sensor->sd.ctrl_handler = &sensor->ctrl_hdl;
> + if (sensor->ctrl_hdl.error) {
> + int err = sensor->ctrl_hdl.error;
> +
> + v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
> +
> + v4l2_err(&sensor->sd, "%s: error %d\n", __func__, err);
> + return err;
> + }
> + v4l2_ctrl_handler_setup(&sensor->ctrl_hdl);
> +
> + return 0;
> +}
> +
> +static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_frame_size_enum *fse)
> +{
> + if (fse->pad != 0)
> + return -EINVAL;
> + if (fse->index >= ov5640_num_modes)
> + return -EINVAL;
> +
> + fse->min_width = fse->max_width =
> + ov5640_mode_info_data[0][fse->index].width;
> + fse->min_height = fse->max_height =
> + ov5640_mode_info_data[0][fse->index].height;
> +
> + return 0;
> +}
> +
> +static int ov5640_enum_frame_interval(
> + struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_frame_interval_enum *fie)
> +{
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> + enum ov5640_mode mode;
> +
> + if (fie->pad != 0)
> + return -EINVAL;
> + if (fie->index < 0 || fie->index >= ov5640_num_framerates)
> + return -EINVAL;
> +
> + if (fie->width == 0 || fie->height == 0)
> + return -EINVAL;
> +
> + mode = ov5640_find_nearest_mode(sensor, fie->width, fie->height);
> +
> + if (ov5640_mode_info_data[fie->index][mode].init_data_ptr == NULL)
> + return -EINVAL;
> +
> + fie->interval.numerator = 1;
> + fie->interval.denominator = ov5640_framerates[fie->index];
> +
> + dev_dbg(sensor->dev, "%dx%d: [%d] = %d fps\n",
> + fie->width, fie->height, fie->index, fie->interval.denominator);
> + return 0;
> +}
> +
> +static int ov5640_g_input_status(struct v4l2_subdev *sd, u32 *status)
> +{
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> +
> + *status = !sensor->on ? V4L2_IN_ST_NO_POWER : 0;
> +
> + return 0;
> +}
> +
> +static int ov5640_s_routing(struct v4l2_subdev *sd, u32 input,
> + u32 output, u32 config)
> +{
> + return (input != 0) ? -EINVAL : 0;
> +}
> +
> +static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> +
> + if (code->pad != 0)
> + return -EINVAL;
> + if (code->index != 0)
> + return -EINVAL;
> +
> + code->code = sensor->fmt.code;
> +
> + return 0;
> +}
> +
> +static int ov5640_g_mbus_config(struct v4l2_subdev *sd,
> + struct v4l2_mbus_config *cfg)
> +{
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> +
> + cfg->type = V4L2_MBUS_CSI2;
> + cfg->flags = sensor->ep.bus.mipi_csi2.flags;
> + cfg->flags |= (1 << (sensor->ep.bus.mipi_csi2.num_data_lanes - 1));
> + cfg->flags |= V4L2_MBUS_CSI2_CHANNEL_0;
> +
> + return 0;
> +}
> +
> +static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> +
> + v4l2_info(sd, "stream %s\n", enable ? "ON" : "OFF");
> +
> + return ov5640_set_stream(sensor, enable);
> +}
> +
> +static struct v4l2_subdev_core_ops ov5640_core_ops = {
> + .s_power = ov5640_s_power,
> +};
> +
> +static struct v4l2_subdev_video_ops ov5640_video_ops = {
> + .s_parm = ov5640_s_parm,
> + .g_parm = ov5640_g_parm,
> + .g_input_status = ov5640_g_input_status,
> + .s_routing = ov5640_s_routing,
> + .g_mbus_config = ov5640_g_mbus_config,
> + .s_stream = ov5640_s_stream,
> +};
> +
> +static struct v4l2_subdev_pad_ops ov5640_pad_ops = {
> + .enum_mbus_code = ov5640_enum_mbus_code,
> + .get_fmt = ov5640_get_fmt,
> + .set_fmt = ov5640_set_fmt,
> + .enum_frame_size = ov5640_enum_frame_size,
> + .enum_frame_interval = ov5640_enum_frame_interval,
> +};
> +
> +static struct v4l2_subdev_ops ov5640_subdev_ops = {
> + .core = &ov5640_core_ops,
> + .video = &ov5640_video_ops,
> + .pad = &ov5640_pad_ops,
> +};
> +
> +static void ov5640_power(struct ov5640_dev *sensor, bool enable)
> +{
> + gpiod_set_value(sensor->pwdn_gpio, enable ? 0 : 1);
> +}
> +
> +static void ov5640_reset(struct ov5640_dev *sensor)
> +{
> + gpiod_set_value(sensor->reset_gpio, 0);
> +
> + /* camera power cycle */
> + ov5640_power(sensor, false);
> + usleep_range(5000, 10000);
> + ov5640_power(sensor, true);
> + usleep_range(5000, 10000);
> +
> + gpiod_set_value(sensor->reset_gpio, 1);
> + usleep_range(1000, 2000);
> +
> + gpiod_set_value(sensor->reset_gpio, 0);
> + usleep_range(5000, 10000);
> +}
> +
> +static void ov5640_get_regulators(struct ov5640_dev *sensor)
> +{
> + sensor->io_regulator = devm_regulator_get(sensor->dev, "DOVDD");
> + if (!IS_ERR(sensor->io_regulator)) {
> + regulator_set_voltage(sensor->io_regulator,
> + OV5640_VOLTAGE_DIGITAL_IO,
> + OV5640_VOLTAGE_DIGITAL_IO);
> + } else {
> + dev_dbg(sensor->dev, "%s: no io voltage reg found\n",
> + __func__);
> + sensor->io_regulator = NULL;
> + }
> +
> + sensor->core_regulator = devm_regulator_get(sensor->dev, "DVDD");
> + if (!IS_ERR(sensor->core_regulator)) {
> + regulator_set_voltage(sensor->core_regulator,
> + OV5640_VOLTAGE_DIGITAL_CORE,
> + OV5640_VOLTAGE_DIGITAL_CORE);
> + } else {
> + sensor->core_regulator = NULL;
> + dev_dbg(sensor->dev, "%s: no core voltage reg found\n",
> + __func__);
> + }
> +
> + sensor->analog_regulator = devm_regulator_get(sensor->dev, "AVDD");
> + if (!IS_ERR(sensor->analog_regulator)) {
> + regulator_set_voltage(sensor->analog_regulator,
> + OV5640_VOLTAGE_ANALOG,
> + OV5640_VOLTAGE_ANALOG);
> + } else {
> + sensor->analog_regulator = NULL;
> + dev_dbg(sensor->dev, "%s: no analog voltage reg found\n",
> + __func__);
> + }
> +}
> +
> +static int ov5640_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct device *dev = &client->dev;
> + struct device_node *endpoint;
> + struct ov5640_dev *sensor;
> + int i, xclk, ret;
> +
> + sensor = devm_kzalloc(dev, sizeof(struct ov5640_dev), GFP_KERNEL);
> + if (!sensor)
> + return -ENOMEM;
> +
> + sensor->i2c_client = client;
> + sensor->dev = dev;
> + sensor->fmt.code = MEDIA_BUS_FMT_UYVY8_2X8;
> + sensor->fmt.width = 640;
> + sensor->fmt.height = 480;
> + sensor->fmt.field = V4L2_FIELD_NONE;
> + sensor->streamcap.capability = V4L2_MODE_HIGHQUALITY |
> + V4L2_CAP_TIMEPERFRAME;
> + sensor->streamcap.capturemode = 0;
> + sensor->streamcap.timeperframe.denominator = DEFAULT_FPS;
> + sensor->streamcap.timeperframe.numerator = 1;
> +
> + sensor->current_mode = ov5640_mode_VGA_640_480;
> + sensor->current_fr = ov5640_30_fps;
> +
> + sensor->ae_target = 52;
> +
> + endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL);
> + if (!endpoint) {
> + dev_err(dev, "endpoint node not found\n");
> + return -EINVAL;
> + }
> +
> + v4l2_of_parse_endpoint(endpoint, &sensor->ep);
> + if (sensor->ep.bus_type != V4L2_MBUS_CSI2) {
> + dev_err(dev, "invalid bus type, must be MIPI CSI2\n");
> + return -EINVAL;
> + }
> + of_node_put(endpoint);
> +
> + /* get system clock (xclk) frequency */
> + ret = of_property_read_u32(dev->of_node, "xclk", &xclk);
> + if (!ret) {
> + if (xclk < OV5640_XCLK_MIN || xclk > OV5640_XCLK_MAX) {
> + dev_err(dev, "invalid xclk frequency\n");
> + return -EINVAL;
> + }
> + sensor->xclk_freq = xclk;
> + }
> +
> + /* get system clock (xclk) */
> + sensor->xclk = devm_clk_get(dev, "xclk");
> + if (!IS_ERR(sensor->xclk)) {
> + if (!sensor->xclk_freq) {
> + dev_err(dev, "xclk requires xclk frequency!\n");
> + return -EINVAL;
> + }
> + clk_set_rate(sensor->xclk, sensor->xclk_freq);
> + } else {
> + /* assume system clock enabled by default */
> + sensor->xclk = NULL;
> + }
> +
> + /* request power down pin */
> + sensor->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_HIGH);
> + if (IS_ERR(sensor->pwdn_gpio)) {
> + dev_err(dev, "request for power down gpio failed\n");
> + return PTR_ERR(sensor->pwdn_gpio);
> + }
> +
> + /* request reset pin */
> + sensor->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(sensor->reset_gpio)) {
> + dev_err(dev, "request for reset gpio failed\n");
> + return PTR_ERR(sensor->reset_gpio);
> + }
> +
> + /* initialize the cached controls to their defaults */
> + for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
> + struct ov5640_control *c = &ov5640_ctrls[i];
> +
> + sensor->ctrl_cache[i] = c->ctrl.default_value;
> + }
> + sensor->awb_on = sensor->agc_on = true;
> +
> + v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);
> +
> + sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> + sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
> + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
> + if (ret)
> + return ret;
> +
> + ov5640_get_regulators(sensor);
> +
> + ret = ov5640_s_power(&sensor->sd, 1);
> + if (ret)
> + goto entity_cleanup;
> + ret = ov5640_init_controls(sensor);
> + if (ret)
> + goto power_off;
> +
> + ret = ov5640_s_power(&sensor->sd, 0);
> + if (ret)
> + goto free_ctrls;
> +
> + ret = v4l2_async_register_subdev(&sensor->sd);
> + if (ret)
> + goto free_ctrls;
> +
> + return 0;
> +
> +free_ctrls:
> + v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
> +power_off:
> + ov5640_s_power(&sensor->sd, 0);
> +entity_cleanup:
> + media_entity_cleanup(&sensor->sd.entity);
> + ov5640_regulators_off(sensor);
> + return ret;
> +}
> +
> +/*!
> + * ov5640 I2C detach function
> + *
> + * @param client struct i2c_client *
> + * @return Error code indicating success or failure
> + */
> +static int ov5640_remove(struct i2c_client *client)
> +{
> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
> + struct ov5640_dev *sensor = to_ov5640_dev(sd);
> +
> + ov5640_regulators_off(sensor);
> +
> + v4l2_async_unregister_subdev(&sensor->sd);
> + media_entity_cleanup(&sensor->sd.entity);
> + v4l2_device_unregister_subdev(sd);
> + v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id ov5640_id[] = {
> + {"ov5640", 0},
> + {},
> +};
> +MODULE_DEVICE_TABLE(i2c, ov5640_id);
> +
> +static const struct of_device_id ov5640_dt_ids[] = {
> + { .compatible = "ovti,ov5640" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, ov5640_dt_ids);
> +
> +static struct i2c_driver ov5640_i2c_driver = {
> + .driver = {
> + .name = "ov5640",
> + .of_match_table = ov5640_dt_ids,
> + },
> + .id_table = ov5640_id,
> + .probe = ov5640_probe,
> + .remove = ov5640_remove,
> +};
> +
> +module_i2c_driver(ov5640_i2c_driver);
> +
> +MODULE_AUTHOR("Freescale Semiconductor, Inc.");
> +MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
> +MODULE_DESCRIPTION("OV5640 MIPI Camera Subdev Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION("1.0");
>
Same comments apply to the next patch, so I won't repeat them.
Regards,
Hans
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox