From 5f5b4fa522e630cfa154c59ae7649b5568913f96 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 7 Jan 2019 13:08:30 -0500 Subject: ipu3: add missing #include Lots of warning due to non-static functions are generated because the headers with define them were not included. Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/ipu3-css-params.c | 1 + drivers/staging/media/ipu3/ipu3-dmamap.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c index 776206ded83b..053edce54b71 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.c +++ b/drivers/staging/media/ipu3/ipu3-css-params.c @@ -6,6 +6,7 @@ #include "ipu3-css.h" #include "ipu3-css-fw.h" #include "ipu3-tables.h" +#include "ipu3-css-params.h" #define DIV_ROUND_CLOSEST_DOWN(a, b) (((a) + ((b) / 2) - 1) / (b)) #define roundclosest_down(a, b) (DIV_ROUND_CLOSEST_DOWN(a, b) * (b)) diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.c b/drivers/staging/media/ipu3/ipu3-dmamap.c index 93a393d4e15e..5bed01d5b8df 100644 --- a/drivers/staging/media/ipu3/ipu3-dmamap.c +++ b/drivers/staging/media/ipu3/ipu3-dmamap.c @@ -12,6 +12,7 @@ #include "ipu3.h" #include "ipu3-css-pool.h" #include "ipu3-mmu.h" +#include "ipu3-dmamap.h" /* * Free a buffer allocated by ipu3_dmamap_alloc_buffer() -- cgit v1.2.3-59-g8ed1b From e2d8ffe2e76028457759988ba6216fd13eeea01b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 13 Nov 2018 03:52:18 -0500 Subject: media: v4l2-mem2mem: add v4l2_m2m_buf_copy_data helper function Memory-to-memory devices should copy various parts of struct v4l2_buffer from the output buffer to the capture buffer. Add a helper function that does that to simplify the driver code. Signed-off-by: Hans Verkuil Reviewed-by: Paul Kocialkowski Reviewed-by: Alexandre Courbot Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 20 ++++++++++++++++++++ include/media/v4l2-mem2mem.h | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 5bbdec55b7d7..631f4e2aa942 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -975,6 +975,26 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue); +void v4l2_m2m_buf_copy_data(const struct vb2_v4l2_buffer *out_vb, + struct vb2_v4l2_buffer *cap_vb, + bool copy_frame_flags) +{ + u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + + if (copy_frame_flags) + mask |= V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME; + + cap_vb->vb2_buf.timestamp = out_vb->vb2_buf.timestamp; + + if (out_vb->flags & V4L2_BUF_FLAG_TIMECODE) + cap_vb->timecode = out_vb->timecode; + cap_vb->field = out_vb->field; + cap_vb->flags &= ~mask; + cap_vb->flags |= out_vb->flags & mask; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_copy_data); + void v4l2_m2m_request_queue(struct media_request *req) { struct media_request_object *obj, *obj_safe; diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 5467264771ec..43e447dcf69d 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -622,6 +622,26 @@ v4l2_m2m_dst_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx) return v4l2_m2m_buf_remove_by_idx(&m2m_ctx->cap_q_ctx, idx); } +/** + * v4l2_m2m_buf_copy_data() - copy buffer data from the output buffer to the + * capture buffer + * + * @out_vb: the output buffer that is the source of the data. + * @cap_vb: the capture buffer that will receive the data. + * @copy_frame_flags: copy the KEY/B/PFRAME flags as well. + * + * This helper function copies the timestamp, timecode (if the TIMECODE + * buffer flag was set), field and the TIMECODE, KEYFRAME, BFRAME, PFRAME + * and TSTAMP_SRC_MASK flags from @out_vb to @cap_vb. + * + * If @copy_frame_flags is false, then the KEYFRAME, BFRAME and PFRAME + * flags are not copied. This is typically needed for encoders that + * set this bits explicitly. + */ +void v4l2_m2m_buf_copy_data(const struct vb2_v4l2_buffer *out_vb, + struct vb2_v4l2_buffer *cap_vb, + bool copy_frame_flags); + /* v4l2 request helper */ void v4l2_m2m_request_queue(struct media_request *req); -- cgit v1.2.3-59-g8ed1b From 7aca565ee3d0febeb980545a5ce72452b401ac90 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 24 Oct 2018 06:51:20 -0400 Subject: media: vim2m: use v4l2_m2m_buf_copy_data Use the new v4l2_m2m_buf_copy_data() function in vim2m. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index d01821a6906a..33397d4a1402 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -241,17 +241,7 @@ static int device_process(struct vim2m_ctx *ctx, out_vb->sequence = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; in_vb->sequence = q_data->sequence++; - out_vb->vb2_buf.timestamp = in_vb->vb2_buf.timestamp; - - if (in_vb->flags & V4L2_BUF_FLAG_TIMECODE) - out_vb->timecode = in_vb->timecode; - out_vb->field = in_vb->field; - out_vb->flags = in_vb->flags & - (V4L2_BUF_FLAG_TIMECODE | - V4L2_BUF_FLAG_KEYFRAME | - V4L2_BUF_FLAG_PFRAME | - V4L2_BUF_FLAG_BFRAME | - V4L2_BUF_FLAG_TSTAMP_SRC_MASK); + v4l2_m2m_buf_copy_data(out_vb, in_vb, true); switch (ctx->mode) { case MEM2MEM_HFLIP | MEM2MEM_VFLIP: -- cgit v1.2.3-59-g8ed1b From 96bddd5ff590e11b47255cf06af5a0d5ccaa7807 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 24 Oct 2018 06:51:30 -0400 Subject: media: vicodec: use v4l2_m2m_buf_copy_data Use the new v4l2_m2m_buf_copy_data() function in vicodec. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 0d7876f5acf0..2378582d9790 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -190,18 +190,8 @@ static int device_process(struct vicodec_ctx *ctx, } dst_vb->sequence = q_dst->sequence++; - dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; - - if (src_vb->flags & V4L2_BUF_FLAG_TIMECODE) - dst_vb->timecode = src_vb->timecode; - dst_vb->field = src_vb->field; dst_vb->flags &= ~V4L2_BUF_FLAG_LAST; - dst_vb->flags |= src_vb->flags & - (V4L2_BUF_FLAG_TIMECODE | - V4L2_BUF_FLAG_KEYFRAME | - V4L2_BUF_FLAG_PFRAME | - V4L2_BUF_FLAG_BFRAME | - V4L2_BUF_FLAG_TSTAMP_SRC_MASK); + v4l2_m2m_buf_copy_data(src_vb, dst_vb, !ctx->is_enc); return 0; } -- cgit v1.2.3-59-g8ed1b From 0e9109ffded1cfa2aeb5617a9a683ca3467e6e99 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 13 Nov 2018 03:32:25 -0500 Subject: media: buffer.rst: clean up timecode documentation V4L2_BUF_FLAG_TIMECODE is not video capture specific, so drop that part. The 'Timecodes' section was a bit messy, so that's cleaned up. Signed-off-by: Hans Verkuil Reviewed-by: Paul Kocialkowski Reviewed-by: Alexandre Courbot Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/buffer.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Documentation/media/uapi/v4l/buffer.rst b/Documentation/media/uapi/v4l/buffer.rst index 86878bb0087f..81ffdcb89057 100644 --- a/Documentation/media/uapi/v4l/buffer.rst +++ b/Documentation/media/uapi/v4l/buffer.rst @@ -230,8 +230,7 @@ struct v4l2_buffer * - struct :c:type:`v4l2_timecode` - ``timecode`` - - - When ``type`` is ``V4L2_BUF_TYPE_VIDEO_CAPTURE`` and the - ``V4L2_BUF_FLAG_TIMECODE`` flag is set in ``flags``, this + - When the ``V4L2_BUF_FLAG_TIMECODE`` flag is set in ``flags``, this structure contains a frame timecode. In :c:type:`V4L2_FIELD_ALTERNATE ` mode the top and bottom field contain the same timecode. Timecodes are intended to @@ -714,10 +713,10 @@ enum v4l2_memory Timecodes ========= -The struct :c:type:`v4l2_timecode` structure is designed to hold a -:ref:`smpte12m` or similar timecode. (struct -struct :c:type:`timeval` timestamps are stored in struct -:c:type:`v4l2_buffer` field ``timestamp``.) +The :c:type:`v4l2_buffer_timecode` structure is designed to hold a +:ref:`smpte12m` or similar timecode. +(struct :c:type:`timeval` timestamps are stored in the struct +:c:type:`v4l2_buffer` ``timestamp`` field.) .. c:type:: v4l2_timecode -- cgit v1.2.3-59-g8ed1b From c2eb8effb265ac5cdd960d8e61ecb931e9c767cd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 24 Oct 2018 06:50:34 -0400 Subject: media: videodev2.h: add v4l2_timeval_to_ns inline function We want to be able to uniquely identify buffers for stateless codecs. The internal timestamp (a u64) as stored internally in the kernel is a suitable candidate for that, but in struct v4l2_buffer it is represented as a struct timeval. Add a v4l2_timeval_to_ns() function that converts the struct timeval into a u64 in the same way that the kernel does. This makes it possible to use this u64 elsewhere as a unique identifier of the buffer. Since timestamps are also copied from the output buffer to the corresponding capture buffer(s) by M2M devices, the u64 can be used to refer to both output and capture buffers. The plan is that in the future we redesign struct v4l2_buffer and use u64 for the timestamp instead of a struct timeval (which has lots of problems with 32 vs 64 bit and y2038 layout changes), and then there is no more need to use this function. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index b5671ce2724f..d6eed479c3a6 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -973,6 +973,18 @@ struct v4l2_buffer { }; }; +/** + * v4l2_timeval_to_ns - Convert timeval to nanoseconds + * @ts: pointer to the timeval variable to be converted + * + * Returns the scalar nanosecond representation of the timeval + * parameter. + */ +static inline __u64 v4l2_timeval_to_ns(const struct timeval *tv) +{ + return (__u64)tv->tv_sec * 1000000000ULL + tv->tv_usec * 1000; +} + /* Flags for 'flags' field */ /* Buffer is mapped (flag) */ #define V4L2_BUF_FLAG_MAPPED 0x00000001 -- cgit v1.2.3-59-g8ed1b From 245ede423b43a6e081e94e0e5d4e895bd1f31228 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 24 Oct 2018 06:51:01 -0400 Subject: media: vb2: add vb2_find_timestamp() Use v4l2_timeval_to_ns instead of timeval_to_ns to ensure that both kernelspace and userspace will use the same conversion function. Next add a new vb2_find_timestamp() function to find buffers with a specific timestamp. This function will only look at DEQUEUED and DONE buffers, i.e. buffers that are already processed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 19 ++++++++++++++++++- include/media/videobuf2-v4l2.h | 17 +++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 3a0ca2f9854f..75ea90e795d8 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -143,7 +143,7 @@ static void __copy_timestamp(struct vb2_buffer *vb, const void *pb) * and the timecode field and flag if needed. */ if (q->copy_timestamp) - vb->timestamp = timeval_to_ns(&b->timestamp); + vb->timestamp = v4l2_timeval_to_ns(&b->timestamp); vbuf->flags |= b->flags & V4L2_BUF_FLAG_TIMECODE; if (b->flags & V4L2_BUF_FLAG_TIMECODE) vbuf->timecode = b->timecode; @@ -589,6 +589,23 @@ static const struct vb2_buf_ops v4l2_buf_ops = { .copy_timestamp = __copy_timestamp, }; +int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp, + unsigned int start_idx) +{ + unsigned int i; + + for (i = start_idx; i < q->num_buffers; i++) { + struct vb2_buffer *vb = q->bufs[i]; + + if ((vb->state == VB2_BUF_STATE_DEQUEUED || + vb->state == VB2_BUF_STATE_DONE) && + vb->timestamp == timestamp) + return i; + } + return -1; +} +EXPORT_SYMBOL_GPL(vb2_find_timestamp); + /* * vb2_querybuf() - query video buffer information * @q: videobuf queue diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h index 727855463838..a9961bc776dc 100644 --- a/include/media/videobuf2-v4l2.h +++ b/include/media/videobuf2-v4l2.h @@ -55,6 +55,23 @@ struct vb2_v4l2_buffer { #define to_vb2_v4l2_buffer(vb) \ container_of(vb, struct vb2_v4l2_buffer, vb2_buf) +/** + * vb2_find_timestamp() - Find buffer with given timestamp in the queue + * + * @q: pointer to &struct vb2_queue with videobuf2 queue. + * @timestamp: the timestamp to find. Only buffers in state DEQUEUED or DONE + * are considered. + * @start_idx: the start index (usually 0) in the buffer array to start + * searching from. Note that there may be multiple buffers + * with the same timestamp value, so you can restart the search + * by setting @start_idx to the previously found index + 1. + * + * Returns the buffer index of the buffer with the given @timestamp, or + * -1 if no buffer with @timestamp was found. + */ +int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp, + unsigned int start_idx); + int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b); /** -- cgit v1.2.3-59-g8ed1b From d998e03e322fc497454f584adfc35743713a44c5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 9 Nov 2018 04:16:21 -0500 Subject: media: cedrus: identify buffers by timestamp Use the new v4l2_m2m_buf_copy_data helper function and use timestamps to refer to reference frames instead of using buffer indices. Also remove the padding fields in the structs, that's a bad idea. Just use the right types to keep everything aligned. Signed-off-by: Hans Verkuil Tested-by: Paul Kocialkowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 9 --------- drivers/staging/media/sunxi/cedrus/cedrus.h | 9 ++++++--- drivers/staging/media/sunxi/cedrus/cedrus_dec.c | 2 ++ drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c | 23 +++++++++++------------ include/media/mpeg2-ctrls.h | 14 +++++--------- 5 files changed, 24 insertions(+), 33 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5e3806feb5d7..e3bd441fa29a 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1661,15 +1661,6 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, return -EINVAL; } - if (p_mpeg2_slice_params->backward_ref_index >= VIDEO_MAX_FRAME || - p_mpeg2_slice_params->forward_ref_index >= VIDEO_MAX_FRAME) - return -EINVAL; - - if (p_mpeg2_slice_params->pad || - p_mpeg2_slice_params->picture.pad || - p_mpeg2_slice_params->sequence.pad) - return -EINVAL; - return 0; case V4L2_CTRL_TYPE_MPEG2_QUANTIZATION: diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index 3acfdcf83691..4aedd24a9848 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -140,11 +140,14 @@ static inline dma_addr_t cedrus_buf_addr(struct vb2_buffer *buf, } static inline dma_addr_t cedrus_dst_buf_addr(struct cedrus_ctx *ctx, - unsigned int index, - unsigned int plane) + int index, unsigned int plane) { - struct vb2_buffer *buf = ctx->dst_bufs[index]; + struct vb2_buffer *buf; + if (index < 0) + return 0; + + buf = ctx->dst_bufs[index]; return buf ? cedrus_buf_addr(buf, &ctx->dst_fmt, plane) : 0; } diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 591d191d4286..443fb037e1cf 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -50,6 +50,8 @@ void cedrus_device_run(void *priv) break; } + v4l2_m2m_buf_copy_data(run.src, run.dst, true); + dev->dec_ops[ctx->current_codec]->setup(ctx, &run); /* Complete request(s) controls if needed. */ diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index 9abd39cae38c..cb45fda9aaeb 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -82,7 +82,10 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) dma_addr_t fwd_luma_addr, fwd_chroma_addr; dma_addr_t bwd_luma_addr, bwd_chroma_addr; struct cedrus_dev *dev = ctx->dev; + struct vb2_queue *cap_q = &ctx->fh.m2m_ctx->cap_q_ctx.q; const u8 *matrix; + int forward_idx; + int backward_idx; unsigned int i; u32 reg; @@ -156,23 +159,19 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_PICBOUNDSIZE, reg); /* Forward and backward prediction reference buffers. */ + forward_idx = vb2_find_timestamp(cap_q, + slice_params->forward_ref_ts, 0); - fwd_luma_addr = cedrus_dst_buf_addr(ctx, - slice_params->forward_ref_index, - 0); - fwd_chroma_addr = cedrus_dst_buf_addr(ctx, - slice_params->forward_ref_index, - 1); + fwd_luma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 0); + fwd_chroma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 1); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr); - bwd_luma_addr = cedrus_dst_buf_addr(ctx, - slice_params->backward_ref_index, - 0); - bwd_chroma_addr = cedrus_dst_buf_addr(ctx, - slice_params->backward_ref_index, - 1); + backward_idx = vb2_find_timestamp(cap_q, + slice_params->backward_ref_ts, 0); + bwd_luma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 0); + bwd_chroma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 1); cedrus_write(dev, VE_DEC_MPEG_BWD_REF_LUMA_ADDR, bwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_BWD_REF_CHROMA_ADDR, bwd_chroma_addr); diff --git a/include/media/mpeg2-ctrls.h b/include/media/mpeg2-ctrls.h index d21f40edc09e..6601455b3d5e 100644 --- a/include/media/mpeg2-ctrls.h +++ b/include/media/mpeg2-ctrls.h @@ -30,10 +30,9 @@ struct v4l2_mpeg2_sequence { __u32 vbv_buffer_size; /* ISO/IEC 13818-2, ITU-T Rec. H.262: Sequence extension */ - __u8 profile_and_level_indication; + __u16 profile_and_level_indication; __u8 progressive_sequence; __u8 chroma_format; - __u8 pad; }; struct v4l2_mpeg2_picture { @@ -51,23 +50,20 @@ struct v4l2_mpeg2_picture { __u8 intra_vlc_format; __u8 alternate_scan; __u8 repeat_first_field; - __u8 progressive_frame; - __u8 pad; + __u16 progressive_frame; }; struct v4l2_ctrl_mpeg2_slice_params { __u32 bit_size; __u32 data_bit_offset; + __u64 backward_ref_ts; + __u64 forward_ref_ts; struct v4l2_mpeg2_sequence sequence; struct v4l2_mpeg2_picture picture; /* ISO/IEC 13818-2, ITU-T Rec. H.262: Slice */ - __u8 quantiser_scale_code; - - __u8 backward_ref_index; - __u8 forward_ref_index; - __u8 pad; + __u32 quantiser_scale_code; }; struct v4l2_ctrl_mpeg2_quantization { -- cgit v1.2.3-59-g8ed1b From bb03641f8a0d7fc7efcdafb02a50aefe8ef1c17d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 5 Dec 2018 05:30:55 -0500 Subject: media: extended-controls.rst: update the mpeg2 compound controls The layout of the compound controls has changed to fix 32/64 bit alignment issues and the use of timestamps instead of buffer indices to refer to buffers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/extended-controls.rst | 28 +++++++++++++--------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index 286a2dd7ec36..af4273aa5e85 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -1546,17 +1546,23 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - - ``picture`` - Structure with MPEG-2 picture metadata, merging relevant fields from the picture header and picture coding extension parts of the bitstream. - * - __u8 + * - __u64 + - ``backward_ref_ts`` + - Timestamp of the V4L2 capture buffer to use as backward reference, used + with B-coded and P-coded frames. The timestamp refers to the + ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the + :c:func:`v4l2_timeval_to_ns()` function to convert the struct + :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. + * - __u64 + - ``forward_ref_ts`` + - Timestamp for the V4L2 capture buffer to use as forward reference, used + with B-coded frames. The timestamp refers to the ``timestamp`` field in + struct :c:type:`v4l2_buffer`. Use the :c:func:`v4l2_timeval_to_ns()` + function to convert the struct :c:type:`timeval` in struct + :c:type:`v4l2_buffer` to a __u64. + * - __u32 - ``quantiser_scale_code`` - Code used to determine the quantization scale to use for the IDCT. - * - __u8 - - ``backward_ref_index`` - - Index for the V4L2 buffer to use as backward reference, used with - B-coded and P-coded frames. - * - __u8 - - ``forward_ref_index`` - - Index for the V4L2 buffer to use as forward reference, used with - B-coded frames. .. c:type:: v4l2_mpeg2_sequence @@ -1577,7 +1583,7 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - - ``vbv_buffer_size`` - Used to calculate the required size of the video buffering verifier, defined (in bits) as: 16 * 1024 * vbv_buffer_size. - * - __u8 + * - __u16 - ``profile_and_level_indication`` - The current profile and level indication as extracted from the bitstream. @@ -1635,7 +1641,7 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - * - __u8 - ``repeat_first_field`` - This flag affects the decoding process of progressive frames. - * - __u8 + * - __u16 - ``progressive_frame`` - Indicates whether the current frame is progressive. -- cgit v1.2.3-59-g8ed1b From 1b4fd9de6ec7f3722c2b3e08cc5ad171c11f93be Mon Sep 17 00:00:00 2001 From: "French, Nicholas A" Date: Sun, 9 Dec 2018 02:11:18 -0500 Subject: media: lgdt330x: fix lock status reporting A typo in code cleanup commit db9c1007bc07 ("media: lgdt330x: do some cleanups at status logic") broke the FE_HAS_LOCK reporting for 3303 chips by inadvertently modifying the register mask. The broken lock status is critial as it prevents video capture cards from reporting signal strength, scanning for channels, and capturing video. Fix regression by reverting mask change. Cc: stable@vger.kernel.org # Kernel 4.17+ Fixes: db9c1007bc07 ("media: lgdt330x: do some cleanups at status logic") Signed-off-by: Nick French Reviewed-by: Laurent Pinchart Tested-by: Adam Stylinski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/lgdt330x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index 96807e134886..8abb1a510a81 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -783,7 +783,7 @@ static int lgdt3303_read_status(struct dvb_frontend *fe, if ((buf[0] & 0x02) == 0x00) *status |= FE_HAS_SYNC; - if ((buf[0] & 0xfd) == 0x01) + if ((buf[0] & 0x01) == 0x01) *status |= FE_HAS_VITERBI | FE_HAS_LOCK; break; default: -- cgit v1.2.3-59-g8ed1b From d706f52120f69f5bca53db081f682cb56f5186d6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 14 Dec 2018 07:27:54 -0500 Subject: media: Documentation: staging/ipu3-imgu: Fix reference file name The intel-ipu3.h intended-to-be-uAPI header is currently under drivers/staging/media/ipu3/include/, not include/uapi/linux. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/v4l-drivers/ipu3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/media/v4l-drivers/ipu3.rst b/Documentation/media/v4l-drivers/ipu3.rst index f89b51dafadd..3369aabf919e 100644 --- a/Documentation/media/v4l-drivers/ipu3.rst +++ b/Documentation/media/v4l-drivers/ipu3.rst @@ -358,7 +358,7 @@ The source can be located under hal/intel directory. References ========== -.. [#f5] include/uapi/linux/intel-ipu3.h +.. [#f5] drivers/staging/media/ipu3/include/intel-ipu3.h .. [#f1] https://github.com/intel/nvt -- cgit v1.2.3-59-g8ed1b From 3eefa620d08dc6aa9323e409f1a307783c528004 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 14 Dec 2018 16:53:43 -0500 Subject: media: Documentation: staging/ipu3-imgu: Add license information The driver documentation is under GPL v2 and the uAPI documentation under GNU FDL 1.1+ (without invariant sections) or GPL v2. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../media/uapi/v4l/pixfmt-meta-intel-ipu3.rst | 25 +++++++++++++++++++++- Documentation/media/v4l-drivers/ipu3.rst | 2 ++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst b/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst index dc871006b41a..659e58aa9c93 100644 --- a/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst +++ b/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst @@ -1,4 +1,27 @@ -.. -*- coding: utf-8; mode: rst -*- +.. This file is dual-licensed: you can use it either under the terms +.. of the GPL 2.0 or the GFDL 1.1+ license, at your option. Note that this +.. dual licensing only applies to this file, and not this project as a +.. whole. +.. +.. a) This file is free software; you can redistribute it and/or +.. modify it under the terms of the GNU General Public License version +.. 2.0 as published by the Free Software Foundation. +.. +.. This file 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 version 2.0 for more details. +.. +.. Or, alternatively, +.. +.. b) Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections .. _v4l2-meta-fmt-params: .. _v4l2-meta-fmt-stat-3a: diff --git a/Documentation/media/v4l-drivers/ipu3.rst b/Documentation/media/v4l-drivers/ipu3.rst index 3369aabf919e..804f37300623 100644 --- a/Documentation/media/v4l-drivers/ipu3.rst +++ b/Documentation/media/v4l-drivers/ipu3.rst @@ -1,3 +1,5 @@ +.. SPDX-License-Identifier: GPL-2.0 + .. include:: =============================================================== -- cgit v1.2.3-59-g8ed1b From 07758747a2d7963593421b4e782b0f20a80f58d3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 17 Dec 2018 07:14:35 -0500 Subject: media: platform: Fix missing spin_lock_init() The driver allocates the spinlock but not initialize it. Use spin_lock_init() on it to initialize it correctly. This is detected by Coccinelle semantic patch. Fixes: d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver") Signed-off-by: Wei Yongjun Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/aspeed-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index dfec813f50a9..692e08ef38c0 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -1661,6 +1661,7 @@ static int aspeed_video_probe(struct platform_device *pdev) video->frame_rate = 30; video->dev = &pdev->dev; + spin_lock_init(&video->lock); mutex_init(&video->video_lock); init_waitqueue_head(&video->wait); INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work); -- cgit v1.2.3-59-g8ed1b From 1e0d0a5fd38192f23304ea2fc2b531fea7c74247 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 7 Jan 2019 07:04:14 -0500 Subject: media: s5p-mfc: fix incorrect bus assignment in virtual child device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Virtual MFC codec's child devices must not be assigned to platform bus, because they are allocated as raw 'struct device' and don't have the corresponding 'platform' part. This fixes NULL pointer access revealed recently by commit a66d972465d1 ("devres: Align data[] to ARCH_KMALLOC_MINALIGN"). Fixes: c79667dd93b0 ("media: s5p-mfc: replace custom reserved memory handling code with generic one") Reported-by: Paweł Chmiel Signed-off-by: Marek Szyprowski Tested-by: Paweł Chmiel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 8a5ba3bec3af..0a9f59d89185 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1089,7 +1089,6 @@ static struct device *s5p_mfc_alloc_memdev(struct device *dev, device_initialize(child); dev_set_name(child, "%s:%s", dev_name(dev), name); child->parent = dev; - child->bus = dev->bus; child->coherent_dma_mask = dev->coherent_dma_mask; child->dma_mask = dev->dma_mask; child->release = s5p_mfc_memdev_release; -- cgit v1.2.3-59-g8ed1b From c1d5fb019f65e1093c77734356f97b6e7ac4d629 Mon Sep 17 00:00:00 2001 From: "Matwey V. Kornilov" Date: Fri, 9 Nov 2018 14:03:26 -0500 Subject: media: usb: pwc: Introduce TRACE_EVENTs for pwc_isoc_handler() There were reports that PWC-based webcams don't work at some embedded ARM platforms. [1] Isochronous transfer handler seems to work too long leading to the issues in MUSB USB host subsystem. Also note, that urb->giveback() handlers are still called with disabled interrupts. In order to be able to measure performance of PWC driver, traces are introduced in URB handler section. [1] https://www.spinics.net/lists/linux-usb/msg165735.html Signed-off-by: Matwey V. Kornilov Reviewed-by: Steven Rostedt (VMware) Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc-if.c | 7 +++++ include/trace/events/pwc.h | 65 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 include/trace/events/pwc.h diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 72704f4d5330..53c111bd5a22 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -76,6 +76,9 @@ #include "pwc-dec23.h" #include "pwc-dec1.h" +#define CREATE_TRACE_POINTS +#include + /* Function prototypes and driver templates */ /* hotplug device table support */ @@ -260,6 +263,8 @@ static void pwc_isoc_handler(struct urb *urb) int i, fst, flen; unsigned char *iso_buf = NULL; + trace_pwc_handler_enter(urb, pdev); + if (urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN) { PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronously.\n", @@ -348,6 +353,8 @@ static void pwc_isoc_handler(struct urb *urb) } handler_end: + trace_pwc_handler_exit(urb, pdev); + i = usb_submit_urb(urb, GFP_ATOMIC); if (i != 0) PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); diff --git a/include/trace/events/pwc.h b/include/trace/events/pwc.h new file mode 100644 index 000000000000..a2da764a3b41 --- /dev/null +++ b/include/trace/events/pwc.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#if !defined(_TRACE_PWC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_PWC_H + +#include +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM pwc + +TRACE_EVENT(pwc_handler_enter, + TP_PROTO(struct urb *urb, struct pwc_device *pdev), + TP_ARGS(urb, pdev), + TP_STRUCT__entry( + __field(struct urb*, urb) + __field(struct pwc_frame_buf*, fbuf) + __field(int, urb__status) + __field(u32, urb__actual_length) + __field(int, fbuf__filled) + __string(name, pdev->v4l2_dev.name) + ), + TP_fast_assign( + __entry->urb = urb; + __entry->fbuf = pdev->fill_buf; + __entry->urb__status = urb->status; + __entry->urb__actual_length = urb->actual_length; + __entry->fbuf__filled = (pdev->fill_buf + ? pdev->fill_buf->filled : 0); + __assign_str(name, pdev->v4l2_dev.name); + ), + TP_printk("dev=%s (fbuf=%p filled=%d) urb=%p (status=%d actual_length=%u)", + __get_str(name), + __entry->fbuf, + __entry->fbuf__filled, + __entry->urb, + __entry->urb__status, + __entry->urb__actual_length) +); + +TRACE_EVENT(pwc_handler_exit, + TP_PROTO(struct urb *urb, struct pwc_device *pdev), + TP_ARGS(urb, pdev), + TP_STRUCT__entry( + __field(struct urb*, urb) + __field(struct pwc_frame_buf*, fbuf) + __field(int, fbuf__filled) + __string(name, pdev->v4l2_dev.name) + ), + TP_fast_assign( + __entry->urb = urb; + __entry->fbuf = pdev->fill_buf; + __entry->fbuf__filled = pdev->fill_buf->filled; + __assign_str(name, pdev->v4l2_dev.name); + ), + TP_printk(" dev=%s (fbuf=%p filled=%d) urb=%p", + __get_str(name), + __entry->fbuf, + __entry->fbuf__filled, + __entry->urb) +); + +#endif /* _TRACE_PWC_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3-59-g8ed1b From 1161db6776bd1c35257e1e362e91bcb1b3fe4347 Mon Sep 17 00:00:00 2001 From: "Matwey V. Kornilov" Date: Fri, 9 Nov 2018 14:03:27 -0500 Subject: media: usb: pwc: Don't use coherent DMA buffers for ISO transfer DMA cocherency slows the transfer down on systems without hardware coherent DMA. Instead we use noncocherent DMA memory and explicit sync at data receive handler. Based on previous commit the following performance benchmarks have been carried out. Average memcpy() data transfer rate (rate) and handler completion time (time) have been measured when running video stream at 640x480 resolution at 10fps. x86_64 based system (Intel Core i5-3470). This platform has hardware coherent DMA support and proposed change doesn't make big difference here. * kmalloc: rate = (2.0 +- 0.4) GBps time = (5.0 +- 3.0) usec * usb_alloc_coherent: rate = (3.4 +- 1.2) GBps time = (3.5 +- 3.0) usec We see that the measurements agree within error ranges in this case. So theoretically predicted performance downgrade cannot be reliably measured here. armv7l based system (TI AM335x BeagleBone Black @ 300MHz). This platform has no hardware coherent DMA support. DMA coherence is implemented via disabled page caching that slows down memcpy() due to memory controller behaviour. * kmalloc: rate = ( 94 +- 4) MBps time = (101 +- 4) usec * usb_alloc_coherent: rate = (28.1 +- 0.1) MBps time = (341 +- 2) usec Note, that quantative difference leads (this commit leads to 3.3 times acceleration) to qualitative behavior change in this case. As it was stated before, the video stream cannot be successfully received at AM335x platforms with MUSB based USB host controller due to performance issues [1]. [1] https://www.spinics.net/lists/linux-usb/msg165735.html Signed-off-by: Matwey V. Kornilov Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc-if.c | 62 +++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 53c111bd5a22..a81fb319b339 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -159,6 +159,32 @@ static const struct video_device pwc_template = { /***************************************************************************/ /* Private functions */ +static void *pwc_alloc_urb_buffer(struct device *dev, + size_t size, dma_addr_t *dma_handle) +{ + void *buffer = kmalloc(size, GFP_KERNEL); + + if (!buffer) + return NULL; + + *dma_handle = dma_map_single(dev, buffer, size, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, *dma_handle)) { + kfree(buffer); + return NULL; + } + + return buffer; +} + +static void pwc_free_urb_buffer(struct device *dev, + size_t size, + void *buffer, + dma_addr_t dma_handle) +{ + dma_unmap_single(dev, dma_handle, size, DMA_FROM_DEVICE); + kfree(buffer); +} + static struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev) { unsigned long flags = 0; @@ -306,6 +332,11 @@ static void pwc_isoc_handler(struct urb *urb) /* Reset ISOC error counter. We did get here, after all. */ pdev->visoc_errors = 0; + dma_sync_single_for_cpu(&urb->dev->dev, + urb->transfer_dma, + urb->transfer_buffer_length, + DMA_FROM_DEVICE); + /* vsync: 0 = don't copy data 1 = sync-hunt 2 = synched @@ -352,6 +383,11 @@ static void pwc_isoc_handler(struct urb *urb) pdev->vlast_packet_size = flen; } + dma_sync_single_for_device(&urb->dev->dev, + urb->transfer_dma, + urb->transfer_buffer_length, + DMA_FROM_DEVICE); + handler_end: trace_pwc_handler_exit(urb, pdev); @@ -428,16 +464,15 @@ retry: urb->dev = udev; urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->transfer_buffer = usb_alloc_coherent(udev, - ISO_BUFFER_SIZE, - GFP_KERNEL, - &urb->transfer_dma); + urb->transfer_buffer_length = ISO_BUFFER_SIZE; + urb->transfer_buffer = pwc_alloc_urb_buffer(&udev->dev, + urb->transfer_buffer_length, + &urb->transfer_dma); if (urb->transfer_buffer == NULL) { PWC_ERROR("Failed to allocate urb buffer %d\n", i); pwc_isoc_cleanup(pdev); return -ENOMEM; } - urb->transfer_buffer_length = ISO_BUFFER_SIZE; urb->complete = pwc_isoc_handler; urb->context = pdev; urb->start_frame = 0; @@ -488,15 +523,16 @@ static void pwc_iso_free(struct pwc_device *pdev) /* Freeing ISOC buffers one by one */ for (i = 0; i < MAX_ISO_BUFS; i++) { - if (pdev->urbs[i]) { + struct urb *urb = pdev->urbs[i]; + + if (urb) { PWC_DEBUG_MEMORY("Freeing URB\n"); - if (pdev->urbs[i]->transfer_buffer) { - usb_free_coherent(pdev->udev, - pdev->urbs[i]->transfer_buffer_length, - pdev->urbs[i]->transfer_buffer, - pdev->urbs[i]->transfer_dma); - } - usb_free_urb(pdev->urbs[i]); + if (urb->transfer_buffer) + pwc_free_urb_buffer(&urb->dev->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); + usb_free_urb(urb); pdev->urbs[i] = NULL; } } -- cgit v1.2.3-59-g8ed1b From 986ac47eaff77892a6393b4685c36ab9c9a4e089 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 12 Dec 2018 12:36:43 -0500 Subject: media: MAINTAINERS: added include/trace/events/pwc.h Added include/trace/events/pwc.h to the list of files. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 32d444476a90..f916b6ca5a42 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12368,6 +12368,7 @@ L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git S: Odd Fixes F: drivers/media/usb/pwc/* +F: include/trace/events/pwc.h PWM FAN DRIVER M: Kamil Debski -- cgit v1.2.3-59-g8ed1b From 6c67fa07a3b43dd83158e55584addae00d38a514 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 7 Dec 2018 11:31:34 -0500 Subject: media: vsp1: Fix trivial documentation In the partition sizing the term 'prevents' is inappropriately pluralized. Simplify to 'prevent'. Signed-off-by: Kieran Bingham Reviewed-by: Simon Horman Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 771dfe1f7c20..7ceaf3222145 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -223,7 +223,7 @@ static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe, * If the modulus is less than half of the partition size, * the penultimate partition is reduced to half, which is added * to the final partition: |1234|1234|1234|12|341| - * to prevents this: |1234|1234|1234|1234|1|. + * to prevent this: |1234|1234|1234|1234|1|. */ if (modulus) { /* -- cgit v1.2.3-59-g8ed1b From aa8a1012ba624e92fd1ab58463f22501bce78af8 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 10 Dec 2018 09:53:55 -0500 Subject: media: rcar-csi2: Fix PHTW table values for E3/V3M MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PHTW selection algorithm implemented in rcsi2_phtw_write_mbps() checks for lower bound of the interval used to match the desired bandwidth. Use that in place of the currently used upper bound. Fixes: 10c08812fe60 ("media: rcar: rcar-csi2: Update V3M/E3 PHTW tables") Signed-off-by: Jacopo Mondi Acked-by: Niklas Söderlund Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-csi2.c | 62 ++++++++++++++--------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 6d356f5a9456..b2bd0b9627d9 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -152,37 +152,37 @@ static const struct rcsi2_mbps_reg phtw_mbps_h3_v3h_m3n[] = { }; static const struct rcsi2_mbps_reg phtw_mbps_v3m_e3[] = { - { .mbps = 89, .reg = 0x00 }, - { .mbps = 99, .reg = 0x20 }, - { .mbps = 109, .reg = 0x40 }, - { .mbps = 129, .reg = 0x02 }, - { .mbps = 139, .reg = 0x22 }, - { .mbps = 149, .reg = 0x42 }, - { .mbps = 169, .reg = 0x04 }, - { .mbps = 179, .reg = 0x24 }, - { .mbps = 199, .reg = 0x44 }, - { .mbps = 219, .reg = 0x06 }, - { .mbps = 239, .reg = 0x26 }, - { .mbps = 249, .reg = 0x46 }, - { .mbps = 269, .reg = 0x08 }, - { .mbps = 299, .reg = 0x28 }, - { .mbps = 329, .reg = 0x0a }, - { .mbps = 359, .reg = 0x2a }, - { .mbps = 399, .reg = 0x4a }, - { .mbps = 449, .reg = 0x0c }, - { .mbps = 499, .reg = 0x2c }, - { .mbps = 549, .reg = 0x0e }, - { .mbps = 599, .reg = 0x2e }, - { .mbps = 649, .reg = 0x10 }, - { .mbps = 699, .reg = 0x30 }, - { .mbps = 749, .reg = 0x12 }, - { .mbps = 799, .reg = 0x32 }, - { .mbps = 849, .reg = 0x52 }, - { .mbps = 899, .reg = 0x72 }, - { .mbps = 949, .reg = 0x14 }, - { .mbps = 999, .reg = 0x34 }, - { .mbps = 1049, .reg = 0x54 }, - { .mbps = 1099, .reg = 0x74 }, + { .mbps = 80, .reg = 0x00 }, + { .mbps = 90, .reg = 0x20 }, + { .mbps = 100, .reg = 0x40 }, + { .mbps = 110, .reg = 0x02 }, + { .mbps = 130, .reg = 0x22 }, + { .mbps = 140, .reg = 0x42 }, + { .mbps = 150, .reg = 0x04 }, + { .mbps = 170, .reg = 0x24 }, + { .mbps = 180, .reg = 0x44 }, + { .mbps = 200, .reg = 0x06 }, + { .mbps = 220, .reg = 0x26 }, + { .mbps = 240, .reg = 0x46 }, + { .mbps = 250, .reg = 0x08 }, + { .mbps = 270, .reg = 0x28 }, + { .mbps = 300, .reg = 0x0a }, + { .mbps = 330, .reg = 0x2a }, + { .mbps = 360, .reg = 0x4a }, + { .mbps = 400, .reg = 0x0c }, + { .mbps = 450, .reg = 0x2c }, + { .mbps = 500, .reg = 0x0e }, + { .mbps = 550, .reg = 0x2e }, + { .mbps = 600, .reg = 0x10 }, + { .mbps = 650, .reg = 0x30 }, + { .mbps = 700, .reg = 0x12 }, + { .mbps = 750, .reg = 0x32 }, + { .mbps = 800, .reg = 0x52 }, + { .mbps = 850, .reg = 0x72 }, + { .mbps = 900, .reg = 0x14 }, + { .mbps = 950, .reg = 0x34 }, + { .mbps = 1000, .reg = 0x54 }, + { .mbps = 1050, .reg = 0x74 }, { .mbps = 1125, .reg = 0x16 }, { /* sentinel */ }, }; -- cgit v1.2.3-59-g8ed1b From 66e988e96b31d0c06471234cabb0a77316841fc3 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 13 Dec 2018 08:31:07 -0500 Subject: media: Change Andrzej Pietrasiewicz's e-mail address My @samsung.com address is going to cease existing soon, so change it to an address which can actually be used to contact me. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-dma-sg.c | 2 +- drivers/media/platform/s5p-jpeg/jpeg-core.c | 4 ++-- drivers/media/platform/s5p-jpeg/jpeg-core.h | 2 +- drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c | 2 +- drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h | 2 +- drivers/media/platform/s5p-jpeg/jpeg-regs.h | 2 +- include/media/videobuf2-dma-sg.h | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 015e737095cd..f02876d971d7 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * * 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 diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 3f9000b70385..580d6d02557b 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -3,7 +3,7 @@ * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * Author: Jacek Anaszewski * * This program is free software; you can redistribute it and/or modify @@ -3220,7 +3220,7 @@ static struct platform_driver s5p_jpeg_driver = { module_platform_driver(s5p_jpeg_driver); -MODULE_AUTHOR("Andrzej Pietrasiewicz "); +MODULE_AUTHOR("Andrzej Pietrasiewicz "); MODULE_AUTHOR("Jacek Anaszewski "); MODULE_DESCRIPTION("Samsung JPEG codec driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index a46465e10351..90fda4b720eb 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -3,7 +3,7 @@ * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c index b5f20e722b63..59c6263a71bf 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c @@ -3,7 +3,7 @@ * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h index f208fa3ed738..bfe746f8f750 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h @@ -3,7 +3,7 @@ * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h index df790b10140c..574f0e8021e5 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-regs.h @@ -5,7 +5,7 @@ * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * Author: Jacek Anaszewski * * This program is free software; you can redistribute it and/or modify diff --git a/include/media/videobuf2-dma-sg.h b/include/media/videobuf2-dma-sg.h index 52afa0e2bb17..f28fcb0cfac7 100644 --- a/include/media/videobuf2-dma-sg.h +++ b/include/media/videobuf2-dma-sg.h @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Samsung Electronics * - * Author: Andrzej Pietrasiewicz + * Author: Andrzej Pietrasiewicz * * 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 -- cgit v1.2.3-59-g8ed1b From 886ba97fb69eee710e1fd90d8aac5937e7fabfd1 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 13 Dec 2018 08:36:38 -0500 Subject: media: MAINTAINERS: Change s5p-jpeg maintainer information. My @samsung.com address is going to cease existing soon, so change it to an address which can actually be used to contact me. Adding Sylwester Nawrocki, who still has access to a wide spectrum of Exynos-based hardware. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index f916b6ca5a42..02d9ebcafb97 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2084,8 +2084,9 @@ F: drivers/media/platform/s5p-cec/ F: Documentation/devicetree/bindings/media/s5p-cec.txt ARM/SAMSUNG S5P SERIES JPEG CODEC SUPPORT -M: Andrzej Pietrasiewicz +M: Andrzej Pietrasiewicz M: Jacek Anaszewski +M: Sylwester Nawrocki L: linux-arm-kernel@lists.infradead.org L: linux-media@vger.kernel.org S: Maintained -- cgit v1.2.3-59-g8ed1b From 2a850b94135db67d87f4ab6ab2a325a42ecbe00a Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 17 Dec 2018 12:56:17 -0500 Subject: media: vicodec: bugfix - replace '=' with '|=' In the fwht_encode_frame, 'encoding = encode_plane' should be replaced with 'encoding |= encode_plane' so existing flags won't be overwrriten. Signed-off-by: Dafna Hirschfeld Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.c b/drivers/media/platform/vicodec/codec-fwht.c index 5630f1dc45e6..a6fd0477633b 100644 --- a/drivers/media/platform/vicodec/codec-fwht.c +++ b/drivers/media/platform/vicodec/codec-fwht.c @@ -787,10 +787,10 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, if (frm->components_num == 4) { rlco_max = rlco + size / 2 - 256; - encoding = encode_plane(frm->alpha, ref_frm->alpha, &rlco, - rlco_max, cf, frm->height, frm->width, - frm->luma_alpha_step, - is_intra, next_is_intra); + encoding |= encode_plane(frm->alpha, ref_frm->alpha, &rlco, + rlco_max, cf, frm->height, frm->width, + frm->luma_alpha_step, + is_intra, next_is_intra); if (encoding & FWHT_FRAME_UNENCODED) encoding |= FWHT_ALPHA_UNENCODED; encoding &= ~FWHT_FRAME_UNENCODED; -- cgit v1.2.3-59-g8ed1b From f45f3f753b0a3d739acda8e311b4f744d82dc52a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 18 Dec 2018 08:37:08 -0500 Subject: media: v4l2-ctrls.c/uvc: zero v4l2_event Control events can leak kernel memory since they do not fully zero the event. The same code is present in both v4l2-ctrls.c and uvc_ctrl.c, so fix both. It appears that all other event code is properly zeroing the structure, it's these two places. Signed-off-by: Hans Verkuil Reported-by: syzbot+4f021cf3697781dbd9fb@syzkaller.appspotmail.com Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 2 +- drivers/media/v4l2-core/v4l2-ctrls.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index d45415cbe6e7..14cff91b7aea 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1212,7 +1212,7 @@ static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl); - memset(ev->reserved, 0, sizeof(ev->reserved)); + memset(ev, 0, sizeof(*ev)); ev->type = V4L2_EVENT_CTRL; ev->id = v4l2_ctrl.id; ev->u.ctrl.value = value; diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e3bd441fa29a..8c47d8f00429 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1387,7 +1387,7 @@ static u32 user_flags(const struct v4l2_ctrl *ctrl) static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes) { - memset(ev->reserved, 0, sizeof(ev->reserved)); + memset(ev, 0, sizeof(*ev)); ev->type = V4L2_EVENT_CTRL; ev->id = ctrl->id; ev->u.ctrl.changes = changes; -- cgit v1.2.3-59-g8ed1b From 6f2c6afa79e0513d339871337bfc8c6c621d3ab1 Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Fri, 21 Dec 2018 11:56:41 -0500 Subject: media: sunxi: cedrus: Fix missing error message context When cedrus_hw_probe is called, v4l2_dev is not yet initialized. Use dev_err instead. Signed-off-by: Ondrej Jirman Acked-by: Paul Kocialkowski Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_hw.c | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c index 300339fee1bc..0acf219a8c91 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c @@ -157,14 +157,14 @@ int cedrus_hw_probe(struct cedrus_dev *dev) irq_dec = platform_get_irq(dev->pdev, 0); if (irq_dec <= 0) { - v4l2_err(&dev->v4l2_dev, "Failed to get IRQ\n"); + dev_err(dev->dev, "Failed to get IRQ\n"); return irq_dec; } ret = devm_request_irq(dev->dev, irq_dec, cedrus_irq, 0, dev_name(dev->dev), dev); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to request IRQ\n"); + dev_err(dev->dev, "Failed to request IRQ\n"); return ret; } @@ -182,21 +182,21 @@ int cedrus_hw_probe(struct cedrus_dev *dev) ret = of_reserved_mem_device_init(dev->dev); if (ret && ret != -ENODEV) { - v4l2_err(&dev->v4l2_dev, "Failed to reserve memory\n"); + dev_err(dev->dev, "Failed to reserve memory\n"); return ret; } ret = sunxi_sram_claim(dev->dev); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to claim SRAM\n"); + dev_err(dev->dev, "Failed to claim SRAM\n"); goto err_mem; } dev->ahb_clk = devm_clk_get(dev->dev, "ahb"); if (IS_ERR(dev->ahb_clk)) { - v4l2_err(&dev->v4l2_dev, "Failed to get AHB clock\n"); + dev_err(dev->dev, "Failed to get AHB clock\n"); ret = PTR_ERR(dev->ahb_clk); goto err_sram; @@ -204,7 +204,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) dev->mod_clk = devm_clk_get(dev->dev, "mod"); if (IS_ERR(dev->mod_clk)) { - v4l2_err(&dev->v4l2_dev, "Failed to get MOD clock\n"); + dev_err(dev->dev, "Failed to get MOD clock\n"); ret = PTR_ERR(dev->mod_clk); goto err_sram; @@ -212,7 +212,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) dev->ram_clk = devm_clk_get(dev->dev, "ram"); if (IS_ERR(dev->ram_clk)) { - v4l2_err(&dev->v4l2_dev, "Failed to get RAM clock\n"); + dev_err(dev->dev, "Failed to get RAM clock\n"); ret = PTR_ERR(dev->ram_clk); goto err_sram; @@ -220,7 +220,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) dev->rstc = devm_reset_control_get(dev->dev, NULL); if (IS_ERR(dev->rstc)) { - v4l2_err(&dev->v4l2_dev, "Failed to get reset control\n"); + dev_err(dev->dev, "Failed to get reset control\n"); ret = PTR_ERR(dev->rstc); goto err_sram; @@ -229,7 +229,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) res = platform_get_resource(dev->pdev, IORESOURCE_MEM, 0); dev->base = devm_ioremap_resource(dev->dev, res); if (IS_ERR(dev->base)) { - v4l2_err(&dev->v4l2_dev, "Failed to map registers\n"); + dev_err(dev->dev, "Failed to map registers\n"); ret = PTR_ERR(dev->base); goto err_sram; @@ -237,35 +237,35 @@ int cedrus_hw_probe(struct cedrus_dev *dev) ret = clk_set_rate(dev->mod_clk, CEDRUS_CLOCK_RATE_DEFAULT); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to set clock rate\n"); + dev_err(dev->dev, "Failed to set clock rate\n"); goto err_sram; } ret = clk_prepare_enable(dev->ahb_clk); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to enable AHB clock\n"); + dev_err(dev->dev, "Failed to enable AHB clock\n"); goto err_sram; } ret = clk_prepare_enable(dev->mod_clk); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to enable MOD clock\n"); + dev_err(dev->dev, "Failed to enable MOD clock\n"); goto err_ahb_clk; } ret = clk_prepare_enable(dev->ram_clk); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to enable RAM clock\n"); + dev_err(dev->dev, "Failed to enable RAM clock\n"); goto err_mod_clk; } ret = reset_control_reset(dev->rstc); if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to apply reset\n"); + dev_err(dev->dev, "Failed to apply reset\n"); goto err_ram_clk; } -- cgit v1.2.3-59-g8ed1b From 5ceaf5452c1b2a452dadaf377f9f07af7bda9cc3 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 25 Dec 2018 01:31:21 -0500 Subject: media: usb: gspca: add a missed return-value check for do_command do_command() may fail. The fix adds the missed return value of do_command(). If it fails, returns its error code. Signed-off-by: Kangjie Lu Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/cpia1.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c index 2b09af8865f4..23fbda56fc91 100644 --- a/drivers/media/usb/gspca/cpia1.c +++ b/drivers/media/usb/gspca/cpia1.c @@ -547,10 +547,14 @@ static int do_command(struct gspca_dev *gspca_dev, u16 command, } if (sd->params.qx3.button) { /* button pressed - unlock the latch */ - do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, + ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 3, 0xdf, 0xdf, 0); - do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, + if (ret) + return ret; + ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 3, 0xff, 0xff, 0); + if (ret) + return ret; } /* test whether microscope is cradled */ -- cgit v1.2.3-59-g8ed1b From 5b711870bec4dc9a6d705d41e127e73944fa3650 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 25 Dec 2018 03:24:41 -0500 Subject: media: usb: gspca: add a missed check for goto_low_power The fix checks if goto_low_power() fails, and if so, issues an error message. Signed-off-by: Kangjie Lu Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/cpia1.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c index 23fbda56fc91..7c817a4a93c4 100644 --- a/drivers/media/usb/gspca/cpia1.c +++ b/drivers/media/usb/gspca/cpia1.c @@ -1434,6 +1434,7 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; + int ret; sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ; reset_camera_params(gspca_dev); @@ -1445,7 +1446,10 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = mode; cam->nmodes = ARRAY_SIZE(mode); - goto_low_power(gspca_dev); + ret = goto_low_power(gspca_dev); + if (ret) + gspca_err(gspca_dev, "Cannot go to low power mode: %d\n", + ret); /* Check the firmware version. */ sd->params.version.firmwareVersion = 0; get_version_information(gspca_dev); -- cgit v1.2.3-59-g8ed1b From a21a0eb56b4e8fe4a330243af8030f890cde2283 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Fri, 28 Dec 2018 13:37:36 -0500 Subject: media: gspca: Check the return value of write_bridge for timeout In po1030_probe(), m5602_write_bridge() can timeout and return an error value. The fix checks for the return value and propagates upstream consistent with other usb drivers. Signed-off-by: Aditya Pakki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/m5602/m5602_po1030.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/gspca/m5602/m5602_po1030.c b/drivers/media/usb/gspca/m5602/m5602_po1030.c index 37d2891e5f5b..5e43b4782f02 100644 --- a/drivers/media/usb/gspca/m5602/m5602_po1030.c +++ b/drivers/media/usb/gspca/m5602/m5602_po1030.c @@ -158,6 +158,7 @@ static const struct v4l2_ctrl_config po1030_greenbal_cfg = { int po1030_probe(struct sd *sd) { + int rc = 0; u8 dev_id_h = 0, i; struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; @@ -177,11 +178,14 @@ int po1030_probe(struct sd *sd) for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) { u8 data = preinit_po1030[i][2]; if (preinit_po1030[i][0] == SENSOR) - m5602_write_sensor(sd, + rc |= m5602_write_sensor(sd, preinit_po1030[i][1], &data, 1); else - m5602_write_bridge(sd, preinit_po1030[i][1], data); + rc |= m5602_write_bridge(sd, preinit_po1030[i][1], + data); } + if (rc < 0) + return rc; if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1)) return -ENODEV; -- cgit v1.2.3-59-g8ed1b From 656025850074f5c1ba2e05be37bda57ba2b8d491 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Fri, 28 Dec 2018 13:51:10 -0500 Subject: media: gspca: mt9m111: Check write_bridge for timeout In mt9m111_probe, m5602_write_bridge can timeout and return a negative error value. The fix checks for this error and passes it upstream. Signed-off-by: Aditya Pakki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/m5602/m5602_mt9m111.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/gspca/m5602/m5602_mt9m111.c b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c index c9947c4a0f63..8fac814f4779 100644 --- a/drivers/media/usb/gspca/m5602/m5602_mt9m111.c +++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c @@ -199,7 +199,7 @@ static const struct v4l2_ctrl_config mt9m111_greenbal_cfg = { int mt9m111_probe(struct sd *sd) { u8 data[2] = {0x00, 0x00}; - int i; + int i, rc = 0; struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; if (force_sensor) { @@ -217,16 +217,18 @@ int mt9m111_probe(struct sd *sd) /* Do the preinit */ for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) { if (preinit_mt9m111[i][0] == BRIDGE) { - m5602_write_bridge(sd, + rc |= m5602_write_bridge(sd, preinit_mt9m111[i][1], preinit_mt9m111[i][2]); } else { data[0] = preinit_mt9m111[i][2]; data[1] = preinit_mt9m111[i][3]; - m5602_write_sensor(sd, + rc |= m5602_write_sensor(sd, preinit_mt9m111[i][1], data, 2); } } + if (rc < 0) + return rc; if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2)) return -ENODEV; -- cgit v1.2.3-59-g8ed1b From 49710c32cd9d6626a77c9f5f978a5f58cb536b35 Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Sat, 29 Dec 2018 10:46:01 -0500 Subject: media: s5p-jpeg: Check for fmt_ver_flag when doing fmt enumeration Previously when doing format enumeration, it was returning all formats supported by driver, even if they're not supported by hw. Add missing check for fmt_ver_flag, so it'll be fixed and only those supported by hw will be returned. Similar thing is already done in s5p_jpeg_find_format. It was found by using v4l2-compliance tool and checking result of VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS test and using v4l2-ctl to get list of all supported formats. Tested on s5pv210-galaxys (Samsung i9000 phone). Fixes: bb677f3ac434 ("[media] Exynos4 JPEG codec v4l2 driver") Signed-off-by: Pawe? Chmiel Reviewed-by: Jacek Anaszewski [hverkuil-cisco@xs4all.nl: fix a few alignment issues] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 580d6d02557b..85110766043f 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1293,13 +1293,16 @@ static int s5p_jpeg_querycap(struct file *file, void *priv, return 0; } -static int enum_fmt(struct s5p_jpeg_fmt *sjpeg_formats, int n, +static int enum_fmt(struct s5p_jpeg_ctx *ctx, + struct s5p_jpeg_fmt *sjpeg_formats, int n, struct v4l2_fmtdesc *f, u32 type) { int i, num = 0; + unsigned int fmt_ver_flag = ctx->jpeg->variant->fmt_ver_flag; for (i = 0; i < n; ++i) { - if (sjpeg_formats[i].flags & type) { + if (sjpeg_formats[i].flags & type && + sjpeg_formats[i].flags & fmt_ver_flag) { /* index-th format of type type found ? */ if (num == f->index) break; @@ -1326,11 +1329,11 @@ static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv, struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) - return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, SJPEG_FMT_FLAG_ENC_CAPTURE); - return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, - SJPEG_FMT_FLAG_DEC_CAPTURE); + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_DEC_CAPTURE); } static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, @@ -1339,11 +1342,11 @@ static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) - return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, SJPEG_FMT_FLAG_ENC_OUTPUT); - return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, - SJPEG_FMT_FLAG_DEC_OUTPUT); + return enum_fmt(ctx, sjpeg_formats, SJPEG_NUM_FORMATS, f, + SJPEG_FMT_FLAG_DEC_OUTPUT); } static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx, -- cgit v1.2.3-59-g8ed1b From d6b10dd0017641fee31daa84e478708a33d2f00a Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sun, 30 Dec 2018 06:41:40 -0500 Subject: media: tw9910: fix failure handling in tw9910_power_on() If gpiod_get_optional() fails in tw9910_power_on(), clk is left undisabled. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tw9910.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index a54548cc4285..0971f8a34afb 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -610,6 +610,11 @@ static int tw9910_power_on(struct tw9910_priv *priv) GPIOD_OUT_LOW); if (IS_ERR(priv->rstb_gpio)) { dev_info(&client->dev, "Unable to get GPIO \"rstb\""); + clk_disable_unprepare(priv->clk); + if (priv->pdn_gpio) { + gpiod_set_value(priv->pdn_gpio, 1); + usleep_range(500, 1000); + } return PTR_ERR(priv->rstb_gpio); } -- cgit v1.2.3-59-g8ed1b From cf6a9896622da6233f8b25beeabb4a98729a49c3 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sun, 30 Dec 2018 06:41:41 -0500 Subject: media: tw9910: add helper function for setting gpiod value tw9910 driver tries to sleep for the same period of time after each gpiod_set_value(). The patch moves duplicated code to a helper function. Signed-off-by: Alexey Khoroshilov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tw9910.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 0971f8a34afb..8d1138e13803 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -584,6 +584,14 @@ static int tw9910_s_register(struct v4l2_subdev *sd, } #endif +static void tw9910_set_gpio_value(struct gpio_desc *desc, int value) +{ + if (desc) { + gpiod_set_value(desc, value); + usleep_range(500, 1000); + } +} + static int tw9910_power_on(struct tw9910_priv *priv) { struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); @@ -595,10 +603,7 @@ static int tw9910_power_on(struct tw9910_priv *priv) return ret; } - if (priv->pdn_gpio) { - gpiod_set_value(priv->pdn_gpio, 0); - usleep_range(500, 1000); - } + tw9910_set_gpio_value(priv->pdn_gpio, 0); /* * FIXME: The reset signal is connected to a shared GPIO on some @@ -611,18 +616,13 @@ static int tw9910_power_on(struct tw9910_priv *priv) if (IS_ERR(priv->rstb_gpio)) { dev_info(&client->dev, "Unable to get GPIO \"rstb\""); clk_disable_unprepare(priv->clk); - if (priv->pdn_gpio) { - gpiod_set_value(priv->pdn_gpio, 1); - usleep_range(500, 1000); - } + tw9910_set_gpio_value(priv->pdn_gpio, 1); return PTR_ERR(priv->rstb_gpio); } if (priv->rstb_gpio) { - gpiod_set_value(priv->rstb_gpio, 1); - usleep_range(500, 1000); - gpiod_set_value(priv->rstb_gpio, 0); - usleep_range(500, 1000); + tw9910_set_gpio_value(priv->rstb_gpio, 1); + tw9910_set_gpio_value(priv->rstb_gpio, 0); gpiod_put(priv->rstb_gpio); } @@ -633,11 +633,7 @@ static int tw9910_power_on(struct tw9910_priv *priv) static int tw9910_power_off(struct tw9910_priv *priv) { clk_disable_unprepare(priv->clk); - - if (priv->pdn_gpio) { - gpiod_set_value(priv->pdn_gpio, 1); - usleep_range(500, 1000); - } + tw9910_set_gpio_value(priv->pdn_gpio, 1); return 0; } -- cgit v1.2.3-59-g8ed1b From a86f2b590aa0259b961ffc39852e7c88f6c7e468 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 30 Dec 2018 08:20:16 -0500 Subject: media: staging: bcm2835-camera: use V4L2_FRACT_COMPARE Now the equivalent of FRACT_CMP() is added in v4l2 common internal API header. Signed-off-by: Akinobu Mita Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index 611a6ee2943a..7c6cf41645eb 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -1370,10 +1370,6 @@ static int vidioc_g_parm(struct file *file, void *priv, return 0; } -#define FRACT_CMP(a, OP, b) \ - ((u64)(a).numerator * (b).denominator OP \ - (u64)(b).numerator * (a).denominator) - static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *parm) { @@ -1387,8 +1383,8 @@ static int vidioc_s_parm(struct file *file, void *priv, /* tpf: {*, 0} resets timing; clip to [min, max]*/ tpf = tpf.denominator ? tpf : tpf_default; - tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; - tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; + tpf = V4L2_FRACT_COMPARE(tpf, <, tpf_min) ? tpf_min : tpf; + tpf = V4L2_FRACT_COMPARE(tpf, >, tpf_max) ? tpf_max : tpf; dev->capture.timeperframe = tpf; parm->parm.capture.timeperframe = tpf; -- cgit v1.2.3-59-g8ed1b From 6458afc8c49148f0a5015d8853039a0b3fb91d98 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Fri, 4 Jan 2019 11:12:18 -0500 Subject: media: rcar-vin: remove unneeded locking in async callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The locking implemented in the async notifier callbacks are unnecessary as the global list_lock in v4l2-async.c is held whenever one of the callbacks are called. The locking in itself is not harmful however it produces a LOCKDEP warning between the global v4l2-async list_lock and the rcar-vin local locking schema. Remove the rcar-vin locking for the async callbacks to reduce complexity and silent the false LOCKDEP warning. Signed-off-by: Niklas Söderlund Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index f0719ce24b97..0e81b557f3b6 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -542,9 +542,7 @@ static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name); - mutex_lock(&vin->lock); rvin_parallel_subdevice_detach(vin); - mutex_unlock(&vin->lock); } static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, @@ -554,9 +552,7 @@ static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); int ret; - mutex_lock(&vin->lock); ret = rvin_parallel_subdevice_attach(vin, subdev); - mutex_unlock(&vin->lock); if (ret) return ret; @@ -664,7 +660,6 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) } /* Create all media device links between VINs and CSI-2's. */ - mutex_lock(&vin->group->lock); for (route = vin->info->routes; route->mask; route++) { struct media_pad *source_pad, *sink_pad; struct media_entity *source, *sink; @@ -700,7 +695,6 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) break; } } - mutex_unlock(&vin->group->lock); return ret; } @@ -716,8 +710,6 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, if (vin->group->vin[i]) rvin_v4l2_unregister(vin->group->vin[i]); - mutex_lock(&vin->group->lock); - for (i = 0; i < RVIN_CSI_MAX; i++) { if (vin->group->csi[i].fwnode != asd->match.fwnode) continue; @@ -725,8 +717,6 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, vin_dbg(vin, "Unbind CSI-2 %s from slot %u\n", subdev->name, i); break; } - - mutex_unlock(&vin->group->lock); } static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, @@ -736,8 +726,6 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); unsigned int i; - mutex_lock(&vin->group->lock); - for (i = 0; i < RVIN_CSI_MAX; i++) { if (vin->group->csi[i].fwnode != asd->match.fwnode) continue; @@ -746,8 +734,6 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, break; } - mutex_unlock(&vin->group->lock); - return 0; } -- cgit v1.2.3-59-g8ed1b From f27dd0ad68850fdb806536a733a32d8f74810f1e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 10 Dec 2018 15:41:40 -0500 Subject: media: seco-cec: fix RC_CORE dependency All other drivers that need RC_CORE have a dependency rather than using 'select', so we should do the same here to avoid circular dependencies as well as this warning about missing dependencies: WARNING: unmet direct dependencies detected for RC_CORE Depends on [n]: INPUT [=n] Selected by [y]: - VIDEO_SECO_RC [=y] && MEDIA_SUPPORT [=y] && CEC_PLATFORM_DRIVERS [=y] && VIDEO_SECO_CEC [=y] Fixes: daef95769b3a ("media: seco-cec: add Consumer-IR support") Signed-off-by: Arnd Bergmann Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index a505e9f5a1e2..b5ccb60cf664 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -650,7 +650,7 @@ config VIDEO_SECO_CEC config VIDEO_SECO_RC bool "SECO Boards IR RC5 support" depends on VIDEO_SECO_CEC - select RC_CORE + depends on RC_CORE help If you say yes here you will get support for the SECO Boards Consumer-IR in seco-cec driver. -- cgit v1.2.3-59-g8ed1b From c9b7d8f252a5a6f8ca6e948151367cbc7bc4b776 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Thu, 20 Dec 2018 02:48:42 -0500 Subject: media: lgdt3306a: fix a missing check of return value If lgdt3306a_read_reg() fails, the read data in "val" is incorrect, thus shouldn't be further used. The fix inserts a check for the return value of lgdt3306a_read_reg(). If it fails, goto fail. Signed-off-by: Kangjie Lu Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/lgdt3306a.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index cee9c83e48de..99c6289ae585 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -1685,7 +1685,10 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe, case QAM_256: case QAM_AUTO: /* need to know actual modulation to set proper SNR baseline */ - lgdt3306a_read_reg(state, 0x00a6, &val); + ret = lgdt3306a_read_reg(state, 0x00a6, &val); + if (lg_chkerr(ret)) + goto fail; + if(val & 0x04) ref_snr = 2800; /* QAM-256 28dB */ else -- cgit v1.2.3-59-g8ed1b From 9502cdf0807058a10029488052b064cecceb7fc9 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Fri, 21 Dec 2018 02:07:20 -0500 Subject: media: mt312: fix a missing check of mt312 reset mt312_reset() may fail. Although it is called in the end of mt312_set_frontend(), we better check its status and return its error code upstream instead of 0. Signed-off-by: Kangjie Lu Reviewed-by: Matthias Schwarzott Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mt312.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c index 03e74a729168..bfbb879469f2 100644 --- a/drivers/media/dvb-frontends/mt312.c +++ b/drivers/media/dvb-frontends/mt312.c @@ -645,7 +645,9 @@ static int mt312_set_frontend(struct dvb_frontend *fe) if (ret < 0) return ret; - mt312_reset(state, 0); + ret = mt312_reset(state, 0); + if (ret < 0) + return ret; return 0; } -- cgit v1.2.3-59-g8ed1b From c54dbfcda3059c255f335e75a27d7f5c64e6549e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 22 Dec 2018 09:12:26 -0500 Subject: media: cxd2880-spi: fix two memory leaks of dvb_spi There are two return paths that do not kfree dvb_spi. Fix the memory leaks by returning via the exit label fail_adapter that will free dvi_spi. Detected by CoverityScan, CID#1475991 ("Resource Leak") Fixes: cb496cd472af ("media: cxd2880-spi: Add optional vcc regulator") Signed-off-by: Colin Ian King Acked-by: Yasunari Takiguchi ? Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/spi/cxd2880-spi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c index d5c433e20d4a..4077217777f9 100644 --- a/drivers/media/spi/cxd2880-spi.c +++ b/drivers/media/spi/cxd2880-spi.c @@ -522,13 +522,15 @@ cxd2880_spi_probe(struct spi_device *spi) dvb_spi->vcc_supply = devm_regulator_get_optional(&spi->dev, "vcc"); if (IS_ERR(dvb_spi->vcc_supply)) { - if (PTR_ERR(dvb_spi->vcc_supply) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (PTR_ERR(dvb_spi->vcc_supply) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto fail_adapter; + } dvb_spi->vcc_supply = NULL; } else { ret = regulator_enable(dvb_spi->vcc_supply); if (ret) - return ret; + goto fail_adapter; } dvb_spi->spi = spi; -- cgit v1.2.3-59-g8ed1b From 88903a1abd00e051f3ddfe7b86949882260ddf86 Mon Sep 17 00:00:00 2001 From: Ettore Chimenti Date: Thu, 27 Dec 2018 08:06:35 -0500 Subject: media: secocec: fix ir address shift The actual value of the RC5 System Number (address) is stored in the IR_READ_DATA common register masked with 0x1F00 so it have to be shifted by 8 bits. Signed-off-by: Ettore Chimenti Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/seco-cec/seco-cec.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/seco-cec/seco-cec.h b/drivers/media/platform/seco-cec/seco-cec.h index e632c4a2a044..843de8c7dfd4 100644 --- a/drivers/media/platform/seco-cec/seco-cec.h +++ b/drivers/media/platform/seco-cec/seco-cec.h @@ -106,7 +106,7 @@ #define SECOCEC_IR_COMMAND_MASK 0x007F #define SECOCEC_IR_COMMAND_SHL 0 #define SECOCEC_IR_ADDRESS_MASK 0x1F00 -#define SECOCEC_IR_ADDRESS_SHL 7 +#define SECOCEC_IR_ADDRESS_SHL 8 #define SECOCEC_IR_TOGGLE_MASK 0x8000 #define SECOCEC_IR_TOGGLE_SHL 15 -- cgit v1.2.3-59-g8ed1b From 0f787c12ee7b2b41a74594ed158a0112736f4e4e Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Thu, 27 Dec 2018 13:47:20 -0500 Subject: media: dvb: add return value check on Write16 Write16 can return an error code -1 when the i2c_write fails. The fix checks for these failures and returns the error upstream Signed-off-by: Aditya Pakki Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/drxd_hard.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index 684d428efb0d..0a5b15bee1d7 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -1144,6 +1144,8 @@ static int EnableAndResetMB(struct drxd_state *state) static int InitCC(struct drxd_state *state) { + int status = 0; + if (state->osc_clock_freq == 0 || state->osc_clock_freq > 20000 || (state->osc_clock_freq % 4000) != 0) { @@ -1151,14 +1153,17 @@ static int InitCC(struct drxd_state *state) return -1; } - Write16(state, CC_REG_OSC_MODE__A, CC_REG_OSC_MODE_M20, 0); - Write16(state, CC_REG_PLL_MODE__A, CC_REG_PLL_MODE_BYPASS_PLL | - CC_REG_PLL_MODE_PUMP_CUR_12, 0); - Write16(state, CC_REG_REF_DIVIDE__A, state->osc_clock_freq / 4000, 0); - Write16(state, CC_REG_PWD_MODE__A, CC_REG_PWD_MODE_DOWN_PLL, 0); - Write16(state, CC_REG_UPDATE__A, CC_REG_UPDATE_KEY, 0); + status |= Write16(state, CC_REG_OSC_MODE__A, CC_REG_OSC_MODE_M20, 0); + status |= Write16(state, CC_REG_PLL_MODE__A, + CC_REG_PLL_MODE_BYPASS_PLL | + CC_REG_PLL_MODE_PUMP_CUR_12, 0); + status |= Write16(state, CC_REG_REF_DIVIDE__A, + state->osc_clock_freq / 4000, 0); + status |= Write16(state, CC_REG_PWD_MODE__A, CC_REG_PWD_MODE_DOWN_PLL, + 0); + status |= Write16(state, CC_REG_UPDATE__A, CC_REG_UPDATE_KEY, 0); - return 0; + return status; } static int ResetECOD(struct drxd_state *state) @@ -1312,7 +1317,10 @@ static int SC_SendCommand(struct drxd_state *state, u16 cmd) int status = 0, ret; u16 errCode; - Write16(state, SC_RA_RAM_CMD__A, cmd, 0); + status = Write16(state, SC_RA_RAM_CMD__A, cmd, 0); + if (status < 0) + return status; + SC_WaitForReady(state); ret = Read16(state, SC_RA_RAM_CMD_ADDR__A, &errCode, 0); @@ -1339,9 +1347,9 @@ static int SC_ProcStartCommand(struct drxd_state *state, break; } SC_WaitForReady(state); - Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0); - Write16(state, SC_RA_RAM_PARAM1__A, param1, 0); - Write16(state, SC_RA_RAM_PARAM0__A, param0, 0); + status |= Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0); + status |= Write16(state, SC_RA_RAM_PARAM1__A, param1, 0); + status |= Write16(state, SC_RA_RAM_PARAM0__A, param0, 0); SC_SendCommand(state, SC_RA_RAM_CMD_PROC_START); } while (0); -- cgit v1.2.3-59-g8ed1b From 467a37fba93f2b4fe3ab597ff6a517b22b566882 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Thu, 27 Dec 2018 13:58:01 -0500 Subject: media: dvb: Add check on sp8870_readreg In sp8870_set_frontend_parameters, the function sp8870_readreg may return an error when i2c_transfer fails. The fix checks for this error and returns upstream consistent with other invocations. Signed-off-by: Aditya Pakki Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/sp8870.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/sp8870.c b/drivers/media/dvb-frontends/sp8870.c index 8d31cf3f4f07..270a3c559e08 100644 --- a/drivers/media/dvb-frontends/sp8870.c +++ b/drivers/media/dvb-frontends/sp8870.c @@ -293,7 +293,9 @@ static int sp8870_set_frontend_parameters(struct dvb_frontend *fe) sp8870_writereg(state, 0xc05, reg0xc05); // read status reg in order to clear pending irqs - sp8870_readreg(state, 0x200); + err = sp8870_readreg(state, 0x200); + if (err) + return err; // system controller start sp8870_microcontroller_start(state); -- cgit v1.2.3-59-g8ed1b From 6af61a0bc43201425afa6052d24f2a67648f69d2 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Wed, 28 Nov 2018 21:01:44 -0500 Subject: media: dt-bindings: adv748x: make data-lanes property mandatory for CSI-2 endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CSI-2 transmitters can use a different number of lanes to transmit data. Make the data-lanes mandatory for the endpoints that describe the transmitters as no good default can be set to fallback on. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/i2c/adv748x.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/i2c/adv748x.txt b/Documentation/devicetree/bindings/media/i2c/adv748x.txt index 5dddc95f9cc4..4f91686e54a6 100644 --- a/Documentation/devicetree/bindings/media/i2c/adv748x.txt +++ b/Documentation/devicetree/bindings/media/i2c/adv748x.txt @@ -48,7 +48,16 @@ are numbered as follows. TXA source 10 TXB source 11 -The digital output port nodes must contain at least one endpoint. +The digital output port nodes, when present, shall contain at least one +endpoint. Each of those endpoints shall contain the data-lanes property as +described in video-interfaces.txt. + +Required source endpoint properties: + - data-lanes: an array of physical data lane indexes + The accepted value(s) for this property depends on which of the two + sources are described. For TXA 1, 2 or 4 data lanes can be described + while for TXB only 1 data lane is valid. See video-interfaces.txt + for detailed description. Ports are optional if they are not connected to anything at the hardware level. -- cgit v1.2.3-59-g8ed1b From 90917bc8f68ff8357eccda3a53519a53504a99a2 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Wed, 28 Nov 2018 21:01:45 -0500 Subject: media: i2c: adv748x: reuse power up sequence when initializing CSI-2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the MIPI CSI-2 power up sequence to match the power up sequence in the hardware manual chapter "9.5.1 Power Up Sequence". This change allows the power up functions to be reused when initializing the hardware reducing code duplicating as well aligning with the documentation. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Tested-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 50 +++++++++----------------------- 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 6854d898fdd1..2384f50dacb0 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -234,6 +234,12 @@ static const struct adv748x_reg_value adv748x_power_up_txa_4lane[] = { {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */ + {ADV748X_PAGE_TXA, 0xdb, 0x10}, /* ADI Required Write */ + {ADV748X_PAGE_TXA, 0xd6, 0x07}, /* ADI Required Write */ + {ADV748X_PAGE_TXA, 0xc4, 0x0a}, /* ADI Required Write */ + {ADV748X_PAGE_TXA, 0x71, 0x33}, /* ADI Required Write */ + {ADV748X_PAGE_TXA, 0x72, 0x11}, /* ADI Required Write */ + {ADV748X_PAGE_TXA, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */ {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */ @@ -263,6 +269,11 @@ static const struct adv748x_reg_value adv748x_power_up_txb_1lane[] = { {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */ + {ADV748X_PAGE_TXB, 0xd2, 0x40}, /* ADI Required Write */ + {ADV748X_PAGE_TXB, 0xc4, 0x0a}, /* ADI Required Write */ + {ADV748X_PAGE_TXB, 0x71, 0x33}, /* ADI Required Write */ + {ADV748X_PAGE_TXB, 0x72, 0x11}, /* ADI Required Write */ + {ADV748X_PAGE_TXB, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */ @@ -383,25 +394,6 @@ static const struct adv748x_reg_value adv748x_init_txa_4lane[] = { {ADV748X_PAGE_IO, 0x0c, 0xe0}, /* Enable LLC_DLL & Double LLC Timing */ {ADV748X_PAGE_IO, 0x0e, 0xdd}, /* LLC/PIX/SPI PINS TRISTATED AUD */ - {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ - {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */ - {ADV748X_PAGE_TXA, 0xdb, 0x10}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xd6, 0x07}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xc4, 0x0a}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x71, 0x33}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x72, 0x11}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ - - {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ - {ADV748X_PAGE_TXA, 0x00, 0x24 },/* Power-up CSI-TX */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXA, 0xc1, 0x2b}, /* ADI Required Write */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXA, 0x31, 0x80}, /* ADI Required Write */ - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; @@ -435,24 +427,6 @@ static const struct adv748x_reg_value adv748x_init_txb_1lane[] = { {ADV748X_PAGE_SDP, 0x31, 0x12}, /* ADI Required Write */ {ADV748X_PAGE_SDP, 0xe6, 0x4f}, /* V bit end pos manually in NTSC */ - {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ - {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */ - {ADV748X_PAGE_TXB, 0xd2, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xc4, 0x0a}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x71, 0x33}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x72, 0x11}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ - {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ - {ADV748X_PAGE_TXB, 0x00, 0x21 },/* Power-up CSI-TX */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXB, 0xc1, 0x2b}, /* ADI Required Write */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXB, 0x31, 0x80}, /* ADI Required Write */ - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; @@ -474,6 +448,7 @@ static int adv748x_reset(struct adv748x_state *state) if (ret) return ret; + adv748x_tx_power(&state->txa, 1); adv748x_tx_power(&state->txa, 0); /* Init and power down TXB */ @@ -481,6 +456,7 @@ static int adv748x_reset(struct adv748x_state *state) if (ret) return ret; + adv748x_tx_power(&state->txb, 1); adv748x_tx_power(&state->txb, 0); /* Disable chip powerdown & Enable HDMI Rx block */ -- cgit v1.2.3-59-g8ed1b From 6a18865da8e3ebc16b7783c126fbb18be91b5622 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Wed, 28 Nov 2018 21:01:46 -0500 Subject: media: i2c: adv748x: store number of CSI-2 lanes described in device tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The adv748x CSI-2 transmitters TXA and TXB can use different number of lanes to transmit data. In order to be able to configure the device correctly this information need to be parsed from device tree and stored in each TX private data structure. TXA supports 1, 2 and 4 lanes while TXB supports 1 lane. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Tested-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 50 ++++++++++++++++++++++++++++++++ drivers/media/i2c/adv748x/adv748x.h | 1 + 2 files changed, 51 insertions(+) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 2384f50dacb0..9d80d7f3062b 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "adv748x.h" @@ -521,12 +522,56 @@ void adv748x_subdev_init(struct v4l2_subdev *sd, struct adv748x_state *state, sd->entity.ops = &adv748x_media_ops; } +static int adv748x_parse_csi2_lanes(struct adv748x_state *state, + unsigned int port, + struct device_node *ep) +{ + struct v4l2_fwnode_endpoint vep; + unsigned int num_lanes; + int ret; + + if (port != ADV748X_PORT_TXA && port != ADV748X_PORT_TXB) + return 0; + + vep.bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &vep); + if (ret) + return ret; + + num_lanes = vep.bus.mipi_csi2.num_data_lanes; + + if (vep.base.port == ADV748X_PORT_TXA) { + if (num_lanes != 1 && num_lanes != 2 && num_lanes != 4) { + adv_err(state, "TXA: Invalid number (%u) of lanes\n", + num_lanes); + return -EINVAL; + } + + state->txa.num_lanes = num_lanes; + adv_dbg(state, "TXA: using %u lanes\n", state->txa.num_lanes); + } + + if (vep.base.port == ADV748X_PORT_TXB) { + if (num_lanes != 1) { + adv_err(state, "TXB: Invalid number (%u) of lanes\n", + num_lanes); + return -EINVAL; + } + + state->txb.num_lanes = num_lanes; + adv_dbg(state, "TXB: using %u lanes\n", state->txb.num_lanes); + } + + return 0; +} + static int adv748x_parse_dt(struct adv748x_state *state) { struct device_node *ep_np = NULL; struct of_endpoint ep; bool out_found = false; bool in_found = false; + int ret; for_each_endpoint_of_node(state->dev->of_node, ep_np) { of_graph_parse_endpoint(ep_np, &ep); @@ -557,6 +602,11 @@ static int adv748x_parse_dt(struct adv748x_state *state) in_found = true; else out_found = true; + + /* Store number of CSI-2 lanes used for TXA and TXB. */ + ret = adv748x_parse_csi2_lanes(state, ep.port, ep_np); + if (ret) + return ret; } return in_found && out_found ? 0 : -ENODEV; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 39c2fdc3b416..b482c7fe6957 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -79,6 +79,7 @@ struct adv748x_csi2 { struct v4l2_mbus_framefmt format; unsigned int page; unsigned int port; + unsigned int num_lanes; struct media_pad pads[ADV748X_CSI2_NR_PADS]; struct v4l2_ctrl_handler ctrl_hdl; -- cgit v1.2.3-59-g8ed1b From 0ad5b80cff509360ef9953ac7ce5a56964210c30 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Wed, 28 Nov 2018 21:01:47 -0500 Subject: media: i2c: adv748x: configure number of lanes used for TXA CSI-2 transmitter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver fixed the TXA CSI-2 transmitter in 4-lane mode while it could operate using 1-, 2- and 4-lanes. Update the driver to support all available modes. The driver makes use of large tables of static register/value writes when powering up/down the TXA and TXB transmitters which include the write to the NUM_LANES register. By converting the tables into functions and using parameters the power up/down functions for TXA and TXB power up/down can be merged and used for both transmitters. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Tested-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 139 ++++++++++++++++--------------- 1 file changed, 70 insertions(+), 69 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 9d80d7f3062b..d94c63cb6a2e 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -125,6 +125,16 @@ int adv748x_write(struct adv748x_state *state, u8 page, u8 reg, u8 value) return regmap_write(state->regmap[page], reg, value); } +static int adv748x_write_check(struct adv748x_state *state, u8 page, u8 reg, + u8 value, int *error) +{ + if (*error) + return *error; + + *error = adv748x_write(state, page, reg, value); + return *error; +} + /* adv748x_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX * size to one or more registers. * @@ -231,79 +241,77 @@ static int adv748x_write_regs(struct adv748x_state *state, * TXA and TXB */ -static const struct adv748x_reg_value adv748x_power_up_txa_4lane[] = { - - {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ - {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */ - {ADV748X_PAGE_TXA, 0xdb, 0x10}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xd6, 0x07}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xc4, 0x0a}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x71, 0x33}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x72, 0x11}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ - - {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ - {ADV748X_PAGE_TXA, 0x00, 0x24 },/* Power-up CSI-TX */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXA, 0xc1, 0x2b}, /* ADI Required Write */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXA, 0x31, 0x80}, /* ADI Required Write */ +static int adv748x_power_up_tx(struct adv748x_csi2 *tx) +{ + struct adv748x_state *state = tx->state; + u8 page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB; + int ret = 0; - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ -}; + /* Enable n-lane MIPI */ + adv748x_write_check(state, page, 0x00, 0x80 | tx->num_lanes, &ret); -static const struct adv748x_reg_value adv748x_power_down_txa_4lane[] = { + /* Set Auto DPHY Timing */ + adv748x_write_check(state, page, 0x00, 0xa0 | tx->num_lanes, &ret); - {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x1e, 0x00}, /* ADI Required Write */ - {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ - {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - {ADV748X_PAGE_TXA, 0xc1, 0x3b}, /* ADI Required Write */ + /* ADI Required Write */ + if (is_txa(tx)) { + adv748x_write_check(state, page, 0xdb, 0x10, &ret); + adv748x_write_check(state, page, 0xd6, 0x07, &ret); + } else { + adv748x_write_check(state, page, 0xd2, 0x40, &ret); + } - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ -}; + adv748x_write_check(state, page, 0xc4, 0x0a, &ret); + adv748x_write_check(state, page, 0x71, 0x33, &ret); + adv748x_write_check(state, page, 0x72, 0x11, &ret); -static const struct adv748x_reg_value adv748x_power_up_txb_1lane[] = { - - {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ - {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */ - {ADV748X_PAGE_TXB, 0xd2, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xc4, 0x0a}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x71, 0x33}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x72, 0x11}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ - - {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ - {ADV748X_PAGE_TXB, 0x00, 0x21 },/* Power-up CSI-TX */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXB, 0xc1, 0x2b}, /* ADI Required Write */ - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ - {ADV748X_PAGE_TXB, 0x31, 0x80}, /* ADI Required Write */ + /* i2c_dphy_pwdn - 1'b0 */ + adv748x_write_check(state, page, 0xf0, 0x00, &ret); - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ -}; + /* ADI Required Writes*/ + adv748x_write_check(state, page, 0x31, 0x82, &ret); + adv748x_write_check(state, page, 0x1e, 0x40, &ret); -static const struct adv748x_reg_value adv748x_power_down_txb_1lane[] = { + /* i2c_mipi_pll_en - 1'b1 */ + adv748x_write_check(state, page, 0xda, 0x01, &ret); + usleep_range(2000, 2500); - {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x1e, 0x00}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ - {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ - {ADV748X_PAGE_TXB, 0xc1, 0x3b}, /* ADI Required Write */ + /* Power-up CSI-TX */ + adv748x_write_check(state, page, 0x00, 0x20 | tx->num_lanes, &ret); + usleep_range(1000, 1500); - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ -}; + /* ADI Required Writes */ + adv748x_write_check(state, page, 0xc1, 0x2b, &ret); + usleep_range(1000, 1500); + adv748x_write_check(state, page, 0x31, 0x80, &ret); -int adv748x_tx_power(struct adv748x_csi2 *tx, bool on) + return ret; +} + +static int adv748x_power_down_tx(struct adv748x_csi2 *tx) { struct adv748x_state *state = tx->state; - const struct adv748x_reg_value *reglist; + u8 page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB; + int ret = 0; + + /* ADI Required Writes */ + adv748x_write_check(state, page, 0x31, 0x82, &ret); + adv748x_write_check(state, page, 0x1e, 0x00, &ret); + + /* Enable n-lane MIPI */ + adv748x_write_check(state, page, 0x00, 0x80 | tx->num_lanes, &ret); + + /* i2c_mipi_pll_en - 1'b1 */ + adv748x_write_check(state, page, 0xda, 0x01, &ret); + + /* ADI Required Write */ + adv748x_write_check(state, page, 0xc1, 0x3b, &ret); + + return ret; +} + +int adv748x_tx_power(struct adv748x_csi2 *tx, bool on) +{ int val; if (!is_tx_enabled(tx)) @@ -321,14 +329,7 @@ int adv748x_tx_power(struct adv748x_csi2 *tx, bool on) WARN_ONCE((on && val & ADV748X_CSI_FS_AS_LS_UNKNOWN), "Enabling with unknown bit set"); - if (on) - reglist = is_txa(tx) ? adv748x_power_up_txa_4lane : - adv748x_power_up_txb_1lane; - else - reglist = is_txa(tx) ? adv748x_power_down_txa_4lane : - adv748x_power_down_txb_1lane; - - return adv748x_write_regs(state, reglist); + return on ? adv748x_power_up_tx(tx) : adv748x_power_down_tx(tx); } /* ----------------------------------------------------------------------------- -- cgit v1.2.3-59-g8ed1b From 9640ee1c308c1bb21c224a0cf41d9ed1b7e3a427 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:21:49 -0500 Subject: media: dt-bindings: rcar-csi2: Add r8a774c0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the compatible string for RZ/G2E (a.k.a. R8A774C0) to the list of supported SoCs. Signed-off-by: Fabrizio Castro Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/renesas,rcar-csi2.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/renesas,rcar-csi2.txt b/Documentation/devicetree/bindings/media/renesas,rcar-csi2.txt index 541d936b62e8..d63275e17afd 100644 --- a/Documentation/devicetree/bindings/media/renesas,rcar-csi2.txt +++ b/Documentation/devicetree/bindings/media/renesas,rcar-csi2.txt @@ -2,12 +2,13 @@ Renesas R-Car MIPI CSI-2 ------------------------ The R-Car CSI-2 receiver device provides MIPI CSI-2 capabilities for the -Renesas R-Car family of devices. It is used in conjunction with the +Renesas R-Car and RZ/G2 family of devices. It is used in conjunction with the R-Car VIN module, which provides the video capture capabilities. Mandatory properties -------------------- - compatible: Must be one or more of the following + - "renesas,r8a774c0-csi2" for the R8A774C0 device. - "renesas,r8a7795-csi2" for the R8A7795 device. - "renesas,r8a7796-csi2" for the R8A7796 device. - "renesas,r8a77965-csi2" for the R8A77965 device. -- cgit v1.2.3-59-g8ed1b From 73960b787c615b41333600e1291f6a47ff7db21c Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:21:59 -0500 Subject: media: dt-bindings: rcar-vin: Add R8A774C0 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the compatible string for RZ/G2E (a.k.a. R8A774C0) to the list of SoCs supported by rcar-vin driver. Signed-off-by: Fabrizio Castro Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/rcar_vin.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt index 0dd84a183ca7..224a4615b418 100644 --- a/Documentation/devicetree/bindings/media/rcar_vin.txt +++ b/Documentation/devicetree/bindings/media/rcar_vin.txt @@ -7,12 +7,13 @@ family of devices. Each VIN instance has a single parallel input that supports RGB and YUV video, with both external synchronization and BT.656 synchronization for the latter. Depending on the instance the VIN input is connected to external SoC pins, or -on Gen3 platforms to a CSI-2 receiver. +on Gen3 and RZ/G2 platforms to a CSI-2 receiver. - compatible: Must be one or more of the following - "renesas,vin-r8a7743" for the R8A7743 device - "renesas,vin-r8a7744" for the R8A7744 device - "renesas,vin-r8a7745" for the R8A7745 device + - "renesas,vin-r8a774c0" for the R8A774C0 device - "renesas,vin-r8a7778" for the R8A7778 device - "renesas,vin-r8a7779" for the R8A7779 device - "renesas,vin-r8a7790" for the R8A7790 device @@ -61,10 +62,10 @@ The per-board settings Gen2 platforms: - data-enable-active: polarity of CLKENB signal, see [1] for description. Default is active high. -The per-board settings Gen3 platforms: +The per-board settings Gen3 and RZ/G2 platforms: -Gen3 platforms can support both a single connected parallel input source -from external SoC pins (port@0) and/or multiple parallel input sources +Gen3 and RZ/G2 platforms can support both a single connected parallel input +source from external SoC pins (port@0) and/or multiple parallel input sources from local SoC CSI-2 receivers (port@1) depending on SoC. - renesas,id - ID number of the VIN, VINx in the documentation. -- cgit v1.2.3-59-g8ed1b From f74e5a32fa0c03d9f4bb84ea20d0c7be34fe3725 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:24:04 -0500 Subject: media: rcar-vin: Add support for RZ/G2E MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the RZ/G2 User's manual, RZ/G2E and R-Car E3 VIN blocks are identical, therefore use R-Car E3 definitions to add RZ/G2E support. Signed-off-by: Fabrizio Castro Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 0e81b557f3b6..b28e3ff191c2 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -1131,6 +1131,10 @@ static const struct rvin_info rcar_info_r8a77995 = { }; static const struct of_device_id rvin_of_id_table[] = { + { + .compatible = "renesas,vin-r8a774c0", + .data = &rcar_info_r8a77990, + }, { .compatible = "renesas,vin-r8a7778", .data = &rcar_info_m1, -- cgit v1.2.3-59-g8ed1b From 8809b8ceb0bd283c0528a0c7233656b12bcda2dc Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:24:12 -0500 Subject: media: rcar-csi2: Add support for RZ/G2E MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the RZ/G2 User's manual, RZ/G2E and R-Car E3 CSI-2 blocks are identical, therefore use R-Car E3 definitions to add RZ/G2E support. Signed-off-by: Fabrizio Castro Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-csi2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index b2bd0b9627d9..f64528d2be3c 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -985,6 +985,10 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = { }; static const struct of_device_id rcar_csi2_of_table[] = { + { + .compatible = "renesas,r8a774c0-csi2", + .data = &rcar_csi2_info_r8a77990, + }, { .compatible = "renesas,r8a7795-csi2", .data = &rcar_csi2_info_r8a7795, -- cgit v1.2.3-59-g8ed1b From 8db63f3d14dc44c043afc00e5adeb541106ec16b Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 9 Jan 2019 06:24:56 -0500 Subject: media: saa7146: make use of i2c_8bit_addr_from_msg Because it looks neater. Signed-off-by: Peter Rosin Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_i2c.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/common/saa7146/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c index 3feddc52c446..df9ebe2a168c 100644 --- a/drivers/media/common/saa7146/saa7146_i2c.c +++ b/drivers/media/common/saa7146/saa7146_i2c.c @@ -54,10 +54,7 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op) /* loop through all messages */ for(i = 0; i < num; i++) { - /* insert the address of the i2c-slave. - note: we get 7 bit i2c-addresses, - so we have to perform a translation */ - addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0); + addr = i2c_8bit_addr_from_msg(&m[i]); h1 = op_count/3; h2 = op_count%3; op[h1] |= cpu_to_le32( (u8)addr << ((3-h2)*8)); op[h1] |= cpu_to_le32(SAA7146_I2C_START << ((3-h2)*2)); -- cgit v1.2.3-59-g8ed1b From 19c624c6b29e244c418f8b44a711cbf5e82e3cd4 Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Wed, 9 Jan 2019 13:00:41 -0500 Subject: media: s5p-jpeg: Correct step and max values for V4L2_CID_JPEG_RESTART_INTERVAL This commit corrects max and step values for v4l2 control for V4L2_CID_JPEG_RESTART_INTERVAL. Max should be 0xffff and step should be 1. It was found by using v4l2-compliance tool and checking result of VIDIOC_QUERY_EXT_CTRL/QUERYMENU test. Previously it was complaining that step was bigger than difference between max and min. Fixes: 15f4bc3b1f42 ("[media] s5p-jpeg: Add JPEG controls support") Signed-off-by: Pawe? Chmiel Reviewed-by: Jacek Anaszewski Reviewed-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 85110766043f..0a23b2d19e14 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2005,7 +2005,7 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx) v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, V4L2_CID_JPEG_RESTART_INTERVAL, - 0, 3, 0xffff, 0); + 0, 0xffff, 1, 0); if (ctx->jpeg->variant->version == SJPEG_S5P) mask = ~0x06; /* 422, 420 */ } -- cgit v1.2.3-59-g8ed1b From b9da9b376711f61d0e3c3e419ee348249ca2bd80 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 30 Nov 2018 17:30:48 -0500 Subject: media: ipu3-cio2: Allow probe to succeed if there are no sensors connected The device won't be powered off on systems that have no sensors connected unless it has a driver bound to it. Allow that to happen even if there are no sensors connected to cio2. Reviewed-by: Rajneesh Bhardwaj Tested-by: Rajneesh Bhardwaj Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 447baaebca44..e281e55cdca4 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1810,7 +1810,8 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, /* Register notifier for subdevices we care */ r = cio2_notifier_init(cio2); - if (r) + /* Proceed without sensors connected to allow the device to suspend. */ + if (r && r != -ENODEV) goto fail_cio2_queue_exit; r = devm_request_irq(&pci_dev->dev, pci_dev->irq, cio2_irq, -- cgit v1.2.3-59-g8ed1b From 47ee7bde31fa035fbe92a55ca8782a4eafefb0b6 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 5 Dec 2018 11:32:21 -0500 Subject: media: imx274: fix wrong order in test pattern menus The description of test patterns 11 and 12 are swapped. Checked against the live sensor. Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 5fac7fd32634..95e3d90309e8 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -207,8 +207,8 @@ static const char * const tp_qmenu[] = { "Vertical Stripe (555h / 000h)", "Vertical Stripe (000h / FFFh)", "Vertical Stripe (FFFh / 000h)", - "Horizontal Color Bars", "Vertical Color Bars", + "Horizontal Color Bars", }; /* -- cgit v1.2.3-59-g8ed1b From b0a7d0e1c09b70a07f3251af7d4e538f310245f4 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 9 Dec 2018 00:20:31 -0500 Subject: media: ov2640: set default window and format code at probe time Set default window and format code at probe time instead of always checking if they have not been set yet when VIDIOC_SUBDEV_G_FMT ioctl is called. This change simplifies the next patch (make VIDIOC_SUBDEV_G_FMT ioctl work with V4L2_SUBDEV_FORMAT_TRY). Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2640.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 5d2d6735cc78..1fd893be76f3 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -842,9 +842,6 @@ static int ov2640_set_params(struct i2c_client *client, u8 val; int ret; - if (!win) - return -EINVAL; - switch (code) { case MEDIA_BUS_FMT_RGB565_2X8_BE: dev_dbg(&client->dev, "%s: Selected cfmt RGB565 BE", __func__); @@ -929,10 +926,6 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, if (format->pad) return -EINVAL; - if (!priv->win) { - priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); - priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; - } mf->width = priv->win->width; mf->height = priv->win->height; @@ -1193,6 +1186,9 @@ static int ov2640_probe(struct i2c_client *client, if (ret) goto err_clk; + priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); + priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; + v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; -- cgit v1.2.3-59-g8ed1b From 8d3b307a150a50cb7c1c905209488dbfe9b6221a Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 9 Dec 2018 00:20:32 -0500 Subject: media: ov2640: make VIDIOC_SUBDEV_G_FMT ioctl work with V4L2_SUBDEV_FORMAT_TRY The VIDIOC_SUBDEV_G_FMT ioctl for this driver doesn't recognize V4L2_SUBDEV_FORMAT_TRY and always works as if V4L2_SUBDEV_FORMAT_ACTIVE is specified. Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2640.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 1fd893be76f3..d3f10d92fcc0 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -926,6 +926,15 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, if (format->pad) return -EINVAL; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + mf = v4l2_subdev_get_try_format(sd, cfg, 0); + format->format = *mf; + return 0; +#else + return -ENOTTY; +#endif + } mf->width = priv->win->width; mf->height = priv->win->height; @@ -992,6 +1001,27 @@ out: return ret; } +static int ov2640_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, cfg, 0); + + try_fmt->width = priv->win->width; + try_fmt->height = priv->win->height; + try_fmt->code = priv->cfmt_code; + try_fmt->colorspace = V4L2_COLORSPACE_SRGB; + try_fmt->field = V4L2_FIELD_NONE; + try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +#endif + return 0; +} + static int ov2640_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) @@ -1101,6 +1131,7 @@ static const struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { }; static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { + .init_cfg = ov2640_init_cfg, .enum_mbus_code = ov2640_enum_mbus_code, .get_selection = ov2640_get_selection, .get_fmt = ov2640_get_fmt, -- cgit v1.2.3-59-g8ed1b From bd0405f929ce4c8b8bd0feca0a8efe144345306b Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 9 Dec 2018 00:20:33 -0500 Subject: media: ov2640: set all mbus format field when G_FMT and S_FMT ioctls This driver doesn't set all members of mbus format field when the VIDIOC_SUBDEV_{S,G}_FMT ioctls are called. This is detected by v4l2-compliance. Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2640.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index d3f10d92fcc0..37e7c986af86 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -941,6 +941,9 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, mf->code = priv->cfmt_code; mf->colorspace = V4L2_COLORSPACE_SRGB; mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; return 0; } @@ -967,6 +970,9 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, mf->field = V4L2_FIELD_NONE; mf->colorspace = V4L2_COLORSPACE_SRGB; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; switch (mf->code) { case MEDIA_BUS_FMT_RGB565_2X8_BE: -- cgit v1.2.3-59-g8ed1b From fe367be508473dac43c3277062555d3ab2b995ec Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 14 Nov 2018 07:13:38 -0500 Subject: media: dt-bindings: media: i2c: Fix external clock frequency for OV5645 Commit "4adb0a0432f4 media: ov5645: Supported external clock is 24MHz" modified the external clock frequency to be 24MHz instead of the 23.88MHz in driver. Hence, modify the frequency value in binding. Signed-off-by: Manivannan Sadhasivam Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/i2c/ov5645.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/i2c/ov5645.txt b/Documentation/devicetree/bindings/media/i2c/ov5645.txt index fd7aec9f8e24..b032abfcea36 100644 --- a/Documentation/devicetree/bindings/media/i2c/ov5645.txt +++ b/Documentation/devicetree/bindings/media/i2c/ov5645.txt @@ -37,7 +37,7 @@ Example: clocks = <&clks 200>; clock-names = "xclk"; - clock-frequency = <23880000>; + clock-frequency = <24000000>; vdddo-supply = <&camera_dovdd_1v8>; vdda-supply = <&camera_avdd_2v8>; -- cgit v1.2.3-59-g8ed1b From 961304d17a615f42c38b867640980a3390e3baa6 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 9 Nov 2018 02:56:43 -0500 Subject: media: dt-bindings: media: i2c: Fix i2c address for OV5645 camera sensor The i2c address for the Omnivision OV5645 camera sensor is 0x3c. It is incorrectly mentioned as 0x78 in binding. Hence fix that. Fixes: 09c716af36e6 [media] media: i2c/ov5645: add the device tree binding document Signed-off-by: Manivannan Sadhasivam Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/i2c/ov5645.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/i2c/ov5645.txt b/Documentation/devicetree/bindings/media/i2c/ov5645.txt index b032abfcea36..72ad992f77be 100644 --- a/Documentation/devicetree/bindings/media/i2c/ov5645.txt +++ b/Documentation/devicetree/bindings/media/i2c/ov5645.txt @@ -26,9 +26,9 @@ Example: &i2c1 { ... - ov5645: ov5645@78 { + ov5645: ov5645@3c { compatible = "ovti,ov5645"; - reg = <0x78>; + reg = <0x3c>; enable-gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>; reset-gpios = <&gpio5 20 GPIO_ACTIVE_LOW>; -- cgit v1.2.3-59-g8ed1b From b7ed3abd808afd90ce8a9701799af8a158846b14 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 2 Nov 2018 12:38:43 -0400 Subject: media: ov5640: Add RAW bayer format support OV5640 sensor supports raw image output (bayer). Configure ISP mux/format registers accordingly. Signed-off-by: Loic Poulain Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 58 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index bef3f3aae0ed..5a909abd0a2d 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -115,6 +115,15 @@ enum ov5640_frame_rate { OV5640_NUM_FRAMERATES, }; +enum ov5640_format_mux { + OV5640_FMT_MUX_YUV422 = 0, + OV5640_FMT_MUX_RGB, + OV5640_FMT_MUX_DITHER, + OV5640_FMT_MUX_RAW_DPC, + OV5640_FMT_MUX_SNR_RAW, + OV5640_FMT_MUX_RAW_CIP, +}; + struct ov5640_pixfmt { u32 code; u32 colorspace; @@ -126,6 +135,10 @@ static const struct ov5640_pixfmt ov5640_formats[] = { { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, }, { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_COLORSPACE_SRGB, }, + { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_COLORSPACE_SRGB, }, }; /* @@ -2200,46 +2213,67 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor, struct v4l2_mbus_framefmt *format) { int ret = 0; - bool is_rgb = false; bool is_jpeg = false; - u8 val; + u8 fmt, mux; switch (format->code) { case MEDIA_BUS_FMT_UYVY8_2X8: /* YUV422, UYVY */ - val = 0x3f; + fmt = 0x3f; + mux = OV5640_FMT_MUX_YUV422; break; case MEDIA_BUS_FMT_YUYV8_2X8: /* YUV422, YUYV */ - val = 0x30; + fmt = 0x30; + mux = OV5640_FMT_MUX_YUV422; break; case MEDIA_BUS_FMT_RGB565_2X8_LE: /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */ - val = 0x6F; - is_rgb = true; + fmt = 0x6F; + mux = OV5640_FMT_MUX_RGB; break; case MEDIA_BUS_FMT_RGB565_2X8_BE: /* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */ - val = 0x61; - is_rgb = true; + fmt = 0x61; + mux = OV5640_FMT_MUX_RGB; break; case MEDIA_BUS_FMT_JPEG_1X8: /* YUV422, YUYV */ - val = 0x30; + fmt = 0x30; + mux = OV5640_FMT_MUX_YUV422; is_jpeg = true; break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + /* Raw, BGBG... / GRGR... */ + fmt = 0x00; + mux = OV5640_FMT_MUX_RAW_DPC; + break; + case MEDIA_BUS_FMT_SGBRG8_1X8: + /* Raw bayer, GBGB... / RGRG... */ + fmt = 0x01; + mux = OV5640_FMT_MUX_RAW_DPC; + break; + case MEDIA_BUS_FMT_SGRBG8_1X8: + /* Raw bayer, GRGR... / BGBG... */ + fmt = 0x02; + mux = OV5640_FMT_MUX_RAW_DPC; + break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + /* Raw bayer, RGRG... / GBGB... */ + fmt = 0x03; + mux = OV5640_FMT_MUX_RAW_DPC; + break; default: return -EINVAL; } /* FORMAT CONTROL00: YUV and RGB formatting */ - ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, val); + ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, fmt); if (ret) return ret; /* FORMAT MUX CONTROL: ISP YUV or RGB */ - ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, - is_rgb ? 0x01 : 0x00); + ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, mux); if (ret) return ret; -- cgit v1.2.3-59-g8ed1b From 61da76beef1e4f0b6ba7be4f8d0cf0dac7ce1f55 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 29 Dec 2017 07:22:26 -0500 Subject: media: v4l2: i2c: ov7670: Fix PLL bypass register values The following commits: commit f6dd927f34d6 ("[media] media: ov7670: calculate framerate properly for ov7675") commit 04ee6d92047e ("[media] media: ov7670: add possibility to bypass pll for ov7675") introduced the ability to bypass PLL multiplier and use input clock (xvclk) as pixel clock output frequency for ov7675 sensor. PLL is bypassed using register DBLV[7:6], according to ov7670 and ov7675 sensor manuals. Macros used to set DBLV register seem wrong in the driver, as their values do not match what reported in the datasheet. Fix by changing DBLV_* macros to use bits [7:6] and set bits [3:0] to default 0x0a reserved value (according to datasheets). While at there, remove a write to DBLV register in "ov7675_set_framerate()" that over-writes the previous one to the same register that takes "info->pll_bypass" flag into account instead of setting PLL multiplier to 4x unconditionally. And, while at there, since "info->pll_bypass" is only used in set/get_framerate() functions used by ov7675 only, it is not necessary to check for the device id at probe time to make sure that when using ov7670 "info->pll_bypass" is set to false. Fixes: f6dd927f34d6 ("[media] media: ov7670: calculate framerate properly for ov7675") Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index a70a6ff7b36e..4939a83b50e4 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -160,10 +160,10 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define REG_GFIX 0x69 /* Fix gain control */ #define REG_DBLV 0x6b /* PLL control an debugging */ -#define DBLV_BYPASS 0x00 /* Bypass PLL */ -#define DBLV_X4 0x01 /* clock x4 */ -#define DBLV_X6 0x10 /* clock x6 */ -#define DBLV_X8 0x11 /* clock x8 */ +#define DBLV_BYPASS 0x0a /* Bypass PLL */ +#define DBLV_X4 0x4a /* clock x4 */ +#define DBLV_X6 0x8a /* clock x6 */ +#define DBLV_X8 0xca /* clock x8 */ #define REG_SCALING_XSC 0x70 /* Test pattern and horizontal scale factor */ #define TEST_PATTTERN_0 0x80 @@ -863,7 +863,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, if (ret < 0) return ret; - return ov7670_write(sd, REG_DBLV, DBLV_X4); + return 0; } static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, @@ -1801,11 +1801,7 @@ static int ov7670_probe(struct i2c_client *client, if (config->clock_speed) info->clock_speed = config->clock_speed; - /* - * It should be allowed for ov7670 too when it is migrated to - * the new frame rate formula. - */ - if (config->pll_bypass && id->driver_data != MODEL_OV7670) + if (config->pll_bypass) info->pll_bypass = true; if (config->pclk_hb_disable) -- cgit v1.2.3-59-g8ed1b From 57b0ad9ebe60366b1243bcf612a93a990a3ceff1 Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:12 -0500 Subject: media: soc_camera: ov9640: move ov9640 out of soc_camera Initial part of ov9640 transition from soc_camera subsystem to a standalone v4l2 subdevice. The soc_camera version seems to be used only in Palm Zire72 and in (the future) HTC Magician. On these two devices the support is broken as pxa_camera driver doesn't use soc_camera anymore. The other mentions from git grep are "TODOs" (in board-osk.c) or chip names for unsupported sensors on HW which doesn't use soc_camera at all (irelevant). Copy the driver files from soc_camera and mark the original ones in the Kconfig description as obsoleted. Add config option VIDEO_OV9640 to the build files in drivers/media/i2c. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 7 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov9640.c | 738 +++++++++++++++++++++++++++++++++++ drivers/media/i2c/ov9640.h | 208 ++++++++++ drivers/media/i2c/soc_camera/Kconfig | 6 +- 5 files changed, 958 insertions(+), 2 deletions(-) create mode 100644 drivers/media/i2c/ov9640.c create mode 100644 drivers/media/i2c/ov9640.h diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 4c936e129500..119aaee5318b 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -820,6 +820,13 @@ config VIDEO_OV7740 This is a Video4Linux2 sensor driver for the OmniVision OV7740 VGA camera sensor. +config VIDEO_OV9640 + tristate "OmniVision OV9640 sensor support" + depends on I2C && VIDEO_V4L2 + help + This is a Video4Linux2 sensor driver for the OmniVision + OV9640 camera sensor. + config VIDEO_OV9650 tristate "OmniVision OV9650/OV9652 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 65fae7732de0..5639cb2f20aa 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV772X) += ov772x.o obj-$(CONFIG_VIDEO_OV7740) += ov7740.o +obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c new file mode 100644 index 000000000000..eb91b8240083 --- /dev/null +++ b/drivers/media/i2c/ov9640.c @@ -0,0 +1,738 @@ +/* + * OmniVision OV96xx Camera Driver + * + * Copyright (C) 2009 Marek Vasut + * + * Based on ov772x camera driver: + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on ov7670 and soc_camera_platform driver, + * + * Copyright 2006-7 Jonathan Corbet + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2008, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ov9640.h" + +#define to_ov9640_sensor(sd) container_of(sd, struct ov9640_priv, subdev) + +/* default register setup */ +static const struct ov9640_reg ov9640_regs_dflt[] = { + { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP }, + { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS | + OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN }, + { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) }, + { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD }, + { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY }, + { OV9640_COM16, OV9640_COM16_RB_AVG }, + + /* Gamma curve P */ + { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 }, + { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 }, + { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, + { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 }, + + /* Gamma curve T */ + { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 }, + { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, + { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e }, + { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 }, +}; + +/* Configurations + * NOTE: for YUV, alter the following registers: + * COM12 |= OV9640_COM12_YUV_AVG + * + * for RGB, alter the following registers: + * COM7 |= OV9640_COM7_RGB + * COM13 |= OV9640_COM13_RGB_AVG + * COM15 |= proper RGB color encoding mode + */ +static const struct ov9640_reg ov9640_regs_qqcif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, + { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QCIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qqvga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, + { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QVGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qcif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QCIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qvga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QVGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_cif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, OV9640_COM7_CIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_vga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, OV9640_COM7_VGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_sxga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, 0 }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_yuv[] = { + { OV9640_MTX1, 0x58 }, + { OV9640_MTX2, 0x48 }, + { OV9640_MTX3, 0x10 }, + { OV9640_MTX4, 0x28 }, + { OV9640_MTX5, 0x48 }, + { OV9640_MTX6, 0x70 }, + { OV9640_MTX7, 0x40 }, + { OV9640_MTX8, 0x40 }, + { OV9640_MTX9, 0x40 }, + { OV9640_MTXS, 0x0f }, +}; + +static const struct ov9640_reg ov9640_regs_rgb[] = { + { OV9640_MTX1, 0x71 }, + { OV9640_MTX2, 0x3e }, + { OV9640_MTX3, 0x0c }, + { OV9640_MTX4, 0x33 }, + { OV9640_MTX5, 0x72 }, + { OV9640_MTX6, 0x00 }, + { OV9640_MTX7, 0x2b }, + { OV9640_MTX8, 0x66 }, + { OV9640_MTX9, 0xd2 }, + { OV9640_MTXS, 0x65 }, +}; + +static u32 ov9640_codes[] = { + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + MEDIA_BUS_FMT_RGB565_2X8_LE, +}; + +/* read a register */ +static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) +{ + int ret; + u8 data = reg; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &data, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + goto err; + + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + goto err; + + *val = data; + return 0; + +err: + dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); + return ret; +} + +/* write a register */ +static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret; + u8 _val; + unsigned char data[2] = { reg, val }; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = data, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); + return ret; + } + + /* we have to read the register back ... no idea why, maybe HW bug */ + ret = ov9640_reg_read(client, reg, &_val); + if (ret) + dev_err(&client->dev, + "Failed reading back register 0x%02x!\n", reg); + + return 0; +} + + +/* Read a register, alter its bits, write it back */ +static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset) +{ + u8 val; + int ret; + + ret = ov9640_reg_read(client, reg, &val); + if (ret) { + dev_err(&client->dev, + "[Read]-Modify-Write of register %02x failed!\n", reg); + return ret; + } + + val |= set; + val &= ~unset; + + ret = ov9640_reg_write(client, reg, val); + if (ret) + dev_err(&client->dev, + "Read-Modify-[Write] of register %02x failed!\n", reg); + + return ret; +} + +/* Soft reset the camera. This has nothing to do with the RESET pin! */ +static int ov9640_reset(struct i2c_client *client) +{ + int ret; + + ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET); + if (ret) + dev_err(&client->dev, + "An error occurred while entering soft reset!\n"); + + return ret; +} + +/* Start/Stop streaming from the device */ +static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) +{ + return 0; +} + +/* Set status of additional camera capabilities */ +static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl); + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->val) + return ov9640_reg_rmw(client, OV9640_MVFP, + OV9640_MVFP_V, 0); + return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V); + case V4L2_CID_HFLIP: + if (ctrl->val) + return ov9640_reg_rmw(client, OV9640_MVFP, + OV9640_MVFP_H, 0); + return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); + } + return -EINVAL; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov9640_get_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u8 val; + + if (reg->reg & ~0xff) + return -EINVAL; + + reg->size = 1; + + ret = ov9640_reg_read(client, reg->reg, &val); + if (ret) + return ret; + + reg->val = (__u64)val; + + return 0; +} + +static int ov9640_set_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg & ~0xff || reg->val & ~0xff) + return -EINVAL; + + return ov9640_reg_write(client, reg->reg, reg->val); +} +#endif + +static int ov9640_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct ov9640_priv *priv = to_ov9640_sensor(sd); + + return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); +} + +/* select nearest higher resolution for capture */ +static void ov9640_res_roundup(u32 *width, u32 *height) +{ + int i; + enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; + static const int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; + static const int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; + + for (i = 0; i < ARRAY_SIZE(res_x); i++) { + if (res_x[i] >= *width && res_y[i] >= *height) { + *width = res_x[i]; + *height = res_y[i]; + return; + } + } + + *width = res_x[SXGA]; + *height = res_y[SXGA]; +} + +/* Prepare necessary register changes depending on color encoding */ +static void ov9640_alter_regs(u32 code, + struct ov9640_reg_alt *alt) +{ + switch (code) { + default: + case MEDIA_BUS_FMT_UYVY8_2X8: + alt->com12 = OV9640_COM12_YUV_AVG; + alt->com13 = OV9640_COM13_Y_DELAY_EN | + OV9640_COM13_YUV_DLY(0x01); + break; + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: + alt->com7 = OV9640_COM7_RGB; + alt->com13 = OV9640_COM13_RGB_AVG; + alt->com15 = OV9640_COM15_RGB_555; + break; + case MEDIA_BUS_FMT_RGB565_2X8_LE: + alt->com7 = OV9640_COM7_RGB; + alt->com13 = OV9640_COM13_RGB_AVG; + alt->com15 = OV9640_COM15_RGB_565; + break; + } +} + +/* Setup registers according to resolution and color encoding */ +static int ov9640_write_regs(struct i2c_client *client, u32 width, + u32 code, struct ov9640_reg_alt *alts) +{ + const struct ov9640_reg *ov9640_regs, *matrix_regs; + int ov9640_regs_len, matrix_regs_len; + int i, ret; + u8 val; + + /* select register configuration for given resolution */ + switch (width) { + case W_QQCIF: + ov9640_regs = ov9640_regs_qqcif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif); + break; + case W_QQVGA: + ov9640_regs = ov9640_regs_qqvga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga); + break; + case W_QCIF: + ov9640_regs = ov9640_regs_qcif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif); + break; + case W_QVGA: + ov9640_regs = ov9640_regs_qvga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga); + break; + case W_CIF: + ov9640_regs = ov9640_regs_cif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif); + break; + case W_VGA: + ov9640_regs = ov9640_regs_vga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga); + break; + case W_SXGA: + ov9640_regs = ov9640_regs_sxga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga); + break; + default: + dev_err(&client->dev, "Failed to select resolution!\n"); + return -EINVAL; + } + + /* select color matrix configuration for given color encoding */ + if (code == MEDIA_BUS_FMT_UYVY8_2X8) { + matrix_regs = ov9640_regs_yuv; + matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); + } else { + matrix_regs = ov9640_regs_rgb; + matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb); + } + + /* write register settings into the module */ + for (i = 0; i < ov9640_regs_len; i++) { + val = ov9640_regs[i].val; + + switch (ov9640_regs[i].reg) { + case OV9640_COM7: + val |= alts->com7; + break; + case OV9640_COM12: + val |= alts->com12; + break; + case OV9640_COM13: + val |= alts->com13; + break; + case OV9640_COM15: + val |= alts->com15; + break; + } + + ret = ov9640_reg_write(client, ov9640_regs[i].reg, val); + if (ret) + return ret; + } + + /* write color matrix configuration into the module */ + for (i = 0; i < matrix_regs_len; i++) { + ret = ov9640_reg_write(client, matrix_regs[i].reg, + matrix_regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +/* program default register values */ +static int ov9640_prog_dflt(struct i2c_client *client) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { + ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, + ov9640_regs_dflt[i].val); + if (ret) + return ret; + } + + /* wait for the changes to actually happen, 140ms are not enough yet */ + mdelay(150); + + return 0; +} + +/* set the format we will capture in */ +static int ov9640_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov9640_reg_alt alts = {0}; + int ret; + + ov9640_alter_regs(mf->code, &alts); + + ov9640_reset(client); + + ret = ov9640_prog_dflt(client); + if (ret) + return ret; + + return ov9640_write_regs(client, mf->width, mf->code, &alts); +} + +static int ov9640_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; + + ov9640_res_roundup(&mf->width, &mf->height); + + mf->field = V4L2_FIELD_NONE; + + switch (mf->code) { + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB565_2X8_LE: + mf->colorspace = V4L2_COLORSPACE_SRGB; + break; + default: + mf->code = MEDIA_BUS_FMT_UYVY8_2X8; + /* fall through */ + case MEDIA_BUS_FMT_UYVY8_2X8: + mf->colorspace = V4L2_COLORSPACE_JPEG; + break; + } + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return ov9640_s_fmt(sd, mf); + + cfg->try_fmt = *mf; + return 0; +} + +static int ov9640_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index >= ARRAY_SIZE(ov9640_codes)) + return -EINVAL; + + code->code = ov9640_codes[code->index]; + return 0; +} + +static int ov9640_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + sel->r.left = 0; + sel->r.top = 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP: + sel->r.width = W_SXGA; + sel->r.height = H_SXGA; + return 0; + default: + return -EINVAL; + } +} + +static int ov9640_video_probe(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9640_priv *priv = to_ov9640_sensor(sd); + u8 pid, ver, midh, midl; + const char *devname; + int ret; + + ret = ov9640_s_power(&priv->subdev, 1); + if (ret < 0) + return ret; + + /* + * check and show product ID and manufacturer ID + */ + + ret = ov9640_reg_read(client, OV9640_PID, &pid); + if (!ret) + ret = ov9640_reg_read(client, OV9640_VER, &ver); + if (!ret) + ret = ov9640_reg_read(client, OV9640_MIDH, &midh); + if (!ret) + ret = ov9640_reg_read(client, OV9640_MIDL, &midl); + if (ret) + goto done; + + switch (VERSION(pid, ver)) { + case OV9640_V2: + devname = "ov9640"; + priv->revision = 2; + break; + case OV9640_V3: + devname = "ov9640"; + priv->revision = 3; + break; + default: + dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); + ret = -ENODEV; + goto done; + } + + dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", + devname, pid, ver, midh, midl); + + ret = v4l2_ctrl_handler_setup(&priv->hdl); + +done: + ov9640_s_power(&priv->subdev, 0); + return ret; +} + +static const struct v4l2_ctrl_ops ov9640_ctrl_ops = { + .s_ctrl = ov9640_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops ov9640_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov9640_get_register, + .s_register = ov9640_set_register, +#endif + .s_power = ov9640_s_power, +}; + +/* Request bus settings on camera side */ +static int ov9640_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov9640_video_ops = { + .s_stream = ov9640_s_stream, + .g_mbus_config = ov9640_g_mbus_config, +}; + +static const struct v4l2_subdev_pad_ops ov9640_pad_ops = { + .enum_mbus_code = ov9640_enum_mbus_code, + .get_selection = ov9640_get_selection, + .set_fmt = ov9640_set_fmt, +}; + +static const struct v4l2_subdev_ops ov9640_subdev_ops = { + .core = &ov9640_core_ops, + .video = &ov9640_video_ops, + .pad = &ov9640_pad_ops, +}; + +/* + * i2c_driver function + */ +static int ov9640_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov9640_priv *priv; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + int ret; + + if (!ssdd) { + dev_err(&client->dev, "Missing platform_data for driver\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); + + v4l2_ctrl_handler_init(&priv->hdl, 2); + v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + priv->subdev.ctrl_handler = &priv->hdl; + if (priv->hdl.error) + return priv->hdl.error; + + priv->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + goto eclkget; + } + + ret = ov9640_video_probe(client); + if (ret) { + v4l2_clk_put(priv->clk); +eclkget: + v4l2_ctrl_handler_free(&priv->hdl); + } + + return ret; +} + +static int ov9640_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9640_priv *priv = to_ov9640_sensor(sd); + + v4l2_clk_put(priv->clk); + v4l2_device_unregister_subdev(&priv->subdev); + v4l2_ctrl_handler_free(&priv->hdl); + return 0; +} + +static const struct i2c_device_id ov9640_id[] = { + { "ov9640", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov9640_id); + +static struct i2c_driver ov9640_i2c_driver = { + .driver = { + .name = "ov9640", + }, + .probe = ov9640_probe, + .remove = ov9640_remove, + .id_table = ov9640_id, +}; + +module_i2c_driver(ov9640_i2c_driver); + +MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); +MODULE_AUTHOR("Marek Vasut "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov9640.h b/drivers/media/i2c/ov9640.h new file mode 100644 index 000000000000..65d13ff17536 --- /dev/null +++ b/drivers/media/i2c/ov9640.h @@ -0,0 +1,208 @@ +/* + * OmniVision OV96xx Camera Header File + * + * Copyright (C) 2009 Marek Vasut + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__ +#define __DRIVERS_MEDIA_VIDEO_OV9640_H__ + +/* Register definitions */ +#define OV9640_GAIN 0x00 +#define OV9640_BLUE 0x01 +#define OV9640_RED 0x02 +#define OV9640_VFER 0x03 +#define OV9640_COM1 0x04 +#define OV9640_BAVE 0x05 +#define OV9640_GEAVE 0x06 +#define OV9640_RSID 0x07 +#define OV9640_RAVE 0x08 +#define OV9640_COM2 0x09 +#define OV9640_PID 0x0a +#define OV9640_VER 0x0b +#define OV9640_COM3 0x0c +#define OV9640_COM4 0x0d +#define OV9640_COM5 0x0e +#define OV9640_COM6 0x0f +#define OV9640_AECH 0x10 +#define OV9640_CLKRC 0x11 +#define OV9640_COM7 0x12 +#define OV9640_COM8 0x13 +#define OV9640_COM9 0x14 +#define OV9640_COM10 0x15 +/* 0x16 - RESERVED */ +#define OV9640_HSTART 0x17 +#define OV9640_HSTOP 0x18 +#define OV9640_VSTART 0x19 +#define OV9640_VSTOP 0x1a +#define OV9640_PSHFT 0x1b +#define OV9640_MIDH 0x1c +#define OV9640_MIDL 0x1d +#define OV9640_MVFP 0x1e +#define OV9640_LAEC 0x1f +#define OV9640_BOS 0x20 +#define OV9640_GBOS 0x21 +#define OV9640_GROS 0x22 +#define OV9640_ROS 0x23 +#define OV9640_AEW 0x24 +#define OV9640_AEB 0x25 +#define OV9640_VPT 0x26 +#define OV9640_BBIAS 0x27 +#define OV9640_GBBIAS 0x28 +/* 0x29 - RESERVED */ +#define OV9640_EXHCH 0x2a +#define OV9640_EXHCL 0x2b +#define OV9640_RBIAS 0x2c +#define OV9640_ADVFL 0x2d +#define OV9640_ADVFH 0x2e +#define OV9640_YAVE 0x2f +#define OV9640_HSYST 0x30 +#define OV9640_HSYEN 0x31 +#define OV9640_HREF 0x32 +#define OV9640_CHLF 0x33 +#define OV9640_ARBLM 0x34 +/* 0x35..0x36 - RESERVED */ +#define OV9640_ADC 0x37 +#define OV9640_ACOM 0x38 +#define OV9640_OFON 0x39 +#define OV9640_TSLB 0x3a +#define OV9640_COM11 0x3b +#define OV9640_COM12 0x3c +#define OV9640_COM13 0x3d +#define OV9640_COM14 0x3e +#define OV9640_EDGE 0x3f +#define OV9640_COM15 0x40 +#define OV9640_COM16 0x41 +#define OV9640_COM17 0x42 +/* 0x43..0x4e - RESERVED */ +#define OV9640_MTX1 0x4f +#define OV9640_MTX2 0x50 +#define OV9640_MTX3 0x51 +#define OV9640_MTX4 0x52 +#define OV9640_MTX5 0x53 +#define OV9640_MTX6 0x54 +#define OV9640_MTX7 0x55 +#define OV9640_MTX8 0x56 +#define OV9640_MTX9 0x57 +#define OV9640_MTXS 0x58 +/* 0x59..0x61 - RESERVED */ +#define OV9640_LCC1 0x62 +#define OV9640_LCC2 0x63 +#define OV9640_LCC3 0x64 +#define OV9640_LCC4 0x65 +#define OV9640_LCC5 0x66 +#define OV9640_MANU 0x67 +#define OV9640_MANV 0x68 +#define OV9640_HV 0x69 +#define OV9640_MBD 0x6a +#define OV9640_DBLV 0x6b +#define OV9640_GSP 0x6c /* ... till 0x7b */ +#define OV9640_GST 0x7c /* ... till 0x8a */ + +#define OV9640_CLKRC_DPLL_EN 0x80 +#define OV9640_CLKRC_DIRECT 0x40 +#define OV9640_CLKRC_DIV(x) ((x) & 0x3f) + +#define OV9640_PSHFT_VAL(x) ((x) & 0xff) + +#define OV9640_ACOM_2X_ANALOG 0x80 +#define OV9640_ACOM_RSVD 0x12 + +#define OV9640_MVFP_V 0x10 +#define OV9640_MVFP_H 0x20 + +#define OV9640_COM1_HREF_NOSKIP 0x00 +#define OV9640_COM1_HREF_2SKIP 0x04 +#define OV9640_COM1_HREF_3SKIP 0x08 +#define OV9640_COM1_QQFMT 0x20 + +#define OV9640_COM2_SSM 0x10 + +#define OV9640_COM3_VP 0x04 + +#define OV9640_COM4_QQ_VP 0x80 +#define OV9640_COM4_RSVD 0x40 + +#define OV9640_COM5_SYSCLK 0x80 +#define OV9640_COM5_LONGEXP 0x01 + +#define OV9640_COM6_OPT_BLC 0x40 +#define OV9640_COM6_ADBLC_BIAS 0x08 +#define OV9640_COM6_FMT_RST 0x82 +#define OV9640_COM6_ADBLC_OPTEN 0x01 + +#define OV9640_COM7_RAW_RGB 0x01 +#define OV9640_COM7_RGB 0x04 +#define OV9640_COM7_QCIF 0x08 +#define OV9640_COM7_QVGA 0x10 +#define OV9640_COM7_CIF 0x20 +#define OV9640_COM7_VGA 0x40 +#define OV9640_COM7_SCCB_RESET 0x80 + +#define OV9640_TSLB_YVYU_YUYV 0x04 +#define OV9640_TSLB_YUYV_UYVY 0x08 + +#define OV9640_COM12_YUV_AVG 0x04 +#define OV9640_COM12_RSVD 0x40 + +#define OV9640_COM13_GAMMA_NONE 0x00 +#define OV9640_COM13_GAMMA_Y 0x40 +#define OV9640_COM13_GAMMA_RAW 0x80 +#define OV9640_COM13_RGB_AVG 0x20 +#define OV9640_COM13_MATRIX_EN 0x10 +#define OV9640_COM13_Y_DELAY_EN 0x08 +#define OV9640_COM13_YUV_DLY(x) ((x) & 0x07) + +#define OV9640_COM15_OR_00FF 0x00 +#define OV9640_COM15_OR_01FE 0x40 +#define OV9640_COM15_OR_10F0 0xc0 +#define OV9640_COM15_RGB_NORM 0x00 +#define OV9640_COM15_RGB_565 0x10 +#define OV9640_COM15_RGB_555 0x30 + +#define OV9640_COM16_RB_AVG 0x01 + +/* IDs */ +#define OV9640_V2 0x9648 +#define OV9640_V3 0x9649 +#define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF)) + +/* supported resolutions */ +enum { + W_QQCIF = 88, + W_QQVGA = 160, + W_QCIF = 176, + W_QVGA = 320, + W_CIF = 352, + W_VGA = 640, + W_SXGA = 1280 +}; +#define H_SXGA 960 + +/* Misc. structures */ +struct ov9640_reg_alt { + u8 com7; + u8 com12; + u8 com13; + u8 com15; +}; + +struct ov9640_reg { + u8 reg; + u8 val; +}; + +struct ov9640_priv { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct v4l2_clk *clk; + + int model; + int revision; +}; + +#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */ diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 7c2aabc8a3f6..7d7b801ab2ce 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -42,10 +42,12 @@ config SOC_CAMERA_OV772X This is a ov772x camera driver config SOC_CAMERA_OV9640 - tristate "ov9640 camera support" + tristate "ov9640 camera support (OBSOLETE)" + default n depends on SOC_CAMERA && I2C help - This is a ov9640 camera driver + This is an obsoleted version of ov9640 camera driver. Please use + the v4l2 standalone one (VIDEO_OV9640). config SOC_CAMERA_OV9740 tristate "ov9740 camera support" -- cgit v1.2.3-59-g8ed1b From 9f7e55d235b62e95043917fcb86a87064678074c Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:13 -0500 Subject: media: i2c: ov9640: drop soc_camera code and switch to v4l2_async This patch removes the dependency on an obsoleted soc_camera from ov9640 driver and changes the code to be a standalone v4l2 async subdevice. It also adds GPIO allocations for power and reset signals (as they are not handled by soc_camera now). The values for waiting on GPIOs (reset and power) settling down were taken from the datasheet (> 1 ms after HW/SW reset). The upper limit was chosen as an arbitrary value. Also one occurrence of mdelay() was changed to msleep(). The delays were successfully tested on a real hardware. The patch makes ov9640 sensor again compatible with the pxa_camera driver. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 76 ++++++++++++++++++++++++++++++++-------------- drivers/media/i2c/ov9640.h | 2 ++ 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index eb91b8240083..1f7cf01d037f 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -9,6 +9,7 @@ * Kuninori Morimoto * * Based on ov7670 and soc_camera_platform driver, + * transition from soc_camera to pxa_camera based on mt9m111 * * Copyright 2006-7 Jonathan Corbet * Copyright (C) 2008 Magnus Damm @@ -27,10 +28,14 @@ #include #include -#include +#include #include #include #include +#include +#include + +#include #include "ov9640.h" @@ -323,11 +328,23 @@ static int ov9640_set_register(struct v4l2_subdev *sd, static int ov9640_s_power(struct v4l2_subdev *sd, int on) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct ov9640_priv *priv = to_ov9640_sensor(sd); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); + int ret = 0; + + if (on) { + gpiod_set_value(priv->gpio_power, 1); + usleep_range(1000, 2000); + ret = v4l2_clk_enable(priv->clk); + usleep_range(1000, 2000); + gpiod_set_value(priv->gpio_reset, 0); + } else { + gpiod_set_value(priv->gpio_reset, 1); + usleep_range(1000, 2000); + v4l2_clk_disable(priv->clk); + usleep_range(1000, 2000); + gpiod_set_value(priv->gpio_power, 0); + } + return ret; } /* select nearest higher resolution for capture */ @@ -475,7 +492,7 @@ static int ov9640_prog_dflt(struct i2c_client *client) } /* wait for the changes to actually happen, 140ms are not enough yet */ - mdelay(150); + msleep(150); return 0; } @@ -630,14 +647,10 @@ static const struct v4l2_subdev_core_ops ov9640_core_ops = { static int ov9640_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -666,18 +679,27 @@ static int ov9640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9640_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!ssdd) { - dev_err(&client->dev, "Missing platform_data for driver\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), + GFP_KERNEL); if (!priv) return -ENOMEM; + priv->gpio_power = devm_gpiod_get(&client->dev, "Camera power", + GPIOD_OUT_LOW); + if (IS_ERR_OR_NULL(priv->gpio_power)) { + ret = PTR_ERR(priv->gpio_power); + return ret; + } + + priv->gpio_reset = devm_gpiod_get(&client->dev, "Camera reset", + GPIOD_OUT_HIGH); + if (IS_ERR_OR_NULL(priv->gpio_reset)) { + ret = PTR_ERR(priv->gpio_reset); + return ret; + } + v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); v4l2_ctrl_handler_init(&priv->hdl, 2); @@ -696,12 +718,20 @@ static int ov9640_probe(struct i2c_client *client, } ret = ov9640_video_probe(client); - if (ret) { - v4l2_clk_put(priv->clk); -eclkget: - v4l2_ctrl_handler_free(&priv->hdl); - } + if (ret) + goto eprobe; + priv->subdev.dev = &client->dev; + ret = v4l2_async_register_subdev(&priv->subdev); + if (ret) + goto eprobe; + + return 0; + +eprobe: + v4l2_clk_put(priv->clk); +eclkget: + v4l2_ctrl_handler_free(&priv->hdl); return ret; } @@ -711,7 +741,7 @@ static int ov9640_remove(struct i2c_client *client) struct ov9640_priv *priv = to_ov9640_sensor(sd); v4l2_clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); + v4l2_async_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); return 0; } diff --git a/drivers/media/i2c/ov9640.h b/drivers/media/i2c/ov9640.h index 65d13ff17536..be5e4b29ac69 100644 --- a/drivers/media/i2c/ov9640.h +++ b/drivers/media/i2c/ov9640.h @@ -200,6 +200,8 @@ struct ov9640_priv { struct v4l2_subdev subdev; struct v4l2_ctrl_handler hdl; struct v4l2_clk *clk; + struct gpio_desc *gpio_power; + struct gpio_desc *gpio_reset; int model; int revision; -- cgit v1.2.3-59-g8ed1b From f2ed15c30ea21a921ab5d3f7f7791507a407f807 Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:14 -0500 Subject: media: MAINTAINERS: add Petr Cvek as a maintainer for the ov9640 driver The soc_camera drivers are marked as orphaned. Add Petr Cvek as a new maintainer for ov9640 driver after its switch from the soc_camera. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 02d9ebcafb97..3a3a60615b88 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11229,6 +11229,12 @@ S: Maintained F: drivers/media/i2c/ov7740.c F: Documentation/devicetree/bindings/media/i2c/ov7740.txt +OMNIVISION OV9640 SENSOR DRIVER +M: Petr Cvek +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/ov9640.* + OMNIVISION OV9650 SENSOR DRIVER M: Sakari Ailus R: Akinobu Mita -- cgit v1.2.3-59-g8ed1b From f8de593d5348259c25bb383e80698213b9af4ff0 Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:15 -0500 Subject: media: i2c: ov9640: add missing SPDX identifiers Add missing SPDX identifiers to .c and .h files of the sensor driver. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 5 +---- drivers/media/i2c/ov9640.h | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 1f7cf01d037f..9a6fa609e8c4 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * OmniVision OV96xx Camera Driver * @@ -14,10 +15,6 @@ * Copyright 2006-7 Jonathan Corbet * Copyright (C) 2008 Magnus Damm * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include diff --git a/drivers/media/i2c/ov9640.h b/drivers/media/i2c/ov9640.h index be5e4b29ac69..a8ed6992c1a8 100644 --- a/drivers/media/i2c/ov9640.h +++ b/drivers/media/i2c/ov9640.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * OmniVision OV96xx Camera Header File * * Copyright (C) 2009 Marek Vasut - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__ -- cgit v1.2.3-59-g8ed1b From 74d01576fc85a5a5e7ea8c4de276c7db0d6abc1a Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:16 -0500 Subject: media: i2c: ov9640: change array index or length variables to unsigned The driver uses variables to store frame resolutions and to indexing various arrays. These should be unsigned. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 9a6fa609e8c4..08f3f8247759 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -347,10 +347,10 @@ static int ov9640_s_power(struct v4l2_subdev *sd, int on) /* select nearest higher resolution for capture */ static void ov9640_res_roundup(u32 *width, u32 *height) { - int i; + unsigned int i; enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; - static const int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; - static const int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; + static const u32 res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; + static const u32 res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; for (i = 0; i < ARRAY_SIZE(res_x); i++) { if (res_x[i] >= *width && res_y[i] >= *height) { @@ -393,8 +393,9 @@ static int ov9640_write_regs(struct i2c_client *client, u32 width, u32 code, struct ov9640_reg_alt *alts) { const struct ov9640_reg *ov9640_regs, *matrix_regs; - int ov9640_regs_len, matrix_regs_len; - int i, ret; + unsigned int ov9640_regs_len, matrix_regs_len; + unsigned int i; + int ret; u8 val; /* select register configuration for given resolution */ @@ -479,7 +480,8 @@ static int ov9640_write_regs(struct i2c_client *client, u32 width, /* program default register values */ static int ov9640_prog_dflt(struct i2c_client *client) { - int i, ret; + unsigned int i; + int ret; for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, -- cgit v1.2.3-59-g8ed1b From 856b0b8ffbf69d7b146a62669904576538d74aac Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:17 -0500 Subject: media: i2c: ov9640: add space before return for better clarity Some returns were adjoined to unrelated code blocks. This patch adds a space inbetween. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 08f3f8247759..2839aa3b4fb4 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -286,6 +286,7 @@ static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) OV9640_MVFP_H, 0); return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); } + return -EINVAL; } @@ -341,6 +342,7 @@ static int ov9640_s_power(struct v4l2_subdev *sd, int on) usleep_range(1000, 2000); gpiod_set_value(priv->gpio_power, 0); } + return ret; } @@ -545,6 +547,7 @@ static int ov9640_set_fmt(struct v4l2_subdev *sd, return ov9640_s_fmt(sd, mf); cfg->try_fmt = *mf; + return 0; } @@ -556,6 +559,7 @@ static int ov9640_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; code->code = ov9640_codes[code->index]; + return 0; } @@ -731,6 +735,7 @@ eprobe: v4l2_clk_put(priv->clk); eclkget: v4l2_ctrl_handler_free(&priv->hdl); + return ret; } -- cgit v1.2.3-59-g8ed1b From 95531e462288a9afdcb494abf6d1d123f5a4ff1c Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:18 -0500 Subject: media: i2c: ov9640: make array of supported formats constant An array which defines sensor's supported formats is not written anywhere, so it can be constant. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 2839aa3b4fb4..9739fa8d433a 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -161,7 +161,7 @@ static const struct ov9640_reg ov9640_regs_rgb[] = { { OV9640_MTXS, 0x65 }, }; -static u32 ov9640_codes[] = { +static const u32 ov9640_codes[] = { MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, MEDIA_BUS_FMT_RGB565_2X8_LE, -- cgit v1.2.3-59-g8ed1b From ffe305d261f4d96eb851f1cad860d845edfaef25 Mon Sep 17 00:00:00 2001 From: Petr Cvek Date: Thu, 13 Dec 2018 10:39:19 -0500 Subject: media: i2c: ov9640: fix missing error handling in probe Control handlers registration lacked error path with v4l2_ctrl_handler_free() call. Fix it by using goto to alread existing v4l2_ctrl_handler_free() call. Signed-off-by: Petr Cvek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 9739fa8d433a..c183273fd332 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -710,14 +710,18 @@ static int ov9640_probe(struct i2c_client *client, V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + + if (priv->hdl.error) { + ret = priv->hdl.error; + goto ectrlinit; + } + priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; priv->clk = v4l2_clk_get(&client->dev, "mclk"); if (IS_ERR(priv->clk)) { ret = PTR_ERR(priv->clk); - goto eclkget; + goto ectrlinit; } ret = ov9640_video_probe(client); @@ -733,7 +737,7 @@ static int ov9640_probe(struct i2c_client *client, eprobe: v4l2_clk_put(priv->clk); -eclkget: +ectrlinit: v4l2_ctrl_handler_free(&priv->hdl); return ret; -- cgit v1.2.3-59-g8ed1b From 16aaf112d3e53c9e450cbfc6063b407d1686fe1a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 13 Dec 2018 15:29:34 -0500 Subject: media: ov9640: Wrap long and unwrap short lines, align wrapped lines correctly Some little style fixup work. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index c183273fd332..d6831f28378b 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -271,19 +271,20 @@ static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) /* Set status of additional camera capabilities */ static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) { - struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl); + struct ov9640_priv *priv = container_of(ctrl->handler, + struct ov9640_priv, hdl); struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); switch (ctrl->id) { case V4L2_CID_VFLIP: if (ctrl->val) return ov9640_reg_rmw(client, OV9640_MVFP, - OV9640_MVFP_V, 0); + OV9640_MVFP_V, 0); return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V); case V4L2_CID_HFLIP: if (ctrl->val) return ov9640_reg_rmw(client, OV9640_MVFP, - OV9640_MVFP_H, 0); + OV9640_MVFP_H, 0); return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); } @@ -471,7 +472,7 @@ static int ov9640_write_regs(struct i2c_client *client, u32 width, /* write color matrix configuration into the module */ for (i = 0; i < matrix_regs_len; i++) { ret = ov9640_reg_write(client, matrix_regs[i].reg, - matrix_regs[i].val); + matrix_regs[i].val); if (ret) return ret; } @@ -487,7 +488,7 @@ static int ov9640_prog_dflt(struct i2c_client *client) for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, - ov9640_regs_dflt[i].val); + ov9640_regs_dflt[i].val); if (ret) return ret; } @@ -684,8 +685,7 @@ static int ov9640_probe(struct i2c_client *client, struct ov9640_priv *priv; int ret; - priv = devm_kzalloc(&client->dev, sizeof(*priv), - GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -707,9 +707,9 @@ static int ov9640_probe(struct i2c_client *client, v4l2_ctrl_handler_init(&priv->hdl, 2); v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); + V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); + V4L2_CID_HFLIP, 0, 1, 1, 0); if (priv->hdl.error) { ret = priv->hdl.error; @@ -751,6 +751,7 @@ static int ov9640_remove(struct i2c_client *client) v4l2_clk_put(priv->clk); v4l2_async_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); + return 0; } -- cgit v1.2.3-59-g8ed1b From aace50f1fd8426c8dfffd9999a7c408db3c2f40e Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 30 Nov 2018 02:58:44 -0500 Subject: media: dt-bindings: media: sun6i: Separate H3 compatible from A31 The CSI controller found on the H3 (and H5) is a reduced version of the one found on the A31. It only has 1 channel, instead of 4 channels for time-multiplexed BT.656. Since the H3 is a reduced version, it cannot "fallback" to a compatible that implements more features than it supports. Split out the H3 compatible as a separate entry, with no fallback. Fixes: b7eadaa3a02a ("media: dt-bindings: media: sun6i: Add A31 and H3 compatibles") Signed-off-by: Chen-Yu Tsai Reviewed-by: Jagan Teki Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/sun6i-csi.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/sun6i-csi.txt b/Documentation/devicetree/bindings/media/sun6i-csi.txt index d4ab34f2240c..cc37cf7fd051 100644 --- a/Documentation/devicetree/bindings/media/sun6i-csi.txt +++ b/Documentation/devicetree/bindings/media/sun6i-csi.txt @@ -6,7 +6,7 @@ Allwinner V3s SoC features a CSI module(CSI1) with parallel interface. Required properties: - compatible: value must be one of: * "allwinner,sun6i-a31-csi" - * "allwinner,sun8i-h3-csi", "allwinner,sun6i-a31-csi" + * "allwinner,sun8i-h3-csi" * "allwinner,sun8i-v3s-csi" - reg: base address and size of the memory-mapped region. - interrupts: interrupt associated to this IP -- cgit v1.2.3-59-g8ed1b From 2d9a43be4a2e9e5ba0f9810de81ffe5436dc3eff Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 30 Nov 2018 02:58:45 -0500 Subject: media: sun6i: Add H3 compatible The CSI controller found on the H3 (and H5) is a reduced version of the one found on the A31. It only has 1 channel, instead of 4 channels for time-multiplexed BT.656. Since the H3 is a reduced version, it cannot "fallback" to a compatible that implements more features than it supports. Add a compatible string entry for the H3. Signed-off-by: Chen-Yu Tsai Reviewed-by: Jagan Teki Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 6950585edb5a..ee882b66a5ea 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -893,6 +893,7 @@ static int sun6i_csi_remove(struct platform_device *pdev) static const struct of_device_id sun6i_csi_of_match[] = { { .compatible = "allwinner,sun6i-a31-csi", }, + { .compatible = "allwinner,sun8i-h3-csi", }, { .compatible = "allwinner,sun8i-v3s-csi", }, {}, }; -- cgit v1.2.3-59-g8ed1b From 63ca0d7d032153312d2a2acdd21e69f665ba8e3a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 7 Jan 2019 06:06:18 -0500 Subject: media: MAINTAINERS: Update reviewers for ipu3-cio2 Remove Jian Xu from the driver's reviewers. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 3a3a60615b88..17ad1d7b5510 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7696,7 +7696,6 @@ M: Yong Zhi M: Sakari Ailus M: Bingbu Cao R: Tian Shu Qiu -R: Jian Xu Zheng L: linux-media@vger.kernel.org S: Maintained F: drivers/media/pci/intel/ipu3/ -- cgit v1.2.3-59-g8ed1b From 8f57763e521c2798a8e1c91efb822fd6e0fb3b72 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 7 Jan 2019 06:07:05 -0500 Subject: media: ipu3-cio2, dw9714: Remove Jian Xu's e-mail Jian Xu has left the company. Remove his e-mail address that no longer works. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/dw9714.c | 2 +- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c index 26d83693a681..3f0b082f863f 100644 --- a/drivers/media/i2c/dw9714.c +++ b/drivers/media/i2c/dw9714.c @@ -267,7 +267,7 @@ static struct i2c_driver dw9714_i2c_driver = { module_i2c_driver(dw9714_i2c_driver); MODULE_AUTHOR("Tianshu Qiu "); -MODULE_AUTHOR("Jian Xu Zheng "); +MODULE_AUTHOR("Jian Xu Zheng"); MODULE_AUTHOR("Yuning Pu "); MODULE_AUTHOR("Jouni Ukkonen "); MODULE_AUTHOR("Tommi Franttila "); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index e281e55cdca4..01ebd3550d88 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -2048,7 +2048,7 @@ module_pci_driver(cio2_pci_driver); MODULE_AUTHOR("Tuukka Toivonen "); MODULE_AUTHOR("Tianshu Qiu "); -MODULE_AUTHOR("Jian Xu Zheng "); +MODULE_AUTHOR("Jian Xu Zheng"); MODULE_AUTHOR("Yuning Pu "); MODULE_AUTHOR("Yong Zhi "); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 6f7f1bff2d9896fc743e7e110e9ab288f5ad7cbf Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Jan 2019 08:50:54 -0500 Subject: media: vivid: disable VB2_USERPTR if dma_contig was configured It makes no sense to support the USERPTR memory model if the vivid instance was configured as dma_contig. Disable it if this is the case. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-core.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index c931f007e5b0..745ba6678b40 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -1094,7 +1094,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) q = &dev->vb_vid_cap_q; q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + if (!allocator) + q->io_modes |= VB2_USERPTR; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivid_buffer); q->ops = &vivid_vid_cap_qops; @@ -1115,7 +1117,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) q = &dev->vb_vid_out_q; q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_OUTPUT; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; + if (!allocator) + q->io_modes |= VB2_USERPTR; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivid_buffer); q->ops = &vivid_vid_out_qops; @@ -1136,7 +1140,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) q = &dev->vb_vbi_cap_q; q->type = dev->has_raw_vbi_cap ? V4L2_BUF_TYPE_VBI_CAPTURE : V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + if (!allocator) + q->io_modes |= VB2_USERPTR; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivid_buffer); q->ops = &vivid_vbi_cap_qops; @@ -1157,7 +1163,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) q = &dev->vb_vbi_out_q; q->type = dev->has_raw_vbi_out ? V4L2_BUF_TYPE_VBI_OUTPUT : V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; + if (!allocator) + q->io_modes |= VB2_USERPTR; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivid_buffer); q->ops = &vivid_vbi_out_qops; @@ -1177,7 +1185,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) /* initialize sdr_cap queue */ q = &dev->vb_sdr_cap_q; q->type = V4L2_BUF_TYPE_SDR_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + if (!allocator) + q->io_modes |= VB2_USERPTR; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivid_buffer); q->ops = &vivid_sdr_cap_qops; -- cgit v1.2.3-59-g8ed1b From 4b837c6d7ee771f68a30f362c9f68171a95be222 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 14 Jan 2019 09:01:54 -0500 Subject: media: v4l: uAPI: V4L2_BUF_TYPE_META_OUTPUT is an output buffer type V4L2_BUF_TYPE_META_OUTPUT was added by commit 72148d1a57e7 ("media: v4l: Add support for V4L2_BUF_TYPE_META_OUTPUT") but the patch missed adding the type to the macro telling whether a given type is an output type or not. Do that now. Getting this wrong leads to handling the buffer as a capture buffer in a lot of places. Fixes: 72148d1a57e7 ("media: v4l: Add support for V4L2_BUF_TYPE_META_OUTPUT") Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index d6eed479c3a6..c0c36c165bf4 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -161,7 +161,8 @@ enum v4l2_buf_type { || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY \ || (type) == V4L2_BUF_TYPE_VBI_OUTPUT \ || (type) == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT \ - || (type) == V4L2_BUF_TYPE_SDR_OUTPUT) + || (type) == V4L2_BUF_TYPE_SDR_OUTPUT \ + || (type) == V4L2_BUF_TYPE_META_OUTPUT) enum v4l2_tuner_type { V4L2_TUNER_RADIO = 1, -- cgit v1.2.3-59-g8ed1b From 987303cc172a0899bc25b913d1315809a9fb8d17 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Jan 2019 11:23:52 -0500 Subject: media: vivid: take data_offset into account for video output The video output sizeimage calculation did not take data_offset into account. This can cause problems with video loopback or exporting output buffers for use as dmabuf import buffers since the output buffer size is now too small. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-vid-out.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index ea250aee2b2e..55c0628ee9e1 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -28,11 +28,12 @@ static int vid_out_queue_setup(struct vb2_queue *vq, const struct vivid_fmt *vfmt = dev->fmt_out; unsigned planes = vfmt->buffers; unsigned h = dev->fmt_out_rect.height; - unsigned size = dev->bytesperline_out[0] * h; + unsigned int size = dev->bytesperline_out[0] * h + vfmt->data_offset[0]; unsigned p; for (p = vfmt->buffers; p < vfmt->planes; p++) - size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p]; + size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p] + + vfmt->data_offset[p]; if (dev->field_out == V4L2_FIELD_ALTERNATE) { /* @@ -62,12 +63,14 @@ static int vid_out_queue_setup(struct vb2_queue *vq, if (sizes[0] < size) return -EINVAL; for (p = 1; p < planes; p++) { - if (sizes[p] < dev->bytesperline_out[p] * h) + if (sizes[p] < dev->bytesperline_out[p] * h + + vfmt->data_offset[p]) return -EINVAL; } } else { for (p = 0; p < planes; p++) - sizes[p] = p ? dev->bytesperline_out[p] * h : size; + sizes[p] = p ? dev->bytesperline_out[p] * h + + vfmt->data_offset[p] : size; } if (vq->num_buffers + *nbuffers < 2) @@ -321,7 +324,8 @@ int vivid_g_fmt_vid_out(struct file *file, void *priv, for (p = 0; p < mp->num_planes; p++) { mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p]; mp->plane_fmt[p].sizeimage = - mp->plane_fmt[p].bytesperline * mp->height; + mp->plane_fmt[p].bytesperline * mp->height + + fmt->data_offset[p]; } for (p = fmt->buffers; p < fmt->planes; p++) { unsigned stride = dev->bytesperline_out[p]; @@ -399,7 +403,7 @@ int vivid_try_fmt_vid_out(struct file *file, void *priv, pfmt[p].bytesperline = bytesperline; pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) / - fmt->vdownsampling[p]; + fmt->vdownsampling[p] + fmt->data_offset[p]; memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved)); } -- cgit v1.2.3-59-g8ed1b From 3f122df4a2ba6704ccf51f5caec583443514644f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Jan 2019 06:37:03 -0500 Subject: media: vivid: do not implement VIDIOC_S_PARM for output streams v4l2_compliance gave a warning for the S_PARM test for output streams: warn: v4l2-test-formats.cpp(1235): S_PARM is supported for buftype 2, but not for ENUM_FRAMEINTERVALS The reason is that vivid mapped s_parm for output streams to g_parm. But if S_PARM doesn't actually change anything, then it shouldn't be enabled at all. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 745ba6678b40..29e7b14fa704 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -371,7 +371,7 @@ static int vidioc_s_parm(struct file *file, void *fh, if (vdev->vfl_dir == VFL_DIR_RX) return vivid_vid_cap_s_parm(file, fh, parm); - return vivid_vid_out_g_parm(file, fh, parm); + return -ENOTTY; } static int vidioc_log_status(struct file *file, void *fh) -- cgit v1.2.3-59-g8ed1b From 86824694184642a4dc1be0c050df9854da5f51d3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Jan 2019 06:43:12 -0500 Subject: media: vim2m: the v4l2_m2m_buf_copy_data args were swapped The buffer arguments to v4l2_m2m_buf_copy_data args were swapped. The reason is confusing naming conventions in vim2m. It certainly could be improved. Fixes: 7aca565ee3d0 ("media: vim2m: use v4l2_m2m_buf_copy_data") Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 33397d4a1402..a7a152fb3075 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -241,7 +241,7 @@ static int device_process(struct vim2m_ctx *ctx, out_vb->sequence = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; in_vb->sequence = q_data->sequence++; - v4l2_m2m_buf_copy_data(out_vb, in_vb, true); + v4l2_m2m_buf_copy_data(in_vb, out_vb, true); switch (ctx->mode) { case MEM2MEM_HFLIP | MEM2MEM_VFLIP: -- cgit v1.2.3-59-g8ed1b From 4d4abf064d676dcc27952ed90f4f50d7087318c7 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:24 -0500 Subject: media: gspca: ov534: replace msleep(10) with usleep_range For short waits, usleep_range should be used instead of msleep, see Documentation/timers/timers-howto.txt. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index d06dc0755b9a..44f06a58bb67 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -679,7 +679,7 @@ static int sccb_check_status(struct gspca_dev *gspca_dev) int i; for (i = 0; i < 5; i++) { - msleep(10); + usleep_range(10000, 20000); data = ov534_reg_read(gspca_dev, OV534_REG_STATUS); switch (data) { @@ -1277,7 +1277,7 @@ static int sd_init(struct gspca_dev *gspca_dev) /* reset sensor */ sccb_reg_write(gspca_dev, 0x12, 0x80); - msleep(10); + usleep_range(10000, 20000); /* probe the sensor */ sccb_reg_read(gspca_dev, 0x0a); -- cgit v1.2.3-59-g8ed1b From fc78e0b2471d6efa19f896d7c93a8ecda9913ddd Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:25 -0500 Subject: media: gspca: support multiple pixel formats in ENUM_FRAMEINTERVALS If a driver supports multiple pixel formats with the same frame size, ENUM_FRAMEINTERVALS will currently only work for the first pixel format. Fix this by adding pixelformat support to wxh_to_mode(). Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/gspca.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 3137f5d89d80..13361cfa6903 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -912,13 +912,14 @@ static void gspca_set_default_mode(struct gspca_dev *gspca_dev) } static int wxh_to_mode(struct gspca_dev *gspca_dev, - int width, int height) + int width, int height, u32 pixelformat) { int i; for (i = 0; i < gspca_dev->cam.nmodes; i++) { if (width == gspca_dev->cam.cam_mode[i].width - && height == gspca_dev->cam.cam_mode[i].height) + && height == gspca_dev->cam.cam_mode[i].height + && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) return i; } return -EINVAL; @@ -1152,7 +1153,8 @@ static int vidioc_enum_frameintervals(struct file *filp, void *priv, int mode; __u32 i; - mode = wxh_to_mode(gspca_dev, fival->width, fival->height); + mode = wxh_to_mode(gspca_dev, fival->width, fival->height, + fival->pixel_format); if (mode < 0) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From d4809b778a0899f732e808e60b3a42cfce0b5e40 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:26 -0500 Subject: media: gspca: support multiple pixel formats in TRY_FMT If a driver supports multiple pixel formats with the same frame size, TRY_FMT will currently always return the first pixel format. Fix this by adding pixelformat support to wxh_to_nearest_mode(). Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/gspca.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 13361cfa6903..ac70b36d67b7 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -926,10 +926,16 @@ static int wxh_to_mode(struct gspca_dev *gspca_dev, } static int wxh_to_nearest_mode(struct gspca_dev *gspca_dev, - int width, int height) + int width, int height, u32 pixelformat) { int i; + for (i = gspca_dev->cam.nmodes; --i > 0; ) { + if (width >= gspca_dev->cam.cam_mode[i].width + && height >= gspca_dev->cam.cam_mode[i].height + && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) + return i; + } for (i = gspca_dev->cam.nmodes; --i > 0; ) { if (width >= gspca_dev->cam.cam_mode[i].width && height >= gspca_dev->cam.cam_mode[i].height) @@ -1059,7 +1065,7 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, fmt->fmt.pix.pixelformat, w, h); /* search the nearest mode for width and height */ - mode = wxh_to_nearest_mode(gspca_dev, w, h); + mode = wxh_to_nearest_mode(gspca_dev, w, h, fmt->fmt.pix.pixelformat); /* OK if right palette */ if (gspca_dev->cam.cam_mode[mode].pixelformat -- cgit v1.2.3-59-g8ed1b From c53304f2ce702091d47fffbac52d351ff8094c68 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:27 -0500 Subject: media: gspca: ov543-ov772x: move video format specific registers into bridge_start In preparation for adding SGBRG8 as a second video format besides YUYV, move video format specific register settings from the bridge_init array into the bridge_start arrays. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 44f06a58bb67..077c49a74709 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -411,9 +411,7 @@ static const u8 sensor_start_qvga_767x[][2] = { }; static const u8 bridge_init_772x[][2] = { - { 0xc2, 0x0c }, { 0x88, 0xf8 }, - { 0xc3, 0x69 }, { 0x89, 0xff }, { 0x76, 0x03 }, { 0x92, 0x01 }, @@ -439,7 +437,6 @@ static const u8 bridge_init_772x[][2] = { { 0x1f, 0x81 }, { 0x34, 0x05 }, { 0xe3, 0x04 }, - { 0x88, 0x00 }, { 0x89, 0x00 }, { 0x76, 0x00 }, { 0xe7, 0x2e }, @@ -460,13 +457,7 @@ static const u8 bridge_init_772x[][2] = { { 0x1d, 0x08 }, /* turn on UVC header */ { 0x1d, 0x0e }, /* .. */ - { 0x8d, 0x1c }, - { 0x8e, 0x80 }, { 0xe5, 0x04 }, - - { 0xc0, 0x50 }, - { 0xc1, 0x3c }, - { 0xc2, 0x0c }, }; static const u8 sensor_init_772x[][2] = { { 0x12, 0x80 }, @@ -562,6 +553,7 @@ static const u8 sensor_init_772x[][2] = { { 0x0c, 0xd0 } }; static const u8 bridge_start_vga_772x[][2] = { + {0x88, 0x00}, {0x1c, 0x00}, {0x1d, 0x40}, {0x1d, 0x02}, @@ -569,8 +561,12 @@ static const u8 bridge_start_vga_772x[][2] = { {0x1d, 0x02}, {0x1d, 0x58}, {0x1d, 0x00}, + {0x8d, 0x1c}, + {0x8e, 0x80}, {0xc0, 0x50}, {0xc1, 0x3c}, + {0xc2, 0x0c}, + {0xc3, 0x69}, }; static const u8 sensor_start_vga_772x[][2] = { {0x12, 0x00}, @@ -583,6 +579,7 @@ static const u8 sensor_start_vga_772x[][2] = { {0x65, 0x20}, }; static const u8 bridge_start_qvga_772x[][2] = { + {0x88, 0x00}, {0x1c, 0x00}, {0x1d, 0x40}, {0x1d, 0x02}, @@ -590,8 +587,12 @@ static const u8 bridge_start_qvga_772x[][2] = { {0x1d, 0x01}, {0x1d, 0x4b}, {0x1d, 0x00}, + {0x8d, 0x1c}, + {0x8e, 0x80}, {0xc0, 0x28}, {0xc1, 0x1e}, + {0xc2, 0x0c}, + {0xc3, 0x69}, }; static const u8 sensor_start_qvga_772x[][2] = { {0x12, 0x40}, -- cgit v1.2.3-59-g8ed1b From 52b756aa90713e4d403ef841a9403610de3fd5b5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:28 -0500 Subject: media: gspca: ov534-ov772x: add SGBRG8 bayer mode support Add support to pass through the sensor's native SGBRG8 bayer pattern, allowing to cut the required USB bandwidth in half. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 115 ++++++++++++++++++++++++++++++++++------ 1 file changed, 98 insertions(+), 17 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 077c49a74709..5b73f7f58ae6 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -103,6 +103,16 @@ static const struct v4l2_pix_format ov772x_mode[] = { .sizeimage = 640 * 480 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, + {320, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, }; static const struct v4l2_pix_format ov767x_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, @@ -127,6 +137,14 @@ static const struct framerates ov772x_framerates[] = { .rates = vga_rates, .nrates = ARRAY_SIZE(vga_rates), }, + { /* 320x240 SGBRG8 */ + .rates = qvga_rates, + .nrates = ARRAY_SIZE(qvga_rates), + }, + { /* 640x480 SGBRG8 */ + .rates = vga_rates, + .nrates = ARRAY_SIZE(vga_rates), + }, }; struct reg_array { @@ -552,7 +570,7 @@ static const u8 sensor_init_772x[][2] = { { 0x8e, 0x00 }, /* De-noise threshold */ { 0x0c, 0xd0 } }; -static const u8 bridge_start_vga_772x[][2] = { +static const u8 bridge_start_vga_yuyv_772x[][2] = { {0x88, 0x00}, {0x1c, 0x00}, {0x1d, 0x40}, @@ -568,7 +586,7 @@ static const u8 bridge_start_vga_772x[][2] = { {0xc2, 0x0c}, {0xc3, 0x69}, }; -static const u8 sensor_start_vga_772x[][2] = { +static const u8 sensor_start_vga_yuyv_772x[][2] = { {0x12, 0x00}, {0x17, 0x26}, {0x18, 0xa0}, @@ -577,8 +595,9 @@ static const u8 sensor_start_vga_772x[][2] = { {0x29, 0xa0}, {0x2c, 0xf0}, {0x65, 0x20}, + {0x67, 0x00}, }; -static const u8 bridge_start_qvga_772x[][2] = { +static const u8 bridge_start_qvga_yuyv_772x[][2] = { {0x88, 0x00}, {0x1c, 0x00}, {0x1d, 0x40}, @@ -594,7 +613,7 @@ static const u8 bridge_start_qvga_772x[][2] = { {0xc2, 0x0c}, {0xc3, 0x69}, }; -static const u8 sensor_start_qvga_772x[][2] = { +static const u8 sensor_start_qvga_yuyv_772x[][2] = { {0x12, 0x40}, {0x17, 0x3f}, {0x18, 0x50}, @@ -603,6 +622,61 @@ static const u8 sensor_start_qvga_772x[][2] = { {0x29, 0x50}, {0x2c, 0x78}, {0x65, 0x2f}, + {0x67, 0x00}, +}; +static const u8 bridge_start_vga_gbrg_772x[][2] = { + {0x88, 0x08}, + {0x1c, 0x00}, + {0x1d, 0x00}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x01}, + {0x1d, 0x2c}, + {0x1d, 0x00}, + {0x8d, 0x00}, + {0x8e, 0x00}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0xc2, 0x01}, + {0xc3, 0x01}, +}; +static const u8 sensor_start_vga_gbrg_772x[][2] = { + {0x12, 0x01}, + {0x17, 0x26}, + {0x18, 0xa0}, + {0x19, 0x07}, + {0x1a, 0xf0}, + {0x29, 0xa0}, + {0x2c, 0xf0}, + {0x65, 0x20}, + {0x67, 0x02}, +}; +static const u8 bridge_start_qvga_gbrg_772x[][2] = { + {0x88, 0x08}, + {0x1c, 0x00}, + {0x1d, 0x00}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x00}, + {0x1d, 0x4b}, + {0x1d, 0x00}, + {0x8d, 0x00}, + {0x8e, 0x00}, + {0xc0, 0x28}, + {0xc1, 0x1e}, + {0xc2, 0x01}, + {0xc3, 0x01}, +}; +static const u8 sensor_start_qvga_gbrg_772x[][2] = { + {0x12, 0x41}, + {0x17, 0x3f}, + {0x18, 0x50}, + {0x19, 0x03}, + {0x1a, 0x78}, + {0x29, 0x50}, + {0x2c, 0x78}, + {0x65, 0x2f}, + {0x67, 0x02}, }; static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) @@ -1316,25 +1390,33 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int mode; - static const struct reg_array bridge_start[NSENSORS][2] = { + static const struct reg_array bridge_start[NSENSORS][4] = { [SENSOR_OV767x] = {{bridge_start_qvga_767x, ARRAY_SIZE(bridge_start_qvga_767x)}, {bridge_start_vga_767x, ARRAY_SIZE(bridge_start_vga_767x)}}, - [SENSOR_OV772x] = {{bridge_start_qvga_772x, - ARRAY_SIZE(bridge_start_qvga_772x)}, - {bridge_start_vga_772x, - ARRAY_SIZE(bridge_start_vga_772x)}}, + [SENSOR_OV772x] = {{bridge_start_qvga_yuyv_772x, + ARRAY_SIZE(bridge_start_qvga_yuyv_772x)}, + {bridge_start_vga_yuyv_772x, + ARRAY_SIZE(bridge_start_vga_yuyv_772x)}, + {bridge_start_qvga_gbrg_772x, + ARRAY_SIZE(bridge_start_qvga_gbrg_772x)}, + {bridge_start_vga_gbrg_772x, + ARRAY_SIZE(bridge_start_vga_gbrg_772x)} }, }; - static const struct reg_array sensor_start[NSENSORS][2] = { + static const struct reg_array sensor_start[NSENSORS][4] = { [SENSOR_OV767x] = {{sensor_start_qvga_767x, ARRAY_SIZE(sensor_start_qvga_767x)}, {sensor_start_vga_767x, ARRAY_SIZE(sensor_start_vga_767x)}}, - [SENSOR_OV772x] = {{sensor_start_qvga_772x, - ARRAY_SIZE(sensor_start_qvga_772x)}, - {sensor_start_vga_772x, - ARRAY_SIZE(sensor_start_vga_772x)}}, + [SENSOR_OV772x] = {{sensor_start_qvga_yuyv_772x, + ARRAY_SIZE(sensor_start_qvga_yuyv_772x)}, + {sensor_start_vga_yuyv_772x, + ARRAY_SIZE(sensor_start_vga_yuyv_772x)}, + {sensor_start_qvga_gbrg_772x, + ARRAY_SIZE(sensor_start_qvga_gbrg_772x)}, + {sensor_start_vga_gbrg_772x, + ARRAY_SIZE(sensor_start_vga_gbrg_772x)} }, }; /* (from ms-win trace) */ @@ -1440,10 +1522,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* If this packet is marked as EOF, end the frame */ } else if (data[1] & UVC_STREAM_EOF) { sd->last_pts = 0; - if (gspca_dev->pixfmt.pixelformat == V4L2_PIX_FMT_YUYV + if (gspca_dev->pixfmt.pixelformat != V4L2_PIX_FMT_JPEG && gspca_dev->image_len + len - 12 != - gspca_dev->pixfmt.width * - gspca_dev->pixfmt.height * 2) { + gspca_dev->pixfmt.sizeimage) { gspca_dbg(gspca_dev, D_PACK, "wrong sized frame\n"); goto discard; } -- cgit v1.2.3-59-g8ed1b From ee1845d1cb9f6e6c74cfce958ce51985bf2a631e Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:29 -0500 Subject: media: gspca: ov534-ov722x: remove mode specific video data registers from bridge_init The video format, payload size, and frame size setup is video format and frame size specific. Those registers are overwritten during bridge_start anyway. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 5b73f7f58ae6..bc9d2eb5db30 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -462,15 +462,6 @@ static const u8 bridge_init_772x[][2] = { { 0x25, 0x42 }, { 0x21, 0xf0 }, - { 0x1c, 0x00 }, - { 0x1d, 0x40 }, - { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */ - { 0x1d, 0x00 }, /* payload size */ - - { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */ - { 0x1d, 0x58 }, /* frame size */ - { 0x1d, 0x00 }, /* frame size */ - { 0x1c, 0x0a }, { 0x1d, 0x08 }, /* turn on UVC header */ { 0x1d, 0x0e }, /* .. */ -- cgit v1.2.3-59-g8ed1b From c5550bb9502bf6ef2eb514eee5eec486fe1cfb5c Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:30 -0500 Subject: media: gspca: ov534-ov722x: remove camera clock setup from bridge_init This register is later overwritten by set_frame_rate anyway. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index bc9d2eb5db30..23deeedd3279 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -465,8 +465,6 @@ static const u8 bridge_init_772x[][2] = { { 0x1c, 0x0a }, { 0x1d, 0x08 }, /* turn on UVC header */ { 0x1d, 0x0e }, /* .. */ - - { 0xe5, 0x04 }, }; static const u8 sensor_init_772x[][2] = { { 0x12, 0x80 }, -- cgit v1.2.3-59-g8ed1b From 530993347bae43648f4aca2966c37cd269af66b7 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 14 Dec 2018 11:40:31 -0500 Subject: media: gspca: ov534-ov772x: remove unnecessary COM3 initialization The COM3 register at address 0x0c already defaults to 0x10, the two bits COM3[7:6] are set according to V4L2 controls by sethvflip later. There is no need to set it multiple times during bridge initialization. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 23deeedd3279..02c90ad96b76 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -543,13 +543,10 @@ static const u8 sensor_init_772x[][2] = { { 0x8c, 0xe8 }, { 0x8d, 0x20 }, - { 0x0c, 0x90 }, - { 0x2b, 0x00 }, { 0x22, 0x7f }, { 0x23, 0x03 }, { 0x11, 0x01 }, - { 0x0c, 0xd0 }, { 0x64, 0xff }, { 0x0d, 0x41 }, @@ -557,7 +554,6 @@ static const u8 sensor_init_772x[][2] = { { 0x0e, 0xcd }, { 0xac, 0xbf }, { 0x8e, 0x00 }, /* De-noise threshold */ - { 0x0c, 0xd0 } }; static const u8 bridge_start_vga_yuyv_772x[][2] = { {0x88, 0x00}, -- cgit v1.2.3-59-g8ed1b From 50656bad786d001b294764e9f047c5d5b3e4db75 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jan 2019 11:56:09 -0500 Subject: media: v4l2-ctrl: Add control to enable h.264 constrained intra prediction Allow to enable h.264 constrained intra prediction (macroblocks using intra prediction modes are not allowed to use residual data and decoded samples of neighboring macroblocks coded using inter prediction modes). This control directly corresponds to the constrained_intra_pred_flag field in the h.264 picture parameter set. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/extended-controls.rst | 4 ++++ drivers/media/v4l2-core/v4l2-ctrls.c | 2 ++ include/uapi/linux/v4l2-controls.h | 1 + 3 files changed, 7 insertions(+) diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index af4273aa5e85..235d0c293983 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -1154,6 +1154,10 @@ enum v4l2_mpeg_video_h264_entropy_mode - ``V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM (boolean)`` Enable 8X8 transform for H264. Applicable to the H264 encoder. +``V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (boolean)`` + Enable constrained intra prediction for H264. Applicable to the H264 + encoder. + ``V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (integer)`` Cyclic intra macroblock refresh. This is the number of continuous macroblocks refreshed every frame. Each frame a successive set of diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 8c47d8f00429..50b56185c02e 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -825,6 +825,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER:return "H264 Number of HC Layers"; case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP: return "H264 Set QP Value for HC Layers"; + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: + return "H264 Constrained Intra Pred"; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B-Frame QP Value"; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 3dcfc6148f99..fd65c710b144 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -533,6 +533,7 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type { }; #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER (V4L2_CID_MPEG_BASE+381) #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP (V4L2_CID_MPEG_BASE+382) +#define V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (V4L2_CID_MPEG_BASE+383) #define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_MPEG_BASE+400) #define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_MPEG_BASE+401) #define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_MPEG_BASE+402) -- cgit v1.2.3-59-g8ed1b From d034696cbe5a6e00f76ca4b7869c6cdef66aebd5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jan 2019 11:56:10 -0500 Subject: media: v4l2-ctrl: Add control for h.264 chroma qp offset Allow to add fixed quantization parameter offset between luma and chroma quantization parameters. This control directly corresponds to the chroma_qp_index_offset field of the h.264 picture parameter set. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/extended-controls.rst | 5 +++++ drivers/media/v4l2-core/v4l2-ctrls.c | 1 + include/uapi/linux/v4l2-controls.h | 1 + 3 files changed, 7 insertions(+) diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index 235d0c293983..00934efdc9e4 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -1158,6 +1158,11 @@ enum v4l2_mpeg_video_h264_entropy_mode - Enable constrained intra prediction for H264. Applicable to the H264 encoder. +``V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET (integer)`` + Specify the offset that should be added to the luma quantization + parameter to determine the chroma quantization parameter. Applicable + to the H264 encoder. + ``V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (integer)`` Cyclic intra macroblock refresh. This is the number of continuous macroblocks refreshed every frame. Each frame a successive set of diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 50b56185c02e..99308dac2daa 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -827,6 +827,7 @@ const char *v4l2_ctrl_get_name(u32 id) return "H264 Set QP Value for HC Layers"; case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: return "H264 Constrained Intra Pred"; + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: return "H264 Chroma QP Index Offset"; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B-Frame QP Value"; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index fd65c710b144..06479f2fb3ae 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -534,6 +534,7 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type { #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER (V4L2_CID_MPEG_BASE+381) #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP (V4L2_CID_MPEG_BASE+382) #define V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (V4L2_CID_MPEG_BASE+383) +#define V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET (V4L2_CID_MPEG_BASE+384) #define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_MPEG_BASE+400) #define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_MPEG_BASE+401) #define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_MPEG_BASE+402) -- cgit v1.2.3-59-g8ed1b From 31489f6bdabf4fa96f354fe3520ea779bebc6ee7 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jan 2019 11:56:11 -0500 Subject: media: coda: Add control for h.264 constrained intra prediction Allow to enable constrained intra prediction in the h.264 encoder. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 4 +++- drivers/media/platform/coda/coda-common.c | 6 ++++++ drivers/media/platform/coda/coda.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 8e0194993a52..2998c869f79f 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1010,7 +1010,9 @@ static int coda_start_encoding(struct coda_ctx *ctx) CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET) | ((ctx->params.h264_slice_beta_offset_div2 & CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) << - CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET); + CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET) | + (ctx->params.h264_constrained_intra_pred_flag << + CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET); coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA); break; case V4L2_PIX_FMT_JPEG: diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 7518f01c48f7..f6c9273805bb 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1839,6 +1839,9 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: ctx->params.h264_disable_deblocking_filter_idc = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: + ctx->params.h264_constrained_intra_pred_flag = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_H264_PROFILE: /* TODO: switch between baseline and constrained baseline */ if (ctx->inst_type == CODA_INST_ENCODER) @@ -1925,6 +1928,9 @@ static void coda_encode_ctrls(struct coda_ctx *ctx) V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0x0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, + 0); v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 31cea72f5b2a..f3d0cff4ef3a 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -118,6 +118,7 @@ struct coda_params { u8 h264_disable_deblocking_filter_idc; s8 h264_slice_alpha_c0_offset_div2; s8 h264_slice_beta_offset_div2; + bool h264_constrained_intra_pred_flag; u8 h264_profile_idc; u8 h264_level_idc; u8 mpeg4_intra_qp; -- cgit v1.2.3-59-g8ed1b From 3943f059823b6e15884387f31618b84826e924b3 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jan 2019 11:56:12 -0500 Subject: media: coda: Add control for h.264 chroma qp index offset Allow to set a fixed quantization parameter offset between luma and chroma in the h.264 encoder. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 4 +++- drivers/media/platform/coda/coda-common.c | 5 +++++ drivers/media/platform/coda/coda.h | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 2998c869f79f..88065b07149c 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1012,7 +1012,9 @@ static int coda_start_encoding(struct coda_ctx *ctx) CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK) << CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET) | (ctx->params.h264_constrained_intra_pred_flag << - CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET); + CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET) | + (ctx->params.h264_chroma_qp_index_offset & + CODA_264PARAM_CHROMAQPOFFSET_MASK); coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA); break; case V4L2_PIX_FMT_JPEG: diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index f6c9273805bb..390d1ce6ab32 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1842,6 +1842,9 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: ctx->params.h264_constrained_intra_pred_flag = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: + ctx->params.h264_chroma_qp_index_offset = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_H264_PROFILE: /* TODO: switch between baseline and constrained baseline */ if (ctx->inst_type == CODA_INST_ENCODER) @@ -1931,6 +1934,8 @@ static void coda_encode_ctrls(struct coda_ctx *ctx) v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, -12, 12, 1, 0); v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index f3d0cff4ef3a..31c80bda2c0b 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -119,6 +119,7 @@ struct coda_params { s8 h264_slice_alpha_c0_offset_div2; s8 h264_slice_beta_offset_div2; bool h264_constrained_intra_pred_flag; + s8 h264_chroma_qp_index_offset; u8 h264_profile_idc; u8 h264_level_idc; u8 mpeg4_intra_qp; -- cgit v1.2.3-59-g8ed1b From c5ff0edb8e2270a75935c73217fb0de1abd2d910 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Mon, 14 Jan 2019 20:10:19 -0500 Subject: media: rcar-vin: Allow independent VIN link enablement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a block of code in rvin_group_link_notify() that prevents enabling a link to a VIN node if any entity in the media graph is in use. This prevents enabling a VIN link even if there is an in-use entity somewhere in the graph that is independent of the link's pipeline. For example, the code block will prevent enabling a link from the first rcar-csi2 receiver to a VIN node even if there is an enabled link somewhere far upstream on the second independent rcar-csi2 receiver pipeline. If this code block is meant to prevent modifying a link if any entity in the graph is actively involved in streaming (because modifying the CHSEL register fields can disrupt any/all running streams), then the entities stream counts should be checked rather than the use counts. (There is already such a check in __media_entity_setup_link() that verifies the stream_count of the link's source and sink entities are both zero, but that is insufficient, since there should be no running streams in the entire graph). Modify the code block to check the entity stream_count instead of the use_count (and elaborate on the comment). VIN node links can now be enabled even if there are other independent in-use entities that are not streaming. Fixes: c0cc5aef31 ("media: rcar-vin: add link notify for Gen3") Signed-off-by: Steve Longerbeam Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index b28e3ff191c2..594d80434004 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -131,9 +131,13 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags, !is_media_entity_v4l2_video_device(link->sink->entity)) return 0; - /* If any entity is in use don't allow link changes. */ + /* + * Don't allow link changes if any entity in the graph is + * streaming, modifying the CHSEL register fields can disrupt + * running streams. + */ media_device_for_each_entity(entity, &group->mdev) - if (entity->use_count) + if (entity->stream_count) return -EBUSY; mutex_lock(&group->lock); -- cgit v1.2.3-59-g8ed1b From 1c3721b1f22286033abeda30b7e12439b083ed0f Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:04 -0500 Subject: media: videodev2.h: Add more field helper macros Adds two helper macros: V4L2_FIELD_IS_SEQUENTIAL: returns true if the given field type is 'sequential', that is a full frame is transmitted, or exists in memory, as all top field lines followed by all bottom field lines, or vice-versa. V4L2_FIELD_IS_INTERLACED: returns true if the given field type is 'interlaced', that is a full frame is transmitted, or exists in memory, as top field lines interlaced with bottom field lines. Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index c0c36c165bf4..9a920f071ff9 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -130,6 +130,13 @@ enum v4l2_field { ((field) == V4L2_FIELD_BOTTOM ||\ (field) == V4L2_FIELD_TOP ||\ (field) == V4L2_FIELD_ALTERNATE) +#define V4L2_FIELD_IS_INTERLACED(field) \ + ((field) == V4L2_FIELD_INTERLACED ||\ + (field) == V4L2_FIELD_INTERLACED_TB ||\ + (field) == V4L2_FIELD_INTERLACED_BT) +#define V4L2_FIELD_IS_SEQUENTIAL(field) \ + ((field) == V4L2_FIELD_SEQ_TB ||\ + (field) == V4L2_FIELD_SEQ_BT) enum v4l2_buf_type { V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, -- cgit v1.2.3-59-g8ed1b From fc8c723852380b0f4349bb06cf553a6bba47462e Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:05 -0500 Subject: media: gpu: ipu-csi: Swap fields according to input/output field types The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/gpu/ipu-v3/ipu-csi.c | 126 ++++++++++++++++++++---------- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h | 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? + V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, - struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* + * Write the H-V-F codes the CSI will match against the + * incoming data for start/end of active and blanking + * field intervals. If input and output field types are + * sequential but not the same (one is SEQ_BT and the other + * is SEQ_TB), swap the F-bit so that the CSI will capture + * field 1 lines before field 0 lines. + */ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* + * Field0BlankEnd = 110, Field0BlankStart = 010 + * Field0ActiveEnd = 100, Field0ActiveStart = 000 + * Field1BlankEnd = 111, Field1BlankStart = 011 + * Field1ActiveEnd = 101, Field1ActiveStart = 001 + */ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt) { struct ipu_csi_bus_config cfg; unsigned long flags; u32 width, height, data = 0; + v4l2_std_id std; int ret; - ret = fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt); + ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); if (ret < 0) return ret; /* set default sensor frame width and height */ - width = mbus_fmt->width; - height = mbus_fmt->height; + width = infmt->width; + height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + height *= 2; /* Set the CSI_SENS_CONF register remaining fields */ data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | @@ -416,42 +479,22 @@ int ipu_csi_init_interface(struct ipu_csi *csi, ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); break; case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: - if (mbus_fmt->width == 720 && mbus_fmt->height == 576) { - /* - * PAL case - * - * Field0BlankEnd = 0x6, Field0BlankStart = 0x2, - * Field0ActiveEnd = 0x4, Field0ActiveStart = 0 - * Field1BlankEnd = 0x7, Field1BlankStart = 0x3, - * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1 - */ - height = 625; /* framelines for PAL */ - - ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, - CSI_CCIR_CODE_1); - ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); - ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); - } else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) { - /* - * NTSC case - * - * Field0BlankEnd = 0x7, Field0BlankStart = 0x3, - * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1 - * Field1BlankEnd = 0x6, Field1BlankStart = 0x2, - * Field1ActiveEnd = 0x4, Field1ActiveStart = 0 - */ - height = 525; /* framelines for NTSC */ - - ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, - CSI_CCIR_CODE_1); - ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); - ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); + if (width == 720 && height == 480) { + std = V4L2_STD_NTSC; + height = 525; + } else if (width == 720 && height == 576) { + std = V4L2_STD_PAL; + height = 625; } else { dev_err(csi->ipu->dev, - "Unsupported CCIR656 interlaced video mode\n"); - spin_unlock_irqrestore(&csi->lock, flags); - return -EINVAL; + "Unsupported interlaced video mode\n"); + ret = -EINVAL; + goto out_unlock; } + + ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); + if (ret) + goto out_unlock; break; case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: @@ -476,9 +519,10 @@ int ipu_csi_init_interface(struct ipu_csi *csi, dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); +out_unlock: spin_unlock_irqrestore(&csi->lock, flags); - return 0; + return ret; } EXPORT_SYMBOL_GPL(ipu_csi_init_interface); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 4223f8d418ae..c2a8d9cd31b7 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -679,12 +679,7 @@ static int csi_setup(struct csi_priv *priv) priv->upstream_ep.bus.parallel.flags : priv->upstream_ep.bus.mipi_csi2.flags; - /* - * we need to pass input frame to CSI interface, but - * with translated field type from output format - */ if_fmt = *infmt; - if_fmt.field = outfmt->field; crop = priv->crop; /* @@ -702,7 +697,7 @@ static int csi_setup(struct csi_priv *priv) priv->crop.width == 2 * priv->compose.width, priv->crop.height == 2 * priv->compose.height); - ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt); + ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt, outfmt); ipu_csi_set_dest(priv->csi, priv->dest); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index e582e8e7527a..bbc8481f567d 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -354,8 +354,9 @@ int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, */ struct ipu_csi; int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt); + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt); bool ipu_csi_is_interlaced(struct ipu_csi *csi); void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w); void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w); -- cgit v1.2.3-59-g8ed1b From 9b5c8d5ffb20cc4f4b1476cb1ad57e0f9b934788 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:06 -0500 Subject: media: gpu: ipu-v3: Add planar support to interlaced scan To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 ++++++++++++++++++++++++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 163fadb8a33a..d047a6867c59 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -277,9 +277,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -290,9 +291,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index c2a8d9cd31b7..da4808348845 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index bbc8481f567d..c887f4bee5f8 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -258,7 +258,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- cgit v1.2.3-59-g8ed1b From d969291d8479dcbb7f093738279527d14cdb756c Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:07 -0500 Subject: media: imx: Fix field negotiation IDMAC interlaced scan, a.k.a. interweave, should be enabled in the IDMAC output channels only if the IDMAC output pad field type is 'seq-bt' or 'seq-tb', and field type at the capture interface is 'interlaced*'. V4L2_FIELD_HAS_BOTH() macro should not be used on the input to determine enabling interlaced/interweave scan. That macro includes the 'interlaced' field types, and in those cases the data is already interweaved with top/bottom field lines. The CSI will capture whole frames when the source specifies alternate field mode. So the CSI also enables interweave for alternate input field type and the field type at capture interface is interlaced. Fix the logic for setting field type in try_fmt in CSI entity. The behavior should be: - No restrictions on field type at sink pad. - At the output pads, allow sequential fields in TB order, if the sink pad field type is sequential or alternate. Otherwise passthrough the field type from sink to source pad. Move this logic to new function csi_try_field(). These changes result in the following allowed field transformations from CSI sink -> source pads (all other field types at sink are passed through to source): seq-tb -> seq-tb seq-bt -> seq-tb alternate -> seq-tb In a future patch, the CSI sink -> source will allow: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt This will require supporting interweave with top/bottom line swapping. Until then seq-bt is not allowed at the CSI source pad because there is no way to swap top/bottom lines when interweaving to INTERLACED_BT -- note that despite the name, INTERLACED_BT is top-bottom order in memory. The BT in this case refers to field dominance: the bottom lines are older in time than the top lines. The capture interface device allows selecting IDMAC interweave by choosing INTERLACED_TB if the CSI/PRPENCVF source pad is seq-tb and INTERLACED_BT if the source pad is seq-bt (for future support of seq-bt). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 21 ++++++--- drivers/staging/media/imx/imx-media-capture.c | 14 ++++++ drivers/staging/media/imx/imx-media-csi.c | 64 ++++++++++++++++++++------- 3 files changed, 76 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index af7224846bd5..1a03d4c9d7b8 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -354,12 +354,13 @@ static int prp_setup_channel(struct prp_priv *priv, { struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *outcc; - struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; unsigned int burst_size; struct ipu_image image; + bool interweave; int ret; - infmt = &priv->format_mbus[PRPENCVF_SINK_PAD]; + outfmt = &priv->format_mbus[PRPENCVF_SRC_PAD]; outcc = vdev->cc; ipu_cpmem_zero(channel); @@ -369,6 +370,15 @@ static int prp_setup_channel(struct prp_priv *priv, image.rect.width = image.pix.width; image.rect.height = image.pix.height; + /* + * If the field type at capture interface is interlaced, and + * the output IDMAC pad is sequential, enable interweave at + * the IDMAC output channel. + */ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && + channel == priv->out_ch; + if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); swap(image.rect.width, image.rect.height); @@ -409,9 +419,7 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (image.pix.field == V4L2_FIELD_NONE && - V4L2_FIELD_HAS_BOTH(infmt->field) && - channel == priv->out_ch) + if (interweave) ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, image.pix.pixelformat); @@ -839,8 +847,7 @@ static void prp_try_fmt(struct prp_priv *priv, infmt = __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD, sdformat->which); if (sdformat->pad == PRPENCVF_SRC_PAD) { - if (sdformat->format.field != V4L2_FIELD_NONE) - sdformat->format.field = infmt->field; + sdformat->format.field = infmt->field; prp_bound_align_output(&sdformat->format, infmt, priv->rot_mode); diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index b37e1186eb2f..01ec9443de55 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -239,6 +239,20 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, cc = cc_src; } + /* allow IDMAC interweave but enforce field order from source */ + if (V4L2_FIELD_IS_INTERLACED(f->fmt.pix.field)) { + switch (fmt_src.format.field) { + case V4L2_FIELD_SEQ_TB: + fmt_src.format.field = V4L2_FIELD_INTERLACED_TB; + break; + case V4L2_FIELD_SEQ_BT: + fmt_src.format.field = V4L2_FIELD_INTERLACED_BT; + break; + default: + break; + } + } + imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src.format, cc); return 0; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index da4808348845..e3a4f39dbf73 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -398,16 +398,18 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *incc; struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; + bool passthrough, interweave; struct ipu_image image; u32 passthrough_bits; u32 passthrough_cycles; dma_addr_t phys[2]; - bool passthrough; u32 burst_size; int ret; infmt = &priv->format_mbus[CSI_SINK_PAD]; incc = priv->cc[CSI_SINK_PAD]; + outfmt = &priv->format_mbus[CSI_SRC_PAD_IDMAC]; ipu_cpmem_zero(priv->idmac_ch); @@ -424,6 +426,14 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough = requires_passthrough(&priv->upstream_ep, infmt, incc); passthrough_cycles = 1; + /* + * If the field type at capture interface is interlaced, and + * the output IDMAC pad is sequential, enable interweave at + * the IDMAC output channel. + */ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + switch (image.pix.pixelformat) { case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SGBRG8: @@ -509,8 +519,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ipu_smfc_set_burstsize(priv->smfc, burst_size); - if (image.pix.field == V4L2_FIELD_NONE && - V4L2_FIELD_HAS_BOTH(infmt->field)) + if (interweave) ipu_cpmem_interlaced_scan(priv->idmac_ch, image.pix.bytesperline, image.pix.pixelformat); @@ -1304,6 +1313,38 @@ out: return ret; } +static void csi_try_field(struct csi_priv *priv, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct v4l2_mbus_framefmt *infmt = + __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sdformat->which); + + /* no restrictions on sink pad field type */ + if (sdformat->pad == CSI_SINK_PAD) + return; + + switch (infmt->field) { + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + case V4L2_FIELD_ALTERNATE: + /* + * If the sink is sequential or alternating fields, + * allow only SEQ_TB at the source. + * + * This driver does not support alternate field mode, and + * the CSI captures a whole frame, so the CSI never presents + * alternate mode at its source pads. + */ + sdformat->format.field = V4L2_FIELD_SEQ_TB; + break; + default: + /* Passthrough for all other input field types */ + sdformat->format.field = infmt->field; + break; + } +} + static void csi_try_fmt(struct csi_priv *priv, struct v4l2_fwnode_endpoint *upstream_ep, struct v4l2_subdev_pad_config *cfg, @@ -1343,25 +1384,14 @@ static void csi_try_fmt(struct csi_priv *priv, } } - if (sdformat->pad == CSI_SRC_PAD_DIRECT || - sdformat->format.field != V4L2_FIELD_NONE) - sdformat->format.field = infmt->field; - - /* - * translate V4L2_FIELD_ALTERNATE to SEQ_TB or SEQ_BT - * depending on input height (assume NTSC top-bottom - * order if 480 lines, otherwise PAL bottom-top order). - */ - if (sdformat->format.field == V4L2_FIELD_ALTERNATE) { - sdformat->format.field = (infmt->height == 480) ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT; - } + csi_try_field(priv, cfg, sdformat); /* propagate colorimetry from sink */ sdformat->format.colorspace = infmt->colorspace; sdformat->format.xfer_func = infmt->xfer_func; sdformat->format.quantization = infmt->quantization; sdformat->format.ycbcr_enc = infmt->ycbcr_enc; + break; case CSI_SINK_PAD: v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W, @@ -1389,6 +1419,8 @@ static void csi_try_fmt(struct csi_priv *priv, sdformat->format.code = (*cc)->codes[0]; } + csi_try_field(priv, cfg, sdformat); + imx_media_fill_default_mbus_fields( &sdformat->format, infmt, priv->active_output_pad == CSI_SRC_PAD_DIRECT); -- cgit v1.2.3-59-g8ed1b From 2a87c0c9344489afe6c7a613d883a041a5441bcd Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:08 -0500 Subject: media: imx-csi: Double crop height for alternate fields at sink If the incoming sink field type is alternate, the reset crop height and crop height bounds must be set to twice the incoming height, because in alternate field mode, upstream will report only the lines for a single field, and the CSI captures the whole frame. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e3a4f39dbf73..10945cbdbd71 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1142,6 +1142,8 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_mbus_framefmt *infmt, struct v4l2_fwnode_endpoint *upstream_ep) { + u32 in_height; + crop->width = min_t(__u32, infmt->width, crop->width); if (crop->left + crop->width > infmt->width) crop->left = infmt->width - crop->width; @@ -1149,6 +1151,10 @@ static void csi_try_crop(struct csi_priv *priv, crop->left &= ~0x3; crop->width &= ~0x7; + in_height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + in_height *= 2; + /* * FIXME: not sure why yet, but on interlaced bt.656, * changing the vertical cropping causes loss of vertical @@ -1158,12 +1164,12 @@ static void csi_try_crop(struct csi_priv *priv, if (upstream_ep->bus_type == V4L2_MBUS_BT656 && (V4L2_FIELD_HAS_BOTH(infmt->field) || infmt->field == V4L2_FIELD_ALTERNATE)) { - crop->height = infmt->height; - crop->top = (infmt->height == 480) ? 2 : 0; + crop->height = in_height; + crop->top = (in_height == 480) ? 2 : 0; } else { - crop->height = min_t(__u32, infmt->height, crop->height); - if (crop->top + crop->height > infmt->height) - crop->top = infmt->height - crop->height; + crop->height = min_t(__u32, in_height, crop->height); + if (crop->top + crop->height > in_height) + crop->top = in_height - crop->height; } } @@ -1403,6 +1409,8 @@ static void csi_try_fmt(struct csi_priv *priv, crop->top = 0; crop->width = sdformat->format.width; crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); compose->left = 0; compose->top = 0; @@ -1530,6 +1538,8 @@ static int csi_get_selection(struct v4l2_subdev *sd, sel->r.top = 0; sel->r.width = infmt->width; sel->r.height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + sel->r.height *= 2; break; case V4L2_SEL_TGT_CROP: sel->r = *crop; -- cgit v1.2.3-59-g8ed1b From bc11dd6699960c241dc067d55aa4be37b722afd5 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:09 -0500 Subject: media: imx: interweave and odd-chroma-row skip are incompatible If IDMAC interweaving is enabled in a write channel, the channel must write the odd chroma rows for 4:2:0 formats. Skipping writing the odd chroma rows produces corrupted captured 4:2:0 images when interweave is enabled. Reported-by: Krzysztof Ha?asa Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 9 +++++++-- drivers/staging/media/imx/imx-media-csi.c | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 1a03d4c9d7b8..cf76b0432371 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -391,12 +391,17 @@ static int prp_setup_channel(struct prp_priv *priv, image.phys0 = addr0; image.phys1 = addr1; - if (channel == priv->out_ch || channel == priv->rot_out_ch) { + /* + * Skip writing U and V components to odd rows in the output + * channels for planar 4:2:0 (but not when enabling IDMAC + * interweaving, they are incompatible). + */ + if (!interweave && (channel == priv->out_ch || + channel == priv->rot_out_ch)) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: - /* Skip writing U and V components to odd rows */ ipu_cpmem_skip_odd_chroma_rows(channel); break; } diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 10945cbdbd71..604d0bd24389 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -457,8 +457,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; passthrough_bits = 16; - /* Skip writing U and V components to odd rows */ - ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); + /* + * Skip writing U and V components to odd rows (but not + * when enabling IDMAC interweaving, they are incompatible). + */ + if (!interweave) + ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: -- cgit v1.2.3-59-g8ed1b From 792f061b9f752f28fdaa1986b43f109689dc1416 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:10 -0500 Subject: media: imx-csi: Allow skipping odd chroma rows for YVU420 Skip writing U/V components to odd rows for YVU420 in addition to YUV420 and NV12. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 604d0bd24389..6f1e11b8a7cb 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -452,6 +452,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough_bits = 16; break; case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? -- cgit v1.2.3-59-g8ed1b From 6e537b58de772cfc89d4fbd0cddd0c1de5f93822 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:11 -0500 Subject: media: imx: vdic: rely on VDIC for correct field order prepare_vdi_in_buffers() was setting up the dma pointers as if the VDIC is always programmed to receive the fields in bottom-top order, i.e. as if ipu_vdi_set_field_order() only programs BT order in the VDIC. But that's not true, ipu_vdi_set_field_order() is working correctly. So fix prepare_vdi_in_buffers() to give the VDIC the fields in whatever order they were received by the video source, and rely on the VDIC to sort out which is top and which is bottom. Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-vdic.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 482250d47e7c..4a890714193e 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -219,26 +219,18 @@ static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv, switch (priv->fieldtype) { case V4L2_FIELD_SEQ_TB: - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; case V4L2_FIELD_SEQ_BT: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; break; + case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; break; - default: - /* assume V4L2_FIELD_INTERLACED_TB */ - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; } ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys); -- cgit v1.2.3-59-g8ed1b From ab2f05cdb15421c1e57d3345852f6fb1405e3398 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:12 -0500 Subject: media: imx-csi: Move crop/compose reset after filling default mbus fields If caller passes un-initialized field type V4L2_FIELD_ANY to CSI sink pad, the reset CSI crop window would not be correct, because the crop window depends on a valid input field type. To fix move the reset of crop and compose windows to after the call to imx_media_fill_default_mbus_fields(). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 6f1e11b8a7cb..8537ecb7dd17 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1409,19 +1409,6 @@ static void csi_try_fmt(struct csi_priv *priv, W_ALIGN, &sdformat->format.height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); - /* Reset crop and compose rectangles */ - crop->left = 0; - crop->top = 0; - crop->width = sdformat->format.width; - crop->height = sdformat->format.height; - if (sdformat->format.field == V4L2_FIELD_ALTERNATE) - crop->height *= 2; - csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); - compose->left = 0; - compose->top = 0; - compose->width = crop->width; - compose->height = crop->height; - *cc = imx_media_find_mbus_format(sdformat->format.code, CS_SEL_ANY, true); if (!*cc) { @@ -1437,6 +1424,20 @@ static void csi_try_fmt(struct csi_priv *priv, imx_media_fill_default_mbus_fields( &sdformat->format, infmt, priv->active_output_pad == CSI_SRC_PAD_DIRECT); + + /* Reset crop and compose rectangles */ + crop->left = 0; + crop->top = 0; + crop->width = sdformat->format.width; + crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; + csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); + compose->left = 0; + compose->top = 0; + compose->width = crop->width; + compose->height = crop->height; + break; } } -- cgit v1.2.3-59-g8ed1b From 3c6640a6065fe77691e88ae135f45be6826acf0a Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:13 -0500 Subject: media: imx: Allow interweave with top/bottom lines swapped Allow sequential->interlaced interweaving but with top/bottom lines swapped to the output buffer. This can be accomplished by adding one line length to IDMAC output channel address, with a negative line length for the interlace offset. This is to allow the seq-bt -> interlaced-bt transformation, where bottom lines are still dominant (older in time) but with top lines first in the interweaved output buffer. With this support, the CSI can now allow seq-bt at its source pads, e.g. the following transformations are allowed in CSI from sink to source: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt Suggested-by: Philipp Zabel Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 25 +++++++++++++----- drivers/staging/media/imx/imx-media-csi.c | 40 +++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index cf76b0432371..33ada6612fee 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -106,6 +106,7 @@ struct prp_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof; /* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -235,6 +236,9 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch) if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(ch, priv->ipu_buf_num); + if (priv->interweave_swap && ch == priv->out_ch) + phys += vdev->fmt.fmt.pix.bytesperline; + ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys); } @@ -376,8 +380,9 @@ static int prp_setup_channel(struct prp_priv *priv, * the IDMAC output channel. */ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && - V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && - channel == priv->out_ch; + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + priv->interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); @@ -388,6 +393,11 @@ static int prp_setup_channel(struct prp_priv *priv, (image.pix.width * outcc->bpp) >> 3; } + if (priv->interweave_swap && channel == priv->out_ch) { + /* start interweave scan at 1st top line (2nd line) */ + image.rect.top = 1; + } + image.phys0 = addr0; image.phys1 = addr1; @@ -396,8 +406,8 @@ static int prp_setup_channel(struct prp_priv *priv, * channels for planar 4:2:0 (but not when enabling IDMAC * interweaving, they are incompatible). */ - if (!interweave && (channel == priv->out_ch || - channel == priv->rot_out_ch)) { + if ((channel == priv->out_ch && !interweave) || + channel == priv->rot_out_ch) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: @@ -424,8 +434,11 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (interweave) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + if (interweave && channel == priv->out_ch) + ipu_cpmem_interlaced_scan(channel, + priv->interweave_swap ? + -image.pix.bytesperline : + image.pix.bytesperline, image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 8537ecb7dd17..555aa45e02e3 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -114,6 +114,7 @@ struct csi_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof; /* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -286,6 +287,9 @@ static void csi_vb2_buf_done(struct csi_priv *priv) if (ipu_idmac_buffer_is_ready(priv->idmac_ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(priv->idmac_ch, priv->ipu_buf_num); + if (priv->interweave_swap) + phys += vdev->fmt.fmt.pix.bytesperline; + ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, phys); } @@ -433,6 +437,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) */ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + priv->interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; switch (image.pix.pixelformat) { case V4L2_PIX_FMT_SBGGR8: @@ -486,6 +492,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) } if (passthrough) { + if (priv->interweave_swap) { + /* start interweave scan at 1st top line (2nd line) */ + image.phys0 += image.pix.bytesperline; + image.phys1 += image.pix.bytesperline; + } + ipu_cpmem_set_resolution(priv->idmac_ch, image.rect.width * passthrough_cycles, image.rect.height); @@ -495,6 +507,11 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ipu_cpmem_set_format_passthrough(priv->idmac_ch, passthrough_bits); } else { + if (priv->interweave_swap) { + /* start interweave scan at 1st top line (2nd line) */ + image.rect.top = 1; + } + ret = ipu_cpmem_set_image(priv->idmac_ch, &image); if (ret) goto unsetup_vb2; @@ -526,6 +543,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (interweave) ipu_cpmem_interlaced_scan(priv->idmac_ch, + priv->interweave_swap ? + -image.pix.bytesperline : image.pix.bytesperline, image.pix.pixelformat); @@ -1338,16 +1357,27 @@ static void csi_try_field(struct csi_priv *priv, switch (infmt->field) { case V4L2_FIELD_SEQ_TB: case V4L2_FIELD_SEQ_BT: + /* + * If the user requests sequential at the source pad, + * allow it (along with possibly inverting field order). + * Otherwise passthrough the field type. + */ + if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field)) + sdformat->format.field = infmt->field; + break; case V4L2_FIELD_ALTERNATE: /* - * If the sink is sequential or alternating fields, - * allow only SEQ_TB at the source. - * * This driver does not support alternate field mode, and * the CSI captures a whole frame, so the CSI never presents - * alternate mode at its source pads. + * alternate mode at its source pads. If user has not + * already requested sequential, translate ALTERNATE at + * sink pad to SEQ_TB or SEQ_BT at the source pad depending + * on input height (assume NTSC BT order if 480 total active + * frame lines, otherwise PAL TB order). */ - sdformat->format.field = V4L2_FIELD_SEQ_TB; + if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field)) + sdformat->format.field = (infmt->height == 480 / 2) ? + V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB; break; default: /* Passthrough for all other input field types */ -- cgit v1.2.3-59-g8ed1b From eed2235876ef5b0a04cd4716c5b2bb7bf68a56ca Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 9 Jan 2019 13:30:14 -0500 Subject: media: imx.rst: Update doc to reflect fixes to interlaced capture Also add an example pipeline for unconverted capture with interweave on SabreAuto. Cleanup some language in various places in the process. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/v4l-drivers/imx.rst | 103 ++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 37 deletions(-) diff --git a/Documentation/media/v4l-drivers/imx.rst b/Documentation/media/v4l-drivers/imx.rst index 6922dde4a82b..9314af00d067 100644 --- a/Documentation/media/v4l-drivers/imx.rst +++ b/Documentation/media/v4l-drivers/imx.rst @@ -24,8 +24,8 @@ memory. Various dedicated DMA channels exist for both video capture and display paths. During transfer, the IDMAC is also capable of vertical image flip, 8x8 block transfer (see IRT description), pixel component re-ordering (for example UYVY to YUYV) within the same colorspace, and -even packed <--> planar conversion. It can also perform a simple -de-interlacing by interleaving even and odd lines during transfer +packed <--> planar conversion. The IDMAC can also perform a simple +de-interlacing by interweaving even and odd lines during transfer (without motion compensation which requires the VDIC). The CSI is the backend capture unit that interfaces directly with @@ -175,15 +175,21 @@ via the SMFC and an IDMAC channel, bypassing IC pre-processing. This source pad is routed to a capture device node, with a node name of the format "ipuX_csiY capture". -Note that since the IDMAC source pad makes use of an IDMAC channel, it -can do pixel reordering within the same colorspace. For example, the -sink pad can take UYVY2X8, but the IDMAC source pad can output YUYV2X8. -If the sink pad is receiving YUV, the output at the capture device can -also be converted to a planar YUV format such as YUV420. - -It will also perform simple de-interlace without motion compensation, -which is activated if the sink pad's field type is an interlaced type, -and the IDMAC source pad field type is set to none. +Note that since the IDMAC source pad makes use of an IDMAC channel, +pixel reordering within the same colorspace can be carried out by the +IDMAC channel. For example, if the CSI sink pad is receiving in UYVY +order, the capture device linked to the IDMAC source pad can capture +in YUYV order. Also, if the CSI sink pad is receiving a packed YUV +format, the capture device can capture a planar YUV format such as +YUV420. + +The IDMAC channel at the IDMAC source pad also supports simple +interweave without motion compensation, which is activated if the source +pad's field type is sequential top-bottom or bottom-top, and the +requested capture interface field type is set to interlaced (t-b, b-t, +or unqualified interlaced). The capture interface will enforce the same +field order as the source pad field order (interlaced-bt if source pad +is seq-bt, interlaced-tb if source pad is seq-tb). This subdev can generate the following event when enabling the second IDMAC source pad: @@ -325,14 +331,14 @@ ipuX_vdic The VDIC carries out motion compensated de-interlacing, with three motion compensation modes: low, medium, and high motion. The mode is -specified with the menu control V4L2_CID_DEINTERLACING_MODE. It has -two sink pads and a single source pad. +specified with the menu control V4L2_CID_DEINTERLACING_MODE. The VDIC +has two sink pads and a single source pad. The direct sink pad receives from an ipuX_csiY direct pad. With this link the VDIC can only operate in high motion mode. When the IDMAC sink pad is activated, it receives from an output -or mem2mem device node. With this pipeline, it can also operate +or mem2mem device node. With this pipeline, the VDIC can also operate in low and medium modes, because these modes require receiving frames from memory buffers. Note that an output or mem2mem device is not implemented yet, so this sink pad currently has no links. @@ -345,8 +351,8 @@ ipuX_ic_prp This is the IC pre-processing entity. It acts as a router, routing data from its sink pad to one or both of its source pads. -It has a single sink pad. The sink pad can receive from the ipuX_csiY -direct pad, or from ipuX_vdic. +This entity has a single sink pad. The sink pad can receive from the +ipuX_csiY direct pad, or from ipuX_vdic. This entity has two source pads. One source pad routes to the pre-process encode task entity (ipuX_ic_prpenc), the other to the @@ -369,8 +375,8 @@ color-space conversion, resizing (downscaling and upscaling), horizontal and vertical flip, and 90/270 degree rotation. Flip and rotation are provided via standard V4L2 controls. -Like the ipuX_csiY IDMAC source, it can also perform simple de-interlace -without motion compensation, and pixel reordering. +Like the ipuX_csiY IDMAC source, this entity also supports simple +de-interlace without motion compensation, and pixel reordering. ipuX_ic_prpvf ------------- @@ -380,18 +386,18 @@ pad from ipuX_ic_prp, and a single source pad. The source pad is routed to a capture device node, with a node name of the format "ipuX_ic_prpvf capture". -It is identical in operation to ipuX_ic_prpenc, with the same resizing -and CSC operations and flip/rotation controls. It will receive and -process de-interlaced frames from the ipuX_vdic if ipuX_ic_prp is +This entity is identical in operation to ipuX_ic_prpenc, with the same +resizing and CSC operations and flip/rotation controls. It will receive +and process de-interlaced frames from the ipuX_vdic if ipuX_ic_prp is receiving from ipuX_vdic. -Like the ipuX_csiY IDMAC source, it can perform simple de-interlace -without motion compensation. However, note that if the ipuX_vdic is -included in the pipeline (ipuX_ic_prp is receiving from ipuX_vdic), -it's not possible to use simple de-interlace in ipuX_ic_prpvf, since -the ipuX_vdic has already carried out de-interlacing (with motion -compensation) and therefore the field type output from ipuX_ic_prp can -only be none. +Like the ipuX_csiY IDMAC source, this entity supports simple +interweaving without motion compensation. However, note that if the +ipuX_vdic is included in the pipeline (ipuX_ic_prp is receiving from +ipuX_vdic), it's not possible to use interweave in ipuX_ic_prpvf, +since the ipuX_vdic has already carried out de-interlacing (with +motion compensation) and therefore the field type output from +ipuX_vdic can only be none (progressive). Capture Pipelines ----------------- @@ -516,10 +522,33 @@ On the SabreAuto, an on-board ADV7180 SD decoder is connected to the parallel bus input on the internal video mux to IPU1 CSI0. The following example configures a pipeline to capture from the ADV7180 -video decoder, assuming NTSC 720x480 input signals, with Motion -Compensated de-interlacing. Pad field types assume the adv7180 outputs -"interlaced". $outputfmt can be any format supported by the ipu1_ic_prpvf -entity at its output pad: +video decoder, assuming NTSC 720x480 input signals, using simple +interweave (unconverted and without motion compensation). The adv7180 +must output sequential or alternating fields (field type 'seq-bt' for +NTSC, or 'alternate'): + +.. code-block:: none + + # Setup links + media-ctl -l "'adv7180 3-0021':0 -> 'ipu1_csi0_mux':1[1]" + media-ctl -l "'ipu1_csi0_mux':2 -> 'ipu1_csi0':0[1]" + media-ctl -l "'ipu1_csi0':2 -> 'ipu1_csi0 capture':0[1]" + # Configure pads + media-ctl -V "'adv7180 3-0021':0 [fmt:UYVY2X8/720x480 field:seq-bt]" + media-ctl -V "'ipu1_csi0_mux':2 [fmt:UYVY2X8/720x480]" + media-ctl -V "'ipu1_csi0':2 [fmt:AYUV32/720x480]" + # Configure "ipu1_csi0 capture" interface (assumed at /dev/video4) + v4l2-ctl -d4 --set-fmt-video=field=interlaced_bt + +Streaming can then begin on /dev/video4. The v4l2-ctl tool can also be +used to select any supported YUV pixelformat on /dev/video4. + +This example configures a pipeline to capture from the ADV7180 +video decoder, assuming PAL 720x576 input signals, with Motion +Compensated de-interlacing. The adv7180 must output sequential or +alternating fields (field type 'seq-tb' for PAL, or 'alternate'). +$outputfmt can be any format supported by the ipu1_ic_prpvf entity +at its output pad: .. code-block:: none @@ -531,11 +560,11 @@ entity at its output pad: media-ctl -l "'ipu1_ic_prp':2 -> 'ipu1_ic_prpvf':0[1]" media-ctl -l "'ipu1_ic_prpvf':1 -> 'ipu1_ic_prpvf capture':0[1]" # Configure pads - media-ctl -V "'adv7180 3-0021':0 [fmt:UYVY2X8/720x480]" - media-ctl -V "'ipu1_csi0_mux':2 [fmt:UYVY2X8/720x480 field:interlaced]" - media-ctl -V "'ipu1_csi0':1 [fmt:AYUV32/720x480 field:interlaced]" - media-ctl -V "'ipu1_vdic':2 [fmt:AYUV32/720x480 field:none]" - media-ctl -V "'ipu1_ic_prp':2 [fmt:AYUV32/720x480 field:none]" + media-ctl -V "'adv7180 3-0021':0 [fmt:UYVY2X8/720x576 field:seq-tb]" + media-ctl -V "'ipu1_csi0_mux':2 [fmt:UYVY2X8/720x576]" + media-ctl -V "'ipu1_csi0':1 [fmt:AYUV32/720x576]" + media-ctl -V "'ipu1_vdic':2 [fmt:AYUV32/720x576 field:none]" + media-ctl -V "'ipu1_ic_prp':2 [fmt:AYUV32/720x576 field:none]" media-ctl -V "'ipu1_ic_prpvf':1 [fmt:$outputfmt field:none]" Streaming can then begin on the capture device node at -- cgit v1.2.3-59-g8ed1b From e8f9b16d72631870e30a3d8e4ee9f1c097bc7ba0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 17 Jan 2019 07:53:55 -0500 Subject: media: remove soc_camera ov9640 This driver got converted to not depend on soc_camera on commit 57b0ad9ebe60 ("media: soc_camera: ov9640: move ov9640 out of soc_camera"). There's no sense on keeping the old version there. Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 8 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/ov9640.h | 208 --------- drivers/media/i2c/soc_camera/soc_ov9640.c | 738 ------------------------------ 4 files changed, 955 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/ov9640.h delete mode 100644 drivers/media/i2c/soc_camera/soc_ov9640.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 7d7b801ab2ce..f67499187bda 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -41,14 +41,6 @@ config SOC_CAMERA_OV772X help This is a ov772x camera driver -config SOC_CAMERA_OV9640 - tristate "ov9640 camera support (OBSOLETE)" - default n - depends on SOC_CAMERA && I2C - help - This is an obsoleted version of ov9640 camera driver. Please use - the v4l2 standalone one (VIDEO_OV9640). - config SOC_CAMERA_OV9740 tristate "ov9740 camera support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index 09ae483b96ef..0d15864cfda8 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -4,7 +4,6 @@ obj-$(CONFIG_SOC_CAMERA_MT9T112) += soc_mt9t112.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV772X) += soc_ov772x.o -obj-$(CONFIG_SOC_CAMERA_OV9640) += soc_ov9640.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += soc_rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += soc_tw9910.o diff --git a/drivers/media/i2c/soc_camera/ov9640.h b/drivers/media/i2c/soc_camera/ov9640.h deleted file mode 100644 index 65d13ff17536..000000000000 --- a/drivers/media/i2c/soc_camera/ov9640.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * OmniVision OV96xx Camera Header File - * - * Copyright (C) 2009 Marek Vasut - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__ -#define __DRIVERS_MEDIA_VIDEO_OV9640_H__ - -/* Register definitions */ -#define OV9640_GAIN 0x00 -#define OV9640_BLUE 0x01 -#define OV9640_RED 0x02 -#define OV9640_VFER 0x03 -#define OV9640_COM1 0x04 -#define OV9640_BAVE 0x05 -#define OV9640_GEAVE 0x06 -#define OV9640_RSID 0x07 -#define OV9640_RAVE 0x08 -#define OV9640_COM2 0x09 -#define OV9640_PID 0x0a -#define OV9640_VER 0x0b -#define OV9640_COM3 0x0c -#define OV9640_COM4 0x0d -#define OV9640_COM5 0x0e -#define OV9640_COM6 0x0f -#define OV9640_AECH 0x10 -#define OV9640_CLKRC 0x11 -#define OV9640_COM7 0x12 -#define OV9640_COM8 0x13 -#define OV9640_COM9 0x14 -#define OV9640_COM10 0x15 -/* 0x16 - RESERVED */ -#define OV9640_HSTART 0x17 -#define OV9640_HSTOP 0x18 -#define OV9640_VSTART 0x19 -#define OV9640_VSTOP 0x1a -#define OV9640_PSHFT 0x1b -#define OV9640_MIDH 0x1c -#define OV9640_MIDL 0x1d -#define OV9640_MVFP 0x1e -#define OV9640_LAEC 0x1f -#define OV9640_BOS 0x20 -#define OV9640_GBOS 0x21 -#define OV9640_GROS 0x22 -#define OV9640_ROS 0x23 -#define OV9640_AEW 0x24 -#define OV9640_AEB 0x25 -#define OV9640_VPT 0x26 -#define OV9640_BBIAS 0x27 -#define OV9640_GBBIAS 0x28 -/* 0x29 - RESERVED */ -#define OV9640_EXHCH 0x2a -#define OV9640_EXHCL 0x2b -#define OV9640_RBIAS 0x2c -#define OV9640_ADVFL 0x2d -#define OV9640_ADVFH 0x2e -#define OV9640_YAVE 0x2f -#define OV9640_HSYST 0x30 -#define OV9640_HSYEN 0x31 -#define OV9640_HREF 0x32 -#define OV9640_CHLF 0x33 -#define OV9640_ARBLM 0x34 -/* 0x35..0x36 - RESERVED */ -#define OV9640_ADC 0x37 -#define OV9640_ACOM 0x38 -#define OV9640_OFON 0x39 -#define OV9640_TSLB 0x3a -#define OV9640_COM11 0x3b -#define OV9640_COM12 0x3c -#define OV9640_COM13 0x3d -#define OV9640_COM14 0x3e -#define OV9640_EDGE 0x3f -#define OV9640_COM15 0x40 -#define OV9640_COM16 0x41 -#define OV9640_COM17 0x42 -/* 0x43..0x4e - RESERVED */ -#define OV9640_MTX1 0x4f -#define OV9640_MTX2 0x50 -#define OV9640_MTX3 0x51 -#define OV9640_MTX4 0x52 -#define OV9640_MTX5 0x53 -#define OV9640_MTX6 0x54 -#define OV9640_MTX7 0x55 -#define OV9640_MTX8 0x56 -#define OV9640_MTX9 0x57 -#define OV9640_MTXS 0x58 -/* 0x59..0x61 - RESERVED */ -#define OV9640_LCC1 0x62 -#define OV9640_LCC2 0x63 -#define OV9640_LCC3 0x64 -#define OV9640_LCC4 0x65 -#define OV9640_LCC5 0x66 -#define OV9640_MANU 0x67 -#define OV9640_MANV 0x68 -#define OV9640_HV 0x69 -#define OV9640_MBD 0x6a -#define OV9640_DBLV 0x6b -#define OV9640_GSP 0x6c /* ... till 0x7b */ -#define OV9640_GST 0x7c /* ... till 0x8a */ - -#define OV9640_CLKRC_DPLL_EN 0x80 -#define OV9640_CLKRC_DIRECT 0x40 -#define OV9640_CLKRC_DIV(x) ((x) & 0x3f) - -#define OV9640_PSHFT_VAL(x) ((x) & 0xff) - -#define OV9640_ACOM_2X_ANALOG 0x80 -#define OV9640_ACOM_RSVD 0x12 - -#define OV9640_MVFP_V 0x10 -#define OV9640_MVFP_H 0x20 - -#define OV9640_COM1_HREF_NOSKIP 0x00 -#define OV9640_COM1_HREF_2SKIP 0x04 -#define OV9640_COM1_HREF_3SKIP 0x08 -#define OV9640_COM1_QQFMT 0x20 - -#define OV9640_COM2_SSM 0x10 - -#define OV9640_COM3_VP 0x04 - -#define OV9640_COM4_QQ_VP 0x80 -#define OV9640_COM4_RSVD 0x40 - -#define OV9640_COM5_SYSCLK 0x80 -#define OV9640_COM5_LONGEXP 0x01 - -#define OV9640_COM6_OPT_BLC 0x40 -#define OV9640_COM6_ADBLC_BIAS 0x08 -#define OV9640_COM6_FMT_RST 0x82 -#define OV9640_COM6_ADBLC_OPTEN 0x01 - -#define OV9640_COM7_RAW_RGB 0x01 -#define OV9640_COM7_RGB 0x04 -#define OV9640_COM7_QCIF 0x08 -#define OV9640_COM7_QVGA 0x10 -#define OV9640_COM7_CIF 0x20 -#define OV9640_COM7_VGA 0x40 -#define OV9640_COM7_SCCB_RESET 0x80 - -#define OV9640_TSLB_YVYU_YUYV 0x04 -#define OV9640_TSLB_YUYV_UYVY 0x08 - -#define OV9640_COM12_YUV_AVG 0x04 -#define OV9640_COM12_RSVD 0x40 - -#define OV9640_COM13_GAMMA_NONE 0x00 -#define OV9640_COM13_GAMMA_Y 0x40 -#define OV9640_COM13_GAMMA_RAW 0x80 -#define OV9640_COM13_RGB_AVG 0x20 -#define OV9640_COM13_MATRIX_EN 0x10 -#define OV9640_COM13_Y_DELAY_EN 0x08 -#define OV9640_COM13_YUV_DLY(x) ((x) & 0x07) - -#define OV9640_COM15_OR_00FF 0x00 -#define OV9640_COM15_OR_01FE 0x40 -#define OV9640_COM15_OR_10F0 0xc0 -#define OV9640_COM15_RGB_NORM 0x00 -#define OV9640_COM15_RGB_565 0x10 -#define OV9640_COM15_RGB_555 0x30 - -#define OV9640_COM16_RB_AVG 0x01 - -/* IDs */ -#define OV9640_V2 0x9648 -#define OV9640_V3 0x9649 -#define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF)) - -/* supported resolutions */ -enum { - W_QQCIF = 88, - W_QQVGA = 160, - W_QCIF = 176, - W_QVGA = 320, - W_CIF = 352, - W_VGA = 640, - W_SXGA = 1280 -}; -#define H_SXGA 960 - -/* Misc. structures */ -struct ov9640_reg_alt { - u8 com7; - u8 com12; - u8 com13; - u8 com15; -}; - -struct ov9640_reg { - u8 reg; - u8 val; -}; - -struct ov9640_priv { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; - - int model; - int revision; -}; - -#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */ diff --git a/drivers/media/i2c/soc_camera/soc_ov9640.c b/drivers/media/i2c/soc_camera/soc_ov9640.c deleted file mode 100644 index eb91b8240083..000000000000 --- a/drivers/media/i2c/soc_camera/soc_ov9640.c +++ /dev/null @@ -1,738 +0,0 @@ -/* - * OmniVision OV96xx Camera Driver - * - * Copyright (C) 2009 Marek Vasut - * - * Based on ov772x camera driver: - * - * Copyright (C) 2008 Renesas Solutions Corp. - * Kuninori Morimoto - * - * Based on ov7670 and soc_camera_platform driver, - * - * Copyright 2006-7 Jonathan Corbet - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "ov9640.h" - -#define to_ov9640_sensor(sd) container_of(sd, struct ov9640_priv, subdev) - -/* default register setup */ -static const struct ov9640_reg ov9640_regs_dflt[] = { - { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP }, - { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS | - OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN }, - { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) }, - { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD }, - { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY }, - { OV9640_COM16, OV9640_COM16_RB_AVG }, - - /* Gamma curve P */ - { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 }, - { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 }, - { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, - { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 }, - - /* Gamma curve T */ - { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 }, - { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, - { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e }, - { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 }, -}; - -/* Configurations - * NOTE: for YUV, alter the following registers: - * COM12 |= OV9640_COM12_YUV_AVG - * - * for RGB, alter the following registers: - * COM7 |= OV9640_COM7_RGB - * COM13 |= OV9640_COM13_RGB_AVG - * COM15 |= proper RGB color encoding mode - */ -static const struct ov9640_reg ov9640_regs_qqcif[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, - { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, - { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, - { OV9640_COM7, OV9640_COM7_QCIF }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_qqvga[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, - { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, - { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, - { OV9640_COM7, OV9640_COM7_QVGA }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_qcif[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, - { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, - { OV9640_COM7, OV9640_COM7_QCIF }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_qvga[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, - { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, - { OV9640_COM7, OV9640_COM7_QVGA }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_cif[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, - { OV9640_COM3, OV9640_COM3_VP }, - { OV9640_COM7, OV9640_COM7_CIF }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_vga[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, - { OV9640_COM3, OV9640_COM3_VP }, - { OV9640_COM7, OV9640_COM7_VGA }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_sxga[] = { - { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, - { OV9640_COM3, OV9640_COM3_VP }, - { OV9640_COM7, 0 }, - { OV9640_COM12, OV9640_COM12_RSVD }, - { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, - { OV9640_COM15, OV9640_COM15_OR_10F0 }, -}; - -static const struct ov9640_reg ov9640_regs_yuv[] = { - { OV9640_MTX1, 0x58 }, - { OV9640_MTX2, 0x48 }, - { OV9640_MTX3, 0x10 }, - { OV9640_MTX4, 0x28 }, - { OV9640_MTX5, 0x48 }, - { OV9640_MTX6, 0x70 }, - { OV9640_MTX7, 0x40 }, - { OV9640_MTX8, 0x40 }, - { OV9640_MTX9, 0x40 }, - { OV9640_MTXS, 0x0f }, -}; - -static const struct ov9640_reg ov9640_regs_rgb[] = { - { OV9640_MTX1, 0x71 }, - { OV9640_MTX2, 0x3e }, - { OV9640_MTX3, 0x0c }, - { OV9640_MTX4, 0x33 }, - { OV9640_MTX5, 0x72 }, - { OV9640_MTX6, 0x00 }, - { OV9640_MTX7, 0x2b }, - { OV9640_MTX8, 0x66 }, - { OV9640_MTX9, 0xd2 }, - { OV9640_MTXS, 0x65 }, -}; - -static u32 ov9640_codes[] = { - MEDIA_BUS_FMT_UYVY8_2X8, - MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - MEDIA_BUS_FMT_RGB565_2X8_LE, -}; - -/* read a register */ -static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) -{ - int ret; - u8 data = reg; - struct i2c_msg msg = { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = &data, - }; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) - goto err; - - msg.flags = I2C_M_RD; - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) - goto err; - - *val = data; - return 0; - -err: - dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); - return ret; -} - -/* write a register */ -static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val) -{ - int ret; - u8 _val; - unsigned char data[2] = { reg, val }; - struct i2c_msg msg = { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = data, - }; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); - return ret; - } - - /* we have to read the register back ... no idea why, maybe HW bug */ - ret = ov9640_reg_read(client, reg, &_val); - if (ret) - dev_err(&client->dev, - "Failed reading back register 0x%02x!\n", reg); - - return 0; -} - - -/* Read a register, alter its bits, write it back */ -static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset) -{ - u8 val; - int ret; - - ret = ov9640_reg_read(client, reg, &val); - if (ret) { - dev_err(&client->dev, - "[Read]-Modify-Write of register %02x failed!\n", reg); - return ret; - } - - val |= set; - val &= ~unset; - - ret = ov9640_reg_write(client, reg, val); - if (ret) - dev_err(&client->dev, - "Read-Modify-[Write] of register %02x failed!\n", reg); - - return ret; -} - -/* Soft reset the camera. This has nothing to do with the RESET pin! */ -static int ov9640_reset(struct i2c_client *client) -{ - int ret; - - ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET); - if (ret) - dev_err(&client->dev, - "An error occurred while entering soft reset!\n"); - - return ret; -} - -/* Start/Stop streaming from the device */ -static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) -{ - return 0; -} - -/* Set status of additional camera capabilities */ -static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl); - struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - return ov9640_reg_rmw(client, OV9640_MVFP, - OV9640_MVFP_V, 0); - return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V); - case V4L2_CID_HFLIP: - if (ctrl->val) - return ov9640_reg_rmw(client, OV9640_MVFP, - OV9640_MVFP_H, 0); - return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); - } - return -EINVAL; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov9640_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - u8 val; - - if (reg->reg & ~0xff) - return -EINVAL; - - reg->size = 1; - - ret = ov9640_reg_read(client, reg->reg, &val); - if (ret) - return ret; - - reg->val = (__u64)val; - - return 0; -} - -static int ov9640_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg & ~0xff || reg->val & ~0xff) - return -EINVAL; - - return ov9640_reg_write(client, reg->reg, reg->val); -} -#endif - -static int ov9640_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov9640_priv *priv = to_ov9640_sensor(sd); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -/* select nearest higher resolution for capture */ -static void ov9640_res_roundup(u32 *width, u32 *height) -{ - int i; - enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; - static const int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; - static const int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; - - for (i = 0; i < ARRAY_SIZE(res_x); i++) { - if (res_x[i] >= *width && res_y[i] >= *height) { - *width = res_x[i]; - *height = res_y[i]; - return; - } - } - - *width = res_x[SXGA]; - *height = res_y[SXGA]; -} - -/* Prepare necessary register changes depending on color encoding */ -static void ov9640_alter_regs(u32 code, - struct ov9640_reg_alt *alt) -{ - switch (code) { - default: - case MEDIA_BUS_FMT_UYVY8_2X8: - alt->com12 = OV9640_COM12_YUV_AVG; - alt->com13 = OV9640_COM13_Y_DELAY_EN | - OV9640_COM13_YUV_DLY(0x01); - break; - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: - alt->com7 = OV9640_COM7_RGB; - alt->com13 = OV9640_COM13_RGB_AVG; - alt->com15 = OV9640_COM15_RGB_555; - break; - case MEDIA_BUS_FMT_RGB565_2X8_LE: - alt->com7 = OV9640_COM7_RGB; - alt->com13 = OV9640_COM13_RGB_AVG; - alt->com15 = OV9640_COM15_RGB_565; - break; - } -} - -/* Setup registers according to resolution and color encoding */ -static int ov9640_write_regs(struct i2c_client *client, u32 width, - u32 code, struct ov9640_reg_alt *alts) -{ - const struct ov9640_reg *ov9640_regs, *matrix_regs; - int ov9640_regs_len, matrix_regs_len; - int i, ret; - u8 val; - - /* select register configuration for given resolution */ - switch (width) { - case W_QQCIF: - ov9640_regs = ov9640_regs_qqcif; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif); - break; - case W_QQVGA: - ov9640_regs = ov9640_regs_qqvga; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga); - break; - case W_QCIF: - ov9640_regs = ov9640_regs_qcif; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif); - break; - case W_QVGA: - ov9640_regs = ov9640_regs_qvga; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga); - break; - case W_CIF: - ov9640_regs = ov9640_regs_cif; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif); - break; - case W_VGA: - ov9640_regs = ov9640_regs_vga; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga); - break; - case W_SXGA: - ov9640_regs = ov9640_regs_sxga; - ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga); - break; - default: - dev_err(&client->dev, "Failed to select resolution!\n"); - return -EINVAL; - } - - /* select color matrix configuration for given color encoding */ - if (code == MEDIA_BUS_FMT_UYVY8_2X8) { - matrix_regs = ov9640_regs_yuv; - matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); - } else { - matrix_regs = ov9640_regs_rgb; - matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb); - } - - /* write register settings into the module */ - for (i = 0; i < ov9640_regs_len; i++) { - val = ov9640_regs[i].val; - - switch (ov9640_regs[i].reg) { - case OV9640_COM7: - val |= alts->com7; - break; - case OV9640_COM12: - val |= alts->com12; - break; - case OV9640_COM13: - val |= alts->com13; - break; - case OV9640_COM15: - val |= alts->com15; - break; - } - - ret = ov9640_reg_write(client, ov9640_regs[i].reg, val); - if (ret) - return ret; - } - - /* write color matrix configuration into the module */ - for (i = 0; i < matrix_regs_len; i++) { - ret = ov9640_reg_write(client, matrix_regs[i].reg, - matrix_regs[i].val); - if (ret) - return ret; - } - - return 0; -} - -/* program default register values */ -static int ov9640_prog_dflt(struct i2c_client *client) -{ - int i, ret; - - for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { - ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, - ov9640_regs_dflt[i].val); - if (ret) - return ret; - } - - /* wait for the changes to actually happen, 140ms are not enough yet */ - mdelay(150); - - return 0; -} - -/* set the format we will capture in */ -static int ov9640_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov9640_reg_alt alts = {0}; - int ret; - - ov9640_alter_regs(mf->code, &alts); - - ov9640_reset(client); - - ret = ov9640_prog_dflt(client); - if (ret) - return ret; - - return ov9640_write_regs(client, mf->width, mf->code, &alts); -} - -static int ov9640_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - - if (format->pad) - return -EINVAL; - - ov9640_res_roundup(&mf->width, &mf->height); - - mf->field = V4L2_FIELD_NONE; - - switch (mf->code) { - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: - case MEDIA_BUS_FMT_RGB565_2X8_LE: - mf->colorspace = V4L2_COLORSPACE_SRGB; - break; - default: - mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - /* fall through */ - case MEDIA_BUS_FMT_UYVY8_2X8: - mf->colorspace = V4L2_COLORSPACE_JPEG; - break; - } - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov9640_s_fmt(sd, mf); - - cfg->try_fmt = *mf; - return 0; -} - -static int ov9640_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(ov9640_codes)) - return -EINVAL; - - code->code = ov9640_codes[code->index]; - return 0; -} - -static int ov9640_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - sel->r.left = 0; - sel->r.top = 0; - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP: - sel->r.width = W_SXGA; - sel->r.height = H_SXGA; - return 0; - default: - return -EINVAL; - } -} - -static int ov9640_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov9640_priv *priv = to_ov9640_sensor(sd); - u8 pid, ver, midh, midl; - const char *devname; - int ret; - - ret = ov9640_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show product ID and manufacturer ID - */ - - ret = ov9640_reg_read(client, OV9640_PID, &pid); - if (!ret) - ret = ov9640_reg_read(client, OV9640_VER, &ver); - if (!ret) - ret = ov9640_reg_read(client, OV9640_MIDH, &midh); - if (!ret) - ret = ov9640_reg_read(client, OV9640_MIDL, &midl); - if (ret) - goto done; - - switch (VERSION(pid, ver)) { - case OV9640_V2: - devname = "ov9640"; - priv->revision = 2; - break; - case OV9640_V3: - devname = "ov9640"; - priv->revision = 3; - break; - default: - dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", - devname, pid, ver, midh, midl); - - ret = v4l2_ctrl_handler_setup(&priv->hdl); - -done: - ov9640_s_power(&priv->subdev, 0); - return ret; -} - -static const struct v4l2_ctrl_ops ov9640_ctrl_ops = { - .s_ctrl = ov9640_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops ov9640_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov9640_get_register, - .s_register = ov9640_set_register, -#endif - .s_power = ov9640_s_power, -}; - -/* Request bus settings on camera side */ -static int ov9640_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static const struct v4l2_subdev_video_ops ov9640_video_ops = { - .s_stream = ov9640_s_stream, - .g_mbus_config = ov9640_g_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops ov9640_pad_ops = { - .enum_mbus_code = ov9640_enum_mbus_code, - .get_selection = ov9640_get_selection, - .set_fmt = ov9640_set_fmt, -}; - -static const struct v4l2_subdev_ops ov9640_subdev_ops = { - .core = &ov9640_core_ops, - .video = &ov9640_video_ops, - .pad = &ov9640_pad_ops, -}; - -/* - * i2c_driver function - */ -static int ov9640_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov9640_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "Missing platform_data for driver\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); - - v4l2_ctrl_handler_init(&priv->hdl, 2); - v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - goto eclkget; - } - - ret = ov9640_video_probe(client); - if (ret) { - v4l2_clk_put(priv->clk); -eclkget: - v4l2_ctrl_handler_free(&priv->hdl); - } - - return ret; -} - -static int ov9640_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov9640_priv *priv = to_ov9640_sensor(sd); - - v4l2_clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); - v4l2_ctrl_handler_free(&priv->hdl); - return 0; -} - -static const struct i2c_device_id ov9640_id[] = { - { "ov9640", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov9640_id); - -static struct i2c_driver ov9640_i2c_driver = { - .driver = { - .name = "ov9640", - }, - .probe = ov9640_probe, - .remove = ov9640_remove, - .id_table = ov9640_id, -}; - -module_i2c_driver(ov9640_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); -MODULE_AUTHOR("Marek Vasut "); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 341fe1d301f587c930509f6b9153436b957f649c Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 17 Jan 2019 12:33:04 -0200 Subject: media: tw9910: Unregister subdevice with v4l2-async As the tw9910 subdevice is registered through the v4l2-async framework, use the v4l2-async provided function to register it. Fixes: 7b20f325a566 ("media: i2c: tw9910: Remove soc_camera dependencies") Signed-off-by: Jacopo Mondi Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tw9910.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 8d1138e13803..4d7cd736b930 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -1001,7 +1001,7 @@ static int tw9910_remove(struct i2c_client *client) if (priv->pdn_gpio) gpiod_put(priv->pdn_gpio); clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); + v4l2_async_unregister_subdev(&priv->subdev); return 0; } -- cgit v1.2.3-59-g8ed1b From 6f80a5e44ef2828fadd6bca67e5af564f533b6fc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:27:00 -0200 Subject: media: soc_mt9t112: remove obsolete sensor driver This driver got converted to not depend on soc_camera in commit 6a26f141bf62 ("media: i2c: mt9t112: Remove soc_camera dependencies"). There's no sense in keeping the old version there. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 6 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/soc_mt9t112.c | 1157 ---------------------------- 3 files changed, 1164 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/soc_mt9t112.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index f67499187bda..773d067c44a3 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -17,12 +17,6 @@ config SOC_CAMERA_MT9M111 This is the legacy configuration which shouldn't be used anymore, while VIDEO_MT9M111 should be used instead. -config SOC_CAMERA_MT9T112 - tristate "mt9t112 support" - depends on SOC_CAMERA && I2C - help - This driver supports MT9T112 cameras from Aptina. - config SOC_CAMERA_MT9V022 tristate "mt9v022 and mt9v024 support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index 0d15864cfda8..ea8da4074a01 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_SOC_CAMERA_MT9M001) += soc_mt9m001.o -obj-$(CONFIG_SOC_CAMERA_MT9T112) += soc_mt9t112.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV772X) += soc_ov772x.o diff --git a/drivers/media/i2c/soc_camera/soc_mt9t112.c b/drivers/media/i2c/soc_camera/soc_mt9t112.c deleted file mode 100644 index ea1ff270bc2d..000000000000 --- a/drivers/media/i2c/soc_camera/soc_mt9t112.c +++ /dev/null @@ -1,1157 +0,0 @@ -/* - * mt9t112 Camera Driver - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Kuninori Morimoto - * - * Based on ov772x driver, mt9m111 driver, - * - * Copyright (C) 2008 Kuninori Morimoto - * Copyright (C) 2008, Robert Jarzmik - * Copyright 2006-7 Jonathan Corbet - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* you can check PLL/clock info */ -/* #define EXT_CLOCK 24000000 */ - -/************************************************************************ - macro -************************************************************************/ -/* - * frame size - */ -#define MAX_WIDTH 2048 -#define MAX_HEIGHT 1536 - -/* - * macro of read/write - */ -#define ECHECKER(ret, x) \ - do { \ - (ret) = (x); \ - if ((ret) < 0) \ - return (ret); \ - } while (0) - -#define mt9t112_reg_write(ret, client, a, b) \ - ECHECKER(ret, __mt9t112_reg_write(client, a, b)) -#define mt9t112_mcu_write(ret, client, a, b) \ - ECHECKER(ret, __mt9t112_mcu_write(client, a, b)) - -#define mt9t112_reg_mask_set(ret, client, a, b, c) \ - ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c)) -#define mt9t112_mcu_mask_set(ret, client, a, b, c) \ - ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c)) - -#define mt9t112_reg_read(ret, client, a) \ - ECHECKER(ret, __mt9t112_reg_read(client, a)) - -/* - * Logical address - */ -#define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff)) -#define VAR(id, offset) _VAR(id, offset, 0x0000) -#define VAR8(id, offset) _VAR(id, offset, 0x8000) - -/************************************************************************ - struct -************************************************************************/ -struct mt9t112_format { - u32 code; - enum v4l2_colorspace colorspace; - u16 fmt; - u16 order; -}; - -struct mt9t112_priv { - struct v4l2_subdev subdev; - struct mt9t112_platform_data *info; - struct i2c_client *client; - struct v4l2_rect frame; - struct v4l2_clk *clk; - const struct mt9t112_format *format; - int num_formats; - u32 flags; -/* for flags */ -#define INIT_DONE (1 << 0) -#define PCLK_RISING (1 << 1) -}; - -/************************************************************************ - supported format -************************************************************************/ - -static const struct mt9t112_format mt9t112_cfmts[] = { - { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 1, - .order = 0, - }, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 1, - .order = 1, - }, { - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 1, - .order = 2, - }, { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 1, - .order = 3, - }, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 8, - .order = 2, - }, { - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .fmt = 4, - .order = 2, - }, -}; - -/************************************************************************ - general function -************************************************************************/ -static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), - struct mt9t112_priv, - subdev); -} - -static int __mt9t112_reg_read(const struct i2c_client *client, u16 command) -{ - struct i2c_msg msg[2]; - u8 buf[2]; - int ret; - - command = swab16(command); - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 2; - msg[0].buf = (u8 *)&command; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = 2; - msg[1].buf = buf; - - /* - * if return value of this function is < 0, - * it mean error. - * else, under 16bit is valid data. - */ - ret = i2c_transfer(client->adapter, msg, 2); - if (ret < 0) - return ret; - - memcpy(&ret, buf, 2); - return swab16(ret); -} - -static int __mt9t112_reg_write(const struct i2c_client *client, - u16 command, u16 data) -{ - struct i2c_msg msg; - u8 buf[4]; - int ret; - - command = swab16(command); - data = swab16(data); - - memcpy(buf + 0, &command, 2); - memcpy(buf + 2, &data, 2); - - msg.addr = client->addr; - msg.flags = 0; - msg.len = 4; - msg.buf = buf; - - /* - * i2c_transfer return message length, - * but this function should return 0 if correct case - */ - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret >= 0) - ret = 0; - - return ret; -} - -static int __mt9t112_reg_mask_set(const struct i2c_client *client, - u16 command, - u16 mask, - u16 set) -{ - int val = __mt9t112_reg_read(client, command); - if (val < 0) - return val; - - val &= ~mask; - val |= set & mask; - - return __mt9t112_reg_write(client, command, val); -} - -/* mcu access */ -static int __mt9t112_mcu_read(const struct i2c_client *client, u16 command) -{ - int ret; - - ret = __mt9t112_reg_write(client, 0x098E, command); - if (ret < 0) - return ret; - - return __mt9t112_reg_read(client, 0x0990); -} - -static int __mt9t112_mcu_write(const struct i2c_client *client, - u16 command, u16 data) -{ - int ret; - - ret = __mt9t112_reg_write(client, 0x098E, command); - if (ret < 0) - return ret; - - return __mt9t112_reg_write(client, 0x0990, data); -} - -static int __mt9t112_mcu_mask_set(const struct i2c_client *client, - u16 command, - u16 mask, - u16 set) -{ - int val = __mt9t112_mcu_read(client, command); - if (val < 0) - return val; - - val &= ~mask; - val |= set & mask; - - return __mt9t112_mcu_write(client, command, val); -} - -static int mt9t112_reset(const struct i2c_client *client) -{ - int ret; - - mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0001); - msleep(1); - mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0000); - - return ret; -} - -#ifndef EXT_CLOCK -#define CLOCK_INFO(a, b) -#else -#define CLOCK_INFO(a, b) mt9t112_clock_info(a, b) -static int mt9t112_clock_info(const struct i2c_client *client, u32 ext) -{ - int m, n, p1, p2, p3, p4, p5, p6, p7; - u32 vco, clk; - char *enable; - - ext /= 1000; /* kbyte order */ - - mt9t112_reg_read(n, client, 0x0012); - p1 = n & 0x000f; - n = n >> 4; - p2 = n & 0x000f; - n = n >> 4; - p3 = n & 0x000f; - - mt9t112_reg_read(n, client, 0x002a); - p4 = n & 0x000f; - n = n >> 4; - p5 = n & 0x000f; - n = n >> 4; - p6 = n & 0x000f; - - mt9t112_reg_read(n, client, 0x002c); - p7 = n & 0x000f; - - mt9t112_reg_read(n, client, 0x0010); - m = n & 0x00ff; - n = (n >> 8) & 0x003f; - - enable = ((6000 > ext) || (54000 < ext)) ? "X" : ""; - dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable); - - vco = 2 * m * ext / (n+1); - enable = ((384000 > vco) || (768000 < vco)) ? "X" : ""; - dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable); - - clk = vco / (p1+1) / (p2+1); - enable = (96000 < clk) ? "X" : ""; - dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable); - - clk = vco / (p3+1); - enable = (768000 < clk) ? "X" : ""; - dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable); - - clk = vco / (p6+1); - enable = (96000 < clk) ? "X" : ""; - dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable); - - clk = vco / (p5+1); - enable = (54000 < clk) ? "X" : ""; - dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable); - - clk = vco / (p4+1); - enable = (70000 < clk) ? "X" : ""; - dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable); - - clk = vco / (p7+1); - dev_dbg(&client->dev, "External sensor : %10u K\n", clk); - - clk = ext / (n+1); - enable = ((2000 > clk) || (24000 < clk)) ? "X" : ""; - dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable); - - return 0; -} -#endif - -static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top) -{ - soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH); - soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT); -} - -static int mt9t112_set_a_frame_size(const struct i2c_client *client, - u16 width, - u16 height) -{ - int ret; - u16 wstart = (MAX_WIDTH - width) / 2; - u16 hstart = (MAX_HEIGHT - height) / 2; - - /* (Context A) Image Width/Height */ - mt9t112_mcu_write(ret, client, VAR(26, 0), width); - mt9t112_mcu_write(ret, client, VAR(26, 2), height); - - /* (Context A) Output Width/Height */ - mt9t112_mcu_write(ret, client, VAR(18, 43), 8 + width); - mt9t112_mcu_write(ret, client, VAR(18, 45), 8 + height); - - /* (Context A) Start Row/Column */ - mt9t112_mcu_write(ret, client, VAR(18, 2), 4 + hstart); - mt9t112_mcu_write(ret, client, VAR(18, 4), 4 + wstart); - - /* (Context A) End Row/Column */ - mt9t112_mcu_write(ret, client, VAR(18, 6), 11 + height + hstart); - mt9t112_mcu_write(ret, client, VAR(18, 8), 11 + width + wstart); - - mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); - - return ret; -} - -static int mt9t112_set_pll_dividers(const struct i2c_client *client, - u8 m, u8 n, - u8 p1, u8 p2, u8 p3, - u8 p4, u8 p5, u8 p6, - u8 p7) -{ - int ret; - u16 val; - - /* N/M */ - val = (n << 8) | - (m << 0); - mt9t112_reg_mask_set(ret, client, 0x0010, 0x3fff, val); - - /* P1/P2/P3 */ - val = ((p3 & 0x0F) << 8) | - ((p2 & 0x0F) << 4) | - ((p1 & 0x0F) << 0); - mt9t112_reg_mask_set(ret, client, 0x0012, 0x0fff, val); - - /* P4/P5/P6 */ - val = (0x7 << 12) | - ((p6 & 0x0F) << 8) | - ((p5 & 0x0F) << 4) | - ((p4 & 0x0F) << 0); - mt9t112_reg_mask_set(ret, client, 0x002A, 0x7fff, val); - - /* P7 */ - val = (0x1 << 12) | - ((p7 & 0x0F) << 0); - mt9t112_reg_mask_set(ret, client, 0x002C, 0x100f, val); - - return ret; -} - -static int mt9t112_init_pll(const struct i2c_client *client) -{ - struct mt9t112_priv *priv = to_mt9t112(client); - int data, i, ret; - - mt9t112_reg_mask_set(ret, client, 0x0014, 0x003, 0x0001); - - /* PLL control: BYPASS PLL = 8517 */ - mt9t112_reg_write(ret, client, 0x0014, 0x2145); - - /* Replace these registers when new timing parameters are generated */ - mt9t112_set_pll_dividers(client, - priv->info->divider.m, - priv->info->divider.n, - priv->info->divider.p1, - priv->info->divider.p2, - priv->info->divider.p3, - priv->info->divider.p4, - priv->info->divider.p5, - priv->info->divider.p6, - priv->info->divider.p7); - - /* - * TEST_BYPASS on - * PLL_ENABLE on - * SEL_LOCK_DET on - * TEST_BYPASS off - */ - mt9t112_reg_write(ret, client, 0x0014, 0x2525); - mt9t112_reg_write(ret, client, 0x0014, 0x2527); - mt9t112_reg_write(ret, client, 0x0014, 0x3427); - mt9t112_reg_write(ret, client, 0x0014, 0x3027); - - mdelay(10); - - /* - * PLL_BYPASS off - * Reference clock count - * I2C Master Clock Divider - */ - mt9t112_reg_write(ret, client, 0x0014, 0x3046); - mt9t112_reg_write(ret, client, 0x0016, 0x0400); /* JPEG initialization workaround */ - mt9t112_reg_write(ret, client, 0x0022, 0x0190); - mt9t112_reg_write(ret, client, 0x3B84, 0x0212); - - /* External sensor clock is PLL bypass */ - mt9t112_reg_write(ret, client, 0x002E, 0x0500); - - mt9t112_reg_mask_set(ret, client, 0x0018, 0x0002, 0x0002); - mt9t112_reg_mask_set(ret, client, 0x3B82, 0x0004, 0x0004); - - /* MCU disabled */ - mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0x0004); - - /* out of standby */ - mt9t112_reg_mask_set(ret, client, 0x0018, 0x0001, 0); - - mdelay(50); - - /* - * Standby Workaround - * Disable Secondary I2C Pads - */ - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - mt9t112_reg_write(ret, client, 0x0614, 0x0001); - mdelay(1); - - /* poll to verify out of standby. Must Poll this bit */ - for (i = 0; i < 100; i++) { - mt9t112_reg_read(data, client, 0x0018); - if (!(0x4000 & data)) - break; - - mdelay(10); - } - - return ret; -} - -static int mt9t112_init_setting(const struct i2c_client *client) -{ - - int ret; - - /* Adaptive Output Clock (A) */ - mt9t112_mcu_mask_set(ret, client, VAR(26, 160), 0x0040, 0x0000); - - /* Read Mode (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 12), 0x0024); - - /* Fine Correction (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 15), 0x00CC); - - /* Fine IT Min (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 17), 0x01f1); - - /* Fine IT Max Margin (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 19), 0x00fF); - - /* Base Frame Lines (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 29), 0x032D); - - /* Min Line Length (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 31), 0x073a); - - /* Line Length (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 37), 0x07d0); - - /* Adaptive Output Clock (B) */ - mt9t112_mcu_mask_set(ret, client, VAR(27, 160), 0x0040, 0x0000); - - /* Row Start (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 74), 0x004); - - /* Column Start (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 76), 0x004); - - /* Row End (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 78), 0x60B); - - /* Column End (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 80), 0x80B); - - /* Fine Correction (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 87), 0x008C); - - /* Fine IT Min (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 89), 0x01F1); - - /* Fine IT Max Margin (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 91), 0x00FF); - - /* Base Frame Lines (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 101), 0x0668); - - /* Min Line Length (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 103), 0x0AF0); - - /* Line Length (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0); - - /* - * Flicker Dectection registers - * This section should be replaced whenever new Timing file is generated - * All the following registers need to be replaced - * Following registers are generated from Register Wizard but user can - * modify them. For detail see auto flicker detection tuning - */ - - /* FD_FDPERIOD_SELECT */ - mt9t112_mcu_write(ret, client, VAR8(8, 5), 0x01); - - /* PRI_B_CONFIG_FD_ALGO_RUN */ - mt9t112_mcu_write(ret, client, VAR(27, 17), 0x0003); - - /* PRI_A_CONFIG_FD_ALGO_RUN */ - mt9t112_mcu_write(ret, client, VAR(26, 17), 0x0003); - - /* - * AFD range detection tuning registers - */ - - /* search_f1_50 */ - mt9t112_mcu_write(ret, client, VAR8(18, 165), 0x25); - - /* search_f2_50 */ - mt9t112_mcu_write(ret, client, VAR8(18, 166), 0x28); - - /* search_f1_60 */ - mt9t112_mcu_write(ret, client, VAR8(18, 167), 0x2C); - - /* search_f2_60 */ - mt9t112_mcu_write(ret, client, VAR8(18, 168), 0x2F); - - /* period_50Hz (A) */ - mt9t112_mcu_write(ret, client, VAR8(18, 68), 0xBA); - - /* secret register by aptina */ - /* period_50Hz (A MSB) */ - mt9t112_mcu_write(ret, client, VAR8(18, 303), 0x00); - - /* period_60Hz (A) */ - mt9t112_mcu_write(ret, client, VAR8(18, 69), 0x9B); - - /* secret register by aptina */ - /* period_60Hz (A MSB) */ - mt9t112_mcu_write(ret, client, VAR8(18, 301), 0x00); - - /* period_50Hz (B) */ - mt9t112_mcu_write(ret, client, VAR8(18, 140), 0x82); - - /* secret register by aptina */ - /* period_50Hz (B) MSB */ - mt9t112_mcu_write(ret, client, VAR8(18, 304), 0x00); - - /* period_60Hz (B) */ - mt9t112_mcu_write(ret, client, VAR8(18, 141), 0x6D); - - /* secret register by aptina */ - /* period_60Hz (B) MSB */ - mt9t112_mcu_write(ret, client, VAR8(18, 302), 0x00); - - /* FD Mode */ - mt9t112_mcu_write(ret, client, VAR8(8, 2), 0x10); - - /* Stat_min */ - mt9t112_mcu_write(ret, client, VAR8(8, 9), 0x02); - - /* Stat_max */ - mt9t112_mcu_write(ret, client, VAR8(8, 10), 0x03); - - /* Min_amplitude */ - mt9t112_mcu_write(ret, client, VAR8(8, 12), 0x0A); - - /* RX FIFO Watermark (A) */ - mt9t112_mcu_write(ret, client, VAR(18, 70), 0x0014); - - /* RX FIFO Watermark (B) */ - mt9t112_mcu_write(ret, client, VAR(18, 142), 0x0014); - - /* MCLK: 16MHz - * PCLK: 73MHz - * CorePixCLK: 36.5 MHz - */ - mt9t112_mcu_write(ret, client, VAR8(18, 0x0044), 133); - mt9t112_mcu_write(ret, client, VAR8(18, 0x0045), 110); - mt9t112_mcu_write(ret, client, VAR8(18, 0x008c), 130); - mt9t112_mcu_write(ret, client, VAR8(18, 0x008d), 108); - - mt9t112_mcu_write(ret, client, VAR8(18, 0x00A5), 27); - mt9t112_mcu_write(ret, client, VAR8(18, 0x00a6), 30); - mt9t112_mcu_write(ret, client, VAR8(18, 0x00a7), 32); - mt9t112_mcu_write(ret, client, VAR8(18, 0x00a8), 35); - - return ret; -} - -static int mt9t112_auto_focus_setting(const struct i2c_client *client) -{ - int ret; - - mt9t112_mcu_write(ret, client, VAR(12, 13), 0x000F); - mt9t112_mcu_write(ret, client, VAR(12, 23), 0x0F0F); - mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); - - mt9t112_reg_write(ret, client, 0x0614, 0x0000); - - mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05); - mt9t112_mcu_write(ret, client, VAR8(12, 2), 0x02); - mt9t112_mcu_write(ret, client, VAR(12, 3), 0x0002); - mt9t112_mcu_write(ret, client, VAR(17, 3), 0x8001); - mt9t112_mcu_write(ret, client, VAR(17, 11), 0x0025); - mt9t112_mcu_write(ret, client, VAR(17, 13), 0x0193); - mt9t112_mcu_write(ret, client, VAR8(17, 33), 0x18); - mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05); - - return ret; -} - -static int mt9t112_auto_focus_trigger(const struct i2c_client *client) -{ - int ret; - - mt9t112_mcu_write(ret, client, VAR8(12, 25), 0x01); - - return ret; -} - -static int mt9t112_init_camera(const struct i2c_client *client) -{ - int ret; - - ECHECKER(ret, mt9t112_reset(client)); - - ECHECKER(ret, mt9t112_init_pll(client)); - - ECHECKER(ret, mt9t112_init_setting(client)); - - ECHECKER(ret, mt9t112_auto_focus_setting(client)); - - mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0); - - /* Analog setting B */ - mt9t112_reg_write(ret, client, 0x3084, 0x2409); - mt9t112_reg_write(ret, client, 0x3092, 0x0A49); - mt9t112_reg_write(ret, client, 0x3094, 0x4949); - mt9t112_reg_write(ret, client, 0x3096, 0x4950); - - /* - * Disable adaptive clock - * PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR - * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR - */ - mt9t112_mcu_write(ret, client, VAR(26, 160), 0x0A2E); - mt9t112_mcu_write(ret, client, VAR(27, 160), 0x0A2E); - - /* Configure STatus in Status_before_length Format and enable header */ - /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */ - mt9t112_mcu_write(ret, client, VAR(27, 144), 0x0CB4); - - /* Enable JPEG in context B */ - /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */ - mt9t112_mcu_write(ret, client, VAR8(27, 142), 0x01); - - /* Disable Dac_TXLO */ - mt9t112_reg_write(ret, client, 0x316C, 0x350F); - - /* Set max slew rates */ - mt9t112_reg_write(ret, client, 0x1E, 0x777); - - return ret; -} - -/************************************************************************ - v4l2_subdev_core_ops -************************************************************************/ - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9t112_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - reg->size = 2; - mt9t112_reg_read(ret, client, reg->reg); - - reg->val = (__u64)ret; - - return 0; -} - -static int mt9t112_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - mt9t112_reg_write(ret, client, reg->reg, reg->val); - - return ret; -} -#endif - -static int mt9t112_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9t112_priv *priv = to_mt9t112(client); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -static const struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9t112_g_register, - .s_register = mt9t112_s_register, -#endif - .s_power = mt9t112_s_power, -}; - - -/************************************************************************ - v4l2_subdev_video_ops -************************************************************************/ -static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - int ret = 0; - - if (!enable) { - /* FIXME - * - * If user selected large output size, - * and used it long time, - * mt9t112 camera will be very warm. - * - * But current driver can not stop mt9t112 camera. - * So, set small size here to solve this problem. - */ - mt9t112_set_a_frame_size(client, VGA_WIDTH, VGA_HEIGHT); - return ret; - } - - if (!(priv->flags & INIT_DONE)) { - u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000; - - ECHECKER(ret, mt9t112_init_camera(client)); - - /* Invert PCLK (Data sampled on falling edge of pixclk) */ - mt9t112_reg_write(ret, client, 0x3C20, param); - - mdelay(5); - - priv->flags |= INIT_DONE; - } - - mt9t112_mcu_write(ret, client, VAR(26, 7), priv->format->fmt); - mt9t112_mcu_write(ret, client, VAR(26, 9), priv->format->order); - mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); - - mt9t112_set_a_frame_size(client, - priv->frame.width, - priv->frame.height); - - ECHECKER(ret, mt9t112_auto_focus_trigger(client)); - - dev_dbg(&client->dev, "format : %d\n", priv->format->code); - dev_dbg(&client->dev, "size : %d x %d\n", - priv->frame.width, - priv->frame.height); - - CLOCK_INFO(client, EXT_CLOCK); - - return ret; -} - -static int mt9t112_set_params(struct mt9t112_priv *priv, - const struct v4l2_rect *rect, - u32 code) -{ - int i; - - /* - * get color format - */ - for (i = 0; i < priv->num_formats; i++) - if (mt9t112_cfmts[i].code == code) - break; - - if (i == priv->num_formats) - return -EINVAL; - - priv->frame = *rect; - - /* - * frame size check - */ - mt9t112_frame_check(&priv->frame.width, &priv->frame.height, - &priv->frame.left, &priv->frame.top); - - priv->format = mt9t112_cfmts + i; - - return 0; -} - -static int mt9t112_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = MAX_WIDTH; - sel->r.height = MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = priv->frame; - return 0; - default: - return -EINVAL; - } -} - -static int mt9t112_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - const struct v4l2_rect *rect = &sel->r; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - return mt9t112_set_params(priv, rect, priv->format->code); -} - -static int mt9t112_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - - if (format->pad) - return -EINVAL; - - mf->width = priv->frame.width; - mf->height = priv->frame.height; - mf->colorspace = priv->format->colorspace; - mf->code = priv->format->code; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int mt9t112_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - struct v4l2_rect rect = { - .width = mf->width, - .height = mf->height, - .left = priv->frame.left, - .top = priv->frame.top, - }; - int ret; - - ret = mt9t112_set_params(priv, &rect, mf->code); - - if (!ret) - mf->colorspace = priv->format->colorspace; - - return ret; -} - -static int mt9t112_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - unsigned int top, left; - int i; - - if (format->pad) - return -EINVAL; - - for (i = 0; i < priv->num_formats; i++) - if (mt9t112_cfmts[i].code == mf->code) - break; - - if (i == priv->num_formats) { - mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - mf->colorspace = V4L2_COLORSPACE_JPEG; - } else { - mf->colorspace = mt9t112_cfmts[i].colorspace; - } - - mt9t112_frame_check(&mf->width, &mf->height, &left, &top); - - mf->field = V4L2_FIELD_NONE; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return mt9t112_s_fmt(sd, mf); - cfg->try_fmt = *mf; - return 0; -} - -static int mt9t112_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t112_priv *priv = to_mt9t112(client); - - if (code->pad || code->index >= priv->num_formats) - return -EINVAL; - - code->code = mt9t112_cfmts[code->index].code; - - return 0; -} - -static int mt9t112_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9t112_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9t112_priv *priv = to_mt9t112(client); - - if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) - priv->flags |= PCLK_RISING; - - return 0; -} - -static const struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = { - .s_stream = mt9t112_s_stream, - .g_mbus_config = mt9t112_g_mbus_config, - .s_mbus_config = mt9t112_s_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops mt9t112_subdev_pad_ops = { - .enum_mbus_code = mt9t112_enum_mbus_code, - .get_selection = mt9t112_get_selection, - .set_selection = mt9t112_set_selection, - .get_fmt = mt9t112_get_fmt, - .set_fmt = mt9t112_set_fmt, -}; - -/************************************************************************ - i2c driver -************************************************************************/ -static const struct v4l2_subdev_ops mt9t112_subdev_ops = { - .core = &mt9t112_subdev_core_ops, - .video = &mt9t112_subdev_video_ops, - .pad = &mt9t112_subdev_pad_ops, -}; - -static int mt9t112_camera_probe(struct i2c_client *client) -{ - struct mt9t112_priv *priv = to_mt9t112(client); - const char *devname; - int chipid; - int ret; - - ret = mt9t112_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show chip ID - */ - mt9t112_reg_read(chipid, client, 0x0000); - - switch (chipid) { - case 0x2680: - devname = "mt9t111"; - priv->num_formats = 1; - break; - case 0x2682: - devname = "mt9t112"; - priv->num_formats = ARRAY_SIZE(mt9t112_cfmts); - break; - default: - dev_err(&client->dev, "Product ID error %04x\n", chipid); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "%s chip ID %04x\n", devname, chipid); - -done: - mt9t112_s_power(&priv->subdev, 0); - return ret; -} - -static int mt9t112_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9t112_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct v4l2_rect rect = { - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - .left = (MAX_WIDTH - VGA_WIDTH) / 2, - .top = (MAX_HEIGHT - VGA_HEIGHT) / 2, - }; - int ret; - - if (!ssdd || !ssdd->drv_priv) { - dev_err(&client->dev, "mt9t112: missing platform data!\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->info = ssdd->drv_priv; - - v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); - - ret = mt9t112_camera_probe(client); - - /* Cannot fail: using the default supported pixel code */ - if (!ret) - mt9t112_set_params(priv, &rect, MEDIA_BUS_FMT_UYVY8_2X8); - else - v4l2_clk_put(priv->clk); - - return ret; -} - -static int mt9t112_remove(struct i2c_client *client) -{ - struct mt9t112_priv *priv = to_mt9t112(client); - - v4l2_clk_put(priv->clk); - return 0; -} - -static const struct i2c_device_id mt9t112_id[] = { - { "mt9t112", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9t112_id); - -static struct i2c_driver mt9t112_i2c_driver = { - .driver = { - .name = "mt9t112", - }, - .probe = mt9t112_probe, - .remove = mt9t112_remove, - .id_table = mt9t112_id, -}; - -module_i2c_driver(mt9t112_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for mt9t112"); -MODULE_AUTHOR("Kuninori Morimoto"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 9a3a49f9246b2abf468bad43e7615b6bab907931 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:30:16 -0200 Subject: media: soc_ov772x: remove obsolete sensor driver This driver got converted to not depend on soc_camera in commit 762c28121d7c ("media: i2c: ov772x: Remove soc_camera dependencies"). There's no sense in keeping the old version there. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 6 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/soc_ov772x.c | 1123 ----------------------------- 3 files changed, 1130 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/soc_ov772x.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 773d067c44a3..62a6562f4f0c 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -29,12 +29,6 @@ config SOC_CAMERA_OV5642 help This is a V4L2 camera driver for the OmniVision OV5642 sensor -config SOC_CAMERA_OV772X - tristate "ov772x camera support" - depends on SOC_CAMERA && I2C - help - This is a ov772x camera driver - config SOC_CAMERA_OV9740 tristate "ov9740 camera support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index ea8da4074a01..1e82912c5b05 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -2,7 +2,6 @@ obj-$(CONFIG_SOC_CAMERA_MT9M001) += soc_mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o -obj-$(CONFIG_SOC_CAMERA_OV772X) += soc_ov772x.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += soc_rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += soc_tw9910.o diff --git a/drivers/media/i2c/soc_camera/soc_ov772x.c b/drivers/media/i2c/soc_camera/soc_ov772x.c deleted file mode 100644 index fafd372527b2..000000000000 --- a/drivers/media/i2c/soc_camera/soc_ov772x.c +++ /dev/null @@ -1,1123 +0,0 @@ -/* - * ov772x Camera Driver - * - * Copyright (C) 2008 Renesas Solutions Corp. - * Kuninori Morimoto - * - * Based on ov7670 and soc_camera_platform driver, - * - * Copyright 2006-7 Jonathan Corbet - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* - * register offset - */ -#define GAIN 0x00 /* AGC - Gain control gain setting */ -#define BLUE 0x01 /* AWB - Blue channel gain setting */ -#define RED 0x02 /* AWB - Red channel gain setting */ -#define GREEN 0x03 /* AWB - Green channel gain setting */ -#define COM1 0x04 /* Common control 1 */ -#define BAVG 0x05 /* U/B Average Level */ -#define GAVG 0x06 /* Y/Gb Average Level */ -#define RAVG 0x07 /* V/R Average Level */ -#define AECH 0x08 /* Exposure Value - AEC MSBs */ -#define COM2 0x09 /* Common control 2 */ -#define PID 0x0A /* Product ID Number MSB */ -#define VER 0x0B /* Product ID Number LSB */ -#define COM3 0x0C /* Common control 3 */ -#define COM4 0x0D /* Common control 4 */ -#define COM5 0x0E /* Common control 5 */ -#define COM6 0x0F /* Common control 6 */ -#define AEC 0x10 /* Exposure Value */ -#define CLKRC 0x11 /* Internal clock */ -#define COM7 0x12 /* Common control 7 */ -#define COM8 0x13 /* Common control 8 */ -#define COM9 0x14 /* Common control 9 */ -#define COM10 0x15 /* Common control 10 */ -#define REG16 0x16 /* Register 16 */ -#define HSTART 0x17 /* Horizontal sensor size */ -#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */ -#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */ -#define VSIZE 0x1A /* Vertical sensor size */ -#define PSHFT 0x1B /* Data format - pixel delay select */ -#define MIDH 0x1C /* Manufacturer ID byte - high */ -#define MIDL 0x1D /* Manufacturer ID byte - low */ -#define LAEC 0x1F /* Fine AEC value */ -#define COM11 0x20 /* Common control 11 */ -#define BDBASE 0x22 /* Banding filter Minimum AEC value */ -#define DBSTEP 0x23 /* Banding filter Maximum Setp */ -#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ -#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ -#define VPT 0x26 /* AGC/AEC Fast mode operating region */ -#define REG28 0x28 /* Register 28 */ -#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */ -#define EXHCH 0x2A /* Dummy pixel insert MSB */ -#define EXHCL 0x2B /* Dummy pixel insert LSB */ -#define VOUTSIZE 0x2C /* Vertical data output size MSBs */ -#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ -#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */ -#define YAVE 0x2F /* Y/G Channel Average value */ -#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */ -#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */ -#define HREF 0x32 /* Image start and size control */ -#define DM_LNL 0x33 /* Dummy line low 8 bits */ -#define DM_LNH 0x34 /* Dummy line high 8 bits */ -#define ADOFF_B 0x35 /* AD offset compensation value for B channel */ -#define ADOFF_R 0x36 /* AD offset compensation value for R channel */ -#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */ -#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */ -#define OFF_B 0x39 /* Analog process B channel offset value */ -#define OFF_R 0x3A /* Analog process R channel offset value */ -#define OFF_GB 0x3B /* Analog process Gb channel offset value */ -#define OFF_GR 0x3C /* Analog process Gr channel offset value */ -#define COM12 0x3D /* Common control 12 */ -#define COM13 0x3E /* Common control 13 */ -#define COM14 0x3F /* Common control 14 */ -#define COM15 0x40 /* Common control 15*/ -#define COM16 0x41 /* Common control 16 */ -#define TGT_B 0x42 /* BLC blue channel target value */ -#define TGT_R 0x43 /* BLC red channel target value */ -#define TGT_GB 0x44 /* BLC Gb channel target value */ -#define TGT_GR 0x45 /* BLC Gr channel target value */ -/* for ov7720 */ -#define LCC0 0x46 /* Lens correction control 0 */ -#define LCC1 0x47 /* Lens correction option 1 - X coordinate */ -#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */ -#define LCC3 0x49 /* Lens correction option 3 */ -#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */ -#define LCC5 0x4B /* Lens correction option 5 */ -#define LCC6 0x4C /* Lens correction option 6 */ -/* for ov7725 */ -#define LC_CTR 0x46 /* Lens correction control */ -#define LC_XC 0x47 /* X coordinate of lens correction center relative */ -#define LC_YC 0x48 /* Y coordinate of lens correction center relative */ -#define LC_COEF 0x49 /* Lens correction coefficient */ -#define LC_RADI 0x4A /* Lens correction radius */ -#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */ -#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */ - -#define FIXGAIN 0x4D /* Analog fix gain amplifer */ -#define AREF0 0x4E /* Sensor reference control */ -#define AREF1 0x4F /* Sensor reference current control */ -#define AREF2 0x50 /* Analog reference control */ -#define AREF3 0x51 /* ADC reference control */ -#define AREF4 0x52 /* ADC reference control */ -#define AREF5 0x53 /* ADC reference control */ -#define AREF6 0x54 /* Analog reference control */ -#define AREF7 0x55 /* Analog reference control */ -#define UFIX 0x60 /* U channel fixed value output */ -#define VFIX 0x61 /* V channel fixed value output */ -#define AWBB_BLK 0x62 /* AWB option for advanced AWB */ -#define AWB_CTRL0 0x63 /* AWB control byte 0 */ -#define DSP_CTRL1 0x64 /* DSP control byte 1 */ -#define DSP_CTRL2 0x65 /* DSP control byte 2 */ -#define DSP_CTRL3 0x66 /* DSP control byte 3 */ -#define DSP_CTRL4 0x67 /* DSP control byte 4 */ -#define AWB_BIAS 0x68 /* AWB BLC level clip */ -#define AWB_CTRL1 0x69 /* AWB control 1 */ -#define AWB_CTRL2 0x6A /* AWB control 2 */ -#define AWB_CTRL3 0x6B /* AWB control 3 */ -#define AWB_CTRL4 0x6C /* AWB control 4 */ -#define AWB_CTRL5 0x6D /* AWB control 5 */ -#define AWB_CTRL6 0x6E /* AWB control 6 */ -#define AWB_CTRL7 0x6F /* AWB control 7 */ -#define AWB_CTRL8 0x70 /* AWB control 8 */ -#define AWB_CTRL9 0x71 /* AWB control 9 */ -#define AWB_CTRL10 0x72 /* AWB control 10 */ -#define AWB_CTRL11 0x73 /* AWB control 11 */ -#define AWB_CTRL12 0x74 /* AWB control 12 */ -#define AWB_CTRL13 0x75 /* AWB control 13 */ -#define AWB_CTRL14 0x76 /* AWB control 14 */ -#define AWB_CTRL15 0x77 /* AWB control 15 */ -#define AWB_CTRL16 0x78 /* AWB control 16 */ -#define AWB_CTRL17 0x79 /* AWB control 17 */ -#define AWB_CTRL18 0x7A /* AWB control 18 */ -#define AWB_CTRL19 0x7B /* AWB control 19 */ -#define AWB_CTRL20 0x7C /* AWB control 20 */ -#define AWB_CTRL21 0x7D /* AWB control 21 */ -#define GAM1 0x7E /* Gamma Curve 1st segment input end point */ -#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */ -#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */ -#define GAM4 0x81 /* Gamma Curve 4th segment input end point */ -#define GAM5 0x82 /* Gamma Curve 5th segment input end point */ -#define GAM6 0x83 /* Gamma Curve 6th segment input end point */ -#define GAM7 0x84 /* Gamma Curve 7th segment input end point */ -#define GAM8 0x85 /* Gamma Curve 8th segment input end point */ -#define GAM9 0x86 /* Gamma Curve 9th segment input end point */ -#define GAM10 0x87 /* Gamma Curve 10th segment input end point */ -#define GAM11 0x88 /* Gamma Curve 11th segment input end point */ -#define GAM12 0x89 /* Gamma Curve 12th segment input end point */ -#define GAM13 0x8A /* Gamma Curve 13th segment input end point */ -#define GAM14 0x8B /* Gamma Curve 14th segment input end point */ -#define GAM15 0x8C /* Gamma Curve 15th segment input end point */ -#define SLOP 0x8D /* Gamma curve highest segment slope */ -#define DNSTH 0x8E /* De-noise threshold */ -#define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */ -#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */ -#define DNSOFF 0x91 /* Auto De-noise threshold control */ -#define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */ -#define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */ -#define MTX1 0x94 /* Matrix coefficient 1 */ -#define MTX2 0x95 /* Matrix coefficient 2 */ -#define MTX3 0x96 /* Matrix coefficient 3 */ -#define MTX4 0x97 /* Matrix coefficient 4 */ -#define MTX5 0x98 /* Matrix coefficient 5 */ -#define MTX6 0x99 /* Matrix coefficient 6 */ -#define MTX_CTRL 0x9A /* Matrix control */ -#define BRIGHT 0x9B /* Brightness control */ -#define CNTRST 0x9C /* Contrast contrast */ -#define CNTRST_CTRL 0x9D /* Contrast contrast center */ -#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */ -#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */ -#define SCAL0 0xA0 /* Scaling control 0 */ -#define SCAL1 0xA1 /* Scaling control 1 */ -#define SCAL2 0xA2 /* Scaling control 2 */ -#define FIFODLYM 0xA3 /* FIFO manual mode delay control */ -#define FIFODLYA 0xA4 /* FIFO auto mode delay control */ -#define SDE 0xA6 /* Special digital effect control */ -#define USAT 0xA7 /* U component saturation control */ -#define VSAT 0xA8 /* V component saturation control */ -/* for ov7720 */ -#define HUE0 0xA9 /* Hue control 0 */ -#define HUE1 0xAA /* Hue control 1 */ -/* for ov7725 */ -#define HUECOS 0xA9 /* Cosine value */ -#define HUESIN 0xAA /* Sine value */ - -#define SIGN 0xAB /* Sign bit for Hue and contrast */ -#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */ - -/* - * register detail - */ - -/* COM2 */ -#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ - /* Output drive capability */ -#define OCAP_1x 0x00 /* 1x */ -#define OCAP_2x 0x01 /* 2x */ -#define OCAP_3x 0x02 /* 3x */ -#define OCAP_4x 0x03 /* 4x */ - -/* COM3 */ -#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML) -#define IMG_MASK (VFLIP_IMG | HFLIP_IMG) - -#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */ -#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */ -#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */ -#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */ -#define SWAP_ML 0x08 /* Swap output MSB/LSB */ - /* Tri-state option for output clock */ -#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */ - /* 1: No tri-state at this period */ - /* Tri-state option for output data */ -#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */ - /* 1: No tri-state at this period */ -#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */ - -/* COM4 */ - /* PLL frequency control */ -#define PLL_BYPASS 0x00 /* 00: Bypass PLL */ -#define PLL_4x 0x40 /* 01: PLL 4x */ -#define PLL_6x 0x80 /* 10: PLL 6x */ -#define PLL_8x 0xc0 /* 11: PLL 8x */ - /* AEC evaluate window */ -#define AEC_FULL 0x00 /* 00: Full window */ -#define AEC_1p2 0x10 /* 01: 1/2 window */ -#define AEC_1p4 0x20 /* 10: 1/4 window */ -#define AEC_2p3 0x30 /* 11: Low 2/3 window */ - -/* COM5 */ -#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */ -#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */ - /* Auto frame rate max rate control */ -#define AFR_NO_RATE 0x00 /* No reduction of frame rate */ -#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */ -#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */ -#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */ - /* Auto frame rate active point control */ -#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */ -#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */ -#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */ -#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */ - /* AEC max step control */ -#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */ - /* 1 : No limit to AEC increase step */ - -/* COM7 */ - /* SCCB Register Reset */ -#define SCCB_RESET 0x80 /* 0 : No change */ - /* 1 : Resets all registers to default */ - /* Resolution selection */ -#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */ -#define SLCT_VGA 0x00 /* 0 : VGA */ -#define SLCT_QVGA 0x40 /* 1 : QVGA */ -#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */ -#define SENSOR_RAW 0x10 /* Sensor RAW */ - /* RGB output format control */ -#define FMT_MASK 0x0c /* Mask of color format */ -#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */ -#define FMT_RGB565 0x04 /* 01 : RGB 565 */ -#define FMT_RGB555 0x08 /* 10 : RGB 555 */ -#define FMT_RGB444 0x0c /* 11 : RGB 444 */ - /* Output format control */ -#define OFMT_MASK 0x03 /* Mask of output format */ -#define OFMT_YUV 0x00 /* 00 : YUV */ -#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */ -#define OFMT_RGB 0x02 /* 10 : RGB */ -#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */ - -/* COM8 */ -#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */ - /* AEC Setp size limit */ -#define UNLMT_STEP 0x40 /* 0 : Step size is limited */ - /* 1 : Unlimited step size */ -#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */ -#define AEC_BND 0x10 /* Enable AEC below banding value */ -#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */ -#define AGC_ON 0x04 /* AGC Enable */ -#define AWB_ON 0x02 /* AWB Enable */ -#define AEC_ON 0x01 /* AEC Enable */ - -/* COM9 */ -#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */ - /* Automatic gain ceiling - maximum AGC value */ -#define GAIN_2x 0x00 /* 000 : 2x */ -#define GAIN_4x 0x10 /* 001 : 4x */ -#define GAIN_8x 0x20 /* 010 : 8x */ -#define GAIN_16x 0x30 /* 011 : 16x */ -#define GAIN_32x 0x40 /* 100 : 32x */ -#define GAIN_64x 0x50 /* 101 : 64x */ -#define GAIN_128x 0x60 /* 110 : 128x */ -#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ -#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ - -/* COM11 */ -#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */ -#define SGLF_TRIG 0x01 /* Single frame transfer trigger */ - -/* HREF */ -#define HREF_VSTART_SHIFT 6 /* VSTART LSB */ -#define HREF_HSTART_SHIFT 4 /* HSTART 2 LSBs */ -#define HREF_VSIZE_SHIFT 2 /* VSIZE LSB */ -#define HREF_HSIZE_SHIFT 0 /* HSIZE 2 LSBs */ - -/* EXHCH */ -#define EXHCH_VSIZE_SHIFT 2 /* VOUTSIZE LSB */ -#define EXHCH_HSIZE_SHIFT 0 /* HOUTSIZE 2 LSBs */ - -/* DSP_CTRL1 */ -#define FIFO_ON 0x80 /* FIFO enable/disable selection */ -#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */ -#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */ -#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */ -#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */ -#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */ -#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */ -#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */ - -/* DSP_CTRL3 */ -#define UV_MASK 0x80 /* UV output sequence option */ -#define UV_ON 0x80 /* ON */ -#define UV_OFF 0x00 /* OFF */ -#define CBAR_MASK 0x20 /* DSP Color bar mask */ -#define CBAR_ON 0x20 /* ON */ -#define CBAR_OFF 0x00 /* OFF */ - -/* DSP_CTRL4 */ -#define DSP_OFMT_YUV 0x00 -#define DSP_OFMT_RGB 0x00 -#define DSP_OFMT_RAW8 0x02 -#define DSP_OFMT_RAW10 0x03 - -/* DSPAUTO (DSP Auto Function ON/OFF Control) */ -#define AWB_ACTRL 0x80 /* AWB auto threshold control */ -#define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */ -#define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */ -#define UV_ACTRL 0x10 /* UV adjust auto slope control */ -#define SCAL0_ACTRL 0x08 /* Auto scaling factor control */ -#define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */ - -#define OV772X_MAX_WIDTH VGA_WIDTH -#define OV772X_MAX_HEIGHT VGA_HEIGHT - -/* - * ID - */ -#define OV7720 0x7720 -#define OV7725 0x7721 -#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF)) - -/* - * struct - */ - -struct ov772x_color_format { - u32 code; - enum v4l2_colorspace colorspace; - u8 dsp3; - u8 dsp4; - u8 com3; - u8 com7; -}; - -struct ov772x_win_size { - char *name; - unsigned char com7_bit; - struct v4l2_rect rect; -}; - -struct ov772x_priv { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; - struct ov772x_camera_info *info; - const struct ov772x_color_format *cfmt; - const struct ov772x_win_size *win; - unsigned short flag_vflip:1; - unsigned short flag_hflip:1; - /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ - unsigned short band_filter; -}; - -/* - * supported color format list - */ -static const struct ov772x_color_format ov772x_cfmts[] = { - { - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = SWAP_YUV, - .com7 = OFMT_YUV, - }, - { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .dsp3 = UV_ON, - .dsp4 = DSP_OFMT_YUV, - .com3 = SWAP_YUV, - .com7 = OFMT_YUV, - }, - { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = 0x0, - .com7 = OFMT_YUV, - }, - { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = SWAP_RGB, - .com7 = FMT_RGB555 | OFMT_RGB, - }, - { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .colorspace = V4L2_COLORSPACE_SRGB, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = 0x0, - .com7 = FMT_RGB555 | OFMT_RGB, - }, - { - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = SWAP_RGB, - .com7 = FMT_RGB565 | OFMT_RGB, - }, - { - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .colorspace = V4L2_COLORSPACE_SRGB, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_YUV, - .com3 = 0x0, - .com7 = FMT_RGB565 | OFMT_RGB, - }, - { - /* Setting DSP4 to DSP_OFMT_RAW8 still gives 10-bit output, - * regardless of the COM7 value. We can thus only support 10-bit - * Bayer until someone figures it out. - */ - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .colorspace = V4L2_COLORSPACE_SRGB, - .dsp3 = 0x0, - .dsp4 = DSP_OFMT_RAW10, - .com3 = 0x0, - .com7 = SENSOR_RAW | OFMT_BRAW, - }, -}; - - -/* - * window size list - */ - -static const struct ov772x_win_size ov772x_win_sizes[] = { - { - .name = "VGA", - .com7_bit = SLCT_VGA, - .rect = { - .left = 140, - .top = 14, - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - }, - }, { - .name = "QVGA", - .com7_bit = SLCT_QVGA, - .rect = { - .left = 252, - .top = 6, - .width = QVGA_WIDTH, - .height = QVGA_HEIGHT, - }, - }, -}; - -/* - * general function - */ - -static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd) -{ - return container_of(sd, struct ov772x_priv, subdev); -} - -static inline int ov772x_read(struct i2c_client *client, u8 addr) -{ - return i2c_smbus_read_byte_data(client, addr); -} - -static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value) -{ - return i2c_smbus_write_byte_data(client, addr, value); -} - -static int ov772x_mask_set(struct i2c_client *client, u8 command, u8 mask, - u8 set) -{ - s32 val = ov772x_read(client, command); - if (val < 0) - return val; - - val &= ~mask; - val |= set & mask; - - return ov772x_write(client, command, val); -} - -static int ov772x_reset(struct i2c_client *client) -{ - int ret; - - ret = ov772x_write(client, COM7, SCCB_RESET); - if (ret < 0) - return ret; - - msleep(1); - - return ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); -} - -/* - * soc_camera_ops function - */ - -static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov772x_priv *priv = to_ov772x(sd); - - if (!enable) { - ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); - return 0; - } - - ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0); - - dev_dbg(&client->dev, "format %d, win %s\n", - priv->cfmt->code, priv->win->name); - - return 0; -} - -static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct ov772x_priv *priv = container_of(ctrl->handler, - struct ov772x_priv, hdl); - struct v4l2_subdev *sd = &priv->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; - u8 val; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - val = ctrl->val ? VFLIP_IMG : 0x00; - priv->flag_vflip = ctrl->val; - if (priv->info->flags & OV772X_FLAG_VFLIP) - val ^= VFLIP_IMG; - return ov772x_mask_set(client, COM3, VFLIP_IMG, val); - case V4L2_CID_HFLIP: - val = ctrl->val ? HFLIP_IMG : 0x00; - priv->flag_hflip = ctrl->val; - if (priv->info->flags & OV772X_FLAG_HFLIP) - val ^= HFLIP_IMG; - return ov772x_mask_set(client, COM3, HFLIP_IMG, val); - case V4L2_CID_BAND_STOP_FILTER: - if (!ctrl->val) { - /* Switch the filter off, it is on now */ - ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff); - if (!ret) - ret = ov772x_mask_set(client, COM8, - BNDF_ON_OFF, 0); - } else { - /* Switch the filter on, set AEC low limit */ - val = 256 - ctrl->val; - ret = ov772x_mask_set(client, COM8, - BNDF_ON_OFF, BNDF_ON_OFF); - if (!ret) - ret = ov772x_mask_set(client, BDBASE, - 0xff, val); - } - if (!ret) - priv->band_filter = ctrl->val; - return ret; - } - - return -EINVAL; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov772x_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - reg->size = 1; - if (reg->reg > 0xff) - return -EINVAL; - - ret = ov772x_read(client, reg->reg); - if (ret < 0) - return ret; - - reg->val = (__u64)ret; - - return 0; -} - -static int ov772x_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff || - reg->val > 0xff) - return -EINVAL; - - return ov772x_write(client, reg->reg, reg->val); -} -#endif - -static int ov772x_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov772x_priv *priv = to_ov772x(sd); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) -{ - const struct ov772x_win_size *win = &ov772x_win_sizes[0]; - u32 best_diff = UINT_MAX; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(ov772x_win_sizes); ++i) { - u32 diff = abs(width - ov772x_win_sizes[i].rect.width) - + abs(height - ov772x_win_sizes[i].rect.height); - if (diff < best_diff) { - best_diff = diff; - win = &ov772x_win_sizes[i]; - } - } - - return win; -} - -static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf, - const struct ov772x_color_format **cfmt, - const struct ov772x_win_size **win) -{ - unsigned int i; - - /* Select a format. */ - *cfmt = &ov772x_cfmts[0]; - - for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { - if (mf->code == ov772x_cfmts[i].code) { - *cfmt = &ov772x_cfmts[i]; - break; - } - } - - /* Select a window size. */ - *win = ov772x_select_win(mf->width, mf->height); -} - -static int ov772x_set_params(struct ov772x_priv *priv, - const struct ov772x_color_format *cfmt, - const struct ov772x_win_size *win) -{ - struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - int ret; - u8 val; - - /* - * reset hardware - */ - ov772x_reset(client); - - /* - * Edge Ctrl - */ - if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) { - - /* - * Manual Edge Control Mode - * - * Edge auto strength bit is set by default. - * Remove it when manual mode. - */ - - ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00); - if (ret < 0) - goto ov772x_set_fmt_error; - - ret = ov772x_mask_set(client, - EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK, - priv->info->edgectrl.threshold); - if (ret < 0) - goto ov772x_set_fmt_error; - - ret = ov772x_mask_set(client, - EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK, - priv->info->edgectrl.strength); - if (ret < 0) - goto ov772x_set_fmt_error; - - } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) { - /* - * Auto Edge Control Mode - * - * set upper and lower limit - */ - ret = ov772x_mask_set(client, - EDGE_UPPER, OV772X_EDGE_UPPER_MASK, - priv->info->edgectrl.upper); - if (ret < 0) - goto ov772x_set_fmt_error; - - ret = ov772x_mask_set(client, - EDGE_LOWER, OV772X_EDGE_LOWER_MASK, - priv->info->edgectrl.lower); - if (ret < 0) - goto ov772x_set_fmt_error; - } - - /* Format and window size */ - ret = ov772x_write(client, HSTART, win->rect.left >> 2); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, HSIZE, win->rect.width >> 2); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, VSTART, win->rect.top >> 1); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, VSIZE, win->rect.height >> 1); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, HOUTSIZE, win->rect.width >> 2); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, VOUTSIZE, win->rect.height >> 1); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, HREF, - ((win->rect.top & 1) << HREF_VSTART_SHIFT) | - ((win->rect.left & 3) << HREF_HSTART_SHIFT) | - ((win->rect.height & 1) << HREF_VSIZE_SHIFT) | - ((win->rect.width & 3) << HREF_HSIZE_SHIFT)); - if (ret < 0) - goto ov772x_set_fmt_error; - ret = ov772x_write(client, EXHCH, - ((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) | - ((win->rect.width & 3) << EXHCH_HSIZE_SHIFT)); - if (ret < 0) - goto ov772x_set_fmt_error; - - /* - * set DSP_CTRL3 - */ - val = cfmt->dsp3; - if (val) { - ret = ov772x_mask_set(client, - DSP_CTRL3, UV_MASK, val); - if (ret < 0) - goto ov772x_set_fmt_error; - } - - /* DSP_CTRL4: AEC reference point and DSP output format. */ - if (cfmt->dsp4) { - ret = ov772x_write(client, DSP_CTRL4, cfmt->dsp4); - if (ret < 0) - goto ov772x_set_fmt_error; - } - - /* - * set COM3 - */ - val = cfmt->com3; - if (priv->info->flags & OV772X_FLAG_VFLIP) - val |= VFLIP_IMG; - if (priv->info->flags & OV772X_FLAG_HFLIP) - val |= HFLIP_IMG; - if (priv->flag_vflip) - val ^= VFLIP_IMG; - if (priv->flag_hflip) - val ^= HFLIP_IMG; - - ret = ov772x_mask_set(client, - COM3, SWAP_MASK | IMG_MASK, val); - if (ret < 0) - goto ov772x_set_fmt_error; - - /* COM7: Sensor resolution and output format control. */ - ret = ov772x_write(client, COM7, win->com7_bit | cfmt->com7); - if (ret < 0) - goto ov772x_set_fmt_error; - - /* - * set COM8 - */ - if (priv->band_filter) { - ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); - if (!ret) - ret = ov772x_mask_set(client, BDBASE, - 0xff, 256 - priv->band_filter); - if (ret < 0) - goto ov772x_set_fmt_error; - } - - return ret; - -ov772x_set_fmt_error: - - ov772x_reset(client); - - return ret; -} - -static int ov772x_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - sel->r.left = 0; - sel->r.top = 0; - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.width = OV772X_MAX_WIDTH; - sel->r.height = OV772X_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r.width = VGA_WIDTH; - sel->r.height = VGA_HEIGHT; - return 0; - default: - return -EINVAL; - } -} - -static int ov772x_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct ov772x_priv *priv = to_ov772x(sd); - - if (format->pad) - return -EINVAL; - - mf->width = priv->win->rect.width; - mf->height = priv->win->rect.height; - mf->code = priv->cfmt->code; - mf->colorspace = priv->cfmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int ov772x_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct ov772x_priv *priv = to_ov772x(sd); - struct v4l2_mbus_framefmt *mf = &format->format; - const struct ov772x_color_format *cfmt; - const struct ov772x_win_size *win; - int ret; - - if (format->pad) - return -EINVAL; - - ov772x_select_params(mf, &cfmt, &win); - - mf->code = cfmt->code; - mf->width = win->rect.width; - mf->height = win->rect.height; - mf->field = V4L2_FIELD_NONE; - mf->colorspace = cfmt->colorspace; - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; - return 0; - } - - ret = ov772x_set_params(priv, cfmt, win); - if (ret < 0) - return ret; - - priv->win = win; - priv->cfmt = cfmt; - return 0; -} - -static int ov772x_video_probe(struct ov772x_priv *priv) -{ - struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - u8 pid, ver; - const char *devname; - int ret; - - ret = ov772x_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show product ID and manufacturer ID - */ - pid = ov772x_read(client, PID); - ver = ov772x_read(client, VER); - - switch (VERSION(pid, ver)) { - case OV7720: - devname = "ov7720"; - break; - case OV7725: - devname = "ov7725"; - break; - default: - dev_err(&client->dev, - "Product ID error %x:%x\n", pid, ver); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, - "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", - devname, - pid, - ver, - ov772x_read(client, MIDH), - ov772x_read(client, MIDL)); - ret = v4l2_ctrl_handler_setup(&priv->hdl); - -done: - ov772x_s_power(&priv->subdev, 0); - return ret; -} - -static const struct v4l2_ctrl_ops ov772x_ctrl_ops = { - .s_ctrl = ov772x_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov772x_g_register, - .s_register = ov772x_s_register, -#endif - .s_power = ov772x_s_power, -}; - -static int ov772x_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(ov772x_cfmts)) - return -EINVAL; - - code->code = ov772x_cfmts[code->index].code; - return 0; -} - -static int ov772x_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static const struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { - .s_stream = ov772x_s_stream, - .g_mbus_config = ov772x_g_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = { - .enum_mbus_code = ov772x_enum_mbus_code, - .get_selection = ov772x_get_selection, - .get_fmt = ov772x_get_fmt, - .set_fmt = ov772x_set_fmt, -}; - -static const struct v4l2_subdev_ops ov772x_subdev_ops = { - .core = &ov772x_subdev_core_ops, - .video = &ov772x_subdev_video_ops, - .pad = &ov772x_subdev_pad_ops, -}; - -/* - * i2c_driver function - */ - -static int ov772x_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov772x_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - int ret; - - if (!ssdd || !ssdd->drv_priv) { - dev_err(&client->dev, "OV772X: missing platform data!\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_PROTOCOL_MANGLING)) { - dev_err(&adapter->dev, - "I2C-Adapter doesn't support SMBUS_BYTE_DATA or PROTOCOL_MANGLING\n"); - return -EIO; - } - client->flags |= I2C_CLIENT_SCCB; - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->info = ssdd->drv_priv; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); - v4l2_ctrl_handler_init(&priv->hdl, 3); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); - priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - goto eclkget; - } - - ret = ov772x_video_probe(priv); - if (ret < 0) { - v4l2_clk_put(priv->clk); -eclkget: - v4l2_ctrl_handler_free(&priv->hdl); - } else { - priv->cfmt = &ov772x_cfmts[0]; - priv->win = &ov772x_win_sizes[0]; - } - - return ret; -} - -static int ov772x_remove(struct i2c_client *client) -{ - struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client)); - - v4l2_clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); - v4l2_ctrl_handler_free(&priv->hdl); - return 0; -} - -static const struct i2c_device_id ov772x_id[] = { - { "ov772x", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov772x_id); - -static struct i2c_driver ov772x_i2c_driver = { - .driver = { - .name = "ov772x", - }, - .probe = ov772x_probe, - .remove = ov772x_remove, - .id_table = ov772x_id, -}; - -module_i2c_driver(ov772x_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for ov772x"); -MODULE_AUTHOR("Kuninori Morimoto"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 386a35eb70569b3158392eb573fe42589a669da4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:33:42 -0200 Subject: media: tw9910.h: remove obsolete soc_camera.h include. This include isn't use anymore, so drop it. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/i2c/tw9910.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/media/i2c/tw9910.h b/include/media/i2c/tw9910.h index bec8f7bce745..2f93799d5a21 100644 --- a/include/media/i2c/tw9910.h +++ b/include/media/i2c/tw9910.h @@ -16,8 +16,6 @@ #ifndef __TW9910_H__ #define __TW9910_H__ -#include - /** * tw9910_mpout_pin - MPOUT (multi-purpose output) pin functions */ -- cgit v1.2.3-59-g8ed1b From 4e869529ab21583dcba935d8ac4c8250e5907da1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:37:44 -0200 Subject: media: soc_tw9910: remove obsolete sensor driver This driver got converted to not depend on soc_camera in commit 7b20f325a566 ("media: i2c: tw9910: Remove soc_camera dependencies"). There's no sense in keeping the old version there. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 6 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/soc_tw9910.c | 999 ------------------------------ 3 files changed, 1006 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/soc_tw9910.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 62a6562f4f0c..dea66ef1394d 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -40,9 +40,3 @@ config SOC_CAMERA_RJ54N1 depends on SOC_CAMERA && I2C help This is a rj54n1cb0c video driver - -config SOC_CAMERA_TW9910 - tristate "tw9910 support" - depends on SOC_CAMERA && I2C - help - This is a tw9910 video driver diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index 1e82912c5b05..94659f7aa195 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -4,4 +4,3 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += soc_rj54n1cb0c.o -obj-$(CONFIG_SOC_CAMERA_TW9910) += soc_tw9910.o diff --git a/drivers/media/i2c/soc_camera/soc_tw9910.c b/drivers/media/i2c/soc_camera/soc_tw9910.c deleted file mode 100644 index bdb5e0a431e9..000000000000 --- a/drivers/media/i2c/soc_camera/soc_tw9910.c +++ /dev/null @@ -1,999 +0,0 @@ -/* - * tw9910 Video Driver - * - * Copyright (C) 2008 Renesas Solutions Corp. - * Kuninori Morimoto - * - * Based on ov772x driver, - * - * Copyright (C) 2008 Kuninori Morimoto - * Copyright 2006-7 Jonathan Corbet - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define GET_ID(val) ((val & 0xF8) >> 3) -#define GET_REV(val) (val & 0x07) - -/* - * register offset - */ -#define ID 0x00 /* Product ID Code Register */ -#define STATUS1 0x01 /* Chip Status Register I */ -#define INFORM 0x02 /* Input Format */ -#define OPFORM 0x03 /* Output Format Control Register */ -#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */ -#define OUTCTR1 0x05 /* Output Control I */ -#define ACNTL1 0x06 /* Analog Control Register 1 */ -#define CROP_HI 0x07 /* Cropping Register, High */ -#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */ -#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */ -#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */ -#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */ -#define CNTRL1 0x0C /* Control Register I */ -#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */ -#define SCALE_HI 0x0E /* Scaling Register, High */ -#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */ -#define BRIGHT 0x10 /* BRIGHTNESS Control Register */ -#define CONTRAST 0x11 /* CONTRAST Control Register */ -#define SHARPNESS 0x12 /* SHARPNESS Control Register I */ -#define SAT_U 0x13 /* Chroma (U) Gain Register */ -#define SAT_V 0x14 /* Chroma (V) Gain Register */ -#define HUE 0x15 /* Hue Control Register */ -#define CORING1 0x17 -#define CORING2 0x18 /* Coring and IF compensation */ -#define VBICNTL 0x19 /* VBI Control Register */ -#define ACNTL2 0x1A /* Analog Control 2 */ -#define OUTCTR2 0x1B /* Output Control 2 */ -#define SDT 0x1C /* Standard Selection */ -#define SDTR 0x1D /* Standard Recognition */ -#define TEST 0x1F /* Test Control Register */ -#define CLMPG 0x20 /* Clamping Gain */ -#define IAGC 0x21 /* Individual AGC Gain */ -#define AGCGAIN 0x22 /* AGC Gain */ -#define PEAKWT 0x23 /* White Peak Threshold */ -#define CLMPL 0x24 /* Clamp level */ -#define SYNCT 0x25 /* Sync Amplitude */ -#define MISSCNT 0x26 /* Sync Miss Count Register */ -#define PCLAMP 0x27 /* Clamp Position Register */ -#define VCNTL1 0x28 /* Vertical Control I */ -#define VCNTL2 0x29 /* Vertical Control II */ -#define CKILL 0x2A /* Color Killer Level Control */ -#define COMB 0x2B /* Comb Filter Control */ -#define LDLY 0x2C /* Luma Delay and H Filter Control */ -#define MISC1 0x2D /* Miscellaneous Control I */ -#define LOOP 0x2E /* LOOP Control Register */ -#define MISC2 0x2F /* Miscellaneous Control II */ -#define MVSN 0x30 /* Macrovision Detection */ -#define STATUS2 0x31 /* Chip STATUS II */ -#define HFREF 0x32 /* H monitor */ -#define CLMD 0x33 /* CLAMP MODE */ -#define IDCNTL 0x34 /* ID Detection Control */ -#define CLCNTL1 0x35 /* Clamp Control I */ -#define ANAPLLCTL 0x4C -#define VBIMIN 0x4D -#define HSLOWCTL 0x4E -#define WSS3 0x4F -#define FILLDATA 0x50 -#define SDID 0x51 -#define DID 0x52 -#define WSS1 0x53 -#define WSS2 0x54 -#define VVBI 0x55 -#define LCTL6 0x56 -#define LCTL7 0x57 -#define LCTL8 0x58 -#define LCTL9 0x59 -#define LCTL10 0x5A -#define LCTL11 0x5B -#define LCTL12 0x5C -#define LCTL13 0x5D -#define LCTL14 0x5E -#define LCTL15 0x5F -#define LCTL16 0x60 -#define LCTL17 0x61 -#define LCTL18 0x62 -#define LCTL19 0x63 -#define LCTL20 0x64 -#define LCTL21 0x65 -#define LCTL22 0x66 -#define LCTL23 0x67 -#define LCTL24 0x68 -#define LCTL25 0x69 -#define LCTL26 0x6A -#define HSBEGIN 0x6B -#define HSEND 0x6C -#define OVSDLY 0x6D -#define OVSEND 0x6E -#define VBIDELAY 0x6F - -/* - * register detail - */ - -/* INFORM */ -#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */ -#define FC27_FF 0x00 /* 0 : Square pixel mode. */ - /* Must use 24.54MHz for 60Hz field rate */ - /* source or 29.5MHz for 50Hz field rate */ -#define IFSEL_S 0x10 /* 01 : S-video decoding */ -#define IFSEL_C 0x00 /* 00 : Composite video decoding */ - /* Y input video selection */ -#define YSEL_M0 0x00 /* 00 : Mux0 selected */ -#define YSEL_M1 0x04 /* 01 : Mux1 selected */ -#define YSEL_M2 0x08 /* 10 : Mux2 selected */ -#define YSEL_M3 0x10 /* 11 : Mux3 selected */ - -/* OPFORM */ -#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */ - /* 1 : ITU-R-656 compatible data sequence format */ -#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */ - /* 1 : 16-bit YCrCb 4:2:2 output format.*/ -#define LLCMODE 0x20 /* 1 : LLC output mode. */ - /* 0 : free-run output mode */ -#define AINC 0x10 /* Serial interface auto-indexing control */ - /* 0 : auto-increment */ - /* 1 : non-auto */ -#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */ - /* 0 : Vertical out ctrl by HACTIVE and DVALID */ -#define OEN_TRI_SEL_MASK 0x07 -#define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */ -#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */ -#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */ - -/* OUTCTR1 */ -#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */ -#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */ - /* VS pin output control */ -#define VSSL_VSYNC 0x00 /* 0 : VSYNC */ -#define VSSL_VACT 0x10 /* 1 : VACT */ -#define VSSL_FIELD 0x20 /* 2 : FIELD */ -#define VSSL_VVALID 0x30 /* 3 : VVALID */ -#define VSSL_ZERO 0x70 /* 7 : 0 */ -#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */ -#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/ - /* HS pin output control */ -#define HSSL_HACT 0x00 /* 0 : HACT */ -#define HSSL_HSYNC 0x01 /* 1 : HSYNC */ -#define HSSL_DVALID 0x02 /* 2 : DVALID */ -#define HSSL_HLOCK 0x03 /* 3 : HLOCK */ -#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */ -#define HSSL_ZERO 0x07 /* 7 : 0 */ - -/* ACNTL1 */ -#define SRESET 0x80 /* resets the device to its default state - * but all register content remain unchanged. - * This bit is self-resetting. - */ -#define ACNTL1_PDN_MASK 0x0e -#define CLK_PDN 0x08 /* system clock power down */ -#define Y_PDN 0x04 /* Luma ADC power down */ -#define C_PDN 0x02 /* Chroma ADC power down */ - -/* ACNTL2 */ -#define ACNTL2_PDN_MASK 0x40 -#define PLL_PDN 0x40 /* PLL power down */ - -/* VBICNTL */ - -/* RTSEL : control the real time signal output from the MPOUT pin */ -#define RTSEL_MASK 0x07 -#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */ -#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */ -#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */ -#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */ -#define RTSEL_MONO 0x04 /* 0100 = MONO */ -#define RTSEL_DET50 0x05 /* 0101 = DET50 */ -#define RTSEL_FIELD 0x06 /* 0110 = FIELD */ -#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */ - -/* HSYNC start and end are constant for now */ -#define HSYNC_START 0x0260 -#define HSYNC_END 0x0300 - -/* - * structure - */ - -struct regval_list { - unsigned char reg_num; - unsigned char value; -}; - -struct tw9910_scale_ctrl { - char *name; - unsigned short width; - unsigned short height; - u16 hscale; - u16 vscale; -}; - -struct tw9910_priv { - struct v4l2_subdev subdev; - struct v4l2_clk *clk; - struct tw9910_video_info *info; - const struct tw9910_scale_ctrl *scale; - v4l2_std_id norm; - u32 revision; -}; - -static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = { - { - .name = "NTSC SQ", - .width = 640, - .height = 480, - .hscale = 0x0100, - .vscale = 0x0100, - }, - { - .name = "NTSC CCIR601", - .width = 720, - .height = 480, - .hscale = 0x0100, - .vscale = 0x0100, - }, - { - .name = "NTSC SQ (CIF)", - .width = 320, - .height = 240, - .hscale = 0x0200, - .vscale = 0x0200, - }, - { - .name = "NTSC CCIR601 (CIF)", - .width = 360, - .height = 240, - .hscale = 0x0200, - .vscale = 0x0200, - }, - { - .name = "NTSC SQ (QCIF)", - .width = 160, - .height = 120, - .hscale = 0x0400, - .vscale = 0x0400, - }, - { - .name = "NTSC CCIR601 (QCIF)", - .width = 180, - .height = 120, - .hscale = 0x0400, - .vscale = 0x0400, - }, -}; - -static const struct tw9910_scale_ctrl tw9910_pal_scales[] = { - { - .name = "PAL SQ", - .width = 768, - .height = 576, - .hscale = 0x0100, - .vscale = 0x0100, - }, - { - .name = "PAL CCIR601", - .width = 720, - .height = 576, - .hscale = 0x0100, - .vscale = 0x0100, - }, - { - .name = "PAL SQ (CIF)", - .width = 384, - .height = 288, - .hscale = 0x0200, - .vscale = 0x0200, - }, - { - .name = "PAL CCIR601 (CIF)", - .width = 360, - .height = 288, - .hscale = 0x0200, - .vscale = 0x0200, - }, - { - .name = "PAL SQ (QCIF)", - .width = 192, - .height = 144, - .hscale = 0x0400, - .vscale = 0x0400, - }, - { - .name = "PAL CCIR601 (QCIF)", - .width = 180, - .height = 144, - .hscale = 0x0400, - .vscale = 0x0400, - }, -}; - -/* - * general function - */ -static struct tw9910_priv *to_tw9910(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct tw9910_priv, - subdev); -} - -static int tw9910_mask_set(struct i2c_client *client, u8 command, - u8 mask, u8 set) -{ - s32 val = i2c_smbus_read_byte_data(client, command); - if (val < 0) - return val; - - val &= ~mask; - val |= set & mask; - - return i2c_smbus_write_byte_data(client, command, val); -} - -static int tw9910_set_scale(struct i2c_client *client, - const struct tw9910_scale_ctrl *scale) -{ - int ret; - - ret = i2c_smbus_write_byte_data(client, SCALE_HI, - (scale->vscale & 0x0F00) >> 4 | - (scale->hscale & 0x0F00) >> 8); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, HSCALE_LO, - scale->hscale & 0x00FF); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, VSCALE_LO, - scale->vscale & 0x00FF); - - return ret; -} - -static int tw9910_set_hsync(struct i2c_client *client) -{ - struct tw9910_priv *priv = to_tw9910(client); - int ret; - - /* bit 10 - 3 */ - ret = i2c_smbus_write_byte_data(client, HSBEGIN, - (HSYNC_START & 0x07F8) >> 3); - if (ret < 0) - return ret; - - /* bit 10 - 3 */ - ret = i2c_smbus_write_byte_data(client, HSEND, - (HSYNC_END & 0x07F8) >> 3); - if (ret < 0) - return ret; - - /* So far only revisions 0 and 1 have been seen */ - /* bit 2 - 0 */ - if (1 == priv->revision) - ret = tw9910_mask_set(client, HSLOWCTL, 0x77, - (HSYNC_START & 0x0007) << 4 | - (HSYNC_END & 0x0007)); - - return ret; -} - -static void tw9910_reset(struct i2c_client *client) -{ - tw9910_mask_set(client, ACNTL1, SRESET, SRESET); - msleep(1); -} - -static int tw9910_power(struct i2c_client *client, int enable) -{ - int ret; - u8 acntl1; - u8 acntl2; - - if (enable) { - acntl1 = 0; - acntl2 = 0; - } else { - acntl1 = CLK_PDN | Y_PDN | C_PDN; - acntl2 = PLL_PDN; - } - - ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1); - if (ret < 0) - return ret; - - return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2); -} - -static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm, - u32 width, u32 height) -{ - const struct tw9910_scale_ctrl *scale; - const struct tw9910_scale_ctrl *ret = NULL; - __u32 diff = 0xffffffff, tmp; - int size, i; - - if (norm & V4L2_STD_NTSC) { - scale = tw9910_ntsc_scales; - size = ARRAY_SIZE(tw9910_ntsc_scales); - } else if (norm & V4L2_STD_PAL) { - scale = tw9910_pal_scales; - size = ARRAY_SIZE(tw9910_pal_scales); - } else { - return NULL; - } - - for (i = 0; i < size; i++) { - tmp = abs(width - scale[i].width) + - abs(height - scale[i].height); - if (tmp < diff) { - diff = tmp; - ret = scale + i; - } - } - - return ret; -} - -/* - * subdevice operations - */ -static int tw9910_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - u8 val; - int ret; - - if (!enable) { - switch (priv->revision) { - case 0: - val = OEN_TRI_SEL_ALL_OFF_r0; - break; - case 1: - val = OEN_TRI_SEL_ALL_OFF_r1; - break; - default: - dev_err(&client->dev, "un-supported revision\n"); - return -EINVAL; - } - } else { - val = OEN_TRI_SEL_ALL_ON; - - if (!priv->scale) { - dev_err(&client->dev, "norm select error\n"); - return -EPERM; - } - - dev_dbg(&client->dev, "%s %dx%d\n", - priv->scale->name, - priv->scale->width, - priv->scale->height); - } - - ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val); - if (ret < 0) - return ret; - - return tw9910_power(client, enable); -} - -static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - - *norm = priv->norm; - - return 0; -} - -static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - const unsigned hact = 720; - const unsigned hdelay = 15; - unsigned vact; - unsigned vdelay; - int ret; - - if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL))) - return -EINVAL; - - priv->norm = norm; - if (norm & V4L2_STD_525_60) { - vact = 240; - vdelay = 18; - ret = tw9910_mask_set(client, VVBI, 0x10, 0x10); - } else { - vact = 288; - vdelay = 24; - ret = tw9910_mask_set(client, VVBI, 0x10, 0x00); - } - if (!ret) - ret = i2c_smbus_write_byte_data(client, CROP_HI, - ((vdelay >> 2) & 0xc0) | - ((vact >> 4) & 0x30) | - ((hdelay >> 6) & 0x0c) | - ((hact >> 8) & 0x03)); - if (!ret) - ret = i2c_smbus_write_byte_data(client, VDELAY_LO, - vdelay & 0xff); - if (!ret) - ret = i2c_smbus_write_byte_data(client, VACTIVE_LO, - vact & 0xff); - - return ret; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int tw9910_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 1; - ret = i2c_smbus_read_byte_data(client, reg->reg); - if (ret < 0) - return ret; - - /* - * ret = int - * reg->val = __u64 - */ - reg->val = (__u64)ret; - - return 0; -} - -static int tw9910_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff || - reg->val > 0xff) - return -EINVAL; - - return i2c_smbus_write_byte_data(client, reg->reg, reg->val); -} -#endif - -static int tw9910_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct tw9910_priv *priv = to_tw9910(client); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - int ret = -EINVAL; - u8 val; - - /* - * select suitable norm - */ - priv->scale = tw9910_select_norm(priv->norm, *width, *height); - if (!priv->scale) - goto tw9910_set_fmt_error; - - /* - * reset hardware - */ - tw9910_reset(client); - - /* - * set bus width - */ - val = 0x00; - if (SOCAM_DATAWIDTH_16 == priv->info->buswidth) - val = LEN; - - ret = tw9910_mask_set(client, OPFORM, LEN, val); - if (ret < 0) - goto tw9910_set_fmt_error; - - /* - * select MPOUT behavior - */ - switch (priv->info->mpout) { - case TW9910_MPO_VLOSS: - val = RTSEL_VLOSS; break; - case TW9910_MPO_HLOCK: - val = RTSEL_HLOCK; break; - case TW9910_MPO_SLOCK: - val = RTSEL_SLOCK; break; - case TW9910_MPO_VLOCK: - val = RTSEL_VLOCK; break; - case TW9910_MPO_MONO: - val = RTSEL_MONO; break; - case TW9910_MPO_DET50: - val = RTSEL_DET50; break; - case TW9910_MPO_FIELD: - val = RTSEL_FIELD; break; - case TW9910_MPO_RTCO: - val = RTSEL_RTCO; break; - default: - val = 0; - } - - ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val); - if (ret < 0) - goto tw9910_set_fmt_error; - - /* - * set scale - */ - ret = tw9910_set_scale(client, priv->scale); - if (ret < 0) - goto tw9910_set_fmt_error; - - /* - * set hsync - */ - ret = tw9910_set_hsync(client); - if (ret < 0) - goto tw9910_set_fmt_error; - - *width = priv->scale->width; - *height = priv->scale->height; - - return ret; - -tw9910_set_fmt_error: - - tw9910_reset(client); - priv->scale = NULL; - - return ret; -} - -static int tw9910_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - /* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported */ - if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS) - return -EINVAL; - - sel->r.left = 0; - sel->r.top = 0; - if (priv->norm & V4L2_STD_NTSC) { - sel->r.width = 640; - sel->r.height = 480; - } else { - sel->r.width = 768; - sel->r.height = 576; - } - return 0; -} - -static int tw9910_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - - if (format->pad) - return -EINVAL; - - if (!priv->scale) { - priv->scale = tw9910_select_norm(priv->norm, 640, 480); - if (!priv->scale) - return -EINVAL; - } - - mf->width = priv->scale->width; - mf->height = priv->scale->height; - mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - mf->field = V4L2_FIELD_INTERLACED_BT; - - return 0; -} - -static int tw9910_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - u32 width = mf->width, height = mf->height; - int ret; - - WARN_ON(mf->field != V4L2_FIELD_ANY && - mf->field != V4L2_FIELD_INTERLACED_BT); - - /* - * check color format - */ - if (mf->code != MEDIA_BUS_FMT_UYVY8_2X8) - return -EINVAL; - - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - - ret = tw9910_set_frame(sd, &width, &height); - if (!ret) { - mf->width = width; - mf->height = height; - } - return ret; -} - -static int tw9910_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tw9910_priv *priv = to_tw9910(client); - const struct tw9910_scale_ctrl *scale; - - if (format->pad) - return -EINVAL; - - if (V4L2_FIELD_ANY == mf->field) { - mf->field = V4L2_FIELD_INTERLACED_BT; - } else if (V4L2_FIELD_INTERLACED_BT != mf->field) { - dev_err(&client->dev, "Field type %d invalid.\n", mf->field); - return -EINVAL; - } - - mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - mf->colorspace = V4L2_COLORSPACE_SMPTE170M; - - /* - * select suitable norm - */ - scale = tw9910_select_norm(priv->norm, mf->width, mf->height); - if (!scale) - return -EINVAL; - - mf->width = scale->width; - mf->height = scale->height; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return tw9910_s_fmt(sd, mf); - cfg->try_fmt = *mf; - return 0; -} - -static int tw9910_video_probe(struct i2c_client *client) -{ - struct tw9910_priv *priv = to_tw9910(client); - s32 id; - int ret; - - /* - * tw9910 only use 8 or 16 bit bus width - */ - if (SOCAM_DATAWIDTH_16 != priv->info->buswidth && - SOCAM_DATAWIDTH_8 != priv->info->buswidth) { - dev_err(&client->dev, "bus width error\n"); - return -ENODEV; - } - - ret = tw9910_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show Product ID - * So far only revisions 0 and 1 have been seen - */ - id = i2c_smbus_read_byte_data(client, ID); - priv->revision = GET_REV(id); - id = GET_ID(id); - - if (0x0B != id || - 0x01 < priv->revision) { - dev_err(&client->dev, - "Product ID error %x:%x\n", - id, priv->revision); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, - "tw9910 Product ID %0x:%0x\n", id, priv->revision); - - priv->norm = V4L2_STD_NTSC; - priv->scale = &tw9910_ntsc_scales[0]; - -done: - tw9910_s_power(&priv->subdev, 0); - return ret; -} - -static const struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = tw9910_g_register, - .s_register = tw9910_s_register, -#endif - .s_power = tw9910_s_power, -}; - -static int tw9910_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_UYVY8_2X8; - return 0; -} - -static int tw9910_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int tw9910_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - u8 val = VSSL_VVALID | HSSL_DVALID; - unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); - - /* - * set OUTCTR1 - * - * We use VVALID and DVALID signals to control VSYNC and HSYNC - * outputs, in this mode their polarity is inverted. - */ - if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - val |= HSP_HI; - - if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - val |= VSP_HI; - - return i2c_smbus_write_byte_data(client, OUTCTR1, val); -} - -static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) -{ - *norm = V4L2_STD_NTSC | V4L2_STD_PAL; - return 0; -} - -static const struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { - .s_std = tw9910_s_std, - .g_std = tw9910_g_std, - .s_stream = tw9910_s_stream, - .g_mbus_config = tw9910_g_mbus_config, - .s_mbus_config = tw9910_s_mbus_config, - .g_tvnorms = tw9910_g_tvnorms, -}; - -static const struct v4l2_subdev_pad_ops tw9910_subdev_pad_ops = { - .enum_mbus_code = tw9910_enum_mbus_code, - .get_selection = tw9910_get_selection, - .get_fmt = tw9910_get_fmt, - .set_fmt = tw9910_set_fmt, -}; - -static const struct v4l2_subdev_ops tw9910_subdev_ops = { - .core = &tw9910_subdev_core_ops, - .video = &tw9910_subdev_video_ops, - .pad = &tw9910_subdev_pad_ops, -}; - -/* - * i2c_driver function - */ - -static int tw9910_probe(struct i2c_client *client, - const struct i2c_device_id *did) - -{ - struct tw9910_priv *priv; - struct tw9910_video_info *info; - struct i2c_adapter *adapter = - to_i2c_adapter(client->dev.parent); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd || !ssdd->drv_priv) { - dev_err(&client->dev, "TW9910: missing platform data!\n"); - return -EINVAL; - } - - info = ssdd->drv_priv; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_err(&client->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE_DATA\n"); - return -EIO; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->info = info; - - v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); - - ret = tw9910_video_probe(client); - if (ret < 0) - v4l2_clk_put(priv->clk); - - return ret; -} - -static int tw9910_remove(struct i2c_client *client) -{ - struct tw9910_priv *priv = to_tw9910(client); - v4l2_clk_put(priv->clk); - return 0; -} - -static const struct i2c_device_id tw9910_id[] = { - { "tw9910", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tw9910_id); - -static struct i2c_driver tw9910_i2c_driver = { - .driver = { - .name = "tw9910", - }, - .probe = tw9910_probe, - .remove = tw9910_remove, - .id_table = tw9910_id, -}; - -module_i2c_driver(tw9910_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for tw9910"); -MODULE_AUTHOR("Kuninori Morimoto"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 43a445f188e1a4d58e1be052b23afc9aacccbdd8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:41:23 -0200 Subject: media: sh_mobile_ceu_camera: remove obsolete soc_camera driver This driver got converted to not depend on soc_camera in commit 32e5a70dc8f4 ("media: platform: Add Renesas CEU driver"). There's no sense in keeping the old version there. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 9 - drivers/media/platform/soc_camera/Makefile | 3 - .../platform/soc_camera/sh_mobile_ceu_camera.c | 1810 -------------------- include/media/drv-intf/sh_mobile_ceu.h | 29 - 4 files changed, 1851 deletions(-) delete mode 100644 drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c delete mode 100644 include/media/drv-intf/sh_mobile_ceu.h diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 669d116b8f09..94907d0611d6 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -15,12 +15,3 @@ config SOC_CAMERA_PLATFORM depends on SOC_CAMERA help This is a generic SoC camera platform driver, useful for testing - -config VIDEO_SH_MOBILE_CEU - tristate "SuperH Mobile CEU Interface driver" - depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK - depends on ARCH_SHMOBILE || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select SOC_CAMERA_SCALE_CROP - ---help--- - This is a v4l2 driver for the SuperH Mobile CEU Interface diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 07a451e8b228..aa4b855a0882 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -4,6 +4,3 @@ obj-$(CONFIG_SOC_CAMERA_SCALE_CROP) += soc_scale_crop.o # a platform subdevice driver stub, allowing to support cameras by adding a # couple of callback functions to the board code obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o - -# soc-camera host drivers have to be linked after camera drivers -obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c deleted file mode 100644 index 6803f744e307..000000000000 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ /dev/null @@ -1,1810 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * V4L2 Driver for SuperH Mobile CEU interface - * - * Copyright (C) 2008 Magnus Damm - * - * Based on V4L2 Driver for PXA camera host - "pxa_camera.c", - * - * Copyright (C) 2006, Sascha Hauer, Pengutronix - * Copyright (C) 2008, Guennadi Liakhovetski - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "soc_scale_crop.h" - -/* register offsets for sh7722 / sh7723 */ - -#define CAPSR 0x00 /* Capture start register */ -#define CAPCR 0x04 /* Capture control register */ -#define CAMCR 0x08 /* Capture interface control register */ -#define CMCYR 0x0c /* Capture interface cycle register */ -#define CAMOR 0x10 /* Capture interface offset register */ -#define CAPWR 0x14 /* Capture interface width register */ -#define CAIFR 0x18 /* Capture interface input format register */ -#define CSTCR 0x20 /* Camera strobe control register (<= sh7722) */ -#define CSECR 0x24 /* Camera strobe emission count register (<= sh7722) */ -#define CRCNTR 0x28 /* CEU register control register */ -#define CRCMPR 0x2c /* CEU register forcible control register */ -#define CFLCR 0x30 /* Capture filter control register */ -#define CFSZR 0x34 /* Capture filter size clip register */ -#define CDWDR 0x38 /* Capture destination width register */ -#define CDAYR 0x3c /* Capture data address Y register */ -#define CDACR 0x40 /* Capture data address C register */ -#define CDBYR 0x44 /* Capture data bottom-field address Y register */ -#define CDBCR 0x48 /* Capture data bottom-field address C register */ -#define CBDSR 0x4c /* Capture bundle destination size register */ -#define CFWCR 0x5c /* Firewall operation control register */ -#define CLFCR 0x60 /* Capture low-pass filter control register */ -#define CDOCR 0x64 /* Capture data output control register */ -#define CDDCR 0x68 /* Capture data complexity level register */ -#define CDDAR 0x6c /* Capture data complexity level address register */ -#define CEIER 0x70 /* Capture event interrupt enable register */ -#define CETCR 0x74 /* Capture event flag clear register */ -#define CSTSR 0x7c /* Capture status register */ -#define CSRTR 0x80 /* Capture software reset register */ -#define CDSSR 0x84 /* Capture data size register */ -#define CDAYR2 0x90 /* Capture data address Y register 2 */ -#define CDACR2 0x94 /* Capture data address C register 2 */ -#define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */ -#define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */ - -#undef DEBUG_GEOMETRY -#ifdef DEBUG_GEOMETRY -#define dev_geo dev_info -#else -#define dev_geo dev_dbg -#endif - -/* per video frame buffer */ -struct sh_mobile_ceu_buffer { - struct vb2_v4l2_buffer vb; /* v4l buffer must be first */ - struct list_head queue; -}; - -struct sh_mobile_ceu_dev { - struct soc_camera_host ici; - - unsigned int irq; - void __iomem *base; - size_t video_limit; - size_t buf_total; - - spinlock_t lock; /* Protects video buffer lists */ - struct list_head capture; - struct vb2_v4l2_buffer *active; - - struct sh_mobile_ceu_info *pdata; - struct completion complete; - - u32 cflcr; - - /* static max sizes either from platform data or default */ - int max_width; - int max_height; - - enum v4l2_field field; - int sequence; - unsigned long flags; - - unsigned int image_mode:1; - unsigned int is_16bit:1; - unsigned int frozen:1; -}; - -struct sh_mobile_ceu_cam { - /* CEU offsets within the camera output, before the CEU scaler */ - unsigned int ceu_left; - unsigned int ceu_top; - /* Client output, as seen by the CEU */ - unsigned int width; - unsigned int height; - /* - * User window from S_SELECTION / G_SELECTION, produced by client cropping and - * scaling, CEU scaling and CEU cropping, mapped back onto the client - * input window - */ - struct v4l2_rect subrect; - /* Camera cropping rectangle */ - struct v4l2_rect rect; - const struct soc_mbus_pixelfmt *extra_fmt; - u32 code; -}; - -static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_v4l2_buffer *vbuf) -{ - return container_of(vbuf, struct sh_mobile_ceu_buffer, vb); -} - -static void ceu_write(struct sh_mobile_ceu_dev *priv, - unsigned long reg_offs, u32 data) -{ - iowrite32(data, priv->base + reg_offs); -} - -static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs) -{ - return ioread32(priv->base + reg_offs); -} - -static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) -{ - int i, success = 0; - - ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ - - /* wait CSTSR.CPTON bit */ - for (i = 0; i < 1000; i++) { - if (!(ceu_read(pcdev, CSTSR) & 1)) { - success++; - break; - } - udelay(1); - } - - /* wait CAPSR.CPKIL bit */ - for (i = 0; i < 1000; i++) { - if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) { - success++; - break; - } - udelay(1); - } - - if (2 != success) { - dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n"); - return -EIO; - } - - return 0; -} - -/* - * Videobuf operations - */ - -/* - * .queue_setup() is called to check, whether the driver can accept the - * requested number of buffers and to fill in plane sizes - * for the current frame format if required - */ -static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, - unsigned int *count, unsigned int *num_planes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - if (!vq->num_buffers) - pcdev->sequence = 0; - - if (!*count) - *count = 2; - - /* Called from VIDIOC_REQBUFS or in compatibility mode */ - if (!*num_planes) - sizes[0] = icd->sizeimage; - else if (sizes[0] < icd->sizeimage) - return -EINVAL; - - /* If *num_planes != 0, we have already verified *count. */ - if (pcdev->video_limit) { - size_t size = PAGE_ALIGN(sizes[0]) * *count; - - if (size + pcdev->buf_total > pcdev->video_limit) - *count = (pcdev->video_limit - pcdev->buf_total) / - PAGE_ALIGN(sizes[0]); - } - - *num_planes = 1; - - dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]); - - return 0; -} - -#define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */ -#define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */ -#define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */ -#define CEU_CEIER_VBP (1 << 20) /* vbp error */ -#define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */ -#define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP) - - -/* - * return value doesn't reflex the success/failure to queue the new buffer, - * but rather the status of the previous buffer. - */ -static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) -{ - struct soc_camera_device *icd = pcdev->ici.icd; - dma_addr_t phys_addr_top, phys_addr_bottom; - unsigned long top1, top2; - unsigned long bottom1, bottom2; - u32 status; - bool planar; - int ret = 0; - - /* - * The hardware is _very_ picky about this sequence. Especially - * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge - * several not-so-well documented interrupt sources in CETCR. - */ - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK); - status = ceu_read(pcdev, CETCR); - ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC); - if (!pcdev->frozen) - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK); - ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); - ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); - - /* - * When a VBP interrupt occurs, a capture end interrupt does not occur - * and the image of that frame is not captured correctly. So, soft reset - * is needed here. - */ - if (status & CEU_CEIER_VBP) { - sh_mobile_ceu_soft_reset(pcdev); - ret = -EIO; - } - - if (pcdev->frozen) { - complete(&pcdev->complete); - return ret; - } - - if (!pcdev->active) - return ret; - - if (V4L2_FIELD_INTERLACED_BT == pcdev->field) { - top1 = CDBYR; - top2 = CDBCR; - bottom1 = CDAYR; - bottom2 = CDACR; - } else { - top1 = CDAYR; - top2 = CDACR; - bottom1 = CDBYR; - bottom2 = CDBCR; - } - - phys_addr_top = - vb2_dma_contig_plane_dma_addr(&pcdev->active->vb2_buf, 0); - - switch (icd->current_fmt->host_fmt->fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - planar = true; - break; - default: - planar = false; - } - - ceu_write(pcdev, top1, phys_addr_top); - if (V4L2_FIELD_NONE != pcdev->field) { - phys_addr_bottom = phys_addr_top + icd->bytesperline; - ceu_write(pcdev, bottom1, phys_addr_bottom); - } - - if (planar) { - phys_addr_top += icd->bytesperline * icd->user_height; - ceu_write(pcdev, top2, phys_addr_top); - if (V4L2_FIELD_NONE != pcdev->field) { - phys_addr_bottom = phys_addr_top + icd->bytesperline; - ceu_write(pcdev, bottom2, phys_addr_bottom); - } - } - - ceu_write(pcdev, CAPSR, 0x1); /* start capture */ - - return ret; -} - -static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf); - - /* Added list head initialization on alloc */ - WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb); - - return 0; -} - -static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf); - unsigned long size; - - size = icd->sizeimage; - - if (vb2_plane_size(vb, 0) < size) { - dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n", - vb->index, vb2_plane_size(vb, 0), size); - goto error; - } - - vb2_set_plane_payload(vb, 0, size); - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - -#ifdef DEBUG - /* - * This can be useful if you want to see if we actually fill - * the buffer with something - */ - if (vb2_plane_vaddr(vb, 0)) - memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0)); -#endif - - spin_lock_irq(&pcdev->lock); - list_add_tail(&buf->queue, &pcdev->capture); - - if (!pcdev->active) { - /* - * Because there were no active buffer at this moment, - * we are not interested in the return value of - * sh_mobile_ceu_capture here. - */ - pcdev->active = vbuf; - sh_mobile_ceu_capture(pcdev); - } - spin_unlock_irq(&pcdev->lock); - - return; - -error: - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -} - -static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - spin_lock_irq(&pcdev->lock); - - if (pcdev->active == vbuf) { - /* disable capture (release DMA buffer), reset */ - ceu_write(pcdev, CAPSR, 1 << 16); - pcdev->active = NULL; - } - - /* - * Doesn't hurt also if the list is empty, but it hurts, if queuing the - * buffer failed, and .buf_init() hasn't been called - */ - if (buf->queue.next) - list_del_init(&buf->queue); - - pcdev->buf_total -= PAGE_ALIGN(vb2_plane_size(vb, 0)); - dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__, - pcdev->buf_total); - - spin_unlock_irq(&pcdev->lock); -} - -static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - pcdev->buf_total += PAGE_ALIGN(vb2_plane_size(vb, 0)); - dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__, - pcdev->buf_total); - - /* This is for locking debugging only */ - INIT_LIST_HEAD(&to_ceu_vb(vbuf)->queue); - return 0; -} - -static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct list_head *buf_head, *tmp; - struct vb2_v4l2_buffer *vbuf; - - spin_lock_irq(&pcdev->lock); - - pcdev->active = NULL; - - list_for_each_safe(buf_head, tmp, &pcdev->capture) { - vbuf = &list_entry(buf_head, struct sh_mobile_ceu_buffer, - queue)->vb; - vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); - list_del_init(buf_head); - } - - spin_unlock_irq(&pcdev->lock); - - sh_mobile_ceu_soft_reset(pcdev); -} - -static const struct vb2_ops sh_mobile_ceu_videobuf_ops = { - .queue_setup = sh_mobile_ceu_videobuf_setup, - .buf_prepare = sh_mobile_ceu_videobuf_prepare, - .buf_queue = sh_mobile_ceu_videobuf_queue, - .buf_cleanup = sh_mobile_ceu_videobuf_release, - .buf_init = sh_mobile_ceu_videobuf_init, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .stop_streaming = sh_mobile_ceu_stop_streaming, -}; - -static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) -{ - struct sh_mobile_ceu_dev *pcdev = data; - struct vb2_v4l2_buffer *vbuf; - int ret; - - spin_lock(&pcdev->lock); - - vbuf = pcdev->active; - if (!vbuf) - /* Stale interrupt from a released buffer */ - goto out; - - list_del_init(&to_ceu_vb(vbuf)->queue); - - if (!list_empty(&pcdev->capture)) - pcdev->active = &list_entry(pcdev->capture.next, - struct sh_mobile_ceu_buffer, queue)->vb; - else - pcdev->active = NULL; - - ret = sh_mobile_ceu_capture(pcdev); - vbuf->vb2_buf.timestamp = ktime_get_ns(); - if (!ret) { - vbuf->field = pcdev->field; - vbuf->sequence = pcdev->sequence++; - } - vb2_buffer_done(&vbuf->vb2_buf, - ret < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - -out: - spin_unlock(&pcdev->lock); - - return IRQ_HANDLED; -} - -static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, - "SuperH Mobile CEU driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, - "SuperH Mobile CEU driver detached from camera %d\n", - icd->devnum); -} - -/* Called with .host_lock held */ -static int sh_mobile_ceu_clock_start(struct soc_camera_host *ici) -{ - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - pm_runtime_get_sync(ici->v4l2_dev.dev); - - pcdev->buf_total = 0; - - sh_mobile_ceu_soft_reset(pcdev); - - return 0; -} - -/* Called with .host_lock held */ -static void sh_mobile_ceu_clock_stop(struct soc_camera_host *ici) -{ - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - /* disable capture, disable interrupts */ - ceu_write(pcdev, CEIER, 0); - sh_mobile_ceu_soft_reset(pcdev); - - /* make sure active buffer is canceled */ - spin_lock_irq(&pcdev->lock); - if (pcdev->active) { - list_del_init(&to_ceu_vb(pcdev->active)->queue); - vb2_buffer_done(&pcdev->active->vb2_buf, VB2_BUF_STATE_ERROR); - pcdev->active = NULL; - } - spin_unlock_irq(&pcdev->lock); - - pm_runtime_put(ici->v4l2_dev.dev); -} - -/* - * See chapter 29.4.12 "Capture Filter Control Register (CFLCR)" - * in SH7722 Hardware Manual - */ -static unsigned int size_dst(unsigned int src, unsigned int scale) -{ - unsigned int mant_pre = scale >> 12; - if (!src || !scale) - return src; - return ((mant_pre + 2 * (src - 1)) / (2 * mant_pre) - 1) * - mant_pre * 4096 / scale + 1; -} - -static u16 calc_scale(unsigned int src, unsigned int *dst) -{ - u16 scale; - - if (src == *dst) - return 0; - - scale = (src * 4096 / *dst) & ~7; - - while (scale > 4096 && size_dst(src, scale) < *dst) - scale -= 8; - - *dst = size_dst(src, scale); - - return scale; -} - -/* rect is guaranteed to not exceed the scaled camera rectangle */ -static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct sh_mobile_ceu_dev *pcdev = ici->priv; - unsigned int height, width, cdwdr_width, in_width, in_height; - unsigned int left_offset, top_offset; - u32 camor; - - dev_geo(icd->parent, "Crop %ux%u@%u:%u\n", - icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top); - - left_offset = cam->ceu_left; - top_offset = cam->ceu_top; - - WARN_ON(icd->user_width & 3 || icd->user_height & 3); - - width = icd->user_width; - - if (pcdev->image_mode) { - in_width = cam->width; - if (!pcdev->is_16bit) { - in_width *= 2; - left_offset *= 2; - } - } else { - unsigned int w_factor; - - switch (icd->current_fmt->host_fmt->packing) { - case SOC_MBUS_PACKING_2X8_PADHI: - w_factor = 2; - break; - default: - w_factor = 1; - } - - in_width = cam->width * w_factor; - left_offset *= w_factor; - } - - cdwdr_width = icd->bytesperline; - - height = icd->user_height; - in_height = cam->height; - if (V4L2_FIELD_NONE != pcdev->field) { - height = (height / 2) & ~3; - in_height /= 2; - top_offset /= 2; - cdwdr_width *= 2; - } - - /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ - camor = left_offset | (top_offset << 16); - - dev_geo(icd->parent, - "CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor, - (in_height << 16) | in_width, (height << 16) | width, - cdwdr_width); - - ceu_write(pcdev, CAMOR, camor); - ceu_write(pcdev, CAPWR, (in_height << 16) | in_width); - /* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */ - ceu_write(pcdev, CFSZR, (height << 16) | width); - ceu_write(pcdev, CDWDR, cdwdr_width); -} - -static u32 capture_save_reset(struct sh_mobile_ceu_dev *pcdev) -{ - u32 capsr = ceu_read(pcdev, CAPSR); - ceu_write(pcdev, CAPSR, 1 << 16); /* reset, stop capture */ - return capsr; -} - -static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr) -{ - unsigned long timeout = jiffies + 10 * HZ; - - /* - * Wait until the end of the current frame. It can take a long time, - * but if it has been aborted by a CAPSR reset, it shoule exit sooner. - */ - while ((ceu_read(pcdev, CSTSR) & 1) && time_before(jiffies, timeout)) - msleep(1); - - if (time_after(jiffies, timeout)) { - dev_err(pcdev->ici.v4l2_dev.dev, - "Timeout waiting for frame end! Interface problem?\n"); - return; - } - - /* Wait until reset clears, this shall not hang... */ - while (ceu_read(pcdev, CAPSR) & (1 << 16)) - udelay(10); - - /* Anything to restore? */ - if (capsr & ~(1 << 16)) - ceu_write(pcdev, CAPSR, capsr); -} - -#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_HSYNC_ACTIVE_LOW | \ - V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_VSYNC_ACTIVE_LOW | \ - V4L2_MBUS_DATA_ACTIVE_HIGH) - -/* Capture is not running, no interrupts, no locking needed */ -static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long value, common_flags = CEU_BUS_FLAGS; - u32 capsr = capture_save_reset(pcdev); - unsigned int yuv_lineskip; - int ret; - - /* - * If the client doesn't implement g_mbus_config, we just use our - * platform data - */ - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, - common_flags); - if (!common_flags) - return -EINVAL; - } else if (ret != -ENOIOCTLCMD) { - return ret; - } - - /* Make choises, based on platform preferences */ - if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (pcdev->flags & SH_CEU_FLAG_HSYNC_LOW) - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { - if (pcdev->flags & SH_CEU_FLAG_VSYNC_LOW) - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - - if (icd->current_fmt->host_fmt->bits_per_sample > 8) - pcdev->is_16bit = 1; - else - pcdev->is_16bit = 0; - - ceu_write(pcdev, CRCNTR, 0); - ceu_write(pcdev, CRCMPR, 0); - - value = 0x00000010; /* data fetch by default */ - yuv_lineskip = 0x10; - - switch (icd->current_fmt->host_fmt->fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - /* convert 4:2:2 -> 4:2:0 */ - yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */ - /* fall-through */ - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - switch (cam->code) { - case MEDIA_BUS_FMT_UYVY8_2X8: - value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */ - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */ - break; - case MEDIA_BUS_FMT_YUYV8_2X8: - value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */ - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */ - break; - default: - BUG(); - } - } - - if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV21 || - icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61) - value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */ - - value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; - value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; - - if (pcdev->is_16bit) - value |= 1 << 12; - else if (pcdev->flags & SH_CEU_FLAG_LOWER_8BIT) - value |= 2 << 12; - - ceu_write(pcdev, CAMCR, value); - - ceu_write(pcdev, CAPCR, 0x00300000); - - switch (pcdev->field) { - case V4L2_FIELD_INTERLACED_TB: - value = 0x101; - break; - case V4L2_FIELD_INTERLACED_BT: - value = 0x102; - break; - default: - value = 0; - break; - } - ceu_write(pcdev, CAIFR, value); - - sh_mobile_ceu_set_rect(icd); - mdelay(1); - - dev_geo(icd->parent, "CFLCR 0x%x\n", pcdev->cflcr); - ceu_write(pcdev, CFLCR, pcdev->cflcr); - - /* - * A few words about byte order (observed in Big Endian mode) - * - * In data fetch mode bytes are received in chunks of 8 bytes. - * D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first) - * - * The data is however by default written to memory in reverse order: - * D7, D6, D5, D4, D3, D2, D1, D0 (D7 written to lowest byte) - * - * The lowest three bits of CDOCR allows us to do swapping, - * using 7 we swap the data bytes to match the incoming order: - * D0, D1, D2, D3, D4, D5, D6, D7 - */ - value = 0x00000007 | yuv_lineskip; - - ceu_write(pcdev, CDOCR, value); - ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ - - capture_restore(pcdev, capsr); - - /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */ - return 0; -} - -static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, - unsigned char buswidth) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - unsigned long common_flags = CEU_BUS_FLAGS; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - int ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) - common_flags = soc_mbus_config_compatible(&cfg, - common_flags); - else if (ret != -ENOIOCTLCMD) - return ret; - - if (!common_flags || buswidth > 16) - return -EINVAL; - - return 0; -} - -static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .name = "NV12", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C, - }, { - .fourcc = V4L2_PIX_FMT_NV21, - .name = "NV21", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C, - }, { - .fourcc = V4L2_PIX_FMT_NV16, - .name = "NV16", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, - }, { - .fourcc = V4L2_PIX_FMT_NV61, - .name = "NV61", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, - }, -}; - -/* This will be corrected as we get more formats */ -static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt) -{ - return fmt->packing == SOC_MBUS_PACKING_NONE || - (fmt->bits_per_sample == 8 && - fmt->packing == SOC_MBUS_PACKING_1_5X8) || - (fmt->bits_per_sample == 8 && - fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) || - (fmt->bits_per_sample > 8 && - fmt->packing == SOC_MBUS_PACKING_EXTEND16); -} - -static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl) -{ - return container_of(ctrl->handler, struct soc_camera_device, - ctrl_handler); -} - -static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct soc_camera_device *icd = ctrl_to_icd(ctrl); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - switch (ctrl->id) { - case V4L2_CID_SHARPNESS: - switch (icd->current_fmt->host_fmt->fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - ceu_write(pcdev, CLFCR, !ctrl->val); - return 0; - } - break; - } - - return -EINVAL; -} - -static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = { - .s_ctrl = sh_mobile_ceu_s_ctrl, -}; - -static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx, - struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - int ret, k, n; - int formats = 0; - struct sh_mobile_ceu_cam *cam; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - const struct soc_mbus_pixelfmt *fmt; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - /* No more formats */ - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_warn(dev, "unsupported format code #%u: %d\n", idx, code.code); - return 0; - } - - ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); - if (ret < 0) - return 0; - - if (!icd->host_priv) { - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - struct v4l2_rect rect; - int shift = 0; - - /* Add our control */ - v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 1, 1, 1); - if (icd->ctrl_handler.error) - return icd->ctrl_handler.error; - - /* FIXME: subwindow is lost between close / open */ - - /* Cache current client geometry */ - ret = soc_camera_client_g_rect(sd, &rect); - if (ret < 0) - return ret; - - /* First time */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - /* - * All currently existing CEU implementations support 2560x1920 - * or larger frames. If the sensor is proposing too big a frame, - * don't bother with possibly supportred by the CEU larger - * sizes, just try VGA multiples. If needed, this can be - * adjusted in the future. - */ - while ((mf->width > pcdev->max_width || - mf->height > pcdev->max_height) && shift < 4) { - /* Try 2560x1920, 1280x960, 640x480, 320x240 */ - mf->width = 2560 >> shift; - mf->height = 1920 >> shift; - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), pad, - set_fmt, NULL, &fmt); - if (ret < 0) - return ret; - shift++; - } - - if (shift == 4) { - dev_err(dev, "Failed to configure the client below %ux%x\n", - mf->width, mf->height); - return -EIO; - } - - dev_geo(dev, "camera fmt %ux%u\n", mf->width, mf->height); - - cam = kzalloc(sizeof(*cam), GFP_KERNEL); - if (!cam) - return -ENOMEM; - - /* We are called with current camera crop, initialise subrect with it */ - cam->rect = rect; - cam->subrect = rect; - - cam->width = mf->width; - cam->height = mf->height; - - icd->host_priv = cam; - } else { - cam = icd->host_priv; - } - - /* Beginning of a pass */ - if (!idx) - cam->extra_fmt = NULL; - - switch (code.code) { - case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_VYUY8_2X8: - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YVYU8_2X8: - if (cam->extra_fmt) - break; - - /* - * Our case is simple so far: for any of the above four camera - * formats we add all our four synthesized NV* formats, so, - * just marking the device with a single flag suffices. If - * the format generation rules are more complex, you would have - * to actually hang your already added / counted formats onto - * the host_priv pointer and check whether the format you're - * going to add now is already there. - */ - cam->extra_fmt = sh_mobile_ceu_formats; - - n = ARRAY_SIZE(sh_mobile_ceu_formats); - formats += n; - for (k = 0; xlate && k < n; k++) { - xlate->host_fmt = &sh_mobile_ceu_formats[k]; - xlate->code = code.code; - xlate++; - dev_dbg(dev, "Providing format %s using code %d\n", - sh_mobile_ceu_formats[k].name, code.code); - } - break; - default: - if (!sh_mobile_ceu_packing_supported(fmt)) - return 0; - } - - /* Generic pass-through */ - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - xlate++; - dev_dbg(dev, "Providing format %s in pass-through mode\n", - fmt->name); - } - - return formats; -} - -static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd) -{ - kfree(icd->host_priv); - icd->host_priv = NULL; -} - -#define scale_down(size, scale) soc_camera_shift_scale(size, 12, scale) -#define calc_generic_scale(in, out) soc_camera_calc_scale(in, 12, out) - -/* - * CEU can scale and crop, but we don't want to waste bandwidth and kill the - * framerate by always requesting the maximum image from the client. See - * Documentation/media/v4l-drivers/sh_mobile_ceu_camera.rst for a description of - * scaling and cropping algorithms and for the meaning of referenced here steps. - */ -static int sh_mobile_ceu_set_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_rect *rect = &sel->r; - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_selection cam_sel; - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_rect *cam_rect = &cam_sel.r; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v, - out_width, out_height; - int interm_width, interm_height; - u32 capsr, cflcr; - int ret; - - dev_geo(dev, "S_SELECTION(%ux%u@%u:%u)\n", rect->width, rect->height, - rect->left, rect->top); - - /* During camera cropping its output window can change too, stop CEU */ - capsr = capture_save_reset(pcdev); - dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); - - /* - * 1. - 2. Apply iterative camera S_SELECTION for new input window, read back - * actual camera rectangle. - */ - ret = soc_camera_client_s_selection(sd, sel, &cam_sel, - &cam->rect, &cam->subrect); - if (ret < 0) - return ret; - - dev_geo(dev, "1-2: camera cropped to %ux%u@%u:%u\n", - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top); - - /* On success cam_crop contains current camera crop */ - - /* 3. Retrieve camera output window */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - if (mf->width > pcdev->max_width || mf->height > pcdev->max_height) - return -EINVAL; - - /* 4. Calculate camera scales */ - scale_cam_h = calc_generic_scale(cam_rect->width, mf->width); - scale_cam_v = calc_generic_scale(cam_rect->height, mf->height); - - /* Calculate intermediate window */ - interm_width = scale_down(rect->width, scale_cam_h); - interm_height = scale_down(rect->height, scale_cam_v); - - if (interm_width < icd->user_width) { - u32 new_scale_h; - - new_scale_h = calc_generic_scale(rect->width, icd->user_width); - - mf->width = scale_down(cam_rect->width, new_scale_h); - } - - if (interm_height < icd->user_height) { - u32 new_scale_v; - - new_scale_v = calc_generic_scale(rect->height, icd->user_height); - - mf->height = scale_down(cam_rect->height, new_scale_v); - } - - if (interm_width < icd->user_width || interm_height < icd->user_height) { - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), pad, - set_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - dev_geo(dev, "New camera output %ux%u\n", mf->width, mf->height); - scale_cam_h = calc_generic_scale(cam_rect->width, mf->width); - scale_cam_v = calc_generic_scale(cam_rect->height, mf->height); - interm_width = scale_down(rect->width, scale_cam_h); - interm_height = scale_down(rect->height, scale_cam_v); - } - - /* Cache camera output window */ - cam->width = mf->width; - cam->height = mf->height; - - if (pcdev->image_mode) { - out_width = min(interm_width, icd->user_width); - out_height = min(interm_height, icd->user_height); - } else { - out_width = interm_width; - out_height = interm_height; - } - - /* - * 5. Calculate CEU scales from camera scales from results of (5) and - * the user window - */ - scale_ceu_h = calc_scale(interm_width, &out_width); - scale_ceu_v = calc_scale(interm_height, &out_height); - - dev_geo(dev, "5: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v); - - /* Apply CEU scales. */ - cflcr = scale_ceu_h | (scale_ceu_v << 16); - if (cflcr != pcdev->cflcr) { - pcdev->cflcr = cflcr; - ceu_write(pcdev, CFLCR, cflcr); - } - - icd->user_width = out_width & ~3; - icd->user_height = out_height & ~3; - /* Offsets are applied at the CEU scaling filter input */ - cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1; - cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1; - - /* 6. Use CEU cropping to crop to the new window. */ - sh_mobile_ceu_set_rect(icd); - - cam->subrect = *rect; - - dev_geo(dev, "6: CEU cropped to %ux%u@%u:%u\n", - icd->user_width, icd->user_height, - cam->ceu_left, cam->ceu_top); - - /* Restore capture. The CE bit can be cleared by the hardware */ - if (pcdev->active) - capsr |= 1; - capture_restore(pcdev, capsr); - - /* Even if only camera cropping succeeded */ - return ret; -} - -static int sh_mobile_ceu_get_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct sh_mobile_ceu_cam *cam = icd->host_priv; - - sel->r = cam->subrect; - - return 0; -} - -/* Similar to set_crop multistage iterative algorithm */ -static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_mbus_framefmt mf; - __u32 pixfmt = pix->pixelformat; - const struct soc_camera_format_xlate *xlate; - unsigned int ceu_sub_width = pcdev->max_width, - ceu_sub_height = pcdev->max_height; - u16 scale_v, scale_h; - int ret; - bool image_mode; - enum v4l2_field field; - - switch (pix->field) { - default: - pix->field = V4L2_FIELD_NONE; - /* fall-through */ - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - case V4L2_FIELD_NONE: - field = pix->field; - break; - case V4L2_FIELD_INTERLACED: - field = V4L2_FIELD_INTERLACED_TB; - break; - } - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - dev_warn(dev, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - /* 1.-4. Calculate desired client output geometry */ - soc_camera_calc_client_output(icd, &cam->rect, &cam->subrect, pix, &mf, 12); - mf.field = pix->field; - mf.colorspace = pix->colorspace; - mf.code = xlate->code; - - switch (pixfmt) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - image_mode = true; - break; - default: - image_mode = false; - } - - dev_geo(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code, - pix->width, pix->height); - - dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height); - - /* 5. - 9. */ - ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect, - &mf, &ceu_sub_width, &ceu_sub_height, - image_mode && V4L2_FIELD_NONE == field, 12); - - dev_geo(dev, "5-9: client scale return %d\n", ret); - - /* Done with the camera. Now see if we can improve the result */ - - dev_geo(dev, "fmt %ux%u, requested %ux%u\n", - mf.width, mf.height, pix->width, pix->height); - if (ret < 0) - return ret; - - if (mf.code != xlate->code) - return -EINVAL; - - /* 9. Prepare CEU crop */ - cam->width = mf.width; - cam->height = mf.height; - - /* 10. Use CEU scaling to scale to the requested user window. */ - - /* We cannot scale up */ - if (pix->width > ceu_sub_width) - ceu_sub_width = pix->width; - - if (pix->height > ceu_sub_height) - ceu_sub_height = pix->height; - - pix->colorspace = mf.colorspace; - - if (image_mode) { - /* Scale pix->{width x height} down to width x height */ - scale_h = calc_scale(ceu_sub_width, &pix->width); - scale_v = calc_scale(ceu_sub_height, &pix->height); - } else { - pix->width = ceu_sub_width; - pix->height = ceu_sub_height; - scale_h = 0; - scale_v = 0; - } - - pcdev->cflcr = scale_h | (scale_v << 16); - - /* - * We have calculated CFLCR, the actual configuration will be performed - * in sh_mobile_ceu_set_bus_param() - */ - - dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n", - ceu_sub_width, scale_h, pix->width, - ceu_sub_height, scale_v, pix->height); - - cam->code = xlate->code; - icd->current_fmt = xlate; - - pcdev->field = field; - pcdev->image_mode = image_mode; - - /* CFSZR requirement */ - pix->width &= ~3; - pix->height &= ~3; - - return 0; -} - -#define CEU_CHDW_MAX 8188U /* Maximum line stride */ - -static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - __u32 pixfmt = pix->pixelformat; - int width, height; - int ret; - - dev_geo(icd->parent, "TRY_FMT(pix=0x%x, %ux%u)\n", - pixfmt, pix->width, pix->height); - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - xlate = icd->current_fmt; - dev_dbg(icd->parent, "Format %x not found, keeping %x\n", - pixfmt, xlate->host_fmt->fourcc); - pixfmt = xlate->host_fmt->fourcc; - pix->pixelformat = pixfmt; - pix->colorspace = icd->colorspace; - } - - /* FIXME: calculate using depth and bus width */ - - /* CFSZR requires height and width to be 4-pixel aligned */ - v4l_bound_align_image(&pix->width, 2, pcdev->max_width, 2, - &pix->height, 4, pcdev->max_height, 2, 0); - - width = pix->width; - height = pix->height; - - /* limit to sensor capabilities */ - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->code = xlate->code; - mf->colorspace = pix->colorspace; - - ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), - pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - - switch (pixfmt) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - /* FIXME: check against rect_max after converting soc-camera */ - /* We can scale precisely, need a bigger image from camera */ - if (pix->width < width || pix->height < height) { - /* - * We presume, the sensor behaves sanely, i.e., if - * requested a bigger rectangle, it will not return a - * smaller one. - */ - mf->width = pcdev->max_width; - mf->height = pcdev->max_height; - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), pad, - set_fmt, &pad_cfg, &format); - if (ret < 0) { - /* Shouldn't actually happen... */ - dev_err(icd->parent, - "FIXME: client try_fmt() = %d\n", ret); - return ret; - } - } - /* We will scale exactly */ - if (mf->width > width) - pix->width = width; - if (mf->height > height) - pix->height = height; - - pix->bytesperline = max(pix->bytesperline, pix->width); - pix->bytesperline = min(pix->bytesperline, CEU_CHDW_MAX); - pix->bytesperline &= ~3; - break; - - default: - /* Configurable stride isn't supported in pass-through mode. */ - pix->bytesperline = 0; - } - - pix->width &= ~3; - pix->height &= ~3; - pix->sizeimage = 0; - - dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n", - __func__, ret, pix->pixelformat, pix->width, pix->height); - - return ret; -} - -static int sh_mobile_ceu_set_liveselection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - u32 out_width = icd->user_width, out_height = icd->user_height; - int ret; - - /* Freeze queue */ - pcdev->frozen = 1; - /* Wait for frame */ - ret = wait_for_completion_interruptible(&pcdev->complete); - /* Stop the client */ - ret = v4l2_subdev_call(sd, video, s_stream, 0); - if (ret < 0) - dev_warn(icd->parent, - "Client failed to stop the stream: %d\n", ret); - else - /* Do the crop, if it fails, there's nothing more we can do */ - sh_mobile_ceu_set_selection(icd, sel); - - dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height); - - if (icd->user_width != out_width || icd->user_height != out_height) { - struct v4l2_format f = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt.pix = { - .width = out_width, - .height = out_height, - .pixelformat = icd->current_fmt->host_fmt->fourcc, - .field = pcdev->field, - .colorspace = icd->colorspace, - }, - }; - ret = sh_mobile_ceu_set_fmt(icd, &f); - if (!ret && (out_width != f.fmt.pix.width || - out_height != f.fmt.pix.height)) - ret = -EINVAL; - if (!ret) { - icd->user_width = out_width & ~3; - icd->user_height = out_height & ~3; - ret = sh_mobile_ceu_set_bus_param(icd); - } - } - - /* Thaw the queue */ - pcdev->frozen = 0; - spin_lock_irq(&pcdev->lock); - sh_mobile_ceu_capture(pcdev); - spin_unlock_irq(&pcdev->lock); - /* Start the client */ - ret = v4l2_subdev_call(sd, video, s_stream, 1); - return ret; -} - -static __poll_t sh_mobile_ceu_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_poll(&icd->vb2_vidq, file, pt); -} - -static int sh_mobile_ceu_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - strscpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card)); - strscpy(cap->driver, "sh_mobile_ceu", sizeof(cap->driver)); - strscpy(cap->bus_info, "platform:sh_mobile_ceu", sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->drv_priv = icd; - q->ops = &sh_mobile_ceu_videobuf_ops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ici->host_lock; - q->dev = ici->v4l2_dev.dev; - - return vb2_queue_init(q); -} - -static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { - .owner = THIS_MODULE, - .add = sh_mobile_ceu_add_device, - .remove = sh_mobile_ceu_remove_device, - .clock_start = sh_mobile_ceu_clock_start, - .clock_stop = sh_mobile_ceu_clock_stop, - .get_formats = sh_mobile_ceu_get_formats, - .put_formats = sh_mobile_ceu_put_formats, - .get_selection = sh_mobile_ceu_get_selection, - .set_selection = sh_mobile_ceu_set_selection, - .set_liveselection = sh_mobile_ceu_set_liveselection, - .set_fmt = sh_mobile_ceu_set_fmt, - .try_fmt = sh_mobile_ceu_try_fmt, - .poll = sh_mobile_ceu_poll, - .querycap = sh_mobile_ceu_querycap, - .set_bus_param = sh_mobile_ceu_set_bus_param, - .init_videobuf2 = sh_mobile_ceu_init_videobuf, -}; - -struct bus_wait { - struct notifier_block notifier; - struct completion completion; - struct device *dev; -}; - -static int bus_notify(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct device *dev = data; - struct bus_wait *wait = container_of(nb, struct bus_wait, notifier); - - if (wait->dev != dev) - return NOTIFY_DONE; - - switch (action) { - case BUS_NOTIFY_UNBOUND_DRIVER: - /* Protect from module unloading */ - wait_for_completion(&wait->completion); - return NOTIFY_OK; - } - return NOTIFY_DONE; -} - -static int sh_mobile_ceu_probe(struct platform_device *pdev) -{ - struct sh_mobile_ceu_dev *pcdev; - struct resource *res; - void __iomem *base; - unsigned int irq; - int err; - struct bus_wait wait = { - .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion), - .notifier.notifier_call = bus_notify, - }; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!res || (int)irq <= 0) { - dev_err(&pdev->dev, "Not enough CEU platform resources.\n"); - return -ENODEV; - } - - pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); - if (!pcdev) { - dev_err(&pdev->dev, "Could not allocate pcdev\n"); - return -ENOMEM; - } - - INIT_LIST_HEAD(&pcdev->capture); - spin_lock_init(&pcdev->lock); - init_completion(&pcdev->complete); - - pcdev->pdata = pdev->dev.platform_data; - if (!pcdev->pdata && !pdev->dev.of_node) { - dev_err(&pdev->dev, "CEU platform data not set.\n"); - return -EINVAL; - } - - /* TODO: implement per-device bus flags */ - if (pcdev->pdata) { - pcdev->max_width = pcdev->pdata->max_width; - pcdev->max_height = pcdev->pdata->max_height; - pcdev->flags = pcdev->pdata->flags; - } - pcdev->field = V4L2_FIELD_NONE; - - if (!pcdev->max_width) { - unsigned int v; - err = of_property_read_u32(pdev->dev.of_node, "renesas,max-width", &v); - if (!err) - pcdev->max_width = v; - - if (!pcdev->max_width) - pcdev->max_width = 2560; - } - if (!pcdev->max_height) { - unsigned int v; - err = of_property_read_u32(pdev->dev.of_node, "renesas,max-height", &v); - if (!err) - pcdev->max_height = v; - - if (!pcdev->max_height) - pcdev->max_height = 1920; - } - - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - pcdev->irq = irq; - pcdev->base = base; - pcdev->video_limit = 0; /* only enabled if second resource exists */ - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) { - err = dma_declare_coherent_memory(&pdev->dev, res->start, - res->start, - resource_size(res), - DMA_MEMORY_EXCLUSIVE); - if (err) { - dev_err(&pdev->dev, "Unable to declare CEU memory.\n"); - return err; - } - - pcdev->video_limit = resource_size(res); - } - - /* request irq */ - err = devm_request_irq(&pdev->dev, pcdev->irq, sh_mobile_ceu_irq, - 0, dev_name(&pdev->dev), pcdev); - if (err) { - dev_err(&pdev->dev, "Unable to register CEU interrupt.\n"); - goto exit_release_mem; - } - - pm_suspend_ignore_children(&pdev->dev, true); - pm_runtime_enable(&pdev->dev); - pm_runtime_resume(&pdev->dev); - - pcdev->ici.priv = pcdev; - pcdev->ici.v4l2_dev.dev = &pdev->dev; - pcdev->ici.nr = pdev->id; - pcdev->ici.drv_name = dev_name(&pdev->dev); - pcdev->ici.ops = &sh_mobile_ceu_host_ops; - pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE; - - if (pcdev->pdata && pcdev->pdata->asd_sizes) { - pcdev->ici.asd = pcdev->pdata->asd; - pcdev->ici.asd_sizes = pcdev->pdata->asd_sizes; - } - - err = soc_camera_host_register(&pcdev->ici); - if (err) - goto exit_free_clk; - - return 0; - -exit_free_clk: - pm_runtime_disable(&pdev->dev); -exit_release_mem: - if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) - dma_release_declared_memory(&pdev->dev); - return err; -} - -static int sh_mobile_ceu_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - - soc_camera_host_unregister(soc_host); - pm_runtime_disable(&pdev->dev); - if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) - dma_release_declared_memory(&pdev->dev); - - return 0; -} - -static int sh_mobile_ceu_runtime_nop(struct device *dev) -{ - /* Runtime PM callback shared between ->runtime_suspend() - * and ->runtime_resume(). Simply returns success. - * - * This driver re-initializes all registers after - * pm_runtime_get_sync() anyway so there is no need - * to save and restore registers here. - */ - return 0; -} - -static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = { - .runtime_suspend = sh_mobile_ceu_runtime_nop, - .runtime_resume = sh_mobile_ceu_runtime_nop, -}; - -static const struct of_device_id sh_mobile_ceu_of_match[] = { - { .compatible = "renesas,sh-mobile-ceu" }, - { } -}; -MODULE_DEVICE_TABLE(of, sh_mobile_ceu_of_match); - -static struct platform_driver sh_mobile_ceu_driver = { - .driver = { - .name = "sh_mobile_ceu", - .pm = &sh_mobile_ceu_dev_pm_ops, - .of_match_table = sh_mobile_ceu_of_match, - }, - .probe = sh_mobile_ceu_probe, - .remove = sh_mobile_ceu_remove, -}; - -module_platform_driver(sh_mobile_ceu_driver); - -MODULE_DESCRIPTION("SuperH Mobile CEU driver"); -MODULE_AUTHOR("Magnus Damm"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1.0"); -MODULE_ALIAS("platform:sh_mobile_ceu"); diff --git a/include/media/drv-intf/sh_mobile_ceu.h b/include/media/drv-intf/sh_mobile_ceu.h deleted file mode 100644 index 555f0ecc0fde..000000000000 --- a/include/media/drv-intf/sh_mobile_ceu.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __ASM_SH_MOBILE_CEU_H__ -#define __ASM_SH_MOBILE_CEU_H__ - -#define SH_CEU_FLAG_USE_8BIT_BUS (1 << 0) /* use 8bit bus width */ -#define SH_CEU_FLAG_USE_16BIT_BUS (1 << 1) /* use 16bit bus width */ -#define SH_CEU_FLAG_HSYNC_LOW (1 << 2) /* default High if possible */ -#define SH_CEU_FLAG_VSYNC_LOW (1 << 3) /* default High if possible */ -#define SH_CEU_FLAG_LOWER_8BIT (1 << 4) /* default upper 8bit */ - -struct device; -struct resource; - -struct sh_mobile_ceu_companion { - u32 num_resources; - struct resource *resource; - int id; - void *platform_data; -}; - -struct sh_mobile_ceu_info { - unsigned long flags; - int max_width; - int max_height; - struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ - unsigned int *asd_sizes; /* 0-terminated array pf asd group sizes */ -}; - -#endif /* __ASM_SH_MOBILE_CEU_H__ */ -- cgit v1.2.3-59-g8ed1b From e48fef523e9bee354f27ae5d0b4927006a2a19c0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:44:21 -0200 Subject: media: soc_camera/soc_scale_crop: drop this unused code With the removal of sh_mobile_ceu_camera.c this code is no longer used and can be removed. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 3 - drivers/media/platform/soc_camera/Makefile | 1 - drivers/media/platform/soc_camera/soc_scale_crop.c | 426 --------------------- drivers/media/platform/soc_camera/soc_scale_crop.h | 47 --- 4 files changed, 477 deletions(-) delete mode 100644 drivers/media/platform/soc_camera/soc_scale_crop.c delete mode 100644 drivers/media/platform/soc_camera/soc_scale_crop.h diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 94907d0611d6..d471d34b884c 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -7,9 +7,6 @@ config SOC_CAMERA over a bus like PCI or USB. For example some i2c camera connected directly to the data bus of an SoC. -config SOC_CAMERA_SCALE_CROP - tristate - config SOC_CAMERA_PLATFORM tristate "platform camera support" depends on SOC_CAMERA diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index aa4b855a0882..2cb7022e073b 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -1,5 +1,4 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o -obj-$(CONFIG_SOC_CAMERA_SCALE_CROP) += soc_scale_crop.o # a platform subdevice driver stub, allowing to support cameras by adding a # couple of callback functions to the board code diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c deleted file mode 100644 index 8d25ca0490f7..000000000000 --- a/drivers/media/platform/soc_camera/soc_scale_crop.c +++ /dev/null @@ -1,426 +0,0 @@ -/* - * soc-camera generic scaling-cropping manipulation functions - * - * Copyright (C) 2013 Guennadi Liakhovetski - * - * 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 -#include - -#include -#include - -#include "soc_scale_crop.h" - -#ifdef DEBUG_GEOMETRY -#define dev_geo dev_info -#else -#define dev_geo dev_dbg -#endif - -/* Check if any dimension of r1 is smaller than respective one of r2 */ -static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2) -{ - return r1->width < r2->width || r1->height < r2->height; -} - -/* Check if r1 fails to cover r2 */ -static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2) -{ - return r1->left > r2->left || r1->top > r2->top || - r1->left + r1->width < r2->left + r2->width || - r1->top + r1->height < r2->top + r2->height; -} - -/* Get and store current client crop */ -int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) -{ - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP, - }; - int ret; - - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); - if (!ret) { - *rect = sdsel.r; - return ret; - } - - sdsel.target = V4L2_SEL_TGT_CROP_BOUNDS; - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); - if (!ret) - *rect = sdsel.r; - - return ret; -} -EXPORT_SYMBOL(soc_camera_client_g_rect); - -/* Client crop has changed, update our sub-rectangle to remain within the area */ -static void move_and_crop_subrect(struct v4l2_rect *rect, - struct v4l2_rect *subrect) -{ - if (rect->width < subrect->width) - subrect->width = rect->width; - - if (rect->height < subrect->height) - subrect->height = rect->height; - - if (rect->left > subrect->left) - subrect->left = rect->left; - else if (rect->left + rect->width < - subrect->left + subrect->width) - subrect->left = rect->left + rect->width - - subrect->width; - - if (rect->top > subrect->top) - subrect->top = rect->top; - else if (rect->top + rect->height < - subrect->top + subrect->height) - subrect->top = rect->top + rect->height - - subrect->height; -} - -/* - * The common for both scaling and cropping iterative approach is: - * 1. try if the client can produce exactly what requested by the user - * 2. if (1) failed, try to double the client image until we get one big enough - * 3. if (2) failed, try to request the maximum image - */ -int soc_camera_client_s_selection(struct v4l2_subdev *sd, - struct v4l2_selection *sel, struct v4l2_selection *cam_sel, - struct v4l2_rect *target_rect, struct v4l2_rect *subrect) -{ - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - .flags = sel->flags, - .r = sel->r, - }; - struct v4l2_subdev_selection bounds = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP_BOUNDS, - }; - struct v4l2_rect *rect = &sel->r, *cam_rect = &cam_sel->r; - struct device *dev = sd->v4l2_dev->dev; - int ret; - unsigned int width, height; - - v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); - sel->r = sdsel.r; - ret = soc_camera_client_g_rect(sd, cam_rect); - if (ret < 0) - return ret; - - /* - * Now cam_crop contains the current camera input rectangle, and it must - * be within camera cropcap bounds - */ - if (!memcmp(rect, cam_rect, sizeof(*rect))) { - /* Even if camera S_SELECTION failed, but camera rectangle matches */ - dev_dbg(dev, "Camera S_SELECTION successful for %dx%d@%d:%d\n", - rect->width, rect->height, rect->left, rect->top); - *target_rect = *cam_rect; - return 0; - } - - /* Try to fix cropping, that camera hasn't managed to set */ - dev_geo(dev, "Fix camera S_SELECTION for %dx%d@%d:%d to %dx%d@%d:%d\n", - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top, - rect->width, rect->height, rect->left, rect->top); - - /* We need sensor maximum rectangle */ - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &bounds); - if (ret < 0) - return ret; - - /* Put user requested rectangle within sensor bounds */ - soc_camera_limit_side(&rect->left, &rect->width, sdsel.r.left, 2, - bounds.r.width); - soc_camera_limit_side(&rect->top, &rect->height, sdsel.r.top, 4, - bounds.r.height); - - /* - * Popular special case - some cameras can only handle fixed sizes like - * QVGA, VGA,... Take care to avoid infinite loop. - */ - width = max_t(unsigned int, cam_rect->width, 2); - height = max_t(unsigned int, cam_rect->height, 2); - - /* - * Loop as long as sensor is not covering the requested rectangle and - * is still within its bounds - */ - while (!ret && (is_smaller(cam_rect, rect) || - is_inside(cam_rect, rect)) && - (bounds.r.width > width || bounds.r.height > height)) { - - width *= 2; - height *= 2; - - cam_rect->width = width; - cam_rect->height = height; - - /* - * We do not know what capabilities the camera has to set up - * left and top borders. We could try to be smarter in iterating - * them, e.g., if camera current left is to the right of the - * target left, set it to the middle point between the current - * left and minimum left. But that would add too much - * complexity: we would have to iterate each border separately. - * Instead we just drop to the left and top bounds. - */ - if (cam_rect->left > rect->left) - cam_rect->left = bounds.r.left; - - if (cam_rect->left + cam_rect->width < rect->left + rect->width) - cam_rect->width = rect->left + rect->width - - cam_rect->left; - - if (cam_rect->top > rect->top) - cam_rect->top = bounds.r.top; - - if (cam_rect->top + cam_rect->height < rect->top + rect->height) - cam_rect->height = rect->top + rect->height - - cam_rect->top; - - sdsel.r = *cam_rect; - v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); - *cam_rect = sdsel.r; - ret = soc_camera_client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_SELECTION %d for %dx%d@%d:%d\n", ret, - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top); - } - - /* S_SELECTION must not modify the rectangle */ - if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) { - /* - * The camera failed to configure a suitable cropping, - * we cannot use the current rectangle, set to max - */ - sdsel.r = bounds.r; - v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); - *cam_rect = sdsel.r; - - ret = soc_camera_client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_SELECTION %d for max %dx%d@%d:%d\n", ret, - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top); - } - - if (!ret) { - *target_rect = *cam_rect; - move_and_crop_subrect(target_rect, subrect); - } - - return ret; -} -EXPORT_SYMBOL(soc_camera_client_s_selection); - -/* Iterative set_fmt, also updates cached client crop on success */ -static int client_set_fmt(struct soc_camera_device *icd, - struct v4l2_rect *rect, struct v4l2_rect *subrect, - unsigned int max_width, unsigned int max_height, - struct v4l2_subdev_format *format, bool host_can_scale) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - struct v4l2_mbus_framefmt *mf = &format->format; - unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP_BOUNDS, - }; - bool host_1to1; - int ret; - - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), pad, - set_fmt, NULL, format); - if (ret < 0) - return ret; - - dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); - - if (width == mf->width && height == mf->height) { - /* Perfect! The client has done it all. */ - host_1to1 = true; - goto update_cache; - } - - host_1to1 = false; - if (!host_can_scale) - goto update_cache; - - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); - if (ret < 0) - return ret; - - if (max_width > sdsel.r.width) - max_width = sdsel.r.width; - if (max_height > sdsel.r.height) - max_height = sdsel.r.height; - - /* Camera set a format, but geometry is not precise, try to improve */ - tmp_w = mf->width; - tmp_h = mf->height; - - /* width <= max_width && height <= max_height - guaranteed by try_fmt */ - while ((width > tmp_w || height > tmp_h) && - tmp_w < max_width && tmp_h < max_height) { - tmp_w = min(2 * tmp_w, max_width); - tmp_h = min(2 * tmp_h, max_height); - mf->width = tmp_w; - mf->height = tmp_h; - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), pad, - set_fmt, NULL, format); - dev_geo(dev, "Camera scaled to %ux%u\n", - mf->width, mf->height); - if (ret < 0) { - /* This shouldn't happen */ - dev_err(dev, "Client failed to set format: %d\n", ret); - return ret; - } - } - -update_cache: - /* Update cache */ - ret = soc_camera_client_g_rect(sd, rect); - if (ret < 0) - return ret; - - if (host_1to1) - *subrect = *rect; - else - move_and_crop_subrect(rect, subrect); - - return 0; -} - -/** - * soc_camera_client_scale - * @icd: soc-camera device - * @rect: camera cropping window - * @subrect: part of rect, sent to the user - * @mf: in- / output camera output window - * @width: on input: max host input width; - * on output: user width, mapped back to input - * @height: on input: max host input height; - * on output: user height, mapped back to input - * @host_can_scale: host can scale this pixel format - * @shift: shift, used for scaling - */ -int soc_camera_client_scale(struct soc_camera_device *icd, - struct v4l2_rect *rect, struct v4l2_rect *subrect, - struct v4l2_mbus_framefmt *mf, - unsigned int *width, unsigned int *height, - bool host_can_scale, unsigned int shift) -{ - struct device *dev = icd->parent; - struct v4l2_subdev_format fmt_tmp = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .format = *mf, - }; - struct v4l2_mbus_framefmt *mf_tmp = &fmt_tmp.format; - unsigned int scale_h, scale_v; - int ret; - - /* - * 5. Apply iterative camera S_FMT for camera user window (also updates - * client crop cache and the imaginary sub-rectangle). - */ - ret = client_set_fmt(icd, rect, subrect, *width, *height, - &fmt_tmp, host_can_scale); - if (ret < 0) - return ret; - - dev_geo(dev, "5: camera scaled to %ux%u\n", - mf_tmp->width, mf_tmp->height); - - /* 6. Retrieve camera output window (g_fmt) */ - - /* unneeded - it is already in "mf_tmp" */ - - /* 7. Calculate new client scales. */ - scale_h = soc_camera_calc_scale(rect->width, shift, mf_tmp->width); - scale_v = soc_camera_calc_scale(rect->height, shift, mf_tmp->height); - - mf->width = mf_tmp->width; - mf->height = mf_tmp->height; - mf->colorspace = mf_tmp->colorspace; - - /* - * 8. Calculate new host crop - apply camera scales to previously - * updated "effective" crop. - */ - *width = soc_camera_shift_scale(subrect->width, shift, scale_h); - *height = soc_camera_shift_scale(subrect->height, shift, scale_v); - - dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height); - - return 0; -} -EXPORT_SYMBOL(soc_camera_client_scale); - -/* - * Calculate real client output window by applying new scales to the current - * client crop. New scales are calculated from the requested output format and - * host crop, mapped backed onto the client input (subrect). - */ -void soc_camera_calc_client_output(struct soc_camera_device *icd, - struct v4l2_rect *rect, struct v4l2_rect *subrect, - const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf, - unsigned int shift) -{ - struct device *dev = icd->parent; - unsigned int scale_v, scale_h; - - if (subrect->width == rect->width && - subrect->height == rect->height) { - /* No sub-cropping */ - mf->width = pix->width; - mf->height = pix->height; - return; - } - - /* 1.-2. Current camera scales and subwin - cached. */ - - dev_geo(dev, "2: subwin %ux%u@%u:%u\n", - subrect->width, subrect->height, - subrect->left, subrect->top); - - /* - * 3. Calculate new combined scales from input sub-window to requested - * user window. - */ - - /* - * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF - * (128x96) or larger than VGA. This and similar limitations have to be - * taken into account here. - */ - scale_h = soc_camera_calc_scale(subrect->width, shift, pix->width); - scale_v = soc_camera_calc_scale(subrect->height, shift, pix->height); - - dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); - - /* - * 4. Calculate desired client output window by applying combined scales - * to client (real) input window. - */ - mf->width = soc_camera_shift_scale(rect->width, shift, scale_h); - mf->height = soc_camera_shift_scale(rect->height, shift, scale_v); -} -EXPORT_SYMBOL(soc_camera_calc_client_output); - -MODULE_DESCRIPTION("soc-camera scaling-cropping functions"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.h b/drivers/media/platform/soc_camera/soc_scale_crop.h deleted file mode 100644 index 9ca469312a1f..000000000000 --- a/drivers/media/platform/soc_camera/soc_scale_crop.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * soc-camera generic scaling-cropping manipulation functions - * - * Copyright (C) 2013 Guennadi Liakhovetski - * - * 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. - */ - -#ifndef SOC_SCALE_CROP_H -#define SOC_SCALE_CROP_H - -#include - -struct soc_camera_device; - -struct v4l2_selection; -struct v4l2_mbus_framefmt; -struct v4l2_pix_format; -struct v4l2_rect; -struct v4l2_subdev; - -static inline unsigned int soc_camera_shift_scale(unsigned int size, - unsigned int shift, unsigned int scale) -{ - return DIV_ROUND_CLOSEST(size << shift, scale); -} - -#define soc_camera_calc_scale(in, shift, out) soc_camera_shift_scale(in, shift, out) - -int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); -int soc_camera_client_s_selection(struct v4l2_subdev *sd, - struct v4l2_selection *sel, struct v4l2_selection *cam_sel, - struct v4l2_rect *target_rect, struct v4l2_rect *subrect); -int soc_camera_client_scale(struct soc_camera_device *icd, - struct v4l2_rect *rect, struct v4l2_rect *subrect, - struct v4l2_mbus_framefmt *mf, - unsigned int *width, unsigned int *height, - bool host_can_scale, unsigned int shift); -void soc_camera_calc_client_output(struct soc_camera_device *icd, - struct v4l2_rect *rect, struct v4l2_rect *subrect, - const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf, - unsigned int shift); - -#endif -- cgit v1.2.3-59-g8ed1b From dc60a4cfb77c891f67f31953025208067b05883c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jan 2019 11:47:55 -0200 Subject: media: soc_camera_platform: remove obsolete soc_camera test driver This is a test stub driver for soc_camera. Since soc_camera is being deprecated (and in fact, nobody is using it anymore) there's no sense in keeping this test driver. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 6 - drivers/media/platform/soc_camera/Makefile | 4 - .../platform/soc_camera/soc_camera_platform.c | 188 --------------------- .../platform_data/media/soc_camera_platform.h | 83 --------- 4 files changed, 281 deletions(-) delete mode 100644 drivers/media/platform/soc_camera/soc_camera_platform.c delete mode 100644 include/linux/platform_data/media/soc_camera_platform.h diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index d471d34b884c..8f9b3bac5450 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -6,9 +6,3 @@ config SOC_CAMERA SoC Camera is a common API to several cameras, not connecting over a bus like PCI or USB. For example some i2c camera connected directly to the data bus of an SoC. - -config SOC_CAMERA_PLATFORM - tristate "platform camera support" - depends on SOC_CAMERA - help - This is a generic SoC camera platform driver, useful for testing diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 2cb7022e073b..85d5e74f3b2b 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -1,5 +1 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o - -# a platform subdevice driver stub, allowing to support cameras by adding a -# couple of callback functions to the board code -obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c deleted file mode 100644 index 79fbe1fea95f..000000000000 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ /dev/null @@ -1,188 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Generic Platform Camera Driver - * - * Copyright (C) 2008 Magnus Damm - * Based on mt9m001 driver, - * Copyright (C) 2008, Guennadi Liakhovetski - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct soc_camera_platform_priv { - struct v4l2_subdev subdev; -}; - -static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev) -{ - struct v4l2_subdev *subdev = platform_get_drvdata(pdev); - return container_of(subdev, struct soc_camera_platform_priv, subdev); -} - -static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - return p->set_capture(p, enable); -} - -static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *mf = &format->format; - - mf->width = p->format.width; - mf->height = p->format.height; - mf->code = p->format.code; - mf->colorspace = p->format.colorspace; - mf->field = p->format.field; - - return 0; -} - -static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - - return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, NULL, on); -} - -static const struct v4l2_subdev_core_ops platform_subdev_core_ops = { - .s_power = soc_camera_platform_s_power, -}; - -static int soc_camera_platform_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - - if (code->pad || code->index) - return -EINVAL; - - code->code = p->format.code; - return 0; -} - -static int soc_camera_platform_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = p->format.width; - sel->r.height = p->format.height; - return 0; - default: - return -EINVAL; - } -} - -static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - - cfg->flags = p->mbus_param; - cfg->type = p->mbus_type; - - return 0; -} - -static const struct v4l2_subdev_video_ops platform_subdev_video_ops = { - .s_stream = soc_camera_platform_s_stream, - .g_mbus_config = soc_camera_platform_g_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops platform_subdev_pad_ops = { - .enum_mbus_code = soc_camera_platform_enum_mbus_code, - .get_selection = soc_camera_platform_get_selection, - .get_fmt = soc_camera_platform_fill_fmt, - .set_fmt = soc_camera_platform_fill_fmt, -}; - -static const struct v4l2_subdev_ops platform_subdev_ops = { - .core = &platform_subdev_core_ops, - .video = &platform_subdev_video_ops, - .pad = &platform_subdev_pad_ops, -}; - -static int soc_camera_platform_probe(struct platform_device *pdev) -{ - struct soc_camera_host *ici; - struct soc_camera_platform_priv *priv; - struct soc_camera_platform_info *p = pdev->dev.platform_data; - struct soc_camera_device *icd; - - if (!p) - return -EINVAL; - - if (!p->icd) { - dev_err(&pdev->dev, - "Platform has not set soc_camera_device pointer!\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - icd = p->icd; - - /* soc-camera convention: control's drvdata points to the subdev */ - platform_set_drvdata(pdev, &priv->subdev); - /* Set the control device reference */ - icd->control = &pdev->dev; - - ici = to_soc_camera_host(icd->parent); - - v4l2_subdev_init(&priv->subdev, &platform_subdev_ops); - v4l2_set_subdevdata(&priv->subdev, p); - strscpy(priv->subdev.name, dev_name(&pdev->dev), - sizeof(priv->subdev.name)); - - return v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev); -} - -static int soc_camera_platform_remove(struct platform_device *pdev) -{ - struct soc_camera_platform_priv *priv = get_priv(pdev); - struct soc_camera_platform_info *p = v4l2_get_subdevdata(&priv->subdev); - - p->icd->control = NULL; - v4l2_device_unregister_subdev(&priv->subdev); - return 0; -} - -static struct platform_driver soc_camera_platform_driver = { - .driver = { - .name = "soc_camera_platform", - }, - .probe = soc_camera_platform_probe, - .remove = soc_camera_platform_remove, -}; - -module_platform_driver(soc_camera_platform_driver); - -MODULE_DESCRIPTION("SoC Camera Platform driver"); -MODULE_AUTHOR("Magnus Damm"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:soc_camera_platform"); diff --git a/include/linux/platform_data/media/soc_camera_platform.h b/include/linux/platform_data/media/soc_camera_platform.h deleted file mode 100644 index 1e5065dab430..000000000000 --- a/include/linux/platform_data/media/soc_camera_platform.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Generic Platform Camera Driver Header - * - * Copyright (C) 2008 Magnus Damm - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __SOC_CAMERA_H__ -#define __SOC_CAMERA_H__ - -#include -#include -#include - -struct device; - -struct soc_camera_platform_info { - const char *format_name; - unsigned long format_depth; - struct v4l2_mbus_framefmt format; - unsigned long mbus_param; - enum v4l2_mbus_type mbus_type; - struct soc_camera_device *icd; - int (*set_capture)(struct soc_camera_platform_info *info, int enable); -}; - -static inline void soc_camera_platform_release(struct platform_device **pdev) -{ - *pdev = NULL; -} - -static inline int soc_camera_platform_add(struct soc_camera_device *icd, - struct platform_device **pdev, - struct soc_camera_link *plink, - void (*release)(struct device *dev), - int id) -{ - struct soc_camera_subdev_desc *ssdd = - (struct soc_camera_subdev_desc *)plink; - struct soc_camera_platform_info *info = ssdd->drv_priv; - int ret; - - if (&icd->sdesc->subdev_desc != ssdd) - return -ENODEV; - - if (*pdev) - return -EBUSY; - - *pdev = platform_device_alloc("soc_camera_platform", id); - if (!*pdev) - return -ENOMEM; - - info->icd = icd; - - (*pdev)->dev.platform_data = info; - (*pdev)->dev.release = release; - - ret = platform_device_add(*pdev); - if (ret < 0) { - platform_device_put(*pdev); - *pdev = NULL; - info->icd = NULL; - } - - return ret; -} - -static inline void soc_camera_platform_del(const struct soc_camera_device *icd, - struct platform_device *pdev, - const struct soc_camera_link *plink) -{ - const struct soc_camera_subdev_desc *ssdd = - (const struct soc_camera_subdev_desc *)plink; - if (&icd->sdesc->subdev_desc != ssdd || !pdev) - return; - - platform_device_unregister(pdev); -} - -#endif /* __SOC_CAMERA_H__ */ -- cgit v1.2.3-59-g8ed1b From 526daee7301d8ec86d8de0698d4c9ada185c5470 Mon Sep 17 00:00:00 2001 From: "French, Nicholas A" Date: Sun, 11 Mar 2018 16:27:28 -0300 Subject: media: ivtv: add parameter to enable ivtvfb on x86 PAT systems ivtvfb was previously disabled for x86 PAT-enabled systems by commit 1bf1735b4780 ("x86/mm/pat, drivers/media/ivtv: Use arch_phys_wc_add() and require PAT disabled") as a workaround to abstract MTRR code away from device drivers. The driver is not easily upgradable to the PAT-aware ioremap_wc() API since the firmware hides the address ranges that should be marked write-combined from the driver. However, since a write-combined cache on the framebuffer is only a performance enhancement not a requirement for the framebuffer to function, completely disabling the driver in this configuration is not necessary. Add force_pat module parameter and a corresponding kernel configuration parameter to optionally force initialization on PAT-enabled x86 systems with a warning about the lack of write-combined caching, and document the reasons the driver cannot be easily updated to support wc caching on all systems. Signed-off-by: Nick French Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: fix typo, split long pr_ lines up] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/Kconfig | 23 ++++++++++++++++++++--- drivers/media/pci/ivtv/ivtvfb.c | 16 ++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/ivtv/Kconfig b/drivers/media/pci/ivtv/Kconfig index c72cbbd2d40c..06ca4e23f9fb 100644 --- a/drivers/media/pci/ivtv/Kconfig +++ b/drivers/media/pci/ivtv/Kconfig @@ -70,8 +70,25 @@ config VIDEO_FB_IVTV This is used in the Hauppauge PVR-350 card. There is a driver homepage at . - In order to use this module, you will need to boot with PAT disabled - on x86 systems, using the nopat kernel parameter. - To compile this driver as a module, choose M here: the module will be called ivtvfb. + +config VIDEO_FB_IVTV_FORCE_PAT + bool "force cx23415 framebuffer init with x86 PAT enabled" + depends on VIDEO_FB_IVTV && X86_PAT + default n + ---help--- + With PAT enabled, the cx23415 framebuffer driver does not + utilize write-combined caching on the framebuffer memory. + For this reason, the driver will by default disable itself + when initializied on a kernel with PAT enabled (i.e. not + using the nopat kernel parameter). + + The driver is not easily upgradable to the PAT-aware + ioremap_wc() API since the firmware hides the address + ranges that should be marked write-combined from the driver. + + With this setting enabled, the framebuffer will initialize on + PAT-enabled systems but the framebuffer memory will be uncached. + + If unsure, say N. diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index 8ec2525d8ef5..cfd21040d0e3 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -55,6 +55,7 @@ /* card parameters */ static int ivtvfb_card_id = -1; static int ivtvfb_debug = 0; +static bool ivtvfb_force_pat = IS_ENABLED(CONFIG_VIDEO_FB_IVTV_FORCE_PAT); static bool osd_laced; static int osd_depth; static int osd_upper; @@ -64,6 +65,7 @@ static int osd_xres; module_param(ivtvfb_card_id, int, 0444); module_param_named(debug,ivtvfb_debug, int, 0644); +module_param_named(force_pat, ivtvfb_force_pat, bool, 0644); module_param(osd_laced, bool, 0444); module_param(osd_depth, int, 0444); module_param(osd_upper, int, 0444); @@ -79,6 +81,9 @@ MODULE_PARM_DESC(debug, "Debug level (bitmask). Default: errors only\n" "\t\t\t(debug = 3 gives full debugging)"); +MODULE_PARM_DESC(force_pat, + "Force initialization on x86 PAT-enabled systems (bool).\n"); + /* Why upper, left, xres, yres, depth, laced ? To match terminology used by fbset. Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */ @@ -1167,8 +1172,15 @@ static int ivtvfb_init_card(struct ivtv *itv) #ifdef CONFIG_X86_64 if (pat_enabled()) { - pr_warn("ivtvfb needs PAT disabled, boot with nopat kernel parameter\n"); - return -ENODEV; + if (ivtvfb_force_pat) { + pr_info("PAT is enabled. Write-combined framebuffer caching will be disabled.\n"); + pr_info("To enable caching, boot with nopat kernel parameter\n"); + } else { + pr_warn("ivtvfb needs PAT disabled for write-combined framebuffer caching.\n"); + pr_warn("Boot with nopat kernel parameter to use caching, or use the\n"); + pr_warn("force_pat module parameter to run with caching disabled\n"); + return -ENODEV; + } } #endif -- cgit v1.2.3-59-g8ed1b From 95f9db59d856010e5c28813263fbdd7d70159f77 Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Fri, 7 Dec 2018 11:58:09 -0200 Subject: media: si470x-i2c: Add device tree support This commit enables device tree support adding simple of_match table. Signed-off-by: Pawe? Chmiel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-i2c.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 9751ea1d80be..250828ddb5fa 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -527,6 +527,13 @@ static int si470x_i2c_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(si470x_i2c_pm, si470x_i2c_suspend, si470x_i2c_resume); #endif +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id si470x_of_match[] = { + { .compatible = "silabs,si470x" }, + { }, +}; +MODULE_DEVICE_TABLE(of, si470x_of_match); +#endif /* * si470x_i2c_driver - i2c driver interface @@ -534,6 +541,7 @@ static SIMPLE_DEV_PM_OPS(si470x_i2c_pm, si470x_i2c_suspend, si470x_i2c_resume); static struct i2c_driver si470x_i2c_driver = { .driver = { .name = "si470x", + .of_match_table = of_match_ptr(si470x_of_match), #ifdef CONFIG_PM_SLEEP .pm = &si470x_i2c_pm, #endif -- cgit v1.2.3-59-g8ed1b From f86c51b66bf6a066190035594965a1a9c661b4e4 Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Fri, 7 Dec 2018 11:58:10 -0200 Subject: media: si470x-i2c: Use managed resource helpers Simplify cleanup of failures by using managed resource helpers Signed-off-by: Pawe? Chmiel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-i2c.c | 29 ++++++++++----------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 250828ddb5fa..a7ac09c55188 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -350,7 +350,7 @@ static int si470x_i2c_probe(struct i2c_client *client, unsigned char version_warning = 0; /* private data allocation and initialization */ - radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); + radio = devm_kzalloc(&client->dev, sizeof(*radio), GFP_KERNEL); if (!radio) { retval = -ENOMEM; goto err_initial; @@ -370,7 +370,7 @@ static int si470x_i2c_probe(struct i2c_client *client, retval = v4l2_device_register(&client->dev, &radio->v4l2_dev); if (retval < 0) { dev_err(&client->dev, "couldn't register v4l2_device\n"); - goto err_radio; + goto err_initial; } v4l2_ctrl_handler_init(&radio->hdl, 2); @@ -396,14 +396,14 @@ static int si470x_i2c_probe(struct i2c_client *client, radio->registers[POWERCFG] = POWERCFG_ENABLE; if (si470x_set_register(radio, POWERCFG) < 0) { retval = -EIO; - goto err_ctrl; + goto err_all; } msleep(110); /* get device and chip versions */ if (si470x_get_all_registers(radio) < 0) { retval = -EIO; - goto err_ctrl; + goto err_all; } dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[SI_CHIPID]); @@ -430,10 +430,10 @@ static int si470x_i2c_probe(struct i2c_client *client, /* rds buffer allocation */ radio->buf_size = rds_buf * 3; - radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); + radio->buffer = devm_kmalloc(&client->dev, radio->buf_size, GFP_KERNEL); if (!radio->buffer) { retval = -EIO; - goto err_ctrl; + goto err_all; } /* rds buffer configuration */ @@ -441,12 +441,13 @@ static int si470x_i2c_probe(struct i2c_client *client, radio->rd_index = 0; init_waitqueue_head(&radio->read_queue); - retval = request_threaded_irq(client->irq, NULL, si470x_i2c_interrupt, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, DRIVER_NAME, - radio); + retval = devm_request_threaded_irq(&client->dev, client->irq, NULL, + si470x_i2c_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + DRIVER_NAME, radio); if (retval) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_rds; + goto err_all; } /* register video device */ @@ -460,15 +461,9 @@ static int si470x_i2c_probe(struct i2c_client *client, return 0; err_all: - free_irq(client->irq, radio); -err_rds: - kfree(radio->buffer); -err_ctrl: v4l2_ctrl_handler_free(&radio->hdl); err_dev: v4l2_device_unregister(&radio->v4l2_dev); -err_radio: - kfree(radio); err_initial: return retval; } @@ -481,9 +476,7 @@ static int si470x_i2c_remove(struct i2c_client *client) { struct si470x_device *radio = i2c_get_clientdata(client); - free_irq(client->irq, radio); video_unregister_device(&radio->videodev); - kfree(radio); return 0; } -- cgit v1.2.3-59-g8ed1b From 1c64222be9ad0e56ad9a222b35e063d481f58cfc Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Fri, 7 Dec 2018 11:58:11 -0200 Subject: media: si470x-i2c: Add optional reset-gpio support If reset-gpio is defined, use it to bring device out of reset. Without this, it's not possible to access si470x registers. Signed-off-by: Pawe? Chmiel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-i2c.c | 15 +++++++++++++++ drivers/media/radio/si470x/radio-si470x.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index a7ac09c55188..15eea2b2c90f 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "radio-si470x.h" @@ -392,6 +393,17 @@ static int si470x_i2c_probe(struct i2c_client *client, radio->videodev.release = video_device_release_empty; video_set_drvdata(&radio->videodev, radio); + radio->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(radio->gpio_reset)) { + retval = PTR_ERR(radio->gpio_reset); + dev_err(&client->dev, "Failed to request gpio: %d\n", retval); + goto err_all; + } + + if (radio->gpio_reset) + gpiod_set_value(radio->gpio_reset, 1); + /* power up : need 110ms */ radio->registers[POWERCFG] = POWERCFG_ENABLE; if (si470x_set_register(radio, POWERCFG) < 0) { @@ -478,6 +490,9 @@ static int si470x_i2c_remove(struct i2c_client *client) video_unregister_device(&radio->videodev); + if (radio->gpio_reset) + gpiod_set_value(radio->gpio_reset, 0); + return 0; } diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h index 35fa0f3bbdd2..6fd6a399cb77 100644 --- a/drivers/media/radio/si470x/radio-si470x.h +++ b/drivers/media/radio/si470x/radio-si470x.h @@ -189,6 +189,7 @@ struct si470x_device { #if IS_ENABLED(CONFIG_I2C_SI470X) struct i2c_client *client; + struct gpio_desc *gpio_reset; #endif }; -- cgit v1.2.3-59-g8ed1b From 9b4fce216f538f848a46183650f29603c1ecf4e6 Mon Sep 17 00:00:00 2001 From: Pawe? Chmiel Date: Fri, 7 Dec 2018 11:58:12 -0200 Subject: media: dt-bindings: Add binding for si470x radio Add device tree bindings for si470x family radio receiver driver. Signed-off-by: Pawe? Chmiel Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/si470x.txt | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/si470x.txt diff --git a/Documentation/devicetree/bindings/media/si470x.txt b/Documentation/devicetree/bindings/media/si470x.txt new file mode 100644 index 000000000000..a9403558362e --- /dev/null +++ b/Documentation/devicetree/bindings/media/si470x.txt @@ -0,0 +1,26 @@ +* Silicon Labs FM Radio receiver + +The Silicon Labs Si470x is family of FM radio receivers with receive power scan +supporting 76-108 MHz, programmable through an I2C interface. +Some of them includes an RDS encoder. + +Required Properties: +- compatible: Should contain "silabs,si470x" +- reg: the I2C address of the device + +Optional Properties: +- interrupts : The interrupt number +- reset-gpios: GPIO specifier for the chips reset line + +Example: + +&i2c2 { + si470x@63 { + compatible = "silabs,si470x"; + reg = <0x63>; + + interrupt-parent = <&gpj2>; + interrupts = <4 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpj2 5 GPIO_ACTIVE_HIGH>; + }; +}; -- cgit v1.2.3-59-g8ed1b From 6372f01cddaa9d910d530ba3e389b9f86c7052e4 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 11 Dec 2018 13:17:00 -0200 Subject: media: dt-bindings: media: video-i2c: add melexis mlx90640 documentation Cc: devicetree@vger.kernel.org Signed-off-by: Matt Ranostay Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/i2c/melexis,mlx90640.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/melexis,mlx90640.txt diff --git a/Documentation/devicetree/bindings/media/i2c/melexis,mlx90640.txt b/Documentation/devicetree/bindings/media/i2c/melexis,mlx90640.txt new file mode 100644 index 000000000000..060d2b7a5893 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/melexis,mlx90640.txt @@ -0,0 +1,20 @@ +* Melexis MLX90640 FIR Sensor + +Melexis MLX90640 FIR sensor support which allows recording of thermal data +with 32x24 resolution excluding 2 lines of coefficient data that is used by +userspace to render processed frames. + +Required Properties: + - compatible : Must be "melexis,mlx90640" + - reg : i2c address of the device + +Example: + + i2c0@1c22000 { + ... + mlx90640@33 { + compatible = "melexis,mlx90640"; + reg = <0x33>; + }; + ... + }; -- cgit v1.2.3-59-g8ed1b From 8866cfbf655067237bb34a32a199f01eca9eeb5a Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 11 Dec 2018 13:17:01 -0200 Subject: media: video-i2c: add Melexis MLX90640 thermal camera Add initial support for MLX90640 thermal cameras which output an 32x24 greyscale pixel image along with 2 rows of coefficent data. Because of this the data outputed is really 32x26 and needs the two rows removed after using the coefficent information to generate processed images in userspace. Signed-off-by: Matt Ranostay Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/video-i2c.c | 110 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 119aaee5318b..e6cd5cf7c3d9 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1107,6 +1107,7 @@ config VIDEO_I2C Enable the I2C transport video support which supports the following: * Panasonic AMG88xx Grid-Eye Sensors + * Melexis MLX90640 Thermal Cameras To compile this driver as a module, choose M here: the module will be called video-i2c diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 01dcf179f203..abd3152df7d0 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -6,6 +6,7 @@ * * Supported: * - Panasonic AMG88xx Grid-Eye Sensors + * - Melexis MLX90640 Thermal Cameras */ #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -66,12 +68,26 @@ static const struct v4l2_frmsize_discrete amg88xx_size = { .height = 8, }; +static const struct v4l2_fmtdesc mlx90640_format = { + .pixelformat = V4L2_PIX_FMT_Y16_BE, +}; + +static const struct v4l2_frmsize_discrete mlx90640_size = { + .width = 32, + .height = 26, /* 24 lines of pixel data + 2 lines of processing data */ +}; + static const struct regmap_config amg88xx_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 0xff }; +static const struct regmap_config mlx90640_regmap_config = { + .reg_bits = 16, + .val_bits = 16, +}; + struct video_i2c_chip { /* video dimensions */ const struct v4l2_fmtdesc *format; @@ -88,6 +104,7 @@ struct video_i2c_chip { unsigned int bpp; const struct regmap_config *regmap_config; + struct nvmem_config *nvmem_config; /* setup function */ int (*setup)(struct video_i2c_data *data); @@ -102,6 +119,22 @@ struct video_i2c_chip { int (*hwmon_init)(struct video_i2c_data *data); }; +static int mlx90640_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct video_i2c_data *data = priv; + + return regmap_bulk_read(data->regmap, 0x2400 + offset, val, bytes); +} + +static struct nvmem_config mlx90640_nvram_config = { + .name = "mlx90640_nvram", + .word_size = 2, + .stride = 1, + .size = 1664, + .reg_read = mlx90640_nvram_read, +}; + /* Power control register */ #define AMG88XX_REG_PCTL 0x00 #define AMG88XX_PCTL_NORMAL 0x00 @@ -122,12 +155,23 @@ struct video_i2c_chip { /* Temperature register */ #define AMG88XX_REG_T01L 0x80 +/* Control register */ +#define MLX90640_REG_CTL1 0x800d +#define MLX90640_REG_CTL1_MASK 0x0380 +#define MLX90640_REG_CTL1_MASK_SHIFT 7 + static int amg88xx_xfer(struct video_i2c_data *data, char *buf) { return regmap_bulk_read(data->regmap, AMG88XX_REG_T01L, buf, data->chip->buffer_size); } +static int mlx90640_xfer(struct video_i2c_data *data, char *buf) +{ + return regmap_bulk_read(data->regmap, 0x400, buf, + data->chip->buffer_size); +} + static int amg88xx_setup(struct video_i2c_data *data) { unsigned int mask = AMG88XX_FPSC_1FPS; @@ -141,6 +185,27 @@ static int amg88xx_setup(struct video_i2c_data *data) return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val); } +static int mlx90640_setup(struct video_i2c_data *data) +{ + unsigned int n, idx; + + for (n = 0; n < data->chip->num_frame_intervals - 1; n++) { + if (data->frame_interval.numerator + != data->chip->frame_intervals[n].numerator) + continue; + + if (data->frame_interval.denominator + == data->chip->frame_intervals[n].denominator) + break; + } + + idx = data->chip->num_frame_intervals - n - 1; + + return regmap_update_bits(data->regmap, MLX90640_REG_CTL1, + MLX90640_REG_CTL1_MASK, + idx << MLX90640_REG_CTL1_MASK_SHIFT); +} + static int amg88xx_set_power_on(struct video_i2c_data *data) { int ret; @@ -274,13 +339,27 @@ static int amg88xx_hwmon_init(struct video_i2c_data *data) #define amg88xx_hwmon_init NULL #endif -#define AMG88XX 0 +enum { + AMG88XX, + MLX90640, +}; static const struct v4l2_fract amg88xx_frame_intervals[] = { { 1, 10 }, { 1, 1 }, }; +static const struct v4l2_fract mlx90640_frame_intervals[] = { + { 1, 64 }, + { 1, 32 }, + { 1, 16 }, + { 1, 8 }, + { 1, 4 }, + { 1, 2 }, + { 1, 1 }, + { 2, 1 }, +}; + static const struct video_i2c_chip video_i2c_chip[] = { [AMG88XX] = { .size = &amg88xx_size, @@ -295,6 +374,18 @@ static const struct video_i2c_chip video_i2c_chip[] = { .set_power = amg88xx_set_power, .hwmon_init = amg88xx_hwmon_init, }, + [MLX90640] = { + .size = &mlx90640_size, + .format = &mlx90640_format, + .frame_intervals = mlx90640_frame_intervals, + .num_frame_intervals = ARRAY_SIZE(mlx90640_frame_intervals), + .buffer_size = 1664, + .bpp = 16, + .regmap_config = &mlx90640_regmap_config, + .nvmem_config = &mlx90640_nvram_config, + .setup = mlx90640_setup, + .xfer = mlx90640_xfer, + }, }; static const struct v4l2_file_operations video_i2c_fops = { @@ -756,6 +847,21 @@ static int video_i2c_probe(struct i2c_client *client, } } + if (data->chip->nvmem_config) { + struct nvmem_config *config = data->chip->nvmem_config; + struct nvmem_device *device; + + config->priv = data; + config->dev = &client->dev; + + device = devm_nvmem_register(&client->dev, config); + + if (IS_ERR(device)) { + dev_warn(&client->dev, + "failed to register nvmem device\n"); + } + } + ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) goto error_pm_disable; @@ -835,12 +941,14 @@ static const struct dev_pm_ops video_i2c_pm_ops = { static const struct i2c_device_id video_i2c_id_table[] = { { "amg88xx", AMG88XX }, + { "mlx90640", MLX90640 }, {} }; MODULE_DEVICE_TABLE(i2c, video_i2c_id_table); static const struct of_device_id video_i2c_of_match[] = { { .compatible = "panasonic,amg88xx", .data = &video_i2c_chip[AMG88XX] }, + { .compatible = "melexis,mlx90640", .data = &video_i2c_chip[MLX90640] }, {} }; MODULE_DEVICE_TABLE(of, video_i2c_of_match); -- cgit v1.2.3-59-g8ed1b From 78a9f4a39bf43d41f0d91dc3cdbc1fdb752fc416 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 12 Dec 2018 14:20:14 -0200 Subject: media: exynos4-is: convert to DEFINE_SHOW_ATTRIBUTE Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Signed-off-by: Yangtao Li Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-is.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index f5fc54de19da..02da0b06e56a 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -738,7 +738,7 @@ int fimc_is_hw_initialize(struct fimc_is *is) return 0; } -static int fimc_is_log_show(struct seq_file *s, void *data) +static int fimc_is_show(struct seq_file *s, void *data) { struct fimc_is *is = s->private; const u8 *buf = is->memory.vaddr + FIMC_IS_DEBUG_REGION_OFFSET; @@ -752,17 +752,7 @@ static int fimc_is_log_show(struct seq_file *s, void *data) return 0; } -static int fimc_is_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, fimc_is_log_show, inode->i_private); -} - -static const struct file_operations fimc_is_debugfs_fops = { - .open = fimc_is_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(fimc_is); static void fimc_is_debugfs_remove(struct fimc_is *is) { @@ -777,7 +767,7 @@ static int fimc_is_debugfs_create(struct fimc_is *is) is->debugfs_entry = debugfs_create_dir("fimc_is", NULL); dentry = debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry, - is, &fimc_is_debugfs_fops); + is, &fimc_is_fops); if (!dentry) fimc_is_debugfs_remove(is); -- cgit v1.2.3-59-g8ed1b From 5e22c19f7e66672966281ea6f557100805c222bc Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 12 Dec 2018 14:27:03 -0200 Subject: media: platform: sti: remove bdisp_dbg_declare() and hva_dbg_declare() We already have the DEFINE_SHOW_ATTRIBUTE. There is no need to define bdisp_dbg_declare and hva_dbg_declare, so remove them. Also use DEFINE_SHOW_ATTRIBUTE to simplify some code. Signed-off-by: Yangtao Li Reviewed-by: Fabien Dessenne Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/bdisp/bdisp-debug.c | 34 ++++++++---------------- drivers/media/platform/sti/hva/hva-debugfs.c | 36 +++++++++----------------- 2 files changed, 23 insertions(+), 47 deletions(-) diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/sti/bdisp/bdisp-debug.c index c6a4e2de5c0c..77ca7517fa3e 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-debug.c +++ b/drivers/media/platform/sti/bdisp/bdisp-debug.c @@ -315,7 +315,7 @@ static void bdisp_dbg_dump_ivmx(struct seq_file *s, seq_puts(s, "Unknown conversion\n"); } -static int bdisp_dbg_last_nodes(struct seq_file *s, void *data) +static int last_nodes_show(struct seq_file *s, void *data) { /* Not dumping all fields, focusing on significant ones */ struct bdisp_dev *bdisp = s->private; @@ -388,7 +388,7 @@ static int bdisp_dbg_last_nodes(struct seq_file *s, void *data) return 0; } -static int bdisp_dbg_last_nodes_raw(struct seq_file *s, void *data) +static int last_nodes_raw_show(struct seq_file *s, void *data) { struct bdisp_dev *bdisp = s->private; struct bdisp_node *node; @@ -437,7 +437,7 @@ static const char *bdisp_fmt_to_str(struct bdisp_frame frame) } } -static int bdisp_dbg_last_request(struct seq_file *s, void *data) +static int last_request_show(struct seq_file *s, void *data) { struct bdisp_dev *bdisp = s->private; struct bdisp_request *request = &bdisp->dbg.copy_request; @@ -474,7 +474,7 @@ static int bdisp_dbg_last_request(struct seq_file *s, void *data) #define DUMP(reg) seq_printf(s, #reg " \t0x%08X\n", readl(bdisp->regs + reg)) -static int bdisp_dbg_regs(struct seq_file *s, void *data) +static int regs_show(struct seq_file *s, void *data) { struct bdisp_dev *bdisp = s->private; int ret; @@ -582,7 +582,7 @@ static int bdisp_dbg_regs(struct seq_file *s, void *data) #define SECOND 1000000 -static int bdisp_dbg_perf(struct seq_file *s, void *data) +static int perf_show(struct seq_file *s, void *data) { struct bdisp_dev *bdisp = s->private; struct bdisp_request *request = &bdisp->dbg.copy_request; @@ -627,27 +627,15 @@ static int bdisp_dbg_perf(struct seq_file *s, void *data) return 0; } -#define bdisp_dbg_declare(name) \ - static int bdisp_dbg_##name##_open(struct inode *i, struct file *f) \ - { \ - return single_open(f, bdisp_dbg_##name, i->i_private); \ - } \ - static const struct file_operations bdisp_dbg_##name##_fops = { \ - .open = bdisp_dbg_##name##_open, \ - .read = seq_read, \ - .llseek = seq_lseek, \ - .release = single_release, \ - } - #define bdisp_dbg_create_entry(name) \ debugfs_create_file(#name, S_IRUGO, bdisp->dbg.debugfs_entry, bdisp, \ - &bdisp_dbg_##name##_fops) + &name##_fops) -bdisp_dbg_declare(regs); -bdisp_dbg_declare(last_nodes); -bdisp_dbg_declare(last_nodes_raw); -bdisp_dbg_declare(last_request); -bdisp_dbg_declare(perf); +DEFINE_SHOW_ATTRIBUTE(regs); +DEFINE_SHOW_ATTRIBUTE(last_nodes); +DEFINE_SHOW_ATTRIBUTE(last_nodes_raw); +DEFINE_SHOW_ATTRIBUTE(last_request); +DEFINE_SHOW_ATTRIBUTE(perf); int bdisp_debugfs_create(struct bdisp_dev *bdisp) { diff --git a/drivers/media/platform/sti/hva/hva-debugfs.c b/drivers/media/platform/sti/hva/hva-debugfs.c index 9f7e8ac875d1..7d12a5b5d914 100644 --- a/drivers/media/platform/sti/hva/hva-debugfs.c +++ b/drivers/media/platform/sti/hva/hva-debugfs.c @@ -271,7 +271,7 @@ static void hva_dbg_perf_compute(struct hva_ctx *ctx) * device debug info */ -static int hva_dbg_device(struct seq_file *s, void *data) +static int device_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; @@ -281,7 +281,7 @@ static int hva_dbg_device(struct seq_file *s, void *data) return 0; } -static int hva_dbg_encoders(struct seq_file *s, void *data) +static int encoders_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; unsigned int i = 0; @@ -299,7 +299,7 @@ static int hva_dbg_encoders(struct seq_file *s, void *data) return 0; } -static int hva_dbg_last(struct seq_file *s, void *data) +static int last_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; struct hva_ctx *last_ctx = &hva->dbg.last_ctx; @@ -316,7 +316,7 @@ static int hva_dbg_last(struct seq_file *s, void *data) return 0; } -static int hva_dbg_regs(struct seq_file *s, void *data) +static int regs_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; @@ -325,26 +325,14 @@ static int hva_dbg_regs(struct seq_file *s, void *data) return 0; } -#define hva_dbg_declare(name) \ - static int hva_dbg_##name##_open(struct inode *i, struct file *f) \ - { \ - return single_open(f, hva_dbg_##name, i->i_private); \ - } \ - static const struct file_operations hva_dbg_##name##_fops = { \ - .open = hva_dbg_##name##_open, \ - .read = seq_read, \ - .llseek = seq_lseek, \ - .release = single_release, \ - } - #define hva_dbg_create_entry(name) \ debugfs_create_file(#name, 0444, hva->dbg.debugfs_entry, hva, \ - &hva_dbg_##name##_fops) + &name##_fops) -hva_dbg_declare(device); -hva_dbg_declare(encoders); -hva_dbg_declare(last); -hva_dbg_declare(regs); +DEFINE_SHOW_ATTRIBUTE(device); +DEFINE_SHOW_ATTRIBUTE(encoders); +DEFINE_SHOW_ATTRIBUTE(last); +DEFINE_SHOW_ATTRIBUTE(regs); void hva_debugfs_create(struct hva_dev *hva) { @@ -380,7 +368,7 @@ void hva_debugfs_remove(struct hva_dev *hva) * context (instance) debug info */ -static int hva_dbg_ctx(struct seq_file *s, void *data) +static int ctx_show(struct seq_file *s, void *data) { struct hva_ctx *ctx = s->private; @@ -392,7 +380,7 @@ static int hva_dbg_ctx(struct seq_file *s, void *data) return 0; } -hva_dbg_declare(ctx); +DEFINE_SHOW_ATTRIBUTE(ctx); void hva_dbg_ctx_create(struct hva_ctx *ctx) { @@ -407,7 +395,7 @@ void hva_dbg_ctx_create(struct hva_ctx *ctx) ctx->dbg.debugfs_entry = debugfs_create_file(name, 0444, hva->dbg.debugfs_entry, - ctx, &hva_dbg_ctx_fops); + ctx, &ctx_fops); } void hva_dbg_ctx_remove(struct hva_ctx *ctx) -- cgit v1.2.3-59-g8ed1b From 8d19d5d03b4d09177b0ae87f964eb751e6f51b7b Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Fri, 14 Dec 2018 04:18:21 -0200 Subject: media: rcar-vin: fix wrong return value in rvin_set_channel_routing() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the operation in rvin_set_channel_routing() is successful the 'ret' variable contains the runtime PM use count for the VIN master device. The intention is not to return the use count to the caller but to return 0 on success else none zero. Fix this by always returning 0 if the operation is successful. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 92323310f735..beb9248992a4 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -1341,5 +1341,5 @@ int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel) pm_runtime_put(vin->dev); - return ret; + return 0; } -- cgit v1.2.3-59-g8ed1b From 6f4b9d9a6c08f692f627700c2d0e250e406ac81f Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Wed, 9 Jan 2019 12:19:19 -0200 Subject: media: cedrus: Cleanup duplicate declarations from cedrus_dec header Some leftover declarations are still in the cedrus_dec header although they were moved to cedrus_video already. Clean them up. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_dec.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h index 4f423d3a1cad..d1ae7903677b 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h @@ -16,12 +16,6 @@ #ifndef _CEDRUS_DEC_H_ #define _CEDRUS_DEC_H_ -extern const struct v4l2_ioctl_ops cedrus_ioctl_ops; - -void cedrus_device_work(struct work_struct *work); void cedrus_device_run(void *priv); -int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); - #endif -- cgit v1.2.3-59-g8ed1b From cf20ae1535eb690a87c29b9cc7af51881384e967 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Wed, 9 Jan 2019 12:19:20 -0200 Subject: media: cedrus: Allow using the current dst buffer as reference It was reported that some cases of interleaved video decoding require using the current destination buffer as a reference. However, this is no longer possible after the move to vb2_find_timestamp because only dequeued and done buffers are considered. Add a helper in our driver that also considers the current destination buffer before resorting to vb2_find_timestamp and use it in MPEG-2. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_dec.c | 13 +++++++++++++ drivers/staging/media/sunxi/cedrus/cedrus_dec.h | 2 ++ drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c | 10 ++++++---- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 443fb037e1cf..2c295286766c 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -22,6 +22,19 @@ #include "cedrus_dec.h" #include "cedrus_hw.h" +int cedrus_reference_index_find(struct vb2_queue *queue, + struct vb2_buffer *vb2_buf, u64 timestamp) +{ + /* + * Allow using the current capture buffer as reference, which can occur + * for field-coded pictures. + */ + if (vb2_buf->timestamp == timestamp) + return vb2_buf->index; + else + return vb2_find_timestamp(queue, timestamp, 0); +} + void cedrus_device_run(void *priv) { struct cedrus_ctx *ctx = priv; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h index d1ae7903677b..8d0fc248220f 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h @@ -16,6 +16,8 @@ #ifndef _CEDRUS_DEC_H_ #define _CEDRUS_DEC_H_ +int cedrus_reference_index_find(struct vb2_queue *queue, + struct vb2_buffer *vb2_buf, u64 timestamp); void cedrus_device_run(void *priv); #endif diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index cb45fda9aaeb..81c66a8aa1ac 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -10,6 +10,7 @@ #include #include "cedrus.h" +#include "cedrus_dec.h" #include "cedrus_hw.h" #include "cedrus_regs.h" @@ -159,8 +160,8 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_PICBOUNDSIZE, reg); /* Forward and backward prediction reference buffers. */ - forward_idx = vb2_find_timestamp(cap_q, - slice_params->forward_ref_ts, 0); + forward_idx = cedrus_reference_index_find(cap_q, &run->dst->vb2_buf, + slice_params->forward_ref_ts); fwd_luma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 0); fwd_chroma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 1); @@ -168,8 +169,9 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr); - backward_idx = vb2_find_timestamp(cap_q, - slice_params->backward_ref_ts, 0); + backward_idx = cedrus_reference_index_find(cap_q, &run->dst->vb2_buf, + slice_params->backward_ref_ts); + bwd_luma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 0); bwd_chroma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 1); -- cgit v1.2.3-59-g8ed1b From fb517583b3fe0bb6e64b8a861bfae5dc39f1cedb Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Fri, 11 Jan 2019 14:17:03 -0200 Subject: media: i2c: adv748x: Use devm to allocate the device struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to devm_kzalloc() when allocating the adv748x device struct. The sizeof() is updated to determine the correct allocation size from the dereferenced pointer type rather than hardcoding the struct type. [Kieran: Change sizeof() to dereference the pointer type] Signed-off-by: Steve Longerbeam Signed-off-by: Kieran Bingham Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index d94c63cb6a2e..bd8a8c8d6f8e 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -631,7 +631,7 @@ static int adv748x_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - state = kzalloc(sizeof(struct adv748x_state), GFP_KERNEL); + state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; @@ -729,7 +729,6 @@ err_cleanup_dt: adv748x_dt_cleanup(state); err_free_mutex: mutex_destroy(&state->mutex); - kfree(state); return ret; } @@ -748,8 +747,6 @@ static int adv748x_remove(struct i2c_client *client) adv748x_dt_cleanup(state); mutex_destroy(&state->mutex); - kfree(state); - return 0; } -- cgit v1.2.3-59-g8ed1b From 04ee6d614c0dbe4350c61e8909c2ca01e364884d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Jan 2019 10:27:45 -0200 Subject: media: vimc: fill in correct driver name in querycap The driver name as returned in v4l2_capabilities must be vimc, not vimc_capture. Fix this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 2 +- drivers/media/platform/vimc/vimc-common.h | 2 ++ drivers/media/platform/vimc/vimc-core.c | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 3f7e9ed56633..aaeddf24b042 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -71,7 +71,7 @@ static int vimc_cap_querycap(struct file *file, void *priv, { struct vimc_cap_device *vcap = video_drvdata(file); - strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strscpy(cap->driver, VIMC_PDEV_NAME, sizeof(cap->driver)); strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vcap->vdev.v4l2_dev->name); diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 2e9981b18166..f491c33c7c14 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -22,6 +22,8 @@ #include #include +#define VIMC_PDEV_NAME "vimc" + /* VIMC-specific controls */ #define VIMC_CID_VIMC_BASE (0x00f00000 | 0xf000) #define VIMC_CID_VIMC_CLASS (0x00f00000 | 1) diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index ce809d2e3d53..bf19f1f9795e 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -24,7 +24,6 @@ #include "vimc-common.h" -#define VIMC_PDEV_NAME "vimc" #define VIMC_MDEV_MODEL_NAME "VIMC MDEV" #define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \ -- cgit v1.2.3-59-g8ed1b From 07b8fd86556c64e8424f198f1b6a75ca194520f9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 09:46:26 -0200 Subject: media: vidioc-prepare-buf.rst: drop reference to NO_CACHE flags This was never implemented, so do not document this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/vidioc-prepare-buf.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Documentation/media/uapi/v4l/vidioc-prepare-buf.rst b/Documentation/media/uapi/v4l/vidioc-prepare-buf.rst index 60986710967b..7c6b5f4e1011 100644 --- a/Documentation/media/uapi/v4l/vidioc-prepare-buf.rst +++ b/Documentation/media/uapi/v4l/vidioc-prepare-buf.rst @@ -43,10 +43,7 @@ Applications can optionally call the :ref:`VIDIOC_PREPARE_BUF` ioctl to pass ownership of the buffer to the driver before actually enqueuing it, using the :ref:`VIDIOC_QBUF ` ioctl, and to prepare it for future I/O. Such preparations may include cache invalidation or cleaning. Performing them -in advance saves time during the actual I/O. In case such cache -operations are not required, the application can use one of -``V4L2_BUF_FLAG_NO_CACHE_INVALIDATE`` and -``V4L2_BUF_FLAG_NO_CACHE_CLEAN`` flags to skip the respective step. +in advance saves time during the actual I/O. The struct :c:type:`v4l2_buffer` structure is specified in :ref:`buffer`. -- cgit v1.2.3-59-g8ed1b From 0a44baa4e9959d67b01b24a38b90dd903f89a0b6 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Wed, 16 Jan 2019 16:49:33 -0200 Subject: media: media/v4l2-core/videobuf-vmalloc.c: Remove dead code This code is commented since version 3.7. If there is no plan to use it in future, we can remove this dead code. Signed-off-by: Souptick Joarder Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf-vmalloc.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf-vmalloc.c b/drivers/media/v4l2-core/videobuf-vmalloc.c index 45fe781aeeec..293213ad9ef7 100644 --- a/drivers/media/v4l2-core/videobuf-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf-vmalloc.c @@ -196,26 +196,6 @@ static int __videobuf_iolock(struct videobuf_queue *q, } dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages); - -#if 0 - int rc; - /* Kernel userptr is used also by read() method. In this case, - there's no need to remap, since data will be copied to user - */ - if (!vb->baddr) - return 0; - - /* FIXME: to properly support USERPTR, remap should occur. - The code below won't work, since mem->vma = NULL - */ - /* Try to remap memory */ - rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0); - if (rc < 0) { - printk(KERN_ERR "mmap: remap failed with error %d", rc); - return -ENOMEM; - } -#endif - break; case V4L2_MEMORY_OVERLAY: default: -- cgit v1.2.3-59-g8ed1b From fbf9aa6aeaebb10afb9d4fca013c4823ae0b5101 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 8 Jan 2019 15:20:16 -0200 Subject: media: coda: use macroblock tiling on CODA960 only Coda7541 and earlier do not support macroblock tiling. They do support the NV12 format, though. Enable macroblock tiling for NV12 only on CODA960. This fixes crashes when trying to use NV12 support on CodaHx4. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 390d1ce6ab32..fa0b22fb7991 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -728,7 +728,7 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f, ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; break; case V4L2_PIX_FMT_NV12: - if (!disable_tiling) { + if (!disable_tiling && ctx->dev->devtype->product == CODA_960) { ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; break; } -- cgit v1.2.3-59-g8ed1b From cd9f125cfaa5047899e1d39d6fc0a9ed5e40afa7 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 8 Jan 2019 15:20:43 -0200 Subject: media: coda: fix decoder capture buffer payload It is not correct to calculate decoder capture payload dynamically from the decoded frame width and height reported by the firmware. These tell us what the decoder wrote into the internal framebuffers. The rotator or VDOA always write the full sizeimage when copying the previously decoded frame from the internal framebuffers into the capture queue. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-bit.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 88065b07149c..b4f396c2e72c 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -2024,7 +2024,6 @@ static void coda_finish_decode(struct coda_ctx *ctx) struct coda_q_data *q_data_dst; struct vb2_v4l2_buffer *dst_buf; struct coda_buffer_meta *meta; - unsigned long payload; int width, height; int decoded_idx; int display_idx; @@ -2230,21 +2229,8 @@ 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: - default: - payload = width * height * 3 / 2; - break; - case V4L2_PIX_FMT_YUV422P: - payload = width * height * 2; - break; - } - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + q_data_dst->sizeimage); if (ctx->frame_errors[ctx->display_idx] || err_vdoa) coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR); -- cgit v1.2.3-59-g8ed1b From 439d8186fb235ab6d9bc08a23a8a9530a5f3ac95 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 17 Jan 2019 13:51:52 -0200 Subject: media: imx: add capture compose rectangle Allowing to compose captured images into larger memory buffers will let us lift alignment restrictions on CSI crop width. For now all compose rectangles are identical to the complete frame width / height. This will be changed in the next patches. Signed-off-by: Philipp Zabel Acked-by: Sakari Ailus Reviewed-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 +-- drivers/staging/media/imx/imx-media-capture.c | 27 +++++++++++++++++++++++++++ drivers/staging/media/imx/imx-media-csi.c | 3 +-- drivers/staging/media/imx/imx-media-vdic.c | 4 ++-- drivers/staging/media/imx/imx-media.h | 2 ++ 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 33ada6612fee..c7855fbc449d 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -371,8 +371,7 @@ static int prp_setup_channel(struct prp_priv *priv, memset(&image, 0, sizeof(image)); image.pix = vdev->fmt.fmt.pix; - image.rect.width = image.pix.width; - image.rect.height = image.pix.height; + image.rect = vdev->compose; /* * If the field type at capture interface is interlaced, and diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 01ec9443de55..8b8ef4a11774 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -276,6 +276,10 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, priv->vdev.fmt.fmt.pix = f->fmt.pix; priv->vdev.cc = imx_media_find_format(f->fmt.pix.pixelformat, CS_SEL_ANY, true); + priv->vdev.compose.left = 0; + priv->vdev.compose.top = 0; + priv->vdev.compose.width = f->fmt.pix.width; + priv->vdev.compose.height = f->fmt.pix.height; return 0; } @@ -304,6 +308,25 @@ static int capture_s_std(struct file *file, void *fh, v4l2_std_id std) return v4l2_subdev_call(priv->src_sd, video, s_std, std); } +static int capture_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct capture_priv *priv = video_drvdata(file); + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_PADDED: + s->r = priv->vdev.compose; + break; + default: + return -EINVAL; + } + + return 0; +} + static int capture_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { @@ -364,6 +387,8 @@ static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_g_std = capture_g_std, .vidioc_s_std = capture_s_std, + .vidioc_g_selection = capture_g_selection, + .vidioc_g_parm = capture_g_parm, .vidioc_s_parm = capture_s_parm, @@ -701,6 +726,8 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev) vdev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix, &fmt_src.format, NULL); + vdev->compose.width = fmt_src.format.width; + vdev->compose.height = fmt_src.format.height; vdev->cc = imx_media_find_format(vdev->fmt.fmt.pix.pixelformat, CS_SEL_ANY, false); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 555aa45e02e3..39fad3bfa6ca 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -419,8 +419,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) memset(&image, 0, sizeof(image)); image.pix = vdev->fmt.fmt.pix; - image.rect.width = image.pix.width; - image.rect.height = image.pix.height; + image.rect = vdev->compose; csi_idmac_setup_vb2_buf(priv, phys); diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 4a890714193e..297951d98ab5 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -255,10 +255,10 @@ static int setup_vdi_channel(struct vdic_priv *priv, memset(&image, 0, sizeof(image)); image.pix = vdev->fmt.fmt.pix; + image.rect = vdev->compose; /* one field to VDIC channels */ image.pix.height /= 2; - image.rect.width = image.pix.width; - image.rect.height = image.pix.height; + image.rect.height /= 2; image.phys0 = phys0; image.phys1 = phys1; diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index bc7feb81937c..7a0e658753f0 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -80,6 +80,8 @@ struct imx_media_video_dev { /* the user format */ struct v4l2_format fmt; + /* the compose rectangle */ + struct v4l2_rect compose; const struct imx_media_pixfmt *cc; /* links this vdev to master list */ -- cgit v1.2.3-59-g8ed1b From 20997568f9a621c54d5ffcf2b6973fd672148cb6 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 17 Jan 2019 13:51:53 -0200 Subject: media: imx: set compose rectangle to mbus format Prepare for mbus format being smaller than the written rectangle due to burst size. Signed-off-by: Philipp Zabel Reviewed-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-capture.c | 68 ++++++++++++++++++--------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 8b8ef4a11774..a93bd7f388ef 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -203,21 +203,13 @@ static int capture_g_fmt_vid_cap(struct file *file, void *fh, return 0; } -static int capture_try_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) +static int __capture_try_fmt_vid_cap(struct capture_priv *priv, + struct v4l2_subdev_format *fmt_src, + struct v4l2_format *f) { - struct capture_priv *priv = video_drvdata(file); - struct v4l2_subdev_format fmt_src; const struct imx_media_pixfmt *cc, *cc_src; - int ret; - fmt_src.pad = priv->src_sd_pad; - fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); - if (ret) - return ret; - - cc_src = imx_media_find_ipu_format(fmt_src.format.code, CS_SEL_ANY); + cc_src = imx_media_find_ipu_format(fmt_src->format.code, CS_SEL_ANY); if (cc_src) { u32 fourcc, cs_sel; @@ -231,7 +223,7 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, cc = imx_media_find_format(fourcc, cs_sel, false); } } else { - cc_src = imx_media_find_mbus_format(fmt_src.format.code, + cc_src = imx_media_find_mbus_format(fmt_src->format.code, CS_SEL_ANY, true); if (WARN_ON(!cc_src)) return -EINVAL; @@ -241,27 +233,44 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, /* allow IDMAC interweave but enforce field order from source */ if (V4L2_FIELD_IS_INTERLACED(f->fmt.pix.field)) { - switch (fmt_src.format.field) { + switch (fmt_src->format.field) { case V4L2_FIELD_SEQ_TB: - fmt_src.format.field = V4L2_FIELD_INTERLACED_TB; + fmt_src->format.field = V4L2_FIELD_INTERLACED_TB; break; case V4L2_FIELD_SEQ_BT: - fmt_src.format.field = V4L2_FIELD_INTERLACED_BT; + fmt_src->format.field = V4L2_FIELD_INTERLACED_BT; break; default: break; } } - imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src.format, cc); + imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src->format, cc); return 0; } +static int capture_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct capture_priv *priv = video_drvdata(file); + struct v4l2_subdev_format fmt_src; + int ret; + + fmt_src.pad = priv->src_sd_pad; + fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); + if (ret) + return ret; + + return __capture_try_fmt_vid_cap(priv, &fmt_src, f); +} + static int capture_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { struct capture_priv *priv = video_drvdata(file); + struct v4l2_subdev_format fmt_src; int ret; if (vb2_is_busy(&priv->q)) { @@ -269,7 +278,13 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, return -EBUSY; } - ret = capture_try_fmt_vid_cap(file, priv, f); + fmt_src.pad = priv->src_sd_pad; + fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); + if (ret) + return ret; + + ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f); if (ret) return ret; @@ -278,8 +293,8 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, CS_SEL_ANY, true); priv->vdev.compose.left = 0; priv->vdev.compose.top = 0; - priv->vdev.compose.width = f->fmt.pix.width; - priv->vdev.compose.height = f->fmt.pix.height; + priv->vdev.compose.width = fmt_src.format.width; + priv->vdev.compose.height = fmt_src.format.height; return 0; } @@ -317,9 +332,20 @@ static int capture_g_selection(struct file *file, void *fh, case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_DEFAULT: case V4L2_SEL_TGT_COMPOSE_BOUNDS: - case V4L2_SEL_TGT_COMPOSE_PADDED: + /* The compose rectangle is fixed to the source format. */ s->r = priv->vdev.compose; break; + case V4L2_SEL_TGT_COMPOSE_PADDED: + /* + * The hardware writes with a configurable but fixed DMA burst + * size. If the source format width is not burst size aligned, + * the written frame contains padding to the right. + */ + s->r.left = 0; + s->r.top = 0; + s->r.width = priv->vdev.fmt.fmt.pix.width; + s->r.height = priv->vdev.fmt.fmt.pix.height; + break; default: return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 451a7b7815d0b9c6e46d005a40d4b07b721fbef4 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 17 Jan 2019 13:51:54 -0200 Subject: media: imx: lift CSI and PRP ENC/VF width alignment restriction The CSI, PRP ENC, and PRP VF subdevices shouldn't have to care about IDMAC line start address alignment. With compose rectangle support in the capture driver, they don't have to anymore. If the direct CSI -> IC path is enabled, the CSI output width must still be aligned to 8 pixels (IC burst length). Signed-off-by: Philipp Zabel Reviewed-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 2 +- drivers/staging/media/imx/imx-media-csi.c | 11 +++++++++-- drivers/staging/media/imx/imx-media-utils.c | 15 ++++++++++++--- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index c7855fbc449d..053a911d477a 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -48,7 +48,7 @@ #define MAX_W_SRC 1024 #define MAX_H_SRC 1024 -#define W_ALIGN_SRC 4 /* multiple of 16 pixels */ +#define W_ALIGN_SRC 1 /* multiple of 2 pixels */ #define H_ALIGN_SRC 1 /* multiple of 2 lines */ #define S_ALIGN 1 /* multiple of 2 */ diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 39fad3bfa6ca..8857994b2a16 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -41,7 +41,7 @@ #define MIN_H 144 #define MAX_W 4096 #define MAX_H 4096 -#define W_ALIGN 4 /* multiple of 16 pixels */ +#define W_ALIGN 1 /* multiple of 2 pixels */ #define H_ALIGN 1 /* multiple of 2 lines */ #define S_ALIGN 1 /* multiple of 2 */ @@ -1029,6 +1029,8 @@ static int csi_link_setup(struct media_entity *entity, v4l2_ctrl_handler_free(&priv->ctrl_hdlr); v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0); priv->sink = NULL; + /* do not apply IC burst alignment in csi_try_crop */ + priv->active_output_pad = CSI_SRC_PAD_IDMAC; goto out; } @@ -1172,7 +1174,10 @@ static void csi_try_crop(struct csi_priv *priv, crop->left = infmt->width - crop->width; /* adjust crop left/width to h/w alignment restrictions */ crop->left &= ~0x3; - crop->width &= ~0x7; + if (priv->active_output_pad == CSI_SRC_PAD_DIRECT) + crop->width &= ~0x7; /* multiple of 8 pixels (IC burst) */ + else + crop->width &= ~0x1; /* multiple of 2 pixels */ in_height = infmt->height; if (infmt->field == V4L2_FIELD_ALTERNATE) @@ -1937,6 +1942,8 @@ static int imx_csi_probe(struct platform_device *pdev) priv->csi_id = pdata->csi; priv->smfc_id = (priv->csi_id == 0) ? 0 : 2; + priv->active_output_pad = CSI_SRC_PAD_IDMAC; + timer_setup(&priv->eof_timeout_timer, csi_idmac_eof_timeout, 0); spin_lock_init(&priv->irqlock); diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 0eaa353d5cb3..5f110d90a4ef 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -580,6 +580,7 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mbus, const struct imx_media_pixfmt *cc) { + u32 width; u32 stride; if (!cc) { @@ -602,9 +603,16 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, cc = imx_media_find_mbus_format(code, CS_SEL_YUV, false); } - stride = cc->planar ? mbus->width : (mbus->width * cc->bpp) >> 3; + /* Round up width for minimum burst size */ + width = round_up(mbus->width, 8); - pix->width = mbus->width; + /* Round up stride for IDMAC line start address alignment */ + if (cc->planar) + stride = round_up(width, 16); + else + stride = round_up((width * cc->bpp) >> 3, 8); + + pix->width = width; pix->height = mbus->height; pix->pixelformat = cc->fourcc; pix->colorspace = mbus->colorspace; @@ -613,7 +621,8 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, pix->quantization = mbus->quantization; pix->field = mbus->field; pix->bytesperline = stride; - pix->sizeimage = (pix->width * pix->height * cc->bpp) >> 3; + pix->sizeimage = cc->planar ? ((stride * pix->height * cc->bpp) >> 3) : + stride * pix->height; return 0; } -- cgit v1.2.3-59-g8ed1b From 958f047a250ae9000308de050e4285ecff5089cf Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Tue, 15 Jan 2019 17:15:46 -0200 Subject: media: imx: queue subdev events to reachable video devices In order to receive events generated by subdevices on the video capture nodes, those events need to be forwarded to the subdevice's list of reachable video capture devices. Note this will queue the event to a video device even if there is no actual _enabled_ media path from the sub-device to the video device. So a future improvement is to skip the video device if there is no enabled path to it from the sub-device. The entity->pipe pointer can't be used for this check because in imx-media a sub-device can be a member to more than one streaming pipeline at a time. Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-dev.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 4b344a4a3706..25e916562c66 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -442,6 +442,29 @@ static const struct media_device_ops imx_media_md_ops = { .link_notify = imx_media_link_notify, }; +static void imx_media_notify(struct v4l2_subdev *sd, + unsigned int notification, + void *arg) +{ + struct media_entity *entity = &sd->entity; + int i; + + if (notification != V4L2_DEVICE_NOTIFY_EVENT) + return; + + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad = &entity->pads[i]; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + + pad_vdev_list = to_pad_vdev_list(sd, pad->index); + if (!pad_vdev_list) + continue; + list_for_each_entry(pad_vdev, pad_vdev_list, list) + v4l2_event_queue(pad_vdev->vdev->vfd, arg); + } +} + static int imx_media_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -462,6 +485,7 @@ static int imx_media_probe(struct platform_device *pdev) mutex_init(&imxmd->mutex); imxmd->v4l2_dev.mdev = &imxmd->md; + imxmd->v4l2_dev.notify = imx_media_notify; strscpy(imxmd->v4l2_dev.name, "imx-media", sizeof(imxmd->v4l2_dev.name)); -- cgit v1.2.3-59-g8ed1b From 54f4bc2f2aada71a681b79b64cf74ca6e8c5d26c Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Tue, 15 Jan 2019 17:16:08 -0200 Subject: media: imx: capture: Allow event subscribe/unsubscribe Implement the vidioc_(un)subscribe_event operations. Imx will allow subscribing to the imx-specific frame interval error events, events from subdevices (V4L2_EVENT_SOURCE_CHANGE), and control events. Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-capture.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index a93bd7f388ef..f92edee63aa6 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -398,6 +398,21 @@ static int capture_s_parm(struct file *file, void *fh, return 0; } +static int capture_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_querycap = vidioc_querycap, @@ -427,6 +442,9 @@ static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_subscribe_event = capture_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* -- cgit v1.2.3-59-g8ed1b From 337e90ed028643c7acdfd0d31e3224d05ca03d66 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Thu, 17 Jan 2019 18:58:37 -0200 Subject: media: imx-csi: Input connections to CSI should be optional Some imx platforms do not have fwnode connections to all CSI input ports, and should not be treated as an error. This includes the imx6q SabreAuto, which has no connections to ipu1_csi1 and ipu2_csi0. Return -ENOTCONN in imx_csi_parse_endpoint() so that v4l2-fwnode endpoint parsing will not treat an unconnected CSI input port as an error. Fixes: c893500a16baf ("media: imx: csi: Register a subdev notifier") Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Tim Harvey Cc: stable@vger.kernel.org Tested-by: Fabio Estevam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 8857994b2a16..7abfe0aa1418 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1865,7 +1865,7 @@ static int imx_csi_parse_endpoint(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) { - return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; + return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN; } static int imx_csi_async_register(struct csi_priv *priv) -- cgit v1.2.3-59-g8ed1b From 1455dddc74bd0951a4ea7e77917ebf3a43cc46d8 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 11 Jan 2019 14:30:42 -0200 Subject: media: imx274: remote unused function imx274_read_reg imx274_read_reg() is not used since commit ca017467c78b ("media: imx274: add helper to read multibyte registers"). Signed-off-by: Luca Ceresoli Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx274.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 95e3d90309e8..c2ac2fd1b96b 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -617,24 +617,6 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) return 0; } -static inline int imx274_read_reg(struct stimx274 *priv, u16 addr, u8 *val) -{ - unsigned int uint_val; - int err; - - err = regmap_read(priv->regmap, addr, &uint_val); - if (err) - dev_err(&priv->client->dev, - "%s : i2c read failed, addr = %x\n", __func__, addr); - else - dev_dbg(&priv->client->dev, - "%s : addr 0x%x, val=0x%x\n", __func__, - addr, uint_val); - - *val = uint_val; - return err; -} - static inline int imx274_write_reg(struct stimx274 *priv, u16 addr, u8 val) { int err; -- cgit v1.2.3-59-g8ed1b From 879347f0c258cffe27b9e28bd4f1f55fc019f857 Mon Sep 17 00:00:00 2001 From: Ben Kao Date: Tue, 15 Jan 2019 06:30:29 -0200 Subject: media: ov8856: Add support for OV8856 sensor This patch adds driver for Omnivision's ov8856 sensor, the driver supports following features: - manual exposure/gain(analog and digital) control support - two link frequencies - VBLANK/HBLANK support - test pattern support - media controller support - runtime PM support - enable Vsync signal output - supported resolutions + 3280x2464 at 30FPS + 1640x1232 at 30FPS Signed-off-by: Ben Kao Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 + drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov8856.c | 1267 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1287 insertions(+) create mode 100644 drivers/media/i2c/ov8856.c diff --git a/MAINTAINERS b/MAINTAINERS index 17ad1d7b5510..3e211916d2bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11234,6 +11234,13 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/ov9640.* +OMNIVISION OV8856 SENSOR DRIVER +M: Ben Kao +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/ov8856.c + OMNIVISION OV9650 SENSOR DRIVER M: Sakari Ailus R: Akinobu Mita diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index e6cd5cf7c3d9..54a26e24baf4 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -820,6 +820,18 @@ config VIDEO_OV7740 This is a Video4Linux2 sensor driver for the OmniVision OV7740 VGA camera sensor. +config VIDEO_OV8856 + tristate "OmniVision OV8856 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV8856 camera sensor. + + To compile this driver as a module, choose M here: the + module will be called ov8856. + config VIDEO_OV9640 tristate "OmniVision OV9640 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 5639cb2f20aa..29bf3fa81113 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV772X) += ov772x.o obj-$(CONFIG_VIDEO_OV7740) += ov7740.o +obj-$(CONFIG_VIDEO_OV8856) += ov8856.o obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c new file mode 100644 index 000000000000..c0d44082c003 --- /dev/null +++ b/drivers/media/i2c/ov8856.c @@ -0,0 +1,1267 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OV8856_REG_VALUE_08BIT 1 +#define OV8856_REG_VALUE_16BIT 2 +#define OV8856_REG_VALUE_24BIT 3 + +#define OV8856_LINK_FREQ_360MHZ 360000000ULL +#define OV8856_LINK_FREQ_180MHZ 180000000ULL +#define OV8856_SCLK 144000000ULL +#define OV8856_MCLK 19200000 +#define OV8856_DATA_LANES 4 +#define OV8856_RGB_DEPTH 10 + +#define OV8856_REG_CHIP_ID 0x300a +#define OV8856_CHIP_ID 0x00885a + +#define OV8856_REG_MODE_SELECT 0x0100 +#define OV8856_MODE_STANDBY 0x00 +#define OV8856_MODE_STREAMING 0x01 + +/* vertical-timings from sensor */ +#define OV8856_REG_VTS 0x380e +#define OV8856_VTS_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define OV8856_REG_HTS 0x380c + +/* Exposure controls from sensor */ +#define OV8856_REG_EXPOSURE 0x3500 +#define OV8856_EXPOSURE_MIN 6 +#define OV8856_EXPOSURE_MAX_MARGIN 6 +#define OV8856_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV8856_REG_ANALOG_GAIN 0x3508 +#define OV8856_ANAL_GAIN_MIN 128 +#define OV8856_ANAL_GAIN_MAX 2047 +#define OV8856_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define OV8856_REG_MWB_R_GAIN 0x5019 +#define OV8856_REG_MWB_G_GAIN 0x501b +#define OV8856_REG_MWB_B_GAIN 0x501d +#define OV8856_DGTL_GAIN_MIN 0 +#define OV8856_DGTL_GAIN_MAX 4095 +#define OV8856_DGTL_GAIN_STEP 1 +#define OV8856_DGTL_GAIN_DEFAULT 1024 + +/* Test Pattern Control */ +#define OV8856_REG_TEST_PATTERN 0x5e00 +#define OV8856_TEST_PATTERN_ENABLE BIT(7) +#define OV8856_TEST_PATTERN_BAR_SHIFT 2 + +#define to_ov8856(_sd) container_of(_sd, struct ov8856, sd) + +enum { + OV8856_LINK_FREQ_720MBPS, + OV8856_LINK_FREQ_360MBPS, +}; + +struct ov8856_reg { + u16 address; + u8 val; +}; + +struct ov8856_reg_list { + u32 num_of_regs; + const struct ov8856_reg *regs; +}; + +struct ov8856_link_freq_config { + const struct ov8856_reg_list reg_list; +}; + +struct ov8856_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Default vertical timining size */ + u32 vts_def; + + /* Min vertical timining size */ + u32 vts_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct ov8856_reg_list reg_list; +}; + +static const struct ov8856_reg mipi_data_rate_720mbps[] = { + {0x0103, 0x01}, + {0x0100, 0x00}, + {0x0302, 0x4b}, + {0x0303, 0x01}, + {0x030b, 0x02}, + {0x030d, 0x4b}, + {0x031e, 0x0c}, +}; + +static const struct ov8856_reg mipi_data_rate_360mbps[] = { + {0x0103, 0x01}, + {0x0100, 0x00}, + {0x0302, 0x4b}, + {0x0303, 0x03}, + {0x030b, 0x02}, + {0x030d, 0x4b}, + {0x031e, 0x0c}, +}; + +static const struct ov8856_reg mode_3280x2464_regs[] = { + {0x3000, 0x20}, + {0x3003, 0x08}, + {0x300e, 0x20}, + {0x3010, 0x00}, + {0x3015, 0x84}, + {0x3018, 0x72}, + {0x3021, 0x23}, + {0x3033, 0x24}, + {0x3500, 0x00}, + {0x3501, 0x9a}, + {0x3502, 0x20}, + {0x3503, 0x08}, + {0x3505, 0x83}, + {0x3508, 0x01}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x350e, 0x04}, + {0x350f, 0x00}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3600, 0x72}, + {0x3601, 0x40}, + {0x3602, 0x30}, + {0x3610, 0xc5}, + {0x3611, 0x58}, + {0x3612, 0x5c}, + {0x3613, 0xca}, + {0x3614, 0x20}, + {0x3628, 0xff}, + {0x3629, 0xff}, + {0x362a, 0xff}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x10}, + {0x3663, 0x08}, + {0x3669, 0x34}, + {0x366e, 0x10}, + {0x3706, 0x86}, + {0x370b, 0x7e}, + {0x3714, 0x23}, + {0x3730, 0x12}, + {0x3733, 0x10}, + {0x3764, 0x00}, + {0x3765, 0x00}, + {0x3769, 0x62}, + {0x376a, 0x2a}, + {0x376b, 0x30}, + {0x3780, 0x00}, + {0x3781, 0x24}, + {0x3782, 0x00}, + {0x3783, 0x23}, + {0x3798, 0x2f}, + {0x37a1, 0x60}, + {0x37a8, 0x6a}, + {0x37ab, 0x3f}, + {0x37c2, 0x04}, + {0x37c3, 0xf1}, + {0x37c9, 0x80}, + {0x37cb, 0x16}, + {0x37cc, 0x16}, + {0x37cd, 0x16}, + {0x37ce, 0x16}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x07}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa6}, + {0x3808, 0x0c}, + {0x3809, 0xd0}, + {0x380a, 0x09}, + {0x380b, 0xa0}, + {0x380c, 0x07}, + {0x380d, 0x88}, + {0x380e, 0x09}, + {0x380f, 0xb8}, + {0x3810, 0x00}, + {0x3811, 0x00}, + {0x3812, 0x00}, + {0x3813, 0x00}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x10}, + {0x3820, 0x80}, + {0x3821, 0x46}, + {0x382a, 0x01}, + {0x382b, 0x01}, + {0x3830, 0x06}, + {0x3836, 0x02}, + {0x3862, 0x04}, + {0x3863, 0x08}, + {0x3cc0, 0x33}, + {0x3d85, 0x17}, + {0x3d8c, 0x73}, + {0x3d8d, 0xde}, + {0x4001, 0xe0}, + {0x4003, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x0b}, + {0x400a, 0x00}, + {0x400b, 0x84}, + {0x400f, 0x80}, + {0x4010, 0xf0}, + {0x4011, 0xff}, + {0x4012, 0x02}, + {0x4013, 0x01}, + {0x4014, 0x01}, + {0x4015, 0x01}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4048, 0x00}, + {0x4049, 0x80}, + {0x4041, 0x03}, + {0x404c, 0x20}, + {0x404d, 0x00}, + {0x404e, 0x20}, + {0x4203, 0x80}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4503, 0x08}, + {0x4601, 0x80}, + {0x4800, 0x44}, + {0x4816, 0x53}, + {0x481b, 0x58}, + {0x481f, 0x27}, + {0x4837, 0x16}, + {0x483c, 0x0f}, + {0x484b, 0x05}, + {0x5000, 0x57}, + {0x5001, 0x0a}, + {0x5004, 0x04}, + {0x502e, 0x03}, + {0x5030, 0x41}, + {0x5780, 0x14}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x5795, 0x02}, + {0x5796, 0x20}, + {0x5797, 0x20}, + {0x5798, 0xd5}, + {0x5799, 0xd5}, + {0x579a, 0x00}, + {0x579b, 0x50}, + {0x579c, 0x00}, + {0x579d, 0x2c}, + {0x579e, 0x0c}, + {0x579f, 0x40}, + {0x57a0, 0x09}, + {0x57a1, 0x40}, + {0x59f8, 0x3d}, + {0x5a08, 0x02}, + {0x5b00, 0x02}, + {0x5b01, 0x10}, + {0x5b02, 0x03}, + {0x5b03, 0xcf}, + {0x5b05, 0x6c}, + {0x5e00, 0x00} +}; + +static const struct ov8856_reg mode_1640x1232_regs[] = { + {0x3000, 0x20}, + {0x3003, 0x08}, + {0x300e, 0x20}, + {0x3010, 0x00}, + {0x3015, 0x84}, + {0x3018, 0x72}, + {0x3021, 0x23}, + {0x3033, 0x24}, + {0x3500, 0x00}, + {0x3501, 0x4c}, + {0x3502, 0xe0}, + {0x3503, 0x08}, + {0x3505, 0x83}, + {0x3508, 0x01}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x350e, 0x04}, + {0x350f, 0x00}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3600, 0x72}, + {0x3601, 0x40}, + {0x3602, 0x30}, + {0x3610, 0xc5}, + {0x3611, 0x58}, + {0x3612, 0x5c}, + {0x3613, 0xca}, + {0x3614, 0x20}, + {0x3628, 0xff}, + {0x3629, 0xff}, + {0x362a, 0xff}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x10}, + {0x3663, 0x08}, + {0x3669, 0x34}, + {0x366e, 0x08}, + {0x3706, 0x86}, + {0x370b, 0x7e}, + {0x3714, 0x27}, + {0x3730, 0x12}, + {0x3733, 0x10}, + {0x3764, 0x00}, + {0x3765, 0x00}, + {0x3769, 0x62}, + {0x376a, 0x2a}, + {0x376b, 0x30}, + {0x3780, 0x00}, + {0x3781, 0x24}, + {0x3782, 0x00}, + {0x3783, 0x23}, + {0x3798, 0x2f}, + {0x37a1, 0x60}, + {0x37a8, 0x6a}, + {0x37ab, 0x3f}, + {0x37c2, 0x14}, + {0x37c3, 0xf1}, + {0x37c9, 0x80}, + {0x37cb, 0x16}, + {0x37cc, 0x16}, + {0x37cd, 0x16}, + {0x37ce, 0x16}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x07}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa6}, + {0x3808, 0x06}, + {0x3809, 0x68}, + {0x380a, 0x04}, + {0x380b, 0xd0}, + {0x380c, 0x0e}, + {0x380d, 0xec}, + {0x380e, 0x04}, + {0x380f, 0xe8}, + {0x3810, 0x00}, + {0x3811, 0x00}, + {0x3812, 0x00}, + {0x3813, 0x00}, + {0x3814, 0x03}, + {0x3815, 0x01}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x10}, + {0x3820, 0x90}, + {0x3821, 0x67}, + {0x382a, 0x03}, + {0x382b, 0x01}, + {0x3830, 0x06}, + {0x3836, 0x02}, + {0x3862, 0x04}, + {0x3863, 0x08}, + {0x3cc0, 0x33}, + {0x3d85, 0x17}, + {0x3d8c, 0x73}, + {0x3d8d, 0xde}, + {0x4001, 0xe0}, + {0x4003, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x05}, + {0x400a, 0x00}, + {0x400b, 0x84}, + {0x400f, 0x80}, + {0x4010, 0xf0}, + {0x4011, 0xff}, + {0x4012, 0x02}, + {0x4013, 0x01}, + {0x4014, 0x01}, + {0x4015, 0x01}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4048, 0x00}, + {0x4049, 0x80}, + {0x4041, 0x03}, + {0x404c, 0x20}, + {0x404d, 0x00}, + {0x404e, 0x20}, + {0x4203, 0x80}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4503, 0x08}, + {0x4601, 0x80}, + {0x4800, 0x44}, + {0x4816, 0x53}, + {0x481b, 0x58}, + {0x481f, 0x27}, + {0x4837, 0x16}, + {0x483c, 0x0f}, + {0x484b, 0x05}, + {0x5000, 0x57}, + {0x5001, 0x0a}, + {0x5004, 0x04}, + {0x502e, 0x03}, + {0x5030, 0x41}, + {0x5780, 0x14}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x5795, 0x00}, + {0x5796, 0x10}, + {0x5797, 0x10}, + {0x5798, 0x73}, + {0x5799, 0x73}, + {0x579a, 0x00}, + {0x579b, 0x28}, + {0x579c, 0x00}, + {0x579d, 0x16}, + {0x579e, 0x06}, + {0x579f, 0x20}, + {0x57a0, 0x04}, + {0x57a1, 0xa0}, + {0x59f8, 0x3d}, + {0x5a08, 0x02}, + {0x5b00, 0x02}, + {0x5b01, 0x10}, + {0x5b02, 0x03}, + {0x5b03, 0xcf}, + {0x5b05, 0x6c}, + {0x5e00, 0x00} +}; + +static const char * const ov8856_test_pattern_menu[] = { + "Disabled", + "Standard Color Bar", + "Top-Bottom Darker Color Bar", + "Right-Left Darker Color Bar", + "Bottom-Top Darker Color Bar" +}; + +static const s64 link_freq_menu_items[] = { + OV8856_LINK_FREQ_360MHZ, + OV8856_LINK_FREQ_180MHZ +}; + +static const struct ov8856_link_freq_config link_freq_configs[] = { + [OV8856_LINK_FREQ_720MBPS] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_720mbps), + .regs = mipi_data_rate_720mbps, + } + }, + [OV8856_LINK_FREQ_360MBPS] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_360mbps), + .regs = mipi_data_rate_360mbps, + } + } +}; + +static const struct ov8856_mode supported_modes[] = { + { + .width = 3280, + .height = 2464, + .hts = 1928, + .vts_def = 2488, + .vts_min = 2488, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), + .regs = mode_3280x2464_regs, + }, + .link_freq_index = OV8856_LINK_FREQ_720MBPS, + }, + { + .width = 1640, + .height = 1232, + .hts = 3820, + .vts_def = 1256, + .vts_min = 1256, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs), + .regs = mode_1640x1232_regs, + }, + .link_freq_index = OV8856_LINK_FREQ_360MBPS, + } +}; + +struct ov8856 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct ov8856_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV8856_DATA_LANES; + + do_div(pixel_rate, OV8856_RGB_DEPTH); + + return pixel_rate; +} + +static u64 to_pixels_per_line(u32 hts, u32 f_index) +{ + u64 ppl = hts * to_pixel_rate(f_index); + + do_div(ppl, OV8856_SCLK); + + return ppl; +} + +static int ov8856_read_reg(struct ov8856 *ov8856, u16 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2] = {reg >> 8, reg & 0xff}; + u8 data_buf[4] = {0, }; + int ret; + + if (len > 4) + return -EINVAL; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +static int ov8856_write_reg(struct ov8856 *ov8856, u16 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << 8 * (4 - len), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int ov8856_write_reg_list(struct ov8856 *ov8856, + const struct ov8856_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + unsigned int i; + int ret; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = ov8856_write_reg(ov8856, r_list->regs[i].address, 1, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "failed to write reg 0x%4.4x. error = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int ov8856_update_digital_gain(struct ov8856 *ov8856, u32 d_gain) +{ + int ret; + + ret = ov8856_write_reg(ov8856, OV8856_REG_MWB_R_GAIN, + OV8856_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + ret = ov8856_write_reg(ov8856, OV8856_REG_MWB_G_GAIN, + OV8856_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + return ov8856_write_reg(ov8856, OV8856_REG_MWB_B_GAIN, + OV8856_REG_VALUE_16BIT, d_gain); +} + +static int ov8856_test_pattern(struct ov8856 *ov8856, u32 pattern) +{ + if (pattern) + pattern = (pattern - 1) << OV8856_TEST_PATTERN_BAR_SHIFT | + OV8856_TEST_PATTERN_ENABLE; + + return ov8856_write_reg(ov8856, OV8856_REG_TEST_PATTERN, + OV8856_REG_VALUE_08BIT, pattern); +} + +static int ov8856_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov8856 *ov8856 = container_of(ctrl->handler, + struct ov8856, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = ov8856->cur_mode->height + ctrl->val - + OV8856_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(ov8856->exposure, + ov8856->exposure->minimum, + exposure_max, ov8856->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = ov8856_write_reg(ov8856, OV8856_REG_ANALOG_GAIN, + OV8856_REG_VALUE_16BIT, ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = ov8856_update_digital_gain(ov8856, ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = ov8856_write_reg(ov8856, OV8856_REG_EXPOSURE, + OV8856_REG_VALUE_24BIT, ctrl->val << 4); + break; + + case V4L2_CID_VBLANK: + ret = ov8856_write_reg(ov8856, OV8856_REG_VTS, + OV8856_REG_VALUE_16BIT, + ov8856->cur_mode->height + ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = ov8856_test_pattern(ov8856, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov8856_ctrl_ops = { + .s_ctrl = ov8856_set_ctrl, +}; + +static int ov8856_init_controls(struct ov8856 *ov8856) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, h_blank; + int ret; + + ctrl_hdlr = &ov8856->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ctrl_hdlr->lock = &ov8856->mutex; + ov8856->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + 0, link_freq_menu_items); + if (ov8856->link_freq) + ov8856->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ov8856->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + to_pixel_rate(OV8856_LINK_FREQ_720MBPS), + 1, + to_pixel_rate(OV8856_LINK_FREQ_720MBPS)); + ov8856->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_VBLANK, + ov8856->cur_mode->vts_min - ov8856->cur_mode->height, + OV8856_VTS_MAX - ov8856->cur_mode->height, 1, + ov8856->cur_mode->vts_def - ov8856->cur_mode->height); + h_blank = to_pixels_per_line(ov8856->cur_mode->hts, + ov8856->cur_mode->link_freq_index) - ov8856->cur_mode->width; + ov8856->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (ov8856->hblank) + ov8856->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV8856_ANAL_GAIN_MIN, OV8856_ANAL_GAIN_MAX, + OV8856_ANAL_GAIN_STEP, OV8856_ANAL_GAIN_MIN); + v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV8856_DGTL_GAIN_MIN, OV8856_DGTL_GAIN_MAX, + OV8856_DGTL_GAIN_STEP, OV8856_DGTL_GAIN_DEFAULT); + exposure_max = ov8856->cur_mode->vts_def - OV8856_EXPOSURE_MAX_MARGIN; + ov8856->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_EXPOSURE, + OV8856_EXPOSURE_MIN, exposure_max, + OV8856_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov8856_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov8856_test_pattern_menu) - 1, + 0, 0, ov8856_test_pattern_menu); + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov8856->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov8856_update_pad_format(const struct ov8856_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov8856_start_streaming(struct ov8856 *ov8856) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + const struct ov8856_reg_list *reg_list; + int link_freq_index, ret; + + link_freq_index = ov8856->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = ov8856_write_reg_list(ov8856, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set plls"); + return ret; + } + + reg_list = &ov8856->cur_mode->reg_list; + ret = ov8856_write_reg_list(ov8856, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(ov8856->sd.ctrl_handler); + if (ret) + return ret; + + ret = ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, + OV8856_REG_VALUE_08BIT, OV8856_MODE_STREAMING); + if (ret) { + dev_err(&client->dev, "failed to set stream"); + return ret; + } + + return 0; +} + +static void ov8856_stop_streaming(struct ov8856 *ov8856) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + + if (ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, + OV8856_REG_VALUE_08BIT, OV8856_MODE_STANDBY)) + dev_err(&client->dev, "failed to set stream"); +} + +static int ov8856_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (ov8856->streaming == enable) + return 0; + + mutex_lock(&ov8856->mutex); + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&ov8856->mutex); + return ret; + } + + ret = ov8856_start_streaming(ov8856); + if (ret) { + enable = 0; + ov8856_stop_streaming(ov8856); + pm_runtime_put(&client->dev); + } + } else { + ov8856_stop_streaming(ov8856); + pm_runtime_put(&client->dev); + } + + ov8856->streaming = enable; + mutex_unlock(&ov8856->mutex); + + return ret; +} + +static int __maybe_unused ov8856_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8856 *ov8856 = to_ov8856(sd); + + mutex_lock(&ov8856->mutex); + if (ov8856->streaming) + ov8856_stop_streaming(ov8856); + + mutex_unlock(&ov8856->mutex); + + return 0; +} + +static int __maybe_unused ov8856_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8856 *ov8856 = to_ov8856(sd); + int ret; + + mutex_lock(&ov8856->mutex); + if (ov8856->streaming) { + ret = ov8856_start_streaming(ov8856); + if (ret) { + ov8856->streaming = false; + ov8856_stop_streaming(ov8856); + mutex_unlock(&ov8856->mutex); + return ret; + } + } + + mutex_unlock(&ov8856->mutex); + + return 0; +} + +static int ov8856_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + const struct ov8856_mode *mode; + s32 vblank_def, h_blank; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, + height, fmt->format.width, + fmt->format.height); + + mutex_lock(&ov8856->mutex); + ov8856_update_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + } else { + ov8856->cur_mode = mode; + __v4l2_ctrl_s_ctrl(ov8856->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(ov8856->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov8856->vblank, + mode->vts_min - mode->height, + OV8856_VTS_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(ov8856->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - + mode->width; + __v4l2_ctrl_modify_range(ov8856->hblank, h_blank, h_blank, 1, + h_blank); + } + + mutex_unlock(&ov8856->mutex); + + return 0; +} + +static int ov8856_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + + mutex_lock(&ov8856->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&ov8856->sd, cfg, + fmt->pad); + else + ov8856_update_pad_format(ov8856->cur_mode, &fmt->format); + + mutex_unlock(&ov8856->mutex); + + return 0; +} + +static int ov8856_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + /* Only one bayer order GRBG is supported */ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov8856_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov8856_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + + mutex_lock(&ov8856->mutex); + ov8856_update_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->pad, 0)); + mutex_unlock(&ov8856->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov8856_video_ops = { + .s_stream = ov8856_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ov8856_pad_ops = { + .set_fmt = ov8856_set_format, + .get_fmt = ov8856_get_format, + .enum_mbus_code = ov8856_enum_mbus_code, + .enum_frame_size = ov8856_enum_frame_size, +}; + +static const struct v4l2_subdev_ops ov8856_subdev_ops = { + .video = &ov8856_video_ops, + .pad = &ov8856_pad_ops, +}; + +static const struct media_entity_operations ov8856_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov8856_internal_ops = { + .open = ov8856_open, +}; + +static int ov8856_identify_module(struct ov8856 *ov8856) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + int ret; + u32 val; + + ret = ov8856_read_reg(ov8856, OV8856_REG_CHIP_ID, + OV8856_REG_VALUE_24BIT, &val); + if (ret) + return ret; + + if (val != OV8856_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + OV8856_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +static int ov8856_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + u32 mclk; + int ret; + unsigned int i, j; + + if (!fwnode) + return -ENXIO; + + fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + if (mclk != OV8856_MCLK) { + dev_err(dev, "external clock %d is not supported", mclk); + return -EINVAL; + } + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENXIO; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV8856_DATA_LANES) { + dev_err(dev, "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto check_hwcfg_error; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto check_hwcfg_error; + } + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + link_freq_menu_items[i]); + ret = -EINVAL; + goto check_hwcfg_error; + } + } + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov8856_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8856 *ov8856 = to_ov8856(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&ov8856->mutex); + + return 0; +} + +static int ov8856_probe(struct i2c_client *client) +{ + struct ov8856 *ov8856; + int ret; + + ret = ov8856_check_hwcfg(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to check HW configuration: %d", + ret); + return ret; + } + + ov8856 = devm_kzalloc(&client->dev, sizeof(*ov8856), GFP_KERNEL); + if (!ov8856) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ov8856->sd, client, &ov8856_subdev_ops); + ret = ov8856_identify_module(ov8856); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + return ret; + } + + mutex_init(&ov8856->mutex); + ov8856->cur_mode = &supported_modes[0]; + ret = ov8856_init_controls(ov8856); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov8856->sd.internal_ops = &ov8856_internal_ops; + ov8856->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov8856->sd.entity.ops = &ov8856_subdev_entity_ops; + ov8856->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov8856->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov8856->sd.entity, 1, &ov8856->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ret = v4l2_async_register_subdev_sensor_common(&ov8856->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_media_entity_cleanup; + } + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +probe_error_media_entity_cleanup: + media_entity_cleanup(&ov8856->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov8856->sd.ctrl_handler); + mutex_destroy(&ov8856->mutex); + + return ret; +} + +static const struct dev_pm_ops ov8856_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ov8856_suspend, ov8856_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ov8856_acpi_ids[] = { + {"OVTI8856"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, ov8856_acpi_ids); +#endif + +static struct i2c_driver ov8856_i2c_driver = { + .driver = { + .name = "ov8856", + .pm = &ov8856_pm_ops, + .acpi_match_table = ACPI_PTR(ov8856_acpi_ids), + }, + .probe_new = ov8856_probe, + .remove = ov8856_remove, +}; + +module_i2c_driver(ov8856_i2c_driver); + +MODULE_AUTHOR("Ben Kao "); +MODULE_DESCRIPTION("OmniVision OV8856 sensor driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From f1fb085562411f71ab9259fc131b78d93406c907 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 15 Jan 2019 06:54:44 -0200 Subject: media: ov7670: Remove useless use of a ret variable Instead of assigning the return value to ret and then checking and returning it, just return the value to the caller directly. The success value is always 0. Signed-off-by: Sakari Ailus Acked-by: Lubomir Rintel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 4939a83b50e4..61c47c61c693 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -859,11 +859,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, /* Recalculate frame rate */ ov7675_get_framerate(sd, tpf); - ret = ov7670_write(sd, REG_CLKRC, info->clkrc); - if (ret < 0) - return ret; - - return 0; + return ov7670_write(sd, REG_CLKRC, info->clkrc); } static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, -- cgit v1.2.3-59-g8ed1b From 5556ab2a3f2c91f09b53c761972bce8af72174bc Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 15 Jan 2019 06:54:47 -0200 Subject: media: ov7670: split register setting from set_fmt() logic This will allow us to restore the last set format after the device returns from a power off. Signed-off-by: Lubomir Rintel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 80 ++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 61c47c61c693..386c493362f3 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -241,6 +241,7 @@ struct ov7670_info { }; struct v4l2_mbus_framefmt format; struct ov7670_format_struct *fmt; /* Current format */ + struct ov7670_win_size *wsize; struct clk *clk; struct gpio_desc *resetb_gpio; struct gpio_desc *pwdn_gpio; @@ -1000,48 +1001,20 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, return 0; } -/* - * Set a format. - */ -static int ov7670_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) +static int ov7670_apply_fmt(struct v4l2_subdev *sd) { - struct ov7670_format_struct *ovfmt; - struct ov7670_win_size *wsize; struct ov7670_info *info = to_state(sd); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - struct v4l2_mbus_framefmt *mbus_fmt; -#endif + struct ov7670_win_size *wsize = info->wsize; unsigned char com7, com10 = 0; int ret; - if (format->pad) - return -EINVAL; - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); - if (ret) - return ret; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); - *mbus_fmt = format->format; - return 0; -#else - return -ENOTTY; -#endif - } - - ret = ov7670_try_fmt_internal(sd, &format->format, &ovfmt, &wsize); - if (ret) - return ret; /* * COM7 is a pain in the ass, it doesn't like to be read then * quickly written afterward. But we have everything we need * to set it absolutely here, as long as the format-specific * register sets list it first. */ - com7 = ovfmt->regs[0].value; + com7 = info->fmt->regs[0].value; com7 |= wsize->com7_bit; ret = ov7670_write(sd, REG_COM7, com7); if (ret) @@ -1063,7 +1036,7 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, /* * Now write the rest of the array. Also store start/stops */ - ret = ov7670_write_array(sd, ovfmt->regs + 1); + ret = ov7670_write_array(sd, info->fmt->regs + 1); if (ret) return ret; @@ -1078,8 +1051,6 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, return ret; } - info->fmt = ovfmt; - /* * If we're running RGB565, we must rewrite clkrc after setting * the other parameters or the image looks poor. If we're *not* @@ -1097,6 +1068,46 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd, return 0; } +/* + * Set a format. + */ +static int ov7670_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov7670_info *info = to_state(sd); +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + struct v4l2_mbus_framefmt *mbus_fmt; +#endif + int ret; + + if (format->pad) + return -EINVAL; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); + if (ret) + return ret; +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + *mbus_fmt = format->format; + return 0; +#else + return -ENOTTY; +#endif + } + + ret = ov7670_try_fmt_internal(sd, &format->format, &info->fmt, &info->wsize); + if (ret) + return ret; + + ret = ov7670_apply_fmt(sd); + if (ret) + return ret; + + return 0; +} + static int ov7670_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) @@ -1843,6 +1854,7 @@ static int ov7670_probe(struct i2c_client *client, info->devtype = &ov7670_devdata[id->driver_data]; info->fmt = &ov7670_formats[0]; + info->wsize = &info->devtype->win_sizes[0]; ov7670_get_default_format(sd, &info->format); -- cgit v1.2.3-59-g8ed1b From 40012cd5ecaa48ad7bf36cb477d4d6f033639d0e Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 15 Jan 2019 06:54:48 -0200 Subject: media: ov7670: split register setting from set_framerate() logic This will allow us to restore the last set frame rate after the device returns from a power off. [sakari.ailus@linux.intel.com>: Wrap a line over 80 characters] Signed-off-by: Lubomir Rintel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 386c493362f3..6ab88626c760 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -811,13 +811,25 @@ static void ov7675_get_framerate(struct v4l2_subdev *sd, (4 * clkrc); } +static int ov7675_apply_framerate(struct v4l2_subdev *sd) +{ + struct ov7670_info *info = to_state(sd); + int ret; + + ret = ov7670_write(sd, REG_CLKRC, info->clkrc); + if (ret < 0) + return ret; + + return ov7670_write(sd, REG_DBLV, + info->pll_bypass ? DBLV_BYPASS : DBLV_X4); +} + static int ov7675_set_framerate(struct v4l2_subdev *sd, struct v4l2_fract *tpf) { struct ov7670_info *info = to_state(sd); u32 clkrc; int pll_factor; - int ret; /* * The formula is fps = 5/4*pixclk for YUV/RGB and @@ -826,19 +838,10 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, * pixclk = clock_speed / (clkrc + 1) * PLLfactor * */ - if (info->pll_bypass) { - pll_factor = 1; - ret = ov7670_write(sd, REG_DBLV, DBLV_BYPASS); - } else { - pll_factor = PLL_FACTOR; - ret = ov7670_write(sd, REG_DBLV, DBLV_X4); - } - if (ret < 0) - return ret; - if (tpf->numerator == 0 || tpf->denominator == 0) { clkrc = 0; } else { + pll_factor = info->pll_bypass ? 1 : PLL_FACTOR; clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) / (4 * tpf->denominator); if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8) @@ -860,7 +863,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, /* Recalculate frame rate */ ov7675_get_framerate(sd, tpf); - return ov7670_write(sd, REG_CLKRC, info->clkrc); + return ov7675_apply_framerate(sd); } static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, -- cgit v1.2.3-59-g8ed1b From 3d6a8fe256059034ddd592f1e169da5ff9d28d7e Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 15 Jan 2019 06:54:45 -0200 Subject: media: ov7670: hook s_power onto v4l2 core The commit 71862f63f351 ("media: ov7670: Add the ov7670_s_power function") added a power control routing. However, it was not good enough to use as a s_power() callback: it merely flipped on the power GPIOs without restoring the register settings. Fix this now and register an actual power callback. Signed-off-by: Lubomir Rintel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 50 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 6ab88626c760..a5b591dde10c 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -243,6 +243,7 @@ struct ov7670_info { struct ov7670_format_struct *fmt; /* Current format */ struct ov7670_win_size *wsize; struct clk *clk; + int on; struct gpio_desc *resetb_gpio; struct gpio_desc *pwdn_gpio; unsigned int mbus_config; /* Media bus configuration flags */ @@ -1617,19 +1618,54 @@ static int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_regis } #endif -static int ov7670_s_power(struct v4l2_subdev *sd, int on) +static void ov7670_power_on(struct v4l2_subdev *sd) { struct ov7670_info *info = to_state(sd); + if (info->on) + return; + if (info->pwdn_gpio) - gpiod_set_value(info->pwdn_gpio, !on); - if (on && info->resetb_gpio) { + gpiod_set_value(info->pwdn_gpio, 0); + if (info->resetb_gpio) { gpiod_set_value(info->resetb_gpio, 1); usleep_range(500, 1000); gpiod_set_value(info->resetb_gpio, 0); usleep_range(3000, 5000); } + info->on = true; +} + +static void ov7670_power_off(struct v4l2_subdev *sd) +{ + struct ov7670_info *info = to_state(sd); + + if (!info->on) + return; + + if (info->pwdn_gpio) + gpiod_set_value(info->pwdn_gpio, 1); + + info->on = false; +} + +static int ov7670_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov7670_info *info = to_state(sd); + + if (info->on == on) + return 0; + + if (on) { + ov7670_power_on (sd); + ov7670_apply_fmt(sd); + ov7675_apply_framerate(sd); + v4l2_ctrl_handler_setup(&info->hdl); + } else { + ov7670_power_off (sd); + } + return 0; } @@ -1662,6 +1698,7 @@ static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) static const struct v4l2_subdev_core_ops ov7670_core_ops = { .reset = ov7670_reset, .init = ov7670_init, + .s_power = ov7670_s_power, .log_status = v4l2_ctrl_subdev_log_status, .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, @@ -1826,6 +1863,7 @@ static int ov7670_probe(struct i2c_client *client, else return ret; } + if (info->clk) { ret = clk_prepare_enable(info->clk); if (ret) @@ -1842,7 +1880,7 @@ static int ov7670_probe(struct i2c_client *client, if (ret) goto clk_disable; - ov7670_s_power(sd, 1); + ov7670_power_on(sd); /* Make sure it's an ov7670 */ ret = ov7670_detect(sd); @@ -1930,7 +1968,7 @@ entity_cleanup: hdl_free: v4l2_ctrl_handler_free(&info->hdl); power_off: - ov7670_s_power(sd, 0); + ov7670_power_off(sd); clk_disable: clk_disable_unprepare(info->clk); return ret; @@ -1946,7 +1984,7 @@ static int ov7670_remove(struct i2c_client *client) v4l2_ctrl_handler_free(&info->hdl); clk_disable_unprepare(info->clk); media_entity_cleanup(&info->sd.entity); - ov7670_s_power(sd, 0); + ov7670_power_off(sd); return 0; } -- cgit v1.2.3-59-g8ed1b From 030f9f682e66ea3be214f673ca8ba53294eeecd2 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 15 Jan 2019 06:54:46 -0200 Subject: media: ov7670: control clock along with power This provides more power saving when the sensor is off. While at that, do the delay on power/clock enable even if the sensor driver itself doesn't control the GPIOs. This is required for the OLPC XO-1 platform, that lacks the proper power/reset properties in its DT, but needs the delay after the sensor is clocked up. Signed-off-by: Lubomir Rintel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index a5b591dde10c..a7d26b294eb5 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1625,14 +1625,17 @@ static void ov7670_power_on(struct v4l2_subdev *sd) if (info->on) return; + clk_prepare_enable(info->clk); + if (info->pwdn_gpio) gpiod_set_value(info->pwdn_gpio, 0); if (info->resetb_gpio) { gpiod_set_value(info->resetb_gpio, 1); usleep_range(500, 1000); gpiod_set_value(info->resetb_gpio, 0); - usleep_range(3000, 5000); } + if (info->pwdn_gpio || info->resetb_gpio || info->clk) + usleep_range(3000, 5000); info->on = true; } @@ -1644,6 +1647,8 @@ static void ov7670_power_off(struct v4l2_subdev *sd) if (!info->on) return; + clk_disable_unprepare(info->clk); + if (info->pwdn_gpio) gpiod_set_value(info->pwdn_gpio, 1); @@ -1864,24 +1869,20 @@ static int ov7670_probe(struct i2c_client *client, return ret; } - if (info->clk) { - ret = clk_prepare_enable(info->clk); - if (ret) - return ret; + ret = ov7670_init_gpio(client, info); + if (ret) + return ret; + ov7670_power_on(sd); + + if (info->clk) { info->clock_speed = clk_get_rate(info->clk) / 1000000; if (info->clock_speed < 10 || info->clock_speed > 48) { ret = -EINVAL; - goto clk_disable; + goto power_off; } } - ret = ov7670_init_gpio(client, info); - if (ret) - goto clk_disable; - - ov7670_power_on(sd); - /* Make sure it's an ov7670 */ ret = ov7670_detect(sd); if (ret) { @@ -1961,6 +1962,7 @@ static int ov7670_probe(struct i2c_client *client, if (ret < 0) goto entity_cleanup; + ov7670_power_off(sd); return 0; entity_cleanup: @@ -1969,12 +1971,9 @@ hdl_free: v4l2_ctrl_handler_free(&info->hdl); power_off: ov7670_power_off(sd); -clk_disable: - clk_disable_unprepare(info->clk); return ret; } - static int ov7670_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); @@ -1982,7 +1981,6 @@ static int ov7670_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(&info->hdl); - clk_disable_unprepare(info->clk); media_entity_cleanup(&info->sd.entity); ov7670_power_off(sd); return 0; -- cgit v1.2.3-59-g8ed1b From fa6527340141bfec061db2f2a9cca7f82584f94e Mon Sep 17 00:00:00 2001 From: Ben Kao Date: Tue, 15 Jan 2019 07:36:07 -0200 Subject: media: ov8856: Modify ov8856 register reading function to be simplified We use put_unaligned_be16() to be simplified for setting register address in ov8856_read_reg() and use sizeof() to be better suited for bytes copying. Signed-off-by: Ben Kao Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov8856.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index c0d44082c003..dbf1095b9440 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -605,16 +605,17 @@ static int ov8856_read_reg(struct ov8856 *ov8856, u16 reg, u16 len, u32 *val) { struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); struct i2c_msg msgs[2]; - u8 addr_buf[2] = {reg >> 8, reg & 0xff}; - u8 data_buf[4] = {0, }; + u8 addr_buf[2]; + u8 data_buf[4] = {0}; int ret; if (len > 4) return -EINVAL; + put_unaligned_be16(reg, addr_buf); msgs[0].addr = client->addr; msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].len = sizeof(addr_buf); msgs[0].buf = addr_buf; msgs[1].addr = client->addr; msgs[1].flags = I2C_M_RD; -- cgit v1.2.3-59-g8ed1b From f8ce7c35e6aef69d9110e2e24756146e3c486cbb Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:38 -0200 Subject: media: i2c: mt9m001: copy mt9m001 soc_camera sensor driver Copy the soc_camera based driver in v4l2 sensor driver directory. This commit just copies the original file without modifying it. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 7 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/mt9m001.c | 757 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 765 insertions(+) create mode 100644 drivers/media/i2c/mt9m001.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 54a26e24baf4..7629e03ebcc9 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -867,6 +867,13 @@ config VIDEO_VS6624 To compile this driver as a module, choose M here: the module will be called vs6624. +config VIDEO_MT9M001 + tristate "mt9m001 support" + depends on SOC_CAMERA && I2C + help + This driver supports MT9M001 cameras from Micron, monochrome + and colour models. + config VIDEO_MT9M032 tristate "MT9M032 camera sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 29bf3fa81113..2e5e4b0bf7f3 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_VIDEO_OV8856) += ov8856.o obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o +obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c new file mode 100644 index 000000000000..a1a85ff838c5 --- /dev/null +++ b/drivers/media/i2c/mt9m001.c @@ -0,0 +1,757 @@ +/* + * Driver for MT9M001 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * mt9m001 i2c address 0x5d + * The platform has to define struct i2c_board_info objects and link to them + * from struct soc_camera_host_desc + */ + +/* mt9m001 selected register addresses */ +#define MT9M001_CHIP_VERSION 0x00 +#define MT9M001_ROW_START 0x01 +#define MT9M001_COLUMN_START 0x02 +#define MT9M001_WINDOW_HEIGHT 0x03 +#define MT9M001_WINDOW_WIDTH 0x04 +#define MT9M001_HORIZONTAL_BLANKING 0x05 +#define MT9M001_VERTICAL_BLANKING 0x06 +#define MT9M001_OUTPUT_CONTROL 0x07 +#define MT9M001_SHUTTER_WIDTH 0x09 +#define MT9M001_FRAME_RESTART 0x0b +#define MT9M001_SHUTTER_DELAY 0x0c +#define MT9M001_RESET 0x0d +#define MT9M001_READ_OPTIONS1 0x1e +#define MT9M001_READ_OPTIONS2 0x20 +#define MT9M001_GLOBAL_GAIN 0x35 +#define MT9M001_CHIP_ENABLE 0xF1 + +#define MT9M001_MAX_WIDTH 1280 +#define MT9M001_MAX_HEIGHT 1024 +#define MT9M001_MIN_WIDTH 48 +#define MT9M001_MIN_HEIGHT 32 +#define MT9M001_COLUMN_SKIP 20 +#define MT9M001_ROW_SKIP 12 + +/* MT9M001 has only one fixed colorspace per pixelcode */ +struct mt9m001_datafmt { + u32 code; + enum v4l2_colorspace colorspace; +}; + +/* Find a data format by a pixel code in an array */ +static const struct mt9m001_datafmt *mt9m001_find_datafmt( + u32 code, const struct mt9m001_datafmt *fmt, + int n) +{ + int i; + for (i = 0; i < n; i++) + if (fmt[i].code == code) + return fmt + i; + + return NULL; +} + +static const struct mt9m001_datafmt mt9m001_colour_fmts[] = { + /* + * Order important: first natively supported, + * second supported with a GPIO extender + */ + {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, +}; + +static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = { + /* Order important - see above */ + {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, + {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, +}; + +struct mt9m001 { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct { + /* exposure/auto-exposure cluster */ + struct v4l2_ctrl *autoexposure; + struct v4l2_ctrl *exposure; + }; + struct v4l2_rect rect; /* Sensor window */ + struct v4l2_clk *clk; + const struct mt9m001_datafmt *fmt; + const struct mt9m001_datafmt *fmts; + int num_fmts; + unsigned int total_h; + unsigned short y_skip_top; /* Lines to skip at the top */ +}; + +static struct mt9m001 *to_mt9m001(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9m001, subdev); +} + +static int reg_read(struct i2c_client *client, const u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int reg_write(struct i2c_client *client, const u8 reg, + const u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static int reg_set(struct i2c_client *client, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, ret | data); +} + +static int reg_clear(struct i2c_client *client, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, ret & ~data); +} + +static int mt9m001_init(struct i2c_client *client) +{ + int ret; + + dev_dbg(&client->dev, "%s\n", __func__); + + /* + * We don't know, whether platform provides reset, issue a soft reset + * too. This returns all registers to their default values. + */ + ret = reg_write(client, MT9M001_RESET, 1); + if (!ret) + ret = reg_write(client, MT9M001_RESET, 0); + + /* Disable chip, synchronous option update */ + if (!ret) + ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0); + + return ret; +} + +static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* Switch to master "normal" mode or stop sensor readout */ + if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0) + return -EIO; + return 0; +} + +static int mt9m001_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_rect rect = sel->r; + const u16 hblank = 9, vblank = 25; + int ret; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + if (mt9m001->fmts == mt9m001_colour_fmts) + /* + * Bayer format - even number of rows for simplicity, + * but let the user play with the top row. + */ + rect.height = ALIGN(rect.height, 2); + + /* Datasheet requirement: see register description */ + rect.width = ALIGN(rect.width, 2); + rect.left = ALIGN(rect.left, 2); + + soc_camera_limit_side(&rect.left, &rect.width, + MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); + + mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank; + + /* Blanking and start values - default... */ + ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); + if (!ret) + ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank); + + /* + * The caller provides a supported format, as verified per + * call to .set_fmt(FORMAT_TRY). + */ + if (!ret) + ret = reg_write(client, MT9M001_COLUMN_START, rect.left); + if (!ret) + ret = reg_write(client, MT9M001_ROW_START, rect.top); + if (!ret) + ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1); + if (!ret) + ret = reg_write(client, MT9M001_WINDOW_HEIGHT, + rect.height + mt9m001->y_skip_top - 1); + if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO) + ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h); + + if (!ret) + mt9m001->rect = rect; + + return ret; +} + +static int mt9m001_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = MT9M001_COLUMN_SKIP; + sel->r.top = MT9M001_ROW_SKIP; + sel->r.width = MT9M001_MAX_WIDTH; + sel->r.height = MT9M001_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = mt9m001->rect; + return 0; + default: + return -EINVAL; + } +} + +static int mt9m001_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; + + mf->width = mt9m001->rect.width; + mf->height = mt9m001->rect.height; + mf->code = mt9m001->fmt->code; + mf->colorspace = mt9m001->fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int mt9m001_s_fmt(struct v4l2_subdev *sd, + const struct mt9m001_datafmt *fmt, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP, + .r.left = mt9m001->rect.left, + .r.top = mt9m001->rect.top, + .r.width = mf->width, + .r.height = mf->height, + }; + int ret; + + /* No support for scaling so far, just crop. TODO: use skipping */ + ret = mt9m001_set_selection(sd, NULL, &sel); + if (!ret) { + mf->width = mt9m001->rect.width; + mf->height = mt9m001->rect.height; + mt9m001->fmt = fmt; + mf->colorspace = fmt->colorspace; + } + + return ret; +} + +static int mt9m001_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + const struct mt9m001_datafmt *fmt; + + if (format->pad) + return -EINVAL; + + v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH, + MT9M001_MAX_WIDTH, 1, + &mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top, + MT9M001_MAX_HEIGHT + mt9m001->y_skip_top, 0, 0); + + if (mt9m001->fmts == mt9m001_colour_fmts) + mf->height = ALIGN(mf->height - 1, 2); + + fmt = mt9m001_find_datafmt(mf->code, mt9m001->fmts, + mt9m001->num_fmts); + if (!fmt) { + fmt = mt9m001->fmt; + mf->code = fmt->code; + } + + mf->colorspace = fmt->colorspace; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return mt9m001_s_fmt(sd, fmt, mf); + cfg->try_fmt = *mf; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9m001_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff) + return -EINVAL; + + reg->size = 2; + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9m001_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff) + return -EINVAL; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static int mt9m001_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct mt9m001 *mt9m001 = to_mt9m001(client); + + return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on); +} + +static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m001 *mt9m001 = container_of(ctrl->handler, + struct mt9m001, hdl); + s32 min, max; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_AUTO: + min = mt9m001->exposure->minimum; + max = mt9m001->exposure->maximum; + mt9m001->exposure->val = + (524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min; + break; + } + return 0; +} + +static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9m001 *mt9m001 = container_of(ctrl->handler, + struct mt9m001, hdl); + struct v4l2_subdev *sd = &mt9m001->subdev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_ctrl *exp = mt9m001->exposure; + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->val) + data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000); + else + data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000); + if (data < 0) + return -EIO; + return 0; + + case V4L2_CID_GAIN: + /* See Datasheet Table 7, Gain settings. */ + if (ctrl->val <= ctrl->default_value) { + /* Pack it into 0..1 step 0.125, register values 0..8 */ + unsigned long range = ctrl->default_value - ctrl->minimum; + data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; + + dev_dbg(&client->dev, "Setting gain %d\n", data); + data = reg_write(client, MT9M001_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } else { + /* Pack it into 1.125..15 variable step, register values 9..67 */ + /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ + unsigned long range = ctrl->maximum - ctrl->default_value - 1; + unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * + 111 + range / 2) / range + 9; + + if (gain <= 32) + data = gain; + else if (gain <= 64) + data = ((gain - 32) * 16 + 16) / 32 + 80; + else + data = ((gain - 64) * 7 + 28) / 56 + 96; + + dev_dbg(&client->dev, "Setting gain from %d to %d\n", + reg_read(client, MT9M001_GLOBAL_GAIN), data); + data = reg_write(client, MT9M001_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } + return 0; + + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_MANUAL) { + unsigned long range = exp->maximum - exp->minimum; + unsigned long shutter = ((exp->val - (s32)exp->minimum) * 1048 + + range / 2) / range + 1; + + dev_dbg(&client->dev, + "Setting shutter width from %d to %lu\n", + reg_read(client, MT9M001_SHUTTER_WIDTH), shutter); + if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0) + return -EIO; + } else { + const u16 vblank = 25; + + mt9m001->total_h = mt9m001->rect.height + + mt9m001->y_skip_top + vblank; + if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0) + return -EIO; + } + return 0; + } + return -EINVAL; +} + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, + struct i2c_client *client) +{ + struct mt9m001 *mt9m001 = to_mt9m001(client); + s32 data; + unsigned long flags; + int ret; + + ret = mt9m001_s_power(&mt9m001->subdev, 1); + if (ret < 0) + return ret; + + /* Enable the chip */ + data = reg_write(client, MT9M001_CHIP_ENABLE, 1); + dev_dbg(&client->dev, "write: %d\n", data); + + /* Read out the chip version register */ + data = reg_read(client, MT9M001_CHIP_VERSION); + + /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ + switch (data) { + case 0x8411: + case 0x8421: + mt9m001->fmts = mt9m001_colour_fmts; + break; + case 0x8431: + mt9m001->fmts = mt9m001_monochrome_fmts; + break; + default: + dev_err(&client->dev, + "No MT9M001 chip detected, register read %x\n", data); + ret = -ENODEV; + goto done; + } + + mt9m001->num_fmts = 0; + + /* + * This is a 10bit sensor, so by default we only allow 10bit. + * The platform may support different bus widths due to + * different routing of the data lines. + */ + if (ssdd->query_bus_param) + flags = ssdd->query_bus_param(ssdd); + else + flags = SOCAM_DATAWIDTH_10; + + if (flags & SOCAM_DATAWIDTH_10) + mt9m001->num_fmts++; + else + mt9m001->fmts++; + + if (flags & SOCAM_DATAWIDTH_8) + mt9m001->num_fmts++; + + mt9m001->fmt = &mt9m001->fmts[0]; + + dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, + data == 0x8431 ? "C12STM" : "C12ST"); + + ret = mt9m001_init(client); + if (ret < 0) { + dev_err(&client->dev, "Failed to initialise the camera\n"); + goto done; + } + + /* mt9m001_init() has reset the chip, returning registers to defaults */ + ret = v4l2_ctrl_handler_setup(&mt9m001->hdl); + +done: + mt9m001_s_power(&mt9m001->subdev, 0); + return ret; +} + +static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd) +{ + if (ssdd->free_bus) + ssdd->free_bus(ssdd); +} + +static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + + *lines = mt9m001->y_skip_top; + + return 0; +} + +static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = { + .g_volatile_ctrl = mt9m001_g_volatile_ctrl, + .s_ctrl = mt9m001_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9m001_g_register, + .s_register = mt9m001_s_register, +#endif + .s_power = mt9m001_s_power, +}; + +static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + + if (code->pad || code->index >= mt9m001->num_fmts) + return -EINVAL; + + code->code = mt9m001->fmts[code->index].code; + return 0; +} + +static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + /* MT9M001 has all capture_format parameters fixed */ + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); + + return 0; +} + +static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + const struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct mt9m001 *mt9m001 = to_mt9m001(client); + unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; + + if (ssdd->set_bus_param) + return ssdd->set_bus_param(ssdd, 1 << (bps - 1)); + + /* + * Without board specific bus width settings we only support the + * sensors native bus width + */ + return bps == 10 ? 0 : -EINVAL; +} + +static const struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { + .s_stream = mt9m001_s_stream, + .g_mbus_config = mt9m001_g_mbus_config, + .s_mbus_config = mt9m001_s_mbus_config, +}; + +static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { + .g_skip_top_lines = mt9m001_g_skip_top_lines, +}; + +static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { + .enum_mbus_code = mt9m001_enum_mbus_code, + .get_selection = mt9m001_get_selection, + .set_selection = mt9m001_set_selection, + .get_fmt = mt9m001_get_fmt, + .set_fmt = mt9m001_set_fmt, +}; + +static const struct v4l2_subdev_ops mt9m001_subdev_ops = { + .core = &mt9m001_subdev_core_ops, + .video = &mt9m001_subdev_video_ops, + .sensor = &mt9m001_subdev_sensor_ops, + .pad = &mt9m001_subdev_pad_ops, +}; + +static int mt9m001_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9m001 *mt9m001; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + int ret; + + if (!ssdd) { + dev_err(&client->dev, "MT9M001 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL); + if (!mt9m001) + return -ENOMEM; + + v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); + v4l2_ctrl_handler_init(&mt9m001->hdl, 4); + v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 255, 1, 255); + /* + * Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width + */ + mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl, + &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, + V4L2_EXPOSURE_AUTO); + mt9m001->subdev.ctrl_handler = &mt9m001->hdl; + if (mt9m001->hdl.error) + return mt9m001->hdl.error; + + v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure, + V4L2_EXPOSURE_MANUAL, true); + + /* Second stage probe - when a capture adapter is there */ + mt9m001->y_skip_top = 0; + mt9m001->rect.left = MT9M001_COLUMN_SKIP; + mt9m001->rect.top = MT9M001_ROW_SKIP; + mt9m001->rect.width = MT9M001_MAX_WIDTH; + mt9m001->rect.height = MT9M001_MAX_HEIGHT; + + mt9m001->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(mt9m001->clk)) { + ret = PTR_ERR(mt9m001->clk); + goto eclkget; + } + + ret = mt9m001_video_probe(ssdd, client); + if (ret) { + v4l2_clk_put(mt9m001->clk); +eclkget: + v4l2_ctrl_handler_free(&mt9m001->hdl); + } + + return ret; +} + +static int mt9m001_remove(struct i2c_client *client) +{ + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + v4l2_clk_put(mt9m001->clk); + v4l2_device_unregister_subdev(&mt9m001->subdev); + v4l2_ctrl_handler_free(&mt9m001->hdl); + mt9m001_video_remove(ssdd); + + return 0; +} + +static const struct i2c_device_id mt9m001_id[] = { + { "mt9m001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9m001_id); + +static struct i2c_driver mt9m001_i2c_driver = { + .driver = { + .name = "mt9m001", + }, + .probe = mt9m001_probe, + .remove = mt9m001_remove, + .id_table = mt9m001_id, +}; + +module_i2c_driver(mt9m001_i2c_driver); + +MODULE_DESCRIPTION("Micron MT9M001 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 4915255bfcae85ffed97150df7c445120f67a64f Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:39 -0200 Subject: media: i2c: mt9m001: dt: add binding for mt9m001 Add device tree binding documentation for the MT9M001 CMOS image sensor. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/mt9m001.txt | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/mt9m001.txt diff --git a/Documentation/devicetree/bindings/media/i2c/mt9m001.txt b/Documentation/devicetree/bindings/media/i2c/mt9m001.txt new file mode 100644 index 000000000000..c920552b03ef --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/mt9m001.txt @@ -0,0 +1,38 @@ +MT9M001: 1/2-Inch Megapixel Digital Image Sensor + +The MT9M001 is an SXGA-format with a 1/2-inch CMOS active-pixel digital +image sensor. It is programmable through I2C interface. + +Required Properties: + +- compatible: shall be "onnn,mt9m001". +- clocks: reference to the master clock into sensor + +Optional Properties: + +- reset-gpios: GPIO handle which is connected to the reset pin of the chip. + Active low. +- standby-gpios: GPIO handle which is connected to the standby pin of the chip. + Active high. + +The device node must contain one 'port' child node with one 'endpoint' child +sub-node for its digital output video port, in accordance with the video +interface bindings defined in: +Documentation/devicetree/bindings/media/video-interfaces.txt + +Example: + + &i2c1 { + camera-sensor@5d { + compatible = "onnn,mt9m001"; + reg = <0x5d>; + reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + standby-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + clocks = <&camera_clk>; + port { + mt9m001_out: endpoint { + remote-endpoint = <&vcap_in>; + }; + }; + }; + }; -- cgit v1.2.3-59-g8ed1b From 1f01091fa1c0416b0423b92f927a83227c22d8d3 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:40 -0200 Subject: media: mt9m001: convert to SPDX license identifer Replace GPL license statements with SPDX license identifiers (GPL-2.0). This also fixes MODULE_LICENSE() ident to match the actual license text. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index a1a85ff838c5..c0b6b0c9d081 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for MT9M001 CMOS Image Sensor from Micron * * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include @@ -754,4 +751,4 @@ module_i2c_driver(mt9m001_i2c_driver); MODULE_DESCRIPTION("Micron MT9M001 Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 9f4f37b03e3c45ef343044816fa33b99c7200319 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:41 -0200 Subject: media: mt9m001: sort headers alphabetically Sort header block alphabetically for easy maintenance. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index c0b6b0c9d081..dc6cf46ed8d0 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -5,17 +5,17 @@ * Copyright (C) 2008, Guennadi Liakhovetski */ -#include -#include #include #include #include +#include +#include -#include #include +#include #include -#include #include +#include /* * mt9m001 i2c address 0x5d -- cgit v1.2.3-59-g8ed1b From 1e57e4ce90f4190b989670fe1b161995b5b5f333 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:42 -0200 Subject: media: mt9m001: add of_match_table Add of_match_table for the MT9M001 CMOS image sensor. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index dc6cf46ed8d0..3864d884b8c3 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -738,9 +738,16 @@ static const struct i2c_device_id mt9m001_id[] = { }; MODULE_DEVICE_TABLE(i2c, mt9m001_id); +static const struct of_device_id mt9m001_of_match[] = { + { .compatible = "onnn,mt9m001", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mt9m001_of_match); + static struct i2c_driver mt9m001_i2c_driver = { .driver = { .name = "mt9m001", + .of_match_table = mt9m001_of_match, }, .probe = mt9m001_probe, .remove = mt9m001_remove, -- cgit v1.2.3-59-g8ed1b From a5826484d46ebc2334ad338d570c6865b8d9f70c Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:43 -0200 Subject: media: mt9m001: introduce multi_reg_write() Introduce multi_reg_write() to write multiple registers to the device and use it where possible. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 88 +++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 3864d884b8c3..7ba11a8da037 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -47,6 +47,8 @@ #define MT9M001_MIN_HEIGHT 32 #define MT9M001_COLUMN_SKIP 20 #define MT9M001_ROW_SKIP 12 +#define MT9M001_DEFAULT_HBLANK 9 +#define MT9M001_DEFAULT_VBLANK 25 /* MT9M001 has only one fixed colorspace per pixelcode */ struct mt9m001_datafmt { @@ -137,25 +139,65 @@ static int reg_clear(struct i2c_client *client, const u8 reg, return reg_write(client, reg, ret & ~data); } +struct mt9m001_reg { + u8 reg; + u16 data; +}; + +static int multi_reg_write(struct i2c_client *client, + const struct mt9m001_reg *regs, int num) +{ + int i; + + for (i = 0; i < num; i++) { + int ret = reg_write(client, regs[i].reg, regs[i].data); + + if (ret) + return ret; + } + + return 0; +} + static int mt9m001_init(struct i2c_client *client) { - int ret; + const struct mt9m001_reg init_regs[] = { + /* + * Issue a soft reset. This returns all registers to their + * default values. + */ + { MT9M001_RESET, 1 }, + { MT9M001_RESET, 0 }, + /* Disable chip, synchronous option update */ + { MT9M001_OUTPUT_CONTROL, 0 } + }; dev_dbg(&client->dev, "%s\n", __func__); - /* - * We don't know, whether platform provides reset, issue a soft reset - * too. This returns all registers to their default values. - */ - ret = reg_write(client, MT9M001_RESET, 1); - if (!ret) - ret = reg_write(client, MT9M001_RESET, 0); + return multi_reg_write(client, init_regs, ARRAY_SIZE(init_regs)); +} - /* Disable chip, synchronous option update */ - if (!ret) - ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0); +static int mt9m001_apply_selection(struct v4l2_subdev *sd, + struct v4l2_rect *rect) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + const struct mt9m001_reg regs[] = { + /* Blanking and start values - default... */ + { MT9M001_HORIZONTAL_BLANKING, MT9M001_DEFAULT_HBLANK }, + { MT9M001_VERTICAL_BLANKING, MT9M001_DEFAULT_VBLANK }, + /* + * The caller provides a supported format, as verified per + * call to .set_fmt(FORMAT_TRY). + */ + { MT9M001_COLUMN_START, rect->left }, + { MT9M001_ROW_START, rect->top }, + { MT9M001_WINDOW_WIDTH, rect->width - 1 }, + { MT9M001_WINDOW_HEIGHT, + rect->height + mt9m001->y_skip_top - 1 }, + }; - return ret; + return multi_reg_write(client, regs, ARRAY_SIZE(regs)); } static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) @@ -175,7 +217,6 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); struct v4l2_rect rect = sel->r; - const u16 hblank = 9, vblank = 25; int ret; if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || @@ -199,26 +240,11 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd, soc_camera_limit_side(&rect.top, &rect.height, MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); - mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank; + mt9m001->total_h = rect.height + mt9m001->y_skip_top + + MT9M001_DEFAULT_VBLANK; - /* Blanking and start values - default... */ - ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); - if (!ret) - ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank); - /* - * The caller provides a supported format, as verified per - * call to .set_fmt(FORMAT_TRY). - */ - if (!ret) - ret = reg_write(client, MT9M001_COLUMN_START, rect.left); - if (!ret) - ret = reg_write(client, MT9M001_ROW_START, rect.top); - if (!ret) - ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1); - if (!ret) - ret = reg_write(client, MT9M001_WINDOW_HEIGHT, - rect.height + mt9m001->y_skip_top - 1); + ret = mt9m001_apply_selection(sd, &rect); if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO) ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h); -- cgit v1.2.3-59-g8ed1b From 8fcfc491c6ca5887bb341b3a622cca3ed8e3c9f0 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:44 -0200 Subject: media: mt9m001: switch s_power callback to runtime PM Switch s_power() callback to runtime PM framework. This also removes soc_camera specific power management code and introduces reset and standby gpios instead. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 222 +++++++++++++++++++++++++++++++------------- 1 file changed, 160 insertions(+), 62 deletions(-) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 7ba11a8da037..1619c8cdbcb6 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -5,15 +5,18 @@ * Copyright (C) 2008, Guennadi Liakhovetski */ +#include +#include +#include #include #include #include +#include #include #include #include #include -#include #include #include @@ -92,8 +95,12 @@ struct mt9m001 { struct v4l2_ctrl *autoexposure; struct v4l2_ctrl *exposure; }; + bool streaming; + struct mutex mutex; struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; + struct clk *clk; + struct gpio_desc *standby_gpio; + struct gpio_desc *reset_gpio; const struct mt9m001_datafmt *fmt; const struct mt9m001_datafmt *fmts; int num_fmts; @@ -177,8 +184,7 @@ static int mt9m001_init(struct i2c_client *client) return multi_reg_write(client, init_regs, ARRAY_SIZE(init_regs)); } -static int mt9m001_apply_selection(struct v4l2_subdev *sd, - struct v4l2_rect *rect) +static int mt9m001_apply_selection(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); @@ -190,11 +196,11 @@ static int mt9m001_apply_selection(struct v4l2_subdev *sd, * The caller provides a supported format, as verified per * call to .set_fmt(FORMAT_TRY). */ - { MT9M001_COLUMN_START, rect->left }, - { MT9M001_ROW_START, rect->top }, - { MT9M001_WINDOW_WIDTH, rect->width - 1 }, + { MT9M001_COLUMN_START, mt9m001->rect.left }, + { MT9M001_ROW_START, mt9m001->rect.top }, + { MT9M001_WINDOW_WIDTH, mt9m001->rect.width - 1 }, { MT9M001_WINDOW_HEIGHT, - rect->height + mt9m001->y_skip_top - 1 }, + mt9m001->rect.height + mt9m001->y_skip_top - 1 }, }; return multi_reg_write(client, regs, ARRAY_SIZE(regs)); @@ -203,11 +209,48 @@ static int mt9m001_apply_selection(struct v4l2_subdev *sd, static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) { struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + int ret = 0; + + mutex_lock(&mt9m001->mutex); + + if (mt9m001->streaming == enable) + goto done; + + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) + goto put_unlock; + + ret = mt9m001_apply_selection(sd); + if (ret) + goto put_unlock; + + ret = __v4l2_ctrl_handler_setup(&mt9m001->hdl); + if (ret) + goto put_unlock; + + /* Switch to master "normal" mode */ + ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 2); + if (ret < 0) + goto put_unlock; + } else { + /* Switch to master stop sensor readout */ + reg_write(client, MT9M001_OUTPUT_CONTROL, 0); + pm_runtime_put(&client->dev); + } + + mt9m001->streaming = enable; +done: + mutex_unlock(&mt9m001->mutex); - /* Switch to master "normal" mode or stop sensor readout */ - if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0) - return -EIO; return 0; + +put_unlock: + pm_runtime_put(&client->dev); + mutex_unlock(&mt9m001->mutex); + + return ret; } static int mt9m001_set_selection(struct v4l2_subdev *sd, @@ -217,7 +260,6 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9m001 *mt9m001 = to_mt9m001(client); struct v4l2_rect rect = sel->r; - int ret; if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || sel->target != V4L2_SEL_TGT_CROP) @@ -243,15 +285,9 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd, mt9m001->total_h = rect.height + mt9m001->y_skip_top + MT9M001_DEFAULT_VBLANK; + mt9m001->rect = rect; - ret = mt9m001_apply_selection(sd, &rect); - if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO) - ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h); - - if (!ret) - mt9m001->rect = rect; - - return ret; + return 0; } static int mt9m001_get_selection(struct v4l2_subdev *sd, @@ -395,13 +431,40 @@ static int mt9m001_s_register(struct v4l2_subdev *sd, } #endif -static int mt9m001_s_power(struct v4l2_subdev *sd, int on) +static int mt9m001_power_on(struct device *dev) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct i2c_client *client = to_i2c_client(dev); struct mt9m001 *mt9m001 = to_mt9m001(client); + int ret; + + ret = clk_prepare_enable(mt9m001->clk); + if (ret) + return ret; + + if (mt9m001->standby_gpio) { + gpiod_set_value_cansleep(mt9m001->standby_gpio, 0); + usleep_range(1000, 2000); + } - return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on); + if (mt9m001->reset_gpio) { + gpiod_set_value_cansleep(mt9m001->reset_gpio, 1); + usleep_range(1000, 2000); + gpiod_set_value_cansleep(mt9m001->reset_gpio, 0); + usleep_range(1000, 2000); + } + + return 0; +} + +static int mt9m001_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mt9m001 *mt9m001 = to_mt9m001(client); + + gpiod_set_value_cansleep(mt9m001->standby_gpio, 1); + clk_disable_unprepare(mt9m001->clk); + + return 0; } static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -429,16 +492,18 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) struct i2c_client *client = v4l2_get_subdevdata(sd); struct v4l2_ctrl *exp = mt9m001->exposure; int data; + int ret; + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; switch (ctrl->id) { case V4L2_CID_VFLIP: if (ctrl->val) - data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000); + ret = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000); else - data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000); - if (data < 0) - return -EIO; - return 0; + ret = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000); + break; case V4L2_CID_GAIN: /* See Datasheet Table 7, Gain settings. */ @@ -448,9 +513,7 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; dev_dbg(&client->dev, "Setting gain %d\n", data); - data = reg_write(client, MT9M001_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; + ret = reg_write(client, MT9M001_GLOBAL_GAIN, data); } else { /* Pack it into 1.125..15 variable step, register values 9..67 */ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ @@ -467,11 +530,9 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) dev_dbg(&client->dev, "Setting gain from %d to %d\n", reg_read(client, MT9M001_GLOBAL_GAIN), data); - data = reg_write(client, MT9M001_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; + ret = reg_write(client, MT9M001_GLOBAL_GAIN, data); } - return 0; + break; case V4L2_CID_EXPOSURE_AUTO: if (ctrl->val == V4L2_EXPOSURE_MANUAL) { @@ -482,19 +543,22 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) dev_dbg(&client->dev, "Setting shutter width from %d to %lu\n", reg_read(client, MT9M001_SHUTTER_WIDTH), shutter); - if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0) - return -EIO; + ret = reg_write(client, MT9M001_SHUTTER_WIDTH, shutter); } else { - const u16 vblank = 25; - mt9m001->total_h = mt9m001->rect.height + - mt9m001->y_skip_top + vblank; - if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0) - return -EIO; + mt9m001->y_skip_top + MT9M001_DEFAULT_VBLANK; + ret = reg_write(client, MT9M001_SHUTTER_WIDTH, + mt9m001->total_h); } - return 0; + break; + default: + ret = -EINVAL; + break; } - return -EINVAL; + + pm_runtime_put(&client->dev); + + return ret; } /* @@ -509,10 +573,6 @@ static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, unsigned long flags; int ret; - ret = mt9m001_s_power(&mt9m001->subdev, 1); - if (ret < 0) - return ret; - /* Enable the chip */ data = reg_write(client, MT9M001_CHIP_ENABLE, 1); dev_dbg(&client->dev, "write: %d\n", data); @@ -571,7 +631,6 @@ static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, ret = v4l2_ctrl_handler_setup(&mt9m001->hdl); done: - mt9m001_s_power(&mt9m001->subdev, 0); return ret; } @@ -601,7 +660,6 @@ static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { .g_register = mt9m001_g_register, .s_register = mt9m001_s_register, #endif - .s_power = mt9m001_s_power, }; static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, @@ -700,6 +758,20 @@ static int mt9m001_probe(struct i2c_client *client, if (!mt9m001) return -ENOMEM; + mt9m001->clk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(mt9m001->clk)) + return PTR_ERR(mt9m001->clk); + + mt9m001->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby", + GPIOD_OUT_LOW); + if (IS_ERR(mt9m001->standby_gpio)) + return PTR_ERR(mt9m001->standby_gpio); + + mt9m001->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(mt9m001->reset_gpio)) + return PTR_ERR(mt9m001->reset_gpio); + v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); v4l2_ctrl_handler_init(&mt9m001->hdl, 4); v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, @@ -722,6 +794,9 @@ static int mt9m001_probe(struct i2c_client *client, v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure, V4L2_EXPOSURE_MANUAL, true); + mutex_init(&mt9m001->mutex); + mt9m001->hdl.lock = &mt9m001->mutex; + /* Second stage probe - when a capture adapter is there */ mt9m001->y_skip_top = 0; mt9m001->rect.left = MT9M001_COLUMN_SKIP; @@ -729,18 +804,29 @@ static int mt9m001_probe(struct i2c_client *client, mt9m001->rect.width = MT9M001_MAX_WIDTH; mt9m001->rect.height = MT9M001_MAX_HEIGHT; - mt9m001->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9m001->clk)) { - ret = PTR_ERR(mt9m001->clk); - goto eclkget; - } + ret = mt9m001_power_on(&client->dev); + if (ret) + goto error_hdl_free; + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); ret = mt9m001_video_probe(ssdd, client); - if (ret) { - v4l2_clk_put(mt9m001->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9m001->hdl); - } + if (ret) + goto error_power_off; + + pm_runtime_idle(&client->dev); + + return 0; + +error_power_off: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + mt9m001_power_off(&client->dev); + +error_hdl_free: + v4l2_ctrl_handler_free(&mt9m001->hdl); + mutex_destroy(&mt9m001->mutex); return ret; } @@ -750,10 +836,17 @@ static int mt9m001_remove(struct i2c_client *client) struct mt9m001 *mt9m001 = to_mt9m001(client); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - v4l2_clk_put(mt9m001->clk); v4l2_device_unregister_subdev(&mt9m001->subdev); + pm_runtime_get_sync(&client->dev); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + mt9m001_power_off(&client->dev); + v4l2_ctrl_handler_free(&mt9m001->hdl); mt9m001_video_remove(ssdd); + mutex_destroy(&mt9m001->mutex); return 0; } @@ -764,6 +857,10 @@ static const struct i2c_device_id mt9m001_id[] = { }; MODULE_DEVICE_TABLE(i2c, mt9m001_id); +static const struct dev_pm_ops mt9m001_pm_ops = { + SET_RUNTIME_PM_OPS(mt9m001_power_off, mt9m001_power_on, NULL) +}; + static const struct of_device_id mt9m001_of_match[] = { { .compatible = "onnn,mt9m001", }, { /* sentinel */ }, @@ -773,6 +870,7 @@ MODULE_DEVICE_TABLE(of, mt9m001_of_match); static struct i2c_driver mt9m001_i2c_driver = { .driver = { .name = "mt9m001", + .pm = &mt9m001_pm_ops, .of_match_table = mt9m001_of_match, }, .probe = mt9m001_probe, -- cgit v1.2.3-59-g8ed1b From 74021329a76b049d8f411828867d80fc288d2e5a Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 9 Jan 2019 13:07:46 -0200 Subject: media: mt9m001: remove remaining soc_camera specific code Remove remaining soc_camera specific code and drop soc_camera dependency from this driver. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 +- drivers/media/i2c/mt9m001.c | 84 ++++++++------------------------------------- 2 files changed, 15 insertions(+), 71 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 7629e03ebcc9..ac35284f2c7a 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -869,7 +869,7 @@ config VIDEO_VS6624 config VIDEO_MT9M001 tristate "mt9m001 support" - depends on SOC_CAMERA && I2C + depends on I2C && VIDEO_V4L2 help This driver supports MT9M001 cameras from Micron, monochrome and colour models. diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 1619c8cdbcb6..5a3b61238fca 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -15,15 +15,12 @@ #include #include -#include -#include #include +#include #include /* * mt9m001 i2c address 0x5d - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc */ /* mt9m001 selected register addresses */ @@ -276,11 +273,15 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd, rect.width = ALIGN(rect.width, 2); rect.left = ALIGN(rect.left, 2); - soc_camera_limit_side(&rect.left, &rect.width, - MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH); + rect.width = clamp_t(u32, rect.width, MT9M001_MIN_WIDTH, + MT9M001_MAX_WIDTH); + rect.left = clamp_t(u32, rect.left, MT9M001_COLUMN_SKIP, + MT9M001_COLUMN_SKIP + MT9M001_MAX_WIDTH - rect.width); - soc_camera_limit_side(&rect.top, &rect.height, - MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); + rect.height = clamp_t(u32, rect.height, MT9M001_MIN_HEIGHT, + MT9M001_MAX_HEIGHT); + rect.top = clamp_t(u32, rect.top, MT9M001_ROW_SKIP, + MT9M001_ROW_SKIP + MT9M001_MAX_HEIGHT - rect.height); mt9m001->total_h = rect.height + mt9m001->y_skip_top + MT9M001_DEFAULT_VBLANK; @@ -565,12 +566,10 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, - struct i2c_client *client) +static int mt9m001_video_probe(struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); s32 data; - unsigned long flags; int ret; /* Enable the chip */ @@ -585,9 +584,11 @@ static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, case 0x8411: case 0x8421: mt9m001->fmts = mt9m001_colour_fmts; + mt9m001->num_fmts = ARRAY_SIZE(mt9m001_colour_fmts); break; case 0x8431: mt9m001->fmts = mt9m001_monochrome_fmts; + mt9m001->num_fmts = ARRAY_SIZE(mt9m001_monochrome_fmts); break; default: dev_err(&client->dev, @@ -596,26 +597,6 @@ static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, goto done; } - mt9m001->num_fmts = 0; - - /* - * This is a 10bit sensor, so by default we only allow 10bit. - * The platform may support different bus widths due to - * different routing of the data lines. - */ - if (ssdd->query_bus_param) - flags = ssdd->query_bus_param(ssdd); - else - flags = SOCAM_DATAWIDTH_10; - - if (flags & SOCAM_DATAWIDTH_10) - mt9m001->num_fmts++; - else - mt9m001->fmts++; - - if (flags & SOCAM_DATAWIDTH_8) - mt9m001->num_fmts++; - mt9m001->fmt = &mt9m001->fmts[0]; dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, @@ -634,12 +615,6 @@ done: return ret; } -static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd) -{ - if (ssdd->free_bus) - ssdd->free_bus(ssdd); -} - static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -679,41 +654,18 @@ static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - /* MT9M001 has all capture_format parameters fixed */ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } -static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - const struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9m001 *mt9m001 = to_mt9m001(client); - unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; - - if (ssdd->set_bus_param) - return ssdd->set_bus_param(ssdd, 1 << (bps - 1)); - - /* - * Without board specific bus width settings we only support the - * sensors native bus width - */ - return bps == 10 ? 0 : -EINVAL; -} - static const struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { .s_stream = mt9m001_s_stream, .g_mbus_config = mt9m001_g_mbus_config, - .s_mbus_config = mt9m001_s_mbus_config, }; static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { @@ -740,21 +692,15 @@ static int mt9m001_probe(struct i2c_client *client, { struct mt9m001 *mt9m001; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!ssdd) { - dev_err(&client->dev, "MT9M001 driver needs platform data\n"); - return -EINVAL; - } - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { dev_warn(&adapter->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); return -EIO; } - mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL); + mt9m001 = devm_kzalloc(&client->dev, sizeof(*mt9m001), GFP_KERNEL); if (!mt9m001) return -ENOMEM; @@ -811,7 +757,7 @@ static int mt9m001_probe(struct i2c_client *client, pm_runtime_set_active(&client->dev); pm_runtime_enable(&client->dev); - ret = mt9m001_video_probe(ssdd, client); + ret = mt9m001_video_probe(client); if (ret) goto error_power_off; @@ -834,7 +780,6 @@ error_hdl_free: static int mt9m001_remove(struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); v4l2_device_unregister_subdev(&mt9m001->subdev); pm_runtime_get_sync(&client->dev); @@ -845,7 +790,6 @@ static int mt9m001_remove(struct i2c_client *client) mt9m001_power_off(&client->dev); v4l2_ctrl_handler_free(&mt9m001->hdl); - mt9m001_video_remove(ssdd); mutex_destroy(&mt9m001->mutex); return 0; -- cgit v1.2.3-59-g8ed1b From 12d85c3e275be6b72fb815eb03d15cab31ee10c1 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:46 -0200 Subject: media: mt9m001: add media controller support Create a source pad and set the media controller type to the sensor. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 ++ drivers/media/i2c/mt9m001.c | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index ac35284f2c7a..2a9e87a7ba25 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -870,6 +870,8 @@ config VIDEO_VS6624 config VIDEO_MT9M001 tristate "mt9m001 support" depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + depends on MEDIA_CONTROLLER help This driver supports MT9M001 cameras from Micron, monochrome and colour models. diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 5a3b61238fca..0a648e2df00d 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -103,6 +103,7 @@ struct mt9m001 { int num_fmts; unsigned int total_h; unsigned short y_skip_top; /* Lines to skip at the top */ + struct media_pad pad; }; static struct mt9m001 *to_mt9m001(const struct i2c_client *client) @@ -761,6 +762,12 @@ static int mt9m001_probe(struct i2c_client *client, if (ret) goto error_power_off; + mt9m001->pad.flags = MEDIA_PAD_FL_SOURCE; + mt9m001->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&mt9m001->subdev.entity, 1, &mt9m001->pad); + if (ret) + goto error_power_off; + pm_runtime_idle(&client->dev); return 0; @@ -784,6 +791,8 @@ static int mt9m001_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&mt9m001->subdev); pm_runtime_get_sync(&client->dev); + media_entity_cleanup(&mt9m001->subdev.entity); + pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); pm_runtime_put_noidle(&client->dev); -- cgit v1.2.3-59-g8ed1b From 73b338609845a2c429953f7dd5d027f477b189ea Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:47 -0200 Subject: media: mt9m001: register to V4L2 asynchronous subdevice framework Register a sub-device to the asynchronous subdevice framework, and also create subdevice device node. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 3 +-- drivers/media/i2c/mt9m001.c | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 2a9e87a7ba25..19c112cda078 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -869,9 +869,8 @@ config VIDEO_VS6624 config VIDEO_MT9M001 tristate "mt9m001 support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT - depends on MEDIA_CONTROLLER help This driver supports MT9M001 cameras from Micron, monochrome and colour models. diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 0a648e2df00d..94c9919963b4 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -720,6 +720,7 @@ static int mt9m001_probe(struct i2c_client *client, return PTR_ERR(mt9m001->reset_gpio); v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); + mt9m001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; v4l2_ctrl_handler_init(&mt9m001->hdl, 4); v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); @@ -768,10 +769,16 @@ static int mt9m001_probe(struct i2c_client *client, if (ret) goto error_power_off; + ret = v4l2_async_register_subdev(&mt9m001->subdev); + if (ret) + goto error_entity_cleanup; + pm_runtime_idle(&client->dev); return 0; +error_entity_cleanup: + media_entity_cleanup(&mt9m001->subdev.entity); error_power_off: pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); @@ -788,9 +795,9 @@ static int mt9m001_remove(struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); - v4l2_device_unregister_subdev(&mt9m001->subdev); pm_runtime_get_sync(&client->dev); + v4l2_async_unregister_subdev(&mt9m001->subdev); media_entity_cleanup(&mt9m001->subdev.entity); pm_runtime_disable(&client->dev); -- cgit v1.2.3-59-g8ed1b From dd15f0702f12cf80ecebe173c6b04ec3dfa10703 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:48 -0200 Subject: media: mt9m001: support log_status ioctl and event interface This adds log_status ioctl and event interface for mt9m001's v4l2 controls. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 94c9919963b4..479e42dffd2e 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -17,6 +17,7 @@ #include #include +#include #include /* @@ -632,6 +633,9 @@ static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = { }; static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9m001_g_register, .s_register = mt9m001_s_register, @@ -720,7 +724,8 @@ static int mt9m001_probe(struct i2c_client *client, return PTR_ERR(mt9m001->reset_gpio); v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); - mt9m001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mt9m001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; v4l2_ctrl_handler_init(&mt9m001->hdl, 4); v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); -- cgit v1.2.3-59-g8ed1b From 71f247daf0c7a82a2e9713daa0895f462a6fd8df Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:49 -0200 Subject: media: mt9m001: make SUBDEV_G_FMT ioctl work with SUBDEV_FORMAT_TRY The VIDIOC_SUBDEV_G_FMT ioctl for this driver doesn't recognize V4L2_SUBDEV_FORMAT_TRY and always works as if V4L2_SUBDEV_FORMAT_ACTIVE is specified. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 479e42dffd2e..eac10ffce795 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -329,6 +329,12 @@ static int mt9m001_get_fmt(struct v4l2_subdev *sd, if (format->pad) return -EINVAL; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(sd, cfg, 0); + format->format = *mf; + return 0; + } + mf->width = mt9m001->rect.width; mf->height = mt9m001->rect.height; mf->code = mt9m001->fmt->code; @@ -642,6 +648,26 @@ static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { #endif }; +static int mt9m001_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, cfg, 0); + + try_fmt->width = MT9M001_MAX_WIDTH; + try_fmt->height = MT9M001_MAX_HEIGHT; + try_fmt->code = mt9m001->fmts[0].code; + try_fmt->colorspace = mt9m001->fmts[0].colorspace; + try_fmt->field = V4L2_FIELD_NONE; + try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + return 0; +} + static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) @@ -678,6 +704,7 @@ static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { }; static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { + .init_cfg = mt9m001_init_cfg, .enum_mbus_code = mt9m001_enum_mbus_code, .get_selection = mt9m001_get_selection, .set_selection = mt9m001_set_selection, -- cgit v1.2.3-59-g8ed1b From 772f63c00c31d651cf07c1910683093bdd3ad4be Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 8 Jan 2019 12:51:50 -0200 Subject: media: mt9m001: set all mbus format field when G_FMT and S_FMT ioctls This driver doesn't set all members of mbus format field when the VIDIOC_SUBDEV_{S,G}_FMT ioctls are called. This is detected by v4l2-compliance. Cc: Guennadi Liakhovetski Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index eac10ffce795..4b23fde937b3 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -340,6 +340,9 @@ static int mt9m001_get_fmt(struct v4l2_subdev *sd, mf->code = mt9m001->fmt->code; mf->colorspace = mt9m001->fmt->colorspace; mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; return 0; } @@ -400,6 +403,10 @@ static int mt9m001_set_fmt(struct v4l2_subdev *sd, } mf->colorspace = fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return mt9m001_s_fmt(sd, fmt, mf); -- cgit v1.2.3-59-g8ed1b From 49410d3abf4492d8be1e6ce6e8ece0e16407f22d Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 15 Jan 2019 12:05:39 -0200 Subject: media: mt9m111: make SUBDEV_G_FMT ioctl work with SUBDEV_FORMAT_TRY The VIDIOC_SUBDEV_G_FMT ioctl for this driver doesn't recognize V4L2_SUBDEV_FORMAT_TRY and always works as if V4L2_SUBDEV_FORMAT_ACTIVE is specified. Cc: Enrico Scholz Cc: Michael Grzeschik Cc: Marco Felsch Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m111.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index d639b9bcf64a..63a5253a3376 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -528,6 +528,16 @@ static int mt9m111_get_fmt(struct v4l2_subdev *sd, if (format->pad) return -EINVAL; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + mf = v4l2_subdev_get_try_format(sd, cfg, format->pad); + format->format = *mf; + return 0; +#else + return -ENOTTY; +#endif + } + mf->width = mt9m111->width; mf->height = mt9m111->height; mf->code = mt9m111->fmt->code; @@ -1089,6 +1099,25 @@ static int mt9m111_s_stream(struct v4l2_subdev *sd, int enable) return 0; } +static int mt9m111_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + struct v4l2_mbus_framefmt *format = + v4l2_subdev_get_try_format(sd, cfg, 0); + + format->width = MT9M111_MAX_WIDTH; + format->height = MT9M111_MAX_HEIGHT; + format->code = mt9m111_colour_fmts[0].code; + format->colorspace = mt9m111_colour_fmts[0].colorspace; + format->field = V4L2_FIELD_NONE; + format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + format->quantization = V4L2_QUANTIZATION_DEFAULT; + format->xfer_func = V4L2_XFER_FUNC_DEFAULT; +#endif + return 0; +} + static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { @@ -1114,6 +1143,7 @@ static const struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { }; static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = { + .init_cfg = mt9m111_init_cfg, .enum_mbus_code = mt9m111_enum_mbus_code, .get_selection = mt9m111_get_selection, .set_selection = mt9m111_set_selection, -- cgit v1.2.3-59-g8ed1b From 2e1566abe54b3ad3e5094c7b80883bc70eda6539 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 15 Jan 2019 12:05:40 -0200 Subject: media: mt9m111: set all mbus format field when G_FMT and S_FMT ioctls This driver doesn't set all members of mbus format field when the VIDIOC_SUBDEV_{S,G}_FMT ioctls are called. This is detected by v4l2-compliance. Cc: Enrico Scholz Cc: Michael Grzeschik Cc: Marco Felsch Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m111.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 63a5253a3376..9c92eca8020c 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -543,6 +543,9 @@ static int mt9m111_get_fmt(struct v4l2_subdev *sd, mf->code = mt9m111->fmt->code; mf->colorspace = mt9m111->fmt->colorspace; mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; return 0; } @@ -670,6 +673,10 @@ static int mt9m111_set_fmt(struct v4l2_subdev *sd, mf->code = fmt->code; mf->colorspace = fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; if (format->which == V4L2_SUBDEV_FORMAT_TRY) { cfg->try_fmt = *mf; -- cgit v1.2.3-59-g8ed1b From 29856308137de1c21eda89411695f4fc6e9780ff Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 15 Jan 2019 12:05:41 -0200 Subject: media: mt9m111: set initial frame size other than 0x0 This driver sets initial frame width and height to 0x0, which is invalid. So set it to selection rectangle bounds instead. This is detected by v4l2-compliance detected. Cc: Enrico Scholz Cc: Michael Grzeschik Cc: Marco Felsch Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m111.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 9c92eca8020c..5168bb5880c4 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -1310,6 +1310,8 @@ static int mt9m111_probe(struct i2c_client *client, mt9m111->rect.top = MT9M111_MIN_DARK_ROWS; mt9m111->rect.width = MT9M111_MAX_WIDTH; mt9m111->rect.height = MT9M111_MAX_HEIGHT; + mt9m111->width = mt9m111->rect.width; + mt9m111->height = mt9m111->rect.height; mt9m111->fmt = &mt9m111_colour_fmts[0]; mt9m111->lastpage = -1; mutex_init(&mt9m111->power_lock); -- cgit v1.2.3-59-g8ed1b From 61c1baa63430b391f09dce6b6cd2390293c4bcd6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Jan 2019 17:54:54 -0200 Subject: media: s5k4ecgx: delete a bogus error message This function prints an error message on success. I don't have the hardware, I just noticed this while reading the code. Fixes: 8b99312b7214 ("[media] Add v4l2 subdev driver for S5K4ECGX sensor") Signed-off-by: Dan Carpenter Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5k4ecgx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c index 79aa2740edc4..79c1894c2c83 100644 --- a/drivers/media/i2c/s5k4ecgx.c +++ b/drivers/media/i2c/s5k4ecgx.c @@ -263,8 +263,6 @@ static int s5k4ecgx_read(struct i2c_client *client, u32 addr, u16 *val) ret = s5k4ecgx_i2c_write(client, REG_CMDRD_ADDRL, low); if (!ret) ret = s5k4ecgx_i2c_read(client, REG_CMDBUF0_ADDR, val); - if (!ret) - dev_err(&client->dev, "Failed to execute read command\n"); return ret; } -- cgit v1.2.3-59-g8ed1b From adbd2969afbb9868f9a480cee4234455577a93c8 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 22 Jan 2019 11:42:59 -0200 Subject: media: ov2640: fix initial try format Set initial try format with default configuration instead of current one. Fixes: 8d3b307a150a ("media: ov2640: make VIDIOC_SUBDEV_G_FMT ioctl work with V4L2_SUBDEV_FORMAT_TRY") Signed-off-by: Akinobu Mita Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2640.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 37e7c986af86..83031cfc7914 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -1011,14 +1011,14 @@ static int ov2640_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov2640_priv *priv = to_ov2640(client); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + const struct ov2640_win_size *win = + ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); - try_fmt->width = priv->win->width; - try_fmt->height = priv->win->height; - try_fmt->code = priv->cfmt_code; + try_fmt->width = win->width; + try_fmt->height = win->height; + try_fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; try_fmt->colorspace = V4L2_COLORSPACE_SRGB; try_fmt->field = V4L2_FIELD_NONE; try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; -- cgit v1.2.3-59-g8ed1b From 5792ae7c3dd41883cdfa91284b802104b75c59d4 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 6 Dec 2018 11:28:43 -0200 Subject: media: venus: firmware: check fw size against DT memory region size By historical reasons we defined firmware memory size to be 6MB even that the firmware size for all supported Venus versions is 5MBs. Correct that by compare the required firmware size returned from mdt loader and the one provided by DT reserved memory region. We proceed further if the required firmware size is smaller than provided by DT memory region. Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/firmware.c | 53 ++++++++++++++++------------ 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 6382cea29185..79c7e816c706 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -134,6 +134,7 @@ struct venus_core { struct video_firmware { struct device *dev; struct iommu_domain *iommu_domain; + size_t mapped_mem_size; } fw; struct mutex lock; struct list_head instances; diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index c29acfd70c1b..6cfa8021721e 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -35,14 +35,15 @@ static void venus_reset_cpu(struct venus_core *core) { + u32 fw_size = core->fw.mapped_mem_size; void __iomem *base = core->base; writel(0, base + WRAPPER_FW_START_ADDR); - writel(VENUS_FW_MEM_SIZE, base + WRAPPER_FW_END_ADDR); + writel(fw_size, base + WRAPPER_FW_END_ADDR); writel(0, base + WRAPPER_CPA_START_ADDR); - writel(VENUS_FW_MEM_SIZE, base + WRAPPER_CPA_END_ADDR); - writel(VENUS_FW_MEM_SIZE, base + WRAPPER_NONPIX_START_ADDR); - writel(VENUS_FW_MEM_SIZE, base + WRAPPER_NONPIX_END_ADDR); + writel(fw_size, base + WRAPPER_CPA_END_ADDR); + writel(fw_size, base + WRAPPER_NONPIX_START_ADDR); + writel(fw_size, base + WRAPPER_NONPIX_END_ADDR); writel(0x0, base + WRAPPER_CPU_CGC_DIS); writel(0x0, base + WRAPPER_CPU_CLOCK_CONFIG); @@ -74,6 +75,9 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, void *mem_va; int ret; + *mem_phys = 0; + *mem_size = 0; + dev = core->dev; node = of_parse_phandle(dev->of_node, "memory-region", 0); if (!node) { @@ -85,28 +89,30 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, if (ret) return ret; + ret = request_firmware(&mdt, fwname, dev); + if (ret < 0) + return ret; + + fw_size = qcom_mdt_get_size(mdt); + if (fw_size < 0) { + ret = fw_size; + goto err_release_fw; + } + *mem_phys = r.start; *mem_size = resource_size(&r); - if (*mem_size < VENUS_FW_MEM_SIZE) - return -EINVAL; + if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) { + ret = -EINVAL; + goto err_release_fw; + } mem_va = memremap(r.start, *mem_size, MEMREMAP_WC); if (!mem_va) { dev_err(dev, "unable to map memory region: %pa+%zx\n", &r.start, *mem_size); - return -ENOMEM; - } - - ret = request_firmware(&mdt, fwname, dev); - if (ret < 0) - goto err_unmap; - - fw_size = qcom_mdt_get_size(mdt); - if (fw_size < 0) { - ret = fw_size; - release_firmware(mdt); - goto err_unmap; + ret = -ENOMEM; + goto err_release_fw; } if (core->use_tz) @@ -116,10 +122,9 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, ret = qcom_mdt_load_no_init(dev, mdt, fwname, VENUS_PAS_ID, mem_va, *mem_phys, *mem_size, NULL); - release_firmware(mdt); - -err_unmap: memunmap(mem_va); +err_release_fw: + release_firmware(mdt); return ret; } @@ -135,6 +140,7 @@ static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys, return -EPROBE_DEFER; iommu = core->fw.iommu_domain; + core->fw.mapped_mem_size = mem_size; ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size, IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV); @@ -150,6 +156,7 @@ static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys, static int venus_shutdown_no_tz(struct venus_core *core) { + const size_t mapped = core->fw.mapped_mem_size; struct iommu_domain *iommu; size_t unmapped; u32 reg; @@ -166,8 +173,8 @@ static int venus_shutdown_no_tz(struct venus_core *core) iommu = core->fw.iommu_domain; - unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, VENUS_FW_MEM_SIZE); - if (unmapped != VENUS_FW_MEM_SIZE) + unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped); + if (unmapped != mapped) dev_err(dev, "failed to unmap firmware\n"); return 0; -- cgit v1.2.3-59-g8ed1b From de5a0bafcfc479b3b9513d6171b608f73a72362b Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 6 Dec 2018 11:52:45 -0200 Subject: media: venus: core: correct maximum hardware load for sdm845 This correct maximum hardware load constant in per SoC resources for sdm845 aka Venus v4. Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index cb411eb85ee4..d95185ea32c3 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -455,7 +455,7 @@ static const struct venus_resources msm8996_res = { .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset), .clks = {"core", "iface", "bus", "mbus" }, .clks_num = 4, - .max_load = 2563200, + .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_3XX, .vmem_id = VIDC_RESOURCE_NONE, .vmem_size = 0, -- cgit v1.2.3-59-g8ed1b From d24f800247b5030794a0e9540c2a95ab49afff7f Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 6 Dec 2018 14:00:15 -0200 Subject: media: venus: core: correct frequency table for sdm845 This corrects clock frequency table rates to be in sync with video clock controller frequency table. Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index d95185ea32c3..739366744e0f 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -465,10 +465,12 @@ static const struct venus_resources msm8996_res = { }; static const struct freq_tbl sdm845_freq_table[] = { - { 1944000, 380000000 }, /* 4k UHD @ 60 */ - { 972000, 320000000 }, /* 4k UHD @ 30 */ - { 489600, 200000000 }, /* 1080p @ 60 */ - { 244800, 100000000 }, /* 1080p @ 30 */ + { 3110400, 533000000 }, /* 4096x2160@90 */ + { 2073600, 444000000 }, /* 4096x2160@60 */ + { 1944000, 404000000 }, /* 3840x2160@60 */ + { 972000, 330000000 }, /* 3840x2160@30 */ + { 489600, 200000000 }, /* 1920x1080@60 */ + { 244800, 100000000 }, /* 1920x1080@30 */ }; static const struct venus_resources sdm845_res = { -- cgit v1.2.3-59-g8ed1b From 947e3b3cf190929604965ab06588ff025fbec2b3 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 7 Dec 2018 11:04:13 -0200 Subject: media: venus: helpers: drop setting of timestamp invalid flag The zero timestamp is really valid so fix that mistake by dropping the code which checks for zero timestamp. Reviewed-by: Alexandre Courbot Tested-by: Alexandre Courbot Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index e436385bc5ab..5cad601d4c57 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -439,9 +439,6 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) fdata.flags = 0; fdata.clnt_data = vbuf->vb2_buf.index; - if (!fdata.timestamp) - fdata.flags |= HFI_BUFFERFLAG_TIMESTAMPINVALID; - if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { fdata.buffer_type = HFI_BUFFER_INPUT; fdata.filled_len = vb2_get_plane_payload(vb, 0); -- cgit v1.2.3-59-g8ed1b From 60b6527952edf59f9c86c04fd8f91019ce77a0a6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 09:14:32 -0200 Subject: media: v4l2-pci-skeleton.c: fix outdated irq code Fix the outdated irq example code. It is under an #ifdef TODO so it is never actually compiled and this code was never compiled. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- samples/v4l/v4l2-pci-skeleton.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/v4l/v4l2-pci-skeleton.c b/samples/v4l/v4l2-pci-skeleton.c index 27ec30952cfa..758ced8c3d06 100644 --- a/samples/v4l/v4l2-pci-skeleton.c +++ b/samples/v4l/v4l2-pci-skeleton.c @@ -139,16 +139,16 @@ static irqreturn_t skeleton_irq(int irq, void *dev_id) spin_lock(&skel->qlock); list_del(&new_buf->list); spin_unlock(&skel->qlock); - v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp); - new_buf->vb.v4l2_buf.sequence = skel->sequence++; - new_buf->vb.v4l2_buf.field = skel->field; + new_buf->vb.vb2_buf.timestamp = ktime_get_ns(); + new_buf->vb.sequence = skel->sequence++; + new_buf->vb.field = skel->field; if (skel->format.field == V4L2_FIELD_ALTERNATE) { if (skel->field == V4L2_FIELD_BOTTOM) skel->field = V4L2_FIELD_TOP; else if (skel->field == V4L2_FIELD_TOP) skel->field = V4L2_FIELD_BOTTOM; } - vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE); + vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } #endif return IRQ_HANDLED; -- cgit v1.2.3-59-g8ed1b From b4ba92d56b4f8ea692a5aaa4b0054824aa6fe47c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Jan 2019 09:27:25 -0200 Subject: media: dev-effect.rst: remove unused Effect Interface chapter We never had an effect interface, and if you want to do such things you use the mem2mem interface instead. Just drop this from the spec. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/dev-effect.rst | 28 ---------------------------- Documentation/media/uapi/v4l/devices.rst | 1 - 2 files changed, 29 deletions(-) delete mode 100644 Documentation/media/uapi/v4l/dev-effect.rst diff --git a/Documentation/media/uapi/v4l/dev-effect.rst b/Documentation/media/uapi/v4l/dev-effect.rst deleted file mode 100644 index b165e2c20910..000000000000 --- a/Documentation/media/uapi/v4l/dev-effect.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. Permission is granted to copy, distribute and/or modify this -.. document under the terms of the GNU Free Documentation License, -.. Version 1.1 or any later version published by the Free Software -.. Foundation, with no Invariant Sections, no Front-Cover Texts -.. and no Back-Cover Texts. A copy of the license is included at -.. Documentation/media/uapi/fdl-appendix.rst. -.. -.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections - -.. _effect: - -************************ -Effect Devices Interface -************************ - -.. note:: - This interface has been be suspended from the V4L2 API. - The implementation for such effects should be done - via mem2mem devices. - -A V4L2 video effect device can do image effects, filtering, or combine -two or more images or image streams. For example video transitions or -wipes. Applications send data to be processed and receive the result -data either with :ref:`read() ` and -:ref:`write() ` functions, or through the streaming I/O -mechanism. - -[to do] diff --git a/Documentation/media/uapi/v4l/devices.rst b/Documentation/media/uapi/v4l/devices.rst index 5dbe9d13b6e6..c959c0443c2f 100644 --- a/Documentation/media/uapi/v4l/devices.rst +++ b/Documentation/media/uapi/v4l/devices.rst @@ -22,7 +22,6 @@ Interfaces dev-output dev-osd dev-codec - dev-effect dev-raw-vbi dev-sliced-vbi dev-teletext -- cgit v1.2.3-59-g8ed1b From 0b7a4b41ab0e4209c0744442231976448ea3b2d3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Jan 2019 09:27:26 -0200 Subject: media: dev-teletext.rst: remove obsolete teletext interface The teletext interface has been dead for years now, just remove it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/dev-teletext.rst | 41 --------------------------- Documentation/media/uapi/v4l/devices.rst | 1 - 2 files changed, 42 deletions(-) delete mode 100644 Documentation/media/uapi/v4l/dev-teletext.rst diff --git a/Documentation/media/uapi/v4l/dev-teletext.rst b/Documentation/media/uapi/v4l/dev-teletext.rst deleted file mode 100644 index 35e8c4b35458..000000000000 --- a/Documentation/media/uapi/v4l/dev-teletext.rst +++ /dev/null @@ -1,41 +0,0 @@ -.. Permission is granted to copy, distribute and/or modify this -.. document under the terms of the GNU Free Documentation License, -.. Version 1.1 or any later version published by the Free Software -.. Foundation, with no Invariant Sections, no Front-Cover Texts -.. and no Back-Cover Texts. A copy of the license is included at -.. Documentation/media/uapi/fdl-appendix.rst. -.. -.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections - -.. _ttx: - -****************** -Teletext Interface -****************** - -This interface was aimed at devices receiving and demodulating Teletext -data [:ref:`ets300706`, :ref:`itu653`], evaluating the Teletext -packages and storing formatted pages in cache memory. Such devices are -usually implemented as microcontrollers with serial interface -(I\ :sup:`2`\ C) and could be found on old TV cards, dedicated Teletext -decoding cards and home-brew devices connected to the PC parallel port. - -The Teletext API was designed by Martin Buck. It was defined in the -kernel header file ``linux/videotext.h``, the specification is available -from -`ftp://ftp.gwdg.de/pub/linux/misc/videotext/ `__. -(Videotext is the name of the German public television Teletext -service.) - -Eventually the Teletext API was integrated into the V4L API with -character device file names ``/dev/vtx0`` to ``/dev/vtx31``, device -major number 81, minor numbers 192 to 223. - -However, teletext decoders were quickly replaced by more generic VBI -demodulators and those dedicated teletext decoders no longer exist. For -many years the vtx devices were still around, even though nobody used -them. So the decision was made to finally remove support for the -Teletext API in kernel 2.6.37. - -Modern devices all use the :ref:`raw ` or -:ref:`sliced` VBI API. diff --git a/Documentation/media/uapi/v4l/devices.rst b/Documentation/media/uapi/v4l/devices.rst index c959c0443c2f..d6fcf3db5909 100644 --- a/Documentation/media/uapi/v4l/devices.rst +++ b/Documentation/media/uapi/v4l/devices.rst @@ -24,7 +24,6 @@ Interfaces dev-codec dev-raw-vbi dev-sliced-vbi - dev-teletext dev-radio dev-rds dev-sdr -- cgit v1.2.3-59-g8ed1b From 49179ff480ee51c096253c8e4092f7cdeb81ca91 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 23 Jan 2019 06:07:20 -0200 Subject: media: Documentation/media: rename "Codec Interface" The "Codec Interface" chapter is poorly named since codecs are just one use-case of the Memory-to-Memory Interface. Rename it and clean up the text a bit. Signed-off-by: Hans Verkuil Reviewed-by: Tomasz Figa Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/mediactl/request-api.rst | 4 +- Documentation/media/uapi/v4l/dev-codec.rst | 43 ---------------------- Documentation/media/uapi/v4l/dev-mem2mem.rst | 42 +++++++++++++++++++++ Documentation/media/uapi/v4l/devices.rst | 2 +- Documentation/media/uapi/v4l/pixfmt-compressed.rst | 2 +- Documentation/media/uapi/v4l/vidioc-qbuf.rst | 2 +- 6 files changed, 47 insertions(+), 48 deletions(-) delete mode 100644 Documentation/media/uapi/v4l/dev-codec.rst create mode 100644 Documentation/media/uapi/v4l/dev-mem2mem.rst diff --git a/Documentation/media/uapi/mediactl/request-api.rst b/Documentation/media/uapi/mediactl/request-api.rst index 4b25ad03f45a..1ad631e549fe 100644 --- a/Documentation/media/uapi/mediactl/request-api.rst +++ b/Documentation/media/uapi/mediactl/request-api.rst @@ -91,7 +91,7 @@ A request must contain at least one buffer, otherwise ``ENOENT`` is returned. A queued request cannot be modified anymore. .. caution:: - For :ref:`memory-to-memory devices ` you can use requests only for + For :ref:`memory-to-memory devices ` you can use requests only for output buffers, not for capture buffers. Attempting to add a capture buffer to a request will result in an ``EACCES`` error. @@ -152,7 +152,7 @@ if it had just been allocated. Example for a Codec Device -------------------------- -For use-cases such as :ref:`codecs `, the request API can be used +For use-cases such as :ref:`codecs `, the request API can be used to associate specific controls to be applied by the driver for the OUTPUT buffer, allowing user-space to queue many such buffers in advance. It can also take advantage of requests' diff --git a/Documentation/media/uapi/v4l/dev-codec.rst b/Documentation/media/uapi/v4l/dev-codec.rst deleted file mode 100644 index b5e017c17834..000000000000 --- a/Documentation/media/uapi/v4l/dev-codec.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. Permission is granted to copy, distribute and/or modify this -.. document under the terms of the GNU Free Documentation License, -.. Version 1.1 or any later version published by the Free Software -.. Foundation, with no Invariant Sections, no Front-Cover Texts -.. and no Back-Cover Texts. A copy of the license is included at -.. Documentation/media/uapi/fdl-appendix.rst. -.. -.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections - -.. _codec: - -*************** -Codec Interface -*************** - -A V4L2 codec can compress, decompress, transform, or otherwise convert -video data from one format into another format, in memory. Typically -such devices are memory-to-memory devices (i.e. devices with the -``V4L2_CAP_VIDEO_M2M`` or ``V4L2_CAP_VIDEO_M2M_MPLANE`` capability set). - -A memory-to-memory video node acts just like a normal video node, but it -supports both output (sending frames from memory to the codec hardware) -and capture (receiving the processed frames from the codec hardware into -memory) stream I/O. An application will have to setup the stream I/O for -both sides and finally call :ref:`VIDIOC_STREAMON ` -for both capture and output to start the codec. - -Video compression codecs use the MPEG controls to setup their codec -parameters - -.. note:: - - The MPEG controls actually support many more codecs than - just MPEG. See :ref:`mpeg-controls`. - -Memory-to-memory devices function as a shared resource: you can -open the video node multiple times, each application setting up their -own codec properties that are local to the file handle, and each can use -it independently from the others. The driver will arbitrate access to -the codec and reprogram it whenever another file handler gets access. -This is different from the usual video node behavior where the video -properties are global to the device (i.e. changing something through one -file handle is visible through another file handle). diff --git a/Documentation/media/uapi/v4l/dev-mem2mem.rst b/Documentation/media/uapi/v4l/dev-mem2mem.rst new file mode 100644 index 000000000000..67a980818dc8 --- /dev/null +++ b/Documentation/media/uapi/v4l/dev-mem2mem.rst @@ -0,0 +1,42 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _mem2mem: + +******************************** +Video Memory-To-Memory Interface +******************************** + +A V4L2 memory-to-memory device can compress, decompress, transform, or +otherwise convert video data from one format into another format, in memory. +Such memory-to-memory devices set the ``V4L2_CAP_VIDEO_M2M`` or +``V4L2_CAP_VIDEO_M2M_MPLANE`` capability. Examples of memory-to-memory +devices are codecs, scalers, deinterlacers or format converters (i.e. +converting from YUV to RGB). + +A memory-to-memory video node acts just like a normal video node, but it +supports both output (sending frames from memory to the hardware) +and capture (receiving the processed frames from the hardware into +memory) stream I/O. An application will have to setup the stream I/O for +both sides and finally call :ref:`VIDIOC_STREAMON ` +for both capture and output to start the hardware. + +Memory-to-memory devices function as a shared resource: you can +open the video node multiple times, each application setting up their +own properties that are local to the file handle, and each can use +it independently from the others. The driver will arbitrate access to +the hardware and reprogram it whenever another file handler gets access. +This is different from the usual video node behavior where the video +properties are global to the device (i.e. changing something through one +file handle is visible through another file handle). + +One of the most common memory-to-memory device is the codec. Codecs +are more complicated than most and require additional setup for +their codec parameters. This is done through codec controls. +See :ref:`mpeg-controls`. diff --git a/Documentation/media/uapi/v4l/devices.rst b/Documentation/media/uapi/v4l/devices.rst index d6fcf3db5909..07f8d047662b 100644 --- a/Documentation/media/uapi/v4l/devices.rst +++ b/Documentation/media/uapi/v4l/devices.rst @@ -21,7 +21,7 @@ Interfaces dev-overlay dev-output dev-osd - dev-codec + dev-mem2mem dev-raw-vbi dev-sliced-vbi dev-radio diff --git a/Documentation/media/uapi/v4l/pixfmt-compressed.rst b/Documentation/media/uapi/v4l/pixfmt-compressed.rst index e4c5e456df59..2675bef3eefe 100644 --- a/Documentation/media/uapi/v4l/pixfmt-compressed.rst +++ b/Documentation/media/uapi/v4l/pixfmt-compressed.rst @@ -73,7 +73,7 @@ Compressed Formats - 'MG2S' - MPEG-2 parsed slice data, as extracted from the MPEG-2 bitstream. This format is adapted for stateless video decoders that implement a - MPEG-2 pipeline (using the :ref:`codec` and :ref:`media-request-api`). + MPEG-2 pipeline (using the :ref:`mem2mem` and :ref:`media-request-api`). Metadata associated with the frame to decode is required to be passed through the ``V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS`` control and quantization matrices can optionally be specified through the diff --git a/Documentation/media/uapi/v4l/vidioc-qbuf.rst b/Documentation/media/uapi/v4l/vidioc-qbuf.rst index 3259168a7358..c138d149faea 100644 --- a/Documentation/media/uapi/v4l/vidioc-qbuf.rst +++ b/Documentation/media/uapi/v4l/vidioc-qbuf.rst @@ -123,7 +123,7 @@ then ``EINVAL`` will be returned. :ref:`VIDIOC_STREAMOFF ` or calling :ref:`VIDIOC_REQBUFS` the check for this will be reset. - For :ref:`memory-to-memory devices ` you can specify the + For :ref:`memory-to-memory devices ` you can specify the ``request_fd`` only for output buffers, not for capture buffers. Attempting to specify this for a capture buffer will result in an ``EACCES`` error. -- cgit v1.2.3-59-g8ed1b From 8c1d02f140182fcd7cbecc77db68c2b5181c266f Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Sun, 20 Jan 2019 11:29:02 -0200 Subject: media: vicodec: Add num_planes field to v4l2_fwht_pixfmt_info Add the field 'num_planes' to 'v4l2_fwht_pixfmt_info' struct. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-v4l2-fwht.c | 48 ++++++++++++------------ drivers/media/platform/vicodec/codec-v4l2-fwht.h | 1 + drivers/media/platform/vicodec/vicodec-core.c | 2 +- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index 8cb0212df67f..5e9040f6c902 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -11,30 +11,30 @@ #include "codec-v4l2-fwht.h" static const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = { - { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3}, - { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3}, - { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3}, - { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3}, - { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3}, - { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3}, - { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3}, - { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3}, - { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3}, - { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3}, - { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3}, - { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3}, - { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3}, - { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3}, - { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3}, - { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3}, - { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 3}, - { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 3}, - { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 3}, - { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 3}, - { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 3}, - { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4}, - { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4}, - { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1}, + { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3}, + { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3}, + { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3}, + { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2}, + { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2}, + { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2}, + { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2}, + { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2}, + { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2}, + { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1}, + { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1}, + { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1}, + { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1}, + { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, + { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, + { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, + { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, + { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, + { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, + { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, + { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, + { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1}, + { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1}, + { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1}, }; const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat) diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h index ed53e28d4f9c..685b665590c1 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h @@ -19,6 +19,7 @@ struct v4l2_fwht_pixfmt_info { unsigned int width_div; unsigned int height_div; unsigned int components_num; + unsigned int planes_num; }; struct v4l2_fwht_state { diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 2378582d9790..6a7643bceb92 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -61,7 +61,7 @@ struct pixfmt_info { }; static const struct v4l2_fwht_pixfmt_info pixfmt_fwht = { - V4L2_PIX_FMT_FWHT, 0, 3, 1, 1, 1, 1, 1, 0 + V4L2_PIX_FMT_FWHT, 0, 3, 1, 1, 1, 1, 1, 0, 1 }; static void vicodec_dev_release(struct device *dev) -- cgit v1.2.3-59-g8ed1b From 9e812549883730f559cdd95d0c05419f5bfa3713 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 21 Jan 2019 09:46:14 -0200 Subject: media: vicodec: add support for CROP and COMPOSE selection Add support for the selection api for the crop and compose targets. The driver rounds up the coded width and height such that all planes dimensions are multiple of 8. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.c | 83 ++++--- drivers/media/platform/vicodec/codec-fwht.h | 17 +- drivers/media/platform/vicodec/codec-v4l2-fwht.c | 293 ++++++++++++++++------- drivers/media/platform/vicodec/codec-v4l2-fwht.h | 7 +- drivers/media/platform/vicodec/vicodec-core.c | 166 ++++++++++--- 5 files changed, 404 insertions(+), 162 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.c b/drivers/media/platform/vicodec/codec-fwht.c index a6fd0477633b..e5e0a80c2f73 100644 --- a/drivers/media/platform/vicodec/codec-fwht.c +++ b/drivers/media/platform/vicodec/codec-fwht.c @@ -10,6 +10,7 @@ */ #include +#include #include "codec-fwht.h" /* @@ -237,8 +238,6 @@ static void fwht(const u8 *block, s16 *output_block, unsigned int stride, unsigned int i; /* stage 1 */ - stride *= input_step; - for (i = 0; i < 8; i++, tmp += stride, out += 8) { switch (input_step) { case 1: @@ -562,7 +561,7 @@ static void fill_encoder_block(const u8 *input, s16 *dst, for (i = 0; i < 8; i++) { for (j = 0; j < 8; j++, input += input_step) *dst++ = *input; - input += (stride - 8) * input_step; + input += stride - 8 * input_step; } } @@ -660,7 +659,7 @@ static void add_deltas(s16 *deltas, const u8 *ref, int stride) static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max, struct fwht_cframe *cf, u32 height, u32 width, - unsigned int input_step, + u32 stride, unsigned int input_step, bool is_intra, bool next_is_intra) { u8 *input_start = input; @@ -671,7 +670,11 @@ static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max, unsigned int last_size = 0; unsigned int i, j; + width = round_up(width, 8); + height = round_up(height, 8); + for (j = 0; j < height / 8; j++) { + input = input_start + j * 8 * stride; for (i = 0; i < width / 8; i++) { /* intra code, first frame is always intra coded. */ int blocktype = IBLOCK; @@ -679,9 +682,9 @@ static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max, if (!is_intra) blocktype = decide_blocktype(input, refp, - deltablock, width, input_step); + deltablock, stride, input_step); if (blocktype == IBLOCK) { - fwht(input, cf->coeffs, width, input_step, 1); + fwht(input, cf->coeffs, stride, input_step, 1); quantize_intra(cf->coeffs, cf->de_coeffs, cf->i_frame_qp); } else { @@ -722,12 +725,12 @@ static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max, } last_size = size; } - input += width * 7 * input_step; } exit_loop: if (encoding & FWHT_FRAME_UNENCODED) { u8 *out = (u8 *)rlco_start; + u8 *p; input = input_start; /* @@ -736,8 +739,11 @@ exit_loop: * by 0xfe. Since YUV is limited range such values * shouldn't appear anyway. */ - for (i = 0; i < height * width; i++, input += input_step) - *out++ = (*input == 0xff) ? 0xfe : *input; + for (j = 0; j < height; j++) { + for (i = 0, p = input; i < width; i++, p += input_step) + *out++ = (*p == 0xff) ? 0xfe : *p; + input += stride; + } *rlco = (__be16 *)out; encoding &= ~FWHT_FRAME_PCODED; } @@ -747,30 +753,32 @@ exit_loop: u32 fwht_encode_frame(struct fwht_raw_frame *frm, struct fwht_raw_frame *ref_frm, struct fwht_cframe *cf, - bool is_intra, bool next_is_intra) + bool is_intra, bool next_is_intra, + unsigned int width, unsigned int height, + unsigned int stride, unsigned int chroma_stride) { - unsigned int size = frm->height * frm->width; + unsigned int size = height * width; __be16 *rlco = cf->rlc_data; __be16 *rlco_max; u32 encoding; rlco_max = rlco + size / 2 - 256; encoding = encode_plane(frm->luma, ref_frm->luma, &rlco, rlco_max, cf, - frm->height, frm->width, + height, width, stride, frm->luma_alpha_step, is_intra, next_is_intra); if (encoding & FWHT_FRAME_UNENCODED) encoding |= FWHT_LUMA_UNENCODED; encoding &= ~FWHT_FRAME_UNENCODED; if (frm->components_num >= 3) { - u32 chroma_h = frm->height / frm->height_div; - u32 chroma_w = frm->width / frm->width_div; + u32 chroma_h = height / frm->height_div; + u32 chroma_w = width / frm->width_div; unsigned int chroma_size = chroma_h * chroma_w; rlco_max = rlco + chroma_size / 2 - 256; encoding |= encode_plane(frm->cb, ref_frm->cb, &rlco, rlco_max, cf, chroma_h, chroma_w, - frm->chroma_step, + chroma_stride, frm->chroma_step, is_intra, next_is_intra); if (encoding & FWHT_FRAME_UNENCODED) encoding |= FWHT_CB_UNENCODED; @@ -778,7 +786,7 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, rlco_max = rlco + chroma_size / 2 - 256; encoding |= encode_plane(frm->cr, ref_frm->cr, &rlco, rlco_max, cf, chroma_h, chroma_w, - frm->chroma_step, + chroma_stride, frm->chroma_step, is_intra, next_is_intra); if (encoding & FWHT_FRAME_UNENCODED) encoding |= FWHT_CR_UNENCODED; @@ -788,8 +796,8 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, if (frm->components_num == 4) { rlco_max = rlco + size / 2 - 256; encoding |= encode_plane(frm->alpha, ref_frm->alpha, &rlco, - rlco_max, cf, frm->height, frm->width, - frm->luma_alpha_step, + rlco_max, cf, height, width, + stride, frm->luma_alpha_step, is_intra, next_is_intra); if (encoding & FWHT_FRAME_UNENCODED) encoding |= FWHT_ALPHA_UNENCODED; @@ -801,13 +809,17 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, } static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, - u32 height, u32 width, bool uncompressed) + u32 height, u32 width, u32 coded_width, + bool uncompressed) { unsigned int copies = 0; s16 copy[8 * 8]; s16 stat; unsigned int i, j; + width = round_up(width, 8); + height = round_up(height, 8); + if (uncompressed) { memcpy(ref, *rlco, width * height); *rlco += width * height / 2; @@ -822,13 +834,15 @@ static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, */ for (j = 0; j < height / 8; j++) { for (i = 0; i < width / 8; i++) { - u8 *refp = ref + j * 8 * width + i * 8; + u8 *refp = ref + j * 8 * coded_width + i * 8; if (copies) { memcpy(cf->de_fwht, copy, sizeof(copy)); if (stat & PFRAME_BIT) - add_deltas(cf->de_fwht, refp, width); - fill_decoder_block(refp, cf->de_fwht, width); + add_deltas(cf->de_fwht, refp, + coded_width); + fill_decoder_block(refp, cf->de_fwht, + coded_width); copies--; continue; } @@ -847,35 +861,40 @@ static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, if (copies) memcpy(copy, cf->de_fwht, sizeof(copy)); if (stat & PFRAME_BIT) - add_deltas(cf->de_fwht, refp, width); - fill_decoder_block(refp, cf->de_fwht, width); + add_deltas(cf->de_fwht, refp, coded_width); + fill_decoder_block(refp, cf->de_fwht, coded_width); } } } void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, - u32 hdr_flags, unsigned int components_num) + u32 hdr_flags, unsigned int components_num, + unsigned int width, unsigned int height, + unsigned int coded_width) { const __be16 *rlco = cf->rlc_data; - decode_plane(cf, &rlco, ref->luma, cf->height, cf->width, + decode_plane(cf, &rlco, ref->luma, height, width, coded_width, hdr_flags & FWHT_FL_LUMA_IS_UNCOMPRESSED); if (components_num >= 3) { - u32 h = cf->height; - u32 w = cf->width; + u32 h = height; + u32 w = width; + u32 c = coded_width; if (!(hdr_flags & FWHT_FL_CHROMA_FULL_HEIGHT)) h /= 2; - if (!(hdr_flags & FWHT_FL_CHROMA_FULL_WIDTH)) + if (!(hdr_flags & FWHT_FL_CHROMA_FULL_WIDTH)) { w /= 2; - decode_plane(cf, &rlco, ref->cb, h, w, + c /= 2; + } + decode_plane(cf, &rlco, ref->cb, h, w, c, hdr_flags & FWHT_FL_CB_IS_UNCOMPRESSED); - decode_plane(cf, &rlco, ref->cr, h, w, + decode_plane(cf, &rlco, ref->cr, h, w, c, hdr_flags & FWHT_FL_CR_IS_UNCOMPRESSED); } if (components_num == 4) - decode_plane(cf, &rlco, ref->alpha, cf->height, cf->width, + decode_plane(cf, &rlco, ref->alpha, height, width, coded_width, hdr_flags & FWHT_FL_ALPHA_IS_UNCOMPRESSED); } diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h index 90ff8962fca7..6d230f5e9d60 100644 --- a/drivers/media/platform/vicodec/codec-fwht.h +++ b/drivers/media/platform/vicodec/codec-fwht.h @@ -81,6 +81,13 @@ #define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(17, 16) #define FWHT_FL_COMPONENTS_NUM_OFFSET 16 +/* + * A macro to calculate the needed padding in order to make sure + * both luma and chroma components resolutions are rounded up to + * a multiple of 8 + */ +#define vic_round_dim(dim, div) (round_up((dim) / (div), 8) * (div)) + struct fwht_cframe_hdr { u32 magic1; u32 magic2; @@ -95,7 +102,6 @@ struct fwht_cframe_hdr { }; struct fwht_cframe { - unsigned int width, height; u16 i_frame_qp; u16 p_frame_qp; __be16 *rlc_data; @@ -106,7 +112,6 @@ struct fwht_cframe { }; struct fwht_raw_frame { - unsigned int width, height; unsigned int width_div; unsigned int height_div; unsigned int luma_alpha_step; @@ -125,8 +130,12 @@ struct fwht_raw_frame { u32 fwht_encode_frame(struct fwht_raw_frame *frm, struct fwht_raw_frame *ref_frm, struct fwht_cframe *cf, - bool is_intra, bool next_is_intra); + bool is_intra, bool next_is_intra, + unsigned int width, unsigned int height, + unsigned int stride, unsigned int chroma_stride); void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, - u32 hdr_flags, unsigned int components_num); + u32 hdr_flags, unsigned int components_num, + unsigned int width, unsigned int height, + unsigned int coded_width); #endif diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index 5e9040f6c902..e9c522d209ab 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -56,7 +56,8 @@ const struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx) int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) { - unsigned int size = state->width * state->height; + unsigned int size = state->stride * state->coded_height; + unsigned int chroma_stride = state->stride; const struct v4l2_fwht_pixfmt_info *info = state->info; struct fwht_cframe_hdr *p_hdr; struct fwht_cframe cf; @@ -66,8 +67,7 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) if (!info) return -EINVAL; - rf.width = state->width; - rf.height = state->height; + rf.luma = p_in; rf.width_div = info->width_div; rf.height_div = info->height_div; @@ -84,14 +84,17 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) case V4L2_PIX_FMT_YUV420: rf.cb = rf.luma + size; rf.cr = rf.cb + size / 4; + chroma_stride /= 2; break; case V4L2_PIX_FMT_YVU420: rf.cr = rf.luma + size; rf.cb = rf.cr + size / 4; + chroma_stride /= 2; break; case V4L2_PIX_FMT_YUV422P: rf.cb = rf.luma + size; rf.cr = rf.cb + size / 2; + chroma_stride /= 2; break; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: @@ -163,15 +166,16 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) return -EINVAL; } - cf.width = state->width; - cf.height = state->height; cf.i_frame_qp = state->i_frame_qp; cf.p_frame_qp = state->p_frame_qp; cf.rlc_data = (__be16 *)(p_out + sizeof(*p_hdr)); encoding = fwht_encode_frame(&rf, &state->ref_frame, &cf, !state->gop_cnt, - state->gop_cnt == state->gop_size - 1); + state->gop_cnt == state->gop_size - 1, + state->visible_width, + state->visible_height, + state->stride, chroma_stride); if (!(encoding & FWHT_FRAME_PCODED)) state->gop_cnt = 0; if (++state->gop_cnt >= state->gop_size) @@ -181,8 +185,8 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) p_hdr->magic1 = FWHT_MAGIC1; p_hdr->magic2 = FWHT_MAGIC2; p_hdr->version = htonl(FWHT_VERSION); - p_hdr->width = htonl(cf.width); - p_hdr->height = htonl(cf.height); + p_hdr->width = htonl(state->visible_width); + p_hdr->height = htonl(state->visible_height); flags |= (info->components_num - 1) << FWHT_FL_COMPONENTS_NUM_OFFSET; if (encoding & FWHT_LUMA_UNENCODED) flags |= FWHT_FL_LUMA_IS_UNCOMPRESSED; @@ -202,29 +206,26 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) p_hdr->ycbcr_enc = htonl(state->ycbcr_enc); p_hdr->quantization = htonl(state->quantization); p_hdr->size = htonl(cf.size); - state->ref_frame.width = cf.width; - state->ref_frame.height = cf.height; return cf.size + sizeof(*p_hdr); } int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) { - unsigned int size = state->width * state->height; - unsigned int chroma_size = size; - unsigned int i; + unsigned int i, j, k; u32 flags; struct fwht_cframe_hdr *p_hdr; struct fwht_cframe cf; - u8 *p; + u8 *p, *ref_p; unsigned int components_num = 3; unsigned int version; + const struct v4l2_fwht_pixfmt_info *info; + unsigned int hdr_width_div, hdr_height_div; if (!state->info) return -EINVAL; + info = state->info; p_hdr = (struct fwht_cframe_hdr *)p_in; - cf.width = ntohl(p_hdr->width); - cf.height = ntohl(p_hdr->height); version = ntohl(p_hdr->version); if (!version || version > FWHT_VERSION) { @@ -234,12 +235,12 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) } if (p_hdr->magic1 != FWHT_MAGIC1 || - p_hdr->magic2 != FWHT_MAGIC2 || - (cf.width & 7) || (cf.height & 7)) + p_hdr->magic2 != FWHT_MAGIC2) return -EINVAL; /* TODO: support resolution changes */ - if (cf.width != state->width || cf.height != state->height) + if (ntohl(p_hdr->width) != state->visible_width || + ntohl(p_hdr->height) != state->visible_height) return -EINVAL; flags = ntohl(p_hdr->flags); @@ -255,12 +256,15 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) state->quantization = ntohl(p_hdr->quantization); cf.rlc_data = (__be16 *)(p_in + sizeof(*p_hdr)); - if (!(flags & FWHT_FL_CHROMA_FULL_WIDTH)) - chroma_size /= 2; - if (!(flags & FWHT_FL_CHROMA_FULL_HEIGHT)) - chroma_size /= 2; + hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + if (hdr_width_div != info->width_div || + hdr_height_div != info->height_div) + return -EINVAL; - fwht_decode_frame(&cf, &state->ref_frame, flags, components_num); + fwht_decode_frame(&cf, &state->ref_frame, flags, components_num, + state->visible_width, state->visible_height, + state->coded_width); /* * TODO - handle the case where the compressed stream encodes a @@ -268,123 +272,226 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) */ switch (state->info->id) { case V4L2_PIX_FMT_GREY: - memcpy(p_out, state->ref_frame.luma, size); + ref_p = state->ref_frame.luma; + for (i = 0; i < state->coded_height; i++) { + memcpy(p_out, ref_p, state->visible_width); + p_out += state->stride; + ref_p += state->coded_width; + } break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YUV422P: - memcpy(p_out, state->ref_frame.luma, size); - p_out += size; - memcpy(p_out, state->ref_frame.cb, chroma_size); - p_out += chroma_size; - memcpy(p_out, state->ref_frame.cr, chroma_size); + ref_p = state->ref_frame.luma; + for (i = 0; i < state->coded_height; i++) { + memcpy(p_out, ref_p, state->visible_width); + p_out += state->stride; + ref_p += state->coded_width; + } + + ref_p = state->ref_frame.cb; + for (i = 0; i < state->coded_height / 2; i++) { + memcpy(p_out, ref_p, state->visible_width / 2); + p_out += state->stride / 2; + ref_p += state->coded_width / 2; + } + ref_p = state->ref_frame.cr; + for (i = 0; i < state->coded_height / 2; i++) { + memcpy(p_out, ref_p, state->visible_width / 2); + p_out += state->stride / 2; + ref_p += state->coded_width / 2; + } break; case V4L2_PIX_FMT_YVU420: - memcpy(p_out, state->ref_frame.luma, size); - p_out += size; - memcpy(p_out, state->ref_frame.cr, chroma_size); - p_out += chroma_size; - memcpy(p_out, state->ref_frame.cb, chroma_size); + ref_p = state->ref_frame.luma; + for (i = 0; i < state->coded_height; i++) { + memcpy(p_out, ref_p, state->visible_width); + p_out += state->stride; + ref_p += state->coded_width; + } + + ref_p = state->ref_frame.cr; + for (i = 0; i < state->coded_height / 2; i++) { + memcpy(p_out, ref_p, state->visible_width / 2); + p_out += state->stride / 2; + ref_p += state->coded_width / 2; + } + ref_p = state->ref_frame.cb; + for (i = 0; i < state->coded_height / 2; i++) { + memcpy(p_out, ref_p, state->visible_width / 2); + p_out += state->stride / 2; + ref_p += state->coded_width / 2; + } break; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV24: - memcpy(p_out, state->ref_frame.luma, size); - p_out += size; - for (i = 0, p = p_out; i < chroma_size; i++) { - *p++ = state->ref_frame.cb[i]; - *p++ = state->ref_frame.cr[i]; + ref_p = state->ref_frame.luma; + for (i = 0; i < state->coded_height; i++) { + memcpy(p_out, ref_p, state->visible_width); + p_out += state->stride; + ref_p += state->coded_width; + } + + k = 0; + for (i = 0; i < state->coded_height / 2; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.cb[k]; + *p++ = state->ref_frame.cr[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV42: - memcpy(p_out, state->ref_frame.luma, size); - p_out += size; - for (i = 0, p = p_out; i < chroma_size; i++) { - *p++ = state->ref_frame.cr[i]; - *p++ = state->ref_frame.cb[i]; + ref_p = state->ref_frame.luma; + for (i = 0; i < state->coded_height; i++) { + memcpy(p_out, ref_p, state->visible_width); + p_out += state->stride; + ref_p += state->coded_width; + } + + k = 0; + for (i = 0; i < state->coded_height / 2; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.cr[k]; + *p++ = state->ref_frame.cb[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_YUYV: - for (i = 0, p = p_out; i < size; i += 2) { - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cb[i / 2]; - *p++ = state->ref_frame.luma[i + 1]; - *p++ = state->ref_frame.cr[i / 2]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cb[k / 2]; + *p++ = state->ref_frame.luma[k + 1]; + *p++ = state->ref_frame.cr[k / 2]; + k += 2; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_YVYU: - for (i = 0, p = p_out; i < size; i += 2) { - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cr[i / 2]; - *p++ = state->ref_frame.luma[i + 1]; - *p++ = state->ref_frame.cb[i / 2]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cr[k / 2]; + *p++ = state->ref_frame.luma[k + 1]; + *p++ = state->ref_frame.cb[k / 2]; + k += 2; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_UYVY: - for (i = 0, p = p_out; i < size; i += 2) { - *p++ = state->ref_frame.cb[i / 2]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cr[i / 2]; - *p++ = state->ref_frame.luma[i + 1]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.cb[k / 2]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cr[k / 2]; + *p++ = state->ref_frame.luma[k + 1]; + k += 2; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_VYUY: - for (i = 0, p = p_out; i < size; i += 2) { - *p++ = state->ref_frame.cr[i / 2]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cb[i / 2]; - *p++ = state->ref_frame.luma[i + 1]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width / 2; j++) { + *p++ = state->ref_frame.cr[k / 2]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cb[k / 2]; + *p++ = state->ref_frame.luma[k + 1]; + k += 2; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_HSV24: - for (i = 0, p = p_out; i < size; i++) { - *p++ = state->ref_frame.cr[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cb[i]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = state->ref_frame.cr[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cb[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_BGR24: - for (i = 0, p = p_out; i < size; i++) { - *p++ = state->ref_frame.cb[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cr[i]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = state->ref_frame.cb[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cr[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_XRGB32: case V4L2_PIX_FMT_HSV32: - for (i = 0, p = p_out; i < size; i++) { - *p++ = 0; - *p++ = state->ref_frame.cr[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cb[i]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = 0; + *p++ = state->ref_frame.cr[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cb[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_BGR32: case V4L2_PIX_FMT_XBGR32: - for (i = 0, p = p_out; i < size; i++) { - *p++ = state->ref_frame.cb[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cr[i]; - *p++ = 0; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = state->ref_frame.cb[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cr[k]; + *p++ = 0; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_ARGB32: - for (i = 0, p = p_out; i < size; i++) { - *p++ = state->ref_frame.alpha[i]; - *p++ = state->ref_frame.cr[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cb[i]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = state->ref_frame.alpha[k]; + *p++ = state->ref_frame.cr[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cb[k]; + k++; + } + p_out += state->stride; } break; case V4L2_PIX_FMT_ABGR32: - for (i = 0, p = p_out; i < size; i++) { - *p++ = state->ref_frame.cb[i]; - *p++ = state->ref_frame.luma[i]; - *p++ = state->ref_frame.cr[i]; - *p++ = state->ref_frame.alpha[i]; + k = 0; + for (i = 0; i < state->coded_height; i++) { + for (j = 0, p = p_out; j < state->coded_width; j++) { + *p++ = state->ref_frame.cb[k]; + *p++ = state->ref_frame.luma[k]; + *p++ = state->ref_frame.cr[k]; + *p++ = state->ref_frame.alpha[k]; + k++; + } + p_out += state->stride; } break; default: diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h index 685b665590c1..203c45d98905 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h @@ -24,8 +24,11 @@ struct v4l2_fwht_pixfmt_info { struct v4l2_fwht_state { const struct v4l2_fwht_pixfmt_info *info; - unsigned int width; - unsigned int height; + unsigned int visible_width; + unsigned int visible_height; + unsigned int coded_width; + unsigned int coded_height; + unsigned int stride; unsigned int gop_size; unsigned int gop_cnt; u16 i_frame_qp; diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 6a7643bceb92..840e78c2cbe8 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -75,8 +75,10 @@ static struct platform_device vicodec_pdev = { /* Per-queue, driver-specific private data */ struct vicodec_q_data { - unsigned int width; - unsigned int height; + unsigned int coded_width; + unsigned int coded_height; + unsigned int visible_width; + unsigned int visible_height; unsigned int sizeimage; unsigned int sequence; const struct v4l2_fwht_pixfmt_info *info; @@ -454,11 +456,12 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) if (multiplanar) return -EINVAL; pix = &f->fmt.pix; - pix->width = q_data->width; - pix->height = q_data->height; + pix->width = q_data->coded_width; + pix->height = q_data->coded_height; pix->field = V4L2_FIELD_NONE; pix->pixelformat = info->id; - pix->bytesperline = q_data->width * info->bytesperline_mult; + pix->bytesperline = q_data->coded_width * + info->bytesperline_mult; pix->sizeimage = q_data->sizeimage; pix->colorspace = ctx->state.colorspace; pix->xfer_func = ctx->state.xfer_func; @@ -471,13 +474,13 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) if (!multiplanar) return -EINVAL; pix_mp = &f->fmt.pix_mp; - pix_mp->width = q_data->width; - pix_mp->height = q_data->height; + pix_mp->width = q_data->coded_width; + pix_mp->height = q_data->coded_height; pix_mp->field = V4L2_FIELD_NONE; pix_mp->pixelformat = info->id; pix_mp->num_planes = 1; pix_mp->plane_fmt[0].bytesperline = - q_data->width * info->bytesperline_mult; + q_data->coded_width * info->bytesperline_mult; pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage; pix_mp->colorspace = ctx->state.colorspace; pix_mp->xfer_func = ctx->state.xfer_func; @@ -518,8 +521,13 @@ static int vidioc_try_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) pix = &f->fmt.pix; if (pix->pixelformat != V4L2_PIX_FMT_FWHT) info = find_fmt(pix->pixelformat); - pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH) & ~7; - pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT) & ~7; + + pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); + pix->width = vic_round_dim(pix->width, info->width_div); + + pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); + pix->height = vic_round_dim(pix->height, info->height_div); + pix->field = V4L2_FIELD_NONE; pix->bytesperline = pix->width * info->bytesperline_mult; @@ -535,9 +543,14 @@ static int vidioc_try_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) if (pix_mp->pixelformat != V4L2_PIX_FMT_FWHT) info = find_fmt(pix_mp->pixelformat); pix_mp->num_planes = 1; - pix_mp->width = clamp(pix_mp->width, MIN_WIDTH, MAX_WIDTH) & ~7; - pix_mp->height = - clamp(pix_mp->height, MIN_HEIGHT, MAX_HEIGHT) & ~7; + + pix_mp->width = clamp(pix_mp->width, MIN_WIDTH, MAX_WIDTH); + pix_mp->width = vic_round_dim(pix_mp->width, info->width_div); + + pix_mp->height = clamp(pix_mp->height, MIN_HEIGHT, MAX_HEIGHT); + pix_mp->height = vic_round_dim(pix_mp->height, + info->height_div); + pix_mp->field = V4L2_FIELD_NONE; plane->bytesperline = pix_mp->width * info->bytesperline_mult; @@ -648,8 +661,8 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) fmt_changed = q_data->info->id != pix->pixelformat || - q_data->width != pix->width || - q_data->height != pix->height; + q_data->coded_width != pix->width || + q_data->coded_height != pix->height; if (vb2_is_busy(vq) && fmt_changed) return -EBUSY; @@ -658,8 +671,8 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) q_data->info = &pixfmt_fwht; else q_data->info = find_fmt(pix->pixelformat); - q_data->width = pix->width; - q_data->height = pix->height; + q_data->coded_width = pix->width; + q_data->coded_height = pix->height; q_data->sizeimage = pix->sizeimage; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -668,8 +681,8 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) fmt_changed = q_data->info->id != pix_mp->pixelformat || - q_data->width != pix_mp->width || - q_data->height != pix_mp->height; + q_data->coded_width != pix_mp->width || + q_data->coded_height != pix_mp->height; if (vb2_is_busy(vq) && fmt_changed) return -EBUSY; @@ -678,17 +691,24 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) q_data->info = &pixfmt_fwht; else q_data->info = find_fmt(pix_mp->pixelformat); - q_data->width = pix_mp->width; - q_data->height = pix_mp->height; + q_data->coded_width = pix_mp->width; + q_data->coded_height = pix_mp->height; q_data->sizeimage = pix_mp->plane_fmt[0].sizeimage; break; default: return -EINVAL; } + if (q_data->visible_width > q_data->coded_width) + q_data->visible_width = q_data->coded_width; + if (q_data->visible_height > q_data->coded_height) + q_data->visible_height = q_data->coded_height; + dprintk(ctx->dev, - "Setting format for type %d, wxh: %dx%d, fourcc: %08x\n", - f->type, q_data->width, q_data->height, q_data->info->id); + "Setting format for type %d, coded wxh: %dx%d, visible wxh: %dx%d, fourcc: %08x\n", + f->type, q_data->coded_width, q_data->coded_height, + q_data->visible_width, q_data->visible_height, + q_data->info->id); return 0; } @@ -743,6 +763,79 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, return ret; } +static int vidioc_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct vicodec_ctx *ctx = file2ctx(file); + struct vicodec_q_data *q_data; + enum v4l2_buf_type valid_cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + enum v4l2_buf_type valid_out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + if (multiplanar) { + valid_cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + valid_out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + } + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + /* + * encoder supports only cropping on the OUTPUT buffer + * decoder supports only composing on the CAPTURE buffer + */ + if ((ctx->is_enc && s->type == valid_out_type) || + (!ctx->is_enc && s->type == valid_cap_type)) { + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_CROP: + s->r.left = 0; + s->r.top = 0; + s->r.width = q_data->visible_width; + s->r.height = q_data->visible_height; + return 0; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.left = 0; + s->r.top = 0; + s->r.width = q_data->coded_width; + s->r.height = q_data->coded_height; + return 0; + } + } + return -EINVAL; +} + +static int vidioc_s_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct vicodec_ctx *ctx = file2ctx(file); + struct vicodec_q_data *q_data; + enum v4l2_buf_type out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + if (multiplanar) + out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + if (!ctx->is_enc || s->type != out_type || + s->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + s->r.left = 0; + s->r.top = 0; + q_data->visible_width = clamp(s->r.width, MIN_WIDTH, + q_data->coded_width); + s->r.width = q_data->visible_width; + q_data->visible_height = clamp(s->r.height, MIN_HEIGHT, + q_data->coded_height); + s->r.height = q_data->visible_height; + return 0; +} + static void vicodec_mark_last_buf(struct vicodec_ctx *ctx) { static const struct v4l2_event eos_event = { @@ -885,6 +978,9 @@ static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { .vidioc_streamon = v4l2_m2m_ioctl_streamon, .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_g_selection = vidioc_g_selection, + .vidioc_s_selection = vidioc_s_selection, + .vidioc_try_encoder_cmd = vicodec_try_encoder_cmd, .vidioc_encoder_cmd = vicodec_encoder_cmd, .vidioc_try_decoder_cmd = vicodec_try_decoder_cmd, @@ -978,8 +1074,8 @@ static int vicodec_start_streaming(struct vb2_queue *q, struct vicodec_ctx *ctx = vb2_get_drv_priv(q); struct vicodec_q_data *q_data = get_q_data(ctx, q->type); struct v4l2_fwht_state *state = &ctx->state; - unsigned int size = q_data->width * q_data->height; const struct v4l2_fwht_pixfmt_info *info = q_data->info; + unsigned int size = q_data->coded_width * q_data->coded_height; unsigned int chroma_div = info->width_div * info->height_div; unsigned int total_planes_size; @@ -998,17 +1094,23 @@ static int vicodec_start_streaming(struct vb2_queue *q, if (!V4L2_TYPE_IS_OUTPUT(q->type)) { if (!ctx->is_enc) { - state->width = q_data->width; - state->height = q_data->height; + state->visible_width = q_data->visible_width; + state->visible_height = q_data->visible_height; + state->coded_width = q_data->coded_width; + state->coded_height = q_data->coded_height; + state->stride = q_data->coded_width * + info->bytesperline_mult; } return 0; } if (ctx->is_enc) { - state->width = q_data->width; - state->height = q_data->height; + state->visible_width = q_data->visible_width; + state->visible_height = q_data->visible_height; + state->coded_width = q_data->coded_width; + state->coded_height = q_data->coded_height; + state->stride = q_data->coded_width * info->bytesperline_mult; } - state->ref_frame.width = state->ref_frame.height = 0; state->ref_frame.luma = kvmalloc(total_planes_size, GFP_KERNEL); ctx->comp_max_size = total_planes_size + sizeof(struct fwht_cframe_hdr); state->compressed_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); @@ -1194,8 +1296,10 @@ static int vicodec_open(struct file *file) ctx->q_data[V4L2_M2M_SRC].info = ctx->is_enc ? v4l2_fwht_get_pixfmt(0) : &pixfmt_fwht; - ctx->q_data[V4L2_M2M_SRC].width = 1280; - ctx->q_data[V4L2_M2M_SRC].height = 720; + ctx->q_data[V4L2_M2M_SRC].coded_width = 1280; + ctx->q_data[V4L2_M2M_SRC].coded_height = 720; + ctx->q_data[V4L2_M2M_SRC].visible_width = 1280; + ctx->q_data[V4L2_M2M_SRC].visible_height = 720; size = 1280 * 720 * ctx->q_data[V4L2_M2M_SRC].info->sizeimage_mult / ctx->q_data[V4L2_M2M_SRC].info->sizeimage_div; if (ctx->is_enc) -- cgit v1.2.3-59-g8ed1b From 7248d1ff0e2199128618a2eb427e798a46deaaf1 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 21 Jan 2019 09:46:15 -0200 Subject: media: vicodec: use 3 bits for the number of components Use 3 bits for the number of components mask in the fwht header flags Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h index 6d230f5e9d60..2984dc772515 100644 --- a/drivers/media/platform/vicodec/codec-fwht.h +++ b/drivers/media/platform/vicodec/codec-fwht.h @@ -78,7 +78,7 @@ #define FWHT_FL_ALPHA_IS_UNCOMPRESSED BIT(9) /* A 4-values flag - the number of components - 1 */ -#define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(17, 16) +#define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16) #define FWHT_FL_COMPONENTS_NUM_OFFSET 16 /* -- cgit v1.2.3-59-g8ed1b From 5fbd0729cfc6601a703ec79a301928bd00bfcb6f Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 21 Jan 2019 09:46:16 -0200 Subject: media: vicodec: Add pixel encoding flags to fwht header Add flags indicating the pixel encoding - yuv/rgb/hsv to fwht header and to the pixel info. Use it to enumerate the supported pixel formats. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.h | 6 ++ drivers/media/platform/vicodec/codec-v4l2-fwht.c | 77 ++++++++++++++++-------- drivers/media/platform/vicodec/codec-v4l2-fwht.h | 6 ++ drivers/media/platform/vicodec/vicodec-core.c | 19 ++++-- 4 files changed, 78 insertions(+), 30 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h index 2984dc772515..ad8cfc60a152 100644 --- a/drivers/media/platform/vicodec/codec-fwht.h +++ b/drivers/media/platform/vicodec/codec-fwht.h @@ -81,6 +81,12 @@ #define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16) #define FWHT_FL_COMPONENTS_NUM_OFFSET 16 +#define FWHT_FL_PIXENC_MSK GENMASK(20, 19) +#define FWHT_FL_PIXENC_OFFSET 19 +#define FWHT_FL_PIXENC_YUV (1 << FWHT_FL_PIXENC_OFFSET) +#define FWHT_FL_PIXENC_RGB (2 << FWHT_FL_PIXENC_OFFSET) +#define FWHT_FL_PIXENC_HSV (3 << FWHT_FL_PIXENC_OFFSET) + /* * A macro to calculate the needed padding in order to make sure * both luma and chroma components resolutions are rounded up to diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index e9c522d209ab..71ae19062b15 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -11,32 +11,53 @@ #include "codec-v4l2-fwht.h" static const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = { - { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3}, - { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3}, - { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3}, - { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2}, - { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2}, - { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2}, - { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2}, - { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2}, - { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2}, - { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1}, - { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1}, - { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1}, - { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1}, - { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, - { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, - { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1}, - { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, - { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, - { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, - { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, - { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 3, 1}, - { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1}, - { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1}, - { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1}, + { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV}, + { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV}, + { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1, FWHT_FL_PIXENC_RGB}, }; +const struct v4l2_fwht_pixfmt_info *v4l2_fwht_default_fmt(u32 width_div, + u32 height_div, + u32 components_num, + u32 pixenc, + unsigned int start_idx) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++) { + if (v4l2_fwht_pixfmts[i].width_div == width_div && + v4l2_fwht_pixfmts[i].height_div == height_div && + (!pixenc || v4l2_fwht_pixfmts[i].pixenc == pixenc) && + v4l2_fwht_pixfmts[i].components_num == components_num) { + if (start_idx == 0) + return v4l2_fwht_pixfmts + i; + start_idx--; + } + } + return NULL; +} + const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat) { unsigned int i; @@ -188,6 +209,7 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) p_hdr->width = htonl(state->visible_width); p_hdr->height = htonl(state->visible_height); flags |= (info->components_num - 1) << FWHT_FL_COMPONENTS_NUM_OFFSET; + flags |= info->pixenc; if (encoding & FWHT_LUMA_UNENCODED) flags |= FWHT_FL_LUMA_IS_UNCOMPRESSED; if (encoding & FWHT_CB_UNENCODED) @@ -246,10 +268,15 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) flags = ntohl(p_hdr->flags); if (version == FWHT_VERSION) { + if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc) + return -EINVAL; components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> - FWHT_FL_COMPONENTS_NUM_OFFSET); + FWHT_FL_COMPONENTS_NUM_OFFSET); } + if (components_num != info->components_num) + return -EINVAL; + state->colorspace = ntohl(p_hdr->colorspace); state->xfer_func = ntohl(p_hdr->xfer_func); state->ycbcr_enc = ntohl(p_hdr->ycbcr_enc); diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h index 203c45d98905..18ac25978829 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h @@ -20,6 +20,7 @@ struct v4l2_fwht_pixfmt_info { unsigned int height_div; unsigned int components_num; unsigned int planes_num; + unsigned int pixenc; }; struct v4l2_fwht_state { @@ -45,6 +46,11 @@ struct v4l2_fwht_state { const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat); const struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx); +const struct v4l2_fwht_pixfmt_info *v4l2_fwht_default_fmt(u32 width_div, + u32 height_div, + u32 components_num, + u32 pixenc, + unsigned int start_idx); int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out); int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out); diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 840e78c2cbe8..413b6bca91e5 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -395,9 +395,10 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static int enum_fmt(struct v4l2_fmtdesc *f, bool is_enc, bool is_out) +static int enum_fmt(struct v4l2_fmtdesc *f, struct vicodec_ctx *ctx, + bool is_out) { - bool is_uncomp = (is_enc && is_out) || (!is_enc && !is_out); + bool is_uncomp = (ctx->is_enc && is_out) || (!ctx->is_enc && !is_out); if (V4L2_TYPE_IS_MULTIPLANAR(f->type) && !multiplanar) return -EINVAL; @@ -406,8 +407,16 @@ static int enum_fmt(struct v4l2_fmtdesc *f, bool is_enc, bool is_out) if (is_uncomp) { const struct v4l2_fwht_pixfmt_info *info = - v4l2_fwht_get_pixfmt(f->index); + get_q_data(ctx, f->type)->info; + if (ctx->is_enc) + info = v4l2_fwht_get_pixfmt(f->index); + else + info = v4l2_fwht_default_fmt(info->width_div, + info->height_div, + info->components_num, + info->pixenc, + f->index); if (!info) return -EINVAL; f->pixelformat = info->id; @@ -424,7 +433,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, { struct vicodec_ctx *ctx = file2ctx(file); - return enum_fmt(f, ctx->is_enc, false); + return enum_fmt(f, ctx, false); } static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, @@ -432,7 +441,7 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, { struct vicodec_ctx *ctx = file2ctx(file); - return enum_fmt(f, ctx->is_enc, true); + return enum_fmt(f, ctx, true); } static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) -- cgit v1.2.3-59-g8ed1b From ddc1b085275144a80260b5062fa341a503b4495c Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 21 Jan 2019 09:46:17 -0200 Subject: media: vicodec: Separate fwht header from the frame data Keep the fwht header in separated field from the data. Refactor job_ready to use a new function 'get_next_header' Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-v4l2-fwht.c | 24 +++-- drivers/media/platform/vicodec/codec-v4l2-fwht.h | 1 + drivers/media/platform/vicodec/vicodec-core.c | 110 +++++++++++++---------- 3 files changed, 77 insertions(+), 58 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index 71ae19062b15..ee6903b8896c 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -235,7 +235,6 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) { unsigned int i, j, k; u32 flags; - struct fwht_cframe_hdr *p_hdr; struct fwht_cframe cf; u8 *p, *ref_p; unsigned int components_num = 3; @@ -247,25 +246,24 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) return -EINVAL; info = state->info; - p_hdr = (struct fwht_cframe_hdr *)p_in; - version = ntohl(p_hdr->version); + version = ntohl(state->header.version); if (!version || version > FWHT_VERSION) { pr_err("version %d is not supported, current version is %d\n", version, FWHT_VERSION); return -EINVAL; } - if (p_hdr->magic1 != FWHT_MAGIC1 || - p_hdr->magic2 != FWHT_MAGIC2) + if (state->header.magic1 != FWHT_MAGIC1 || + state->header.magic2 != FWHT_MAGIC2) return -EINVAL; /* TODO: support resolution changes */ - if (ntohl(p_hdr->width) != state->visible_width || - ntohl(p_hdr->height) != state->visible_height) + if (ntohl(state->header.width) != state->visible_width || + ntohl(state->header.height) != state->visible_height) return -EINVAL; - flags = ntohl(p_hdr->flags); + flags = ntohl(state->header.flags); if (version == FWHT_VERSION) { if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc) @@ -277,11 +275,11 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) if (components_num != info->components_num) return -EINVAL; - state->colorspace = ntohl(p_hdr->colorspace); - state->xfer_func = ntohl(p_hdr->xfer_func); - state->ycbcr_enc = ntohl(p_hdr->ycbcr_enc); - state->quantization = ntohl(p_hdr->quantization); - cf.rlc_data = (__be16 *)(p_in + sizeof(*p_hdr)); + state->colorspace = ntohl(state->header.colorspace); + state->xfer_func = ntohl(state->header.xfer_func); + state->ycbcr_enc = ntohl(state->header.ycbcr_enc); + state->quantization = ntohl(state->header.quantization); + cf.rlc_data = (__be16 *)p_in; hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h index 18ac25978829..aa6fa90a48be 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h @@ -41,6 +41,7 @@ struct v4l2_fwht_state { enum v4l2_quantization quantization; struct fwht_raw_frame ref_frame; + struct fwht_cframe_hdr header; u8 *compressed_frame; }; diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 413b6bca91e5..7c68959aae34 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -124,6 +124,7 @@ struct vicodec_ctx { u32 cur_buf_offset; u32 comp_max_size; u32 comp_size; + u32 header_size; u32 comp_magic_cnt; u32 comp_frame_size; bool comp_has_frame; @@ -201,6 +202,62 @@ static int device_process(struct vicodec_ctx *ctx, /* * mem2mem callbacks */ +enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx, u8 **pp, u32 sz) +{ + static const u8 magic[] = { + 0x4f, 0x4f, 0x4f, 0x4f, 0xff, 0xff, 0xff, 0xff + }; + u8 *p = *pp; + u32 state; + u8 *header = (u8 *)&ctx->state.header; + + state = VB2_BUF_STATE_DONE; + + if (!ctx->header_size) { + state = VB2_BUF_STATE_ERROR; + for (; p < *pp + sz; p++) { + u32 copy; + + p = memchr(p, magic[ctx->comp_magic_cnt], + *pp + sz - p); + if (!p) { + ctx->comp_magic_cnt = 0; + p = *pp + sz; + break; + } + copy = sizeof(magic) - ctx->comp_magic_cnt; + if (*pp + sz - p < copy) + copy = *pp + sz - p; + + memcpy(header + ctx->comp_magic_cnt, p, copy); + ctx->comp_magic_cnt += copy; + if (!memcmp(header, magic, ctx->comp_magic_cnt)) { + p += copy; + state = VB2_BUF_STATE_DONE; + break; + } + ctx->comp_magic_cnt = 0; + } + if (ctx->comp_magic_cnt < sizeof(magic)) { + *pp = p; + return state; + } + ctx->header_size = sizeof(magic); + } + + if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { + u32 copy = sizeof(struct fwht_cframe_hdr) - ctx->header_size; + + if (*pp + sz - p < copy) + copy = *pp + sz - p; + + memcpy(header + ctx->header_size, p, copy); + p += copy; + ctx->header_size += copy; + } + *pp = p; + return state; +} /* device_run() - prepares and starts the device */ static void device_run(void *priv) @@ -241,6 +298,7 @@ static void device_run(void *priv) } v4l2_m2m_buf_done(dst_buf, state); ctx->comp_size = 0; + ctx->header_size = 0; ctx->comp_magic_cnt = 0; ctx->comp_has_frame = false; spin_unlock(ctx->lock); @@ -291,54 +349,15 @@ restart: state = VB2_BUF_STATE_DONE; - if (!ctx->comp_size) { - state = VB2_BUF_STATE_ERROR; - for (; p < p_src + sz; p++) { - u32 copy; - - p = memchr(p, magic[ctx->comp_magic_cnt], - p_src + sz - p); - if (!p) { - ctx->comp_magic_cnt = 0; - break; - } - copy = sizeof(magic) - ctx->comp_magic_cnt; - if (p_src + sz - p < copy) - copy = p_src + sz - p; - - memcpy(ctx->state.compressed_frame + ctx->comp_magic_cnt, - p, copy); - ctx->comp_magic_cnt += copy; - if (!memcmp(ctx->state.compressed_frame, magic, - ctx->comp_magic_cnt)) { - p += copy; - state = VB2_BUF_STATE_DONE; - break; - } - ctx->comp_magic_cnt = 0; - } - if (ctx->comp_magic_cnt < sizeof(magic)) { + if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { + state = get_next_header(ctx, &p, p_src + sz - p); + if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { job_remove_src_buf(ctx, state); goto restart; } - ctx->comp_size = sizeof(magic); - } - if (ctx->comp_size < sizeof(struct fwht_cframe_hdr)) { - struct fwht_cframe_hdr *p_hdr = - (struct fwht_cframe_hdr *)ctx->state.compressed_frame; - u32 copy = sizeof(struct fwht_cframe_hdr) - ctx->comp_size; - if (copy > p_src + sz - p) - copy = p_src + sz - p; - memcpy(ctx->state.compressed_frame + ctx->comp_size, - p, copy); - p += copy; - ctx->comp_size += copy; - if (ctx->comp_size < sizeof(struct fwht_cframe_hdr)) { - job_remove_src_buf(ctx, state); - goto restart; - } - ctx->comp_frame_size = ntohl(p_hdr->size) + sizeof(*p_hdr); + ctx->comp_frame_size = ntohl(ctx->state.header.size); + if (ctx->comp_frame_size > ctx->comp_max_size) ctx->comp_frame_size = ctx->comp_max_size; } @@ -1121,7 +1140,7 @@ static int vicodec_start_streaming(struct vb2_queue *q, state->stride = q_data->coded_width * info->bytesperline_mult; } state->ref_frame.luma = kvmalloc(total_planes_size, GFP_KERNEL); - ctx->comp_max_size = total_planes_size + sizeof(struct fwht_cframe_hdr); + ctx->comp_max_size = total_planes_size; state->compressed_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); if (!state->ref_frame.luma || !state->compressed_frame) { kvfree(state->ref_frame.luma); @@ -1148,6 +1167,7 @@ static int vicodec_start_streaming(struct vb2_queue *q, state->gop_cnt = 0; ctx->cur_buf_offset = 0; ctx->comp_size = 0; + ctx->header_size = 0; ctx->comp_magic_cnt = 0; ctx->comp_has_frame = false; -- cgit v1.2.3-59-g8ed1b From 3b15f68e19c28a76d175f61943a8c23224afce93 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 21 Jan 2019 09:46:18 -0200 Subject: media: vicodec: Add support for resolution change event. If the the queues are not streaming then the first resolution change is handled in the buf_queue callback. The following resolution change events are handled in job_ready. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: wrap info_from_header prototype] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 362 +++++++++++++++++++++----- 1 file changed, 294 insertions(+), 68 deletions(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 7c68959aae34..454476a9f659 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -126,9 +126,10 @@ struct vicodec_ctx { u32 comp_size; u32 header_size; u32 comp_magic_cnt; - u32 comp_frame_size; bool comp_has_frame; bool comp_has_next_frame; + bool first_source_change_sent; + bool source_changed; }; static inline struct vicodec_ctx *file2ctx(struct file *file) @@ -323,6 +324,96 @@ static void job_remove_src_buf(struct vicodec_ctx *ctx, u32 state) spin_unlock(ctx->lock); } +static const struct v4l2_fwht_pixfmt_info * +info_from_header(const struct fwht_cframe_hdr *p_hdr) +{ + unsigned int flags = ntohl(p_hdr->flags); + unsigned int width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + unsigned int height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + unsigned int components_num = 3; + unsigned int pixenc = 0; + unsigned int version = ntohl(p_hdr->version); + + if (version == FWHT_VERSION) { + components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> + FWHT_FL_COMPONENTS_NUM_OFFSET); + pixenc = (flags & FWHT_FL_PIXENC_MSK); + } + return v4l2_fwht_default_fmt(width_div, height_div, + components_num, pixenc, 0); +} + +static bool is_header_valid(const struct fwht_cframe_hdr *p_hdr) +{ + const struct v4l2_fwht_pixfmt_info *info; + unsigned int w = ntohl(p_hdr->width); + unsigned int h = ntohl(p_hdr->height); + unsigned int version = ntohl(p_hdr->version); + unsigned int flags = ntohl(p_hdr->flags); + + if (!version || version > FWHT_VERSION) + return false; + + if (w < MIN_WIDTH || w > MAX_WIDTH || h < MIN_HEIGHT || h > MAX_HEIGHT) + return false; + + if (version == FWHT_VERSION) { + unsigned int components_num = 1 + + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> + FWHT_FL_COMPONENTS_NUM_OFFSET); + unsigned int pixenc = flags & FWHT_FL_PIXENC_MSK; + + if (components_num == 0 || components_num > 4 || !pixenc) + return false; + } + + info = info_from_header(p_hdr); + if (!info) + return false; + return true; +} + +static void update_capture_data_from_header(struct vicodec_ctx *ctx) +{ + struct vicodec_q_data *q_dst = get_q_data(ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + const struct fwht_cframe_hdr *p_hdr = &ctx->state.header; + const struct v4l2_fwht_pixfmt_info *info = info_from_header(p_hdr); + unsigned int flags = ntohl(p_hdr->flags); + unsigned int hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + unsigned int hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + + q_dst->info = info; + q_dst->visible_width = ntohl(p_hdr->width); + q_dst->visible_height = ntohl(p_hdr->height); + q_dst->coded_width = vic_round_dim(q_dst->visible_width, hdr_width_div); + q_dst->coded_height = vic_round_dim(q_dst->visible_height, + hdr_height_div); + + q_dst->sizeimage = q_dst->coded_width * q_dst->coded_height * + q_dst->info->sizeimage_mult / q_dst->info->sizeimage_div; + ctx->state.colorspace = ntohl(p_hdr->colorspace); + + ctx->state.xfer_func = ntohl(p_hdr->xfer_func); + ctx->state.ycbcr_enc = ntohl(p_hdr->ycbcr_enc); + ctx->state.quantization = ntohl(p_hdr->quantization); +} + +static void set_last_buffer(struct vb2_v4l2_buffer *dst_buf, + const struct vb2_v4l2_buffer *src_buf, + struct vicodec_ctx *ctx) +{ + struct vicodec_q_data *q_dst = get_q_data(ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + dst_buf->sequence = q_dst->sequence++; + + v4l2_m2m_buf_copy_data(src_buf, dst_buf, !ctx->is_enc); + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); +} + static int job_ready(void *priv) { static const u8 magic[] = { @@ -334,7 +425,16 @@ static int job_ready(void *priv) u8 *p; u32 sz; u32 state; - + struct vicodec_q_data *q_dst = get_q_data(ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + unsigned int flags; + unsigned int hdr_width_div; + unsigned int hdr_height_div; + unsigned int max_to_copy; + unsigned int comp_frame_size; + + if (ctx->source_changed) + return 0; if (ctx->is_enc || ctx->comp_has_frame) return 1; @@ -355,14 +455,21 @@ restart: job_remove_src_buf(ctx, state); goto restart; } + } - ctx->comp_frame_size = ntohl(ctx->state.header.size); + comp_frame_size = ntohl(ctx->state.header.size); - if (ctx->comp_frame_size > ctx->comp_max_size) - ctx->comp_frame_size = ctx->comp_max_size; - } - if (ctx->comp_size < ctx->comp_frame_size) { - u32 copy = ctx->comp_frame_size - ctx->comp_size; + /* + * The current scanned frame might be the first frame of a new + * resolution so its size might be larger than ctx->comp_max_size. + * In that case it is copied up to the current buffer capacity and + * the copy will continue after allocating new large enough buffer + * when restreaming + */ + max_to_copy = min(comp_frame_size, ctx->comp_max_size); + + if (ctx->comp_size < max_to_copy) { + u32 copy = max_to_copy - ctx->comp_size; if (copy > p_src + sz - p) copy = p_src + sz - p; @@ -371,15 +478,17 @@ restart: p, copy); p += copy; ctx->comp_size += copy; - if (ctx->comp_size < ctx->comp_frame_size) { + if (ctx->comp_size < max_to_copy) { job_remove_src_buf(ctx, state); goto restart; } } ctx->cur_buf_offset = p - p_src; - ctx->comp_has_frame = true; + if (ctx->comp_size == comp_frame_size) + ctx->comp_has_frame = true; ctx->comp_has_next_frame = false; - if (sz - ctx->cur_buf_offset >= sizeof(struct fwht_cframe_hdr)) { + if (ctx->comp_has_frame && sz - ctx->cur_buf_offset >= + sizeof(struct fwht_cframe_hdr)) { struct fwht_cframe_hdr *p_hdr = (struct fwht_cframe_hdr *)p; u32 frame_size = ntohl(p_hdr->size); u32 remaining = sz - ctx->cur_buf_offset - sizeof(*p_hdr); @@ -387,6 +496,36 @@ restart: if (!memcmp(p, magic, sizeof(magic))) ctx->comp_has_next_frame = remaining >= frame_size; } + /* + * if the header is invalid the device_run will just drop the frame + * with an error + */ + if (!is_header_valid(&ctx->state.header) && ctx->comp_has_frame) + return 1; + flags = ntohl(ctx->state.header.flags); + hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + + if (ntohl(ctx->state.header.width) != q_dst->visible_width || + ntohl(ctx->state.header.height) != q_dst->visible_height || + !q_dst->info || + hdr_width_div != q_dst->info->width_div || + hdr_height_div != q_dst->info->height_div) { + static const struct v4l2_event rs_event = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + struct vb2_v4l2_buffer *dst_buf = + v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + update_capture_data_from_header(ctx); + ctx->first_source_change_sent = true; + v4l2_event_queue_fh(&ctx->fh, &rs_event); + set_last_buffer(dst_buf, src_buf, ctx); + ctx->source_changed = true; + return 0; + } return 1; } @@ -428,7 +567,7 @@ static int enum_fmt(struct v4l2_fmtdesc *f, struct vicodec_ctx *ctx, const struct v4l2_fwht_pixfmt_info *info = get_q_data(ctx, f->type)->info; - if (ctx->is_enc) + if (!info || ctx->is_enc) info = v4l2_fwht_get_pixfmt(f->index); else info = v4l2_fwht_default_fmt(info->width_div, @@ -478,6 +617,9 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) q_data = get_q_data(ctx, f->type); info = q_data->info; + if (!info) + info = v4l2_fwht_get_pixfmt(0); + switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: @@ -688,6 +830,7 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) pix = &f->fmt.pix; if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) fmt_changed = + !q_data->info || q_data->info->id != pix->pixelformat || q_data->coded_width != pix->width || q_data->coded_height != pix->height; @@ -708,6 +851,7 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) pix_mp = &f->fmt.pix_mp; if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) fmt_changed = + !q_data->info || q_data->info->id != pix_mp->pixelformat || q_data->coded_width != pix_mp->width || q_data->coded_height != pix_mp->height; @@ -966,6 +1110,7 @@ static int vicodec_subscribe_event(struct v4l2_fh *fh, { switch (sub->type) { case V4L2_EVENT_EOS: + case V4L2_EVENT_SOURCE_CHANGE: return v4l2_event_subscribe(fh, sub, 0, NULL); default: return v4l2_ctrl_subscribe_event(fh, sub); @@ -1074,7 +1219,71 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + unsigned int sz = vb2_get_plane_payload(&vbuf->vb2_buf, 0); + u8 *p_src = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + u8 *p = p_src; + struct vb2_queue *vq_out = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT); + struct vb2_queue *vq_cap = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + bool header_valid = false; + static const struct v4l2_event rs_event = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + /* buf_queue handles only the first source change event */ + if (ctx->first_source_change_sent) { + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + return; + } + + /* + * if both queues are streaming, the source change event is + * handled in job_ready + */ + if (vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out)) { + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + return; + } + + /* + * source change event is relevant only for the decoder + * in the compressed stream + */ + if (ctx->is_enc || !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); + return; + } + + do { + enum vb2_buffer_state state = + get_next_header(ctx, &p, p_src + sz - p); + + if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { + v4l2_m2m_buf_done(vbuf, state); + return; + } + header_valid = is_header_valid(&ctx->state.header); + /* + * p points right after the end of the header in the + * buffer. If the header is invalid we set p to point + * to the next byte after the start of the header + */ + if (!header_valid) { + p = p - sizeof(struct fwht_cframe_hdr) + 1; + if (p < p_src) + p = p_src; + ctx->header_size = 0; + ctx->comp_magic_cnt = 0; + } + } while (!header_valid); + + ctx->cur_buf_offset = p - p_src; + update_capture_data_from_header(ctx); + ctx->first_source_change_sent = true; + v4l2_event_queue_fh(&ctx->fh, &rs_event); v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } @@ -1104,51 +1313,68 @@ static int vicodec_start_streaming(struct vb2_queue *q, struct v4l2_fwht_state *state = &ctx->state; const struct v4l2_fwht_pixfmt_info *info = q_data->info; unsigned int size = q_data->coded_width * q_data->coded_height; - unsigned int chroma_div = info->width_div * info->height_div; + unsigned int chroma_div; unsigned int total_planes_size; + u8 *new_comp_frame; - /* - * we don't know ahead how many components are in the encoding type - * V4L2_PIX_FMT_FWHT, so we will allocate space for 4 planes. - */ - if (info->id == V4L2_PIX_FMT_FWHT || info->components_num == 4) + if (!info) + return -EINVAL; + + chroma_div = info->width_div * info->height_div; + q_data->sequence = 0; + + ctx->last_src_buf = NULL; + ctx->last_dst_buf = NULL; + state->gop_cnt = 0; + + if ((V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || + (!V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) + return 0; + + if (info->id == V4L2_PIX_FMT_FWHT) { + vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); + return -EINVAL; + } + if (info->components_num == 4) total_planes_size = 2 * size + 2 * (size / chroma_div); else if (info->components_num == 3) total_planes_size = size + 2 * (size / chroma_div); else total_planes_size = size; - q_data->sequence = 0; - - if (!V4L2_TYPE_IS_OUTPUT(q->type)) { - if (!ctx->is_enc) { - state->visible_width = q_data->visible_width; - state->visible_height = q_data->visible_height; - state->coded_width = q_data->coded_width; - state->coded_height = q_data->coded_height; - state->stride = q_data->coded_width * - info->bytesperline_mult; - } - return 0; - } + state->visible_width = q_data->visible_width; + state->visible_height = q_data->visible_height; + state->coded_width = q_data->coded_width; + state->coded_height = q_data->coded_height; + state->stride = q_data->coded_width * + info->bytesperline_mult; - if (ctx->is_enc) { - state->visible_width = q_data->visible_width; - state->visible_height = q_data->visible_height; - state->coded_width = q_data->coded_width; - state->coded_height = q_data->coded_height; - state->stride = q_data->coded_width * info->bytesperline_mult; - } state->ref_frame.luma = kvmalloc(total_planes_size, GFP_KERNEL); ctx->comp_max_size = total_planes_size; - state->compressed_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); - if (!state->ref_frame.luma || !state->compressed_frame) { + new_comp_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); + + if (!state->ref_frame.luma || !new_comp_frame) { kvfree(state->ref_frame.luma); - kvfree(state->compressed_frame); + kvfree(new_comp_frame); vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); return -ENOMEM; } - if (info->id == V4L2_PIX_FMT_FWHT || info->components_num >= 3) { + /* + * if state->compressed_frame was already allocated then + * it contain data of the first frame of the new resolution + */ + if (state->compressed_frame) { + if (ctx->comp_size > ctx->comp_max_size) + ctx->comp_size = ctx->comp_max_size; + + memcpy(new_comp_frame, + state->compressed_frame, ctx->comp_size); + } + + kvfree(state->compressed_frame); + state->compressed_frame = new_comp_frame; + + if (info->components_num >= 3) { state->ref_frame.cb = state->ref_frame.luma + size; state->ref_frame.cr = state->ref_frame.cb + size / chroma_div; } else { @@ -1156,21 +1382,11 @@ static int vicodec_start_streaming(struct vb2_queue *q, state->ref_frame.cr = NULL; } - if (info->id == V4L2_PIX_FMT_FWHT || info->components_num == 4) + if (info->components_num == 4) state->ref_frame.alpha = state->ref_frame.cr + size / chroma_div; else state->ref_frame.alpha = NULL; - - ctx->last_src_buf = NULL; - ctx->last_dst_buf = NULL; - state->gop_cnt = 0; - ctx->cur_buf_offset = 0; - ctx->comp_size = 0; - ctx->header_size = 0; - ctx->comp_magic_cnt = 0; - ctx->comp_has_frame = false; - return 0; } @@ -1180,11 +1396,20 @@ static void vicodec_stop_streaming(struct vb2_queue *q) vicodec_return_bufs(q, VB2_BUF_STATE_ERROR); - if (!V4L2_TYPE_IS_OUTPUT(q->type)) - return; - - kvfree(ctx->state.ref_frame.luma); - kvfree(ctx->state.compressed_frame); + if ((!V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || + (V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) { + kvfree(ctx->state.ref_frame.luma); + ctx->comp_max_size = 0; + ctx->source_changed = false; + } + if (V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) { + ctx->cur_buf_offset = 0; + ctx->comp_size = 0; + ctx->header_size = 0; + ctx->comp_magic_cnt = 0; + ctx->comp_has_frame = 0; + ctx->comp_has_next_frame = 0; + } } static const struct vb2_ops vicodec_qops = { @@ -1336,16 +1561,17 @@ static int vicodec_open(struct file *file) else ctx->q_data[V4L2_M2M_SRC].sizeimage = size + sizeof(struct fwht_cframe_hdr); - ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; - ctx->q_data[V4L2_M2M_DST].info = - ctx->is_enc ? &pixfmt_fwht : v4l2_fwht_get_pixfmt(0); - size = 1280 * 720 * ctx->q_data[V4L2_M2M_DST].info->sizeimage_mult / - ctx->q_data[V4L2_M2M_DST].info->sizeimage_div; - if (ctx->is_enc) - ctx->q_data[V4L2_M2M_DST].sizeimage = - size + sizeof(struct fwht_cframe_hdr); - else - ctx->q_data[V4L2_M2M_DST].sizeimage = size; + if (ctx->is_enc) { + ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; + ctx->q_data[V4L2_M2M_DST].info = &pixfmt_fwht; + ctx->q_data[V4L2_M2M_DST].sizeimage = 1280 * 720 * + ctx->q_data[V4L2_M2M_DST].info->sizeimage_mult / + ctx->q_data[V4L2_M2M_DST].info->sizeimage_div + + sizeof(struct fwht_cframe_hdr); + } else { + ctx->q_data[V4L2_M2M_DST].info = NULL; + } + ctx->state.colorspace = V4L2_COLORSPACE_REC709; if (ctx->is_enc) { -- cgit v1.2.3-59-g8ed1b From f863f222b49a9c7a6cfcc72f1ac74ab14c7a7258 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Thu, 24 Jan 2019 07:51:08 -0200 Subject: media: vicodec: ensure comp frame pointer kept in range Make sure that the pointer to the compressed frame does not get out of the buffer. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.c | 71 +++++++++++++++++------- drivers/media/platform/vicodec/codec-fwht.h | 2 +- drivers/media/platform/vicodec/codec-v4l2-fwht.c | 8 ++- drivers/media/platform/vicodec/vicodec-core.c | 4 ++ 4 files changed, 60 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.c b/drivers/media/platform/vicodec/codec-fwht.c index e5e0a80c2f73..d1d6085da9f1 100644 --- a/drivers/media/platform/vicodec/codec-fwht.c +++ b/drivers/media/platform/vicodec/codec-fwht.c @@ -13,6 +13,8 @@ #include #include "codec-fwht.h" +#define OVERFLOW_BIT BIT(14) + /* * Note: bit 0 of the header must always be 0. Otherwise it cannot * be guaranteed that the magic 8 byte sequence (see below) can @@ -104,16 +106,21 @@ static int rlc(const s16 *in, __be16 *output, int blocktype) * This function will worst-case increase rlc_in by 65*2 bytes: * one s16 value for the header and 8 * 8 coefficients of type s16. */ -static s16 derlc(const __be16 **rlc_in, s16 *dwht_out) +static u16 derlc(const __be16 **rlc_in, s16 *dwht_out, + const __be16 *end_of_input) { /* header */ const __be16 *input = *rlc_in; - s16 ret = ntohs(*input++); + u16 stat; int dec_count = 0; s16 block[8 * 8 + 16]; s16 *wp = block; int i; + if (input > end_of_input) + return OVERFLOW_BIT; + stat = ntohs(*input++); + /* * Now de-compress, it expands one byte to up to 15 bytes * (or fills the remainder of the 64 bytes with zeroes if it @@ -123,9 +130,15 @@ static s16 derlc(const __be16 **rlc_in, s16 *dwht_out) * allow for overflow if the incoming data was malformed. */ while (dec_count < 8 * 8) { - s16 in = ntohs(*input++); - int length = in & 0xf; - int coeff = in >> 4; + s16 in; + int length; + int coeff; + + if (input > end_of_input) + return OVERFLOW_BIT; + in = ntohs(*input++); + length = in & 0xf; + coeff = in >> 4; /* fill remainder with zeros */ if (length == 15) { @@ -150,7 +163,7 @@ static s16 derlc(const __be16 **rlc_in, s16 *dwht_out) dwht_out[x + y * 8] = *wp++; } *rlc_in = input; - return ret; + return stat; } static const int quant_table[] = { @@ -808,22 +821,24 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, return encoding; } -static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, +static bool decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, u32 height, u32 width, u32 coded_width, - bool uncompressed) + bool uncompressed, const __be16 *end_of_rlco_buf) { unsigned int copies = 0; s16 copy[8 * 8]; - s16 stat; + u16 stat; unsigned int i, j; width = round_up(width, 8); height = round_up(height, 8); if (uncompressed) { + if (end_of_rlco_buf + 1 < *rlco + width * height / 2) + return false; memcpy(ref, *rlco, width * height); *rlco += width * height / 2; - return; + return true; } /* @@ -847,8 +862,9 @@ static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, continue; } - stat = derlc(rlco, cf->coeffs); - + stat = derlc(rlco, cf->coeffs, end_of_rlco_buf); + if (stat & OVERFLOW_BIT) + return false; if (stat & PFRAME_BIT) dequantize_inter(cf->coeffs); else @@ -865,17 +881,22 @@ static void decode_plane(struct fwht_cframe *cf, const __be16 **rlco, u8 *ref, fill_decoder_block(refp, cf->de_fwht, coded_width); } } + return true; } -void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, +bool fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, u32 hdr_flags, unsigned int components_num, unsigned int width, unsigned int height, unsigned int coded_width) { const __be16 *rlco = cf->rlc_data; + const __be16 *end_of_rlco_buf = cf->rlc_data + + (cf->size / sizeof(*rlco)) - 1; - decode_plane(cf, &rlco, ref->luma, height, width, coded_width, - hdr_flags & FWHT_FL_LUMA_IS_UNCOMPRESSED); + if (!decode_plane(cf, &rlco, ref->luma, height, width, coded_width, + hdr_flags & FWHT_FL_LUMA_IS_UNCOMPRESSED, + end_of_rlco_buf)) + return false; if (components_num >= 3) { u32 h = height; @@ -888,13 +909,21 @@ void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, w /= 2; c /= 2; } - decode_plane(cf, &rlco, ref->cb, h, w, c, - hdr_flags & FWHT_FL_CB_IS_UNCOMPRESSED); - decode_plane(cf, &rlco, ref->cr, h, w, c, - hdr_flags & FWHT_FL_CR_IS_UNCOMPRESSED); + if (!decode_plane(cf, &rlco, ref->cb, h, w, c, + hdr_flags & FWHT_FL_CB_IS_UNCOMPRESSED, + end_of_rlco_buf)) + return false; + if (!decode_plane(cf, &rlco, ref->cr, h, w, c, + hdr_flags & FWHT_FL_CR_IS_UNCOMPRESSED, + end_of_rlco_buf)) + return false; } if (components_num == 4) - decode_plane(cf, &rlco, ref->alpha, height, width, coded_width, - hdr_flags & FWHT_FL_ALPHA_IS_UNCOMPRESSED); + if (!decode_plane(cf, &rlco, ref->alpha, height, width, + coded_width, + hdr_flags & FWHT_FL_ALPHA_IS_UNCOMPRESSED, + end_of_rlco_buf)) + return false; + return true; } diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h index ad8cfc60a152..60d71d9dacb3 100644 --- a/drivers/media/platform/vicodec/codec-fwht.h +++ b/drivers/media/platform/vicodec/codec-fwht.h @@ -139,7 +139,7 @@ u32 fwht_encode_frame(struct fwht_raw_frame *frm, bool is_intra, bool next_is_intra, unsigned int width, unsigned int height, unsigned int stride, unsigned int chroma_stride); -void fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, +bool fwht_decode_frame(struct fwht_cframe *cf, struct fwht_raw_frame *ref, u32 hdr_flags, unsigned int components_num, unsigned int width, unsigned int height, unsigned int coded_width); diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index ee6903b8896c..c15034849133 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -280,6 +280,7 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) state->ycbcr_enc = ntohl(state->header.ycbcr_enc); state->quantization = ntohl(state->header.quantization); cf.rlc_data = (__be16 *)p_in; + cf.size = ntohl(state->header.size); hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; @@ -287,9 +288,10 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) hdr_height_div != info->height_div) return -EINVAL; - fwht_decode_frame(&cf, &state->ref_frame, flags, components_num, - state->visible_width, state->visible_height, - state->coded_width); + if (!fwht_decode_frame(&cf, &state->ref_frame, flags, components_num, + state->visible_width, state->visible_height, + state->coded_width)) + return -EINVAL; /* * TODO - handle the case where the compressed stream encodes a diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 454476a9f659..28c3a3d57783 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -186,6 +186,10 @@ static int device_process(struct vicodec_ctx *ctx, return ret; vb2_set_plane_payload(&dst_vb->vb2_buf, 0, ret); } else { + unsigned int comp_frame_size = ntohl(ctx->state.header.size); + + if (comp_frame_size > ctx->comp_max_size) + return -EINVAL; state->info = q_dst->info; ret = v4l2_fwht_decode(state, p_src, p_dst); if (ret < 0) -- cgit v1.2.3-59-g8ed1b From c9d06df612977a88c484668ad0a37bc8e4463b22 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 26 Jan 2019 09:12:04 -0200 Subject: media: vicodec: get_next_header is static drivers/media/platform/vicodec/vicodec-core.c:drivers/media/platform/vicodec/vicodec-core.c:210:23: warning: symbol 'get_next_header' was not declared. Should it be static? drivers/media/platform/vicodec/vicodec-core.c:210:23: warning: no previous prototype for 'get_next_header' [-Wmissing-prototypes] enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx, u8 **pp, u32 sz) ^~~~~~~~~~~~~~~ Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 28c3a3d57783..3703b587e25e 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -207,7 +207,8 @@ static int device_process(struct vicodec_ctx *ctx, /* * mem2mem callbacks */ -enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx, u8 **pp, u32 sz) +static enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx, + u8 **pp, u32 sz) { static const u8 magic[] = { 0x4f, 0x4f, 0x4f, 0x4f, 0xff, 0xff, 0xff, 0xff -- cgit v1.2.3-59-g8ed1b From 2e0fe66e0a136252f4d89dbbccdcb26deb867eb8 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Mon, 21 Jan 2019 21:35:50 -0200 Subject: media: imx: csi: Disable CSI immediately after last EOF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable the CSI immediately after receiving the last EOF before stream off (and thus before disabling the IDMA channel). Do this by moving the wait for EOF completion into a new function csi_idmac_wait_last_eof(). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d4 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Disabling the CSI before disabling the IDMA channel appears to be a reliable fix for the hard lockup. Fixes: 4a34ec8e470cb ("[media] media: imx: Add CSI subdev driver") Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: stable@vger.kernel.org # for 4.13 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 7abfe0aa1418..920e38885292 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -662,7 +662,7 @@ out_put_ipu: return ret; } -static void csi_idmac_stop(struct csi_priv *priv) +static void csi_idmac_wait_last_eof(struct csi_priv *priv) { unsigned long flags; int ret; @@ -679,7 +679,10 @@ static void csi_idmac_stop(struct csi_priv *priv) &priv->last_eof_comp, msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); if (ret == 0) v4l2_warn(&priv->sd, "wait last EOF timeout\n"); +} +static void csi_idmac_stop(struct csi_priv *priv) +{ devm_free_irq(priv->dev, priv->eof_irq, priv); devm_free_irq(priv->dev, priv->nfb4eof_irq, priv); @@ -786,6 +789,16 @@ idmac_stop: static void csi_stop(struct csi_priv *priv) { + if (priv->dest == IPU_CSI_DEST_IDMAC) + csi_idmac_wait_last_eof(priv); + + /* + * Disable the CSI asap, after syncing with the last EOF. + * Doing so after the IDMA channel is disabled has shown to + * create hard system-wide hangs. + */ + ipu_csi_disable(priv->csi); + if (priv->dest == IPU_CSI_DEST_IDMAC) { csi_idmac_stop(priv); @@ -793,8 +806,6 @@ static void csi_stop(struct csi_priv *priv) if (priv->fim) imx_media_fim_set_stream(priv->fim, NULL, false); } - - ipu_csi_disable(priv->csi); } static const struct csi_skip_desc csi_skip[12] = { -- cgit v1.2.3-59-g8ed1b From 4bc1ab41eee9d02ad2483bf8f51a7b72e3504eba Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Mon, 21 Jan 2019 21:35:51 -0200 Subject: media: imx: csi: Stop upstream before disabling IDMA channel Move upstream stream off to just after receiving the last EOF completion and disabling the CSI (and thus before disabling the IDMA channel) in csi_stop(). For symmetry also move upstream stream on to beginning of csi_start(). Doing this makes csi_s_stream() more symmetric with prp_s_stream() which will require the same change to fix a hard lockup. Signed-off-by: Steve Longerbeam Cc: stable@vger.kernel.org # for 4.13 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-csi.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 920e38885292..d851ca2497b4 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -753,10 +753,16 @@ static int csi_start(struct csi_priv *priv) output_fi = &priv->frame_interval[priv->active_output_pad]; + /* start upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); + ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; + if (ret) + return ret; + if (priv->dest == IPU_CSI_DEST_IDMAC) { ret = csi_idmac_start(priv); if (ret) - return ret; + goto stop_upstream; } ret = csi_setup(priv); @@ -784,6 +790,8 @@ fim_off: idmac_stop: if (priv->dest == IPU_CSI_DEST_IDMAC) csi_idmac_stop(priv); +stop_upstream: + v4l2_subdev_call(priv->src_sd, video, s_stream, 0); return ret; } @@ -799,6 +807,9 @@ static void csi_stop(struct csi_priv *priv) */ ipu_csi_disable(priv->csi); + /* stop upstream */ + v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (priv->dest == IPU_CSI_DEST_IDMAC) { csi_idmac_stop(priv); @@ -966,23 +977,13 @@ static int csi_s_stream(struct v4l2_subdev *sd, int enable) goto update_count; if (enable) { - /* upstream must be started first, before starting CSI */ - ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); - ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; - if (ret) - goto out; - dev_dbg(priv->dev, "stream ON\n"); ret = csi_start(priv); - if (ret) { - v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (ret) goto out; - } } else { dev_dbg(priv->dev, "stream OFF\n"); - /* CSI must be stopped first, then stop upstream */ csi_stop(priv); - v4l2_subdev_call(priv->src_sd, video, s_stream, 0); } update_count: -- cgit v1.2.3-59-g8ed1b From a19c22677377b87e4354f7306f46ad99bc982a9f Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Mon, 21 Jan 2019 21:35:52 -0200 Subject: media: imx: prpencvf: Stop upstream before disabling IDMA channel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream must be stopped immediately after receiving the last EOF and before disabling the IDMA channel. This can be accomplished by moving upstream stream off to just after receiving the last EOF completion in prp_stop(). For symmetry also move upstream stream on to end of prp_start(). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d1 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Stopping the video data stream entering the IDMA channel before disabling the channel itself appears to be a reliable fix for the hard lockup. Fixes: f0d9c8924e2c3 ("[media] media: imx: Add IC subdev drivers") Reported-by: Gaël PORTAY Tested-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: stable@vger.kernel.org # for 4.13 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 053a911d477a..3637693c2bc8 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -706,12 +706,23 @@ static int prp_start(struct prp_priv *priv) goto out_free_nfb4eof_irq; } + /* start upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); + ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; + if (ret) { + v4l2_err(&ic_priv->sd, + "upstream stream on failed: %d\n", ret); + goto out_free_eof_irq; + } + /* start the EOF timeout timer */ mod_timer(&priv->eof_timeout_timer, jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); return 0; +out_free_eof_irq: + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); out_free_nfb4eof_irq: devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); out_unsetup: @@ -743,6 +754,12 @@ static void prp_stop(struct prp_priv *priv) if (ret == 0) v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n"); + /* stop upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + v4l2_warn(&ic_priv->sd, + "upstream stream off failed: %d\n", ret); + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); @@ -1173,15 +1190,6 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable) if (ret) goto out; - /* start/stop upstream */ - ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable); - ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; - if (ret) { - if (enable) - prp_stop(priv); - goto out; - } - update_count: priv->stream_count += enable ? 1 : -1; if (priv->stream_count < 0) -- cgit v1.2.3-59-g8ed1b From d8501cc85069f161466016a140e71ea88ee83376 Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Thu, 17 Jan 2019 03:39:18 -0200 Subject: media: dt-bindings: media: add 'assigned-clocks' to vcodec examples Fix MTK binding document for MT8173 dtsi changed in order to use standard CCF interface. MT8173 SoC from Mediatek. Signed-off-by: Yunfei Dong Signed-off-by: Qianqian Yan Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/mediatek-vcodec.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/media/mediatek-vcodec.txt b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt index 2a615d84a682..b6b5dde6abd8 100644 --- a/Documentation/devicetree/bindings/media/mediatek-vcodec.txt +++ b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt @@ -66,6 +66,15 @@ vcodec_dec: vcodec@16000000 { "vencpll", "venc_lt_sel", "vdec_bus_clk_src"; + assigned-clocks = <&topckgen CLK_TOP_VENC_LT_SEL>, + <&topckgen CLK_TOP_CCI400_SEL>, + <&topckgen CLK_TOP_VDEC_SEL>, + <&apmixedsys CLK_APMIXED_VCODECPLL>, + <&apmixedsys CLK_APMIXED_VENCPLL>; + assigned-clock-parents = <&topckgen CLK_TOP_VCODECPLL_370P5>, + <&topckgen CLK_TOP_UNIVPLL_D2>, + <&topckgen CLK_TOP_VCODECPLL>; + assigned-clock-rates = <0>, <0>, <0>, <1482000000>, <800000000>; }; vcodec_enc: vcodec@18002000 { @@ -105,4 +114,8 @@ vcodec_dec: vcodec@16000000 { "venc_sel", "venc_lt_sel_src", "venc_lt_sel"; + assigned-clocks = <&topckgen CLK_TOP_VENC_SEL>, + <&topckgen CLK_TOP_VENC_LT_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_VENCPLL_D2>, + <&topckgen CLK_TOP_UNIVPLL1_D2>; }; -- cgit v1.2.3-59-g8ed1b From 04bde67410bfff52958298e831fc5c7db04abe2d Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Thu, 17 Jan 2019 03:39:20 -0200 Subject: media: mtk-vcodec: Using common interface to manage vdec/venc clock Vdec: Using standard CCF interface to set parent clock and clock rate in dtsi and using common interface to open/close video decoder clock. Venc: Using standard CCF interface to set parent clock in dtsi and using common interface to open/close video encoder clock. Signed-off-by: Yunfei Dong Signed-off-by: Qianqian Yan Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c | 163 +++++++-------------- drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h | 31 ++-- .../media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c | 104 +++++++------ 3 files changed, 132 insertions(+), 166 deletions(-) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c index 79ca03ac449c..7884465afcd2 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c @@ -27,11 +27,14 @@ int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev) struct device_node *node; struct platform_device *pdev; struct mtk_vcodec_pm *pm; - int ret = 0; + struct mtk_vcodec_clk *dec_clk; + struct mtk_vcodec_clk_info *clk_info; + int i = 0, ret = 0; pdev = mtkdev->plat_dev; pm = &mtkdev->pm; pm->mtkdev = mtkdev; + dec_clk = &pm->vdec_clk; node = of_parse_phandle(pdev->dev.of_node, "mediatek,larb", 0); if (!node) { mtk_v4l2_err("of_parse_phandle mediatek,larb fail!"); @@ -47,52 +50,34 @@ int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev) pdev = mtkdev->plat_dev; pm->dev = &pdev->dev; - pm->vcodecpll = devm_clk_get(&pdev->dev, "vcodecpll"); - if (IS_ERR(pm->vcodecpll)) { - mtk_v4l2_err("devm_clk_get vcodecpll fail"); - ret = PTR_ERR(pm->vcodecpll); + dec_clk->clk_num = + of_property_count_strings(pdev->dev.of_node, "clock-names"); + if (dec_clk->clk_num > 0) { + dec_clk->clk_info = devm_kcalloc(&pdev->dev, + dec_clk->clk_num, sizeof(*clk_info), + GFP_KERNEL); + if (!dec_clk->clk_info) + return -ENOMEM; + } else { + mtk_v4l2_err("Failed to get vdec clock count"); + return -EINVAL; } - pm->univpll_d2 = devm_clk_get(&pdev->dev, "univpll_d2"); - if (IS_ERR(pm->univpll_d2)) { - mtk_v4l2_err("devm_clk_get univpll_d2 fail"); - ret = PTR_ERR(pm->univpll_d2); - } - - pm->clk_cci400_sel = devm_clk_get(&pdev->dev, "clk_cci400_sel"); - if (IS_ERR(pm->clk_cci400_sel)) { - mtk_v4l2_err("devm_clk_get clk_cci400_sel fail"); - ret = PTR_ERR(pm->clk_cci400_sel); - } - - pm->vdec_sel = devm_clk_get(&pdev->dev, "vdec_sel"); - if (IS_ERR(pm->vdec_sel)) { - mtk_v4l2_err("devm_clk_get vdec_sel fail"); - ret = PTR_ERR(pm->vdec_sel); - } - - pm->vdecpll = devm_clk_get(&pdev->dev, "vdecpll"); - if (IS_ERR(pm->vdecpll)) { - mtk_v4l2_err("devm_clk_get vdecpll fail"); - ret = PTR_ERR(pm->vdecpll); - } - - pm->vencpll = devm_clk_get(&pdev->dev, "vencpll"); - if (IS_ERR(pm->vencpll)) { - mtk_v4l2_err("devm_clk_get vencpll fail"); - ret = PTR_ERR(pm->vencpll); - } - - pm->venc_lt_sel = devm_clk_get(&pdev->dev, "venc_lt_sel"); - if (IS_ERR(pm->venc_lt_sel)) { - mtk_v4l2_err("devm_clk_get venc_lt_sel fail"); - ret = PTR_ERR(pm->venc_lt_sel); - } - - pm->vdec_bus_clk_src = devm_clk_get(&pdev->dev, "vdec_bus_clk_src"); - if (IS_ERR(pm->vdec_bus_clk_src)) { - mtk_v4l2_err("devm_clk_get vdec_bus_clk_src"); - ret = PTR_ERR(pm->vdec_bus_clk_src); + for (i = 0; i < dec_clk->clk_num; i++) { + clk_info = &dec_clk->clk_info[i]; + ret = of_property_read_string_index(pdev->dev.of_node, + "clock-names", i, &clk_info->clk_name); + if (ret) { + mtk_v4l2_err("Failed to get clock name id = %d", i); + return ret; + } + clk_info->vcodec_clk = devm_clk_get(&pdev->dev, + clk_info->clk_name); + if (IS_ERR(clk_info->vcodec_clk)) { + mtk_v4l2_err("devm_clk_get (%d)%s fail", i, + clk_info->clk_name); + return PTR_ERR(clk_info->vcodec_clk); + } } pm_runtime_enable(&pdev->dev); @@ -125,78 +110,36 @@ void mtk_vcodec_dec_pw_off(struct mtk_vcodec_pm *pm) void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm) { - int ret; - - ret = clk_set_rate(pm->vcodecpll, 1482 * 1000000); - if (ret) - mtk_v4l2_err("clk_set_rate vcodecpll fail %d", ret); - - ret = clk_set_rate(pm->vencpll, 800 * 1000000); - if (ret) - mtk_v4l2_err("clk_set_rate vencpll fail %d", ret); - - ret = clk_prepare_enable(pm->vcodecpll); - if (ret) - mtk_v4l2_err("clk_prepare_enable vcodecpll fail %d", ret); - - ret = clk_prepare_enable(pm->vencpll); - if (ret) - mtk_v4l2_err("clk_prepare_enable vencpll fail %d", ret); - - ret = clk_prepare_enable(pm->vdec_bus_clk_src); - if (ret) - mtk_v4l2_err("clk_prepare_enable vdec_bus_clk_src fail %d", - ret); - - ret = clk_prepare_enable(pm->venc_lt_sel); - if (ret) - mtk_v4l2_err("clk_prepare_enable venc_lt_sel fail %d", ret); - - ret = clk_set_parent(pm->venc_lt_sel, pm->vdec_bus_clk_src); - if (ret) - mtk_v4l2_err("clk_set_parent venc_lt_sel vdec_bus_clk_src fail %d", - ret); - - ret = clk_prepare_enable(pm->univpll_d2); - if (ret) - mtk_v4l2_err("clk_prepare_enable univpll_d2 fail %d", ret); - - ret = clk_prepare_enable(pm->clk_cci400_sel); - if (ret) - mtk_v4l2_err("clk_prepare_enable clk_cci400_sel fail %d", ret); - - ret = clk_set_parent(pm->clk_cci400_sel, pm->univpll_d2); - if (ret) - mtk_v4l2_err("clk_set_parent clk_cci400_sel univpll_d2 fail %d", - ret); - - ret = clk_prepare_enable(pm->vdecpll); - if (ret) - mtk_v4l2_err("clk_prepare_enable vdecpll fail %d", ret); - - ret = clk_prepare_enable(pm->vdec_sel); - if (ret) - mtk_v4l2_err("clk_prepare_enable vdec_sel fail %d", ret); - - ret = clk_set_parent(pm->vdec_sel, pm->vdecpll); - if (ret) - mtk_v4l2_err("clk_set_parent vdec_sel vdecpll fail %d", ret); + struct mtk_vcodec_clk *dec_clk = &pm->vdec_clk; + int ret, i = 0; + + for (i = 0; i < dec_clk->clk_num; i++) { + ret = clk_prepare_enable(dec_clk->clk_info[i].vcodec_clk); + if (ret) { + mtk_v4l2_err("clk_prepare_enable %d %s fail %d", i, + dec_clk->clk_info[i].clk_name, ret); + goto error; + } + } ret = mtk_smi_larb_get(pm->larbvdec); - if (ret) + if (ret) { mtk_v4l2_err("mtk_smi_larb_get larbvdec fail %d", ret); + goto error; + } + return; +error: + for (i -= 1; i >= 0; i--) + clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); } void mtk_vcodec_dec_clock_off(struct mtk_vcodec_pm *pm) { + struct mtk_vcodec_clk *dec_clk = &pm->vdec_clk; + int i = 0; + mtk_smi_larb_put(pm->larbvdec); - clk_disable_unprepare(pm->vdec_sel); - clk_disable_unprepare(pm->vdecpll); - clk_disable_unprepare(pm->univpll_d2); - clk_disable_unprepare(pm->clk_cci400_sel); - clk_disable_unprepare(pm->venc_lt_sel); - clk_disable_unprepare(pm->vdec_bus_clk_src); - clk_disable_unprepare(pm->vencpll); - clk_disable_unprepare(pm->vcodecpll); + for (i = dec_clk->clk_num - 1; i >= 0; i--) + clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h index 3cffb381ac8e..8aba69555b12 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h @@ -175,23 +175,30 @@ struct mtk_enc_params { unsigned int force_intra; }; +/** + * struct mtk_vcodec_clk_info - Structure used to store clock name + */ +struct mtk_vcodec_clk_info { + const char *clk_name; + struct clk *vcodec_clk; +}; + +/** + * struct mtk_vcodec_clk - Structure used to store vcodec clock information + */ +struct mtk_vcodec_clk { + struct mtk_vcodec_clk_info *clk_info; + int clk_num; +}; + /** * struct mtk_vcodec_pm - Power management data structure */ struct mtk_vcodec_pm { - struct clk *vdec_bus_clk_src; - struct clk *vencpll; - - struct clk *vcodecpll; - struct clk *univpll_d2; - struct clk *clk_cci400_sel; - struct clk *vdecpll; - struct clk *vdec_sel; - struct clk *vencpll_d2; - struct clk *venc_sel; - struct clk *univpll1_d2; - struct clk *venc_lt_sel; + struct mtk_vcodec_clk vdec_clk; struct device *larbvdec; + + struct mtk_vcodec_clk venc_clk; struct device *larbvenc; struct device *larbvenclt; struct device *dev; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c index 7c025045ea90..39375b8ea27c 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c @@ -27,9 +27,11 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) { struct device_node *node; struct platform_device *pdev; - struct device *dev; struct mtk_vcodec_pm *pm; - int ret = 0; + struct mtk_vcodec_clk *enc_clk; + struct mtk_vcodec_clk_info *clk_info; + int ret = 0, i = 0; + struct device *dev; pdev = mtkdev->plat_dev; pm = &mtkdev->pm; @@ -37,6 +39,7 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) pm->mtkdev = mtkdev; pm->dev = &pdev->dev; dev = &pdev->dev; + enc_clk = &pm->venc_clk; node = of_parse_phandle(dev->of_node, "mediatek,larb", 0); if (!node) { @@ -68,28 +71,34 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) pdev = mtkdev->plat_dev; pm->dev = &pdev->dev; - pm->vencpll_d2 = devm_clk_get(&pdev->dev, "venc_sel_src"); - if (IS_ERR(pm->vencpll_d2)) { - mtk_v4l2_err("devm_clk_get vencpll_d2 fail"); - ret = PTR_ERR(pm->vencpll_d2); - } - - pm->venc_sel = devm_clk_get(&pdev->dev, "venc_sel"); - if (IS_ERR(pm->venc_sel)) { - mtk_v4l2_err("devm_clk_get venc_sel fail"); - ret = PTR_ERR(pm->venc_sel); + enc_clk->clk_num = of_property_count_strings(pdev->dev.of_node, + "clock-names"); + if (enc_clk->clk_num > 0) { + enc_clk->clk_info = devm_kcalloc(&pdev->dev, + enc_clk->clk_num, sizeof(*clk_info), + GFP_KERNEL); + if (!enc_clk->clk_info) + return -ENOMEM; + } else { + mtk_v4l2_err("Failed to get venc clock count"); + return -EINVAL; } - pm->univpll1_d2 = devm_clk_get(&pdev->dev, "venc_lt_sel_src"); - if (IS_ERR(pm->univpll1_d2)) { - mtk_v4l2_err("devm_clk_get univpll1_d2 fail"); - ret = PTR_ERR(pm->univpll1_d2); - } - - pm->venc_lt_sel = devm_clk_get(&pdev->dev, "venc_lt_sel"); - if (IS_ERR(pm->venc_lt_sel)) { - mtk_v4l2_err("devm_clk_get venc_lt_sel fail"); - ret = PTR_ERR(pm->venc_lt_sel); + for (i = 0; i < enc_clk->clk_num; i++) { + clk_info = &enc_clk->clk_info[i]; + ret = of_property_read_string_index(pdev->dev.of_node, + "clock-names", i, &clk_info->clk_name); + if (ret) { + mtk_v4l2_err("venc failed to get clk name %d", i); + return ret; + } + clk_info->vcodec_clk = devm_clk_get(&pdev->dev, + clk_info->clk_name); + if (IS_ERR(clk_info->vcodec_clk)) { + mtk_v4l2_err("venc devm_clk_get (%d)%s fail", i, + clk_info->clk_name); + return PTR_ERR(clk_info->vcodec_clk); + } } return ret; @@ -102,38 +111,45 @@ void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *mtkdev) void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm) { - int ret; - - ret = clk_prepare_enable(pm->venc_sel); - if (ret) - mtk_v4l2_err("clk_prepare_enable fail %d", ret); - - ret = clk_set_parent(pm->venc_sel, pm->vencpll_d2); - if (ret) - mtk_v4l2_err("clk_set_parent fail %d", ret); - - ret = clk_prepare_enable(pm->venc_lt_sel); - if (ret) - mtk_v4l2_err("clk_prepare_enable fail %d", ret); - - ret = clk_set_parent(pm->venc_lt_sel, pm->univpll1_d2); - if (ret) - mtk_v4l2_err("clk_set_parent fail %d", ret); + struct mtk_vcodec_clk *enc_clk = &pm->venc_clk; + int ret, i = 0; + + for (i = 0; i < enc_clk->clk_num; i++) { + ret = clk_prepare_enable(enc_clk->clk_info[i].vcodec_clk); + if (ret) { + mtk_v4l2_err("venc clk_prepare_enable %d %s fail %d", i, + enc_clk->clk_info[i].clk_name, ret); + goto clkerr; + } + } ret = mtk_smi_larb_get(pm->larbvenc); - if (ret) + if (ret) { mtk_v4l2_err("mtk_smi_larb_get larb3 fail %d", ret); - + goto larbvencerr; + } ret = mtk_smi_larb_get(pm->larbvenclt); - if (ret) + if (ret) { mtk_v4l2_err("mtk_smi_larb_get larb4 fail %d", ret); + goto larbvenclterr; + } + return; +larbvenclterr: + mtk_smi_larb_put(pm->larbvenc); +larbvencerr: +clkerr: + for (i -= 1; i >= 0; i--) + clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk); } void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm) { + struct mtk_vcodec_clk *enc_clk = &pm->venc_clk; + int i = 0; + mtk_smi_larb_put(pm->larbvenc); mtk_smi_larb_put(pm->larbvenclt); - clk_disable_unprepare(pm->venc_lt_sel); - clk_disable_unprepare(pm->venc_sel); + for (i = enc_clk->clk_num - 1; i >= 0; i--) + clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk); } -- cgit v1.2.3-59-g8ed1b From 03535e7a3a9937da99ee18304309e0574d2504fc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 24 Jan 2019 06:47:49 -0200 Subject: media: vb2: vb2_find_timestamp: drop restriction on buffer state There really is no reason why vb2_find_timestamp can't just find buffers in any state. Drop that part of the test. This also means that vb->timestamp should only be set to 0 when the driver doesn't copy timestamps. This change allows for more efficient pipelining (i.e. you can use a buffer for a reference frame even when it is queued). Signed-off-by: Hans Verkuil Reviewed-by: Tomasz Figa Reviewed-by: Alexandre Courbot Reviewed-by: Paul Kocialkowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 11 +++-------- include/media/videobuf2-v4l2.h | 3 +-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 75ea90e795d8..2f3b3ca5bde6 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -567,7 +567,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); unsigned int plane; - if (!vb->vb2_queue->is_output || !vb->vb2_queue->copy_timestamp) + if (!vb->vb2_queue->copy_timestamp) vb->timestamp = 0; for (plane = 0; plane < vb->num_planes; ++plane) { @@ -594,14 +594,9 @@ int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp, { unsigned int i; - for (i = start_idx; i < q->num_buffers; i++) { - struct vb2_buffer *vb = q->bufs[i]; - - if ((vb->state == VB2_BUF_STATE_DEQUEUED || - vb->state == VB2_BUF_STATE_DONE) && - vb->timestamp == timestamp) + for (i = start_idx; i < q->num_buffers; i++) + if (q->bufs[i]->timestamp == timestamp) return i; - } return -1; } EXPORT_SYMBOL_GPL(vb2_find_timestamp); diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h index a9961bc776dc..8a10889dc2fd 100644 --- a/include/media/videobuf2-v4l2.h +++ b/include/media/videobuf2-v4l2.h @@ -59,8 +59,7 @@ struct vb2_v4l2_buffer { * vb2_find_timestamp() - Find buffer with given timestamp in the queue * * @q: pointer to &struct vb2_queue with videobuf2 queue. - * @timestamp: the timestamp to find. Only buffers in state DEQUEUED or DONE - * are considered. + * @timestamp: the timestamp to find. * @start_idx: the start index (usually 0) in the buffer array to start * searching from. Note that there may be multiple buffers * with the same timestamp value, so you can restart the search -- cgit v1.2.3-59-g8ed1b From 826ef7508833f8bae928ccae4071b6efa6ba6184 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 24 Jan 2019 10:49:45 -0200 Subject: media: Revert "media: cedrus: Allow using the current dst buffer as reference" This reverts commit cf20ae1535eb690a87c29b9cc7af51881384e967. The vb2_find_timestamp helper was modified to allow finding buffers regardless of their current state in the queue. This means that we no longer have to take particular care of references to the current capture buffer. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_dec.c | 13 ------------- drivers/staging/media/sunxi/cedrus/cedrus_dec.h | 2 -- drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c | 10 ++++------ 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 2c295286766c..443fb037e1cf 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -22,19 +22,6 @@ #include "cedrus_dec.h" #include "cedrus_hw.h" -int cedrus_reference_index_find(struct vb2_queue *queue, - struct vb2_buffer *vb2_buf, u64 timestamp) -{ - /* - * Allow using the current capture buffer as reference, which can occur - * for field-coded pictures. - */ - if (vb2_buf->timestamp == timestamp) - return vb2_buf->index; - else - return vb2_find_timestamp(queue, timestamp, 0); -} - void cedrus_device_run(void *priv) { struct cedrus_ctx *ctx = priv; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h index 8d0fc248220f..d1ae7903677b 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h @@ -16,8 +16,6 @@ #ifndef _CEDRUS_DEC_H_ #define _CEDRUS_DEC_H_ -int cedrus_reference_index_find(struct vb2_queue *queue, - struct vb2_buffer *vb2_buf, u64 timestamp); void cedrus_device_run(void *priv); #endif diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index 81c66a8aa1ac..cb45fda9aaeb 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -10,7 +10,6 @@ #include #include "cedrus.h" -#include "cedrus_dec.h" #include "cedrus_hw.h" #include "cedrus_regs.h" @@ -160,8 +159,8 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_PICBOUNDSIZE, reg); /* Forward and backward prediction reference buffers. */ - forward_idx = cedrus_reference_index_find(cap_q, &run->dst->vb2_buf, - slice_params->forward_ref_ts); + forward_idx = vb2_find_timestamp(cap_q, + slice_params->forward_ref_ts, 0); fwd_luma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 0); fwd_chroma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 1); @@ -169,9 +168,8 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr); - backward_idx = cedrus_reference_index_find(cap_q, &run->dst->vb2_buf, - slice_params->backward_ref_ts); - + backward_idx = vb2_find_timestamp(cap_q, + slice_params->backward_ref_ts, 0); bwd_luma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 0); bwd_chroma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 1); -- cgit v1.2.3-59-g8ed1b From 2cc1802f62e562611e86f04d9dae1337c824991e Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Thu, 24 Jan 2019 07:51:55 -0200 Subject: media: vb2: Keep dma-buf buffers mapped until they are freed When using vb2 for video decoding, dequeued capture buffers may still be accessed by the hardware: this is the case when they are used as reference frames for decoding subsequent frames. When the buffer is imported with dma-buf, it needs to be mapped before access. Until now, it was mapped when queuing and unmapped when dequeuing, which doesn't work for access as a reference frames. One way to solve this would be to map the buffer again when it is needed as a reference, but the mapping/unmapping operations can seriously impact performance. As a result, map the buffer once (when it is first needed when queued) and keep it mapped until it is freed. Reviewed-on: https://chromium-review.googlesource.com/334103 [Paul: Updated for mainline and changed commit message] Signed-off-by: Pawel Osciak Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 70e8c3366f9c..ce9294a635cc 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -1196,6 +1196,9 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) * userspace knows sooner rather than later if the dma-buf map fails. */ for (plane = 0; plane < vb->num_planes; ++plane) { + if (vb->planes[plane].dbuf_mapped) + continue; + ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv); if (ret) { dprintk(1, "failed to map dmabuf for plane %d\n", @@ -1758,14 +1761,6 @@ static void __vb2_dqbuf(struct vb2_buffer *vb) vb->state = VB2_BUF_STATE_DEQUEUED; - /* unmap DMABUF buffer */ - if (q->memory == VB2_MEMORY_DMABUF) - for (i = 0; i < vb->num_planes; ++i) { - if (!vb->planes[i].dbuf_mapped) - continue; - call_void_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv); - vb->planes[i].dbuf_mapped = 0; - } call_void_bufop(q, init_buffer, vb); } -- cgit v1.2.3-59-g8ed1b From 065e5a31497d8f285da06eac1c8d8e86c723af47 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 24 Jan 2019 07:51:56 -0200 Subject: media: cedrus: Remove completed item from TODO list (dma-buf references) Access to reference frames that were imported from dma-buf was taken care of and is no longer a pending item on the driver's TODO list. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/TODO | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/TODO b/drivers/staging/media/sunxi/cedrus/TODO index a951b3fd1ea1..ec277ece47af 100644 --- a/drivers/staging/media/sunxi/cedrus/TODO +++ b/drivers/staging/media/sunxi/cedrus/TODO @@ -5,8 +5,3 @@ Before this stateless decoder driver can leave the staging area: * Userspace support for the Request API needs to be reviewed; * Another stateless decoder driver should be submitted; * At least one stateless encoder driver should be submitted. -* When queueing a request containing references to I frames, the - refcount of the memory for those I frames needs to be incremented - and decremented when the request is completed. This will likely - require some help from vb2. The driver should fail the request - if the memory/buffer is gone. -- cgit v1.2.3-59-g8ed1b From 28d77c21cbeb2c6039d48ef88401b87a56a7a07f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 10:01:13 -0200 Subject: media: vb2: add buf_out_validate callback When queueing a buffer to a request the 'field' value is not validated. That field is only validated when the _buf_prepare() is called, which happens when the request is queued. However, this validation should happen at QBUF time, since you want to know about this as soon as possible. Also, the spec requires that the 'field' value is validated at QBUF time. This patch adds a new buf_out_validate callback to validate the output buffer at buf_prepare time or when QBUF queues an unprepared buffer to a request. This callback is mandatory for output queues that support requests. This issue was found by v4l2-compliance since it failed to replace V4L2_FIELD_ANY by a proper field value when testing the vivid video output in combination with requests. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 22 +++++++++++++++++++--- include/media/videobuf2-core.h | 5 +++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index ce9294a635cc..e07b6bdb6982 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -499,9 +499,9 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) pr_info(" buf_init: %u buf_cleanup: %u buf_prepare: %u buf_finish: %u\n", vb->cnt_buf_init, vb->cnt_buf_cleanup, vb->cnt_buf_prepare, vb->cnt_buf_finish); - pr_info(" buf_queue: %u buf_done: %u buf_request_complete: %u\n", - vb->cnt_buf_queue, vb->cnt_buf_done, - vb->cnt_buf_request_complete); + pr_info(" buf_out_validate: %u buf_queue: %u buf_done: %u buf_request_complete: %u\n", + vb->cnt_buf_out_validate, vb->cnt_buf_queue, + vb->cnt_buf_done, vb->cnt_buf_request_complete); pr_info(" alloc: %u put: %u prepare: %u finish: %u mmap: %u\n", vb->cnt_mem_alloc, vb->cnt_mem_put, vb->cnt_mem_prepare, vb->cnt_mem_finish, @@ -1277,6 +1277,14 @@ static int __buf_prepare(struct vb2_buffer *vb) return 0; WARN_ON(vb->synced); + if (q->is_output) { + ret = call_vb_qop(vb, buf_out_validate, vb); + if (ret) { + dprintk(1, "buffer validation failed\n"); + return ret; + } + } + vb->state = VB2_BUF_STATE_PREPARING; switch (q->memory) { @@ -1523,6 +1531,14 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, return -EINVAL; } + if (q->is_output && !vb->prepared) { + ret = call_vb_qop(vb, buf_out_validate, vb); + if (ret) { + dprintk(1, "buffer validation failed\n"); + return ret; + } + } + media_request_object_init(&vb->req_obj); /* Make sure the request is in a safe state for updating. */ diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 4a737b2c610b..4849b865b908 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -296,6 +296,7 @@ struct vb2_buffer { u32 cnt_mem_num_users; u32 cnt_mem_mmap; + u32 cnt_buf_out_validate; u32 cnt_buf_init; u32 cnt_buf_prepare; u32 cnt_buf_finish; @@ -342,6 +343,9 @@ struct vb2_buffer { * @wait_finish: reacquire all locks released in the previous callback; * required to continue operation after sleeping while * waiting for a new buffer to arrive. + * @buf_out_validate: called when the output buffer is prepared or queued + * to a request; drivers can use this to validate + * userspace-provided information; optional. * @buf_init: called once after allocating a buffer (in MMAP case) * or after acquiring a new USERPTR buffer; drivers may * perform additional buffer-related initialization; @@ -409,6 +413,7 @@ struct vb2_ops { void (*wait_prepare)(struct vb2_queue *q); void (*wait_finish)(struct vb2_queue *q); + int (*buf_out_validate)(struct vb2_buffer *vb); int (*buf_init)(struct vb2_buffer *vb); int (*buf_prepare)(struct vb2_buffer *vb); void (*buf_finish)(struct vb2_buffer *vb); -- cgit v1.2.3-59-g8ed1b From ab7afaf33275acc2ccfaab3fe5b46870eaee9d55 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 10:01:14 -0200 Subject: media: vim2m: add buf_out_validate callback Validate the field for an output buffer. This ensures that the field is validated when the buffer is queued to a request, and not when the request itself is queued, which is too late. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index a7a152fb3075..924120d69da2 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -743,25 +743,29 @@ static int vim2m_queue_setup(struct vb2_queue *vq, return 0; } -static int vim2m_buf_prepare(struct vb2_buffer *vb) +static int vim2m_buf_out_validate(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + dprintk(ctx->dev, "%s field isn't supported\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int vim2m_buf_prepare(struct vb2_buffer *vb) +{ + struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vim2m_q_data *q_data; dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type); q_data = get_q_data(ctx, vb->vb2_queue->type); - if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { - if (vbuf->field == V4L2_FIELD_ANY) - vbuf->field = V4L2_FIELD_NONE; - if (vbuf->field != V4L2_FIELD_NONE) { - dprintk(ctx->dev, "%s field isn't supported\n", - __func__); - return -EINVAL; - } - } - if (vb2_plane_size(vb, 0) < q_data->sizeimage) { dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n", __func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage); @@ -822,6 +826,7 @@ static void vim2m_buf_request_complete(struct vb2_buffer *vb) static const struct vb2_ops vim2m_qops = { .queue_setup = vim2m_queue_setup, + .buf_out_validate = vim2m_buf_out_validate, .buf_prepare = vim2m_buf_prepare, .buf_queue = vim2m_buf_queue, .start_streaming = vim2m_start_streaming, -- cgit v1.2.3-59-g8ed1b From 1f2f510753becf607f9974a39a89ab1878f24b9d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 10:01:15 -0200 Subject: media: vivid: add buf_out_validate callback Validate the field for an output buffer. This ensures that the field is validated when the buffer is queued to a request, and not when the request itself is queued, which is too late. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-vid-out.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index 55c0628ee9e1..b42b7fcc6145 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -84,10 +84,24 @@ static int vid_out_queue_setup(struct vb2_queue *vq, return 0; } -static int vid_out_buf_prepare(struct vb2_buffer *vb) +static int vid_out_buf_out_validate(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + + dprintk(dev, 1, "%s\n", __func__); + + if (dev->field_out != V4L2_FIELD_ALTERNATE) + vbuf->field = dev->field_out; + else if (vbuf->field != V4L2_FIELD_TOP && + vbuf->field != V4L2_FIELD_BOTTOM) + return -EINVAL; + return 0; +} + +static int vid_out_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); unsigned long size; unsigned planes; unsigned p; @@ -108,12 +122,6 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb) return -EINVAL; } - if (dev->field_out != V4L2_FIELD_ALTERNATE) - vbuf->field = dev->field_out; - else if (vbuf->field != V4L2_FIELD_TOP && - vbuf->field != V4L2_FIELD_BOTTOM) - return -EINVAL; - for (p = 0; p < planes; p++) { size = dev->bytesperline_out[p] * dev->fmt_out_rect.height + vb->planes[p].data_offset; @@ -191,6 +199,7 @@ static void vid_out_buf_request_complete(struct vb2_buffer *vb) const struct vb2_ops vivid_vid_out_qops = { .queue_setup = vid_out_queue_setup, + .buf_out_validate = vid_out_buf_out_validate, .buf_prepare = vid_out_buf_prepare, .buf_queue = vid_out_buf_queue, .start_streaming = vid_out_start_streaming, -- cgit v1.2.3-59-g8ed1b From 6b3e4c4cc162390b833e57de656644786ca88919 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 10:01:16 -0200 Subject: media: cedrus: add buf_out_validate callback Validate the field for an output buffer. This ensures that the field is validated when the buffer is queued to a request, and not when the request itself is queued, which is too late. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_video.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c index 8721b4a7d496..b5cc79389d67 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c @@ -416,6 +416,14 @@ static void cedrus_buf_cleanup(struct vb2_buffer *vb) ctx->dst_bufs[vb->index] = NULL; } +static int cedrus_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + static int cedrus_buf_prepare(struct vb2_buffer *vb) { struct vb2_queue *vq = vb->vb2_queue; @@ -493,6 +501,7 @@ static struct vb2_ops cedrus_qops = { .buf_init = cedrus_buf_init, .buf_cleanup = cedrus_buf_cleanup, .buf_queue = cedrus_buf_queue, + .buf_out_validate = cedrus_buf_out_validate, .buf_request_complete = cedrus_buf_request_complete, .start_streaming = cedrus_start_streaming, .stop_streaming = cedrus_stop_streaming, -- cgit v1.2.3-59-g8ed1b From 1284ed59a147c27cb882e49213571f7d52976eb5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Jan 2019 10:01:17 -0200 Subject: media: vb2: check that buf_out_validate is present The buf_out_validate is required for output queues in combination with requests. Check this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 2f3b3ca5bde6..3aeaea3af42a 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -409,6 +409,15 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md */ if (WARN_ON(!q->ops->buf_request_complete)) return -EINVAL; + /* + * Make sure this op is implemented by the driver for the output queue. + * It's easy to forget this callback, but is it important to correctly + * validate the 'field' value at QBUF time. + */ + if (WARN_ON((q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT || + q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) && + !q->ops->buf_out_validate)) + return -EINVAL; if (vb->state != VB2_BUF_STATE_DEQUEUED) { dprintk(1, "%s: buffer is not in dequeued state\n", opname); -- cgit v1.2.3-59-g8ed1b From 560c053deb94ff65b22a87f28e8e2fab5940555c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 25 Jan 2019 13:40:04 -0200 Subject: media: vivid: fix vid_out_buf_prepare() The wrong size check was performed for output formats like NV24 which set vfmt->buffers to 1, but vfmt->planes is 2. It was incorrectly checking the payload size for plane 1, which doesn't exist. Note: vfmt->buffers refers to the number of per-plane-buffers that should be allocated. vfmt->planes refers to the number of planes that make up an image. vfmt->planes may be > vfmt->buffers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-vid-out.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index b42b7fcc6145..e61b91b414f9 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -102,17 +102,20 @@ static int vid_out_buf_out_validate(struct vb2_buffer *vb) static int vid_out_buf_prepare(struct vb2_buffer *vb) { struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size; - unsigned planes; + const struct vivid_fmt *vfmt = dev->fmt_out; + unsigned int planes = vfmt->buffers; + unsigned int h = dev->fmt_out_rect.height; + unsigned int size = dev->bytesperline_out[0] * h; unsigned p; + for (p = vfmt->buffers; p < vfmt->planes; p++) + size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p]; + dprintk(dev, 1, "%s\n", __func__); if (WARN_ON(NULL == dev->fmt_out)) return -EINVAL; - planes = dev->fmt_out->planes; - if (dev->buf_prepare_error) { /* * Error injection: test what happens if buf_prepare() returns @@ -123,11 +126,12 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb) } for (p = 0; p < planes; p++) { - size = dev->bytesperline_out[p] * dev->fmt_out_rect.height + - vb->planes[p].data_offset; + if (p) + size = dev->bytesperline_out[p] * h; + size += vb->planes[p].data_offset; if (vb2_get_plane_payload(vb, p) < size) { - dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %lu)\n", + dprintk(dev, 1, "%s the payload is too small for plane %u (%lu < %u)\n", __func__, p, vb2_get_plane_payload(vb, p), size); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 8aa153f103f10807941bd95dda95071380609e05 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 29 Jan 2019 14:00:15 -0200 Subject: media: vim2m: fix driver for it to handle different fourcc formats Despite vim2m is reporting that it supports RGB565BE and YUYV, that's not true. Right now, it just says that it supports both format, but it doesn't actually support them. Also, horizontal flip is not properly implemented. It sounds that it was designed to do a pseudo-horizontal flip using 8 tiles. Yet, as it doesn't do format conversion, the result is a mess. I suspect that it was done this way in order to save CPU time, at the time of OMAP2 days. That's messy and doesn't really help if someone wants to use vim2m to test a pipeline. Worse than that, the unique RGB format it says it supports is RGB565BE, with is not supported by Gstreamer. That prevents practical usage of it, even for tests. So, instead, properly implement fourcc format conversions, adding a few more RGB formats: - RGB and BGR with 24 bits - RGB565LE (known as RGB16 at gstreamer) Also allows using any of the 5 supported formats as either capture or output. Note: The YUYV conversion routines are based on the conversion code written by Hans de Goede inside libv4lconvert (part of v4l-utils), released under LGPGL 2.1 (GPL 2.0 compatible). Tested all possible format combinations except for RGB565BE, as Gstreamer currently doesn't support it. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 380 ++++++++++++++++++++++++++--------------- 1 file changed, 240 insertions(+), 140 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 924120d69da2..036d695eeebe 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -60,8 +60,6 @@ MODULE_PARM_DESC(debug, "activates debug info"); /* Default transaction time in msec */ #define MEM2MEM_DEF_TRANSTIME 40 -#define MEM2MEM_COLOR_STEP (0xff >> 4) -#define MEM2MEM_NUM_TILES 8 /* Flags that indicate processing mode */ #define MEM2MEM_HFLIP (1 << 0) @@ -82,22 +80,24 @@ static struct platform_device vim2m_pdev = { struct vim2m_fmt { u32 fourcc; int depth; - /* Types the format can be used for */ - u32 types; }; static struct vim2m_fmt formats[] = { { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .fourcc = V4L2_PIX_FMT_RGB565, /* rrrrrggg gggbbbbb */ .depth = 16, - /* Both capture and output format */ - .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, - }, - { + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, /* gggbbbbb rrrrrggg */ + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, + .depth = 24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, + .depth = 24, + }, { .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, - /* Output-only format */ - .types = MEM2MEM_OUTPUT, }, }; @@ -201,126 +201,253 @@ static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx, return NULL; } +#define CLIP(__color) \ + (u8)(((__color) > 0xff) ? 0xff : (((__color) < 0) ? 0 : (__color))) -static int device_process(struct vim2m_ctx *ctx, - struct vb2_v4l2_buffer *in_vb, - struct vb2_v4l2_buffer *out_vb) +static void copy_two_pixels(struct vim2m_fmt *in, struct vim2m_fmt *out, + u8 **src, u8 **dst, bool reverse) { - struct vim2m_dev *dev = ctx->dev; - struct vim2m_q_data *q_data; - u8 *p_in, *p_out; - int x, y, t, w; - int tile_w, bytes_left; - int width, height, bytesperline; + u8 _r[2], _g[2], _b[2], *r, *g, *b; + int i, step; - q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + // If format is the same just copy the data, respecting the width + if (in->fourcc == out->fourcc) { + int depth = out->depth >> 3; - width = q_data->width; - height = q_data->height; - bytesperline = (q_data->width * q_data->fmt->depth) >> 3; + if (reverse) { + if (in->fourcc == V4L2_PIX_FMT_YUYV) { + int u, v, y, y1; - p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0); - p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0); - if (!p_in || !p_out) { - v4l2_err(&dev->v4l2_dev, - "Acquiring kernel pointers to buffers failed\n"); - return -EFAULT; - } + *src -= 2; - if (vb2_plane_size(&in_vb->vb2_buf, 0) > - vb2_plane_size(&out_vb->vb2_buf, 0)) { - v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n"); - return -EINVAL; + y1 = (*src)[0]; /* copy as second point */ + u = (*src)[1]; + y = (*src)[2]; /* copy as first point */ + v = (*src)[3]; + + *src -= 2; + + *(*dst)++ = y; + *(*dst)++ = u; + *(*dst)++ = y1; + *(*dst)++ = v; + return; + } + + memcpy(*dst, *src, depth); + memcpy(*dst + depth, *src - depth, depth); + *src -= depth << 1; + } else { + memcpy(*dst, *src, depth << 1); + *src += depth << 1; + } + *dst += depth << 1; + return; } - tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3)) - / MEM2MEM_NUM_TILES; - bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES; - w = 0; + /* Step 1: read two consecutive pixels from src pointer */ - out_vb->sequence = - get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; - in_vb->sequence = q_data->sequence++; - v4l2_m2m_buf_copy_data(in_vb, out_vb, true); + r = _r; + g = _g; + b = _b; - switch (ctx->mode) { - case MEM2MEM_HFLIP | MEM2MEM_VFLIP: - p_out += bytesperline * height - bytes_left; - for (y = 0; y < height; ++y) { - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x1) { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out -= bytes_left; + if (reverse) + step = -1; + else + step = 1; + + switch (in->fourcc) { + case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */ + for (i = 0; i < 2; i++) { + u16 pix = *(u16 *)*src; + + *r++ = (u8)(((pix & 0xf800) >> 11) << 3) | 0x07; + *g++ = (u8)((((pix & 0x07e0) >> 5)) << 2) | 0x03; + *b++ = (u8)((pix & 0x1f) << 3) | 0x07; + + *src += step << 1; } break; + case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */ + for (i = 0; i < 2; i++) { + u16 pix = *(u16 *)*src; - case MEM2MEM_HFLIP: - for (y = 0; y < height; ++y) { - p_out += MEM2MEM_NUM_TILES * tile_w; - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x01) { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *--p_out = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out += bytesperline; + *r++ = (u8)(((0x00f8 & pix) >> 3) << 3) | 0x07; + *g++ = (u8)(((pix & 0x7) << 2) | + ((pix & 0xe000) >> 5)) | 0x03; + *b++ = (u8)(((pix & 0x1f00) >> 8) << 3) | 0x07; + + *src += step << 1; } break; + case V4L2_PIX_FMT_RGB24: + for (i = 0; i < 2; i++) { + *r++ = (*src)[0]; + *g++ = (*src)[1]; + *b++ = (*src)[2]; - case MEM2MEM_VFLIP: - p_out += bytesperline * (height - 1); - for (y = 0; y < height; ++y) { - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x1) { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out += bytes_left - 2 * bytesperline; + *src += step * 3; } break; + case V4L2_PIX_FMT_BGR24: + for (i = 0; i < 2; i++) { + *b++ = (*src)[0]; + *g++ = (*src)[1]; + *r++ = (*src)[2]; - default: - for (y = 0; y < height; ++y) { - for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { - if (w & 0x1) { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ + - MEM2MEM_COLOR_STEP; - } else { - for (x = 0; x < tile_w; ++x) - *p_out++ = *p_in++ - - MEM2MEM_COLOR_STEP; - } - ++w; - } - p_in += bytes_left; - p_out += bytes_left; + *src += step * 3; + } + break; + default: /* V4L2_PIX_FMT_YUYV */ + { + int u, v, y, y1, u1, v1, tmp; + + if (reverse) { + *src -= 2; + + y1 = (*src)[0]; /* copy as second point */ + u = (*src)[1]; + y = (*src)[2]; /* copy as first point */ + v = (*src)[3]; + + *src -= 2; + } else { + y = *(*src)++; + u = *(*src)++; + y1 = *(*src)++; + v = *(*src)++; } + + u1 = (((u - 128) << 7) + (u - 128)) >> 6; + tmp = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *r++ = CLIP(y + v1); + *g++ = CLIP(y - tmp); + *b++ = CLIP(y + u1); + + *r = CLIP(y1 + v1); + *g = CLIP(y1 - tmp); + *b = CLIP(y1 + u1); + break; + } + } + + /* Step 2: store two consecutive points, reversing them if needed */ + + r = _r; + g = _g; + b = _b; + + switch (out->fourcc) { + case V4L2_PIX_FMT_RGB565: /* rrrrrggg gggbbbbb */ + for (i = 0; i < 2; i++) { + u16 *pix = (u16 *)*dst; + + *pix = ((*r << 8) & 0xf800) | ((*g << 3) & 0x07e0) | + (*b >> 3); + + *dst += 2; + } + return; + case V4L2_PIX_FMT_RGB565X: /* gggbbbbb rrrrrggg */ + for (i = 0; i < 2; i++) { + u16 *pix = (u16 *)*dst; + u8 green = *g++ >> 2; + + *pix = ((green << 8) & 0xe000) | (green & 0x07) | + ((*b++ << 5) & 0x1f00) | ((*r++ & 0xf8)); + + *dst += 2; + } + return; + case V4L2_PIX_FMT_RGB24: + for (i = 0; i < 2; i++) { + *(*dst)++ = *r++; + *(*dst)++ = *g++; + *(*dst)++ = *b++; + } + return; + case V4L2_PIX_FMT_BGR24: + for (i = 0; i < 2; i++) { + *(*dst)++ = *b++; + *(*dst)++ = *g++; + *(*dst)++ = *r++; + } + return; + default: /* V4L2_PIX_FMT_YUYV */ + { + u8 y, y1, u, v; + + y = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b) + + 524288) >> 15); + u = ((-4878 * (*r) - 9578 * (*g) + 14456 * (*b) + + 4210688) >> 15); + v = ((14456 * (*r++) - 12105 * (*g++) - 2351 * (*b++) + + 4210688) >> 15); + y1 = ((8453 * (*r) + 16594 * (*g) + 3223 * (*b) + + 524288) >> 15); + + *(*dst)++ = y; + *(*dst)++ = u; + + *(*dst)++ = y1; + *(*dst)++ = v; + return; + } + } +} + +static int device_process(struct vim2m_ctx *ctx, + struct vb2_v4l2_buffer *in_vb, + struct vb2_v4l2_buffer *out_vb) +{ + struct vim2m_dev *dev = ctx->dev; + struct vim2m_q_data *q_data_in, *q_data_out; + u8 *p_in, *p, *p_out; + int width, height, bytesperline, x, y, start, end, step; + struct vim2m_fmt *in, *out; + + q_data_in = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + in = q_data_in->fmt; + width = q_data_in->width; + height = q_data_in->height; + bytesperline = (q_data_in->width * q_data_in->fmt->depth) >> 3; + + q_data_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + out = q_data_out->fmt; + + p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0); + p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0); + if (!p_in || !p_out) { + v4l2_err(&dev->v4l2_dev, + "Acquiring kernel pointers to buffers failed\n"); + return -EFAULT; + } + + out_vb->sequence = get_q_data(ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; + in_vb->sequence = q_data_in->sequence++; + v4l2_m2m_buf_copy_data(in_vb, out_vb, true); + + if (ctx->mode & MEM2MEM_VFLIP) { + start = height - 1; + end = -1; + step = -1; + } else { + start = 0; + end = height; + step = 1; + } + for (y = start; y != end; y += step) { + p = p_in + (y * bytesperline); + if (ctx->mode & MEM2MEM_HFLIP) + p += bytesperline - (q_data_in->fmt->depth >> 3); + + for (x = 0; x < width >> 1; x++) + copy_two_pixels(in, out, &p, &p_out, + ctx->mode & MEM2MEM_HFLIP); } return 0; @@ -433,25 +560,11 @@ static int vidioc_querycap(struct file *file, void *priv, static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) { - int i, num; struct vim2m_fmt *fmt; - num = 0; - - for (i = 0; i < NUM_FORMATS; ++i) { - if (formats[i].types & type) { - /* index-th format of type type found ? */ - if (num == f->index) - break; - /* Correct type but haven't reached our index yet, - * just increment per-type index */ - ++num; - } - } - - if (i < NUM_FORMATS) { + if (f->index < NUM_FORMATS) { /* Format found */ - fmt = &formats[i]; + fmt = &formats[f->index]; f->pixelformat = fmt->fourcc; return 0; } @@ -542,12 +655,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.pixelformat = formats[0].fourcc; fmt = find_format(f); } - if (!(fmt->types & MEM2MEM_CAPTURE)) { - v4l2_err(&ctx->dev->v4l2_dev, - "Fourcc format (0x%08x) invalid.\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } f->fmt.pix.colorspace = ctx->colorspace; f->fmt.pix.xfer_func = ctx->xfer_func; f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; @@ -560,19 +667,12 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct vim2m_fmt *fmt; - struct vim2m_ctx *ctx = file2ctx(file); fmt = find_format(f); if (!fmt) { f->fmt.pix.pixelformat = formats[0].fourcc; fmt = find_format(f); } - if (!(fmt->types & MEM2MEM_OUTPUT)) { - v4l2_err(&ctx->dev->v4l2_dev, - "Fourcc format (0x%08x) invalid.\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } if (!f->fmt.pix.colorspace) f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; -- cgit v1.2.3-59-g8ed1b From b3e64e5b077841f99a0ab27e99a1c4f942e1dd87 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 29 Jan 2019 14:00:16 -0200 Subject: media: vim2m: use per-file handler work queue It doesn't make sense to have a per-device work queue, as the scheduler should be called per file handler. Having a single one causes failures if multiple streams are filtered by vim2m. So, move it to be inside the context structure. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 036d695eeebe..921d656cf988 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -146,9 +146,6 @@ struct vim2m_dev { atomic_t num_inst; struct mutex dev_mutex; - spinlock_t irqlock; - - struct delayed_work work_run; struct v4l2_m2m_dev *m2m_dev; }; @@ -167,6 +164,10 @@ struct vim2m_ctx { /* Transaction time (i.e. simulated processing time) in milliseconds */ u32 transtime; + struct mutex vb_mutex; + struct delayed_work work_run; + spinlock_t irqlock; + /* Abort requested by m2m */ int aborting; @@ -490,7 +491,6 @@ static void job_abort(void *priv) static void device_run(void *priv) { struct vim2m_ctx *ctx = priv; - struct vim2m_dev *dev = ctx->dev; struct vb2_v4l2_buffer *src_buf, *dst_buf; src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); @@ -507,18 +507,18 @@ static void device_run(void *priv) &ctx->hdl); /* Run delayed work, which simulates a hardware irq */ - schedule_delayed_work(&dev->work_run, msecs_to_jiffies(ctx->transtime)); + schedule_delayed_work(&ctx->work_run, msecs_to_jiffies(ctx->transtime)); } static void device_work(struct work_struct *w) { - struct vim2m_dev *vim2m_dev = - container_of(w, struct vim2m_dev, work_run.work); struct vim2m_ctx *curr_ctx; + struct vim2m_dev *vim2m_dev; struct vb2_v4l2_buffer *src_vb, *dst_vb; unsigned long flags; - curr_ctx = v4l2_m2m_get_curr_priv(vim2m_dev->m2m_dev); + curr_ctx = container_of(w, struct vim2m_ctx, work_run.work); + vim2m_dev = curr_ctx->dev; if (NULL == curr_ctx) { pr_err("Instance released before the end of transaction\n"); @@ -530,10 +530,10 @@ static void device_work(struct work_struct *w) curr_ctx->num_processed++; - spin_lock_irqsave(&vim2m_dev->irqlock, flags); + spin_lock_irqsave(&curr_ctx->irqlock, flags); v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); - spin_unlock_irqrestore(&vim2m_dev->irqlock, flags); + spin_unlock_irqrestore(&curr_ctx->irqlock, flags); if (curr_ctx->num_processed == curr_ctx->translen || curr_ctx->aborting) { @@ -897,11 +897,10 @@ static int vim2m_start_streaming(struct vb2_queue *q, unsigned count) static void vim2m_stop_streaming(struct vb2_queue *q) { struct vim2m_ctx *ctx = vb2_get_drv_priv(q); - struct vim2m_dev *dev = ctx->dev; struct vb2_v4l2_buffer *vbuf; unsigned long flags; - cancel_delayed_work_sync(&dev->work_run); + cancel_delayed_work_sync(&ctx->work_run); for (;;) { if (V4L2_TYPE_IS_OUTPUT(q->type)) vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); @@ -911,9 +910,9 @@ static void vim2m_stop_streaming(struct vb2_queue *q) return; v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, &ctx->hdl); - spin_lock_irqsave(&ctx->dev->irqlock, flags); + spin_lock_irqsave(&ctx->irqlock, flags); v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); - spin_unlock_irqrestore(&ctx->dev->irqlock, flags); + spin_unlock_irqrestore(&ctx->irqlock, flags); } } @@ -948,7 +947,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds src_vq->ops = &vim2m_qops; src_vq->mem_ops = &vb2_vmalloc_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &ctx->dev->dev_mutex; + src_vq->lock = &ctx->vb_mutex; src_vq->supports_requests = true; ret = vb2_queue_init(src_vq); @@ -962,7 +961,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds dst_vq->ops = &vim2m_qops; dst_vq->mem_ops = &vb2_vmalloc_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &ctx->dev->dev_mutex; + dst_vq->lock = &ctx->vb_mutex; return vb2_queue_init(dst_vq); } @@ -1037,6 +1036,10 @@ static int vim2m_open(struct file *file) ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); + mutex_init(&ctx->vb_mutex); + spin_lock_init(&ctx->irqlock); + INIT_DELAYED_WORK(&ctx->work_run, device_work); + if (IS_ERR(ctx->fh.m2m_ctx)) { rc = PTR_ERR(ctx->fh.m2m_ctx); @@ -1117,8 +1120,6 @@ static int vim2m_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; - spin_lock_init(&dev->irqlock); - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) return ret; @@ -1130,7 +1131,6 @@ static int vim2m_probe(struct platform_device *pdev) vfd = &dev->vfd; vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; - INIT_DELAYED_WORK(&dev->work_run, device_work); ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { -- cgit v1.2.3-59-g8ed1b From f0ef022c85a899bcc7a1b3a0955c78a3d7109106 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 29 Jan 2019 14:00:17 -0200 Subject: media: vim2m: allow setting the default transaction time via parameter While there's a control to allow setting it at runtime, as the control handler is per file handler, only the application setting the m2m device can change it. As this is a custom control, it is unlikely that existing apps would be able to set it. Due to that, and due to the fact that v4l2-mem2mem serializes all accesses to a m2m device, trying to setup two GStreamer v4l2videoconvert instance at the same time will cause frame drops. So, add an alternate way of setting its default via a modprobe parameter. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 921d656cf988..e31c14c7d37f 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -41,6 +41,11 @@ static unsigned debug; module_param(debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); +/* Default transaction time in msec */ +static unsigned int default_transtime = 40; /* Max 25 fps */ +module_param(default_transtime, uint, 0644); +MODULE_PARM_DESC(default_transtime, "default transaction time in ms"); + #define MIN_W 32 #define MIN_H 32 #define MAX_W 640 @@ -58,9 +63,6 @@ MODULE_PARM_DESC(debug, "activates debug info"); /* In bytes, per queue */ #define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024) -/* Default transaction time in msec */ -#define MEM2MEM_DEF_TRANSTIME 40 - /* Flags that indicate processing mode */ #define MEM2MEM_HFLIP (1 << 0) #define MEM2MEM_VFLIP (1 << 1) @@ -764,6 +766,8 @@ static int vim2m_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_TRANS_TIME_MSEC: ctx->transtime = ctrl->val; + if (ctx->transtime < 1) + ctx->transtime = 1; break; case V4L2_CID_TRANS_NUM_BUFS: @@ -966,12 +970,11 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds return vb2_queue_init(dst_vq); } -static const struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = { +static struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = { .ops = &vim2m_ctrl_ops, .id = V4L2_CID_TRANS_TIME_MSEC, .name = "Transaction Time (msec)", .type = V4L2_CTRL_TYPE_INTEGER, - .def = MEM2MEM_DEF_TRANSTIME, .min = 1, .max = 10001, .step = 1, @@ -1013,6 +1016,8 @@ static int vim2m_open(struct file *file) v4l2_ctrl_handler_init(hdl, 4); v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + + vim2m_ctrl_trans_time_msec.def = default_transtime; v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_time_msec, NULL); v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL); if (hdl->error) { -- cgit v1.2.3-59-g8ed1b From d88937624351202e5997fd21ae34e35c74a1abf2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 31 Jan 2019 09:06:07 -0500 Subject: media: videobuf2: remove unused variable Commit 2cc1802f62e5 ("media: vb2: Keep dma-buf buffers mapped until they are freed") removed code leaving a local variable unused. Remove it to avoid a compiler warning. Fixes: 2cc1802f62e5 ("media: vb2: Keep dma-buf buffers mapped until they are freed") Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index e07b6bdb6982..34cc87ca8d59 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -1769,7 +1769,6 @@ EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers); static void __vb2_dqbuf(struct vb2_buffer *vb) { struct vb2_queue *q = vb->vb2_queue; - unsigned int i; /* nothing to do if the buffer is already dequeued */ if (vb->state == VB2_BUF_STATE_DEQUEUED) -- cgit v1.2.3-59-g8ed1b From 801efd0f7522244b5c09fc4c6630dc55fb37c26c Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 28 Jan 2019 06:37:11 -0500 Subject: media: imx-pxp: fix duplicated if condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a copy&paste error to make RGB -> BT.2020 YUV conversion actually selectable. Fixes the following warning: drivers/media/platform/imx-pxp.c:683:24: warning: duplicated ‘if’ condition [-Wduplicated-cond] Fixes: 51abcf7fdb70 ("media: imx-pxp: add i.MX Pixel Pipeline driver") Reported-by: David Binderman Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/imx-pxp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/imx-pxp.c b/drivers/media/platform/imx-pxp.c index c1c255408d16..f087dc4fc729 100644 --- a/drivers/media/platform/imx-pxp.c +++ b/drivers/media/platform/imx-pxp.c @@ -680,7 +680,7 @@ static void pxp_setup_csc(struct pxp_ctx *ctx) csc2_coef = csc2_coef_rec709_full; else csc2_coef = csc2_coef_rec709_lim; - } else if (ycbcr_enc == V4L2_YCBCR_ENC_709) { + } else if (ycbcr_enc == V4L2_YCBCR_ENC_BT2020) { if (quantization == V4L2_QUANTIZATION_FULL_RANGE) csc2_coef = csc2_coef_bt2020_full; else -- cgit v1.2.3-59-g8ed1b From db9a01b32ca9406394e6e7d19c4cdfcf1cbb5058 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2019 02:33:41 -0500 Subject: media: vicodec: check type in g/s_selection Check that the selection buf_type is valid before calling get_q_data() to avoid hitting the WARN(1) in that function if the buffer type is not valid. Signed-off-by: Hans Verkuil Reported-by: syzbot+44b24cff6bf96006ecfa@syzkaller.appspotmail.com Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 3703b587e25e..cda348114764 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -953,6 +953,9 @@ static int vidioc_g_selection(struct file *file, void *priv, valid_out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; } + if (s->type != valid_cap_type && s->type != valid_out_type) + return -EINVAL; + q_data = get_q_data(ctx, s->type); if (!q_data) return -EINVAL; @@ -994,12 +997,14 @@ static int vidioc_s_selection(struct file *file, void *priv, if (multiplanar) out_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + if (s->type != out_type) + return -EINVAL; + q_data = get_q_data(ctx, s->type); if (!q_data) return -EINVAL; - if (!ctx->is_enc || s->type != out_type || - s->target != V4L2_SEL_TGT_CROP) + if (!ctx->is_enc || s->target != V4L2_SEL_TGT_CROP) return -EINVAL; s->r.left = 0; -- cgit v1.2.3-59-g8ed1b From 0247c75b19c0e9554307d842fa1ad914d59a41b1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2019 08:38:27 -0500 Subject: media: vicodec: fill in bus_info in media_device_info It is good practice to fill in bus_info. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index cda348114764..b5a9dee94c91 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -1669,6 +1669,8 @@ static int vicodec_probe(struct platform_device *pdev) #ifdef CONFIG_MEDIA_CONTROLLER dev->mdev.dev = &pdev->dev; strscpy(dev->mdev.model, "vicodec", sizeof(dev->mdev.model)); + strscpy(dev->mdev.bus_info, "platform:vicodec", + sizeof(dev->mdev.bus_info)); media_device_init(&dev->mdev); dev->v4l2_dev.mdev = &dev->mdev; #endif -- cgit v1.2.3-59-g8ed1b From 281ae39b60b798c12405bfc09c2cb563790ab672 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2019 08:39:17 -0500 Subject: media: vim2m: fill in bus_info in media_device_info It is good practice to fill in the bus_info. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index e31c14c7d37f..bfa1a2a16009 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1159,6 +1159,8 @@ static int vim2m_probe(struct platform_device *pdev) #ifdef CONFIG_MEDIA_CONTROLLER dev->mdev.dev = &pdev->dev; strscpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model)); + strscpy(dev->mdev.bus_info, "platform:vim2m", + sizeof(dev->mdev.bus_info)); media_device_init(&dev->mdev); dev->mdev.ops = &m2m_media_ops; dev->v4l2_dev.mdev = &dev->mdev; -- cgit v1.2.3-59-g8ed1b From 69a9005789ad18806490bd8dc70dd6458319cd28 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 31 Jan 2019 08:21:44 -0500 Subject: media: vicodec: support SOURCE_CHANGE event for decoders only The SOURCE_CHANGE event is decoder specific, so don't allow it for encoders. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index b5a9dee94c91..212da185e4f2 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -1118,9 +1118,14 @@ static int vicodec_enum_framesizes(struct file *file, void *fh, static int vicodec_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { + struct vicodec_ctx *ctx = container_of(fh, struct vicodec_ctx, fh); + switch (sub->type) { - case V4L2_EVENT_EOS: case V4L2_EVENT_SOURCE_CHANGE: + if (ctx->is_enc) + return -EINVAL; + /* fall through */ + case V4L2_EVENT_EOS: return v4l2_event_subscribe(fh, sub, 0, NULL); default: return v4l2_ctrl_subscribe_event(fh, sub); -- cgit v1.2.3-59-g8ed1b From 47bb117911b051bbc90764a8bff96543cbd2005f Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Tue, 18 Dec 2018 20:32:48 -0500 Subject: media: uvcvideo: Fix 'type' check leading to overflow When initially testing the Camera Terminal Descriptor wTerminalType field (buffer[4]), no mask is used. Later in the function, the MSB is overloaded to store the descriptor subtype, and so a mask of 0x7fff is used to check the type. If a descriptor is specially crafted to set this overloaded bit in the original wTerminalType field, the initial type check will fail (falling through, without adjusting the buffer size), but the later type checks will pass, assuming the buffer has been made suitably large, causing an overflow. Avoid this problem by checking for the MSB in the wTerminalType field. If the bit is set, assume the descriptor is bad, and abort parsing it. Originally reported here: https://groups.google.com/forum/#!topic/syzkaller/Ot1fOE6v1d8 A similar (non-compiling) patch was provided at that time. Reported-by: syzbot Signed-off-by: Alistair Strachan Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index b62cbd800111..33a22c016456 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1106,11 +1106,19 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - /* Make sure the terminal type MSB is not null, otherwise it - * could be confused with a unit. + /* + * Reject invalid terminal types that would cause issues: + * + * - The high byte must be non-zero, otherwise it would be + * confused with a unit. + * + * - Bit 15 must be 0, as we use it internally as a terminal + * direction flag. + * + * Other unknown types are accepted. */ type = get_unaligned_le16(&buffer[4]); - if ((type & 0xff00) == 0) { + if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d INPUT_TERMINAL %d has invalid " "type 0x%04x, skipping\n", udev->devnum, -- cgit v1.2.3-59-g8ed1b From 9dd0627d8d62a7ddb001a75f63942d92b5336561 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 30 Jan 2019 05:09:41 -0500 Subject: media: uvcvideo: Avoid NULL pointer dereference at the end of streaming The UVC video driver converts the timestamp from hardware specific unit to one known by the kernel at the time when the buffer is dequeued. This is fine in general, but the streamoff operation consists of the following steps (among other things): 1. uvc_video_clock_cleanup --- the hardware clock sample array is released and the pointer to the array is set to NULL, 2. buffers in active state are returned to the user and 3. buf_finish callback is called on buffers that are prepared. buf_finish includes calling uvc_video_clock_update that accesses the hardware clock sample array. The above is serialised by a queue specific mutex. Address the problem by skipping the clock conversion if the hardware clock sample array is already released. Fixes: 9c0863b1cc48 ("[media] vb2: call buf_finish from __queue_cancel") Reported-by: Chiranjeevi Rapolu Tested-by: Chiranjeevi Rapolu Signed-off-by: Sakari Ailus Cc: stable@vger.kernel.org Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_video.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 84525ff04745..e314657a1843 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -676,6 +676,14 @@ void uvc_video_clock_update(struct uvc_streaming *stream, if (!uvc_hw_timestamps_param) return; + /* + * We will get called from __vb2_queue_cancel() if there are buffers + * done but not dequeued by the user, but the sample array has already + * been released at that time. Just bail out in that case. + */ + if (!clock->samples) + return; + spin_lock_irqsave(&clock->lock, flags); if (clock->count < clock->size) -- cgit v1.2.3-59-g8ed1b From 041bc40662a162eff940676a379cc783cb846f32 Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Wed, 16 Jan 2019 12:18:46 -0500 Subject: media: ipu3-imgu: Use MENU type for mode control This addresses the below TODO item. - Use V4L2_CTRL_TYPE_MENU for dual-pipe mode control. (Sakari) Signed-off-by: Yong Zhi Reviewed-by: Tomasz Figa Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/TODO | 2 -- drivers/staging/media/ipu3/include/intel-ipu3.h | 6 ------ drivers/staging/media/ipu3/ipu3-v4l2.c | 15 +++++++++++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/staging/media/ipu3/TODO b/drivers/staging/media/ipu3/TODO index 905bbb190217..0dc9a2e79978 100644 --- a/drivers/staging/media/ipu3/TODO +++ b/drivers/staging/media/ipu3/TODO @@ -11,8 +11,6 @@ staging directory. - Prefix imgu for all public APIs, i.e. change ipu3_v4l2_register() to imgu_v4l2_register(). (Sakari) -- Use V4L2_CTRL_TYPE_MENU for dual-pipe mode control. (Sakari) - - IPU3 driver documentation (Laurent) Add diagram in driver rst to describe output capability. Comments on configuring v4l2 subdevs for CIO2 and ImgU. diff --git a/drivers/staging/media/ipu3/include/intel-ipu3.h b/drivers/staging/media/ipu3/include/intel-ipu3.h index ec0b74829351..eb6f52aca992 100644 --- a/drivers/staging/media/ipu3/include/intel-ipu3.h +++ b/drivers/staging/media/ipu3/include/intel-ipu3.h @@ -16,12 +16,6 @@ #define V4L2_CID_INTEL_IPU3_BASE (V4L2_CID_USER_BASE + 0x10c0) #define V4L2_CID_INTEL_IPU3_MODE (V4L2_CID_INTEL_IPU3_BASE + 1) -/* custom ctrl to set pipe mode */ -enum ipu3_running_mode { - IPU3_RUNNING_MODE_VIDEO = 0, - IPU3_RUNNING_MODE_STILL = 1, -}; - /******************* ipu3_uapi_stats_3a *******************/ #define IPU3_UAPI_MAX_STRIPES 2 diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index c7936032beb9..e758a650ad2b 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -12,6 +12,9 @@ /******************** v4l2_subdev_ops ********************/ +#define IPU3_RUNNING_MODE_VIDEO 0 +#define IPU3_RUNNING_MODE_STILL 1 + static int ipu3_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imgu_v4l2_subdev *imgu_sd = container_of(sd, @@ -1035,15 +1038,19 @@ static const struct v4l2_ctrl_ops ipu3_subdev_ctrl_ops = { .s_ctrl = ipu3_sd_s_ctrl, }; +static const char * const ipu3_ctrl_mode_strings[] = { + "Video mode", + "Still mode", +}; + static const struct v4l2_ctrl_config ipu3_subdev_ctrl_mode = { .ops = &ipu3_subdev_ctrl_ops, .id = V4L2_CID_INTEL_IPU3_MODE, .name = "IPU3 Pipe Mode", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = IPU3_RUNNING_MODE_VIDEO, - .max = IPU3_RUNNING_MODE_STILL, - .step = 1, + .type = V4L2_CTRL_TYPE_MENU, + .max = ARRAY_SIZE(ipu3_ctrl_mode_strings) - 1, .def = IPU3_RUNNING_MODE_VIDEO, + .qmenu = ipu3_ctrl_mode_strings, }; /******************** Framework registration ********************/ -- cgit v1.2.3-59-g8ed1b From 505ecd35182f1ba006ada118c85deca22b88ebcb Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Fri, 1 Feb 2019 12:23:37 -0500 Subject: media: ipu3-imgu: Remove dead code for NULL check Since ipu3_css_buf_dequeue() never returns NULL, remove the dead code to fix static checker warning: drivers/staging/media/ipu3/ipu3.c:493 imgu_isr_threaded() warn: 'b' is an error pointer or valid Reported-by: Dan Carpenter [Bug report: https://lore.kernel.org/linux-media/20190104122856.GA1169@kadam/] Signed-off-by: Yong Zhi Reviewed-by: Tomasz Figa Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/ipu3.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/ipu3/ipu3.c b/drivers/staging/media/ipu3/ipu3.c index d521b3afb8b1..839d9398f8e9 100644 --- a/drivers/staging/media/ipu3/ipu3.c +++ b/drivers/staging/media/ipu3/ipu3.c @@ -489,12 +489,11 @@ static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr) mutex_unlock(&imgu->lock); } while (PTR_ERR(b) == -EAGAIN); - if (IS_ERR_OR_NULL(b)) { - if (!b || PTR_ERR(b) == -EBUSY) /* All done */ - break; - dev_err(&imgu->pci_dev->dev, - "failed to dequeue buffers (%ld)\n", - PTR_ERR(b)); + if (IS_ERR(b)) { + if (PTR_ERR(b) != -EBUSY) /* All done */ + dev_err(&imgu->pci_dev->dev, + "failed to dequeue buffers (%ld)\n", + PTR_ERR(b)); break; } -- cgit v1.2.3-59-g8ed1b From 81a43d10b8ed0e165bbd1e134ca7261a9608f389 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 22 Dec 2018 06:49:51 -0500 Subject: media: staging: intel-ipu3: fix unsigned comparison with < 0 The comparison css->pipes[pipe].bindex < 0 is always false because bindex is an unsigned int. Fix this by using a signed integer for the comparison. Detected by CoverityScan, CID#1476023 ("Unsigned compared against 0") Fixes: f5f2e4273518 ("media: staging/intel-ipu3: Add css pipeline programming") Signed-off-by: Colin Ian King Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/ipu3-css.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c index 44c55639389a..b9354d2bb692 100644 --- a/drivers/staging/media/ipu3/ipu3-css.c +++ b/drivers/staging/media/ipu3/ipu3-css.c @@ -1751,7 +1751,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, &q[IPU3_CSS_QUEUE_OUT].fmt.mpix; struct v4l2_pix_format_mplane *const vf = &q[IPU3_CSS_QUEUE_VF].fmt.mpix; - int i, s; + int i, s, ret; /* Adjust all formats, get statistics buffer sizes and formats */ for (i = 0; i < IPU3_CSS_QUEUES; i++) { @@ -1826,12 +1826,12 @@ int ipu3_css_fmt_try(struct ipu3_css *css, s = (bds->height - gdc->height) / 2 - FILTER_SIZE; env->height = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; - css->pipes[pipe].bindex = - ipu3_css_find_binary(css, pipe, q, r); - if (css->pipes[pipe].bindex < 0) { + ret = ipu3_css_find_binary(css, pipe, q, r); + if (ret < 0) { dev_err(css->dev, "failed to find suitable binary\n"); return -EINVAL; } + css->pipes[pipe].bindex = ret; dev_dbg(css->dev, "Binary index %d for pipe %d found.", css->pipes[pipe].bindex, pipe); -- cgit v1.2.3-59-g8ed1b From 63635b54e07f646aa1d8e54c96b099162a226f7d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:22 -0500 Subject: media: v4l2-event: keep track of the timestamp in ns Internally use ktime_get_ns() to get the timestamp of the event. Only convert to timespec when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-event.c | 19 +++++++++---------- include/media/v4l2-event.h | 2 ++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c index 481e3c65cf97..c46d14c996fc 100644 --- a/drivers/media/v4l2-core/v4l2-event.c +++ b/drivers/media/v4l2-core/v4l2-event.c @@ -52,6 +52,7 @@ static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event) kev->event.pending = fh->navailable; *event = kev->event; + event->timestamp = ns_to_timespec(kev->ts); kev->sev->first = sev_pos(kev->sev, 1); kev->sev->in_use--; @@ -103,8 +104,8 @@ static struct v4l2_subscribed_event *v4l2_event_subscribed( return NULL; } -static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev, - const struct timespec *ts) +static void __v4l2_event_queue_fh(struct v4l2_fh *fh, + const struct v4l2_event *ev, u64 ts) { struct v4l2_subscribed_event *sev; struct v4l2_kevent *kev; @@ -144,7 +145,7 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e if (copy_payload) kev->event.u = ev->u; kev->event.id = ev->id; - kev->event.timestamp = *ts; + kev->ts = ts; kev->event.sequence = fh->sequence; sev->in_use++; list_add_tail(&kev->list, &fh->available); @@ -158,17 +159,17 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) { struct v4l2_fh *fh; unsigned long flags; - struct timespec timestamp; + u64 ts; if (vdev == NULL) return; - ktime_get_ts(×tamp); + ts = ktime_get_ns(); spin_lock_irqsave(&vdev->fh_lock, flags); list_for_each_entry(fh, &vdev->fh_list, list) - __v4l2_event_queue_fh(fh, ev, ×tamp); + __v4l2_event_queue_fh(fh, ev, ts); spin_unlock_irqrestore(&vdev->fh_lock, flags); } @@ -177,12 +178,10 @@ EXPORT_SYMBOL_GPL(v4l2_event_queue); void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev) { unsigned long flags; - struct timespec timestamp; - - ktime_get_ts(×tamp); + u64 ts = ktime_get_ns(); spin_lock_irqsave(&fh->vdev->fh_lock, flags); - __v4l2_event_queue_fh(fh, ev, ×tamp); + __v4l2_event_queue_fh(fh, ev, ts); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); } EXPORT_SYMBOL_GPL(v4l2_event_queue_fh); diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h index 17833e886e11..c2b6cdc714d2 100644 --- a/include/media/v4l2-event.h +++ b/include/media/v4l2-event.h @@ -34,11 +34,13 @@ struct video_device; * @list: List node for the v4l2_fh->available list. * @sev: Pointer to parent v4l2_subscribed_event. * @event: The event itself. + * @ts: The timestamp of the event. */ struct v4l2_kevent { struct list_head list; struct v4l2_subscribed_event *sev; struct v4l2_event event; + u64 ts; }; /** -- cgit v1.2.3-59-g8ed1b From 15a40b27beb0a85d7f11d747bfc587dbeb69a96c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:23 -0500 Subject: media: videobuf: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_fops.c | 2 +- drivers/media/pci/bt8xx/bttv-driver.c | 8 +++----- drivers/media/pci/cx18/cx18-mailbox.c | 2 +- drivers/media/platform/davinci/vpfe_capture.c | 2 +- drivers/media/platform/fsl-viu.c | 2 +- drivers/media/platform/omap/omap_vout.c | 12 ++++++------ drivers/media/usb/cx231xx/cx231xx-417.c | 4 ++-- drivers/media/usb/cx231xx/cx231xx-vbi.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 2 +- drivers/media/usb/tm6000/tm6000-video.c | 2 +- drivers/media/usb/zr364xx/zr364xx.c | 4 ++-- drivers/media/v4l2-core/videobuf-core.c | 4 ++-- include/media/videobuf-core.h | 2 +- 13 files changed, 23 insertions(+), 25 deletions(-) diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index c790ae264464..be4355a4c126 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -105,7 +105,7 @@ void saa7146_buffer_finish(struct saa7146_dev *dev, } q->curr->vb.state = state; - v4l2_get_timestamp(&q->curr->vb.ts); + q->curr->vb.ts = ktime_get_ns(); wake_up(&q->curr->vb.done); q->curr = NULL; diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index d09785fd37a8..5e769c09dbd0 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3600,9 +3600,7 @@ static void bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, struct bttv_buffer_set *curr, unsigned int state) { - struct timeval ts; - - v4l2_get_timestamp(&ts); + u64 ts = ktime_get_ns(); if (wakeup->top == wakeup->bottom) { if (NULL != wakeup->top && curr->top != wakeup->top) { @@ -3643,7 +3641,7 @@ bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup, if (NULL == wakeup) return; - v4l2_get_timestamp(&wakeup->vb.ts); + wakeup->vb.ts = ktime_get_ns(); wakeup->vb.field_count = btv->field_count; wakeup->vb.state = state; wake_up(&wakeup->vb.done); @@ -3713,7 +3711,7 @@ bttv_irq_wakeup_top(struct bttv *btv) btv->curr.top = NULL; bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); - v4l2_get_timestamp(&wakeup->vb.ts); + wakeup->vb.ts = ktime_get_ns(); wakeup->vb.field_count = btv->field_count; wakeup->vb.state = VIDEOBUF_DONE; wake_up(&wakeup->vb.done); diff --git a/drivers/media/pci/cx18/cx18-mailbox.c b/drivers/media/pci/cx18/cx18-mailbox.c index f66dd63e1994..0ffd2196a980 100644 --- a/drivers/media/pci/cx18/cx18-mailbox.c +++ b/drivers/media/pci/cx18/cx18-mailbox.c @@ -197,7 +197,7 @@ static void cx18_mdl_send_to_videobuf(struct cx18_stream *s, } if (dispatch) { - v4l2_get_timestamp(&vb_buf->vb.ts); + vb_buf->vb.ts = ktime_get_ns(); list_del(&vb_buf->vb.queue); vb_buf->vb.state = VIDEOBUF_DONE; wake_up(&vb_buf->vb.done); diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 9996bab98fe3..26dadbba930f 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -518,7 +518,7 @@ static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) { - v4l2_get_timestamp(&vpfe_dev->cur_frm->ts); + vpfe_dev->cur_frm->ts = ktime_get_ns(); vpfe_dev->cur_frm->state = VIDEOBUF_DONE; vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; wake_up_interruptible(&vpfe_dev->cur_frm->done); diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index ca6d0317ab42..cffebcaacb90 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1090,7 +1090,7 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status) if (waitqueue_active(&buf->vb.done)) { list_del(&buf->vb.queue); - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; wake_up(&buf->vb.done); diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index f447ae3bb465..ff3de2dce5a2 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -513,7 +513,7 @@ static int omapvid_apply_changes(struct omap_vout_device *vout) } static int omapvid_handle_interlace_display(struct omap_vout_device *vout, - unsigned int irqstatus, struct timeval timevalue) + unsigned int irqstatus, u64 ts) { u32 fid; @@ -537,7 +537,7 @@ static int omapvid_handle_interlace_display(struct omap_vout_device *vout, if (vout->cur_frm == vout->next_frm) goto err; - vout->cur_frm->ts = timevalue; + vout->cur_frm->ts = ts; vout->cur_frm->state = VIDEOBUF_DONE; wake_up_interruptible(&vout->cur_frm->done); vout->cur_frm = vout->next_frm; @@ -557,7 +557,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) int ret, fid, mgr_id; u32 addr, irq; struct omap_overlay *ovl; - struct timeval timevalue; + u64 ts; struct omapvideo_info *ovid; struct omap_dss_device *cur_display; struct omap_vout_device *vout = (struct omap_vout_device *)arg; @@ -577,7 +577,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) return; spin_lock(&vout->vbq_lock); - v4l2_get_timestamp(&timevalue); + ts = ktime_get_ns(); switch (cur_display->type) { case OMAP_DISPLAY_TYPE_DSI: @@ -595,7 +595,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) break; case OMAP_DISPLAY_TYPE_VENC: fid = omapvid_handle_interlace_display(vout, irqstatus, - timevalue); + ts); if (!fid) goto vout_isr_err; break; @@ -608,7 +608,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) } if (!vout->first_int && (vout->cur_frm != vout->next_frm)) { - vout->cur_frm->ts = timevalue; + vout->cur_frm->ts = ts; vout->cur_frm->state = VIDEOBUF_DONE; wake_up_interruptible(&vout->cur_frm->done); vout->cur_frm = vout->next_frm; diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 1c48c497bd6a..0f8ae81f4820 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1316,7 +1316,7 @@ static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *ur buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); list_del(&buf->vb.queue); wake_up(&buf->vb.done); dma_q->mpeg_buffer_completed = 0; @@ -1347,7 +1347,7 @@ static void buffer_filled(char *data, int len, struct urb *urb, memcpy(vbuf, data, len); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); list_del(&buf->vb.queue); wake_up(&buf->vb.done); } diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index 10b2eb7338ad..d16b73c04445 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -528,7 +528,7 @@ static inline void vbi_buffer_filled(struct cx231xx *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); dev->vbi_mode.bulk_ctl.buf = NULL; diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 0d451c4ea3b9..aebbaf9d92a6 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -182,7 +182,7 @@ static inline void buffer_filled(struct cx231xx *dev, cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); if (dev->USE_ISO) dev->video_mode.isoc_ctl.buf = NULL; diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index ee7b5318b351..5127be71dd03 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -106,7 +106,7 @@ static inline void buffer_filled(struct tm6000_core *dev, dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index ab35554cbffa..51aad2cf742f 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -521,7 +521,7 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, /* tell v4l buffer was filled */ buf->vb.field_count = cam->frame_count * 2; - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); buf->vb.state = VIDEOBUF_DONE; } @@ -549,7 +549,7 @@ static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) goto unlock; } list_del(&buf->vb.queue); - v4l2_get_timestamp(&buf->vb.ts); + buf->vb.ts = ktime_get_ns(); DBG("[%p/%d] wakeup\n", buf, buf->vb.i); zr364xx_fillbuff(cam, buf, jpgsize); wake_up(&buf->vb.done); diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c index 7491b337002c..d1bcfa91aaf8 100644 --- a/drivers/media/v4l2-core/videobuf-core.c +++ b/drivers/media/v4l2-core/videobuf-core.c @@ -367,7 +367,7 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, } b->field = vb->field; - b->timestamp = vb->ts; + b->timestamp = ns_to_timeval(vb->ts); b->bytesused = vb->size; b->sequence = vb->field_count >> 1; } @@ -581,7 +581,7 @@ int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) || q->type == V4L2_BUF_TYPE_SDR_OUTPUT) { buf->size = b->bytesused; buf->field = b->field; - buf->ts = b->timestamp; + buf->ts = v4l2_timeval_to_ns(&b->timestamp); } break; case V4L2_MEMORY_USERPTR: diff --git a/include/media/videobuf-core.h b/include/media/videobuf-core.h index 60a664febba0..5684dc6f0d0d 100644 --- a/include/media/videobuf-core.h +++ b/include/media/videobuf-core.h @@ -80,7 +80,7 @@ struct videobuf_buffer { struct list_head queue; wait_queue_head_t done; unsigned int field_count; - struct timeval ts; + u64 ts; /* Memory type */ enum v4l2_memory memory; -- cgit v1.2.3-59-g8ed1b From cb7130ea67c4319bbf2d810f245ee6f89bbf6ae7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:24 -0500 Subject: media: meye: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/meye/meye.c | 8 ++++---- drivers/media/pci/meye/meye.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index bd870e60c32b..896d2d856795 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -805,7 +805,7 @@ again: mchip_hsize() * mchip_vsize() * 2); meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); + meye.grab_buffer[reqnr].ts = ktime_get_ns(); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); @@ -826,7 +826,7 @@ again: size); meye.grab_buffer[reqnr].size = size; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); + meye.grab_buffer[reqnr].ts = ktime_get_ns(); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); @@ -1283,7 +1283,7 @@ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->flags |= V4L2_BUF_FLAG_DONE; buf->field = V4L2_FIELD_NONE; - buf->timestamp = meye.grab_buffer[index].timestamp; + buf->timestamp = ns_to_timeval(meye.grab_buffer[index].ts); buf->sequence = meye.grab_buffer[index].sequence; buf->memory = V4L2_MEMORY_MMAP; buf->m.offset = index * gbufsize; @@ -1349,7 +1349,7 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->bytesused = meye.grab_buffer[reqnr].size; buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; - buf->timestamp = meye.grab_buffer[reqnr].timestamp; + buf->timestamp = ns_to_timeval(meye.grab_buffer[reqnr].ts); buf->sequence = meye.grab_buffer[reqnr].sequence; buf->memory = V4L2_MEMORY_MMAP; buf->m.offset = reqnr * gbufsize; diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h index c4a8a5fe040c..0af868eb6210 100644 --- a/drivers/media/pci/meye/meye.h +++ b/drivers/media/pci/meye/meye.h @@ -277,7 +277,7 @@ struct meye_grab_buffer { int state; /* state of buffer */ unsigned long size; /* size of jpg frame */ - struct timeval timestamp; /* timestamp */ + u64 ts; /* timestamp */ unsigned long sequence; /* sequence number */ }; -- cgit v1.2.3-59-g8ed1b From 597f8e9c13644e4fcf4c1d01c2317c78b2e2df92 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:25 -0500 Subject: media: cpia2: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cpia2/cpia2.h | 2 +- drivers/media/usb/cpia2/cpia2_usb.c | 2 +- drivers/media/usb/cpia2/cpia2_v4l.c | 11 +++-------- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/media/usb/cpia2/cpia2.h b/drivers/media/usb/cpia2/cpia2.h index ab238ac8bfc0..d0a464882510 100644 --- a/drivers/media/usb/cpia2/cpia2.h +++ b/drivers/media/usb/cpia2/cpia2.h @@ -350,7 +350,7 @@ struct cpia2_sbuf { }; struct framebuf { - struct timeval timestamp; + u64 ts; unsigned long seq; int num; int length; diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c index a771e0a52610..e5d8dee38fe4 100644 --- a/drivers/media/usb/cpia2/cpia2_usb.c +++ b/drivers/media/usb/cpia2/cpia2_usb.c @@ -324,7 +324,7 @@ static void cpia2_usb_complete(struct urb *urb) continue; } DBG("Start of frame pattern found\n"); - v4l2_get_timestamp(&cam->workbuff->timestamp); + cam->workbuff->ts = ktime_get_ns(); cam->workbuff->seq = cam->frame_count++; cam->workbuff->data[0] = 0xFF; cam->workbuff->data[1] = 0xD8; diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index 748739c2b8b2..95c0bd4a19dc 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -833,7 +833,7 @@ static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) break; case FRAME_READY: buf->bytesused = cam->buffers[buf->index].length; - buf->timestamp = cam->buffers[buf->index].timestamp; + buf->timestamp = ns_to_timeval(cam->buffers[buf->index].ts); buf->sequence = cam->buffers[buf->index].seq; buf->flags = V4L2_BUF_FLAG_DONE; break; @@ -889,12 +889,7 @@ static int find_earliest_filled_buffer(struct camera_data *cam) found = i; } else { /* find which buffer is earlier */ - struct timeval *tv1, *tv2; - tv1 = &cam->buffers[i].timestamp; - tv2 = &cam->buffers[found].timestamp; - if(tv1->tv_sec < tv2->tv_sec || - (tv1->tv_sec == tv2->tv_sec && - tv1->tv_usec < tv2->tv_usec)) + if (cam->buffers[i].ts < cam->buffers[found].ts) found = i; } } @@ -945,7 +940,7 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; - buf->timestamp = cam->buffers[buf->index].timestamp; + buf->timestamp = ns_to_timeval(cam->buffers[buf->index].ts); buf->sequence = cam->buffers[buf->index].seq; buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; buf->length = cam->frame_size; -- cgit v1.2.3-59-g8ed1b From bbd217a44a7024018abdd2aee63c991ad3b01e4b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:26 -0500 Subject: media: stkwebcam: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stkwebcam/stk-webcam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index b8ec74d98e8d..03f5e12b13a5 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -1144,7 +1144,7 @@ static int stk_vidioc_dqbuf(struct file *filp, sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; sbuf->v4lbuf.sequence = ++dev->sequence; - v4l2_get_timestamp(&sbuf->v4lbuf.timestamp); + sbuf->v4lbuf.timestamp = ns_to_timeval(ktime_get_ns()); *buf = sbuf->v4lbuf; return 0; -- cgit v1.2.3-59-g8ed1b From 459d2a5d1e696b6fb029df1be041380daff94032 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:27 -0500 Subject: media: usbvision: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-core.c | 2 +- drivers/media/usb/usbvision/usbvision-video.c | 4 ++-- drivers/media/usb/usbvision/usbvision.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index 31e0e98d6daf..2b843a7b27a4 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -1160,7 +1160,7 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) if (newstate == parse_state_next_frame) { frame->grabstate = frame_state_done; - v4l2_get_timestamp(&(frame->timestamp)); + frame->ts = ktime_get_ns(); frame->sequence = usbvision->frame_num; spin_lock_irqsave(&usbvision->queue_lock, lock_flags); diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index dd2ff8ed6c6a..e611052ebf59 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -706,7 +706,7 @@ static int vidioc_querybuf(struct file *file, vb->length = usbvision->curwidth * usbvision->curheight * usbvision->palette.bytes_per_pixel; - vb->timestamp = usbvision->frame[vb->index].timestamp; + vb->timestamp = ns_to_timeval(usbvision->frame[vb->index].ts); vb->sequence = usbvision->frame[vb->index].sequence; return 0; } @@ -775,7 +775,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb) V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vb->index = f->index; vb->sequence = f->sequence; - vb->timestamp = f->timestamp; + vb->timestamp = ns_to_timeval(f->ts); vb->field = V4L2_FIELD_NONE; vb->bytesused = f->scanlength; diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h index 017e7baf5747..d55088b4fd63 100644 --- a/drivers/media/usb/usbvision/usbvision.h +++ b/drivers/media/usb/usbvision/usbvision.h @@ -316,7 +316,7 @@ struct usbvision_frame { long bytes_read; /* amount of scanlength that has been read from data */ struct usbvision_v4l2_format_st v4l2_format; /* format the user needs*/ int v4l2_linesize; /* bytes for one videoline*/ - struct timeval timestamp; + u64 ts; int sequence; /* How many video frames we send to user */ }; -- cgit v1.2.3-59-g8ed1b From 7b6b9a8c36b9264b8c31059506119a0aac1413ba Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:28 -0500 Subject: media: zoran: use u64 for the timestamp internally Just like vb2 does, use u64 internally to store the timestamps of the buffers. Only convert to timeval when interfacing with userspace. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/zoran/zoran.h | 2 +- drivers/staging/media/zoran/zoran_device.c | 4 ++-- drivers/staging/media/zoran/zoran_driver.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/zoran/zoran.h b/drivers/staging/media/zoran/zoran.h index 9bb3c21aa275..e84fb604a689 100644 --- a/drivers/staging/media/zoran/zoran.h +++ b/drivers/staging/media/zoran/zoran.h @@ -35,7 +35,7 @@ struct zoran_sync { unsigned long frame; /* number of buffer that has been free'd */ unsigned long length; /* number of code bytes in buffer (capture only) */ unsigned long seq; /* frame sequence number */ - struct timeval timestamp; /* timestamp */ + u64 ts; /* timestamp */ }; diff --git a/drivers/staging/media/zoran/zoran_device.c b/drivers/staging/media/zoran/zoran_device.c index 40adceebca7e..d393e7b8aeda 100644 --- a/drivers/staging/media/zoran/zoran_device.c +++ b/drivers/staging/media/zoran/zoran_device.c @@ -1151,7 +1151,7 @@ zoran_reap_stat_com (struct zoran *zr) } frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; buffer = &zr->jpg_buffers.buffer[frame]; - v4l2_get_timestamp(&buffer->bs.timestamp); + buffer->bs.ts = ktime_get_ns(); if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { buffer->bs.length = (stat_com & 0x7fffff) >> 1; @@ -1389,7 +1389,7 @@ zoran_irq (int irq, zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE; zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq; - v4l2_get_timestamp(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); + zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.ts = ktime_get_ns(); zr->v4l_grab_frame = NO_GRAB_ACTIVE; zr->v4l_pend_tail++; } diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c index 27c76e2eeb41..04f88f9d6bb4 100644 --- a/drivers/staging/media/zoran/zoran_driver.c +++ b/drivers/staging/media/zoran/zoran_driver.c @@ -1354,7 +1354,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh, fh->buffers.buffer[num].state == BUZ_STATE_USER) { buf->sequence = fh->buffers.buffer[num].bs.seq; buf->flags |= V4L2_BUF_FLAG_DONE; - buf->timestamp = fh->buffers.buffer[num].bs.timestamp; + buf->timestamp = ns_to_timeval(fh->buffers.buffer[num].bs.ts); } else { buf->flags |= V4L2_BUF_FLAG_QUEUED; } @@ -1388,7 +1388,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh, if (fh->buffers.buffer[num].state == BUZ_STATE_DONE || fh->buffers.buffer[num].state == BUZ_STATE_USER) { buf->sequence = fh->buffers.buffer[num].bs.seq; - buf->timestamp = fh->buffers.buffer[num].bs.timestamp; + buf->timestamp = ns_to_timeval(fh->buffers.buffer[num].bs.ts); buf->bytesused = fh->buffers.buffer[num].bs.length; buf->flags |= V4L2_BUF_FLAG_DONE; } else { -- cgit v1.2.3-59-g8ed1b From 276c1f066bdaaed6fe82ed231e1eff2f41c34882 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jan 2019 08:32:29 -0500 Subject: media: v4l2-common: drop v4l2_get_timestamp This function is no longer used, so drop it. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-common.c | 10 ---------- include/media/v4l2-common.h | 9 --------- 2 files changed, 19 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 50763fb42a1b..663730f088cd 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -398,16 +398,6 @@ __v4l2_find_nearest_size(const void *array, size_t array_size, } EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size); -void v4l2_get_timestamp(struct timeval *tv) -{ - struct timespec ts; - - ktime_get_ts(&ts); - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; -} -EXPORT_SYMBOL_GPL(v4l2_get_timestamp); - int v4l2_g_parm_cap(struct video_device *vdev, struct v4l2_subdev *sd, struct v4l2_streamparm *a) { diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 0c511ed8ffb0..2b93cb281fa5 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -361,15 +361,6 @@ __v4l2_find_nearest_size(const void *array, size_t array_size, size_t entry_size, size_t width_offset, size_t height_offset, s32 width, s32 height); -/** - * v4l2_get_timestamp - helper routine to get a timestamp to be used when - * filling streaming metadata. Internally, it uses ktime_get_ts(), - * which is the recommended way to get it. - * - * @tv: pointer to &struct timeval to be filled. - */ -void v4l2_get_timestamp(struct timeval *tv); - /** * v4l2_g_parm_cap - helper routine for vidioc_g_parm to fill this in by * calling the g_frame_interval op of the given subdev. It only works -- cgit v1.2.3-59-g8ed1b From adc589d2a20808fb99d46a78175cd023f2040338 Mon Sep 17 00:00:00 2001 From: "Lucas A. M. Magalhães" Date: Mon, 21 Jan 2019 20:05:01 -0500 Subject: media: vimc: Add vimc-streamer for stream control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a linear pipeline logic for the stream control. It's created by walking backwards on the entity graph. When the stream starts it will simply loop through the pipeline calling the respective process_frame function of each entity. Fixes: f2fe89061d797 ("vimc: Virtual Media Controller core, capture and sensor") Cc: stable@vger.kernel.org # for v4.20 Signed-off-by: Lucas A. M. Magalhães Acked-by: Helen Koike Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: fixed small space-after-tab issue in the patch] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/Makefile | 3 +- drivers/media/platform/vimc/vimc-capture.c | 18 +-- drivers/media/platform/vimc/vimc-common.c | 35 ------ drivers/media/platform/vimc/vimc-common.h | 15 +-- drivers/media/platform/vimc/vimc-debayer.c | 26 +--- drivers/media/platform/vimc/vimc-scaler.c | 28 +---- drivers/media/platform/vimc/vimc-sensor.c | 56 ++------- drivers/media/platform/vimc/vimc-streamer.c | 188 ++++++++++++++++++++++++++++ drivers/media/platform/vimc/vimc-streamer.h | 38 ++++++ 9 files changed, 260 insertions(+), 147 deletions(-) create mode 100644 drivers/media/platform/vimc/vimc-streamer.c create mode 100644 drivers/media/platform/vimc/vimc-streamer.h diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile index 4b2e3de7856e..c4fc8e7d365a 100644 --- a/drivers/media/platform/vimc/Makefile +++ b/drivers/media/platform/vimc/Makefile @@ -5,6 +5,7 @@ vimc_common-objs := vimc-common.o vimc_debayer-objs := vimc-debayer.o vimc_scaler-objs := vimc-scaler.o vimc_sensor-objs := vimc-sensor.o +vimc_streamer-objs := vimc-streamer.o obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc_common.o vimc-debayer.o \ - vimc_scaler.o vimc_sensor.o + vimc_scaler.o vimc_sensor.o vimc_streamer.o diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index aaeddf24b042..93837d9eecd2 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -24,6 +24,7 @@ #include #include "vimc-common.h" +#include "vimc-streamer.h" #define VIMC_CAP_DRV_NAME "vimc-capture" @@ -44,7 +45,7 @@ struct vimc_cap_device { spinlock_t qlock; struct mutex lock; u32 sequence; - struct media_pipeline pipe; + struct vimc_stream stream; }; static const struct v4l2_pix_format fmt_default = { @@ -248,14 +249,13 @@ static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count) vcap->sequence = 0; /* Start the media pipeline */ - ret = media_pipeline_start(entity, &vcap->pipe); + ret = media_pipeline_start(entity, &vcap->stream.pipe); if (ret) { vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); return ret; } - /* Enable streaming from the pipe */ - ret = vimc_pipeline_s_stream(&vcap->vdev.entity, 1); + ret = vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 1); if (ret) { media_pipeline_stop(entity); vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); @@ -273,8 +273,7 @@ static void vimc_cap_stop_streaming(struct vb2_queue *vq) { struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); - /* Disable streaming from the pipe */ - vimc_pipeline_s_stream(&vcap->vdev.entity, 0); + vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 0); /* Stop the media pipeline */ media_pipeline_stop(&vcap->vdev.entity); @@ -355,8 +354,8 @@ static void vimc_cap_comp_unbind(struct device *comp, struct device *master, kfree(vcap); } -static void vimc_cap_process_frame(struct vimc_ent_device *ved, - struct media_pad *sink, const void *frame) +static void *vimc_cap_process_frame(struct vimc_ent_device *ved, + const void *frame) { struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, ved); @@ -370,7 +369,7 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved, typeof(*vimc_buf), list); if (!vimc_buf) { spin_unlock(&vcap->qlock); - return; + return ERR_PTR(-EAGAIN); } /* Remove this entry from the list */ @@ -391,6 +390,7 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved, vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0, vcap->format.sizeimage); vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE); + return NULL; } static int vimc_cap_comp_bind(struct device *comp, struct device *master, diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 867e24dbd6b5..c1a74bb2df58 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -207,41 +207,6 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat) } EXPORT_SYMBOL_GPL(vimc_pix_map_by_pixelformat); -int vimc_propagate_frame(struct media_pad *src, const void *frame) -{ - struct media_link *link; - - if (!(src->flags & MEDIA_PAD_FL_SOURCE)) - return -EINVAL; - - /* Send this frame to all sink pads that are direct linked */ - list_for_each_entry(link, &src->entity->links, list) { - if (link->source == src && - (link->flags & MEDIA_LNK_FL_ENABLED)) { - struct vimc_ent_device *ved = NULL; - struct media_entity *entity = link->sink->entity; - - if (is_media_entity_v4l2_subdev(entity)) { - struct v4l2_subdev *sd = - container_of(entity, struct v4l2_subdev, - entity); - ved = v4l2_get_subdevdata(sd); - } else if (is_media_entity_v4l2_video_device(entity)) { - struct video_device *vdev = - container_of(entity, - struct video_device, - entity); - ved = video_get_drvdata(vdev); - } - if (ved && ved->process_frame) - ved->process_frame(ved, link->sink, frame); - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(vimc_propagate_frame); - /* Helper function to allocate and initialize pads */ struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) { diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index f491c33c7c14..84539430b5e7 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -115,23 +115,12 @@ struct vimc_pix_map { struct vimc_ent_device { struct media_entity *ent; struct media_pad *pads; - void (*process_frame)(struct vimc_ent_device *ved, - struct media_pad *sink, const void *frame); + void * (*process_frame)(struct vimc_ent_device *ved, + const void *frame); void (*vdev_get_format)(struct vimc_ent_device *ved, struct v4l2_pix_format *fmt); }; -/** - * vimc_propagate_frame - propagate a frame through the topology - * - * @src: the source pad where the frame is being originated - * @frame: the frame to be propagated - * - * This function will call the process_frame callback from the vimc_ent_device - * struct of the nodes directly connected to the @src pad - */ -int vimc_propagate_frame(struct media_pad *src, const void *frame); - /** * vimc_pads_init - initialize pads * diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index 77887f66f323..7d77c63b99d2 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -321,7 +321,6 @@ static void vimc_deb_set_rgb_mbus_fmt_rgb888_1x24(struct vimc_deb_device *vdeb, static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable) { struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); - int ret; if (enable) { const struct vimc_pix_map *vpix; @@ -351,22 +350,10 @@ static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable) if (!vdeb->src_frame) return -ENOMEM; - /* Turn the stream on in the subdevices directly connected */ - ret = vimc_pipeline_s_stream(&vdeb->sd.entity, 1); - if (ret) { - vfree(vdeb->src_frame); - vdeb->src_frame = NULL; - return ret; - } } else { if (!vdeb->src_frame) return 0; - /* Disable streaming from the pipe */ - ret = vimc_pipeline_s_stream(&vdeb->sd.entity, 0); - if (ret) - return ret; - vfree(vdeb->src_frame); vdeb->src_frame = NULL; } @@ -480,9 +467,8 @@ static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb, } } -static void vimc_deb_process_frame(struct vimc_ent_device *ved, - struct media_pad *sink, - const void *sink_frame) +static void *vimc_deb_process_frame(struct vimc_ent_device *ved, + const void *sink_frame) { struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device, ved); @@ -491,7 +477,7 @@ static void vimc_deb_process_frame(struct vimc_ent_device *ved, /* If the stream in this node is not active, just return */ if (!vdeb->src_frame) - return; + return ERR_PTR(-EINVAL); for (i = 0; i < vdeb->sink_fmt.height; i++) for (j = 0; j < vdeb->sink_fmt.width; j++) { @@ -499,12 +485,8 @@ static void vimc_deb_process_frame(struct vimc_ent_device *ved, vdeb->set_rgb_src(vdeb, i, j, rgb); } - /* Propagate the frame through all source pads */ - for (i = 1; i < vdeb->sd.entity.num_pads; i++) { - struct media_pad *pad = &vdeb->sd.entity.pads[i]; + return vdeb->src_frame; - vimc_propagate_frame(pad, vdeb->src_frame); - } } static void vimc_deb_comp_unbind(struct device *comp, struct device *master, diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index b0952ee86296..39b2a73dfcc1 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -217,7 +217,6 @@ static const struct v4l2_subdev_pad_ops vimc_sca_pad_ops = { static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable) { struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); - int ret; if (enable) { const struct vimc_pix_map *vpix; @@ -245,22 +244,10 @@ static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable) if (!vsca->src_frame) return -ENOMEM; - /* Turn the stream on in the subdevices directly connected */ - ret = vimc_pipeline_s_stream(&vsca->sd.entity, 1); - if (ret) { - vfree(vsca->src_frame); - vsca->src_frame = NULL; - return ret; - } } else { if (!vsca->src_frame) return 0; - /* Disable streaming from the pipe */ - ret = vimc_pipeline_s_stream(&vsca->sd.entity, 0); - if (ret) - return ret; - vfree(vsca->src_frame); vsca->src_frame = NULL; } @@ -346,26 +333,19 @@ static void vimc_sca_fill_src_frame(const struct vimc_sca_device *const vsca, vimc_sca_scale_pix(vsca, i, j, sink_frame); } -static void vimc_sca_process_frame(struct vimc_ent_device *ved, - struct media_pad *sink, - const void *sink_frame) +static void *vimc_sca_process_frame(struct vimc_ent_device *ved, + const void *sink_frame) { struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device, ved); - unsigned int i; /* If the stream in this node is not active, just return */ if (!vsca->src_frame) - return; + return ERR_PTR(-EINVAL); vimc_sca_fill_src_frame(vsca, sink_frame); - /* Propagate the frame through all source pads */ - for (i = 1; i < vsca->sd.entity.num_pads; i++) { - struct media_pad *pad = &vsca->sd.entity.pads[i]; - - vimc_propagate_frame(pad, vsca->src_frame); - } + return vsca->src_frame; }; static void vimc_sca_comp_unbind(struct device *comp, struct device *master, diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 32ca9c6172b1..93961a1e694f 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -16,8 +16,6 @@ */ #include -#include -#include #include #include #include @@ -201,38 +199,27 @@ static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = { .set_fmt = vimc_sen_set_fmt, }; -static int vimc_sen_tpg_thread(void *data) +static void *vimc_sen_process_frame(struct vimc_ent_device *ved, + const void *sink_frame) { - struct vimc_sen_device *vsen = data; - unsigned int i; - - set_freezable(); - set_current_state(TASK_UNINTERRUPTIBLE); - - for (;;) { - try_to_freeze(); - if (kthread_should_stop()) - break; - - tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); + struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, + ved); + const struct vimc_pix_map *vpix; + unsigned int frame_size; - /* Send the frame to all source pads */ - for (i = 0; i < vsen->sd.entity.num_pads; i++) - vimc_propagate_frame(&vsen->sd.entity.pads[i], - vsen->frame); + /* Calculate the frame size */ + vpix = vimc_pix_map_by_code(vsen->mbus_format.code); + frame_size = vsen->mbus_format.width * vpix->bpp * + vsen->mbus_format.height; - /* 60 frames per second */ - schedule_timeout(HZ/60); - } - - return 0; + tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); + return vsen->frame; } static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) { struct vimc_sen_device *vsen = container_of(sd, struct vimc_sen_device, sd); - int ret; if (enable) { const struct vimc_pix_map *vpix; @@ -258,26 +245,8 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) /* configure the test pattern generator */ vimc_sen_tpg_s_format(vsen); - /* Initialize the image generator thread */ - vsen->kthread_sen = kthread_run(vimc_sen_tpg_thread, vsen, - "%s-sen", vsen->sd.v4l2_dev->name); - if (IS_ERR(vsen->kthread_sen)) { - dev_err(vsen->dev, "%s: kernel_thread() failed\n", - vsen->sd.name); - vfree(vsen->frame); - vsen->frame = NULL; - return PTR_ERR(vsen->kthread_sen); - } } else { - if (!vsen->kthread_sen) - return 0; - - /* Stop image generator */ - ret = kthread_stop(vsen->kthread_sen); - if (ret) - return ret; - vsen->kthread_sen = NULL; vfree(vsen->frame); vsen->frame = NULL; return 0; @@ -413,6 +382,7 @@ static int vimc_sen_comp_bind(struct device *comp, struct device *master, if (ret) goto err_free_hdl; + vsen->ved.process_frame = vimc_sen_process_frame; dev_set_drvdata(comp, &vsen->ved); vsen->dev = comp; diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c new file mode 100644 index 000000000000..fcc897fb247b --- /dev/null +++ b/drivers/media/platform/vimc/vimc-streamer.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * vimc-streamer.c Virtual Media Controller Driver + * + * Copyright (C) 2018 Lucas A. M. Magalhães + * + */ + +#include +#include +#include +#include + +#include "vimc-streamer.h" + +/** + * vimc_get_source_entity - get the entity connected with the first sink pad + * + * @ent: reference media_entity + * + * Helper function that returns the media entity containing the source pad + * linked with the first sink pad from the given media entity pad list. + */ +static struct media_entity *vimc_get_source_entity(struct media_entity *ent) +{ + struct media_pad *pad; + int i; + + for (i = 0; i < ent->num_pads; i++) { + if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE) + continue; + pad = media_entity_remote_pad(&ent->pads[i]); + return pad ? pad->entity : NULL; + } + return NULL; +} + +/* + * vimc_streamer_pipeline_terminate - Disable stream in all ved in stream + * + * @stream: the pointer to the stream structure with the pipeline to be + * disabled. + * + * Calls s_stream to disable the stream in each entity of the pipeline + * + */ +static void vimc_streamer_pipeline_terminate(struct vimc_stream *stream) +{ + struct media_entity *entity; + struct v4l2_subdev *sd; + + while (stream->pipe_size) { + stream->pipe_size--; + entity = stream->ved_pipeline[stream->pipe_size]->ent; + entity = vimc_get_source_entity(entity); + stream->ved_pipeline[stream->pipe_size] = NULL; + + if (!is_media_entity_v4l2_subdev(entity)) + continue; + + sd = media_entity_to_v4l2_subdev(entity); + v4l2_subdev_call(sd, video, s_stream, 0); + } +} + +/* + * vimc_streamer_pipeline_init - initializes the stream structure + * + * @stream: the pointer to the stream structure to be initialized + * @ved: the pointer to the vimc entity initializing the stream + * + * Initializes the stream structure. Walks through the entity graph to + * construct the pipeline used later on the streamer thread. + * Calls s_stream to enable stream in all entities of the pipeline. + */ +static int vimc_streamer_pipeline_init(struct vimc_stream *stream, + struct vimc_ent_device *ved) +{ + struct media_entity *entity; + struct video_device *vdev; + struct v4l2_subdev *sd; + int ret = 0; + + stream->pipe_size = 0; + while (stream->pipe_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) { + if (!ved) { + vimc_streamer_pipeline_terminate(stream); + return -EINVAL; + } + stream->ved_pipeline[stream->pipe_size++] = ved; + + entity = vimc_get_source_entity(ved->ent); + /* Check if the end of the pipeline was reached*/ + if (!entity) + return 0; + + if (is_media_entity_v4l2_subdev(entity)) { + sd = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(sd, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) { + vimc_streamer_pipeline_terminate(stream); + return ret; + } + ved = v4l2_get_subdevdata(sd); + } else { + vdev = container_of(entity, + struct video_device, + entity); + ved = video_get_drvdata(vdev); + } + } + + vimc_streamer_pipeline_terminate(stream); + return -EINVAL; +} + +static int vimc_streamer_thread(void *data) +{ + struct vimc_stream *stream = data; + int i; + + set_freezable(); + set_current_state(TASK_UNINTERRUPTIBLE); + + for (;;) { + try_to_freeze(); + if (kthread_should_stop()) + break; + + for (i = stream->pipe_size - 1; i >= 0; i--) { + stream->frame = stream->ved_pipeline[i]->process_frame( + stream->ved_pipeline[i], + stream->frame); + if (!stream->frame) + break; + if (IS_ERR(stream->frame)) + break; + } + //wait for 60hz + schedule_timeout(HZ / 60); + } + + return 0; +} + +int vimc_streamer_s_stream(struct vimc_stream *stream, + struct vimc_ent_device *ved, + int enable) +{ + int ret; + + if (!stream || !ved) + return -EINVAL; + + if (enable) { + if (stream->kthread) + return 0; + + ret = vimc_streamer_pipeline_init(stream, ved); + if (ret) + return ret; + + stream->kthread = kthread_run(vimc_streamer_thread, stream, + "vimc-streamer thread"); + + if (IS_ERR(stream->kthread)) + return PTR_ERR(stream->kthread); + + } else { + if (!stream->kthread) + return 0; + + ret = kthread_stop(stream->kthread); + if (ret) + return ret; + + stream->kthread = NULL; + + vimc_streamer_pipeline_terminate(stream); + } + + return 0; +} +EXPORT_SYMBOL_GPL(vimc_streamer_s_stream); + +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Streamer"); +MODULE_AUTHOR("Lucas A. M. Magalhães "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vimc/vimc-streamer.h b/drivers/media/platform/vimc/vimc-streamer.h new file mode 100644 index 000000000000..752af2e2d5a2 --- /dev/null +++ b/drivers/media/platform/vimc/vimc-streamer.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * vimc-streamer.h Virtual Media Controller Driver + * + * Copyright (C) 2018 Lucas A. M. Magalhães + * + */ + +#ifndef _VIMC_STREAMER_H_ +#define _VIMC_STREAMER_H_ + +#include + +#include "vimc-common.h" + +#define VIMC_STREAMER_PIPELINE_MAX_SIZE 16 + +struct vimc_stream { + struct media_pipeline pipe; + struct vimc_ent_device *ved_pipeline[VIMC_STREAMER_PIPELINE_MAX_SIZE]; + unsigned int pipe_size; + u8 *frame; + struct task_struct *kthread; +}; + +/** + * vimc_streamer_s_streamer - start/stop the stream + * + * @stream: the pointer to the stream to start or stop + * @ved: The last entity of the streamer pipeline + * @enable: any non-zero number start the stream, zero stop + * + */ +int vimc_streamer_s_stream(struct vimc_stream *stream, + struct vimc_ent_device *ved, + int enable); + +#endif //_VIMC_STREAMER_H_ -- cgit v1.2.3-59-g8ed1b From a442940534b77f3892dd629c12b0d01e65d4f314 Mon Sep 17 00:00:00 2001 From: André Almeida Date: Mon, 4 Feb 2019 12:37:33 -0500 Subject: media: vivid: add vertical down sampling to imagesize calc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To correctly set the size of the image in a plane, it's needed to divide the height of image by the vertical down sampling factor. This was only happening in vivid_try_fmt_vid_cap(), but now it applied in others sizeimage calculations as well. Signed-off-by: André Almeida Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-vid-cap.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index c059fc12668a..52eeda624d7e 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -124,7 +124,8 @@ static int vid_cap_queue_setup(struct vb2_queue *vq, } } else { for (p = 0; p < buffers; p++) - sizes[p] = tpg_g_line_width(&dev->tpg, p) * h + + sizes[p] = (tpg_g_line_width(&dev->tpg, p) * h) / + dev->fmt_cap->vdownsampling[p] + dev->fmt_cap->data_offset[p]; } @@ -161,7 +162,9 @@ static int vid_cap_buf_prepare(struct vb2_buffer *vb) return -EINVAL; } for (p = 0; p < buffers; p++) { - size = tpg_g_line_width(&dev->tpg, p) * dev->fmt_cap_rect.height + + size = (tpg_g_line_width(&dev->tpg, p) * + dev->fmt_cap_rect.height) / + dev->fmt_cap->vdownsampling[p] + dev->fmt_cap->data_offset[p]; if (vb2_plane_size(vb, p) < size) { @@ -545,7 +548,8 @@ int vivid_g_fmt_vid_cap(struct file *file, void *priv, for (p = 0; p < mp->num_planes; p++) { mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p); mp->plane_fmt[p].sizeimage = - tpg_g_line_width(&dev->tpg, p) * mp->height + + (tpg_g_line_width(&dev->tpg, p) * mp->height) / + dev->fmt_cap->vdownsampling[p] + dev->fmt_cap->data_offset[p]; } return 0; -- cgit v1.2.3-59-g8ed1b From 22f05d646df9fdb4f1ff19582f17d350665c2ea9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 7 Feb 2019 12:28:19 -0500 Subject: media: vim2m: don't use curr_ctx->dev before checking It seems that it is possible that dev to be null, as there's a warning printing: "Instance released before the end of transaction" Solves this warning: drivers/media/platform/vim2m.c: drivers/media/platform/vim2m.c:525 device_work() warn: variable dereferenced before check 'curr_ctx' (see line 523) Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index bfa1a2a16009..bd125ad34343 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -520,13 +520,14 @@ static void device_work(struct work_struct *w) unsigned long flags; curr_ctx = container_of(w, struct vim2m_ctx, work_run.work); - vim2m_dev = curr_ctx->dev; if (NULL == curr_ctx) { pr_err("Instance released before the end of transaction\n"); return; } + vim2m_dev = curr_ctx->dev; + src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); -- cgit v1.2.3-59-g8ed1b From 9bcb830bfceaac456ff8206e3ed74210e133613e Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Sun, 20 Jan 2019 14:13:31 -0500 Subject: media: imx: Validate frame intervals before setting In the .s_frame_interval() subdev op, don't accept or set a frame interval with a zero numerator or denominator. This fixes a v4l2-compliance failure: fail: v4l2-test-formats.cpp(1146): cap->timeperframe.numerator == 0 || cap->timeperframe.denominator == 0 test VIDIOC_G/S_PARM: FAIL Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prp.c | 9 +++++++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 9 +++++++-- drivers/staging/media/imx/imx-media-csi.c | 5 ++++- drivers/staging/media/imx/imx-media-vdic.c | 5 ++++- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 98923fc844ce..a2bb5c702d74 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -422,9 +422,14 @@ static int prp_s_frame_interval(struct v4l2_subdev *sd, if (fi->pad >= PRP_NUM_PADS) return -EINVAL; - /* No limits on frame interval */ mutex_lock(&priv->lock); - priv->frame_interval = fi->interval; + + /* No limits on valid frame intervals */ + if (fi->interval.numerator == 0 || fi->interval.denominator == 0) + fi->interval = priv->frame_interval; + else + priv->frame_interval = fi->interval; + mutex_unlock(&priv->lock); return 0; diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 3637693c2bc8..376b504e8a42 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -1222,9 +1222,14 @@ static int prp_s_frame_interval(struct v4l2_subdev *sd, if (fi->pad >= PRPENCVF_NUM_PADS) return -EINVAL; - /* No limits on frame interval */ mutex_lock(&priv->lock); - priv->frame_interval = fi->interval; + + /* No limits on valid frame intervals */ + if (fi->interval.numerator == 0 || fi->interval.denominator == 0) + fi->interval = priv->frame_interval; + else + priv->frame_interval = fi->interval; + mutex_unlock(&priv->lock); return 0; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index d851ca2497b4..fb5307e2ca43 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -926,7 +926,10 @@ static int csi_s_frame_interval(struct v4l2_subdev *sd, switch (fi->pad) { case CSI_SINK_PAD: - /* No limits on input frame interval */ + /* No limits on valid input frame intervals */ + if (fi->interval.numerator == 0 || + fi->interval.denominator == 0) + fi->interval = *input_fi; /* Reset output intervals and frame skipping ratio to 1:1 */ priv->frame_interval[CSI_SRC_PAD_IDMAC] = fi->interval; priv->frame_interval[CSI_SRC_PAD_DIRECT] = fi->interval; diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 297951d98ab5..2808662e2597 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -818,7 +818,10 @@ static int vdic_s_frame_interval(struct v4l2_subdev *sd, switch (fi->pad) { case VDIC_SINK_PAD_DIRECT: case VDIC_SINK_PAD_IDMAC: - /* No limits on input frame interval */ + /* No limits on valid input frame intervals */ + if (fi->interval.numerator == 0 || + fi->interval.denominator == 0) + fi->interval = priv->frame_interval[fi->pad]; /* Reset output interval */ *output_fi = fi->interval; if (priv->csi_direct) -- cgit v1.2.3-59-g8ed1b From c2d88e7d66f9d327593e0d0f9d7f94755b637e5c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 5 Feb 2019 16:11:15 -0500 Subject: media: vb2: Fix buf_out_validate documentation The .buf_out_validate callback is mandatory for OUTPUT queues. Mark it as such in the callback's doc. Fixes: 28d77c21cb ("media: vb2: add buf_out_validate callback") Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/videobuf2-core.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 4849b865b908..06142c1469cc 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -345,7 +345,8 @@ struct vb2_buffer { * waiting for a new buffer to arrive. * @buf_out_validate: called when the output buffer is prepared or queued * to a request; drivers can use this to validate - * userspace-provided information; optional. + * userspace-provided information; this is required only + * for OUTPUT queues. * @buf_init: called once after allocating a buffer (in MMAP case) * or after acquiring a new USERPTR buffer; drivers may * perform additional buffer-related initialization; -- cgit v1.2.3-59-g8ed1b From a4d3d61254d3645d8de738102c3c473b176180a5 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 5 Feb 2019 16:20:33 -0500 Subject: media: v4l2-mem2mem: Rename v4l2_m2m_buf_copy_data to v4l2_m2m_buf_copy_metadata The v4l2_m2m_buf_copy_data helper is used to copy the buffer metadata, such as its timestamp and its flags. Therefore, the v4l2_m2m_buf_copy_metadata name is more clear and avoids confusion with a payload data copy. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: also fix cedrus_dec.c] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 4 ++-- drivers/media/platform/vim2m.c | 2 +- drivers/media/v4l2-core/v4l2-mem2mem.c | 8 ++++---- drivers/staging/media/sunxi/cedrus/cedrus_dec.c | 2 +- include/media/v4l2-mem2mem.h | 14 +++++++------- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 212da185e4f2..9d739ea5542d 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -199,7 +199,7 @@ static int device_process(struct vicodec_ctx *ctx, dst_vb->sequence = q_dst->sequence++; dst_vb->flags &= ~V4L2_BUF_FLAG_LAST; - v4l2_m2m_buf_copy_data(src_vb, dst_vb, !ctx->is_enc); + v4l2_m2m_buf_copy_metadata(src_vb, dst_vb, !ctx->is_enc); return 0; } @@ -414,7 +414,7 @@ static void set_last_buffer(struct vb2_v4l2_buffer *dst_buf, vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); dst_buf->sequence = q_dst->sequence++; - v4l2_m2m_buf_copy_data(src_buf, dst_buf, !ctx->is_enc); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, !ctx->is_enc); dst_buf->flags |= V4L2_BUF_FLAG_LAST; v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); } diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index bd125ad34343..98be92a4e0b0 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -432,7 +432,7 @@ static int device_process(struct vim2m_ctx *ctx, out_vb->sequence = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; in_vb->sequence = q_data_in->sequence++; - v4l2_m2m_buf_copy_data(in_vb, out_vb, true); + v4l2_m2m_buf_copy_metadata(in_vb, out_vb, true); if (ctx->mode & MEM2MEM_VFLIP) { start = height - 1; diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 631f4e2aa942..1494d0d5951a 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -975,9 +975,9 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue); -void v4l2_m2m_buf_copy_data(const struct vb2_v4l2_buffer *out_vb, - struct vb2_v4l2_buffer *cap_vb, - bool copy_frame_flags) +void v4l2_m2m_buf_copy_metadata(const struct vb2_v4l2_buffer *out_vb, + struct vb2_v4l2_buffer *cap_vb, + bool copy_frame_flags) { u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK; @@ -993,7 +993,7 @@ void v4l2_m2m_buf_copy_data(const struct vb2_v4l2_buffer *out_vb, cap_vb->flags &= ~mask; cap_vb->flags |= out_vb->flags & mask; } -EXPORT_SYMBOL_GPL(v4l2_m2m_buf_copy_data); +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_copy_metadata); void v4l2_m2m_request_queue(struct media_request *req) { diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 443fb037e1cf..4d6d602cdde6 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -50,7 +50,7 @@ void cedrus_device_run(void *priv) break; } - v4l2_m2m_buf_copy_data(run.src, run.dst, true); + v4l2_m2m_buf_copy_metadata(run.src, run.dst, true); dev->dec_ops[ctx->current_codec]->setup(ctx, &run); diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 43e447dcf69d..47c6d9aa0bf4 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -623,11 +623,11 @@ v4l2_m2m_dst_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx) } /** - * v4l2_m2m_buf_copy_data() - copy buffer data from the output buffer to the - * capture buffer + * v4l2_m2m_buf_copy_metadata() - copy buffer metadata from + * the output buffer to the capture buffer * - * @out_vb: the output buffer that is the source of the data. - * @cap_vb: the capture buffer that will receive the data. + * @out_vb: the output buffer that is the source of the metadata. + * @cap_vb: the capture buffer that will receive the metadata. * @copy_frame_flags: copy the KEY/B/PFRAME flags as well. * * This helper function copies the timestamp, timecode (if the TIMECODE @@ -638,9 +638,9 @@ v4l2_m2m_dst_buf_remove_by_idx(struct v4l2_m2m_ctx *m2m_ctx, unsigned int idx) * flags are not copied. This is typically needed for encoders that * set this bits explicitly. */ -void v4l2_m2m_buf_copy_data(const struct vb2_v4l2_buffer *out_vb, - struct vb2_v4l2_buffer *cap_vb, - bool copy_frame_flags); +void v4l2_m2m_buf_copy_metadata(const struct vb2_v4l2_buffer *out_vb, + struct vb2_v4l2_buffer *cap_vb, + bool copy_frame_flags); /* v4l2 request helper */ -- cgit v1.2.3-59-g8ed1b From d75e77ed14f86034ace17e65ab6a6261dd0858c2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 04:13:33 -0500 Subject: media: hdpvr: fix smatch warning drivers/media/usb/hdpvr/hdpvr-i2c.c: drivers/media/usb/hdpvr/hdpvr-i2c.c:78 hdpvr_i2c_read() warn: 'dev->i2c_buf' 4216624615462223872 can't fit into 127 '*data' dev->i2c_buf is a char array, so you can just use dev->i2c_buf to get the start address, no need to do &dev->i2c_buf, even though it is the same address in C. It only confuses smatch. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hdpvr/hdpvr-i2c.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index 5a3cb614a211..d76173f1ced1 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -61,10 +61,10 @@ static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus, return -EINVAL; if (wlen) { - memcpy(&dev->i2c_buf, wdata, wlen); + memcpy(dev->i2c_buf, wdata, wlen); ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, - (bus << 8) | addr, 0, &dev->i2c_buf, + (bus << 8) | addr, 0, dev->i2c_buf, wlen, 1000); if (ret < 0) return ret; @@ -72,10 +72,10 @@ static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus, ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), REQTYPE_I2C_READ, CTRL_READ_REQUEST, - (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); + (bus << 8) | addr, 0, dev->i2c_buf, len, 1000); if (ret == len) { - memcpy(data, &dev->i2c_buf, len); + memcpy(data, dev->i2c_buf, len); ret = 0; } else if (ret >= 0) ret = -EIO; @@ -91,17 +91,17 @@ static int hdpvr_i2c_write(struct hdpvr_device *dev, int bus, if (len > sizeof(dev->i2c_buf)) return -EINVAL; - memcpy(&dev->i2c_buf, data, len); + memcpy(dev->i2c_buf, data, len); ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, - (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); + (bus << 8) | addr, 0, dev->i2c_buf, len, 1000); if (ret < 0) return ret; ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST, - 0, 0, &dev->i2c_buf, 2, 1000); + 0, 0, dev->i2c_buf, 2, 1000); if ((ret == 2) && (dev->i2c_buf[1] == (len - 1))) ret = 0; -- cgit v1.2.3-59-g8ed1b From db89a47fb9c71e769b817fa669631b04c2aba440 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 04:13:38 -0500 Subject: media: pxa_camera: fix smatch warning drivers/media/platform/pxa_camera.c:2400 pxa_camera_probe() error: we previously assumed 'pcdev->pdata' could be null (see line 2397) First check if platform data is provided, then check if DT data is provided, and if neither is provided just return with -ENODEV. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 5f930560eb30..3cf3c6390cc8 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2394,15 +2394,17 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->res = res; pcdev->pdata = pdev->dev.platform_data; - if (pdev->dev.of_node && !pcdev->pdata) { - err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd); - } else { + if (pcdev->pdata) { pcdev->platform_flags = pcdev->pdata->flags; pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C; pcdev->asd.match.i2c.adapter_id = pcdev->pdata->sensor_i2c_adapter_id; pcdev->asd.match.i2c.address = pcdev->pdata->sensor_i2c_address; + } else if (pdev->dev.of_node) { + err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd); + } else { + return -ENODEV; } if (err < 0) return err; -- cgit v1.2.3-59-g8ed1b From 6fd369dd1cb65a032f1ab9227033ecb7b759656d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2019 08:43:51 -0500 Subject: media: vimc: fill in bus_info in media_device_info It is good practice to fill in bus_info. Also just use 'platform:vimc' when filling in the bus_info in querycap: the bus_info has nothing to do with the video device name. Signed-off-by: Hans Verkuil Acked-by: Helen Koike Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 4 +--- drivers/media/platform/vimc/vimc-core.c | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 93837d9eecd2..a6f8715d2b44 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -70,12 +70,10 @@ struct vimc_cap_buffer { static int vimc_cap_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct vimc_cap_device *vcap = video_drvdata(file); - strscpy(cap->driver, VIMC_PDEV_NAME, sizeof(cap->driver)); strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", vcap->vdev.v4l2_dev->name); + "platform:%s", VIMC_PDEV_NAME); return 0; } diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index bf19f1f9795e..c2fdf3ea67ed 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -318,6 +318,8 @@ static int vimc_probe(struct platform_device *pdev) /* Initialize media device */ strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME, sizeof(vimc->mdev.model)); + snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info), + "platform:%s", VIMC_PDEV_NAME); vimc->mdev.dev = &pdev->dev; media_device_init(&vimc->mdev); -- cgit v1.2.3-59-g8ed1b From 14c8e80e68695c2b9342112c39263a79e0963461 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Feb 2019 05:25:42 -0500 Subject: media: v4l: ioctl: Sanitize num_planes before using it The linked commit changed s_fmt/try_fmt to fail if num_planes is bogus. This, however, is against the spec, which mandates drivers to return a proper num_planes value, without an error. Replace the num_planes check and instead clamp it to a sane value, so we still make sure we don't overflow the planes array by accident. Fixes: 9048b2e15b11c5 ("media: v4l: ioctl: Validate num_planes before using it") Signed-off-by: Ezequiel Garcia Reviewed-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 90aad465f9ed..206b7348797e 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1017,6 +1017,12 @@ static void v4l_sanitize_format(struct v4l2_format *fmt) { unsigned int offset; + /* Make sure num_planes is not bogus */ + if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || + fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + fmt->fmt.pix_mp.num_planes = min_t(u32, fmt->fmt.pix_mp.num_planes, + VIDEO_MAX_PLANES); + /* * The v4l2_pix_format structure has been extended with fields that were * not previously required to be set to zero by applications. The priv @@ -1553,8 +1559,6 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane)) break; CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); - if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES) - break; for (i = 0; i < p->fmt.pix_mp.num_planes; i++) CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i], bytesperline); @@ -1586,8 +1590,6 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane)) break; CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); - if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES) - break; for (i = 0; i < p->fmt.pix_mp.num_planes; i++) CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i], bytesperline); @@ -1656,8 +1658,6 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane)) break; CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); - if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES) - break; for (i = 0; i < p->fmt.pix_mp.num_planes; i++) CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i], bytesperline); @@ -1689,8 +1689,6 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane)) break; CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func); - if (p->fmt.pix_mp.num_planes > VIDEO_MAX_PLANES) - break; for (i = 0; i < p->fmt.pix_mp.num_planes; i++) CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i], bytesperline); -- cgit v1.2.3-59-g8ed1b From a8566d79f8ffb09a9d046bc98eb7ab59c79c6b58 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 18 Feb 2019 10:29:31 -0500 Subject: media: vim2m: fix build breakage due to a merge conflict A merge conflict rised when merging from -rc7. Fix it. In this specific case, we don't need the if anymore, as the work_run was moved to its rightful place (struct vim2m_ctx). Fixes: b3e64e5b0778 ("media: vim2m: use per-file handler work queue") Fixes: 240809ef6630 ("media: vim2m: only cancel work if it is for right context") Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 04250adf58e0..a27d3052bb62 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -905,8 +905,7 @@ static void vim2m_stop_streaming(struct vb2_queue *q) struct vb2_v4l2_buffer *vbuf; unsigned long flags; - if (v4l2_m2m_get_curr_priv(dev->m2m_dev) == ctx) - cancel_delayed_work_sync(&dev->work_run); + cancel_delayed_work_sync(&ctx->work_run); for (;;) { if (V4L2_TYPE_IS_OUTPUT(q->type)) -- cgit v1.2.3-59-g8ed1b From 09714569969cfe5e5b51fa5b0d9224ce38744e76 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Jan 2019 09:58:26 -0500 Subject: media: vimc: add USERPTR support Add VB2_USERPTR to the vimc capture device. Signed-off-by: Hans Verkuil Acked-by: Helen Koike Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index a6f8715d2b44..ea869631a3f6 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -429,7 +429,7 @@ static int vimc_cap_comp_bind(struct device *comp, struct device *master, /* Initialize the vb2 queue */ q = &vcap->queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; q->drv_priv = vcap; q->buf_struct_size = sizeof(struct vimc_cap_buffer); q->ops = &vimc_cap_qops; -- cgit v1.2.3-59-g8ed1b From 5964cbd8692252615370b77eb96764dd70c2f837 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Thu, 7 Feb 2019 18:42:55 -0500 Subject: media: imx: Set capture compose rectangle in capture_device_set_format The capture compose rectangle was not getting updated when setting the source subdevice's source pad format. This causes the compose window to be zero (or not updated) at stream start unless the capture device format was set explicitly at the capture device node. Fix by moving the calculation of the capture compose rectangle to imx_media_mbus_fmt_to_pix_fmt(), and pass the rectangle to imx_media_capture_device_set_format(). Fixes: 439d8186fb23 ("media: imx: add capture compose rectangle") Signed-off-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prpencvf.c | 5 +++-- drivers/staging/media/imx/imx-media-capture.c | 24 ++++++++++++------------ drivers/staging/media/imx/imx-media-csi.c | 5 +++-- drivers/staging/media/imx/imx-media-utils.c | 20 +++++++++++++++----- drivers/staging/media/imx/imx-media.h | 6 ++++-- 5 files changed, 37 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 376b504e8a42..5c8e6ad8c025 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -912,6 +912,7 @@ static int prp_set_fmt(struct v4l2_subdev *sd, const struct imx_media_pixfmt *cc; struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect vdev_compose; int ret = 0; if (sdformat->pad >= PRPENCVF_NUM_PADS) @@ -953,11 +954,11 @@ static int prp_set_fmt(struct v4l2_subdev *sd, priv->cc[sdformat->pad] = cc; /* propagate output pad format to capture device */ - imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, + imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose, &priv->format_mbus[PRPENCVF_SRC_PAD], priv->cc[PRPENCVF_SRC_PAD]); mutex_unlock(&priv->lock); - imx_media_capture_device_set_format(vdev, &vdev_fmt); + imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose); return 0; out: diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index f92edee63aa6..9703c85b19c4 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -205,7 +205,8 @@ static int capture_g_fmt_vid_cap(struct file *file, void *fh, static int __capture_try_fmt_vid_cap(struct capture_priv *priv, struct v4l2_subdev_format *fmt_src, - struct v4l2_format *f) + struct v4l2_format *f, + struct v4l2_rect *compose) { const struct imx_media_pixfmt *cc, *cc_src; @@ -245,7 +246,8 @@ static int __capture_try_fmt_vid_cap(struct capture_priv *priv, } } - imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src->format, cc); + imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, compose, + &fmt_src->format, cc); return 0; } @@ -263,7 +265,7 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, if (ret) return ret; - return __capture_try_fmt_vid_cap(priv, &fmt_src, f); + return __capture_try_fmt_vid_cap(priv, &fmt_src, f, NULL); } static int capture_s_fmt_vid_cap(struct file *file, void *fh, @@ -271,6 +273,7 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, { struct capture_priv *priv = video_drvdata(file); struct v4l2_subdev_format fmt_src; + struct v4l2_rect compose; int ret; if (vb2_is_busy(&priv->q)) { @@ -284,17 +287,14 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, if (ret) return ret; - ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f); + ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f, &compose); if (ret) return ret; priv->vdev.fmt.fmt.pix = f->fmt.pix; priv->vdev.cc = imx_media_find_format(f->fmt.pix.pixelformat, CS_SEL_ANY, true); - priv->vdev.compose.left = 0; - priv->vdev.compose.top = 0; - priv->vdev.compose.width = fmt_src.format.width; - priv->vdev.compose.height = fmt_src.format.height; + priv->vdev.compose = compose; return 0; } @@ -655,7 +655,8 @@ static struct video_device capture_videodev = { }; void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, - struct v4l2_pix_format *pix) + const struct v4l2_pix_format *pix, + const struct v4l2_rect *compose) { struct capture_priv *priv = to_capture_priv(vdev); @@ -663,6 +664,7 @@ void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, priv->vdev.fmt.fmt.pix = *pix; priv->vdev.cc = imx_media_find_format(pix->pixelformat, CS_SEL_ANY, true); + priv->vdev.compose = *compose; mutex_unlock(&priv->mutex); } EXPORT_SYMBOL_GPL(imx_media_capture_device_set_format); @@ -768,10 +770,8 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev) } vdev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix, + imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix, &vdev->compose, &fmt_src.format, NULL); - vdev->compose.width = fmt_src.format.width; - vdev->compose.height = fmt_src.format.height; vdev->cc = imx_media_find_format(vdev->fmt.fmt.pix.pixelformat, CS_SEL_ANY, false); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index fb5307e2ca43..5265a2cc93bc 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1502,6 +1502,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd, struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; struct v4l2_rect *crop, *compose; + struct v4l2_rect vdev_compose; int ret; if (sdformat->pad >= CSI_NUM_PADS) @@ -1557,11 +1558,11 @@ static int csi_set_fmt(struct v4l2_subdev *sd, priv->cc[sdformat->pad] = cc; /* propagate IDMAC output pad format to capture device */ - imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, + imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose, &priv->format_mbus[CSI_SRC_PAD_IDMAC], priv->cc[CSI_SRC_PAD_IDMAC]); mutex_unlock(&priv->lock); - imx_media_capture_device_set_format(vdev, &vdev_fmt); + imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose); return 0; out: diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 5f110d90a4ef..e6f544528d31 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -577,7 +577,8 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, EXPORT_SYMBOL_GPL(imx_media_fill_default_mbus_fields); int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, - struct v4l2_mbus_framefmt *mbus, + struct v4l2_rect *compose, + const struct v4l2_mbus_framefmt *mbus, const struct imx_media_pixfmt *cc) { u32 width; @@ -624,6 +625,17 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, pix->sizeimage = cc->planar ? ((stride * pix->height * cc->bpp) >> 3) : stride * pix->height; + /* + * set capture compose rectangle, which is fixed to the + * source subdevice mbus format. + */ + if (compose) { + compose->left = 0; + compose->top = 0; + compose->width = mbus->width; + compose->height = mbus->height; + } + return 0; } EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_pix_fmt); @@ -635,13 +647,11 @@ int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image, memset(image, 0, sizeof(*image)); - ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, mbus, NULL); + ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, &image->rect, + mbus, NULL); if (ret) return ret; - image->rect.width = mbus->width; - image->rect.height = mbus->height; - return 0; } EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_ipu_image); diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index 7a0e658753f0..195e858913a3 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -180,7 +180,8 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, struct v4l2_mbus_framefmt *fmt, bool ic_route); int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, - struct v4l2_mbus_framefmt *mbus, + struct v4l2_rect *compose, + const struct v4l2_mbus_framefmt *mbus, const struct imx_media_pixfmt *cc); int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image, struct v4l2_mbus_framefmt *mbus); @@ -261,7 +262,8 @@ void imx_media_capture_device_unregister(struct imx_media_video_dev *vdev); struct imx_media_buffer * imx_media_capture_device_next_buf(struct imx_media_video_dev *vdev); void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, - struct v4l2_pix_format *pix); + const struct v4l2_pix_format *pix, + const struct v4l2_rect *compose); void imx_media_capture_device_error(struct imx_media_video_dev *vdev); /* subdev group ids */ -- cgit v1.2.3-59-g8ed1b From 5515e414f42bf2769caae15b634004d456658284 Mon Sep 17 00:00:00 2001 From: "Lucas A. M. Magalhães" Date: Thu, 7 Feb 2019 18:59:41 -0500 Subject: media: vimc: Remove unused but set variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused but set variables to clean up the code and avoid warning. Signed-off-by: Lucas A. M. Magalhães Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-sensor.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 93961a1e694f..59195f262623 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -204,13 +204,6 @@ static void *vimc_sen_process_frame(struct vimc_ent_device *ved, { struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, ved); - const struct vimc_pix_map *vpix; - unsigned int frame_size; - - /* Calculate the frame size */ - vpix = vimc_pix_map_by_code(vsen->mbus_format.code); - frame_size = vsen->mbus_format.width * vpix->bpp * - vsen->mbus_format.height; tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); return vsen->frame; -- cgit v1.2.3-59-g8ed1b From 3d769df5fc32dc3cb12d6ccb61690b09371b8b3f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 8 Feb 2019 03:49:23 -0500 Subject: media: v4l2-subdev.h: v4l2_subdev_call: use temp __sd variable The sd argument of this macro can be a more complex expression. Since it is used 5 times in the macro it can be evaluated that many times as well. So assign it to a temp variable in the beginning and use that instead. This also avoids any potential side-effects of evaluating sd. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 47af609dc8f1..34da094a3f40 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1093,13 +1093,14 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, */ #define v4l2_subdev_call(sd, o, f, args...) \ ({ \ + struct v4l2_subdev *__sd = (sd); \ int __result; \ - if (!(sd)) \ + if (!__sd) \ __result = -ENODEV; \ - else if (!((sd)->ops->o && (sd)->ops->o->f)) \ + else if (!(__sd->ops->o && __sd->ops->o->f)) \ __result = -ENOIOCTLCMD; \ else \ - __result = (sd)->ops->o->f((sd), ##args); \ + __result = __sd->ops->o->f(__sd, ##args); \ __result; \ }) -- cgit v1.2.3-59-g8ed1b From bc2dea9e51c8ffa4d1bdb5646438532189e63d9e Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:08 -0500 Subject: media: adv748x: Add is_txb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add small is_txb() macro to the existing is_txa() and use it where appropriate. Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-csi2.c | 2 +- drivers/media/i2c/adv748x/adv748x.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 6ce21542ed48..b6b5d8c7ea7c 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -82,7 +82,7 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd) return adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd, ADV748X_HDMI_SOURCE); - if (!is_txa(tx) && is_afe_enabled(state)) + if (is_txb(tx) && is_afe_enabled(state)) return adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->afe.sd, ADV748X_AFE_SOURCE); diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index b482c7fe6957..ab0c84adbea9 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -89,8 +89,11 @@ struct adv748x_csi2 { #define notifier_to_csi2(n) container_of(n, struct adv748x_csi2, notifier) #define adv748x_sd_to_csi2(sd) container_of(sd, struct adv748x_csi2, sd) + #define is_tx_enabled(_tx) ((_tx)->state->endpoints[(_tx)->port] != NULL) #define is_txa(_tx) ((_tx) == &(_tx)->state->txa) +#define is_txb(_tx) ((_tx) == &(_tx)->state->txb) + #define is_afe_enabled(_state) \ ((_state)->endpoints[ADV748X_PORT_AIN0] != NULL || \ (_state)->endpoints[ADV748X_PORT_AIN1] != NULL || \ -- cgit v1.2.3-59-g8ed1b From 29166e0f8f426a9a3067efd9317d84e4db82f009 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:09 -0500 Subject: media: adv748x: Rename reset procedures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the chip reset procedure as they configure the CP (HDMI) and SD (AFE) cores. Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index bd8a8c8d6f8e..ba91db45547a 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -353,9 +353,8 @@ static const struct adv748x_reg_value adv748x_sw_reset[] = { {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; -/* Supported Formats For Script Below */ -/* - 01-29 HDMI to MIPI TxA CSI 4-Lane - RGB888: */ -static const struct adv748x_reg_value adv748x_init_txa_4lane[] = { +/* Initialize CP Core with RGB888 format. */ +static const struct adv748x_reg_value adv748x_init_hdmi[] = { /* Disable chip powerdown & Enable HDMI Rx block */ {ADV748X_PAGE_IO, 0x00, 0x40}, @@ -399,10 +398,8 @@ static const struct adv748x_reg_value adv748x_init_txa_4lane[] = { {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; -/* 02-01 Analog CVBS to MIPI TX-B CSI 1-Lane - */ -/* Autodetect CVBS Single Ended In Ain 1 - MIPI Out */ -static const struct adv748x_reg_value adv748x_init_txb_1lane[] = { - +/* Initialize AFE core with YUV8 format. */ +static const struct adv748x_reg_value adv748x_init_afe[] = { {ADV748X_PAGE_IO, 0x00, 0x30}, /* Disable chip powerdown Rx */ {ADV748X_PAGE_IO, 0xf2, 0x01}, /* Enable I2C Read Auto-Increment */ @@ -445,19 +442,18 @@ static int adv748x_reset(struct adv748x_state *state) if (ret < 0) return ret; - /* Init and power down TXA */ - ret = adv748x_write_regs(state, adv748x_init_txa_4lane); + /* Initialize CP and AFE cores. */ + ret = adv748x_write_regs(state, adv748x_init_hdmi); if (ret) return ret; - adv748x_tx_power(&state->txa, 1); - adv748x_tx_power(&state->txa, 0); - - /* Init and power down TXB */ - ret = adv748x_write_regs(state, adv748x_init_txb_1lane); + ret = adv748x_write_regs(state, adv748x_init_afe); if (ret) return ret; + /* Reset TXA and TXB */ + adv748x_tx_power(&state->txa, 1); + adv748x_tx_power(&state->txa, 0); adv748x_tx_power(&state->txb, 1); adv748x_tx_power(&state->txb, 0); -- cgit v1.2.3-59-g8ed1b From 21325e19cadec506bd48591d3944bd526f647d93 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:10 -0500 Subject: media: adv748x: csi2: Link AFE with TXA and TXB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADV748x chip supports routing AFE output to either TXA or TXB. In order to support run-time configuration of video stream path, create an additional (not enabled) "AFE:8->TXA:0" link, and remove the IMMUTABLE flag from existing ones. Reviewed-by: Kieran Bingham Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-csi2.c | 44 +++++++++++++++++--------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index b6b5d8c7ea7c..8c3714495e11 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -27,6 +27,7 @@ static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, * @v4l2_dev: Video registration device * @src: Source subdevice to establish link * @src_pad: Pad number of source to link to this @tx + * @enable: Link enabled flag * * Ensure that the subdevice is registered against the v4l2_device, and link the * source pad to the sink pad of the CSI2 bus entity. @@ -34,17 +35,11 @@ static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, static int adv748x_csi2_register_link(struct adv748x_csi2 *tx, struct v4l2_device *v4l2_dev, struct v4l2_subdev *src, - unsigned int src_pad) + unsigned int src_pad, + bool enable) { - int enabled = MEDIA_LNK_FL_ENABLED; int ret; - /* - * Dynamic linking of the AFE is not supported. - * Register the links as immutable. - */ - enabled |= MEDIA_LNK_FL_IMMUTABLE; - if (!src->v4l2_dev) { ret = v4l2_device_register_subdev(v4l2_dev, src); if (ret) @@ -53,7 +48,7 @@ static int adv748x_csi2_register_link(struct adv748x_csi2 *tx, return media_create_pad_link(&src->entity, src_pad, &tx->sd.entity, ADV748X_CSI2_SINK, - enabled); + enable ? MEDIA_LNK_FL_ENABLED : 0); } /* ----------------------------------------------------------------------------- @@ -68,25 +63,32 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd) { struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); struct adv748x_state *state = tx->state; + int ret; adv_dbg(state, "Registered %s (%s)", is_txa(tx) ? "TXA":"TXB", sd->name); /* - * The adv748x hardware allows the AFE to route through the TXA, however - * this is not currently supported in this driver. + * Link TXA to AFE and HDMI, and TXB to AFE only as TXB cannot output + * HDMI. * - * Link HDMI->TXA, and AFE->TXB directly. + * The HDMI->TXA link is enabled by default, as is the AFE->TXB one. */ - if (is_txa(tx) && is_hdmi_enabled(state)) - return adv748x_csi2_register_link(tx, sd->v4l2_dev, - &state->hdmi.sd, - ADV748X_HDMI_SOURCE); - if (is_txb(tx) && is_afe_enabled(state)) - return adv748x_csi2_register_link(tx, sd->v4l2_dev, - &state->afe.sd, - ADV748X_AFE_SOURCE); - return 0; + if (is_afe_enabled(state)) { + ret = adv748x_csi2_register_link(tx, sd->v4l2_dev, + &state->afe.sd, + ADV748X_AFE_SOURCE, + is_txb(tx)); + if (ret) + return ret; + } + + /* Register link to HDMI for TXA only. */ + if (is_txb(tx) || !is_hdmi_enabled(state)) + return 0; + + return adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd, + ADV748X_HDMI_SOURCE, true); } static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = { -- cgit v1.2.3-59-g8ed1b From 3361b9c4ed8790666875cbac1729d5a15e982947 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:11 -0500 Subject: media: adv748x: Store the source subdevice in TX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The power_up_tx() procedure needs to set a few registers conditionally to the selected video source, but it currently checks for the provided tx to be either TXA or TXB. With the introduction of dynamic routing between HDMI and AFE entities to TXA, checking which TX the function is operating on is not meaningful anymore. To fix this, store the subdevice of the source providing video data to the CSI-2 TX in the 'struct adv748x_csi2' representing the TX and check on it. Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 2 +- drivers/media/i2c/adv748x/adv748x-csi2.c | 13 ++++++++++--- drivers/media/i2c/adv748x/adv748x.h | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index ba91db45547a..824bca3219b7 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -254,7 +254,7 @@ static int adv748x_power_up_tx(struct adv748x_csi2 *tx) adv748x_write_check(state, page, 0x00, 0xa0 | tx->num_lanes, &ret); /* ADI Required Write */ - if (is_txa(tx)) { + if (tx->src == &state->hdmi.sd) { adv748x_write_check(state, page, 0xdb, 0x10, &ret); adv748x_write_check(state, page, 0xd6, 0x07, &ret); } else { diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 8c3714495e11..353b6b9bf6a7 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -46,9 +46,16 @@ static int adv748x_csi2_register_link(struct adv748x_csi2 *tx, return ret; } - return media_create_pad_link(&src->entity, src_pad, - &tx->sd.entity, ADV748X_CSI2_SINK, - enable ? MEDIA_LNK_FL_ENABLED : 0); + ret = media_create_pad_link(&src->entity, src_pad, + &tx->sd.entity, ADV748X_CSI2_SINK, + enable ? MEDIA_LNK_FL_ENABLED : 0); + if (ret) + return ret; + + if (enable) + tx->src = src; + + return 0; } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index ab0c84adbea9..d22270f5e2c1 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -84,6 +84,7 @@ struct adv748x_csi2 { struct media_pad pads[ADV748X_CSI2_NR_PADS]; struct v4l2_ctrl_handler ctrl_hdl; struct v4l2_ctrl *pixel_rate; + struct v4l2_subdev *src; struct v4l2_subdev sd; }; -- cgit v1.2.3-59-g8ed1b From a33df6ac31513c499e4b8dae9632e641ebe4a245 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:12 -0500 Subject: media: adv748x: Store the TX sink in HDMI/AFE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both the AFE and HDMI s_stream routines (adv748x_afe_s_stream() and adv748x_hdmi_s_stream()) have to enable the CSI-2 TX they are streaming video data to. With the introduction of dynamic routing between HDMI and AFE entities to TXA, the video stream sink needs to be set at run time, and not statically selected as the s_stream functions are currently doing. To fix this, store a reference to the active CSI-2 TX sink for both HDMI and AFE sources, and operate on it when starting/stopping the stream. Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-afe.c | 2 +- drivers/media/i2c/adv748x/adv748x-csi2.c | 15 +++++++++++++-- drivers/media/i2c/adv748x/adv748x-hdmi.c | 2 +- drivers/media/i2c/adv748x/adv748x.h | 4 ++++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 71714634efb0..dbbb1e4d6363 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -282,7 +282,7 @@ static int adv748x_afe_s_stream(struct v4l2_subdev *sd, int enable) goto unlock; } - ret = adv748x_tx_power(&state->txb, enable); + ret = adv748x_tx_power(afe->tx, enable); if (ret) goto unlock; diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 353b6b9bf6a7..2091cda50935 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -88,14 +88,25 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd) is_txb(tx)); if (ret) return ret; + + /* TXB can output AFE signals only. */ + if (is_txb(tx)) + state->afe.tx = tx; } /* Register link to HDMI for TXA only. */ if (is_txb(tx) || !is_hdmi_enabled(state)) return 0; - return adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd, - ADV748X_HDMI_SOURCE, true); + ret = adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd, + ADV748X_HDMI_SOURCE, true); + if (ret) + return ret; + + /* The default HDMI output is TXA. */ + state->hdmi.tx = tx; + + return 0; } static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = { diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c index 35d027941482..c557f8fdf11a 100644 --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c @@ -358,7 +358,7 @@ static int adv748x_hdmi_s_stream(struct v4l2_subdev *sd, int enable) mutex_lock(&state->mutex); - ret = adv748x_tx_power(&state->txa, enable); + ret = adv748x_tx_power(hdmi->tx, enable); if (ret) goto done; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index d22270f5e2c1..934a9d9a75c8 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -121,6 +121,8 @@ struct adv748x_hdmi { struct v4l2_dv_timings timings; struct v4l2_fract aspect_ratio; + struct adv748x_csi2 *tx; + struct { u8 edid[512]; u32 present; @@ -151,6 +153,8 @@ struct adv748x_afe { struct v4l2_subdev sd; struct v4l2_mbus_framefmt format; + struct adv748x_csi2 *tx; + bool streaming; v4l2_std_id curr_norm; unsigned int input; -- cgit v1.2.3-59-g8ed1b From 9423ca350df71ff71f8db2b01127eecd2254f619 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 10 Jan 2019 09:02:13 -0500 Subject: media: adv748x: Implement TX link_setup callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the adv748x driver is informed about a link being created from HDMI or AFE to a CSI-2 TX output, the 'link_setup()' callback is invoked. Make sure to implement proper routing management at link setup time, to route the selected video stream to the desired TX output. Signed-off-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 48 +++++++++++++++++++++++++++++++- drivers/media/i2c/adv748x/adv748x.h | 2 ++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 824bca3219b7..02f9c440301c 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -335,6 +335,51 @@ int adv748x_tx_power(struct adv748x_csi2 *tx, bool on) /* ----------------------------------------------------------------------------- * Media Operations */ +static int adv748x_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *rsd = media_entity_to_v4l2_subdev(remote->entity); + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct adv748x_state *state = v4l2_get_subdevdata(sd); + struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); + bool enable = flags & MEDIA_LNK_FL_ENABLED; + u8 io10_mask = ADV748X_IO_10_CSI1_EN | + ADV748X_IO_10_CSI4_EN | + ADV748X_IO_10_CSI4_IN_SEL_AFE; + u8 io10 = 0; + + /* Refuse to enable multiple links to the same TX at the same time. */ + if (enable && tx->src) + return -EINVAL; + + /* Set or clear the source (HDMI or AFE) and the current TX. */ + if (rsd == &state->afe.sd) + state->afe.tx = enable ? tx : NULL; + else + state->hdmi.tx = enable ? tx : NULL; + + tx->src = enable ? rsd : NULL; + + if (state->afe.tx) { + /* AFE Requires TXA enabled, even when output to TXB */ + io10 |= ADV748X_IO_10_CSI4_EN; + if (is_txa(tx)) + io10 |= ADV748X_IO_10_CSI4_IN_SEL_AFE; + else + io10 |= ADV748X_IO_10_CSI1_EN; + } + + if (state->hdmi.tx) + io10 |= ADV748X_IO_10_CSI4_EN; + + return io_clrset(state, ADV748X_IO_10, io10_mask, io10); +} + +static const struct media_entity_operations adv748x_tx_media_ops = { + .link_setup = adv748x_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; static const struct media_entity_operations adv748x_media_ops = { .link_validate = v4l2_subdev_link_validate, @@ -516,7 +561,8 @@ void adv748x_subdev_init(struct v4l2_subdev *sd, struct adv748x_state *state, state->client->addr, ident); sd->entity.function = function; - sd->entity.ops = &adv748x_media_ops; + sd->entity.ops = is_tx(adv748x_sd_to_csi2(sd)) ? + &adv748x_tx_media_ops : &adv748x_media_ops; } static int adv748x_parse_csi2_lanes(struct adv748x_state *state, diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 934a9d9a75c8..b00c1995efb0 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -94,6 +94,7 @@ struct adv748x_csi2 { #define is_tx_enabled(_tx) ((_tx)->state->endpoints[(_tx)->port] != NULL) #define is_txa(_tx) ((_tx) == &(_tx)->state->txa) #define is_txb(_tx) ((_tx) == &(_tx)->state->txb) +#define is_tx(_tx) (is_txa(_tx) || is_txb(_tx)) #define is_afe_enabled(_state) \ ((_state)->endpoints[ADV748X_PORT_AIN0] != NULL || \ @@ -223,6 +224,7 @@ struct adv748x_state { #define ADV748X_IO_10_CSI4_EN BIT(7) #define ADV748X_IO_10_CSI1_EN BIT(6) #define ADV748X_IO_10_PIX_OUT_EN BIT(5) +#define ADV748X_IO_10_CSI4_IN_SEL_AFE BIT(3) #define ADV748X_IO_CHIP_REV_ID_1 0xdf #define ADV748X_IO_CHIP_REV_ID_2 0xe0 -- cgit v1.2.3-59-g8ed1b From ac105ab2138f43b7acbc91f17453e0a0ba2f7f33 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 11 Jan 2019 12:41:40 -0500 Subject: media: i2c: adv748x: Convert SW reset routine to function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADV748x is currently reset by writting a small table of registers to the device. The table lacks documentation and contains magic values to perform the actions, including using a fake register address to introduce a delay loop. Remove the table, and convert to code, documenting the purpose of the specific writes along the way. Signed-off-by: Kieran Bingham Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 32 ++++++++++++++++++++++---------- drivers/media/i2c/adv748x/adv748x.h | 16 ++++++++++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 02f9c440301c..252bdb28b18b 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -389,15 +389,6 @@ static const struct media_entity_operations adv748x_media_ops = { * HW setup */ -static const struct adv748x_reg_value adv748x_sw_reset[] = { - - {ADV748X_PAGE_IO, 0xff, 0xff}, /* SW reset */ - {ADV748X_PAGE_WAIT, 0x00, 0x05},/* delay 5 */ - {ADV748X_PAGE_IO, 0x01, 0x76}, /* ADI Required Write */ - {ADV748X_PAGE_IO, 0xf2, 0x01}, /* Enable I2C Read Auto-Increment */ - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ -}; - /* Initialize CP Core with RGB888 format. */ static const struct adv748x_reg_value adv748x_init_hdmi[] = { /* Disable chip powerdown & Enable HDMI Rx block */ @@ -474,12 +465,33 @@ static const struct adv748x_reg_value adv748x_init_afe[] = { {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; +static int adv748x_sw_reset(struct adv748x_state *state) +{ + int ret; + + ret = io_write(state, ADV748X_IO_REG_FF, ADV748X_IO_REG_FF_MAIN_RESET); + if (ret) + return ret; + + usleep_range(5000, 6000); + + /* Disable CEC Wakeup from power-down mode */ + ret = io_clrset(state, ADV748X_IO_REG_01, ADV748X_IO_REG_01_PWRDN_MASK, + ADV748X_IO_REG_01_PWRDNB); + if (ret) + return ret; + + /* Enable I2C Read Auto-Increment for consecutive reads */ + return io_write(state, ADV748X_IO_REG_F2, + ADV748X_IO_REG_F2_READ_AUTO_INC); +} + static int adv748x_reset(struct adv748x_state *state) { int ret; u8 regval = 0; - ret = adv748x_write_regs(state, adv748x_sw_reset); + ret = adv748x_sw_reset(state); if (ret < 0) return ret; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index b00c1995efb0..2f8d751cfbb0 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -211,6 +211,11 @@ struct adv748x_state { #define ADV748X_IO_PD 0x00 /* power down controls */ #define ADV748X_IO_PD_RX_EN BIT(6) +#define ADV748X_IO_REG_01 0x01 /* pwrdn{2}b, prog_xtal_freq */ +#define ADV748X_IO_REG_01_PWRDN_MASK (BIT(7) | BIT(6)) +#define ADV748X_IO_REG_01_PWRDN2B BIT(7) /* CEC Wakeup Support */ +#define ADV748X_IO_REG_01_PWRDNB BIT(6) /* CEC Wakeup Support */ + #define ADV748X_IO_REG_04 0x04 #define ADV748X_IO_REG_04_FORCE_FR BIT(0) /* Force CP free-run */ @@ -229,8 +234,19 @@ struct adv748x_state { #define ADV748X_IO_CHIP_REV_ID_1 0xdf #define ADV748X_IO_CHIP_REV_ID_2 0xe0 +#define ADV748X_IO_REG_F2 0xf2 +#define ADV748X_IO_REG_F2_READ_AUTO_INC BIT(0) + +/* For PAGE slave address offsets */ #define ADV748X_IO_SLAVE_ADDR_BASE 0xf2 +/* + * The ADV748x_Recommended_Settings_PrA_2014-08-20.pdf details both 0x80 and + * 0xff as examples for performing a software reset. + */ +#define ADV748X_IO_REG_FF 0xff +#define ADV748X_IO_REG_FF_MAIN_RESET 0xff + /* HDMI RX Map */ #define ADV748X_HDMI_LW1 0x07 /* line width_1 */ #define ADV748X_HDMI_LW1_VERT_FILTER BIT(7) -- cgit v1.2.3-59-g8ed1b From 16597c2744f79aaf5f9ec0107be477639985bf44 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Tue, 15 Jan 2019 09:25:09 -0500 Subject: media: i2c: adv748x: Remove PAGE_WAIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADV748X_PAGE_WAIT is a fake page to insert arbitrary delays in the register tables. Its only usage was removed, so we can remove the handling and simplify the code. Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Signed-off-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv748x/adv748x-core.c | 19 ++++++------------- drivers/media/i2c/adv748x/adv748x.h | 1 - 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 252bdb28b18b..f57cd77a32fa 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -218,20 +218,13 @@ static int adv748x_write_regs(struct adv748x_state *state, { int ret; - while (regs->page != ADV748X_PAGE_EOR) { - if (regs->page == ADV748X_PAGE_WAIT) { - msleep(regs->value); - } else { - ret = adv748x_write(state, regs->page, regs->reg, - regs->value); - if (ret < 0) { - adv_err(state, - "Error regs page: 0x%02x reg: 0x%02x\n", - regs->page, regs->reg); - return ret; - } + for (; regs->page != ADV748X_PAGE_EOR; regs++) { + ret = adv748x_write(state, regs->page, regs->reg, regs->value); + if (ret < 0) { + adv_err(state, "Error regs page: 0x%02x reg: 0x%02x\n", + regs->page, regs->reg); + return ret; } - regs++; } return 0; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 2f8d751cfbb0..5042f9e94aee 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -39,7 +39,6 @@ enum adv748x_page { ADV748X_PAGE_MAX, /* Fake pages for register sequences */ - ADV748X_PAGE_WAIT, /* Wait x msec */ ADV748X_PAGE_EOR, /* End Mark */ }; -- cgit v1.2.3-59-g8ed1b From 9f6d7bacc726f4809170291072589dd9e62d4a3c Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:01 -0500 Subject: media: ov5640: Move test_pattern_menu before ov5640_set_ctrl_test_pattern The OV5640 has many options for generating test patterns. Unfortunately there is only one V4L2 control for it. Thus the driver would need to list some or all combinations. Move the test_pattern_menu list before the ov5640_set_ctrl_test_pattern function that programs the hardware. This would allow us to add a matching list of values to program into the hardware, while keeping the two lists together for ease of maintenance. Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 5a909abd0a2d..8e4e8fa3685f 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2441,6 +2441,11 @@ static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain) return ret; } +static const char * const test_pattern_menu[] = { + "Disabled", + "Color bars", +}; + static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value) { return ov5640_mod_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1, @@ -2585,11 +2590,6 @@ static const struct v4l2_ctrl_ops ov5640_ctrl_ops = { .s_ctrl = ov5640_s_ctrl, }; -static const char * const test_pattern_menu[] = { - "Disabled", - "Color bars", -}; - static int ov5640_init_controls(struct ov5640_dev *sensor) { const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops; -- cgit v1.2.3-59-g8ed1b From a0c29afb506445e449713afd55e5f149b3d63b79 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:02 -0500 Subject: media: ov5640: Add register definition for test pattern register The OV5640 can generate many types of test patterns, some with additional modifiers, such as a rolling bar, or gamma gradients. Add the bit definitions for all bits in the test pattern register, and use them to compose the values to be written to the register. Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 8e4e8fa3685f..22d07b3cc8a2 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2446,10 +2446,30 @@ static const char * const test_pattern_menu[] = { "Color bars", }; +#define OV5640_TEST_ENABLE BIT(7) +#define OV5640_TEST_ROLLING BIT(6) /* rolling horizontal bar */ +#define OV5640_TEST_TRANSPARENT BIT(5) +#define OV5640_TEST_SQUARE_BW BIT(4) /* black & white squares */ +#define OV5640_TEST_BAR_STANDARD (0 << 2) +#define OV5640_TEST_BAR_VERT_CHANGE_1 (1 << 2) +#define OV5640_TEST_BAR_HOR_CHANGE (2 << 2) +#define OV5640_TEST_BAR_VERT_CHANGE_2 (3 << 2) +#define OV5640_TEST_BAR (0 << 0) +#define OV5640_TEST_RANDOM (1 << 0) +#define OV5640_TEST_SQUARE (2 << 0) +#define OV5640_TEST_BLACK (3 << 0) + +static const u8 test_pattern_val[] = { + 0, + OV5640_TEST_ENABLE | OV5640_TEST_TRANSPARENT | + OV5640_TEST_BAR_VERT_CHANGE_1 | + OV5640_TEST_BAR, +}; + static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value) { - return ov5640_mod_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1, - 0xa4, value ? 0xa4 : 0); + return ov5640_write_reg(sensor, OV5640_REG_PRE_ISP_TEST_SET1, + test_pattern_val[value]); } static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value) -- cgit v1.2.3-59-g8ed1b From 2aff1fc3653ade61d27e4355346934d71ae0fe59 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:03 -0500 Subject: media: ov5640: Disable transparent feature for test pattern The transparent feature for test patterns blends the test pattern with an actual captured image. This makes the result non-static, subject to changes in the sensor's field of view. Test patterns should be predictable and deterministic, even if they are dynamic patterns. Disable the transparent feature of the test pattern. Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 22d07b3cc8a2..a1fd69a21df1 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2461,8 +2461,7 @@ static const char * const test_pattern_menu[] = { static const u8 test_pattern_val[] = { 0, - OV5640_TEST_ENABLE | OV5640_TEST_TRANSPARENT | - OV5640_TEST_BAR_VERT_CHANGE_1 | + OV5640_TEST_ENABLE | OV5640_TEST_BAR_VERT_CHANGE_1 | OV5640_TEST_BAR, }; -- cgit v1.2.3-59-g8ed1b From bddc5cdfc82d11cc9bd80c11ef681afbae11c1db Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:04 -0500 Subject: media: ov5640: Add three more test patterns The OV5640 driver currently supports a static color bar pattern with a small vertical gamma gradient. The hardware also supports a color square pattern, as well as having a rolling bar for dynamic sequences. Add three more test patterns: - color bars with a rolling bar (but without the gamma gradient) - static color squares - color squares with a rolling bar Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index a1fd69a21df1..13311483792c 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2444,6 +2444,9 @@ static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain) static const char * const test_pattern_menu[] = { "Disabled", "Color bars", + "Color bars w/ rolling bar", + "Color squares", + "Color squares w/ rolling bar", }; #define OV5640_TEST_ENABLE BIT(7) @@ -2463,6 +2466,10 @@ static const u8 test_pattern_val[] = { 0, OV5640_TEST_ENABLE | OV5640_TEST_BAR_VERT_CHANGE_1 | OV5640_TEST_BAR, + OV5640_TEST_ENABLE | OV5640_TEST_ROLLING | + OV5640_TEST_BAR_VERT_CHANGE_1 | OV5640_TEST_BAR, + OV5640_TEST_ENABLE | OV5640_TEST_SQUARE, + OV5640_TEST_ENABLE | OV5640_TEST_ROLLING | OV5640_TEST_SQUARE, }; static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value) -- cgit v1.2.3-59-g8ed1b From 7cb013b1049a2a599c1fc344644528eb8bf985b8 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:05 -0500 Subject: media: ov5640: Set JPEG output timings when outputting JPEG data When compression is turned on, the on-bus data is framed according to the compression mode, and the height and width set in VFIFO_VSIZE and VFIFO_HSIZE. If these are not updated correctly, the sensor will send data framed in a manner unexpected by the capture interface, such as having more bytes per line than expected, and having the extra data dropped. This ultimately results in corrupted data. Set the two values when the media bus is configured for JPEG data, meaning the sensor would be in JPEG mode. Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 13311483792c..1c1dc401c678 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -83,6 +83,8 @@ #define OV5640_REG_SIGMADELTA_CTRL0C 0x3c0c #define OV5640_REG_FRAME_CTRL01 0x4202 #define OV5640_REG_FORMAT_CONTROL00 0x4300 +#define OV5640_REG_VFIFO_HSIZE 0x4602 +#define OV5640_REG_VFIFO_VSIZE 0x4604 #define OV5640_REG_POLARITY_CTRL00 0x4740 #define OV5640_REG_MIPI_CTRL00 0x4800 #define OV5640_REG_DEBUG_MODE 0x4814 @@ -1043,12 +1045,31 @@ static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate) (ilog2(pclk_div) << 4)); } +/* set JPEG framing sizes */ +static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +{ + int ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact); + if (ret < 0) + return ret; + + return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact); +} + /* download ov5640 settings to sensor through i2c */ static int ov5640_set_timings(struct ov5640_dev *sensor, const struct ov5640_mode_info *mode) { int ret; + if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) { + ret = ov5640_set_jpeg_timings(sensor, mode); + if (ret < 0) + return ret; + } + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact); if (ret < 0) return ret; -- cgit v1.2.3-59-g8ed1b From 2b5c18f964df7bb8b7c8319bdaf0548080bb9152 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 18 Jan 2019 03:52:06 -0500 Subject: media: ov5640: Consolidate JPEG compression mode setting The register value lists for all the supported resolution settings all include a register address/value pair for setting the JPEG compression mode. With the exception of 1080p (which sets mode 2), all resolutions use mode 3. The only difference between mode 2 and mode 3 is that mode 2 may have padding data on the last line, while mode 3 does not add padding data. As these register values were from dumps of running systems, and the difference between the modes is quite small, using mode 3 for all configurations should be OK. [Sakari Ailus: Align OV5640_REG_JPG_MODE_SELECT register naming.] Signed-off-by: Chen-Yu Tsai Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 1c1dc401c678..d2efb94a5720 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -85,6 +85,7 @@ #define OV5640_REG_FORMAT_CONTROL00 0x4300 #define OV5640_REG_VFIFO_HSIZE 0x4602 #define OV5640_REG_VFIFO_VSIZE 0x4604 +#define OV5640_REG_JPG_MODE_SELECT 0x4713 #define OV5640_REG_POLARITY_CTRL00 0x4740 #define OV5640_REG_MIPI_CTRL00 0x4800 #define OV5640_REG_DEBUG_MODE 0x4814 @@ -303,7 +304,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0}, - {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, + {0x501f, 0x00, 0, 0}, {0x4407, 0x04, 0, 0}, {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x4837, 0x0a, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, @@ -372,7 +373,7 @@ static const struct reg_value ov5640_setting_VGA_640_480[] = { {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}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -391,7 +392,7 @@ static const struct reg_value ov5640_setting_XGA_1024_768[] = { {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}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -410,7 +411,7 @@ static const struct reg_value ov5640_setting_QVGA_320_240[] = { {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}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -429,7 +430,7 @@ static const struct reg_value ov5640_setting_QCIF_176_144[] = { {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}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -448,7 +449,7 @@ static const struct reg_value ov5640_setting_NTSC_720_480[] = { {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}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -467,7 +468,7 @@ static const struct reg_value ov5640_setting_PAL_720_576[] = { {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}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; @@ -486,7 +487,7 @@ static const struct reg_value ov5640_setting_720P_1280_720[] = { {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, 0x03, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, }; @@ -506,7 +507,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {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}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, @@ -518,7 +519,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {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}, + {0x3a15, 0x60, 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}, }; @@ -537,7 +538,7 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = { {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}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70}, }; @@ -1051,6 +1052,17 @@ static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor, { int ret; + /* + * compression mode 3 timing + * + * Data is transmitted with programmable width (VFIFO_HSIZE). + * No padding done. Last line may have less data. Varying + * number of lines per frame, depending on amount of data. + */ + ret = ov5640_mod_reg(sensor, OV5640_REG_JPG_MODE_SELECT, 0x7, 0x3); + if (ret < 0) + return ret; + ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact); if (ret < 0) return ret; -- cgit v1.2.3-59-g8ed1b From 6530a5eb99954699743885e800d88be88752d1ab Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Thu, 24 Jan 2019 12:58:01 -0500 Subject: media: ov5640: Fix set 15fps regression The ov5640_try_frame_interval operation updates the FPS as per user input based on default ov5640_frame_rate, OV5640_30_FPS which is failed to update when user trigger 15fps. So, initialize the default ov5640_frame_rate to OV5640_15_FPS so-that it can satisfy to update all fps. Fixes: 5a3ad937bc78 ("media: ov5640: Make the return rate type more explicit") Signed-off-by: Jagan Teki Acked-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index d2efb94a5720..2387c29b7fb0 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -2105,7 +2105,7 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor, u32 width, u32 height) { const struct ov5640_mode_info *mode; - enum ov5640_frame_rate rate = OV5640_30_FPS; + enum ov5640_frame_rate rate = OV5640_15_FPS; int minfps, maxfps, best_fps, fps; int i; -- cgit v1.2.3-59-g8ed1b From 1d4c41f3d887bcd66e82cb2fda124533dad8808a Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 30 Jan 2019 11:48:07 -0500 Subject: media: i2c: ov5640: Fix post-reset delay According to the ov5640 specification (2.7 power up sequence), host can access the sensor's registers 20ms after reset. Trying to access them before leads to undefined behavior and result in sporadic initialization errors. Signed-off-by: Loic Poulain Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 2387c29b7fb0..5278c8723fdd 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -1939,7 +1939,7 @@ static void ov5640_reset(struct ov5640_dev *sensor) usleep_range(1000, 2000); gpiod_set_value_cansleep(sensor->reset_gpio, 0); - usleep_range(5000, 10000); + usleep_range(20000, 25000); } static int ov5640_set_power_on(struct ov5640_dev *sensor) -- cgit v1.2.3-59-g8ed1b From 27b795adb3c221eede4aaf0df4a3da694ca27ba3 Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Thu, 7 Feb 2019 14:08:55 -0500 Subject: media: ipu3-imgu: Prefix functions with imgu_* instead of ipu3_* This addresses the below TODO item, no function related changes: - Prefix imgu for all public APIs, i.e. change ipu3_v4l2_register() to imgu_v4l2_register(). (Sakari) The changes were obtained by applying the following perl script to driver code under drivers/staging/media/ipu3/. perl -pi.back -e 's/ipu3_(?!uapi)/imgu_/g;' Signed-off-by: Yong Zhi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/ipu3/TODO | 3 - drivers/staging/media/ipu3/ipu3-css-fw.c | 18 +- drivers/staging/media/ipu3/ipu3-css-fw.h | 8 +- drivers/staging/media/ipu3/ipu3-css-params.c | 270 ++++++++-------- drivers/staging/media/ipu3/ipu3-css-params.h | 8 +- drivers/staging/media/ipu3/ipu3-css-pool.c | 32 +- drivers/staging/media/ipu3/ipu3-css-pool.h | 30 +- drivers/staging/media/ipu3/ipu3-css.c | 454 +++++++++++++-------------- drivers/staging/media/ipu3/ipu3-css.h | 92 +++--- drivers/staging/media/ipu3/ipu3-dmamap.c | 42 +-- drivers/staging/media/ipu3/ipu3-dmamap.h | 14 +- drivers/staging/media/ipu3/ipu3-mmu.c | 120 +++---- drivers/staging/media/ipu3/ipu3-mmu.h | 18 +- drivers/staging/media/ipu3/ipu3-tables.c | 50 +-- drivers/staging/media/ipu3/ipu3-tables.h | 54 ++-- drivers/staging/media/ipu3/ipu3-v4l2.c | 290 ++++++++--------- drivers/staging/media/ipu3/ipu3.c | 86 ++--- drivers/staging/media/ipu3/ipu3.h | 18 +- 18 files changed, 802 insertions(+), 805 deletions(-) diff --git a/drivers/staging/media/ipu3/TODO b/drivers/staging/media/ipu3/TODO index 0dc9a2e79978..8b95e74e43a0 100644 --- a/drivers/staging/media/ipu3/TODO +++ b/drivers/staging/media/ipu3/TODO @@ -8,9 +8,6 @@ staging directory. - Using ENABLED and IMMUTABLE link flags for the links where those are relevant. (Sakari) -- Prefix imgu for all public APIs, i.e. change ipu3_v4l2_register() to - imgu_v4l2_register(). (Sakari) - - IPU3 driver documentation (Laurent) Add diagram in driver rst to describe output capability. Comments on configuring v4l2 subdevs for CIO2 and ImgU. diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.c b/drivers/staging/media/ipu3/ipu3-css-fw.c index 55861aa8fb03..4122d4e42db6 100644 --- a/drivers/staging/media/ipu3/ipu3-css-fw.c +++ b/drivers/staging/media/ipu3/ipu3-css-fw.c @@ -10,7 +10,7 @@ #include "ipu3-css-fw.h" #include "ipu3-dmamap.h" -static void ipu3_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi, +static void imgu_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi, const char *name) { unsigned int i; @@ -54,7 +54,7 @@ static void ipu3_css_fw_show_binary(struct device *dev, struct imgu_fw_info *bi, dev_dbg(dev, "\n"); } -unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi) +unsigned int imgu_css_fw_obgrid_size(const struct imgu_fw_info *bi) { unsigned int width = DIV_ROUND_UP(bi->info.isp.sp.internal.max_width, IMGU_OBGRID_TILE_SIZE * 2) + 1; @@ -69,7 +69,7 @@ unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi) return obgrid_size; } -void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe, +void *imgu_css_fw_pipeline_params(struct imgu_css *css, unsigned int pipe, enum imgu_abi_param_class cls, enum imgu_abi_memories mem, struct imgu_fw_isp_parameter *par, @@ -91,7 +91,7 @@ void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe, return binary_params + par->offset; } -void ipu3_css_fw_cleanup(struct ipu3_css *css) +void imgu_css_fw_cleanup(struct imgu_css *css) { struct imgu_device *imgu = dev_get_drvdata(css->dev); @@ -99,7 +99,7 @@ void ipu3_css_fw_cleanup(struct ipu3_css *css) unsigned int i; for (i = 0; i < css->fwp->file_header.binary_nr; i++) - ipu3_dmamap_free(imgu, &css->binary[i]); + imgu_dmamap_free(imgu, &css->binary[i]); kfree(css->binary); } if (css->fw) @@ -109,7 +109,7 @@ void ipu3_css_fw_cleanup(struct ipu3_css *css) css->fw = NULL; } -int ipu3_css_fw_init(struct ipu3_css *css) +int imgu_css_fw_init(struct imgu_css *css) { static const u32 BLOCK_MAX = 65536; struct imgu_device *imgu = dev_get_drvdata(css->dev); @@ -227,7 +227,7 @@ int ipu3_css_fw_init(struct ipu3_css *css) css->fw->size) goto bad_fw; - ipu3_css_fw_show_binary(dev, bi, name); + imgu_css_fw_show_binary(dev, bi, name); } if (css->fw_bl == -1 || css->fw_sp[0] == -1 || css->fw_sp[1] == -1) @@ -246,7 +246,7 @@ int ipu3_css_fw_init(struct ipu3_css *css) void *blob = (void *)css->fwp + bi->blob.offset; size_t size = bi->blob.size; - if (!ipu3_dmamap_alloc(imgu, &css->binary[i], size)) { + if (!imgu_dmamap_alloc(imgu, &css->binary[i], size)) { r = -ENOMEM; goto error_out; } @@ -260,6 +260,6 @@ bad_fw: r = -ENODEV; error_out: - ipu3_css_fw_cleanup(css); + imgu_css_fw_cleanup(css); return r; } diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.h b/drivers/staging/media/ipu3/ipu3-css-fw.h index 07d8bb8b25f3..79ffa7045139 100644 --- a/drivers/staging/media/ipu3/ipu3-css-fw.h +++ b/drivers/staging/media/ipu3/ipu3-css-fw.h @@ -175,11 +175,11 @@ struct imgu_fw_header { /******************* Firmware functions *******************/ -int ipu3_css_fw_init(struct ipu3_css *css); -void ipu3_css_fw_cleanup(struct ipu3_css *css); +int imgu_css_fw_init(struct imgu_css *css); +void imgu_css_fw_cleanup(struct imgu_css *css); -unsigned int ipu3_css_fw_obgrid_size(const struct imgu_fw_info *bi); -void *ipu3_css_fw_pipeline_params(struct ipu3_css *css, unsigned int pipe, +unsigned int imgu_css_fw_obgrid_size(const struct imgu_fw_info *bi); +void *imgu_css_fw_pipeline_params(struct imgu_css *css, unsigned int pipe, enum imgu_abi_param_class cls, enum imgu_abi_memories mem, struct imgu_fw_isp_parameter *par, diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c index 053edce54b71..4533dacad4be 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.c +++ b/drivers/staging/media/ipu3/ipu3-css-params.c @@ -14,7 +14,7 @@ #define IPU3_UAPI_ANR_MAX_RESET ((1 << 12) - 1) #define IPU3_UAPI_ANR_MIN_RESET (((-1) << 12) + 1) -struct ipu3_css_scaler_info { +struct imgu_css_scaler_info { unsigned int phase_step; /* Same for luma/chroma */ int exp_shift; @@ -25,7 +25,7 @@ struct ipu3_css_scaler_info { int crop_top; }; -static unsigned int ipu3_css_scaler_get_exp(unsigned int counter, +static unsigned int imgu_css_scaler_get_exp(unsigned int counter, unsigned int divider) { int i = fls(divider) - fls(counter); @@ -41,13 +41,13 @@ static unsigned int ipu3_css_scaler_get_exp(unsigned int counter, /* Set up the CSS scaler look up table */ static void -ipu3_css_scaler_setup_lut(unsigned int taps, unsigned int input_width, +imgu_css_scaler_setup_lut(unsigned int taps, unsigned int input_width, unsigned int output_width, int phase_step_correction, const int *coeffs, unsigned int coeffs_size, - s8 coeff_lut[], struct ipu3_css_scaler_info *info) + s8 coeff_lut[], struct imgu_css_scaler_info *info) { int tap, phase, phase_sum_left, phase_sum_right; - int exponent = ipu3_css_scaler_get_exp(output_width, input_width); + int exponent = imgu_css_scaler_get_exp(output_width, input_width); int mantissa = (1 << exponent) * output_width; unsigned int phase_step; @@ -114,8 +114,8 @@ ipu3_css_scaler_setup_lut(unsigned int taps, unsigned int input_width, * (must be perfectly aligned with hardware). */ static unsigned int -ipu3_css_scaler_calc_scaled_output(unsigned int input, - struct ipu3_css_scaler_info *info) +imgu_css_scaler_calc_scaled_output(unsigned int input, + struct imgu_css_scaler_info *info) { unsigned int arg1 = input * info->phase_step + (1 - IMGU_SCALER_TAPS_Y / 2) * IMGU_SCALER_FIR_PHASES - @@ -133,10 +133,10 @@ ipu3_css_scaler_calc_scaled_output(unsigned int input, * and chroma details of a scaler */ static void -ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, +imgu_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, u32 target_height, struct imgu_abi_osys_config *cfg, - struct ipu3_css_scaler_info *info_luma, - struct ipu3_css_scaler_info *info_chroma, + struct imgu_css_scaler_info *info_luma, + struct imgu_css_scaler_info *info_chroma, unsigned int *output_width, unsigned int *output_height, unsigned int *procmode) { @@ -165,24 +165,24 @@ ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, do { phase_step_correction++; - ipu3_css_scaler_setup_lut(IMGU_SCALER_TAPS_Y, + imgu_css_scaler_setup_lut(IMGU_SCALER_TAPS_Y, input_width, target_width, phase_step_correction, - ipu3_css_downscale_4taps, + imgu_css_downscale_4taps, IMGU_SCALER_DOWNSCALE_4TAPS_LEN, cfg->scaler_coeffs_luma, info_luma); - ipu3_css_scaler_setup_lut(IMGU_SCALER_TAPS_UV, + imgu_css_scaler_setup_lut(IMGU_SCALER_TAPS_UV, input_width, target_width, phase_step_correction, - ipu3_css_downscale_2taps, + imgu_css_downscale_2taps, IMGU_SCALER_DOWNSCALE_2TAPS_LEN, cfg->scaler_coeffs_chroma, info_chroma); - out_width = ipu3_css_scaler_calc_scaled_output(input_width, + out_width = imgu_css_scaler_calc_scaled_output(input_width, info_luma); - out_height = ipu3_css_scaler_calc_scaled_output(input_height, + out_height = imgu_css_scaler_calc_scaled_output(input_height, info_luma); } while ((out_width < target_width || out_height < target_height || !IS_ALIGNED(out_height, height_alignment)) && @@ -194,7 +194,7 @@ ipu3_css_scaler_calc(u32 input_width, u32 input_height, u32 target_width, /********************** Osys routines for scaler****************************/ -static void ipu3_css_osys_set_format(enum imgu_abi_frame_format host_format, +static void imgu_css_osys_set_format(enum imgu_abi_frame_format host_format, unsigned int *osys_format, unsigned int *osys_tiling) { @@ -231,7 +231,7 @@ static void ipu3_css_osys_set_format(enum imgu_abi_frame_format host_format, * Function calculates input frame stripe offset, based * on output frame stripe offset and filter parameters. */ -static int ipu3_css_osys_calc_stripe_offset(int stripe_offset_out, +static int imgu_css_osys_calc_stripe_offset(int stripe_offset_out, int fir_phases, int phase_init, int phase_step, int pad_left) { @@ -245,12 +245,12 @@ static int ipu3_css_osys_calc_stripe_offset(int stripe_offset_out, * Calculate input frame phase, given the output frame * stripe offset and filter parameters */ -static int ipu3_css_osys_calc_stripe_phase_init(int stripe_offset_out, +static int imgu_css_osys_calc_stripe_phase_init(int stripe_offset_out, int fir_phases, int phase_init, int phase_step, int pad_left) { int stripe_offset_inp = - ipu3_css_osys_calc_stripe_offset(stripe_offset_out, + imgu_css_osys_calc_stripe_offset(stripe_offset_out, fir_phases, phase_init, phase_step, pad_left); @@ -262,7 +262,7 @@ static int ipu3_css_osys_calc_stripe_phase_init(int stripe_offset_out, * This function calculates input frame stripe width, * based on output frame stripe offset and filter parameters */ -static int ipu3_css_osys_calc_inp_stripe_width(int stripe_width_out, +static int imgu_css_osys_calc_inp_stripe_width(int stripe_width_out, int fir_phases, int phase_init, int phase_step, int fir_taps, int pad_left, int pad_right) @@ -279,7 +279,7 @@ static int ipu3_css_osys_calc_inp_stripe_width(int stripe_width_out, * This function calculates output frame stripe width, basedi * on output frame stripe offset and filter parameters */ -static int ipu3_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases, +static int imgu_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases, int phase_init, int phase_step, int fir_taps, int pad_left, int pad_right, int column_offset) @@ -292,7 +292,7 @@ static int ipu3_css_osys_out_stripe_width(int stripe_width_inp, int fir_phases, return stripe_width_out - (fir_taps - 1); } -struct ipu3_css_reso { +struct imgu_css_reso { unsigned int input_width; unsigned int input_height; enum imgu_abi_frame_format input_format; @@ -306,7 +306,7 @@ struct ipu3_css_reso { int block_width; }; -struct ipu3_css_frame_params { +struct imgu_css_frame_params { /* Output pins */ unsigned int enable; unsigned int format; @@ -322,7 +322,7 @@ struct ipu3_css_frame_params { unsigned int crop_top; }; -struct ipu3_css_stripe_params { +struct imgu_css_stripe_params { unsigned int processing_mode; unsigned int phase_step; unsigned int exp_shift; @@ -359,20 +359,20 @@ struct ipu3_css_stripe_params { * frame_params - size IMGU_ABI_OSYS_PINS * stripe_params - size IPU3_UAPI_MAX_STRIPES */ -static int ipu3_css_osys_calc_frame_and_stripe_params( - struct ipu3_css *css, unsigned int stripes, +static int imgu_css_osys_calc_frame_and_stripe_params( + struct imgu_css *css, unsigned int stripes, struct imgu_abi_osys_config *osys, - struct ipu3_css_scaler_info *scaler_luma, - struct ipu3_css_scaler_info *scaler_chroma, - struct ipu3_css_frame_params frame_params[], - struct ipu3_css_stripe_params stripe_params[], + struct imgu_css_scaler_info *scaler_luma, + struct imgu_css_scaler_info *scaler_chroma, + struct imgu_css_frame_params frame_params[], + struct imgu_css_stripe_params stripe_params[], unsigned int pipe) { - struct ipu3_css_reso reso; + struct imgu_css_reso reso; unsigned int output_width, pin, s; u32 input_width, input_height, target_width, target_height; unsigned int procmode = 0; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; input_width = css_pipe->rect[IPU3_CSS_RECT_GDC].width; input_height = css_pipe->rect[IPU3_CSS_RECT_GDC].height; @@ -464,7 +464,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( scaled = 1; } } - ipu3_css_osys_set_format(reso.pin_format[pin], &format, + imgu_css_osys_set_format(reso.pin_format[pin], &format, &tiling); } else { enable = 0; @@ -476,7 +476,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( frame_params[pin].scaled = scaled; } - ipu3_css_scaler_calc(input_width, input_height, target_width, + imgu_css_scaler_calc(input_width, input_height, target_width, target_height, osys, scaler_luma, scaler_chroma, &reso.pin_width[IMGU_ABI_OSYS_PIN_VF], &reso.pin_height[IMGU_ABI_OSYS_PIN_VF], &procmode); @@ -581,14 +581,14 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( stripe_offset_out_uv = stripe_offset_out_y / IMGU_LUMA_TO_CHROMA_RATIO; stripe_offset_inp_y = - ipu3_css_osys_calc_stripe_offset( + imgu_css_osys_calc_stripe_offset( stripe_offset_out_y, IMGU_OSYS_FIR_PHASES, scaler_luma->phase_init, scaler_luma->phase_step, scaler_luma->pad_left); stripe_offset_inp_uv = - ipu3_css_osys_calc_stripe_offset( + imgu_css_osys_calc_stripe_offset( stripe_offset_out_uv, IMGU_OSYS_FIR_PHASES, scaler_chroma->phase_init, @@ -597,14 +597,14 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( /* Calculate stripe phase init */ stripe_phase_init_y = - ipu3_css_osys_calc_stripe_phase_init( + imgu_css_osys_calc_stripe_phase_init( stripe_offset_out_y, IMGU_OSYS_FIR_PHASES, scaler_luma->phase_init, scaler_luma->phase_step, scaler_luma->pad_left); stripe_phase_init_uv = - ipu3_css_osys_calc_stripe_phase_init( + imgu_css_osys_calc_stripe_phase_init( stripe_offset_out_uv, IMGU_OSYS_FIR_PHASES, scaler_chroma->phase_init, @@ -708,7 +708,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( IMGU_LUMA_TO_CHROMA_RATIO; /* Calculate input stripe width */ stripe_input_width_y = stripe_offset_col_y + - ipu3_css_osys_calc_inp_stripe_width( + imgu_css_osys_calc_inp_stripe_width( stripe_output_width_y, IMGU_OSYS_FIR_PHASES, stripe_phase_init_y, @@ -718,7 +718,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( stripe_pad_right_y); stripe_input_width_uv = stripe_offset_col_uv + - ipu3_css_osys_calc_inp_stripe_width( + imgu_css_osys_calc_inp_stripe_width( stripe_output_width_uv, IMGU_OSYS_FIR_PHASES, stripe_phase_init_uv, @@ -753,7 +753,7 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( */ stripe_input_width_y = ALIGN(stripe_input_width_y, 8); stripe_output_width_y = - ipu3_css_osys_out_stripe_width( + imgu_css_osys_out_stripe_width( stripe_input_width_y, IMGU_OSYS_FIR_PHASES, stripe_phase_init_y, @@ -847,23 +847,23 @@ static int ipu3_css_osys_calc_frame_and_stripe_params( * This function configures the Output Formatter System, given the number of * stripes, scaler luma and chrome parameters */ -static int ipu3_css_osys_calc(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_osys_calc(struct imgu_css *css, unsigned int pipe, unsigned int stripes, struct imgu_abi_osys_config *osys, - struct ipu3_css_scaler_info *scaler_luma, - struct ipu3_css_scaler_info *scaler_chroma, + struct imgu_css_scaler_info *scaler_luma, + struct imgu_css_scaler_info *scaler_chroma, struct imgu_abi_stripes block_stripes[]) { - struct ipu3_css_frame_params frame_params[IMGU_ABI_OSYS_PINS]; - struct ipu3_css_stripe_params stripe_params[IPU3_UAPI_MAX_STRIPES]; + struct imgu_css_frame_params frame_params[IMGU_ABI_OSYS_PINS]; + struct imgu_css_stripe_params stripe_params[IPU3_UAPI_MAX_STRIPES]; struct imgu_abi_osys_formatter_params *param; unsigned int pin, s; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; memset(osys, 0, sizeof(*osys)); /* Compute the frame and stripe params */ - if (ipu3_css_osys_calc_frame_and_stripe_params(css, stripes, osys, + if (imgu_css_osys_calc_frame_and_stripe_params(css, stripes, osys, scaler_luma, scaler_chroma, frame_params, @@ -1252,7 +1252,7 @@ static int ipu3_css_osys_calc(struct ipu3_css *css, unsigned int pipe, */ static int -ipu3_css_shd_ops_calc(struct imgu_abi_shd_intra_frame_operations_data *ops, +imgu_css_shd_ops_calc(struct imgu_abi_shd_intra_frame_operations_data *ops, const struct ipu3_uapi_shd_grid_config *grid, unsigned int image_height) { @@ -1496,7 +1496,7 @@ struct process_lines { /* Helper to config intra_frame_operations_data. */ static int -ipu3_css_acc_process_lines(const struct process_lines *pl, +imgu_css_acc_process_lines(const struct process_lines *pl, struct imgu_abi_acc_operation *p_op, struct imgu_abi_acc_process_lines_cmd_data *p_pl, struct imgu_abi_acc_transfer_op_data *p_tr) @@ -1633,12 +1633,12 @@ ipu3_css_acc_process_lines(const struct process_lines *pl, return 0; } -static int ipu3_css_af_ops_calc(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_af_ops_calc(struct imgu_css *css, unsigned int pipe, struct imgu_abi_af_config *af_config) { struct imgu_abi_af_intra_frame_operations_data *to = &af_config->operations_data; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; @@ -1656,17 +1656,17 @@ static int ipu3_css_af_ops_calc(struct ipu3_css *css, unsigned int pipe, .acc_enable = bi->info.isp.sp.enable.af, }; - return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data, NULL); } static int -ipu3_css_awb_fr_ops_calc(struct ipu3_css *css, unsigned int pipe, +imgu_css_awb_fr_ops_calc(struct imgu_css *css, unsigned int pipe, struct imgu_abi_awb_fr_config *awb_fr_config) { struct imgu_abi_awb_fr_intra_frame_operations_data *to = &awb_fr_config->operations_data; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; struct process_lines pl = { @@ -1683,16 +1683,16 @@ ipu3_css_awb_fr_ops_calc(struct ipu3_css *css, unsigned int pipe, .acc_enable = bi->info.isp.sp.enable.awb_fr_acc, }; - return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data, NULL); } -static int ipu3_css_awb_ops_calc(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_awb_ops_calc(struct imgu_css *css, unsigned int pipe, struct imgu_abi_awb_config *awb_config) { struct imgu_abi_awb_intra_frame_operations_data *to = &awb_config->operations_data; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; @@ -1709,33 +1709,33 @@ static int ipu3_css_awb_ops_calc(struct ipu3_css *css, unsigned int pipe, .acc_enable = bi->info.isp.sp.enable.awb_acc, }; - return ipu3_css_acc_process_lines(&pl, to->ops, to->process_lines_data, + return imgu_css_acc_process_lines(&pl, to->ops, to->process_lines_data, to->transfer_data); } -static u16 ipu3_css_grid_end(u16 start, u8 width, u8 block_width_log2) +static u16 imgu_css_grid_end(u16 start, u8 width, u8 block_width_log2) { return (start & IPU3_UAPI_GRID_START_MASK) + (width << block_width_log2) - 1; } -static void ipu3_css_grid_end_calc(struct ipu3_uapi_grid_config *grid_cfg) +static void imgu_css_grid_end_calc(struct ipu3_uapi_grid_config *grid_cfg) { - grid_cfg->x_end = ipu3_css_grid_end(grid_cfg->x_start, grid_cfg->width, + grid_cfg->x_end = imgu_css_grid_end(grid_cfg->x_start, grid_cfg->width, grid_cfg->block_width_log2); - grid_cfg->y_end = ipu3_css_grid_end(grid_cfg->y_start, grid_cfg->height, + grid_cfg->y_end = imgu_css_grid_end(grid_cfg->y_start, grid_cfg->height, grid_cfg->block_height_log2); } /****************** config computation *****************************/ -static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe, +static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, struct imgu_abi_acc_param *acc) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; - struct ipu3_css_scaler_info scaler_luma, scaler_chroma; + struct imgu_css_scaler_info scaler_luma, scaler_chroma; const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; const unsigned int f = IPU3_UAPI_ISP_VEC_ELEMS * 2; unsigned int bds_ds, i; @@ -1744,7 +1744,7 @@ static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe, /* acc_param: osys_config */ - if (ipu3_css_osys_calc(css, pipe, stripes, &acc->osys, &scaler_luma, + if (imgu_css_osys_calc(css, pipe, stripes, &acc->osys, &scaler_luma, &scaler_chroma, acc->stripe.block_stripes)) return -EINVAL; @@ -1901,12 +1901,12 @@ static int ipu3_css_cfg_acc_stripe(struct ipu3_css *css, unsigned int pipe, return 0; } -static void ipu3_css_cfg_acc_dvs(struct ipu3_css *css, +static void imgu_css_cfg_acc_dvs(struct imgu_css *css, struct imgu_abi_acc_param *acc, unsigned int pipe) { unsigned int i; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; /* Disable DVS statistics */ acc->dvs_stat.operations_data.process_lines_data[0].lines = @@ -1920,11 +1920,11 @@ static void ipu3_css_cfg_acc_dvs(struct ipu3_css *css, acc->dvs_stat.cfg.grd_config[i].enable = 0; } -static void acc_bds_per_stripe_data(struct ipu3_css *css, +static void acc_bds_per_stripe_data(struct imgu_css *css, struct imgu_abi_acc_param *acc, const int i, unsigned int pipe) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; acc->bds.per_stripe.aligned_data[i].data.crop.hor_crop_en = 0; acc->bds.per_stripe.aligned_data[i].data.crop.hor_crop_start = 0; @@ -1945,13 +1945,13 @@ static void acc_bds_per_stripe_data(struct ipu3_css *css, * telling which fields to take from the old values (or generate if it is NULL) * and which to take from the new user values. */ -int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_acc(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, struct imgu_abi_acc_param *acc, struct imgu_abi_acc_param *acc_old, struct ipu3_uapi_acc_param *acc_user) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; @@ -1960,7 +1960,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, const unsigned int min_overlap = 10; const struct v4l2_pix_format_mplane *pixm = &css_pipe->queue[IPU3_CSS_QUEUE_IN].fmt.mpix; - const struct ipu3_css_bds_config *cfg_bds; + const struct imgu_css_bds_config *cfg_bds; struct imgu_abi_input_feeder_data *feeder_data; unsigned int bds_ds, ofs_x, ofs_y, i, width, height; @@ -1968,7 +1968,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, /* Update stripe using chroma and luma */ - if (ipu3_css_cfg_acc_stripe(css, pipe, acc)) + if (imgu_css_cfg_acc_stripe(css, pipe, acc)) return -EINVAL; /* acc_param: input_feeder_config */ @@ -2022,7 +2022,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->bnr = acc_old->bnr; } else { /* Calculate from scratch */ - acc->bnr = ipu3_css_bnr_defaults; + acc->bnr = imgu_css_bnr_defaults; } acc->bnr.column_size = tnr_frame_width; @@ -2050,7 +2050,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->dm = acc_old->dm; } else { /* Calculate from scratch */ - acc->dm = ipu3_css_dm_defaults; + acc->dm = imgu_css_dm_defaults; } acc->dm.frame_width = tnr_frame_width; @@ -2065,7 +2065,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->ccm = acc_old->ccm; } else { /* Calculate from scratch */ - acc->ccm = ipu3_css_ccm_defaults; + acc->ccm = imgu_css_ccm_defaults; } /* acc_param: gamma_config */ @@ -2079,7 +2079,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, } else { /* Calculate from scratch */ acc->gamma.gc_ctrl.enable = 1; - acc->gamma.gc_lut = ipu3_css_gamma_lut; + acc->gamma.gc_lut = imgu_css_gamma_lut; } /* acc_param: csc_mat_config */ @@ -2092,7 +2092,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->csc = acc_old->csc; } else { /* Calculate from scratch */ - acc->csc = ipu3_css_csc_defaults; + acc->csc = imgu_css_csc_defaults; } /* acc_param: cds_params */ @@ -2105,7 +2105,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->cds = acc_old->cds; } else { /* Calculate from scratch */ - acc->cds = ipu3_css_cds_defaults; + acc->cds = imgu_css_cds_defaults; } /* acc_param: shd_config */ @@ -2120,7 +2120,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->shd.shd_lut = acc_old->shd.shd_lut; } else { /* Calculate from scratch */ - acc->shd.shd = ipu3_css_shd_defaults; + acc->shd.shd = imgu_css_shd_defaults; memset(&acc->shd.shd_lut, 0, sizeof(acc->shd.shd_lut)); } @@ -2138,12 +2138,12 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->shd.shd.grid.block_height_log2) % acc->shd.shd.grid.grid_height_per_slice; - if (ipu3_css_shd_ops_calc(&acc->shd.shd_ops, &acc->shd.shd.grid, + if (imgu_css_shd_ops_calc(&acc->shd.shd_ops, &acc->shd.shd.grid, css_pipe->rect[IPU3_CSS_RECT_BDS].height)) return -EINVAL; /* acc_param: dvs_stat_config */ - ipu3_css_cfg_acc_dvs(css, acc, pipe); + imgu_css_cfg_acc_dvs(css, acc, pipe); /* acc_param: yuvp1_iefd_config */ @@ -2155,7 +2155,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->iefd = acc_old->iefd; } else { /* Calculate from scratch */ - acc->iefd = ipu3_css_iefd_defaults; + acc->iefd = imgu_css_iefd_defaults; } /* acc_param: yuvp1_yds_config yds_c0 */ @@ -2168,7 +2168,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->yds_c0 = acc_old->yds_c0; } else { /* Calculate from scratch */ - acc->yds_c0 = ipu3_css_yds_defaults; + acc->yds_c0 = imgu_css_yds_defaults; } /* acc_param: yuvp1_chnr_config chnr_c0 */ @@ -2181,7 +2181,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->chnr_c0 = acc_old->chnr_c0; } else { /* Calculate from scratch */ - acc->chnr_c0 = ipu3_css_chnr_defaults; + acc->chnr_c0 = imgu_css_chnr_defaults; } /* acc_param: yuvp1_y_ee_nr_config */ @@ -2194,7 +2194,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->y_ee_nr = acc_old->y_ee_nr; } else { /* Calculate from scratch */ - acc->y_ee_nr = ipu3_css_y_ee_nr_defaults; + acc->y_ee_nr = imgu_css_y_ee_nr_defaults; } /* acc_param: yuvp1_yds_config yds */ @@ -2207,7 +2207,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->yds = acc_old->yds; } else { /* Calculate from scratch */ - acc->yds = ipu3_css_yds_defaults; + acc->yds = imgu_css_yds_defaults; } /* acc_param: yuvp1_chnr_config chnr */ @@ -2220,7 +2220,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->chnr = acc_old->chnr; } else { /* Calculate from scratch */ - acc->chnr = ipu3_css_chnr_defaults; + acc->chnr = imgu_css_chnr_defaults; } /* acc_param: yuvp2_y_tm_lut_static_config */ @@ -2239,7 +2239,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->yds2 = acc_old->yds2; } else { /* Calculate from scratch */ - acc->yds2 = ipu3_css_yds_defaults; + acc->yds2 = imgu_css_yds_defaults; } /* acc_param: yuvp2_tcc_static_config */ @@ -2271,8 +2271,8 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, for (i = 7; i < IPU3_UAPI_YUVP2_TCC_INV_Y_LUT_ELEMENTS; i++) acc->tcc.inv_y_lut.entries[i] = 1024 >> (i - 6); - acc->tcc.gain_pcwl = ipu3_css_tcc_gain_pcwl_lut; - acc->tcc.r_sqr_lut = ipu3_css_tcc_r_sqr_lut; + acc->tcc.gain_pcwl = imgu_css_tcc_gain_pcwl_lut; + acc->tcc.r_sqr_lut = imgu_css_tcc_r_sqr_lut; } /* acc_param: dpc_config */ @@ -2288,10 +2288,10 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, bds_ds = (css_pipe->rect[IPU3_CSS_RECT_EFFECTIVE].height * IMGU_BDS_GRANULARITY) / css_pipe->rect[IPU3_CSS_RECT_BDS].height; if (bds_ds < IMGU_BDS_MIN_SF_INV || - bds_ds - IMGU_BDS_MIN_SF_INV >= ARRAY_SIZE(ipu3_css_bds_configs)) + bds_ds - IMGU_BDS_MIN_SF_INV >= ARRAY_SIZE(imgu_css_bds_configs)) return -EINVAL; - cfg_bds = &ipu3_css_bds_configs[bds_ds - IMGU_BDS_MIN_SF_INV]; + cfg_bds = &imgu_css_bds_configs[bds_ds - IMGU_BDS_MIN_SF_INV]; acc->bds.hor.hor_ctrl1.hor_crop_en = 0; acc->bds.hor.hor_ctrl1.hor_crop_start = 0; acc->bds.hor.hor_ctrl1.hor_crop_end = 0; @@ -2340,7 +2340,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, sizeof(acc->anr.stitch.pyramid)); } else { /* Calculate from scratch */ - acc->anr = ipu3_css_anr_defaults; + acc->anr = imgu_css_anr_defaults; } /* Always enabled */ @@ -2378,10 +2378,10 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb_fr.config = acc_old->awb_fr.config; } else { /* Set from scratch */ - acc->awb_fr.config = ipu3_css_awb_fr_defaults; + acc->awb_fr.config = imgu_css_awb_fr_defaults; } - ipu3_css_grid_end_calc(&acc->awb_fr.config.grid_cfg); + imgu_css_grid_end_calc(&acc->awb_fr.config.grid_cfg); if (acc->awb_fr.config.grid_cfg.width <= 0) return -EINVAL; @@ -2416,7 +2416,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb_fr.stripes[0].grid_cfg.width; b_w_log2 = acc->awb_fr.stripes[0].grid_cfg.block_width_log2; - end = ipu3_css_grid_end(acc->awb_fr.stripes[0].grid_cfg.x_start, + end = imgu_css_grid_end(acc->awb_fr.stripes[0].grid_cfg.x_start, acc->awb_fr.stripes[0].grid_cfg.width, b_w_log2); acc->awb_fr.stripes[0].grid_cfg.x_end = end; @@ -2426,7 +2426,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->stripe.down_scaled_stripes[1].offset) & IPU3_UAPI_GRID_START_MASK; b_w_log2 = acc->awb_fr.stripes[1].grid_cfg.block_width_log2; - end = ipu3_css_grid_end(acc->awb_fr.stripes[1].grid_cfg.x_start, + end = imgu_css_grid_end(acc->awb_fr.stripes[1].grid_cfg.x_start, acc->awb_fr.stripes[1].grid_cfg.width, b_w_log2); acc->awb_fr.stripes[1].grid_cfg.x_end = end; @@ -2440,7 +2440,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb_fr.stripes[i].grid_cfg.height_per_slice = 1; } - if (ipu3_css_awb_fr_ops_calc(css, pipe, &acc->awb_fr)) + if (imgu_css_awb_fr_ops_calc(css, pipe, &acc->awb_fr)) return -EINVAL; /* acc_param: ae_config */ @@ -2462,18 +2462,18 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, static const struct ipu3_uapi_ae_weight_elem weight_def = { 1, 1, 1, 1, 1, 1, 1, 1 }; - acc->ae.grid_cfg = ipu3_css_ae_grid_defaults; - acc->ae.ae_ccm = ipu3_css_ae_ccm_defaults; + acc->ae.grid_cfg = imgu_css_ae_grid_defaults; + acc->ae.ae_ccm = imgu_css_ae_ccm_defaults; for (i = 0; i < IPU3_UAPI_AE_WEIGHTS; i++) acc->ae.weights[i] = weight_def; } b_w_log2 = acc->ae.grid_cfg.block_width_log2; - acc->ae.grid_cfg.x_end = ipu3_css_grid_end(acc->ae.grid_cfg.x_start, + acc->ae.grid_cfg.x_end = imgu_css_grid_end(acc->ae.grid_cfg.x_start, acc->ae.grid_cfg.width, b_w_log2); b_w_log2 = acc->ae.grid_cfg.block_height_log2; - acc->ae.grid_cfg.y_end = ipu3_css_grid_end(acc->ae.grid_cfg.y_start, + acc->ae.grid_cfg.y_end = imgu_css_grid_end(acc->ae.grid_cfg.y_start, acc->ae.grid_cfg.height, b_w_log2); @@ -2502,7 +2502,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->ae.stripes[0].grid.block_width_log2; acc->ae.stripes[0].grid.x_end = - ipu3_css_grid_end(acc->ae.stripes[0].grid.x_start, + imgu_css_grid_end(acc->ae.stripes[0].grid.x_start, acc->ae.stripes[0].grid.width, b_w_log2); @@ -2512,7 +2512,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, IPU3_UAPI_GRID_START_MASK; b_w_log2 = acc->ae.stripes[1].grid.block_width_log2; acc->ae.stripes[1].grid.x_end = - ipu3_css_grid_end(acc->ae.stripes[1].grid.x_start, + imgu_css_grid_end(acc->ae.stripes[1].grid.x_start, acc->ae.stripes[1].grid.width, b_w_log2); } @@ -2529,11 +2529,11 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, } else { /* Set from scratch */ acc->af.config.filter_config = - ipu3_css_af_defaults.filter_config; - acc->af.config.grid_cfg = ipu3_css_af_defaults.grid_cfg; + imgu_css_af_defaults.filter_config; + acc->af.config.grid_cfg = imgu_css_af_defaults.grid_cfg; } - ipu3_css_grid_end_calc(&acc->af.config.grid_cfg); + imgu_css_grid_end_calc(&acc->af.config.grid_cfg); if (acc->af.config.grid_cfg.width <= 0) return -EINVAL; @@ -2579,7 +2579,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->af.stripes[0].grid_cfg.block_width_log2; acc->af.stripes[0].grid_cfg.x_end = - ipu3_css_grid_end(acc->af.stripes[0].grid_cfg.x_start, + imgu_css_grid_end(acc->af.stripes[0].grid_cfg.x_start, acc->af.stripes[0].grid_cfg.width, b_w_log2); @@ -2590,7 +2590,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->af.stripes[1].grid_cfg.block_width_log2; acc->af.stripes[1].grid_cfg.x_end = - ipu3_css_grid_end(acc->af.stripes[1].grid_cfg.x_start, + imgu_css_grid_end(acc->af.stripes[1].grid_cfg.x_start, acc->af.stripes[1].grid_cfg.width, b_w_log2); @@ -2602,7 +2602,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->af.stripes[i].grid_cfg.height_per_slice = 1; } - if (ipu3_css_af_ops_calc(css, pipe, &acc->af)) + if (imgu_css_af_ops_calc(css, pipe, &acc->af)) return -EINVAL; /* acc_param: awb_config */ @@ -2615,7 +2615,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb.config = acc_old->awb.config; } else { /* Set from scratch */ - acc->awb.config = ipu3_css_awb_defaults; + acc->awb.config = imgu_css_awb_defaults; } if (acc->awb.config.grid.width <= 0) @@ -2623,7 +2623,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb.config.grid.height_per_slice = IMGU_ABI_AWB_MAX_CELLS_PER_SET / acc->awb.config.grid.width, - ipu3_css_grid_end_calc(&acc->awb.config.grid); + imgu_css_grid_end_calc(&acc->awb.config.grid); for (i = 0; i < stripes; i++) acc->awb.stripes[i] = acc->awb.config; @@ -2648,7 +2648,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->awb.stripes[0].grid.block_width_log2; acc->awb.stripes[0].grid.x_end = - ipu3_css_grid_end(acc->awb.stripes[0].grid.x_start, + imgu_css_grid_end(acc->awb.stripes[0].grid.x_start, acc->awb.stripes[0].grid.width, b_w_log2); @@ -2659,7 +2659,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, b_w_log2 = acc->awb.stripes[1].grid.block_width_log2; acc->awb.stripes[1].grid.x_end = - ipu3_css_grid_end(acc->awb.stripes[1].grid.x_start, + imgu_css_grid_end(acc->awb.stripes[1].grid.x_start, acc->awb.stripes[1].grid.width, b_w_log2); @@ -2671,7 +2671,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, acc->awb.stripes[i].grid.height_per_slice = 1; } - if (ipu3_css_awb_ops_calc(css, pipe, &acc->awb)) + if (imgu_css_awb_ops_calc(css, pipe, &acc->awb)) return -EINVAL; return 0; @@ -2686,7 +2686,7 @@ int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, * to the structure inside `new_binary_params'. In that case the caller * should calculate and fill the structure from scratch. */ -static void *ipu3_css_cfg_copy(struct ipu3_css *css, +static void *imgu_css_cfg_copy(struct imgu_css *css, unsigned int pipe, bool use_user, void *user_setting, void *old_binary_params, void *new_binary_params, @@ -2697,7 +2697,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css, const enum imgu_abi_param_class c = IMGU_ABI_PARAM_CLASS_PARAM; void *new_setting, *old_setting; - new_setting = ipu3_css_fw_pipeline_params(css, pipe, c, m, par, + new_setting = imgu_css_fw_pipeline_params(css, pipe, c, m, par, par_size, new_binary_params); if (!new_setting) return ERR_PTR(-EPROTO); /* Corrupted firmware */ @@ -2707,7 +2707,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css, memcpy(new_setting, user_setting, par_size); } else if (old_binary_params) { /* Take previous value */ - old_setting = ipu3_css_fw_pipeline_params(css, pipe, c, m, par, + old_setting = imgu_css_fw_pipeline_params(css, pipe, c, m, par, par_size, old_binary_params); if (!old_setting) @@ -2723,7 +2723,7 @@ static void *ipu3_css_cfg_copy(struct ipu3_css *css, /* * Configure VMEM0 parameters (late binding parameters). */ -int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_vmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *vmem0, void *vmem0_old, struct ipu3_uapi_params *user) @@ -2745,7 +2745,7 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* Configure Linearization VMEM0 parameters */ - lin_vmem = ipu3_css_cfg_copy(css, pipe, use && use->lin_vmem_params, + lin_vmem = imgu_css_cfg_copy(css, pipe, use && use->lin_vmem_params, &user->lin_vmem_params, vmem0_old, vmem0, m, &pofs->vmem.lin, sizeof(*lin_vmem)); if (!IS_ERR_OR_NULL(lin_vmem)) { @@ -2765,7 +2765,7 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* Configure TNR3 VMEM parameters */ if (css->pipes[pipe].pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { - tnr_vmem = ipu3_css_cfg_copy(css, pipe, + tnr_vmem = imgu_css_cfg_copy(css, pipe, use && use->tnr3_vmem_params, &user->tnr3_vmem_params, vmem0_old, vmem0, m, @@ -2781,17 +2781,17 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* Configure XNR3 VMEM parameters */ - xnr_vmem = ipu3_css_cfg_copy(css, pipe, use && use->xnr3_vmem_params, + xnr_vmem = imgu_css_cfg_copy(css, pipe, use && use->xnr3_vmem_params, &user->xnr3_vmem_params, vmem0_old, vmem0, m, &pofs->vmem.xnr3, sizeof(*xnr_vmem)); if (!IS_ERR_OR_NULL(xnr_vmem)) { - xnr_vmem->x[i] = ipu3_css_xnr3_vmem_defaults.x + xnr_vmem->x[i] = imgu_css_xnr3_vmem_defaults.x [i % IMGU_XNR3_VMEM_LUT_LEN]; - xnr_vmem->a[i] = ipu3_css_xnr3_vmem_defaults.a + xnr_vmem->a[i] = imgu_css_xnr3_vmem_defaults.a [i % IMGU_XNR3_VMEM_LUT_LEN]; - xnr_vmem->b[i] = ipu3_css_xnr3_vmem_defaults.b + xnr_vmem->b[i] = imgu_css_xnr3_vmem_defaults.b [i % IMGU_XNR3_VMEM_LUT_LEN]; - xnr_vmem->c[i] = ipu3_css_xnr3_vmem_defaults.c + xnr_vmem->c[i] = imgu_css_xnr3_vmem_defaults.c [i % IMGU_XNR3_VMEM_LUT_LEN]; } @@ -2802,12 +2802,12 @@ int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, /* * Configure DMEM0 parameters (late binding parameters). */ -int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_dmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *dmem0, void *dmem0_old, struct ipu3_uapi_params *user) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; struct imgu_fw_param_memory_offsets *pofs = (void *)css->fwp + @@ -2825,7 +2825,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, /* Configure TNR3 DMEM0 parameters */ if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { - tnr_dmem = ipu3_css_cfg_copy(css, pipe, + tnr_dmem = imgu_css_cfg_copy(css, pipe, use && use->tnr3_dmem_params, &user->tnr3_dmem_params, dmem0_old, dmem0, m, @@ -2840,7 +2840,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, /* Configure XNR3 DMEM0 parameters */ - xnr_dmem = ipu3_css_cfg_copy(css, pipe, use && use->xnr3_dmem_params, + xnr_dmem = imgu_css_cfg_copy(css, pipe, use && use->xnr3_dmem_params, &user->xnr3_dmem_params, dmem0_old, dmem0, m, &pofs->dmem.xnr3, sizeof(*xnr_dmem)); if (!IS_ERR_OR_NULL(xnr_dmem)) { @@ -2854,7 +2854,7 @@ int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, } /* Generate unity morphing table without morphing effect */ -void ipu3_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, +void imgu_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, int frame_in_x, int frame_in_y, int frame_out_x, int frame_out_y, int env_w, int env_h) diff --git a/drivers/staging/media/ipu3/ipu3-css-params.h b/drivers/staging/media/ipu3/ipu3-css-params.h index f3a0a47117a4..ffaec6b7d5cc 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.h +++ b/drivers/staging/media/ipu3/ipu3-css-params.h @@ -4,23 +4,23 @@ #ifndef __IPU3_PARAMS_H #define __IPU3_PARAMS_H -int ipu3_css_cfg_acc(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_acc(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, struct imgu_abi_acc_param *acc, struct imgu_abi_acc_param *acc_old, struct ipu3_uapi_acc_param *acc_user); -int ipu3_css_cfg_vmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_vmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *vmem0, void *vmem0_old, struct ipu3_uapi_params *user); -int ipu3_css_cfg_dmem0(struct ipu3_css *css, unsigned int pipe, +int imgu_css_cfg_dmem0(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_flags *use, void *dmem0, void *dmem0_old, struct ipu3_uapi_params *user); -void ipu3_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, +void imgu_css_cfg_gdc_table(struct imgu_abi_gdc_warp_param *gdc, int frame_in_x, int frame_in_y, int frame_out_x, int frame_out_y, int env_w, int env_h); diff --git a/drivers/staging/media/ipu3/ipu3-css-pool.c b/drivers/staging/media/ipu3/ipu3-css-pool.c index 6f271f81669b..fa5b7d3acef2 100644 --- a/drivers/staging/media/ipu3/ipu3-css-pool.c +++ b/drivers/staging/media/ipu3/ipu3-css-pool.c @@ -7,30 +7,30 @@ #include "ipu3-css-pool.h" #include "ipu3-dmamap.h" -int ipu3_css_dma_buffer_resize(struct imgu_device *imgu, - struct ipu3_css_map *map, size_t size) +int imgu_css_dma_buffer_resize(struct imgu_device *imgu, + struct imgu_css_map *map, size_t size) { if (map->size < size && map->vaddr) { dev_warn(&imgu->pci_dev->dev, "dma buf resized from %zu to %zu", map->size, size); - ipu3_dmamap_free(imgu, map); - if (!ipu3_dmamap_alloc(imgu, map, size)) + imgu_dmamap_free(imgu, map); + if (!imgu_dmamap_alloc(imgu, map, size)) return -ENOMEM; } return 0; } -void ipu3_css_pool_cleanup(struct imgu_device *imgu, struct ipu3_css_pool *pool) +void imgu_css_pool_cleanup(struct imgu_device *imgu, struct imgu_css_pool *pool) { unsigned int i; for (i = 0; i < IPU3_CSS_POOL_SIZE; i++) - ipu3_dmamap_free(imgu, &pool->entry[i].param); + imgu_dmamap_free(imgu, &pool->entry[i].param); } -int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, +int imgu_css_pool_init(struct imgu_device *imgu, struct imgu_css_pool *pool, size_t size) { unsigned int i; @@ -42,7 +42,7 @@ int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, continue; } - if (!ipu3_dmamap_alloc(imgu, &pool->entry[i].param, size)) + if (!imgu_dmamap_alloc(imgu, &pool->entry[i].param, size)) goto fail; } @@ -51,14 +51,14 @@ int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, return 0; fail: - ipu3_css_pool_cleanup(imgu, pool); + imgu_css_pool_cleanup(imgu, pool); return -ENOMEM; } /* * Allocate a new parameter via recycling the oldest entry in the pool. */ -void ipu3_css_pool_get(struct ipu3_css_pool *pool) +void imgu_css_pool_get(struct imgu_css_pool *pool) { /* Get the oldest entry */ u32 n = (pool->last + 1) % IPU3_CSS_POOL_SIZE; @@ -70,25 +70,25 @@ void ipu3_css_pool_get(struct ipu3_css_pool *pool) /* * Undo, for all practical purposes, the effect of pool_get(). */ -void ipu3_css_pool_put(struct ipu3_css_pool *pool) +void imgu_css_pool_put(struct imgu_css_pool *pool) { pool->entry[pool->last].valid = false; pool->last = (pool->last + IPU3_CSS_POOL_SIZE - 1) % IPU3_CSS_POOL_SIZE; } /** - * ipu3_css_pool_last - Retrieve the nth pool entry from last + * imgu_css_pool_last - Retrieve the nth pool entry from last * - * @pool: a pointer to &struct ipu3_css_pool. + * @pool: a pointer to &struct imgu_css_pool. * @n: the distance to the last index. * * Returns: * The nth entry from last or null map to indicate no frame stored. */ -const struct ipu3_css_map * -ipu3_css_pool_last(struct ipu3_css_pool *pool, unsigned int n) +const struct imgu_css_map * +imgu_css_pool_last(struct imgu_css_pool *pool, unsigned int n) { - static const struct ipu3_css_map null_map = { 0 }; + static const struct imgu_css_map null_map = { 0 }; int i = (pool->last + IPU3_CSS_POOL_SIZE - n) % IPU3_CSS_POOL_SIZE; WARN_ON(n >= IPU3_CSS_POOL_SIZE); diff --git a/drivers/staging/media/ipu3/ipu3-css-pool.h b/drivers/staging/media/ipu3/ipu3-css-pool.h index 2657c39a4d71..f4a60b41401b 100644 --- a/drivers/staging/media/ipu3/ipu3-css-pool.h +++ b/drivers/staging/media/ipu3/ipu3-css-pool.h @@ -10,15 +10,15 @@ struct imgu_device; #define IPU3_CSS_POOL_SIZE 4 /** - * ipu3_css_map - store DMA mapping info for buffer + * imgu_css_map - store DMA mapping info for buffer * * @size: size of the buffer in bytes. * @vaddr: kernel virtual address. * @daddr: iova dma address to access IPU3. * @vma: private, a pointer to &struct vm_struct, - * used for ipu3_dmamap_free. + * used for imgu_dmamap_free. */ -struct ipu3_css_map { +struct imgu_css_map { size_t size; void *vaddr; dma_addr_t daddr; @@ -26,30 +26,30 @@ struct ipu3_css_map { }; /** - * ipu3_css_pool - circular buffer pool definition + * imgu_css_pool - circular buffer pool definition * * @entry: array with IPU3_CSS_POOL_SIZE elements. - * @entry.param: a &struct ipu3_css_map for storing the mem mapping. + * @entry.param: a &struct imgu_css_map for storing the mem mapping. * @entry.valid: used to mark if the entry has valid data. * @last: write pointer, initialized to IPU3_CSS_POOL_SIZE. */ -struct ipu3_css_pool { +struct imgu_css_pool { struct { - struct ipu3_css_map param; + struct imgu_css_map param; bool valid; } entry[IPU3_CSS_POOL_SIZE]; u32 last; }; -int ipu3_css_dma_buffer_resize(struct imgu_device *imgu, - struct ipu3_css_map *map, size_t size); -void ipu3_css_pool_cleanup(struct imgu_device *imgu, - struct ipu3_css_pool *pool); -int ipu3_css_pool_init(struct imgu_device *imgu, struct ipu3_css_pool *pool, +int imgu_css_dma_buffer_resize(struct imgu_device *imgu, + struct imgu_css_map *map, size_t size); +void imgu_css_pool_cleanup(struct imgu_device *imgu, + struct imgu_css_pool *pool); +int imgu_css_pool_init(struct imgu_device *imgu, struct imgu_css_pool *pool, size_t size); -void ipu3_css_pool_get(struct ipu3_css_pool *pool); -void ipu3_css_pool_put(struct ipu3_css_pool *pool); -const struct ipu3_css_map *ipu3_css_pool_last(struct ipu3_css_pool *pool, +void imgu_css_pool_get(struct imgu_css_pool *pool); +void imgu_css_pool_put(struct imgu_css_pool *pool); +const struct imgu_css_map *imgu_css_pool_last(struct imgu_css_pool *pool, u32 last); #endif diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c index b9354d2bb692..15ab77e4b766 100644 --- a/drivers/staging/media/ipu3/ipu3-css.c +++ b/drivers/staging/media/ipu3/ipu3-css.c @@ -46,7 +46,7 @@ IPU3_CSS_QUEUE_TO_FLAGS(IPU3_CSS_QUEUE_VF) /* Formats supported by IPU3 Camera Sub System */ -static const struct ipu3_css_format ipu3_css_formats[] = { +static const struct imgu_css_format imgu_css_formats[] = { { .pixelformat = V4L2_PIX_FMT_NV12, .colorspace = V4L2_COLORSPACE_SRGB, @@ -100,7 +100,7 @@ static const struct ipu3_css_format ipu3_css_formats[] = { static const struct { enum imgu_abi_queue_id qid; size_t ptr_ofs; -} ipu3_css_queues[IPU3_CSS_QUEUES] = { +} imgu_css_queues[IPU3_CSS_QUEUES] = { [IPU3_CSS_QUEUE_IN] = { IMGU_ABI_QUEUE_C_ID, offsetof(struct imgu_abi_buffer, payload.frame.frame_data) @@ -120,7 +120,7 @@ static const struct { }; /* Initialize queue based on given format, adjust format as needed */ -static int ipu3_css_queue_init(struct ipu3_css_queue *queue, +static int imgu_css_queue_init(struct imgu_css_queue *queue, struct v4l2_pix_format_mplane *fmt, u32 flags) { struct v4l2_pix_format_mplane *const f = &queue->fmt.mpix; @@ -133,11 +133,11 @@ static int ipu3_css_queue_init(struct ipu3_css_queue *queue, if (!fmt) return 0; - for (i = 0; i < ARRAY_SIZE(ipu3_css_formats); i++) { - if (!(ipu3_css_formats[i].flags & flags)) + for (i = 0; i < ARRAY_SIZE(imgu_css_formats); i++) { + if (!(imgu_css_formats[i].flags & flags)) continue; - queue->css_fmt = &ipu3_css_formats[i]; - if (ipu3_css_formats[i].pixelformat == fmt->pixelformat) + queue->css_fmt = &imgu_css_formats[i]; + if (imgu_css_formats[i].pixelformat == fmt->pixelformat) break; } if (!queue->css_fmt) @@ -178,7 +178,7 @@ static int ipu3_css_queue_init(struct ipu3_css_queue *queue, return 0; } -static bool ipu3_css_queue_enabled(struct ipu3_css_queue *q) +static bool imgu_css_queue_enabled(struct imgu_css_queue *q) { return q->css_fmt; } @@ -200,7 +200,7 @@ static inline void writes(const void *mem, ssize_t count, void __iomem *addr) } /* Wait until register `reg', masked with `mask', becomes `cmp' */ -static int ipu3_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) +static int imgu_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) { u32 val; @@ -210,7 +210,7 @@ static int ipu3_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) /* Initialize the IPU3 CSS hardware and associated h/w blocks */ -int ipu3_css_set_powerup(struct device *dev, void __iomem *base) +int imgu_css_set_powerup(struct device *dev, void __iomem *base) { static const unsigned int freq = 450; u32 pm_ctrl, state, val; @@ -221,7 +221,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) writel(0, base + IMGU_REG_GP_BUSY); /* Wait for idle signal */ - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, IMGU_STATE_IDLE_STS)) { dev_err(dev, "failed to set CSS idle\n"); goto fail; @@ -245,7 +245,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) if (state & IMGU_STATE_POWER_DOWN) { writel(IMGU_PM_CTRL_RACE_TO_HALT | IMGU_PM_CTRL_START, base + IMGU_REG_PM_CTRL); - if (ipu3_hw_wait(base, IMGU_REG_PM_CTRL, + if (imgu_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) { dev_err(dev, "failed to power up CSS\n"); goto fail; @@ -263,7 +263,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) val = pm_ctrl & ~(IMGU_PM_CTRL_CSS_PWRDN | IMGU_PM_CTRL_RST_AT_EOF); writel(val, base + IMGU_REG_PM_CTRL); writel(0, base + IMGU_REG_GP_BUSY); - if (ipu3_hw_wait(base, IMGU_REG_STATE, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_PWRDNM_FSM_MASK, 0)) { dev_err(dev, "failed to pwrdn CSS\n"); goto fail; @@ -273,7 +273,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) writel(1, base + IMGU_REG_GP_BUSY); writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_FORCE_HALT, base + IMGU_REG_PM_CTRL); - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, IMGU_STATE_HALT_STS)) { dev_err(dev, "failed to halt CSS\n"); goto fail; @@ -281,7 +281,7 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) writel(readl(base + IMGU_REG_PM_CTRL) | IMGU_PM_CTRL_START, base + IMGU_REG_PM_CTRL); - if (ipu3_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) { + if (imgu_hw_wait(base, IMGU_REG_PM_CTRL, IMGU_PM_CTRL_START, 0)) { dev_err(dev, "failed to start CSS\n"); goto fail; } @@ -296,26 +296,26 @@ int ipu3_css_set_powerup(struct device *dev, void __iomem *base) return 0; fail: - ipu3_css_set_powerdown(dev, base); + imgu_css_set_powerdown(dev, base); return -EIO; } -void ipu3_css_set_powerdown(struct device *dev, void __iomem *base) +void imgu_css_set_powerdown(struct device *dev, void __iomem *base) { dev_dbg(dev, "%s\n", __func__); /* wait for cio idle signal */ - if (ipu3_hw_wait(base, IMGU_REG_CIO_GATE_BURST_STATE, + if (imgu_hw_wait(base, IMGU_REG_CIO_GATE_BURST_STATE, IMGU_CIO_GATE_BURST_MASK, 0)) dev_warn(dev, "wait cio gate idle timeout"); /* wait for css idle signal */ - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, IMGU_STATE_IDLE_STS)) dev_warn(dev, "wait css idle timeout\n"); /* do halt-halted handshake with css */ writel(1, base + IMGU_REG_GP_HALT); - if (ipu3_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, + if (imgu_hw_wait(base, IMGU_REG_STATE, IMGU_STATE_HALT_STS, IMGU_STATE_HALT_STS)) dev_warn(dev, "failed to halt css"); @@ -323,7 +323,7 @@ void ipu3_css_set_powerdown(struct device *dev, void __iomem *base) writel(0, base + IMGU_REG_GP_BUSY); } -static void ipu3_css_hw_enable_irq(struct ipu3_css *css) +static void imgu_css_hw_enable_irq(struct imgu_css *css) { void __iomem *const base = css->base; u32 val, i; @@ -371,7 +371,7 @@ static void ipu3_css_hw_enable_irq(struct ipu3_css *css) } } -static int ipu3_css_hw_init(struct ipu3_css *css) +static int imgu_css_hw_init(struct imgu_css *css) { /* For checking that streaming monitor statuses are valid */ static const struct { @@ -463,11 +463,11 @@ static int ipu3_css_hw_init(struct ipu3_css *css) /* Initialize GDC with default values */ - for (i = 0; i < ARRAY_SIZE(ipu3_css_gdc_lut[0]); i++) { - u32 val0 = ipu3_css_gdc_lut[0][i] & IMGU_GDC_LUT_MASK; - u32 val1 = ipu3_css_gdc_lut[1][i] & IMGU_GDC_LUT_MASK; - u32 val2 = ipu3_css_gdc_lut[2][i] & IMGU_GDC_LUT_MASK; - u32 val3 = ipu3_css_gdc_lut[3][i] & IMGU_GDC_LUT_MASK; + for (i = 0; i < ARRAY_SIZE(imgu_css_gdc_lut[0]); i++) { + u32 val0 = imgu_css_gdc_lut[0][i] & IMGU_GDC_LUT_MASK; + u32 val1 = imgu_css_gdc_lut[1][i] & IMGU_GDC_LUT_MASK; + u32 val2 = imgu_css_gdc_lut[2][i] & IMGU_GDC_LUT_MASK; + u32 val3 = imgu_css_gdc_lut[3][i] & IMGU_GDC_LUT_MASK; writel(val0 | (val1 << 16), base + IMGU_REG_GDC_LUT_BASE + i * 8); @@ -479,7 +479,7 @@ static int ipu3_css_hw_init(struct ipu3_css *css) } /* Boot the given IPU3 CSS SP */ -static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp) +static int imgu_css_hw_start_sp(struct imgu_css *css, int sp) { void __iomem *const base = css->base; struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[sp]]; @@ -501,7 +501,7 @@ static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp) writel(readl(base + IMGU_REG_SP_CTRL(sp)) | IMGU_CTRL_START | IMGU_CTRL_RUN, base + IMGU_REG_SP_CTRL(sp)); - if (ipu3_hw_wait(css->base, IMGU_REG_SP_DMEM_BASE(sp) + if (imgu_hw_wait(css->base, IMGU_REG_SP_DMEM_BASE(sp) + bi->info.sp.sw_state, ~0, IMGU_ABI_SP_SWSTATE_INITIALIZED)) return -EIO; @@ -510,7 +510,7 @@ static int ipu3_css_hw_start_sp(struct ipu3_css *css, int sp) } /* Start the IPU3 CSS ImgU (Imaging Unit) and all the SPs */ -static int ipu3_css_hw_start(struct ipu3_css *css) +static int imgu_css_hw_start(struct imgu_css *css) { static const u32 event_mask = ((1 << IMGU_ABI_EVTTYPE_OUT_FRAME_DONE) | @@ -560,7 +560,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) writel(readl(base + IMGU_REG_ISP_CTRL) | IMGU_CTRL_START | IMGU_CTRL_RUN, base + IMGU_REG_ISP_CTRL); - if (ipu3_hw_wait(css->base, IMGU_REG_ISP_DMEM_BASE + if (imgu_hw_wait(css->base, IMGU_REG_ISP_DMEM_BASE + bl->info.bl.sw_state, ~0, IMGU_ABI_BL_SWSTATE_OK)) { dev_err(css->dev, "failed to start bootloader\n"); @@ -581,7 +581,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.sw_state); writel(1, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.invalidate_tlb); - if (ipu3_css_hw_start_sp(css, 0)) + if (imgu_css_hw_start_sp(css, 0)) return -EIO; writel(0, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.isp_started); @@ -608,7 +608,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) writel(IMGU_ABI_SP_SWSTATE_TERMINATED, base + IMGU_REG_SP_DMEM_BASE(1) + bi->info.sp.sw_state); - if (ipu3_css_hw_start_sp(css, 1)) + if (imgu_css_hw_start_sp(css, 1)) return -EIO; writel(IMGU_ABI_SP_COMM_COMMAND_READY, base + IMGU_REG_SP_DMEM_BASE(1) @@ -617,7 +617,7 @@ static int ipu3_css_hw_start(struct ipu3_css *css) return 0; } -static void ipu3_css_hw_stop(struct ipu3_css *css) +static void imgu_css_hw_stop(struct imgu_css *css) { void __iomem *const base = css->base; struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[0]]; @@ -626,18 +626,18 @@ static void ipu3_css_hw_stop(struct ipu3_css *css) writel(IMGU_ABI_SP_COMM_COMMAND_TERMINATE, base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.host_sp_com + IMGU_ABI_SP_COMM_COMMAND); - if (ipu3_hw_wait(css->base, IMGU_REG_SP_CTRL(0), + if (imgu_hw_wait(css->base, IMGU_REG_SP_CTRL(0), IMGU_CTRL_IDLE, IMGU_CTRL_IDLE)) dev_err(css->dev, "wait sp0 idle timeout.\n"); if (readl(base + IMGU_REG_SP_DMEM_BASE(0) + bi->info.sp.sw_state) != IMGU_ABI_SP_SWSTATE_TERMINATED) dev_err(css->dev, "sp0 is not terminated.\n"); - if (ipu3_hw_wait(css->base, IMGU_REG_ISP_CTRL, + if (imgu_hw_wait(css->base, IMGU_REG_ISP_CTRL, IMGU_CTRL_IDLE, IMGU_CTRL_IDLE)) dev_err(css->dev, "wait isp idle timeout\n"); } -static void ipu3_css_hw_cleanup(struct ipu3_css *css) +static void imgu_css_hw_cleanup(struct imgu_css *css) { void __iomem *const base = css->base; @@ -648,7 +648,7 @@ static void ipu3_css_hw_cleanup(struct ipu3_css *css) writel(0, base + IMGU_REG_GP_BUSY); /* Wait for idle signal */ - if (ipu3_hw_wait(css->base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, + if (imgu_hw_wait(css->base, IMGU_REG_STATE, IMGU_STATE_IDLE_STS, IMGU_STATE_IDLE_STS)) dev_err(css->dev, "failed to shut down hw cleanly\n"); @@ -659,19 +659,19 @@ static void ipu3_css_hw_cleanup(struct ipu3_css *css) usleep_range(200, 300); } -static void ipu3_css_pipeline_cleanup(struct ipu3_css *css, unsigned int pipe) +static void imgu_css_pipeline_cleanup(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int i; - ipu3_css_pool_cleanup(imgu, + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.parameter_set_info); - ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.acc); - ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.gdc); - ipu3_css_pool_cleanup(imgu, &css->pipes[pipe].pool.obgrid); + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.acc); + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.gdc); + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.obgrid); for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - ipu3_css_pool_cleanup(imgu, + imgu_css_pool_cleanup(imgu, &css->pipes[pipe].pool.binary_params_p[i]); } @@ -679,7 +679,7 @@ static void ipu3_css_pipeline_cleanup(struct ipu3_css *css, unsigned int pipe) * This function initializes various stages of the * IPU3 CSS ISP pipeline */ -static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_pipeline_init(struct imgu_css *css, unsigned int pipe) { static const int BYPC = 2; /* Bytes per component */ static const struct imgu_abi_buffer_sp buffer_sp_init = { @@ -697,7 +697,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) const int stage = 0; unsigned int i, j; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; const unsigned int stripes = bi->info.isp.sp.iterator.num_stripes; @@ -725,7 +725,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure iterator */ - cfg_iter = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_iter = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.iterator, sizeof(*cfg_iter), vaddr); if (!cfg_iter) @@ -791,7 +791,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure reference (delay) frames */ - cfg_ref = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_ref = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.ref, sizeof(*cfg_ref), vaddr); if (!cfg_ref) @@ -821,7 +821,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure DVS (digital video stabilization) */ - cfg_dvs = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_dvs = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.dvs, sizeof(*cfg_dvs), vaddr); if (!cfg_dvs) @@ -837,7 +837,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure TNR (temporal noise reduction) */ if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { - cfg_tnr = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_tnr = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &cofs->dmem.tnr3, sizeof(*cfg_tnr), vaddr); @@ -868,7 +868,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) cfg = IMGU_ABI_PARAM_CLASS_STATE; vaddr = css_pipe->binary_params_cs[cfg - 1][m0].vaddr; - cfg_ref_state = ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + cfg_ref_state = imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &sofs->dmem.ref, sizeof(*cfg_ref_state), vaddr); @@ -881,7 +881,7 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Configure tnr dmem state parameters */ if (css_pipe->pipe_id == IPU3_CSS_PIPE_ID_VIDEO) { cfg_tnr_state = - ipu3_css_fw_pipeline_params(css, pipe, cfg, m0, + imgu_css_fw_pipeline_params(css, pipe, cfg, m0, &sofs->dmem.tnr3, sizeof(*cfg_tnr_state), vaddr); @@ -1068,21 +1068,21 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) /* Initialize parameter pools */ - if (ipu3_css_pool_init(imgu, &css_pipe->pool.parameter_set_info, + if (imgu_css_pool_init(imgu, &css_pipe->pool.parameter_set_info, sizeof(struct imgu_abi_parameter_set_info)) || - ipu3_css_pool_init(imgu, &css_pipe->pool.acc, + imgu_css_pool_init(imgu, &css_pipe->pool.acc, sizeof(struct imgu_abi_acc_param)) || - ipu3_css_pool_init(imgu, &css_pipe->pool.gdc, + imgu_css_pool_init(imgu, &css_pipe->pool.gdc, sizeof(struct imgu_abi_gdc_warp_param) * 3 * cfg_dvs->num_horizontal_blocks / 2 * cfg_dvs->num_vertical_blocks) || - ipu3_css_pool_init(imgu, &css_pipe->pool.obgrid, - ipu3_css_fw_obgrid_size( + imgu_css_pool_init(imgu, &css_pipe->pool.obgrid, + imgu_css_fw_obgrid_size( &css->fwp->binary_header[css_pipe->bindex]))) goto out_of_memory; for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - if (ipu3_css_pool_init(imgu, + if (imgu_css_pool_init(imgu, &css_pipe->pool.binary_params_p[i], bi->info.isp.sp.mem_initializers.params [IMGU_ABI_PARAM_CLASS_PARAM][i].size)) @@ -1091,15 +1091,15 @@ static int ipu3_css_pipeline_init(struct ipu3_css *css, unsigned int pipe) return 0; bad_firmware: - ipu3_css_pipeline_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); return -EPROTO; out_of_memory: - ipu3_css_pipeline_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); return -ENOMEM; } -static u8 ipu3_css_queue_pos(struct ipu3_css *css, int queue, int thread) +static u8 imgu_css_queue_pos(struct imgu_css *css, int queue, int thread) { static const unsigned int sp; void __iomem *const base = css->base; @@ -1112,7 +1112,7 @@ static u8 ipu3_css_queue_pos(struct ipu3_css *css, int queue, int thread) } /* Sent data to sp using given buffer queue, or if queue < 0, event queue. */ -static int ipu3_css_queue_data(struct ipu3_css *css, +static int imgu_css_queue_data(struct imgu_css *css, int queue, int thread, u32 data) { static const unsigned int sp; @@ -1151,7 +1151,7 @@ static int ipu3_css_queue_data(struct ipu3_css *css, } /* Receive data using given buffer queue, or if queue < 0, event queue. */ -static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data) +static int imgu_css_dequeue_data(struct imgu_css *css, int queue, u32 *data) { static const unsigned int sp; void __iomem *const base = css->base; @@ -1188,7 +1188,7 @@ static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data) writeb(start2, &q->sp2host_evtq_info.start); /* Acknowledge events dequeued from event queue */ - r = ipu3_css_queue_data(css, queue, 0, + r = imgu_css_queue_data(css, queue, 0, IMGU_ABI_EVENT_EVENT_DEQUEUED); if (r < 0) return r; @@ -1198,52 +1198,52 @@ static int ipu3_css_dequeue_data(struct ipu3_css *css, int queue, u32 *data) } /* Free binary-specific resources */ -static void ipu3_css_binary_cleanup(struct ipu3_css *css, unsigned int pipe) +static void imgu_css_binary_cleanup(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int i, j; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; for (j = 0; j < IMGU_ABI_PARAM_CLASS_NUM - 1; j++) for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->binary_params_cs[j][i]); j = IPU3_CSS_AUX_FRAME_REF; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->aux_frames[j].mem[i]); j = IPU3_CSS_AUX_FRAME_TNR; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->aux_frames[j].mem[i]); } -static int ipu3_css_binary_preallocate(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_binary_preallocate(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int i, j; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; for (j = IMGU_ABI_PARAM_CLASS_CONFIG; j < IMGU_ABI_PARAM_CLASS_NUM; j++) for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->binary_params_cs[j - 1][i], CSS_ABI_SIZE)) goto out_of_memory; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF]. mem[i], CSS_BDS_SIZE)) goto out_of_memory; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR]. mem[i], CSS_GDC_SIZE)) goto out_of_memory; @@ -1251,14 +1251,14 @@ static int ipu3_css_binary_preallocate(struct ipu3_css *css, unsigned int pipe) return 0; out_of_memory: - ipu3_css_binary_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); return -ENOMEM; } /* allocate binary-specific resources */ -static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_binary_setup(struct imgu_css *css, unsigned int pipe) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; struct imgu_fw_info *bi = &css->fwp->binary_header[css_pipe->bindex]; struct imgu_device *imgu = dev_get_drvdata(css->dev); int i, j, size; @@ -1269,7 +1269,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) for (j = IMGU_ABI_PARAM_CLASS_CONFIG; j < IMGU_ABI_PARAM_CLASS_NUM; j++) for (i = 0; i < IMGU_ABI_NUM_MEMORIES; i++) { - if (ipu3_css_dma_buffer_resize( + if (imgu_css_dma_buffer_resize( imgu, &css_pipe->binary_params_cs[j - 1][i], bi->info.isp.sp.mem_initializers.params[j][i].size)) @@ -1292,7 +1292,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].bytesperpixel * w; size = w * h * BYPC + (w / 2) * (h / 2) * BYPC * 2; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (ipu3_css_dma_buffer_resize( + if (imgu_css_dma_buffer_resize( imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i], size)) @@ -1313,7 +1313,7 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) h = css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].height; size = w * ALIGN(h * 3 / 2 + 3, 2); /* +3 for vf_pp prefetch */ for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) - if (ipu3_css_dma_buffer_resize( + if (imgu_css_dma_buffer_resize( imgu, &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].mem[i], size)) @@ -1322,11 +1322,11 @@ static int ipu3_css_binary_setup(struct ipu3_css *css, unsigned int pipe) return 0; out_of_memory: - ipu3_css_binary_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); return -ENOMEM; } -int ipu3_css_start_streaming(struct ipu3_css *css) +int imgu_css_start_streaming(struct imgu_css *css) { u32 data; int r, pipe; @@ -1335,48 +1335,48 @@ int ipu3_css_start_streaming(struct ipu3_css *css) return -EPROTO; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_binary_setup(css, pipe); + r = imgu_css_binary_setup(css, pipe); if (r < 0) return r; } - r = ipu3_css_hw_init(css); + r = imgu_css_hw_init(css); if (r < 0) return r; - r = ipu3_css_hw_start(css); + r = imgu_css_hw_start(css); if (r < 0) goto fail; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_pipeline_init(css, pipe); + r = imgu_css_pipeline_init(css, pipe); if (r < 0) goto fail; } css->streaming = true; - ipu3_css_hw_enable_irq(css); + imgu_css_hw_enable_irq(css); /* Initialize parameters to default */ for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_set_parameters(css, pipe, NULL); + r = imgu_css_set_parameters(css, pipe, NULL); if (r < 0) goto fail; } - while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_A_ID, &data))) + while (!(r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_A_ID, &data))) ; if (r != -EBUSY) goto fail; - while (!(r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_B_ID, &data))) + while (!(r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_B_ID, &data))) ; if (r != -EBUSY) goto fail; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_START_STREAM | pipe << 16); if (r < 0) @@ -1387,22 +1387,22 @@ int ipu3_css_start_streaming(struct ipu3_css *css) fail: css->streaming = false; - ipu3_css_hw_cleanup(css); + imgu_css_hw_cleanup(css); for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - ipu3_css_pipeline_cleanup(css, pipe); - ipu3_css_binary_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); } return r; } -void ipu3_css_stop_streaming(struct ipu3_css *css) +void imgu_css_stop_streaming(struct imgu_css *css) { - struct ipu3_css_buffer *b, *b0; + struct imgu_css_buffer *b, *b0; int q, r, pipe; for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_STOP_STREAM); if (r < 0) dev_warn(css->dev, "failed on stop stream event\n"); @@ -1411,14 +1411,14 @@ void ipu3_css_stop_streaming(struct ipu3_css *css) if (!css->streaming) return; - ipu3_css_hw_stop(css); + imgu_css_hw_stop(css); - ipu3_css_hw_cleanup(css); + imgu_css_hw_cleanup(css); for_each_set_bit(pipe, css->enabled_pipes, IMGU_MAX_PIPE_NUM) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; - ipu3_css_pipeline_cleanup(css, pipe); + imgu_css_pipeline_cleanup(css, pipe); spin_lock(&css_pipe->qlock); for (q = 0; q < IPU3_CSS_QUEUES; q++) @@ -1434,10 +1434,10 @@ void ipu3_css_stop_streaming(struct ipu3_css *css) css->streaming = false; } -bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe) +bool imgu_css_pipe_queue_empty(struct imgu_css *css, unsigned int pipe) { int q; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; spin_lock(&css_pipe->qlock); for (q = 0; q < IPU3_CSS_QUEUES; q++) @@ -1447,44 +1447,44 @@ bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe) return (q == IPU3_CSS_QUEUES); } -bool ipu3_css_queue_empty(struct ipu3_css *css) +bool imgu_css_queue_empty(struct imgu_css *css) { unsigned int pipe; bool ret = 0; for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) - ret &= ipu3_css_pipe_queue_empty(css, pipe); + ret &= imgu_css_pipe_queue_empty(css, pipe); return ret; } -bool ipu3_css_is_streaming(struct ipu3_css *css) +bool imgu_css_is_streaming(struct imgu_css *css) { return css->streaming; } -static int ipu3_css_map_init(struct ipu3_css *css, unsigned int pipe) +static int imgu_css_map_init(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; unsigned int p, q, i; /* Allocate and map common structures with imgu hardware */ for (p = 0; p < IPU3_CSS_PIPE_ID_NUM; p++) for (i = 0; i < IMGU_ABI_MAX_STAGES; i++) { - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe-> xmem_sp_stage_ptrs[p][i], sizeof(struct imgu_abi_sp_stage))) return -ENOMEM; - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe-> xmem_isp_stage_ptrs[p][i], sizeof(struct imgu_abi_isp_stage))) return -ENOMEM; } - if (!ipu3_dmamap_alloc(imgu, &css_pipe->sp_ddr_ptrs, + if (!imgu_dmamap_alloc(imgu, &css_pipe->sp_ddr_ptrs, ALIGN(sizeof(struct imgu_abi_ddr_address_map), IMGU_ABI_ISP_DDR_WORD_BYTES))) return -ENOMEM; @@ -1493,58 +1493,58 @@ static int ipu3_css_map_init(struct ipu3_css *css, unsigned int pipe) unsigned int abi_buf_num = ARRAY_SIZE(css_pipe->abi_buffers[q]); for (i = 0; i < abi_buf_num; i++) - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &css_pipe->abi_buffers[q][i], sizeof(struct imgu_abi_buffer))) return -ENOMEM; } - if (ipu3_css_binary_preallocate(css, pipe)) { - ipu3_css_binary_cleanup(css, pipe); + if (imgu_css_binary_preallocate(css, pipe)) { + imgu_css_binary_cleanup(css, pipe); return -ENOMEM; } return 0; } -static void ipu3_css_pipe_cleanup(struct ipu3_css *css, unsigned int pipe) +static void imgu_css_pipe_cleanup(struct imgu_css *css, unsigned int pipe) { struct imgu_device *imgu = dev_get_drvdata(css->dev); - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; unsigned int p, q, i, abi_buf_num; - ipu3_css_binary_cleanup(css, pipe); + imgu_css_binary_cleanup(css, pipe); for (q = 0; q < IPU3_CSS_QUEUES; q++) { abi_buf_num = ARRAY_SIZE(css_pipe->abi_buffers[q]); for (i = 0; i < abi_buf_num; i++) - ipu3_dmamap_free(imgu, &css_pipe->abi_buffers[q][i]); + imgu_dmamap_free(imgu, &css_pipe->abi_buffers[q][i]); } for (p = 0; p < IPU3_CSS_PIPE_ID_NUM; p++) for (i = 0; i < IMGU_ABI_MAX_STAGES; i++) { - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->xmem_sp_stage_ptrs[p][i]); - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &css_pipe->xmem_isp_stage_ptrs[p][i]); } - ipu3_dmamap_free(imgu, &css_pipe->sp_ddr_ptrs); + imgu_dmamap_free(imgu, &css_pipe->sp_ddr_ptrs); } -void ipu3_css_cleanup(struct ipu3_css *css) +void imgu_css_cleanup(struct imgu_css *css) { struct imgu_device *imgu = dev_get_drvdata(css->dev); unsigned int pipe; - ipu3_css_stop_streaming(css); + imgu_css_stop_streaming(css); for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) - ipu3_css_pipe_cleanup(css, pipe); - ipu3_dmamap_free(imgu, &css->xmem_sp_group_ptrs); - ipu3_css_fw_cleanup(css); + imgu_css_pipe_cleanup(css, pipe); + imgu_dmamap_free(imgu, &css->xmem_sp_group_ptrs); + imgu_css_fw_cleanup(css); } -int ipu3_css_init(struct device *dev, struct ipu3_css *css, +int imgu_css_init(struct device *dev, struct imgu_css *css, void __iomem *base, int length) { struct imgu_device *imgu = dev_get_drvdata(dev); @@ -1556,35 +1556,35 @@ int ipu3_css_init(struct device *dev, struct ipu3_css *css, css->iomem_length = length; for (pipe = 0; pipe < IMGU_MAX_PIPE_NUM; pipe++) { - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; css_pipe->vf_output_en = false; spin_lock_init(&css_pipe->qlock); css_pipe->bindex = IPU3_CSS_DEFAULT_BINARY; css_pipe->pipe_id = IPU3_CSS_PIPE_ID_VIDEO; for (q = 0; q < IPU3_CSS_QUEUES; q++) { - r = ipu3_css_queue_init(&css_pipe->queue[q], NULL, 0); + r = imgu_css_queue_init(&css_pipe->queue[q], NULL, 0); if (r) return r; } - r = ipu3_css_map_init(css, pipe); + r = imgu_css_map_init(css, pipe); if (r) { - ipu3_css_cleanup(css); + imgu_css_cleanup(css); return r; } } - if (!ipu3_dmamap_alloc(imgu, &css->xmem_sp_group_ptrs, + if (!imgu_dmamap_alloc(imgu, &css->xmem_sp_group_ptrs, sizeof(struct imgu_abi_sp_group))) return -ENOMEM; - r = ipu3_css_fw_init(css); + r = imgu_css_fw_init(css); if (r) return r; return 0; } -static u32 ipu3_css_adjust(u32 res, u32 align) +static u32 imgu_css_adjust(u32 res, u32 align) { u32 val = max_t(u32, IPU3_CSS_MIN_RES, res); @@ -1592,9 +1592,9 @@ static u32 ipu3_css_adjust(u32 res, u32 align) } /* Select a binary matching the required resolutions and formats */ -static int ipu3_css_find_binary(struct ipu3_css *css, +static int imgu_css_find_binary(struct imgu_css *css, unsigned int pipe, - struct ipu3_css_queue queue[IPU3_CSS_QUEUES], + struct imgu_css_queue queue[IPU3_CSS_QUEUES], struct v4l2_rect rects[IPU3_CSS_RECTS]) { const int binary_nr = css->fwp->file_header.binary_nr; @@ -1611,7 +1611,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, const char *name; int i, j; - if (!ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_IN])) + if (!imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_IN])) return -EINVAL; /* Find out the strip size boundary */ @@ -1659,7 +1659,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, in->height > bi->info.isp.sp.input.max_height) continue; - if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_OUT])) { + if (imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_OUT])) { if (bi->info.isp.num_output_pins <= 0) continue; @@ -1681,7 +1681,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, continue; } - if (ipu3_css_queue_enabled(&queue[IPU3_CSS_QUEUE_VF])) { + if (imgu_css_queue_enabled(&queue[IPU3_CSS_QUEUE_VF])) { if (bi->info.isp.num_output_pins <= 1) continue; @@ -1716,7 +1716,7 @@ static int ipu3_css_find_binary(struct ipu3_css *css, * found binary number. May modify the given parameters if not exact match * is found. */ -int ipu3_css_fmt_try(struct ipu3_css *css, +int imgu_css_fmt_try(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe) @@ -1744,7 +1744,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, struct v4l2_rect *const bds = &r[IPU3_CSS_RECT_BDS]; struct v4l2_rect *const env = &r[IPU3_CSS_RECT_ENVELOPE]; struct v4l2_rect *const gdc = &r[IPU3_CSS_RECT_GDC]; - struct ipu3_css_queue q[IPU3_CSS_QUEUES]; + struct imgu_css_queue q[IPU3_CSS_QUEUES]; struct v4l2_pix_format_mplane *const in = &q[IPU3_CSS_QUEUE_IN].fmt.mpix; struct v4l2_pix_format_mplane *const out = @@ -1762,7 +1762,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, else dev_dbg(css->dev, "%s %s: (not set)\n", __func__, qnames[i]); - if (ipu3_css_queue_init(&q[i], fmts[i], + if (imgu_css_queue_init(&q[i], fmts[i], IPU3_CSS_QUEUE_TO_FLAGS(i))) { dev_notice(css->dev, "can not initialize queue %s\n", qnames[i]); @@ -1785,13 +1785,13 @@ int ipu3_css_fmt_try(struct ipu3_css *css, } /* Always require one input and vf only if out is also enabled */ - if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_IN]) || - !ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { + if (!imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_IN]) || + !imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { dev_warn(css->dev, "required queues are disabled\n"); return -EINVAL; } - if (!ipu3_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { + if (!imgu_css_queue_enabled(&q[IPU3_CSS_QUEUE_OUT])) { out->width = in->width; out->height = in->height; } @@ -1808,25 +1808,25 @@ int ipu3_css_fmt_try(struct ipu3_css *css, gdc->height = out->height; } - in->width = ipu3_css_adjust(in->width, 1); - in->height = ipu3_css_adjust(in->height, 1); - eff->width = ipu3_css_adjust(eff->width, EFF_ALIGN_W); - eff->height = ipu3_css_adjust(eff->height, 1); - bds->width = ipu3_css_adjust(bds->width, BDS_ALIGN_W); - bds->height = ipu3_css_adjust(bds->height, 1); - gdc->width = ipu3_css_adjust(gdc->width, OUT_ALIGN_W); - gdc->height = ipu3_css_adjust(gdc->height, OUT_ALIGN_H); - out->width = ipu3_css_adjust(out->width, OUT_ALIGN_W); - out->height = ipu3_css_adjust(out->height, OUT_ALIGN_H); - vf->width = ipu3_css_adjust(vf->width, VF_ALIGN_W); - vf->height = ipu3_css_adjust(vf->height, 1); + in->width = imgu_css_adjust(in->width, 1); + in->height = imgu_css_adjust(in->height, 1); + eff->width = imgu_css_adjust(eff->width, EFF_ALIGN_W); + eff->height = imgu_css_adjust(eff->height, 1); + bds->width = imgu_css_adjust(bds->width, BDS_ALIGN_W); + bds->height = imgu_css_adjust(bds->height, 1); + gdc->width = imgu_css_adjust(gdc->width, OUT_ALIGN_W); + gdc->height = imgu_css_adjust(gdc->height, OUT_ALIGN_H); + out->width = imgu_css_adjust(out->width, OUT_ALIGN_W); + out->height = imgu_css_adjust(out->height, OUT_ALIGN_H); + vf->width = imgu_css_adjust(vf->width, VF_ALIGN_W); + vf->height = imgu_css_adjust(vf->height, 1); s = (bds->width - gdc->width) / 2 - FILTER_SIZE; env->width = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; s = (bds->height - gdc->height) / 2 - FILTER_SIZE; env->height = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; - ret = ipu3_css_find_binary(css, pipe, q, r); + ret = imgu_css_find_binary(css, pipe, q, r); if (ret < 0) { dev_err(css->dev, "failed to find suitable binary\n"); return -EINVAL; @@ -1839,7 +1839,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, /* Final adjustment and set back the queried formats */ for (i = 0; i < IPU3_CSS_QUEUES; i++) { if (fmts[i]) { - if (ipu3_css_queue_init(&q[i], &q[i].fmt.mpix, + if (imgu_css_queue_init(&q[i], &q[i].fmt.mpix, IPU3_CSS_QUEUE_TO_FLAGS(i))) { dev_err(css->dev, "final resolution adjustment failed\n"); @@ -1862,7 +1862,7 @@ int ipu3_css_fmt_try(struct ipu3_css *css, return 0; } -int ipu3_css_fmt_set(struct ipu3_css *css, +int imgu_css_fmt_set(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe) @@ -1870,7 +1870,7 @@ int ipu3_css_fmt_set(struct ipu3_css *css, struct v4l2_rect rect_data[IPU3_CSS_RECTS]; struct v4l2_rect *all_rects[IPU3_CSS_RECTS]; int i, r; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; for (i = 0; i < IPU3_CSS_RECTS; i++) { if (rects[i]) @@ -1879,12 +1879,12 @@ int ipu3_css_fmt_set(struct ipu3_css *css, memset(&rect_data[i], 0, sizeof(rect_data[i])); all_rects[i] = &rect_data[i]; } - r = ipu3_css_fmt_try(css, fmts, all_rects, pipe); + r = imgu_css_fmt_try(css, fmts, all_rects, pipe); if (r < 0) return r; for (i = 0; i < IPU3_CSS_QUEUES; i++) - if (ipu3_css_queue_init(&css_pipe->queue[i], fmts[i], + if (imgu_css_queue_init(&css_pipe->queue[i], fmts[i], IPU3_CSS_QUEUE_TO_FLAGS(i))) return -EINVAL; for (i = 0; i < IPU3_CSS_RECTS; i++) { @@ -1896,7 +1896,7 @@ int ipu3_css_fmt_set(struct ipu3_css *css, return 0; } -int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt) +int imgu_css_meta_fmt_set(struct v4l2_meta_format *fmt) { switch (fmt->dataformat) { case V4L2_META_FMT_IPU3_PARAMS: @@ -1913,27 +1913,27 @@ int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt) } /* - * Queue given buffer to CSS. ipu3_css_buf_prepare() must have been first + * Queue given buffer to CSS. imgu_css_buf_prepare() must have been first * called for the buffer. May be called from interrupt context. * Returns 0 on success, -EBUSY if the buffer queue is full, or some other * code on error conditions. */ -int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, - struct ipu3_css_buffer *b) +int imgu_css_buf_queue(struct imgu_css *css, unsigned int pipe, + struct imgu_css_buffer *b) { struct imgu_abi_buffer *abi_buf; struct imgu_addr_t *buf_addr; u32 data; int r; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; if (!css->streaming) return -EPROTO; /* CSS or buffer in wrong state */ - if (b->queue >= IPU3_CSS_QUEUES || !ipu3_css_queues[b->queue].qid) + if (b->queue >= IPU3_CSS_QUEUES || !imgu_css_queues[b->queue].qid) return -EINVAL; - b->queue_pos = ipu3_css_queue_pos(css, ipu3_css_queues[b->queue].qid, + b->queue_pos = imgu_css_queue_pos(css, imgu_css_queues[b->queue].qid, pipe); if (b->queue_pos >= ARRAY_SIZE(css->pipes[pipe].abi_buffers[b->queue])) @@ -1943,7 +1943,7 @@ int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, /* Fill struct abi_buffer for firmware */ memset(abi_buf, 0, sizeof(*abi_buf)); - buf_addr = (void *)abi_buf + ipu3_css_queues[b->queue].ptr_ofs; + buf_addr = (void *)abi_buf + imgu_css_queues[b->queue].ptr_ofs; *(imgu_addr_t *)buf_addr = b->daddr; if (b->queue == IPU3_CSS_QUEUE_STAT_3A) @@ -1963,14 +1963,14 @@ int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, b->state = IPU3_CSS_BUFFER_QUEUED; data = css->pipes[pipe].abi_buffers[b->queue][b->queue_pos].daddr; - r = ipu3_css_queue_data(css, ipu3_css_queues[b->queue].qid, + r = imgu_css_queue_data(css, imgu_css_queues[b->queue].qid, pipe, data); if (r < 0) goto queueing_failed; data = IMGU_ABI_EVENT_BUFFER_ENQUEUED(pipe, - ipu3_css_queues[b->queue].qid); - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, data); + imgu_css_queues[b->queue].qid); + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, data); if (r < 0) goto queueing_failed; @@ -1992,7 +1992,7 @@ queueing_failed: * should be called again, or -EBUSY which means that there are no more * buffers available. May be called from interrupt context. */ -struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) +struct imgu_css_buffer *imgu_css_buf_dequeue(struct imgu_css *css) { static const unsigned char evtype_to_queue[] = { [IMGU_ABI_EVTTYPE_INPUT_FRAME_DONE] = IPU3_CSS_QUEUE_IN, @@ -2000,15 +2000,15 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) [IMGU_ABI_EVTTYPE_VF_OUT_FRAME_DONE] = IPU3_CSS_QUEUE_VF, [IMGU_ABI_EVTTYPE_3A_STATS_DONE] = IPU3_CSS_QUEUE_STAT_3A, }; - struct ipu3_css_buffer *b = ERR_PTR(-EAGAIN); + struct imgu_css_buffer *b = ERR_PTR(-EAGAIN); u32 event, daddr; int evtype, pipe, pipeid, queue, qid, r; - struct ipu3_css_pipe *css_pipe; + struct imgu_css_pipe *css_pipe; if (!css->streaming) return ERR_PTR(-EPROTO); - r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); + r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); if (r < 0) return ERR_PTR(r); @@ -2025,7 +2025,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) pipeid = (event & IMGU_ABI_EVTTYPE_PIPEID_MASK) >> IMGU_ABI_EVTTYPE_PIPEID_SHIFT; queue = evtype_to_queue[evtype]; - qid = ipu3_css_queues[queue].qid; + qid = imgu_css_queues[queue].qid; if (pipe >= IMGU_MAX_PIPE_NUM) { dev_err(css->dev, "Invalid pipe: %i\n", pipe); @@ -2041,14 +2041,14 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) "event: buffer done 0x%x queue %i pipe %i pipeid %i\n", event, queue, pipe, pipeid); - r = ipu3_css_dequeue_data(css, qid, &daddr); + r = imgu_css_dequeue_data(css, qid, &daddr); if (r < 0) { dev_err(css->dev, "failed to dequeue buffer\n"); /* Force real error, not -EBUSY */ return ERR_PTR(-EIO); } - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_BUFFER_DEQUEUED(qid)); if (r < 0) { dev_err(css->dev, "failed to queue event\n"); @@ -2062,7 +2062,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) return ERR_PTR(-EIO); } b = list_first_entry(&css_pipe->queue[queue].bufs, - struct ipu3_css_buffer, list); + struct imgu_css_buffer, list); if (queue != b->queue || daddr != css_pipe->abi_buffers [b->queue][b->queue_pos].daddr) { @@ -2090,7 +2090,7 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) event, pipe); break; case IMGU_ABI_EVTTYPE_TIMER: - r = ipu3_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); + r = imgu_css_dequeue_data(css, IMGU_ABI_QUEUE_EVENT_ID, &event); if (r < 0) return ERR_PTR(r); @@ -2128,11 +2128,11 @@ struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css) * Return index to css->parameter_set_info which has the newly created * parameters or negative value on error. */ -int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, +int imgu_css_set_parameters(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_params *set_params) { static const unsigned int queue_id = IMGU_ABI_QUEUE_A_ID; - struct ipu3_css_pipe *css_pipe = &css->pipes[pipe]; + struct imgu_css_pipe *css_pipe = &css->pipes[pipe]; const int stage = 0; const struct imgu_fw_info *bi; int obgrid_size; @@ -2144,7 +2144,7 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, struct imgu_abi_acc_param *acc = NULL; struct imgu_abi_gdc_warp_param *gdc = NULL; struct ipu3_uapi_obgrid_param *obgrid = NULL; - const struct ipu3_css_map *map; + const struct imgu_css_map *map; void *vmem0 = NULL; void *dmem0 = NULL; @@ -2157,7 +2157,7 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, dev_dbg(css->dev, "%s for pipe %d", __func__, pipe); bi = &css->fwp->binary_header[css_pipe->bindex]; - obgrid_size = ipu3_css_fw_obgrid_size(bi); + obgrid_size = imgu_css_fw_obgrid_size(bi); stripes = bi->info.isp.sp.iterator.num_stripes ? : 1; /* @@ -2165,45 +2165,45 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, * parameters from previous buffers will be overwritten. Fix the driver * not to allow this. */ - ipu3_css_pool_get(&css_pipe->pool.parameter_set_info); - param_set = ipu3_css_pool_last(&css_pipe->pool.parameter_set_info, + imgu_css_pool_get(&css_pipe->pool.parameter_set_info); + param_set = imgu_css_pool_last(&css_pipe->pool.parameter_set_info, 0)->vaddr; /* Get a new acc only if new parameters given, or none yet */ - map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + map = imgu_css_pool_last(&css_pipe->pool.acc, 0); if (set_params || !map->vaddr) { - ipu3_css_pool_get(&css_pipe->pool.acc); - map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + imgu_css_pool_get(&css_pipe->pool.acc); + map = imgu_css_pool_last(&css_pipe->pool.acc, 0); acc = map->vaddr; } /* Get new VMEM0 only if needed, or none yet */ m = IMGU_ABI_MEM_ISP_VMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); if (!map->vaddr || (set_params && (set_params->use.lin_vmem_params || set_params->use.tnr3_vmem_params || set_params->use.xnr3_vmem_params))) { - ipu3_css_pool_get(&css_pipe->pool.binary_params_p[m]); - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + imgu_css_pool_get(&css_pipe->pool.binary_params_p[m]); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); vmem0 = map->vaddr; } /* Get new DMEM0 only if needed, or none yet */ m = IMGU_ABI_MEM_ISP_DMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); if (!map->vaddr || (set_params && (set_params->use.tnr3_dmem_params || set_params->use.xnr3_dmem_params))) { - ipu3_css_pool_get(&css_pipe->pool.binary_params_p[m]); - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + imgu_css_pool_get(&css_pipe->pool.binary_params_p[m]); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); dmem0 = map->vaddr; } /* Configure acc parameter cluster */ if (acc) { /* get acc_old */ - map = ipu3_css_pool_last(&css_pipe->pool.acc, 1); + map = imgu_css_pool_last(&css_pipe->pool.acc, 1); /* user acc */ - r = ipu3_css_cfg_acc(css, pipe, use, acc, map->vaddr, + r = imgu_css_cfg_acc(css, pipe, use, acc, map->vaddr, set_params ? &set_params->acc_param : NULL); if (r < 0) goto fail; @@ -2212,8 +2212,8 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, /* Configure late binding parameters */ if (vmem0) { m = IMGU_ABI_MEM_ISP_VMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); - r = ipu3_css_cfg_vmem0(css, pipe, use, vmem0, + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); + r = imgu_css_cfg_vmem0(css, pipe, use, vmem0, map->vaddr, set_params); if (r < 0) goto fail; @@ -2221,8 +2221,8 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, if (dmem0) { m = IMGU_ABI_MEM_ISP_DMEM0; - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); - r = ipu3_css_cfg_dmem0(css, pipe, use, dmem0, + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 1); + r = imgu_css_cfg_dmem0(css, pipe, use, dmem0, map->vaddr, set_params); if (r < 0) goto fail; @@ -2234,12 +2234,12 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, unsigned int g = IPU3_CSS_RECT_GDC; unsigned int e = IPU3_CSS_RECT_ENVELOPE; - map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + map = imgu_css_pool_last(&css_pipe->pool.gdc, 0); if (!map->vaddr) { - ipu3_css_pool_get(&css_pipe->pool.gdc); - map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + imgu_css_pool_get(&css_pipe->pool.gdc); + map = imgu_css_pool_last(&css_pipe->pool.gdc, 0); gdc = map->vaddr; - ipu3_css_cfg_gdc_table(map->vaddr, + imgu_css_cfg_gdc_table(map->vaddr, css_pipe->aux_frames[a].bytesperline / css_pipe->aux_frames[a].bytesperpixel, css_pipe->aux_frames[a].height, @@ -2252,10 +2252,10 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, } /* Get a new obgrid only if a new obgrid is given, or none yet */ - map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0); if (!map->vaddr || (set_params && set_params->use.obgrid_param)) { - ipu3_css_pool_get(&css_pipe->pool.obgrid); - map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + imgu_css_pool_get(&css_pipe->pool.obgrid); + map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0); obgrid = map->vaddr; /* Configure optical black level grid (obgrid) */ @@ -2269,30 +2269,30 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, /* Configure parameter set info, queued to `queue_id' */ memset(param_set, 0, sizeof(*param_set)); - map = ipu3_css_pool_last(&css_pipe->pool.acc, 0); + map = imgu_css_pool_last(&css_pipe->pool.acc, 0); param_set->mem_map.acc_cluster_params_for_sp = map->daddr; - map = ipu3_css_pool_last(&css_pipe->pool.gdc, 0); + map = imgu_css_pool_last(&css_pipe->pool.gdc, 0); param_set->mem_map.dvs_6axis_params_y = map->daddr; for (i = 0; i < stripes; i++) { - map = ipu3_css_pool_last(&css_pipe->pool.obgrid, 0); + map = imgu_css_pool_last(&css_pipe->pool.obgrid, 0); param_set->mem_map.obgrid_tbl[i] = map->daddr + (obgrid_size / stripes) * i; } for (m = 0; m < IMGU_ABI_NUM_MEMORIES; m++) { - map = ipu3_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); + map = imgu_css_pool_last(&css_pipe->pool.binary_params_p[m], 0); param_set->mem_map.isp_mem_param[stage][m] = map->daddr; } /* Then queue the new parameter buffer */ - map = ipu3_css_pool_last(&css_pipe->pool.parameter_set_info, 0); - r = ipu3_css_queue_data(css, queue_id, pipe, map->daddr); + map = imgu_css_pool_last(&css_pipe->pool.parameter_set_info, 0); + r = imgu_css_queue_data(css, queue_id, pipe, map->daddr); if (r < 0) goto fail; - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_BUFFER_ENQUEUED(pipe, queue_id)); if (r < 0) @@ -2303,12 +2303,12 @@ int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, do { u32 daddr; - r = ipu3_css_dequeue_data(css, queue_id, &daddr); + r = imgu_css_dequeue_data(css, queue_id, &daddr); if (r == -EBUSY) break; if (r) goto fail_no_put; - r = ipu3_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, + r = imgu_css_queue_data(css, IMGU_ABI_QUEUE_EVENT_ID, pipe, IMGU_ABI_EVENT_BUFFER_DEQUEUED (queue_id)); if (r < 0) { @@ -2326,19 +2326,19 @@ fail: * parameters again later. */ - ipu3_css_pool_put(&css_pipe->pool.parameter_set_info); + imgu_css_pool_put(&css_pipe->pool.parameter_set_info); if (acc) - ipu3_css_pool_put(&css_pipe->pool.acc); + imgu_css_pool_put(&css_pipe->pool.acc); if (gdc) - ipu3_css_pool_put(&css_pipe->pool.gdc); + imgu_css_pool_put(&css_pipe->pool.gdc); if (obgrid) - ipu3_css_pool_put(&css_pipe->pool.obgrid); + imgu_css_pool_put(&css_pipe->pool.obgrid); if (vmem0) - ipu3_css_pool_put( + imgu_css_pool_put( &css_pipe->pool.binary_params_p [IMGU_ABI_MEM_ISP_VMEM0]); if (dmem0) - ipu3_css_pool_put( + imgu_css_pool_put( &css_pipe->pool.binary_params_p [IMGU_ABI_MEM_ISP_DMEM0]); @@ -2346,7 +2346,7 @@ fail_no_put: return r; } -int ipu3_css_irq_ack(struct ipu3_css *css) +int imgu_css_irq_ack(struct imgu_css *css) { static const int NUM_SWIRQS = 3; struct imgu_fw_info *bi = &css->fwp->binary_header[css->fw_sp[0]]; diff --git a/drivers/staging/media/ipu3/ipu3-css.h b/drivers/staging/media/ipu3/ipu3-css.h index e88d60f1a0c3..6b8bab27ab1f 100644 --- a/drivers/staging/media/ipu3/ipu3-css.h +++ b/drivers/staging/media/ipu3/ipu3-css.h @@ -43,7 +43,7 @@ * The pipe id type, distinguishes the kind of pipes that * can be run in parallel. */ -enum ipu3_css_pipe_id { +enum imgu_css_pipe_id { IPU3_CSS_PIPE_ID_PREVIEW, IPU3_CSS_PIPE_ID_COPY, IPU3_CSS_PIPE_ID_VIDEO, @@ -53,29 +53,29 @@ enum ipu3_css_pipe_id { IPU3_CSS_PIPE_ID_NUM }; -struct ipu3_css_resolution { +struct imgu_css_resolution { u32 w; u32 h; }; -enum ipu3_css_buffer_state { +enum imgu_css_buffer_state { IPU3_CSS_BUFFER_NEW, /* Not yet queued */ IPU3_CSS_BUFFER_QUEUED, /* Queued, waiting to be filled */ IPU3_CSS_BUFFER_DONE, /* Finished processing, removed from queue */ IPU3_CSS_BUFFER_FAILED, /* Was not processed, removed from queue */ }; -struct ipu3_css_buffer { +struct imgu_css_buffer { /* Private fields: user doesn't touch */ dma_addr_t daddr; unsigned int queue; - enum ipu3_css_buffer_state state; + enum imgu_css_buffer_state state; struct list_head list; u8 queue_pos; unsigned int pipe; }; -struct ipu3_css_format { +struct imgu_css_format { u32 pixelformat; enum v4l2_colorspace colorspace; enum imgu_abi_frame_format frame_format; @@ -89,22 +89,22 @@ struct ipu3_css_format { u8 flags; }; -struct ipu3_css_queue { +struct imgu_css_queue { union { struct v4l2_pix_format_mplane mpix; struct v4l2_meta_format meta; } fmt; - const struct ipu3_css_format *css_fmt; + const struct imgu_css_format *css_fmt; unsigned int width_pad; struct list_head bufs; }; -struct ipu3_css_pipe { - enum ipu3_css_pipe_id pipe_id; +struct imgu_css_pipe { + enum imgu_css_pipe_id pipe_id; unsigned int bindex; - struct ipu3_css_queue queue[IPU3_CSS_QUEUES]; + struct imgu_css_queue queue[IPU3_CSS_QUEUES]; struct v4l2_rect rect[IPU3_CSS_RECTS]; bool vf_output_en; @@ -112,21 +112,21 @@ struct ipu3_css_pipe { spinlock_t qlock; /* Data structures shared with IMGU and driver, always allocated */ - struct ipu3_css_map sp_ddr_ptrs; - struct ipu3_css_map xmem_sp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] + struct imgu_css_map sp_ddr_ptrs; + struct imgu_css_map xmem_sp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] [IMGU_ABI_MAX_STAGES]; - struct ipu3_css_map xmem_isp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] + struct imgu_css_map xmem_isp_stage_ptrs[IPU3_CSS_PIPE_ID_NUM] [IMGU_ABI_MAX_STAGES]; /* * Data structures shared with IMGU and driver, binary specific. * PARAM_CLASS_CONFIG and PARAM_CLASS_STATE parameters. */ - struct ipu3_css_map binary_params_cs[IMGU_ABI_PARAM_CLASS_NUM - 1] + struct imgu_css_map binary_params_cs[IMGU_ABI_PARAM_CLASS_NUM - 1] [IMGU_ABI_NUM_MEMORIES]; struct { - struct ipu3_css_map mem[IPU3_CSS_AUX_FRAMES]; + struct imgu_css_map mem[IPU3_CSS_AUX_FRAMES]; unsigned int width; unsigned int height; unsigned int bytesperline; @@ -134,76 +134,76 @@ struct ipu3_css_pipe { } aux_frames[IPU3_CSS_AUX_FRAME_TYPES]; struct { - struct ipu3_css_pool parameter_set_info; - struct ipu3_css_pool acc; - struct ipu3_css_pool gdc; - struct ipu3_css_pool obgrid; + struct imgu_css_pool parameter_set_info; + struct imgu_css_pool acc; + struct imgu_css_pool gdc; + struct imgu_css_pool obgrid; /* PARAM_CLASS_PARAM parameters for binding while streaming */ - struct ipu3_css_pool binary_params_p[IMGU_ABI_NUM_MEMORIES]; + struct imgu_css_pool binary_params_p[IMGU_ABI_NUM_MEMORIES]; } pool; - struct ipu3_css_map abi_buffers[IPU3_CSS_QUEUES] + struct imgu_css_map abi_buffers[IPU3_CSS_QUEUES] [IMGU_ABI_HOST2SP_BUFQ_SIZE]; }; /* IPU3 Camera Sub System structure */ -struct ipu3_css { +struct imgu_css { struct device *dev; void __iomem *base; const struct firmware *fw; struct imgu_fw_header *fwp; int iomem_length; int fw_bl, fw_sp[IMGU_NUM_SP]; /* Indices of bl and SP binaries */ - struct ipu3_css_map *binary; /* fw binaries mapped to device */ + struct imgu_css_map *binary; /* fw binaries mapped to device */ bool streaming; /* true when streaming is enabled */ - struct ipu3_css_pipe pipes[IMGU_MAX_PIPE_NUM]; - struct ipu3_css_map xmem_sp_group_ptrs; + struct imgu_css_pipe pipes[IMGU_MAX_PIPE_NUM]; + struct imgu_css_map xmem_sp_group_ptrs; /* enabled pipe(s) */ DECLARE_BITMAP(enabled_pipes, IMGU_MAX_PIPE_NUM); }; /******************* css v4l *******************/ -int ipu3_css_init(struct device *dev, struct ipu3_css *css, +int imgu_css_init(struct device *dev, struct imgu_css *css, void __iomem *base, int length); -void ipu3_css_cleanup(struct ipu3_css *css); -int ipu3_css_fmt_try(struct ipu3_css *css, +void imgu_css_cleanup(struct imgu_css *css); +int imgu_css_fmt_try(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe); -int ipu3_css_fmt_set(struct ipu3_css *css, +int imgu_css_fmt_set(struct imgu_css *css, struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES], struct v4l2_rect *rects[IPU3_CSS_RECTS], unsigned int pipe); -int ipu3_css_meta_fmt_set(struct v4l2_meta_format *fmt); -int ipu3_css_buf_queue(struct ipu3_css *css, unsigned int pipe, - struct ipu3_css_buffer *b); -struct ipu3_css_buffer *ipu3_css_buf_dequeue(struct ipu3_css *css); -int ipu3_css_start_streaming(struct ipu3_css *css); -void ipu3_css_stop_streaming(struct ipu3_css *css); -bool ipu3_css_queue_empty(struct ipu3_css *css); -bool ipu3_css_is_streaming(struct ipu3_css *css); -bool ipu3_css_pipe_queue_empty(struct ipu3_css *css, unsigned int pipe); +int imgu_css_meta_fmt_set(struct v4l2_meta_format *fmt); +int imgu_css_buf_queue(struct imgu_css *css, unsigned int pipe, + struct imgu_css_buffer *b); +struct imgu_css_buffer *imgu_css_buf_dequeue(struct imgu_css *css); +int imgu_css_start_streaming(struct imgu_css *css); +void imgu_css_stop_streaming(struct imgu_css *css); +bool imgu_css_queue_empty(struct imgu_css *css); +bool imgu_css_is_streaming(struct imgu_css *css); +bool imgu_css_pipe_queue_empty(struct imgu_css *css, unsigned int pipe); /******************* css hw *******************/ -int ipu3_css_set_powerup(struct device *dev, void __iomem *base); -void ipu3_css_set_powerdown(struct device *dev, void __iomem *base); -int ipu3_css_irq_ack(struct ipu3_css *css); +int imgu_css_set_powerup(struct device *dev, void __iomem *base); +void imgu_css_set_powerdown(struct device *dev, void __iomem *base); +int imgu_css_irq_ack(struct imgu_css *css); /******************* set parameters ************/ -int ipu3_css_set_parameters(struct ipu3_css *css, unsigned int pipe, +int imgu_css_set_parameters(struct imgu_css *css, unsigned int pipe, struct ipu3_uapi_params *set_params); /******************* auxiliary helpers *******************/ -static inline enum ipu3_css_buffer_state -ipu3_css_buf_state(struct ipu3_css_buffer *b) +static inline enum imgu_css_buffer_state +imgu_css_buf_state(struct imgu_css_buffer *b) { return b->state; } /* Initialize given buffer. May be called several times. */ -static inline void ipu3_css_buf_init(struct ipu3_css_buffer *b, +static inline void imgu_css_buf_init(struct imgu_css_buffer *b, unsigned int queue, dma_addr_t daddr) { b->state = IPU3_CSS_BUFFER_NEW; diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.c b/drivers/staging/media/ipu3/ipu3-dmamap.c index 5bed01d5b8df..d978a00e1e0b 100644 --- a/drivers/staging/media/ipu3/ipu3-dmamap.c +++ b/drivers/staging/media/ipu3/ipu3-dmamap.c @@ -15,9 +15,9 @@ #include "ipu3-dmamap.h" /* - * Free a buffer allocated by ipu3_dmamap_alloc_buffer() + * Free a buffer allocated by imgu_dmamap_alloc_buffer() */ -static void ipu3_dmamap_free_buffer(struct page **pages, +static void imgu_dmamap_free_buffer(struct page **pages, size_t size) { int count = size >> PAGE_SHIFT; @@ -31,7 +31,7 @@ static void ipu3_dmamap_free_buffer(struct page **pages, * Based on the implementation of __iommu_dma_alloc_pages() * defined in drivers/iommu/dma-iommu.c */ -static struct page **ipu3_dmamap_alloc_buffer(size_t size, +static struct page **imgu_dmamap_alloc_buffer(size_t size, unsigned long order_mask, gfp_t gfp) { @@ -74,7 +74,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size, __free_pages(page, order); } if (!page) { - ipu3_dmamap_free_buffer(pages, i << PAGE_SHIFT); + imgu_dmamap_free_buffer(pages, i << PAGE_SHIFT); return NULL; } count -= order_size; @@ -86,7 +86,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size, } /** - * ipu3_dmamap_alloc - allocate and map a buffer into KVA + * imgu_dmamap_alloc - allocate and map a buffer into KVA * @imgu: struct device pointer * @map: struct to store mapping variables * @len: size required @@ -95,7 +95,7 @@ static struct page **ipu3_dmamap_alloc_buffer(size_t size, * KVA on success * %NULL on failure */ -void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, +void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map, size_t len) { unsigned long shift = iova_shift(&imgu->iova_domain); @@ -114,7 +114,7 @@ void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, if (!iova) return NULL; - pages = ipu3_dmamap_alloc_buffer(size, alloc_sizes >> PAGE_SHIFT, + pages = imgu_dmamap_alloc_buffer(size, alloc_sizes >> PAGE_SHIFT, GFP_KERNEL); if (!pages) goto out_free_iova; @@ -122,7 +122,7 @@ void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, /* Call IOMMU driver to setup pgt */ iovaddr = iova_dma_addr(&imgu->iova_domain, iova); for (i = 0; i < size / PAGE_SIZE; ++i) { - rval = ipu3_mmu_map(imgu->mmu, iovaddr, + rval = imgu_mmu_map(imgu->mmu, iovaddr, page_to_phys(pages[i]), PAGE_SIZE); if (rval) goto out_unmap; @@ -153,8 +153,8 @@ out_vunmap: vunmap(map->vma->addr); out_unmap: - ipu3_dmamap_free_buffer(pages, size); - ipu3_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + imgu_dmamap_free_buffer(pages, size); + imgu_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), i * PAGE_SIZE); map->vma = NULL; @@ -164,7 +164,7 @@ out_free_iova: return NULL; } -void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map) +void imgu_dmamap_unmap(struct imgu_device *imgu, struct imgu_css_map *map) { struct iova *iova; @@ -173,16 +173,16 @@ void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map) if (WARN_ON(!iova)) return; - ipu3_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + imgu_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), iova_size(iova) << iova_shift(&imgu->iova_domain)); __free_iova(&imgu->iova_domain, iova); } /* - * Counterpart of ipu3_dmamap_alloc + * Counterpart of imgu_dmamap_alloc */ -void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map) +void imgu_dmamap_free(struct imgu_device *imgu, struct imgu_css_map *map) { struct vm_struct *area = map->vma; @@ -192,18 +192,18 @@ void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map) if (!map->vaddr) return; - ipu3_dmamap_unmap(imgu, map); + imgu_dmamap_unmap(imgu, map); if (WARN_ON(!area) || WARN_ON(!area->pages)) return; - ipu3_dmamap_free_buffer(area->pages, map->size); + imgu_dmamap_free_buffer(area->pages, map->size); vunmap(map->vaddr); map->vaddr = NULL; } -int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, - int nents, struct ipu3_css_map *map) +int imgu_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, + int nents, struct imgu_css_map *map) { unsigned long shift = iova_shift(&imgu->iova_domain); struct scatterlist *sg; @@ -233,7 +233,7 @@ int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, dev_dbg(&imgu->pci_dev->dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, iova->pfn_hi); - if (ipu3_mmu_map_sg(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), + if (imgu_mmu_map_sg(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova), sglist, nents) < size) goto out_fail; @@ -249,7 +249,7 @@ out_fail: return -EFAULT; } -int ipu3_dmamap_init(struct imgu_device *imgu) +int imgu_dmamap_init(struct imgu_device *imgu) { unsigned long order, base_pfn; int ret = iova_cache_get(); @@ -264,7 +264,7 @@ int ipu3_dmamap_init(struct imgu_device *imgu) return 0; } -void ipu3_dmamap_exit(struct imgu_device *imgu) +void imgu_dmamap_exit(struct imgu_device *imgu) { put_iova_domain(&imgu->iova_domain); iova_cache_put(); diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.h b/drivers/staging/media/ipu3/ipu3-dmamap.h index b9d224a33273..9db513b3d603 100644 --- a/drivers/staging/media/ipu3/ipu3-dmamap.h +++ b/drivers/staging/media/ipu3/ipu3-dmamap.h @@ -8,15 +8,15 @@ struct imgu_device; struct scatterlist; -void *ipu3_dmamap_alloc(struct imgu_device *imgu, struct ipu3_css_map *map, +void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map, size_t len); -void ipu3_dmamap_free(struct imgu_device *imgu, struct ipu3_css_map *map); +void imgu_dmamap_free(struct imgu_device *imgu, struct imgu_css_map *map); -int ipu3_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, - int nents, struct ipu3_css_map *map); -void ipu3_dmamap_unmap(struct imgu_device *imgu, struct ipu3_css_map *map); +int imgu_dmamap_map_sg(struct imgu_device *imgu, struct scatterlist *sglist, + int nents, struct imgu_css_map *map); +void imgu_dmamap_unmap(struct imgu_device *imgu, struct imgu_css_map *map); -int ipu3_dmamap_init(struct imgu_device *imgu); -void ipu3_dmamap_exit(struct imgu_device *imgu); +int imgu_dmamap_init(struct imgu_device *imgu); +void imgu_dmamap_exit(struct imgu_device *imgu); #endif diff --git a/drivers/staging/media/ipu3/ipu3-mmu.c b/drivers/staging/media/ipu3/ipu3-mmu.c index b9f209541f78..cd2038b22b55 100644 --- a/drivers/staging/media/ipu3/ipu3-mmu.c +++ b/drivers/staging/media/ipu3/ipu3-mmu.c @@ -48,7 +48,7 @@ #define REG_GP_HALT (IMGU_REG_BASE + 0x5dc) #define REG_GP_HALTED (IMGU_REG_BASE + 0x5e0) -struct ipu3_mmu { +struct imgu_mmu { struct device *dev; void __iomem *base; /* protect access to l2pts, l1pt */ @@ -63,28 +63,28 @@ struct ipu3_mmu { u32 **l2pts; u32 *l1pt; - struct ipu3_mmu_info geometry; + struct imgu_mmu_info geometry; }; -static inline struct ipu3_mmu *to_ipu3_mmu(struct ipu3_mmu_info *info) +static inline struct imgu_mmu *to_imgu_mmu(struct imgu_mmu_info *info) { - return container_of(info, struct ipu3_mmu, geometry); + return container_of(info, struct imgu_mmu, geometry); } /** - * ipu3_mmu_tlb_invalidate - invalidate translation look-aside buffer + * imgu_mmu_tlb_invalidate - invalidate translation look-aside buffer * @mmu: MMU to perform the invalidate operation on * * This function invalidates the whole TLB. Must be called when the hardware * is powered on. */ -static void ipu3_mmu_tlb_invalidate(struct ipu3_mmu *mmu) +static void imgu_mmu_tlb_invalidate(struct imgu_mmu *mmu) { writel(TLB_INVALIDATE, mmu->base + REG_TLB_INVALIDATE); } -static void call_if_ipu3_is_powered(struct ipu3_mmu *mmu, - void (*func)(struct ipu3_mmu *mmu)) +static void call_if_imgu_is_powered(struct imgu_mmu *mmu, + void (*func)(struct imgu_mmu *mmu)) { if (!pm_runtime_get_if_in_use(mmu->dev)) return; @@ -94,14 +94,14 @@ static void call_if_ipu3_is_powered(struct ipu3_mmu *mmu, } /** - * ipu3_mmu_set_halt - set CIO gate halt bit + * imgu_mmu_set_halt - set CIO gate halt bit * @mmu: MMU to set the CIO gate bit in. * @halt: Desired state of the gate bit. * * This function sets the CIO gate bit that controls whether external memory * accesses are allowed. Must be called when the hardware is powered on. */ -static void ipu3_mmu_set_halt(struct ipu3_mmu *mmu, bool halt) +static void imgu_mmu_set_halt(struct imgu_mmu *mmu, bool halt) { int ret; u32 val; @@ -116,12 +116,12 @@ static void ipu3_mmu_set_halt(struct ipu3_mmu *mmu, bool halt) } /** - * ipu3_mmu_alloc_page_table - allocate a pre-filled page table + * imgu_mmu_alloc_page_table - allocate a pre-filled page table * @pteval: Value to initialize for page table entries with. * * Return: Pointer to allocated page table or NULL on failure. */ -static u32 *ipu3_mmu_alloc_page_table(u32 pteval) +static u32 *imgu_mmu_alloc_page_table(u32 pteval) { u32 *pt; int pte; @@ -139,10 +139,10 @@ static u32 *ipu3_mmu_alloc_page_table(u32 pteval) } /** - * ipu3_mmu_free_page_table - free page table + * imgu_mmu_free_page_table - free page table * @pt: Page table to free. */ -static void ipu3_mmu_free_page_table(u32 *pt) +static void imgu_mmu_free_page_table(u32 *pt) { set_memory_wb((unsigned long int)pt, IPU3_PT_ORDER); free_page((unsigned long)pt); @@ -168,7 +168,7 @@ static inline void address_to_pte_idx(unsigned long iova, u32 *l1pt_idx, *l1pt_idx = iova & IPU3_L1PT_MASK; } -static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx) +static u32 *imgu_mmu_get_l2pt(struct imgu_mmu *mmu, u32 l1pt_idx) { unsigned long flags; u32 *l2pt, *new_l2pt; @@ -182,7 +182,7 @@ static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx) spin_unlock_irqrestore(&mmu->lock, flags); - new_l2pt = ipu3_mmu_alloc_page_table(mmu->dummy_page_pteval); + new_l2pt = imgu_mmu_alloc_page_table(mmu->dummy_page_pteval); if (!new_l2pt) return NULL; @@ -193,7 +193,7 @@ static u32 *ipu3_mmu_get_l2pt(struct ipu3_mmu *mmu, u32 l1pt_idx) l2pt = mmu->l2pts[l1pt_idx]; if (l2pt) { - ipu3_mmu_free_page_table(new_l2pt); + imgu_mmu_free_page_table(new_l2pt); goto done; } @@ -208,7 +208,7 @@ done: return l2pt; } -static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova, +static int __imgu_mmu_map(struct imgu_mmu *mmu, unsigned long iova, phys_addr_t paddr) { u32 l1pt_idx, l2pt_idx; @@ -220,7 +220,7 @@ static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova, address_to_pte_idx(iova, &l1pt_idx, &l2pt_idx); - l2pt = ipu3_mmu_get_l2pt(mmu, l1pt_idx); + l2pt = imgu_mmu_get_l2pt(mmu, l1pt_idx); if (!l2pt) return -ENOMEM; @@ -242,7 +242,7 @@ static int __ipu3_mmu_map(struct ipu3_mmu *mmu, unsigned long iova, * The following four functions are implemented based on iommu.c * drivers/iommu/iommu.c/iommu_pgsize(). */ -static size_t ipu3_mmu_pgsize(unsigned long pgsize_bitmap, +static size_t imgu_mmu_pgsize(unsigned long pgsize_bitmap, unsigned long addr_merge, size_t size) { unsigned int pgsize_idx; @@ -276,10 +276,10 @@ static size_t ipu3_mmu_pgsize(unsigned long pgsize_bitmap, } /* drivers/iommu/iommu.c/iommu_map() */ -int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, +int imgu_mmu_map(struct imgu_mmu_info *info, unsigned long iova, phys_addr_t paddr, size_t size) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); unsigned int min_pagesz; int ret = 0; @@ -301,13 +301,13 @@ int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, iova, &paddr, size); while (size) { - size_t pgsize = ipu3_mmu_pgsize(mmu->geometry.pgsize_bitmap, + size_t pgsize = imgu_mmu_pgsize(mmu->geometry.pgsize_bitmap, iova | paddr, size); dev_dbg(mmu->dev, "mapping: iova 0x%lx pa %pa pgsize 0x%zx\n", iova, &paddr, pgsize); - ret = __ipu3_mmu_map(mmu, iova, paddr); + ret = __imgu_mmu_map(mmu, iova, paddr); if (ret) break; @@ -316,16 +316,16 @@ int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, size -= pgsize; } - call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate); + call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate); return ret; } /* drivers/iommu/iommu.c/default_iommu_map_sg() */ -size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_map_sg(struct imgu_mmu_info *info, unsigned long iova, struct scatterlist *sg, unsigned int nents) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); struct scatterlist *s; size_t s_length, mapped = 0; unsigned int i, min_pagesz; @@ -345,25 +345,25 @@ size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova, if (i == nents - 1 && !IS_ALIGNED(s->length, min_pagesz)) s_length = PAGE_ALIGN(s->length); - ret = ipu3_mmu_map(info, iova + mapped, phys, s_length); + ret = imgu_mmu_map(info, iova + mapped, phys, s_length); if (ret) goto out_err; mapped += s_length; } - call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate); + call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate); return mapped; out_err: /* undo mappings already done */ - ipu3_mmu_unmap(info, iova, mapped); + imgu_mmu_unmap(info, iova, mapped); return 0; } -static size_t __ipu3_mmu_unmap(struct ipu3_mmu *mmu, +static size_t __imgu_mmu_unmap(struct imgu_mmu *mmu, unsigned long iova, size_t size) { u32 l1pt_idx, l2pt_idx; @@ -395,10 +395,10 @@ static size_t __ipu3_mmu_unmap(struct ipu3_mmu *mmu, } /* drivers/iommu/iommu.c/iommu_unmap() */ -size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_unmap(struct imgu_mmu_info *info, unsigned long iova, size_t size) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); size_t unmapped_page, unmapped = 0; unsigned int min_pagesz; @@ -423,10 +423,10 @@ size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, * or we hit an area that isn't mapped. */ while (unmapped < size) { - size_t pgsize = ipu3_mmu_pgsize(mmu->geometry.pgsize_bitmap, + size_t pgsize = imgu_mmu_pgsize(mmu->geometry.pgsize_bitmap, iova, size - unmapped); - unmapped_page = __ipu3_mmu_unmap(mmu, iova, pgsize); + unmapped_page = __imgu_mmu_unmap(mmu, iova, pgsize); if (!unmapped_page) break; @@ -437,20 +437,20 @@ size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, unmapped += unmapped_page; } - call_if_ipu3_is_powered(mmu, ipu3_mmu_tlb_invalidate); + call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate); return unmapped; } /** - * ipu3_mmu_init() - initialize IPU3 MMU block + * imgu_mmu_init() - initialize IPU3 MMU block * @base: IOMEM base of hardware registers. * * Return: Pointer to IPU3 MMU private data pointer or ERR_PTR() on error. */ -struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) +struct imgu_mmu_info *imgu_mmu_init(struct device *parent, void __iomem *base) { - struct ipu3_mmu *mmu; + struct imgu_mmu *mmu; u32 pteval; mmu = kzalloc(sizeof(*mmu), GFP_KERNEL); @@ -462,7 +462,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) spin_lock_init(&mmu->lock); /* Disallow external memory access when having no valid page tables. */ - ipu3_mmu_set_halt(mmu, true); + imgu_mmu_set_halt(mmu, true); /* * The MMU does not have a "valid" bit, so we have to use a dummy @@ -478,7 +478,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) * Allocate a dummy L2 page table with all entries pointing to * the dummy page. */ - mmu->dummy_l2pt = ipu3_mmu_alloc_page_table(pteval); + mmu->dummy_l2pt = imgu_mmu_alloc_page_table(pteval); if (!mmu->dummy_l2pt) goto fail_dummy_page; pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->dummy_l2pt)); @@ -493,14 +493,14 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) goto fail_l2pt; /* Allocate the L1 page table. */ - mmu->l1pt = ipu3_mmu_alloc_page_table(mmu->dummy_l2pt_pteval); + mmu->l1pt = imgu_mmu_alloc_page_table(mmu->dummy_l2pt_pteval); if (!mmu->l1pt) goto fail_l2pts; pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->l1pt)); writel(pteval, mmu->base + REG_L1_PHYS); - ipu3_mmu_tlb_invalidate(mmu); - ipu3_mmu_set_halt(mmu, false); + imgu_mmu_tlb_invalidate(mmu); + imgu_mmu_set_halt(mmu, false); mmu->geometry.aperture_start = 0; mmu->geometry.aperture_end = DMA_BIT_MASK(IPU3_MMU_ADDRESS_BITS); @@ -511,7 +511,7 @@ struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base) fail_l2pts: vfree(mmu->l2pts); fail_l2pt: - ipu3_mmu_free_page_table(mmu->dummy_l2pt); + imgu_mmu_free_page_table(mmu->dummy_l2pt); fail_dummy_page: free_page((unsigned long)mmu->dummy_page); fail_group: @@ -521,41 +521,41 @@ fail_group: } /** - * ipu3_mmu_exit() - clean up IPU3 MMU block + * imgu_mmu_exit() - clean up IPU3 MMU block * @mmu: IPU3 MMU private data */ -void ipu3_mmu_exit(struct ipu3_mmu_info *info) +void imgu_mmu_exit(struct imgu_mmu_info *info) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); /* We are going to free our page tables, no more memory access. */ - ipu3_mmu_set_halt(mmu, true); - ipu3_mmu_tlb_invalidate(mmu); + imgu_mmu_set_halt(mmu, true); + imgu_mmu_tlb_invalidate(mmu); - ipu3_mmu_free_page_table(mmu->l1pt); + imgu_mmu_free_page_table(mmu->l1pt); vfree(mmu->l2pts); - ipu3_mmu_free_page_table(mmu->dummy_l2pt); + imgu_mmu_free_page_table(mmu->dummy_l2pt); free_page((unsigned long)mmu->dummy_page); kfree(mmu); } -void ipu3_mmu_suspend(struct ipu3_mmu_info *info) +void imgu_mmu_suspend(struct imgu_mmu_info *info) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); - ipu3_mmu_set_halt(mmu, true); + imgu_mmu_set_halt(mmu, true); } -void ipu3_mmu_resume(struct ipu3_mmu_info *info) +void imgu_mmu_resume(struct imgu_mmu_info *info) { - struct ipu3_mmu *mmu = to_ipu3_mmu(info); + struct imgu_mmu *mmu = to_imgu_mmu(info); u32 pteval; - ipu3_mmu_set_halt(mmu, true); + imgu_mmu_set_halt(mmu, true); pteval = IPU3_ADDR2PTE(virt_to_phys(mmu->l1pt)); writel(pteval, mmu->base + REG_L1_PHYS); - ipu3_mmu_tlb_invalidate(mmu); - ipu3_mmu_set_halt(mmu, false); + imgu_mmu_tlb_invalidate(mmu); + imgu_mmu_set_halt(mmu, false); } diff --git a/drivers/staging/media/ipu3/ipu3-mmu.h b/drivers/staging/media/ipu3/ipu3-mmu.h index 8fe63b4c6e1c..fa58827eb19c 100644 --- a/drivers/staging/media/ipu3/ipu3-mmu.h +++ b/drivers/staging/media/ipu3/ipu3-mmu.h @@ -6,13 +6,13 @@ #define __IPU3_MMU_H /** - * struct ipu3_mmu_info - Describes mmu geometry + * struct imgu_mmu_info - Describes mmu geometry * * @aperture_start: First address that can be mapped * @aperture_end: Last address that can be mapped * @pgsize_bitmap: Bitmap of page sizes in use */ -struct ipu3_mmu_info { +struct imgu_mmu_info { dma_addr_t aperture_start; dma_addr_t aperture_end; unsigned long pgsize_bitmap; @@ -21,15 +21,15 @@ struct ipu3_mmu_info { struct device; struct scatterlist; -struct ipu3_mmu_info *ipu3_mmu_init(struct device *parent, void __iomem *base); -void ipu3_mmu_exit(struct ipu3_mmu_info *info); -void ipu3_mmu_suspend(struct ipu3_mmu_info *info); -void ipu3_mmu_resume(struct ipu3_mmu_info *info); +struct imgu_mmu_info *imgu_mmu_init(struct device *parent, void __iomem *base); +void imgu_mmu_exit(struct imgu_mmu_info *info); +void imgu_mmu_suspend(struct imgu_mmu_info *info); +void imgu_mmu_resume(struct imgu_mmu_info *info); -int ipu3_mmu_map(struct ipu3_mmu_info *info, unsigned long iova, +int imgu_mmu_map(struct imgu_mmu_info *info, unsigned long iova, phys_addr_t paddr, size_t size); -size_t ipu3_mmu_unmap(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_unmap(struct imgu_mmu_info *info, unsigned long iova, size_t size); -size_t ipu3_mmu_map_sg(struct ipu3_mmu_info *info, unsigned long iova, +size_t imgu_mmu_map_sg(struct imgu_mmu_info *info, unsigned long iova, struct scatterlist *sg, unsigned int nents); #endif diff --git a/drivers/staging/media/ipu3/ipu3-tables.c b/drivers/staging/media/ipu3/ipu3-tables.c index 334517987eba..3a3730bd4395 100644 --- a/drivers/staging/media/ipu3/ipu3-tables.c +++ b/drivers/staging/media/ipu3/ipu3-tables.c @@ -5,8 +5,8 @@ #define X 0 /* Don't care value */ -const struct ipu3_css_bds_config - ipu3_css_bds_configs[IMGU_BDS_CONFIG_LEN] = { { +const struct imgu_css_bds_config + imgu_css_bds_configs[IMGU_BDS_CONFIG_LEN] = { { /* Scale factor 32 / (32 + 0) = 1 */ .hor_phase_arr = { .even = { { 0, 0, 64, 6, 0, 0, 0 } }, @@ -9015,7 +9015,7 @@ const struct ipu3_css_bds_config .ver_ds_en = 1 } }; -const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = { +const s32 imgu_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = { IMGU_SCALER_FP * -0.000000000000000, IMGU_SCALER_FP * -0.000249009327023, IMGU_SCALER_FP * -0.001022241683322, @@ -9146,7 +9146,7 @@ const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN] = { IMGU_SCALER_FP * -0.000249009327023 }; -const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = { +const s32 imgu_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = { IMGU_SCALER_FP * 0.074300676367033, IMGU_SCALER_FP * 0.094030234498392, IMGU_SCALER_FP * 0.115522859526596, @@ -9214,7 +9214,7 @@ const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN] = { }; /* settings for Geometric Distortion Correction */ -const s16 ipu3_css_gdc_lut[4][256] = { { +const s16 imgu_css_gdc_lut[4][256] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -3, -3, -3, -4, -4, -4, -5, -5, -5, -6, -6, -7, -7, -7, -8, -8, -9, -9, -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -15, -15, @@ -9292,7 +9292,7 @@ const s16 ipu3_css_gdc_lut[4][256] = { { -1, 0, 1, 0, 0, 0, 0, 0, 0, 0 } }; -const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults = { +const struct imgu_css_xnr3_vmem_defaults imgu_css_xnr3_vmem_defaults = { .x = { 1024, 1164, 1320, 1492, 1680, 1884, 2108, 2352, 2616, 2900, 3208, 3540, 3896, 4276, 4684, 5120 @@ -9311,7 +9311,7 @@ const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults = { }; /* settings for Bayer Noise Reduction */ -const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults = { +const struct ipu3_uapi_bnr_static_config imgu_css_bnr_defaults = { { 16, 16, 16, 16 }, /* wb_gains */ { 16, 16, 16, 16 }, /* wb_gains_thr */ { 0, X, 8, 6, X, 14 }, /* thr_coeffs */ @@ -9327,18 +9327,18 @@ const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults = { { 8, 4, 4, X, 8, X, 1, 1, 1, 1 }, /* dn_detect_ctrl */ }; -const struct ipu3_uapi_dm_config ipu3_css_dm_defaults = { +const struct ipu3_uapi_dm_config imgu_css_dm_defaults = { 1, 1, 1, X, X, 8, X, 7, X, 8, X, 8, X, 4, X }; -const struct ipu3_uapi_ccm_mat_config ipu3_css_ccm_defaults = { +const struct ipu3_uapi_ccm_mat_config imgu_css_ccm_defaults = { 9775, -2671, 1087, 0, -1071, 8303, 815, 0, -23, -7887, 16103, 0 }; /* settings for Gamma correction */ -const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut = { { +const struct ipu3_uapi_gamma_corr_lut imgu_css_gamma_lut = { { 63, 79, 95, 111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 271, 287, 303, 319, 335, 351, 367, 383, 399, 415, 431, 447, 463, 479, 495, 511, 527, 543, 559, 575, 591, 607, 623, 639, 655, 671, 687, 703, 719, 735, @@ -9362,13 +9362,13 @@ const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut = { { 7807, 7871, 7935, 7999, 8063, 8127, 8191 } }; -const struct ipu3_uapi_csc_mat_config ipu3_css_csc_defaults = { +const struct ipu3_uapi_csc_mat_config imgu_css_csc_defaults = { 4898, 9617, 1867, 0, -2410, -4732, 7143, 0, 10076, -8437, -1638, 0 }; -const struct ipu3_uapi_cds_params ipu3_css_cds_defaults = { +const struct ipu3_uapi_cds_params imgu_css_cds_defaults = { 1, 3, 3, 1, 1, 3, 3, 1, 4, X, /* ds_nf */ @@ -9376,7 +9376,7 @@ const struct ipu3_uapi_cds_params ipu3_css_cds_defaults = { 0, X /* uv_bin_output */ }; -const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults = { +const struct ipu3_uapi_shd_config_static imgu_css_shd_defaults = { .grid = { .width = 73, .height = 55, @@ -9397,7 +9397,7 @@ const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults = { }, }; -const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults = { +const struct ipu3_uapi_yuvp1_iefd_config imgu_css_iefd_defaults = { .units = { .cu_1 = { 0, 150, 7, 0 }, .cu_ed = { 7, 110, 244, X, 307, 409, 511, X, @@ -9436,17 +9436,17 @@ const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults = { { 1, X, 2, X, 8, X } }, }; -const struct ipu3_uapi_yuvp1_yds_config ipu3_css_yds_defaults = { +const struct ipu3_uapi_yuvp1_yds_config imgu_css_yds_defaults = { 0, 1, 1, 0, 0, 1, 1, 0, 2, X, 0, X }; -const struct ipu3_uapi_yuvp1_chnr_config ipu3_css_chnr_defaults = { +const struct ipu3_uapi_yuvp1_chnr_config imgu_css_chnr_defaults = { .coring = { 0, X, 0, X }, .sense_gain = { 6, 6, 6, X, 4, 4, 4, X }, .iir_fir = { 8, X, 12, X, 0, 256 - 127, X }, }; -const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults = { +const struct ipu3_uapi_yuvp1_y_ee_nr_config imgu_css_y_ee_nr_defaults = { .lpf = { 4, X, 8, X, 16, X, 0 }, .sense = { 8191, X, 0, X, 8191, X, 0, X }, .gain = { 8, X, 0, X, 8, X, 0, X }, @@ -9457,7 +9457,7 @@ const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults = { }; const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config - ipu3_css_tcc_gain_pcwl_lut = { { + imgu_css_tcc_gain_pcwl_lut = { { 1024, 1032, 1040, 1048, 1057, 1065, 1073, 1081, 1089, 1097, 1105, 1113, 1122, 1130, 1138, 1146, 1154, 1162, 1170, 1178, 1187, 1195, 1203, 1211, 1219, 1227, 1235, 1243, 1252, 1260, 1268, 1276, 1284, 1292, 1300, 1308, @@ -9483,12 +9483,12 @@ const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config } }; const struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config - ipu3_css_tcc_r_sqr_lut = { { + imgu_css_tcc_r_sqr_lut = { { 32, 44, 64, 92, 128, 180, 256, 364, 512, 628, 724, 808, 888, 956, 1024, 1088, 1144, 1200, 1256, 1304, 1356, 1404, 1448 } }; -const struct imgu_abi_anr_config ipu3_css_anr_defaults = { +const struct imgu_abi_anr_config imgu_css_anr_defaults = { .transform = { .adaptive_treshhold_en = 1, .alpha = { { 13, 13, 13, 13, 0, 0, 0, 0}, @@ -9545,7 +9545,7 @@ const struct imgu_abi_anr_config ipu3_css_anr_defaults = { }; /* frame settings for Auto White Balance */ -const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults = { +const struct ipu3_uapi_awb_fr_config_s imgu_css_awb_fr_defaults = { .grid_cfg = { .width = 16, .height = 16, @@ -9560,7 +9560,7 @@ const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults = { }; /* settings for Auto Exposure */ -const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults = { +const struct ipu3_uapi_ae_grid_config imgu_css_ae_grid_defaults = { .width = 16, .height = 16, .block_width_log2 = 3, @@ -9571,13 +9571,13 @@ const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults = { }; /* settings for Auto Exposure color correction matrix */ -const struct ipu3_uapi_ae_ccm ipu3_css_ae_ccm_defaults = { +const struct ipu3_uapi_ae_ccm imgu_css_ae_ccm_defaults = { 256, 256, 256, 256, /* gain_gr/r/b/gb */ .mat = { 128, 0, 0, 0, 0, 128, 0, 0, 0, 0, 128, 0, 0, 0, 0, 128 }, }; /* settings for Auto Focus */ -const struct ipu3_uapi_af_config_s ipu3_css_af_defaults = { +const struct ipu3_uapi_af_config_s imgu_css_af_defaults = { .filter_config = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 128 }, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 128 }, 0, @@ -9595,7 +9595,7 @@ const struct ipu3_uapi_af_config_s ipu3_css_af_defaults = { }; /* settings for Auto White Balance */ -const struct ipu3_uapi_awb_config_s ipu3_css_awb_defaults = { +const struct ipu3_uapi_awb_config_s imgu_css_awb_defaults = { 8191, 8191, 8191, 8191 | /* rgbs_thr_gr/r/gb/b */ IPU3_UAPI_AWB_RGBS_THR_B_EN | IPU3_UAPI_AWB_RGBS_THR_B_INCL_SAT, .grid = { diff --git a/drivers/staging/media/ipu3/ipu3-tables.h b/drivers/staging/media/ipu3/ipu3-tables.h index 6563782cbd22..a1bf3286f380 100644 --- a/drivers/staging/media/ipu3/ipu3-tables.h +++ b/drivers/staging/media/ipu3/ipu3-tables.h @@ -19,7 +19,7 @@ #define IMGU_GDC_LUT_UNIT 4 #define IMGU_GDC_LUT_LEN 256 -struct ipu3_css_bds_config { +struct imgu_css_bds_config { struct imgu_abi_bds_phase_arr hor_phase_arr; struct imgu_abi_bds_phase_arr ver_phase_arr; struct imgu_abi_bds_ptrn_arr ptrn_arr; @@ -28,39 +28,39 @@ struct ipu3_css_bds_config { u8 ver_ds_en; }; -struct ipu3_css_xnr3_vmem_defaults { +struct imgu_css_xnr3_vmem_defaults { s16 x[IMGU_XNR3_VMEM_LUT_LEN]; s16 a[IMGU_XNR3_VMEM_LUT_LEN]; s16 b[IMGU_XNR3_VMEM_LUT_LEN]; s16 c[IMGU_XNR3_VMEM_LUT_LEN]; }; -extern const struct ipu3_css_bds_config - ipu3_css_bds_configs[IMGU_BDS_CONFIG_LEN]; -extern const s32 ipu3_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN]; -extern const s32 ipu3_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN]; -extern const s16 ipu3_css_gdc_lut[IMGU_GDC_LUT_UNIT][IMGU_GDC_LUT_LEN]; -extern const struct ipu3_css_xnr3_vmem_defaults ipu3_css_xnr3_vmem_defaults; -extern const struct ipu3_uapi_bnr_static_config ipu3_css_bnr_defaults; -extern const struct ipu3_uapi_dm_config ipu3_css_dm_defaults; -extern const struct ipu3_uapi_ccm_mat_config ipu3_css_ccm_defaults; -extern const struct ipu3_uapi_gamma_corr_lut ipu3_css_gamma_lut; -extern const struct ipu3_uapi_csc_mat_config ipu3_css_csc_defaults; -extern const struct ipu3_uapi_cds_params ipu3_css_cds_defaults; -extern const struct ipu3_uapi_shd_config_static ipu3_css_shd_defaults; -extern const struct ipu3_uapi_yuvp1_iefd_config ipu3_css_iefd_defaults; -extern const struct ipu3_uapi_yuvp1_yds_config ipu3_css_yds_defaults; -extern const struct ipu3_uapi_yuvp1_chnr_config ipu3_css_chnr_defaults; -extern const struct ipu3_uapi_yuvp1_y_ee_nr_config ipu3_css_y_ee_nr_defaults; +extern const struct imgu_css_bds_config + imgu_css_bds_configs[IMGU_BDS_CONFIG_LEN]; +extern const s32 imgu_css_downscale_4taps[IMGU_SCALER_DOWNSCALE_4TAPS_LEN]; +extern const s32 imgu_css_downscale_2taps[IMGU_SCALER_DOWNSCALE_2TAPS_LEN]; +extern const s16 imgu_css_gdc_lut[IMGU_GDC_LUT_UNIT][IMGU_GDC_LUT_LEN]; +extern const struct imgu_css_xnr3_vmem_defaults imgu_css_xnr3_vmem_defaults; +extern const struct ipu3_uapi_bnr_static_config imgu_css_bnr_defaults; +extern const struct ipu3_uapi_dm_config imgu_css_dm_defaults; +extern const struct ipu3_uapi_ccm_mat_config imgu_css_ccm_defaults; +extern const struct ipu3_uapi_gamma_corr_lut imgu_css_gamma_lut; +extern const struct ipu3_uapi_csc_mat_config imgu_css_csc_defaults; +extern const struct ipu3_uapi_cds_params imgu_css_cds_defaults; +extern const struct ipu3_uapi_shd_config_static imgu_css_shd_defaults; +extern const struct ipu3_uapi_yuvp1_iefd_config imgu_css_iefd_defaults; +extern const struct ipu3_uapi_yuvp1_yds_config imgu_css_yds_defaults; +extern const struct ipu3_uapi_yuvp1_chnr_config imgu_css_chnr_defaults; +extern const struct ipu3_uapi_yuvp1_y_ee_nr_config imgu_css_y_ee_nr_defaults; extern const struct ipu3_uapi_yuvp2_tcc_gain_pcwl_lut_static_config - ipu3_css_tcc_gain_pcwl_lut; + imgu_css_tcc_gain_pcwl_lut; extern const struct ipu3_uapi_yuvp2_tcc_r_sqr_lut_static_config - ipu3_css_tcc_r_sqr_lut; -extern const struct imgu_abi_anr_config ipu3_css_anr_defaults; -extern const struct ipu3_uapi_awb_fr_config_s ipu3_css_awb_fr_defaults; -extern const struct ipu3_uapi_ae_grid_config ipu3_css_ae_grid_defaults; -extern const struct ipu3_uapi_ae_ccm ipu3_css_ae_ccm_defaults; -extern const struct ipu3_uapi_af_config_s ipu3_css_af_defaults; -extern const struct ipu3_uapi_awb_config_s ipu3_css_awb_defaults; + imgu_css_tcc_r_sqr_lut; +extern const struct imgu_abi_anr_config imgu_css_anr_defaults; +extern const struct ipu3_uapi_awb_fr_config_s imgu_css_awb_fr_defaults; +extern const struct ipu3_uapi_ae_grid_config imgu_css_ae_grid_defaults; +extern const struct ipu3_uapi_ae_ccm imgu_css_ae_ccm_defaults; +extern const struct ipu3_uapi_af_config_s imgu_css_af_defaults; +extern const struct ipu3_uapi_awb_config_s imgu_css_awb_defaults; #endif diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index e758a650ad2b..9c0352b193a7 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -15,7 +15,7 @@ #define IPU3_RUNNING_MODE_VIDEO 0 #define IPU3_RUNNING_MODE_STILL 1 -static int ipu3_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +static int imgu_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct imgu_v4l2_subdev *imgu_sd = container_of(sd, struct imgu_v4l2_subdev, @@ -50,7 +50,7 @@ static int ipu3_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } -static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) +static int imgu_subdev_s_stream(struct v4l2_subdev *sd, int enable) { int i; unsigned int node; @@ -63,7 +63,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) struct device *dev = &imgu->pci_dev->dev; struct v4l2_pix_format_mplane *fmts[IPU3_CSS_QUEUES] = { NULL }; struct v4l2_rect *rects[IPU3_CSS_RECTS] = { NULL }; - struct ipu3_css_pipe *css_pipe = &imgu->css.pipes[pipe]; + struct imgu_css_pipe *css_pipe = &imgu->css.pipes[pipe]; struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; dev_dbg(dev, "%s %d for pipe %d", __func__, enable, pipe); @@ -107,7 +107,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) rects[IPU3_CSS_RECT_BDS] = &imgu_sd->rect.bds; rects[IPU3_CSS_RECT_GDC] = &imgu_sd->rect.gdc; - r = ipu3_css_fmt_set(&imgu->css, fmts, rects, pipe); + r = imgu_css_fmt_set(&imgu->css, fmts, rects, pipe); if (r) { dev_err(dev, "failed to set initial formats pipe %d with (%d)", pipe, r); @@ -119,7 +119,7 @@ static int ipu3_subdev_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int ipu3_subdev_get_fmt(struct v4l2_subdev *sd, +static int imgu_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { @@ -143,7 +143,7 @@ static int ipu3_subdev_get_fmt(struct v4l2_subdev *sd, return 0; } -static int ipu3_subdev_set_fmt(struct v4l2_subdev *sd, +static int imgu_subdev_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { @@ -189,7 +189,7 @@ static int ipu3_subdev_set_fmt(struct v4l2_subdev *sd, return 0; } -static int ipu3_subdev_get_selection(struct v4l2_subdev *sd, +static int imgu_subdev_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { @@ -222,7 +222,7 @@ static int ipu3_subdev_get_selection(struct v4l2_subdev *sd, return 0; } -static int ipu3_subdev_set_selection(struct v4l2_subdev *sd, +static int imgu_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { @@ -263,7 +263,7 @@ static int ipu3_subdev_set_selection(struct v4l2_subdev *sd, /******************** media_entity_operations ********************/ -static int ipu3_link_setup(struct media_entity *entity, +static int imgu_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { @@ -302,7 +302,7 @@ static int ipu3_link_setup(struct media_entity *entity, /******************** vb2_ops ********************/ -static int ipu3_vb2_buf_init(struct vb2_buffer *vb) +static int imgu_vb2_buf_init(struct vb2_buffer *vb) { struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue); @@ -315,11 +315,11 @@ static int ipu3_vb2_buf_init(struct vb2_buffer *vb) if (queue == IPU3_CSS_QUEUE_PARAMS) return 0; - return ipu3_dmamap_map_sg(imgu, sg->sgl, sg->nents, &buf->map); + return imgu_dmamap_map_sg(imgu, sg->sgl, sg->nents, &buf->map); } /* Called when each buffer is freed */ -static void ipu3_vb2_buf_cleanup(struct vb2_buffer *vb) +static void imgu_vb2_buf_cleanup(struct vb2_buffer *vb) { struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue); struct imgu_buffer *buf = container_of(vb, @@ -331,11 +331,11 @@ static void ipu3_vb2_buf_cleanup(struct vb2_buffer *vb) if (queue == IPU3_CSS_QUEUE_PARAMS) return; - ipu3_dmamap_unmap(imgu, &buf->map); + imgu_dmamap_unmap(imgu, &buf->map); } /* Transfer buffer ownership to me */ -static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) +static void imgu_vb2_buf_queue(struct vb2_buffer *vb) { struct imgu_device *imgu = vb2_get_drv_priv(vb->vb2_queue); struct imgu_video_device *node = @@ -361,7 +361,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) vb2_set_plane_payload(vb, 0, payload); } if (payload >= need_bytes) - r = ipu3_css_set_parameters(&imgu->css, pipe, + r = imgu_css_set_parameters(&imgu->css, pipe, vb2_plane_vaddr(vb, 0)); buf->flags = V4L2_BUF_FLAG_DONE; vb2_buffer_done(vb, r == 0 ? VB2_BUF_STATE_DONE @@ -372,7 +372,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) vid_buf.vbb.vb2_buf); mutex_lock(&imgu->lock); - ipu3_css_buf_init(&buf->css_buf, queue, buf->map.daddr); + imgu_css_buf_init(&buf->css_buf, queue, buf->map.daddr); list_add_tail(&buf->vid_buf.list, &node->buffers); mutex_unlock(&imgu->lock); @@ -388,7 +388,7 @@ static void ipu3_vb2_buf_queue(struct vb2_buffer *vb) } -static int ipu3_vb2_queue_setup(struct vb2_queue *vq, +static int imgu_vb2_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], @@ -425,7 +425,7 @@ static int ipu3_vb2_queue_setup(struct vb2_queue *vq, } /* Check if all enabled video nodes are streaming, exception ignored */ -static bool ipu3_all_nodes_streaming(struct imgu_device *imgu, +static bool imgu_all_nodes_streaming(struct imgu_device *imgu, struct imgu_video_device *except) { unsigned int i, pipe, p; @@ -454,11 +454,11 @@ static bool ipu3_all_nodes_streaming(struct imgu_device *imgu, return true; } -static void ipu3_return_all_buffers(struct imgu_device *imgu, +static void imgu_return_all_buffers(struct imgu_device *imgu, struct imgu_video_device *node, enum vb2_buffer_state state) { - struct ipu3_vb2_buffer *b, *b0; + struct imgu_vb2_buffer *b, *b0; /* Return all buffers */ mutex_lock(&imgu->lock); @@ -469,7 +469,7 @@ static void ipu3_return_all_buffers(struct imgu_device *imgu, mutex_unlock(&imgu->lock); } -static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) +static int imgu_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) { struct imgu_media_pipe *imgu_pipe; struct imgu_device *imgu = vb2_get_drv_priv(vq); @@ -500,7 +500,7 @@ static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) goto fail_return_bufs; - if (!ipu3_all_nodes_streaming(imgu, node)) + if (!imgu_all_nodes_streaming(imgu, node)) return 0; for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM) { @@ -521,12 +521,12 @@ static int ipu3_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) fail_stop_pipeline: media_pipeline_stop(&node->vdev.entity); fail_return_bufs: - ipu3_return_all_buffers(imgu, node, VB2_BUF_STATE_QUEUED); + imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_QUEUED); return r; } -static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) +static void imgu_vb2_stop_streaming(struct vb2_queue *vq) { struct imgu_media_pipe *imgu_pipe; struct imgu_device *imgu = vb2_get_drv_priv(vq); @@ -547,7 +547,7 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) "failed to stop subdev streaming\n"); /* Was this the first node with streaming disabled? */ - if (imgu->streaming && ipu3_all_nodes_streaming(imgu, node)) { + if (imgu->streaming && imgu_all_nodes_streaming(imgu, node)) { /* Yes, really stop streaming now */ dev_dbg(dev, "IMGU streaming is ready to stop"); r = imgu_s_stream(imgu, false); @@ -555,7 +555,7 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) imgu->streaming = false; } - ipu3_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR); + imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR); media_pipeline_stop(&node->vdev.entity); } @@ -566,13 +566,13 @@ static void ipu3_vb2_stop_streaming(struct vb2_queue *vq) #define DEF_VID_CAPTURE 0 #define DEF_VID_OUTPUT 1 -struct ipu3_fmt { +struct imgu_fmt { u32 fourcc; u16 type; /* VID_CAPTURE or VID_OUTPUT not both */ }; /* format descriptions for capture and preview */ -static const struct ipu3_fmt formats[] = { +static const struct imgu_fmt formats[] = { { V4L2_PIX_FMT_NV12, VID_CAPTURE }, { V4L2_PIX_FMT_IPU3_SGRBG10, VID_OUTPUT }, { V4L2_PIX_FMT_IPU3_SBGGR10, VID_OUTPUT }, @@ -581,7 +581,7 @@ static const struct ipu3_fmt formats[] = { }; /* Find the first matched format, return default if not found */ -static const struct ipu3_fmt *find_format(struct v4l2_format *f, u32 type) +static const struct imgu_fmt *find_format(struct v4l2_format *f, u32 type) { unsigned int i; @@ -595,10 +595,10 @@ static const struct ipu3_fmt *find_format(struct v4l2_format *f, u32 type) &formats[DEF_VID_OUTPUT]; } -static int ipu3_vidioc_querycap(struct file *file, void *fh, +static int imgu_vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); strscpy(cap->driver, IMGU_NAME, sizeof(cap->driver)); strscpy(cap->card, IMGU_NAME, sizeof(cap->card)); @@ -646,10 +646,10 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, } /* Propagate forward always the format from the CIO2 subdev */ -static int ipu3_vidioc_g_fmt(struct file *file, void *fh, +static int imgu_vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); f->fmt = node->vdev_fmt.fmt; @@ -670,7 +670,7 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node, struct v4l2_mbus_framefmt pad_fmt; unsigned int i, css_q; int r; - struct ipu3_css_pipe *css_pipe = &imgu->css.pipes[pipe]; + struct imgu_css_pipe *css_pipe = &imgu->css.pipes[pipe]; struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; struct imgu_v4l2_subdev *imgu_sd = &imgu_pipe->imgu_sd; @@ -736,9 +736,9 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node, return -EINVAL; if (try) - r = ipu3_css_fmt_try(&imgu->css, fmts, rects, pipe); + r = imgu_css_fmt_try(&imgu->css, fmts, rects, pipe); else - r = ipu3_css_fmt_set(&imgu->css, fmts, rects, pipe); + r = imgu_css_fmt_set(&imgu->css, fmts, rects, pipe); /* r is the binary number in the firmware blob */ if (r < 0) @@ -752,10 +752,10 @@ static int imgu_fmt(struct imgu_device *imgu, unsigned int pipe, int node, return 0; } -static int ipu3_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +static int imgu_try_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; - const struct ipu3_fmt *fmt; + const struct imgu_fmt *fmt; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) fmt = find_format(f, VID_CAPTURE); @@ -772,58 +772,58 @@ static int ipu3_try_fmt(struct file *file, void *fh, struct v4l2_format *f) return 0; } -static int ipu3_vidioc_try_fmt(struct file *file, void *fh, +static int imgu_vidioc_try_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct imgu_device *imgu = video_drvdata(file); struct device *dev = &imgu->pci_dev->dev; - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; int r; dev_dbg(dev, "%s [%ux%u] for node %d\n", __func__, pix_mp->width, pix_mp->height, node->id); - r = ipu3_try_fmt(file, fh, f); + r = imgu_try_fmt(file, fh, f); if (r) return r; return imgu_fmt(imgu, node->pipe, node->id, f, true); } -static int ipu3_vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +static int imgu_vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct imgu_device *imgu = video_drvdata(file); struct device *dev = &imgu->pci_dev->dev; - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; int r; dev_dbg(dev, "%s [%ux%u] for node %d\n", __func__, pix_mp->width, pix_mp->height, node->id); - r = ipu3_try_fmt(file, fh, f); + r = imgu_try_fmt(file, fh, f); if (r) return r; return imgu_fmt(imgu, node->pipe, node->id, f, false); } -struct ipu3_meta_fmt { +struct imgu_meta_fmt { __u32 fourcc; char *name; }; /* From drivers/media/v4l2-core/v4l2-ioctl.c */ -static const struct ipu3_meta_fmt meta_fmts[] = { +static const struct imgu_meta_fmt meta_fmts[] = { { V4L2_META_FMT_IPU3_PARAMS, "IPU3 processing parameters" }, { V4L2_META_FMT_IPU3_STAT_3A, "IPU3 3A statistics" }, }; -static int ipu3_meta_enum_format(struct file *file, void *fh, +static int imgu_meta_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); unsigned int i = fmt->type == V4L2_BUF_TYPE_META_OUTPUT ? 0 : 1; /* Each node is dedicated to only one meta format */ @@ -836,10 +836,10 @@ static int ipu3_meta_enum_format(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_g_meta_fmt(struct file *file, void *fh, +static int imgu_vidioc_g_meta_fmt(struct file *file, void *fh, struct v4l2_format *f) { - struct imgu_video_device *node = file_to_intel_ipu3_node(file); + struct imgu_video_device *node = file_to_intel_imgu_node(file); if (f->type != node->vbq.type) return -EINVAL; @@ -849,7 +849,7 @@ static int ipu3_vidioc_g_meta_fmt(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_enum_input(struct file *file, void *fh, +static int imgu_vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *input) { if (input->index > 0) @@ -860,19 +860,19 @@ static int ipu3_vidioc_enum_input(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_g_input(struct file *file, void *fh, unsigned int *input) +static int imgu_vidioc_g_input(struct file *file, void *fh, unsigned int *input) { *input = 0; return 0; } -static int ipu3_vidioc_s_input(struct file *file, void *fh, unsigned int input) +static int imgu_vidioc_s_input(struct file *file, void *fh, unsigned int input) { return input == 0 ? 0 : -EINVAL; } -static int ipu3_vidioc_enum_output(struct file *file, void *fh, +static int imgu_vidioc_enum_output(struct file *file, void *fh, struct v4l2_output *output) { if (output->index > 0) @@ -883,7 +883,7 @@ static int ipu3_vidioc_enum_output(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_g_output(struct file *file, void *fh, +static int imgu_vidioc_g_output(struct file *file, void *fh, unsigned int *output) { *output = 0; @@ -891,7 +891,7 @@ static int ipu3_vidioc_g_output(struct file *file, void *fh, return 0; } -static int ipu3_vidioc_s_output(struct file *file, void *fh, +static int imgu_vidioc_s_output(struct file *file, void *fh, unsigned int output) { return output == 0 ? 0 : -EINVAL; @@ -899,54 +899,54 @@ static int ipu3_vidioc_s_output(struct file *file, void *fh, /******************** function pointers ********************/ -static struct v4l2_subdev_internal_ops ipu3_subdev_internal_ops = { - .open = ipu3_subdev_open, +static struct v4l2_subdev_internal_ops imgu_subdev_internal_ops = { + .open = imgu_subdev_open, }; -static const struct v4l2_subdev_core_ops ipu3_subdev_core_ops = { +static const struct v4l2_subdev_core_ops imgu_subdev_core_ops = { .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops ipu3_subdev_video_ops = { - .s_stream = ipu3_subdev_s_stream, +static const struct v4l2_subdev_video_ops imgu_subdev_video_ops = { + .s_stream = imgu_subdev_s_stream, }; -static const struct v4l2_subdev_pad_ops ipu3_subdev_pad_ops = { +static const struct v4l2_subdev_pad_ops imgu_subdev_pad_ops = { .link_validate = v4l2_subdev_link_validate_default, - .get_fmt = ipu3_subdev_get_fmt, - .set_fmt = ipu3_subdev_set_fmt, - .get_selection = ipu3_subdev_get_selection, - .set_selection = ipu3_subdev_set_selection, + .get_fmt = imgu_subdev_get_fmt, + .set_fmt = imgu_subdev_set_fmt, + .get_selection = imgu_subdev_get_selection, + .set_selection = imgu_subdev_set_selection, }; -static const struct v4l2_subdev_ops ipu3_subdev_ops = { - .core = &ipu3_subdev_core_ops, - .video = &ipu3_subdev_video_ops, - .pad = &ipu3_subdev_pad_ops, +static const struct v4l2_subdev_ops imgu_subdev_ops = { + .core = &imgu_subdev_core_ops, + .video = &imgu_subdev_video_ops, + .pad = &imgu_subdev_pad_ops, }; -static const struct media_entity_operations ipu3_media_ops = { - .link_setup = ipu3_link_setup, +static const struct media_entity_operations imgu_media_ops = { + .link_setup = imgu_link_setup, .link_validate = v4l2_subdev_link_validate, }; /****************** vb2_ops of the Q ********************/ -static const struct vb2_ops ipu3_vb2_ops = { - .buf_init = ipu3_vb2_buf_init, - .buf_cleanup = ipu3_vb2_buf_cleanup, - .buf_queue = ipu3_vb2_buf_queue, - .queue_setup = ipu3_vb2_queue_setup, - .start_streaming = ipu3_vb2_start_streaming, - .stop_streaming = ipu3_vb2_stop_streaming, +static const struct vb2_ops imgu_vb2_ops = { + .buf_init = imgu_vb2_buf_init, + .buf_cleanup = imgu_vb2_buf_cleanup, + .buf_queue = imgu_vb2_buf_queue, + .queue_setup = imgu_vb2_queue_setup, + .start_streaming = imgu_vb2_start_streaming, + .stop_streaming = imgu_vb2_stop_streaming, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, }; /****************** v4l2_file_operations *****************/ -static const struct v4l2_file_operations ipu3_v4l2_fops = { +static const struct v4l2_file_operations imgu_v4l2_fops = { .unlocked_ioctl = video_ioctl2, .open = v4l2_fh_open, .release = vb2_fop_release, @@ -956,26 +956,26 @@ static const struct v4l2_file_operations ipu3_v4l2_fops = { /******************** v4l2_ioctl_ops ********************/ -static const struct v4l2_ioctl_ops ipu3_v4l2_ioctl_ops = { - .vidioc_querycap = ipu3_vidioc_querycap, +static const struct v4l2_ioctl_ops imgu_v4l2_ioctl_ops = { + .vidioc_querycap = imgu_vidioc_querycap, .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap_mplane = ipu3_vidioc_g_fmt, - .vidioc_s_fmt_vid_cap_mplane = ipu3_vidioc_s_fmt, - .vidioc_try_fmt_vid_cap_mplane = ipu3_vidioc_try_fmt, + .vidioc_g_fmt_vid_cap_mplane = imgu_vidioc_g_fmt, + .vidioc_s_fmt_vid_cap_mplane = imgu_vidioc_s_fmt, + .vidioc_try_fmt_vid_cap_mplane = imgu_vidioc_try_fmt, .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out, - .vidioc_g_fmt_vid_out_mplane = ipu3_vidioc_g_fmt, - .vidioc_s_fmt_vid_out_mplane = ipu3_vidioc_s_fmt, - .vidioc_try_fmt_vid_out_mplane = ipu3_vidioc_try_fmt, + .vidioc_g_fmt_vid_out_mplane = imgu_vidioc_g_fmt, + .vidioc_s_fmt_vid_out_mplane = imgu_vidioc_s_fmt, + .vidioc_try_fmt_vid_out_mplane = imgu_vidioc_try_fmt, - .vidioc_enum_output = ipu3_vidioc_enum_output, - .vidioc_g_output = ipu3_vidioc_g_output, - .vidioc_s_output = ipu3_vidioc_s_output, + .vidioc_enum_output = imgu_vidioc_enum_output, + .vidioc_g_output = imgu_vidioc_g_output, + .vidioc_s_output = imgu_vidioc_s_output, - .vidioc_enum_input = ipu3_vidioc_enum_input, - .vidioc_g_input = ipu3_vidioc_g_input, - .vidioc_s_input = ipu3_vidioc_s_input, + .vidioc_enum_input = imgu_vidioc_enum_input, + .vidioc_g_input = imgu_vidioc_g_input, + .vidioc_s_input = imgu_vidioc_s_input, /* buffer queue management */ .vidioc_reqbufs = vb2_ioctl_reqbufs, @@ -989,20 +989,20 @@ static const struct v4l2_ioctl_ops ipu3_v4l2_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, }; -static const struct v4l2_ioctl_ops ipu3_v4l2_meta_ioctl_ops = { - .vidioc_querycap = ipu3_vidioc_querycap, +static const struct v4l2_ioctl_ops imgu_v4l2_meta_ioctl_ops = { + .vidioc_querycap = imgu_vidioc_querycap, /* meta capture */ - .vidioc_enum_fmt_meta_cap = ipu3_meta_enum_format, - .vidioc_g_fmt_meta_cap = ipu3_vidioc_g_meta_fmt, - .vidioc_s_fmt_meta_cap = ipu3_vidioc_g_meta_fmt, - .vidioc_try_fmt_meta_cap = ipu3_vidioc_g_meta_fmt, + .vidioc_enum_fmt_meta_cap = imgu_meta_enum_format, + .vidioc_g_fmt_meta_cap = imgu_vidioc_g_meta_fmt, + .vidioc_s_fmt_meta_cap = imgu_vidioc_g_meta_fmt, + .vidioc_try_fmt_meta_cap = imgu_vidioc_g_meta_fmt, /* meta output */ - .vidioc_enum_fmt_meta_out = ipu3_meta_enum_format, - .vidioc_g_fmt_meta_out = ipu3_vidioc_g_meta_fmt, - .vidioc_s_fmt_meta_out = ipu3_vidioc_g_meta_fmt, - .vidioc_try_fmt_meta_out = ipu3_vidioc_g_meta_fmt, + .vidioc_enum_fmt_meta_out = imgu_meta_enum_format, + .vidioc_g_fmt_meta_out = imgu_vidioc_g_meta_fmt, + .vidioc_s_fmt_meta_out = imgu_vidioc_g_meta_fmt, + .vidioc_try_fmt_meta_out = imgu_vidioc_g_meta_fmt, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, @@ -1015,7 +1015,7 @@ static const struct v4l2_ioctl_ops ipu3_v4l2_meta_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, }; -static int ipu3_sd_s_ctrl(struct v4l2_ctrl *ctrl) +static int imgu_sd_s_ctrl(struct v4l2_ctrl *ctrl) { struct imgu_v4l2_subdev *imgu_sd = container_of(ctrl->handler, struct imgu_v4l2_subdev, ctrl_handler); @@ -1034,29 +1034,29 @@ static int ipu3_sd_s_ctrl(struct v4l2_ctrl *ctrl) } } -static const struct v4l2_ctrl_ops ipu3_subdev_ctrl_ops = { - .s_ctrl = ipu3_sd_s_ctrl, +static const struct v4l2_ctrl_ops imgu_subdev_ctrl_ops = { + .s_ctrl = imgu_sd_s_ctrl, }; -static const char * const ipu3_ctrl_mode_strings[] = { +static const char * const imgu_ctrl_mode_strings[] = { "Video mode", "Still mode", }; -static const struct v4l2_ctrl_config ipu3_subdev_ctrl_mode = { - .ops = &ipu3_subdev_ctrl_ops, +static const struct v4l2_ctrl_config imgu_subdev_ctrl_mode = { + .ops = &imgu_subdev_ctrl_ops, .id = V4L2_CID_INTEL_IPU3_MODE, .name = "IPU3 Pipe Mode", .type = V4L2_CTRL_TYPE_MENU, - .max = ARRAY_SIZE(ipu3_ctrl_mode_strings) - 1, + .max = ARRAY_SIZE(imgu_ctrl_mode_strings) - 1, .def = IPU3_RUNNING_MODE_VIDEO, - .qmenu = ipu3_ctrl_mode_strings, + .qmenu = imgu_ctrl_mode_strings, }; /******************** Framework registration ********************/ /* helper function to config node's video properties */ -static void ipu3_node_to_v4l2(u32 node, struct video_device *vdev, +static void imgu_node_to_v4l2(u32 node, struct video_device *vdev, struct v4l2_format *f) { u32 cap; @@ -1068,32 +1068,32 @@ static void ipu3_node_to_v4l2(u32 node, struct video_device *vdev, case IMGU_NODE_IN: cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE; f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - vdev->ioctl_ops = &ipu3_v4l2_ioctl_ops; + vdev->ioctl_ops = &imgu_v4l2_ioctl_ops; break; case IMGU_NODE_PARAMS: cap = V4L2_CAP_META_OUTPUT; f->type = V4L2_BUF_TYPE_META_OUTPUT; f->fmt.meta.dataformat = V4L2_META_FMT_IPU3_PARAMS; - vdev->ioctl_ops = &ipu3_v4l2_meta_ioctl_ops; - ipu3_css_meta_fmt_set(&f->fmt.meta); + vdev->ioctl_ops = &imgu_v4l2_meta_ioctl_ops; + imgu_css_meta_fmt_set(&f->fmt.meta); break; case IMGU_NODE_STAT_3A: cap = V4L2_CAP_META_CAPTURE; f->type = V4L2_BUF_TYPE_META_CAPTURE; f->fmt.meta.dataformat = V4L2_META_FMT_IPU3_STAT_3A; - vdev->ioctl_ops = &ipu3_v4l2_meta_ioctl_ops; - ipu3_css_meta_fmt_set(&f->fmt.meta); + vdev->ioctl_ops = &imgu_v4l2_meta_ioctl_ops; + imgu_css_meta_fmt_set(&f->fmt.meta); break; default: cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE; f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - vdev->ioctl_ops = &ipu3_v4l2_ioctl_ops; + vdev->ioctl_ops = &imgu_v4l2_ioctl_ops; } vdev->device_caps = V4L2_CAP_STREAMING | cap; } -static int ipu3_v4l2_subdev_register(struct imgu_device *imgu, +static int imgu_v4l2_subdev_register(struct imgu_device *imgu, struct imgu_v4l2_subdev *imgu_sd, unsigned int pipe) { @@ -1109,16 +1109,16 @@ static int ipu3_v4l2_subdev_register(struct imgu_device *imgu, "failed initialize subdev media entity (%d)\n", r); return r; } - imgu_sd->subdev.entity.ops = &ipu3_media_ops; + imgu_sd->subdev.entity.ops = &imgu_media_ops; for (i = 0; i < IMGU_NODE_NUM; i++) { imgu_sd->subdev_pads[i].flags = imgu_pipe->nodes[i].output ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; } /* Initialize subdev */ - v4l2_subdev_init(&imgu_sd->subdev, &ipu3_subdev_ops); + v4l2_subdev_init(&imgu_sd->subdev, &imgu_subdev_ops); imgu_sd->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS; - imgu_sd->subdev.internal_ops = &ipu3_subdev_internal_ops; + imgu_sd->subdev.internal_ops = &imgu_subdev_internal_ops; imgu_sd->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; snprintf(imgu_sd->subdev.name, sizeof(imgu_sd->subdev.name), @@ -1127,7 +1127,7 @@ static int ipu3_v4l2_subdev_register(struct imgu_device *imgu, atomic_set(&imgu_sd->running_mode, IPU3_RUNNING_MODE_VIDEO); v4l2_ctrl_handler_init(hdl, 1); imgu_sd->subdev.ctrl_handler = hdl; - imgu_sd->ctrl = v4l2_ctrl_new_custom(hdl, &ipu3_subdev_ctrl_mode, NULL); + imgu_sd->ctrl = v4l2_ctrl_new_custom(hdl, &imgu_subdev_ctrl_mode, NULL); if (hdl->error) { r = hdl->error; dev_err(&imgu->pci_dev->dev, @@ -1151,7 +1151,7 @@ fail_subdev: return r; } -static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, +static int imgu_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, int node_num) { int r; @@ -1196,7 +1196,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, node->pad_fmt = def_bus_fmt; node->id = node_num; node->pipe = pipe; - ipu3_node_to_v4l2(node_num, vdev, &node->vdev_fmt); + imgu_node_to_v4l2(node_num, vdev, &node->vdev_fmt); if (node->vdev_fmt.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || node->vdev_fmt.type == @@ -1221,11 +1221,11 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, /* Initialize vbq */ vbq->type = node->vdev_fmt.type; vbq->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF; - vbq->ops = &ipu3_vb2_ops; + vbq->ops = &imgu_vb2_ops; vbq->mem_ops = &vb2_dma_sg_memops; if (imgu->buf_struct_size <= 0) imgu->buf_struct_size = - sizeof(struct ipu3_vb2_buffer); + sizeof(struct imgu_vb2_buffer); vbq->buf_struct_size = imgu->buf_struct_size; vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; /* can streamon w/o buffers */ @@ -1243,7 +1243,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, snprintf(vdev->name, sizeof(vdev->name), "%s %d %s", IMGU_NAME, pipe, node->name); vdev->release = video_device_release_empty; - vdev->fops = &ipu3_v4l2_fops; + vdev->fops = &imgu_v4l2_fops; vdev->lock = &node->lock; vdev->v4l2_dev = &imgu->v4l2_dev; vdev->queue = &node->vbq; @@ -1276,7 +1276,7 @@ static int ipu3_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, return 0; } -static void ipu3_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu, +static void imgu_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu, unsigned int pipe, int node) { int i; @@ -1289,12 +1289,12 @@ static void ipu3_v4l2_nodes_cleanup_pipe(struct imgu_device *imgu, } } -static int ipu3_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe) +static int imgu_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe) { int i, r; for (i = 0; i < IMGU_NODE_NUM; i++) { - r = ipu3_v4l2_node_setup(imgu, pipe, i); + r = imgu_v4l2_node_setup(imgu, pipe, i); if (r) goto cleanup; } @@ -1302,11 +1302,11 @@ static int ipu3_v4l2_nodes_setup_pipe(struct imgu_device *imgu, int pipe) return 0; cleanup: - ipu3_v4l2_nodes_cleanup_pipe(imgu, pipe, i); + imgu_v4l2_nodes_cleanup_pipe(imgu, pipe, i); return r; } -static void ipu3_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i) +static void imgu_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i) { struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[i]; @@ -1315,13 +1315,13 @@ static void ipu3_v4l2_subdev_cleanup(struct imgu_device *imgu, unsigned int i) media_entity_cleanup(&imgu_pipe->imgu_sd.subdev.entity); } -static void ipu3_v4l2_cleanup_pipes(struct imgu_device *imgu, unsigned int pipe) +static void imgu_v4l2_cleanup_pipes(struct imgu_device *imgu, unsigned int pipe) { int i; for (i = 0; i < pipe; i++) { - ipu3_v4l2_nodes_cleanup_pipe(imgu, i, IMGU_NODE_NUM); - ipu3_v4l2_subdev_cleanup(imgu, i); + imgu_v4l2_nodes_cleanup_pipe(imgu, i, IMGU_NODE_NUM); + imgu_v4l2_subdev_cleanup(imgu, i); } } @@ -1332,15 +1332,15 @@ static int imgu_v4l2_register_pipes(struct imgu_device *imgu) for (i = 0; i < IMGU_MAX_PIPE_NUM; i++) { imgu_pipe = &imgu->imgu_pipe[i]; - r = ipu3_v4l2_subdev_register(imgu, &imgu_pipe->imgu_sd, i); + r = imgu_v4l2_subdev_register(imgu, &imgu_pipe->imgu_sd, i); if (r) { dev_err(&imgu->pci_dev->dev, "failed to register subdev%d ret (%d)\n", i, r); goto pipes_cleanup; } - r = ipu3_v4l2_nodes_setup_pipe(imgu, i); + r = imgu_v4l2_nodes_setup_pipe(imgu, i); if (r) { - ipu3_v4l2_subdev_cleanup(imgu, i); + imgu_v4l2_subdev_cleanup(imgu, i); goto pipes_cleanup; } } @@ -1348,7 +1348,7 @@ static int imgu_v4l2_register_pipes(struct imgu_device *imgu) return 0; pipes_cleanup: - ipu3_v4l2_cleanup_pipes(imgu, i); + imgu_v4l2_cleanup_pipes(imgu, i); return r; } @@ -1396,7 +1396,7 @@ int imgu_v4l2_register(struct imgu_device *imgu) return 0; fail_subdevs: - ipu3_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); + imgu_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); fail_v4l2_pipes: v4l2_device_unregister(&imgu->v4l2_dev); fail_v4l2_dev: @@ -1408,7 +1408,7 @@ fail_v4l2_dev: int imgu_v4l2_unregister(struct imgu_device *imgu) { media_device_unregister(&imgu->media_dev); - ipu3_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); + imgu_v4l2_cleanup_pipes(imgu, IMGU_MAX_PIPE_NUM); v4l2_device_unregister(&imgu->v4l2_dev); media_device_cleanup(&imgu->media_dev); @@ -1418,8 +1418,8 @@ int imgu_v4l2_unregister(struct imgu_device *imgu) void imgu_v4l2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) { - struct ipu3_vb2_buffer *b = - container_of(vb, struct ipu3_vb2_buffer, vbb.vb2_buf); + struct imgu_vb2_buffer *b = + container_of(vb, struct imgu_vb2_buffer, vbb.vb2_buf); list_del(&b->list); vb2_buffer_done(&b->vbb.vb2_buf, state); diff --git a/drivers/staging/media/ipu3/ipu3.c b/drivers/staging/media/ipu3/ipu3.c index 839d9398f8e9..d575ac78c8f0 100644 --- a/drivers/staging/media/ipu3/ipu3.c +++ b/drivers/staging/media/ipu3/ipu3.c @@ -72,7 +72,7 @@ static void imgu_dummybufs_cleanup(struct imgu_device *imgu, unsigned int pipe) struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; for (i = 0; i < IPU3_CSS_QUEUES; i++) - ipu3_dmamap_free(imgu, + imgu_dmamap_free(imgu, &imgu_pipe->queues[i].dmap); } @@ -93,7 +93,7 @@ static int imgu_dummybufs_preallocate(struct imgu_device *imgu, if (i == IMGU_QUEUE_MASTER || size == 0) continue; - if (!ipu3_dmamap_alloc(imgu, + if (!imgu_dmamap_alloc(imgu, &imgu_pipe->queues[i].dmap, size)) { imgu_dummybufs_cleanup(imgu, pipe); return -ENOMEM; @@ -133,7 +133,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe) else size = mpix->plane_fmt[0].sizeimage; - if (ipu3_css_dma_buffer_resize(imgu, + if (imgu_css_dma_buffer_resize(imgu, &imgu_pipe->queues[i].dmap, size)) { imgu_dummybufs_cleanup(imgu, pipe); @@ -141,7 +141,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe) } for (k = 0; k < IMGU_MAX_QUEUE_DEPTH; k++) - ipu3_css_buf_init(&imgu_pipe->queues[i].dummybufs[k], i, + imgu_css_buf_init(&imgu_pipe->queues[i].dummybufs[k], i, imgu_pipe->queues[i].dmap.daddr); } @@ -149,7 +149,7 @@ static int imgu_dummybufs_init(struct imgu_device *imgu, unsigned int pipe) } /* May be called from atomic context */ -static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, +static struct imgu_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, int queue, unsigned int pipe) { unsigned int i; @@ -164,14 +164,14 @@ static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, return NULL; for (i = 0; i < IMGU_MAX_QUEUE_DEPTH; i++) - if (ipu3_css_buf_state(&imgu_pipe->queues[queue].dummybufs[i]) != + if (imgu_css_buf_state(&imgu_pipe->queues[queue].dummybufs[i]) != IPU3_CSS_BUFFER_QUEUED) break; if (i == IMGU_MAX_QUEUE_DEPTH) return NULL; - ipu3_css_buf_init(&imgu_pipe->queues[queue].dummybufs[i], queue, + imgu_css_buf_init(&imgu_pipe->queues[queue].dummybufs[i], queue, imgu_pipe->queues[queue].dmap.daddr); return &imgu_pipe->queues[queue].dummybufs[i]; @@ -179,7 +179,7 @@ static struct ipu3_css_buffer *imgu_dummybufs_get(struct imgu_device *imgu, /* Check if given buffer is a dummy buffer */ static bool imgu_dummybufs_check(struct imgu_device *imgu, - struct ipu3_css_buffer *buf, + struct imgu_css_buffer *buf, unsigned int pipe) { unsigned int i; @@ -200,7 +200,7 @@ static void imgu_buffer_done(struct imgu_device *imgu, struct vb2_buffer *vb, mutex_unlock(&imgu->lock); } -static struct ipu3_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu, +static struct imgu_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu, unsigned int node, unsigned int pipe) { @@ -212,7 +212,7 @@ static struct ipu3_css_buffer *imgu_queue_getbuf(struct imgu_device *imgu, /* Find first free buffer from the node */ list_for_each_entry(buf, &imgu_pipe->nodes[node].buffers, vid_buf.list) { - if (ipu3_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_NEW) + if (imgu_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_NEW) return &buf->css_buf; } @@ -230,7 +230,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe int r = 0; struct imgu_media_pipe *imgu_pipe = &imgu->imgu_pipe[pipe]; - if (!ipu3_css_is_streaming(&imgu->css)) + if (!imgu_css_is_streaming(&imgu->css)) return 0; dev_dbg(&imgu->pci_dev->dev, "Queue buffers to pipe %d", pipe); @@ -247,7 +247,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe "Vf not enabled, ignore queue"); continue; } else if (imgu_pipe->queue_enabled[node]) { - struct ipu3_css_buffer *buf = + struct imgu_css_buffer *buf = imgu_queue_getbuf(imgu, node, pipe); struct imgu_buffer *ibuf = NULL; bool dummy; @@ -255,7 +255,7 @@ int imgu_queue_buffers(struct imgu_device *imgu, bool initial, unsigned int pipe if (!buf) break; - r = ipu3_css_buf_queue(&imgu->css, pipe, buf); + r = imgu_css_buf_queue(&imgu->css, pipe, buf); if (r) break; dummy = imgu_dummybufs_check(imgu, buf, pipe); @@ -300,7 +300,7 @@ failed: list_for_each_entry_safe(buf, buf0, &imgu_pipe->nodes[node].buffers, vid_buf.list) { - if (ipu3_css_buf_state(&buf->css_buf) == + if (imgu_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_QUEUED) continue; /* Was already queued, skip */ @@ -317,18 +317,18 @@ static int imgu_powerup(struct imgu_device *imgu) { int r; - r = ipu3_css_set_powerup(&imgu->pci_dev->dev, imgu->base); + r = imgu_css_set_powerup(&imgu->pci_dev->dev, imgu->base); if (r) return r; - ipu3_mmu_resume(imgu->mmu); + imgu_mmu_resume(imgu->mmu); return 0; } static void imgu_powerdown(struct imgu_device *imgu) { - ipu3_mmu_suspend(imgu->mmu); - ipu3_css_set_powerdown(&imgu->pci_dev->dev, imgu->base); + imgu_mmu_suspend(imgu->mmu); + imgu_css_set_powerdown(&imgu->pci_dev->dev, imgu->base); } int imgu_s_stream(struct imgu_device *imgu, int enable) @@ -341,7 +341,7 @@ int imgu_s_stream(struct imgu_device *imgu, int enable) dev_dbg(dev, "stream off\n"); /* Block new buffers to be queued to CSS. */ atomic_set(&imgu->qbuf_barrier, 1); - ipu3_css_stop_streaming(&imgu->css); + imgu_css_stop_streaming(&imgu->css); synchronize_irq(imgu->pci_dev->irq); atomic_set(&imgu->qbuf_barrier, 0); imgu_powerdown(imgu); @@ -366,7 +366,7 @@ int imgu_s_stream(struct imgu_device *imgu, int enable) } /* Start CSS streaming */ - r = ipu3_css_start_streaming(&imgu->css); + r = imgu_css_start_streaming(&imgu->css); if (r) { dev_err(dev, "failed to start css streaming (%d)", r); goto fail_start_streaming; @@ -393,7 +393,7 @@ fail_queueing: for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM) imgu_dummybufs_cleanup(imgu, pipe); fail_dummybufs: - ipu3_css_stop_streaming(&imgu->css); + imgu_css_stop_streaming(&imgu->css); fail_start_streaming: pm_runtime_put(dev); @@ -435,7 +435,7 @@ static int imgu_video_nodes_init(struct imgu_device *imgu) rects[IPU3_CSS_RECT_EFFECTIVE] = &imgu_pipe->imgu_sd.rect.eff; rects[IPU3_CSS_RECT_BDS] = &imgu_pipe->imgu_sd.rect.bds; - ipu3_css_fmt_set(&imgu->css, fmts, rects, j); + imgu_css_fmt_set(&imgu->css, fmts, rects, j); /* Pre-allocate dummy buffers */ r = imgu_dummybufs_preallocate(imgu, j); @@ -478,14 +478,14 @@ static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr) /* Dequeue / queue buffers */ do { u64 ns = ktime_get_ns(); - struct ipu3_css_buffer *b; + struct imgu_css_buffer *b; struct imgu_buffer *buf = NULL; unsigned int node, pipe; bool dummy; do { mutex_lock(&imgu->lock); - b = ipu3_css_buf_dequeue(&imgu->css); + b = imgu_css_buf_dequeue(&imgu->css); mutex_unlock(&imgu->lock); } while (PTR_ERR(b) == -EAGAIN); @@ -525,12 +525,12 @@ static irqreturn_t imgu_isr_threaded(int irq, void *imgu_ptr) buf->vid_buf.vbb.sequence); } imgu_buffer_done(imgu, &buf->vid_buf.vbb.vb2_buf, - ipu3_css_buf_state(&buf->css_buf) == + imgu_css_buf_state(&buf->css_buf) == IPU3_CSS_BUFFER_DONE ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR); mutex_lock(&imgu->lock); - if (ipu3_css_queue_empty(&imgu->css)) + if (imgu_css_queue_empty(&imgu->css)) wake_up_all(&imgu->buf_drain_wq); mutex_unlock(&imgu->lock); } while (1); @@ -552,7 +552,7 @@ static irqreturn_t imgu_isr(int irq, void *imgu_ptr) struct imgu_device *imgu = imgu_ptr; /* acknowledge interruption */ - if (ipu3_css_irq_ack(&imgu->css) < 0) + if (imgu_css_irq_ack(&imgu->css) < 0) return IRQ_NONE; return IRQ_WAKE_THREAD; @@ -637,21 +637,21 @@ static int imgu_pci_probe(struct pci_dev *pci_dev, atomic_set(&imgu->qbuf_barrier, 0); init_waitqueue_head(&imgu->buf_drain_wq); - r = ipu3_css_set_powerup(&pci_dev->dev, imgu->base); + r = imgu_css_set_powerup(&pci_dev->dev, imgu->base); if (r) { dev_err(&pci_dev->dev, "failed to power up CSS (%d)\n", r); goto out_mutex_destroy; } - imgu->mmu = ipu3_mmu_init(&pci_dev->dev, imgu->base); + imgu->mmu = imgu_mmu_init(&pci_dev->dev, imgu->base); if (IS_ERR(imgu->mmu)) { r = PTR_ERR(imgu->mmu); dev_err(&pci_dev->dev, "failed to initialize MMU (%d)\n", r); goto out_css_powerdown; } - r = ipu3_dmamap_init(imgu); + r = imgu_dmamap_init(imgu); if (r) { dev_err(&pci_dev->dev, "failed to initialize DMA mapping (%d)\n", r); @@ -659,7 +659,7 @@ static int imgu_pci_probe(struct pci_dev *pci_dev, } /* ISP programming */ - r = ipu3_css_init(&pci_dev->dev, &imgu->css, imgu->base, phys_len); + r = imgu_css_init(&pci_dev->dev, &imgu->css, imgu->base, phys_len); if (r) { dev_err(&pci_dev->dev, "failed to initialize CSS (%d)\n", r); goto out_dmamap_exit; @@ -689,13 +689,13 @@ static int imgu_pci_probe(struct pci_dev *pci_dev, out_video_exit: imgu_video_nodes_exit(imgu); out_css_cleanup: - ipu3_css_cleanup(&imgu->css); + imgu_css_cleanup(&imgu->css); out_dmamap_exit: - ipu3_dmamap_exit(imgu); + imgu_dmamap_exit(imgu); out_mmu_exit: - ipu3_mmu_exit(imgu->mmu); + imgu_mmu_exit(imgu->mmu); out_css_powerdown: - ipu3_css_set_powerdown(&pci_dev->dev, imgu->base); + imgu_css_set_powerdown(&pci_dev->dev, imgu->base); out_mutex_destroy: mutex_destroy(&imgu->lock); @@ -710,10 +710,10 @@ static void imgu_pci_remove(struct pci_dev *pci_dev) pm_runtime_get_noresume(&pci_dev->dev); imgu_video_nodes_exit(imgu); - ipu3_css_cleanup(&imgu->css); - ipu3_css_set_powerdown(&pci_dev->dev, imgu->base); - ipu3_dmamap_exit(imgu); - ipu3_mmu_exit(imgu->mmu); + imgu_css_cleanup(&imgu->css); + imgu_css_set_powerdown(&pci_dev->dev, imgu->base); + imgu_dmamap_exit(imgu); + imgu_mmu_exit(imgu->mmu); mutex_destroy(&imgu->lock); } @@ -723,7 +723,7 @@ static int __maybe_unused imgu_suspend(struct device *dev) struct imgu_device *imgu = pci_get_drvdata(pci_dev); dev_dbg(dev, "enter %s\n", __func__); - imgu->suspend_in_stream = ipu3_css_is_streaming(&imgu->css); + imgu->suspend_in_stream = imgu_css_is_streaming(&imgu->css); if (!imgu->suspend_in_stream) goto out; /* Block new buffers to be queued to CSS. */ @@ -735,10 +735,10 @@ static int __maybe_unused imgu_suspend(struct device *dev) synchronize_irq(pci_dev->irq); /* Wait until all buffers in CSS are done. */ if (!wait_event_timeout(imgu->buf_drain_wq, - ipu3_css_queue_empty(&imgu->css), msecs_to_jiffies(1000))) + imgu_css_queue_empty(&imgu->css), msecs_to_jiffies(1000))) dev_err(dev, "wait buffer drain timeout.\n"); - ipu3_css_stop_streaming(&imgu->css); + imgu_css_stop_streaming(&imgu->css); atomic_set(&imgu->qbuf_barrier, 0); imgu_powerdown(imgu); pm_runtime_force_suspend(dev); @@ -768,7 +768,7 @@ static int __maybe_unused imgu_resume(struct device *dev) } /* Start CSS streaming */ - r = ipu3_css_start_streaming(&imgu->css); + r = imgu_css_start_streaming(&imgu->css); if (r) { dev_err(dev, "failed to resume css streaming (%d)", r); goto out; diff --git a/drivers/staging/media/ipu3/ipu3.h b/drivers/staging/media/ipu3/ipu3.h index 04fc99f47ebb..6b408f726667 100644 --- a/drivers/staging/media/ipu3/ipu3.h +++ b/drivers/staging/media/ipu3/ipu3.h @@ -32,7 +32,7 @@ #define IMGU_NODE_STAT_3A 4 /* 3A statistics */ #define IMGU_NODE_NUM 5 -#define file_to_intel_ipu3_node(__file) \ +#define file_to_intel_imgu_node(__file) \ container_of(video_devdata(__file), struct imgu_video_device, vdev) #define IPU3_INPUT_MIN_WIDTH 0U @@ -44,7 +44,7 @@ #define IPU3_OUTPUT_MAX_WIDTH 4480U #define IPU3_OUTPUT_MAX_HEIGHT 34004U -struct ipu3_vb2_buffer { +struct imgu_vb2_buffer { /* Public fields */ struct vb2_v4l2_buffer vbb; /* Must be the first field */ @@ -53,9 +53,9 @@ struct ipu3_vb2_buffer { }; struct imgu_buffer { - struct ipu3_vb2_buffer vid_buf; /* Must be the first field */ - struct ipu3_css_buffer css_buf; - struct ipu3_css_map map; + struct imgu_vb2_buffer vid_buf; /* Must be the first field */ + struct imgu_css_buffer css_buf; + struct imgu_css_map map; }; struct imgu_node_mapping { @@ -107,8 +107,8 @@ struct imgu_media_pipe { /* Internally enabled queues */ struct { - struct ipu3_css_map dmap; - struct ipu3_css_buffer dummybufs[IMGU_MAX_QUEUE_DEPTH]; + struct imgu_css_map dmap; + struct imgu_css_buffer dummybufs[IMGU_MAX_QUEUE_DEPTH]; } queues[IPU3_CSS_QUEUES]; struct imgu_video_device nodes[IMGU_NODE_NUM]; bool queue_enabled[IMGU_NODE_NUM]; @@ -135,11 +135,11 @@ struct imgu_device { struct v4l2_file_operations v4l2_file_ops; /* MMU driver for css */ - struct ipu3_mmu_info *mmu; + struct imgu_mmu_info *mmu; struct iova_domain iova_domain; /* css - Camera Sub-System */ - struct ipu3_css css; + struct imgu_css css; /* * Coarse-grained lock to protect -- cgit v1.2.3-59-g8ed1b From f47c34be55f744ffd10b762e00366770048ce028 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 4 Feb 2019 04:07:25 -0500 Subject: media: soc_camera: Remove the mt9m001 SoC camera sensor driver There is a V4L2 sub-device sensor driver for the mt9m001. There is already a non-soc_camera driver. So, remove the SoC camera one. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 7 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/soc_mt9m001.c | 757 ----------------------------- 3 files changed, 765 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/soc_mt9m001.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index dea66ef1394d..5dcb93c0a902 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -1,12 +1,5 @@ comment "soc_camera sensor drivers" -config SOC_CAMERA_MT9M001 - tristate "mt9m001 support" - depends on SOC_CAMERA && I2C - help - This driver supports MT9M001 cameras from Micron, monochrome - and colour models. - config SOC_CAMERA_MT9M111 tristate "legacy soc_camera mt9m111, mt9m112 and mt9m131 support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index 94659f7aa195..a215d8b095d9 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SOC_CAMERA_MT9M001) += soc_mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o diff --git a/drivers/media/i2c/soc_camera/soc_mt9m001.c b/drivers/media/i2c/soc_camera/soc_mt9m001.c deleted file mode 100644 index a1a85ff838c5..000000000000 --- a/drivers/media/i2c/soc_camera/soc_mt9m001.c +++ /dev/null @@ -1,757 +0,0 @@ -/* - * Driver for MT9M001 CMOS Image Sensor from Micron - * - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* - * mt9m001 i2c address 0x5d - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc - */ - -/* mt9m001 selected register addresses */ -#define MT9M001_CHIP_VERSION 0x00 -#define MT9M001_ROW_START 0x01 -#define MT9M001_COLUMN_START 0x02 -#define MT9M001_WINDOW_HEIGHT 0x03 -#define MT9M001_WINDOW_WIDTH 0x04 -#define MT9M001_HORIZONTAL_BLANKING 0x05 -#define MT9M001_VERTICAL_BLANKING 0x06 -#define MT9M001_OUTPUT_CONTROL 0x07 -#define MT9M001_SHUTTER_WIDTH 0x09 -#define MT9M001_FRAME_RESTART 0x0b -#define MT9M001_SHUTTER_DELAY 0x0c -#define MT9M001_RESET 0x0d -#define MT9M001_READ_OPTIONS1 0x1e -#define MT9M001_READ_OPTIONS2 0x20 -#define MT9M001_GLOBAL_GAIN 0x35 -#define MT9M001_CHIP_ENABLE 0xF1 - -#define MT9M001_MAX_WIDTH 1280 -#define MT9M001_MAX_HEIGHT 1024 -#define MT9M001_MIN_WIDTH 48 -#define MT9M001_MIN_HEIGHT 32 -#define MT9M001_COLUMN_SKIP 20 -#define MT9M001_ROW_SKIP 12 - -/* MT9M001 has only one fixed colorspace per pixelcode */ -struct mt9m001_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -/* Find a data format by a pixel code in an array */ -static const struct mt9m001_datafmt *mt9m001_find_datafmt( - u32 code, const struct mt9m001_datafmt *fmt, - int n) -{ - int i; - for (i = 0; i < n; i++) - if (fmt[i].code == code) - return fmt + i; - - return NULL; -} - -static const struct mt9m001_datafmt mt9m001_colour_fmts[] = { - /* - * Order important: first natively supported, - * second supported with a GPIO extender - */ - {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, -}; - -static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = { - /* Order important - see above */ - {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, -}; - -struct mt9m001 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/auto-exposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; - const struct mt9m001_datafmt *fmt; - const struct mt9m001_datafmt *fmts; - int num_fmts; - unsigned int total_h; - unsigned short y_skip_top; /* Lines to skip at the top */ -}; - -static struct mt9m001 *to_mt9m001(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct mt9m001, subdev); -} - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret | data); -} - -static int reg_clear(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret & ~data); -} - -static int mt9m001_init(struct i2c_client *client) -{ - int ret; - - dev_dbg(&client->dev, "%s\n", __func__); - - /* - * We don't know, whether platform provides reset, issue a soft reset - * too. This returns all registers to their default values. - */ - ret = reg_write(client, MT9M001_RESET, 1); - if (!ret) - ret = reg_write(client, MT9M001_RESET, 0); - - /* Disable chip, synchronous option update */ - if (!ret) - ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0); - - return ret; -} - -static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* Switch to master "normal" mode or stop sensor readout */ - if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0) - return -EIO; - return 0; -} - -static int mt9m001_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - struct v4l2_rect rect = sel->r; - const u16 hblank = 9, vblank = 25; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - if (mt9m001->fmts == mt9m001_colour_fmts) - /* - * Bayer format - even number of rows for simplicity, - * but let the user play with the top row. - */ - rect.height = ALIGN(rect.height, 2); - - /* Datasheet requirement: see register description */ - rect.width = ALIGN(rect.width, 2); - rect.left = ALIGN(rect.left, 2); - - soc_camera_limit_side(&rect.left, &rect.width, - MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); - - mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank; - - /* Blanking and start values - default... */ - ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); - if (!ret) - ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank); - - /* - * The caller provides a supported format, as verified per - * call to .set_fmt(FORMAT_TRY). - */ - if (!ret) - ret = reg_write(client, MT9M001_COLUMN_START, rect.left); - if (!ret) - ret = reg_write(client, MT9M001_ROW_START, rect.top); - if (!ret) - ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1); - if (!ret) - ret = reg_write(client, MT9M001_WINDOW_HEIGHT, - rect.height + mt9m001->y_skip_top - 1); - if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO) - ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h); - - if (!ret) - mt9m001->rect = rect; - - return ret; -} - -static int mt9m001_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = MT9M001_COLUMN_SKIP; - sel->r.top = MT9M001_ROW_SKIP; - sel->r.width = MT9M001_MAX_WIDTH; - sel->r.height = MT9M001_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = mt9m001->rect; - return 0; - default: - return -EINVAL; - } -} - -static int mt9m001_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - struct v4l2_mbus_framefmt *mf = &format->format; - - if (format->pad) - return -EINVAL; - - mf->width = mt9m001->rect.width; - mf->height = mt9m001->rect.height; - mf->code = mt9m001->fmt->code; - mf->colorspace = mt9m001->fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int mt9m001_s_fmt(struct v4l2_subdev *sd, - const struct mt9m001_datafmt *fmt, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - struct v4l2_subdev_selection sel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP, - .r.left = mt9m001->rect.left, - .r.top = mt9m001->rect.top, - .r.width = mf->width, - .r.height = mf->height, - }; - int ret; - - /* No support for scaling so far, just crop. TODO: use skipping */ - ret = mt9m001_set_selection(sd, NULL, &sel); - if (!ret) { - mf->width = mt9m001->rect.width; - mf->height = mt9m001->rect.height; - mt9m001->fmt = fmt; - mf->colorspace = fmt->colorspace; - } - - return ret; -} - -static int mt9m001_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - const struct mt9m001_datafmt *fmt; - - if (format->pad) - return -EINVAL; - - v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH, - MT9M001_MAX_WIDTH, 1, - &mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top, - MT9M001_MAX_HEIGHT + mt9m001->y_skip_top, 0, 0); - - if (mt9m001->fmts == mt9m001_colour_fmts) - mf->height = ALIGN(mf->height - 1, 2); - - fmt = mt9m001_find_datafmt(mf->code, mt9m001->fmts, - mt9m001->num_fmts); - if (!fmt) { - fmt = mt9m001->fmt; - mf->code = fmt->code; - } - - mf->colorspace = fmt->colorspace; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return mt9m001_s_fmt(sd, fmt, mf); - cfg->try_fmt = *mf; - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9m001_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 2; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int mt9m001_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int mt9m001_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9m001 *mt9m001 = to_mt9m001(client); - - return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on); -} - -static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9m001 *mt9m001 = container_of(ctrl->handler, - struct mt9m001, hdl); - s32 min, max; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE_AUTO: - min = mt9m001->exposure->minimum; - max = mt9m001->exposure->maximum; - mt9m001->exposure->val = - (524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min; - break; - } - return 0; -} - -static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9m001 *mt9m001 = container_of(ctrl->handler, - struct mt9m001, hdl); - struct v4l2_subdev *sd = &mt9m001->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct v4l2_ctrl *exp = mt9m001->exposure; - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000); - else - data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000); - if (data < 0) - return -EIO; - return 0; - - case V4L2_CID_GAIN: - /* See Datasheet Table 7, Gain settings. */ - if (ctrl->val <= ctrl->default_value) { - /* Pack it into 0..1 step 0.125, register values 0..8 */ - unsigned long range = ctrl->default_value - ctrl->minimum; - data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; - - dev_dbg(&client->dev, "Setting gain %d\n", data); - data = reg_write(client, MT9M001_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } else { - /* Pack it into 1.125..15 variable step, register values 9..67 */ - /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ - unsigned long range = ctrl->maximum - ctrl->default_value - 1; - unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * - 111 + range / 2) / range + 9; - - if (gain <= 32) - data = gain; - else if (gain <= 64) - data = ((gain - 32) * 16 + 16) / 32 + 80; - else - data = ((gain - 64) * 7 + 28) / 56 + 96; - - dev_dbg(&client->dev, "Setting gain from %d to %d\n", - reg_read(client, MT9M001_GLOBAL_GAIN), data); - data = reg_write(client, MT9M001_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } - return 0; - - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_MANUAL) { - unsigned long range = exp->maximum - exp->minimum; - unsigned long shutter = ((exp->val - (s32)exp->minimum) * 1048 + - range / 2) / range + 1; - - dev_dbg(&client->dev, - "Setting shutter width from %d to %lu\n", - reg_read(client, MT9M001_SHUTTER_WIDTH), shutter); - if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0) - return -EIO; - } else { - const u16 vblank = 25; - - mt9m001->total_h = mt9m001->rect.height + - mt9m001->y_skip_top + vblank; - if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0) - return -EIO; - } - return 0; - } - return -EINVAL; -} - -/* - * Interface active, can use i2c. If it fails, it can indeed mean, that - * this wasn't our capture interface, so, we wait for the right one - */ -static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, - struct i2c_client *client) -{ - struct mt9m001 *mt9m001 = to_mt9m001(client); - s32 data; - unsigned long flags; - int ret; - - ret = mt9m001_s_power(&mt9m001->subdev, 1); - if (ret < 0) - return ret; - - /* Enable the chip */ - data = reg_write(client, MT9M001_CHIP_ENABLE, 1); - dev_dbg(&client->dev, "write: %d\n", data); - - /* Read out the chip version register */ - data = reg_read(client, MT9M001_CHIP_VERSION); - - /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ - switch (data) { - case 0x8411: - case 0x8421: - mt9m001->fmts = mt9m001_colour_fmts; - break; - case 0x8431: - mt9m001->fmts = mt9m001_monochrome_fmts; - break; - default: - dev_err(&client->dev, - "No MT9M001 chip detected, register read %x\n", data); - ret = -ENODEV; - goto done; - } - - mt9m001->num_fmts = 0; - - /* - * This is a 10bit sensor, so by default we only allow 10bit. - * The platform may support different bus widths due to - * different routing of the data lines. - */ - if (ssdd->query_bus_param) - flags = ssdd->query_bus_param(ssdd); - else - flags = SOCAM_DATAWIDTH_10; - - if (flags & SOCAM_DATAWIDTH_10) - mt9m001->num_fmts++; - else - mt9m001->fmts++; - - if (flags & SOCAM_DATAWIDTH_8) - mt9m001->num_fmts++; - - mt9m001->fmt = &mt9m001->fmts[0]; - - dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, - data == 0x8431 ? "C12STM" : "C12ST"); - - ret = mt9m001_init(client); - if (ret < 0) { - dev_err(&client->dev, "Failed to initialise the camera\n"); - goto done; - } - - /* mt9m001_init() has reset the chip, returning registers to defaults */ - ret = v4l2_ctrl_handler_setup(&mt9m001->hdl); - -done: - mt9m001_s_power(&mt9m001->subdev, 0); - return ret; -} - -static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd) -{ - if (ssdd->free_bus) - ssdd->free_bus(ssdd); -} - -static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - - *lines = mt9m001->y_skip_top; - - return 0; -} - -static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = { - .g_volatile_ctrl = mt9m001_g_volatile_ctrl, - .s_ctrl = mt9m001_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9m001_g_register, - .s_register = mt9m001_s_register, -#endif - .s_power = mt9m001_s_power, -}; - -static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m001 *mt9m001 = to_mt9m001(client); - - if (code->pad || code->index >= mt9m001->num_fmts) - return -EINVAL; - - code->code = mt9m001->fmts[code->index].code; - return 0; -} - -static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - /* MT9M001 has all capture_format parameters fixed */ - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - const struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9m001 *mt9m001 = to_mt9m001(client); - unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; - - if (ssdd->set_bus_param) - return ssdd->set_bus_param(ssdd, 1 << (bps - 1)); - - /* - * Without board specific bus width settings we only support the - * sensors native bus width - */ - return bps == 10 ? 0 : -EINVAL; -} - -static const struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { - .s_stream = mt9m001_s_stream, - .g_mbus_config = mt9m001_g_mbus_config, - .s_mbus_config = mt9m001_s_mbus_config, -}; - -static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { - .g_skip_top_lines = mt9m001_g_skip_top_lines, -}; - -static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { - .enum_mbus_code = mt9m001_enum_mbus_code, - .get_selection = mt9m001_get_selection, - .set_selection = mt9m001_set_selection, - .get_fmt = mt9m001_get_fmt, - .set_fmt = mt9m001_set_fmt, -}; - -static const struct v4l2_subdev_ops mt9m001_subdev_ops = { - .core = &mt9m001_subdev_core_ops, - .video = &mt9m001_subdev_video_ops, - .sensor = &mt9m001_subdev_sensor_ops, - .pad = &mt9m001_subdev_pad_ops, -}; - -static int mt9m001_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9m001 *mt9m001; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "MT9M001 driver needs platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL); - if (!mt9m001) - return -ENOMEM; - - v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); - v4l2_ctrl_handler_init(&mt9m001->hdl, 4); - v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 255, 1, 255); - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl, - &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, - V4L2_EXPOSURE_AUTO); - mt9m001->subdev.ctrl_handler = &mt9m001->hdl; - if (mt9m001->hdl.error) - return mt9m001->hdl.error; - - v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - - /* Second stage probe - when a capture adapter is there */ - mt9m001->y_skip_top = 0; - mt9m001->rect.left = MT9M001_COLUMN_SKIP; - mt9m001->rect.top = MT9M001_ROW_SKIP; - mt9m001->rect.width = MT9M001_MAX_WIDTH; - mt9m001->rect.height = MT9M001_MAX_HEIGHT; - - mt9m001->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9m001->clk)) { - ret = PTR_ERR(mt9m001->clk); - goto eclkget; - } - - ret = mt9m001_video_probe(ssdd, client); - if (ret) { - v4l2_clk_put(mt9m001->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9m001->hdl); - } - - return ret; -} - -static int mt9m001_remove(struct i2c_client *client) -{ - struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - v4l2_clk_put(mt9m001->clk); - v4l2_device_unregister_subdev(&mt9m001->subdev); - v4l2_ctrl_handler_free(&mt9m001->hdl); - mt9m001_video_remove(ssdd); - - return 0; -} - -static const struct i2c_device_id mt9m001_id[] = { - { "mt9m001", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9m001_id); - -static struct i2c_driver mt9m001_i2c_driver = { - .driver = { - .name = "mt9m001", - }, - .probe = mt9m001_probe, - .remove = mt9m001_remove, - .id_table = mt9m001_id, -}; - -module_i2c_driver(mt9m001_i2c_driver); - -MODULE_DESCRIPTION("Micron MT9M001 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 3c32db82df60914271d87bf5f3dd5d5bc43ebf55 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 4 Feb 2019 05:18:17 -0500 Subject: media: soc_camera: Remove the rj45n1 SoC camera sensor driver There is a V4L2 sub-device sensor driver for the rj45n1. As there is already another driver, remove the SoC camera one. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/Kconfig | 6 - drivers/media/i2c/soc_camera/Makefile | 1 - drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c | 1415 ------------------------- 3 files changed, 1422 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 5dcb93c0a902..bcd9ef86f40b 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -27,9 +27,3 @@ config SOC_CAMERA_OV9740 depends on SOC_CAMERA && I2C help This is a ov9740 camera driver - -config SOC_CAMERA_RJ54N1 - tristate "rj54n1cb0c support" - depends on SOC_CAMERA && I2C - help - This is a rj54n1cb0c video driver diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index a215d8b095d9..6d63eb31c3b7 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -2,4 +2,3 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o -obj-$(CONFIG_SOC_CAMERA_RJ54N1) += soc_rj54n1cb0c.o diff --git a/drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c b/drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c deleted file mode 100644 index f0cb49a6167b..000000000000 --- a/drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c +++ /dev/null @@ -1,1415 +0,0 @@ -/* - * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp - * - * Copyright (C) 2009, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define RJ54N1_DEV_CODE 0x0400 -#define RJ54N1_DEV_CODE2 0x0401 -#define RJ54N1_OUT_SEL 0x0403 -#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404 -#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405 -#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406 -#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407 -#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408 -#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409 -#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a -#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b -#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c -#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d -#define RJ54N1_RESIZE_N 0x040e -#define RJ54N1_RESIZE_N_STEP 0x040f -#define RJ54N1_RESIZE_STEP 0x0410 -#define RJ54N1_RESIZE_HOLD_H 0x0411 -#define RJ54N1_RESIZE_HOLD_L 0x0412 -#define RJ54N1_H_OBEN_OFS 0x0413 -#define RJ54N1_V_OBEN_OFS 0x0414 -#define RJ54N1_RESIZE_CONTROL 0x0415 -#define RJ54N1_STILL_CONTROL 0x0417 -#define RJ54N1_INC_USE_SEL_H 0x0425 -#define RJ54N1_INC_USE_SEL_L 0x0426 -#define RJ54N1_MIRROR_STILL_MODE 0x0427 -#define RJ54N1_INIT_START 0x0428 -#define RJ54N1_SCALE_1_2_LEV 0x0429 -#define RJ54N1_SCALE_4_LEV 0x042a -#define RJ54N1_Y_GAIN 0x04d8 -#define RJ54N1_APT_GAIN_UP 0x04fa -#define RJ54N1_RA_SEL_UL 0x0530 -#define RJ54N1_BYTE_SWAP 0x0531 -#define RJ54N1_OUT_SIGPO 0x053b -#define RJ54N1_WB_SEL_WEIGHT_I 0x054e -#define RJ54N1_BIT8_WB 0x0569 -#define RJ54N1_HCAPS_WB 0x056a -#define RJ54N1_VCAPS_WB 0x056b -#define RJ54N1_HCAPE_WB 0x056c -#define RJ54N1_VCAPE_WB 0x056d -#define RJ54N1_EXPOSURE_CONTROL 0x058c -#define RJ54N1_FRAME_LENGTH_S_H 0x0595 -#define RJ54N1_FRAME_LENGTH_S_L 0x0596 -#define RJ54N1_FRAME_LENGTH_P_H 0x0597 -#define RJ54N1_FRAME_LENGTH_P_L 0x0598 -#define RJ54N1_PEAK_H 0x05b7 -#define RJ54N1_PEAK_50 0x05b8 -#define RJ54N1_PEAK_60 0x05b9 -#define RJ54N1_PEAK_DIFF 0x05ba -#define RJ54N1_IOC 0x05ef -#define RJ54N1_TG_BYPASS 0x0700 -#define RJ54N1_PLL_L 0x0701 -#define RJ54N1_PLL_N 0x0702 -#define RJ54N1_PLL_EN 0x0704 -#define RJ54N1_RATIO_TG 0x0706 -#define RJ54N1_RATIO_T 0x0707 -#define RJ54N1_RATIO_R 0x0708 -#define RJ54N1_RAMP_TGCLK_EN 0x0709 -#define RJ54N1_OCLK_DSP 0x0710 -#define RJ54N1_RATIO_OP 0x0711 -#define RJ54N1_RATIO_O 0x0712 -#define RJ54N1_OCLK_SEL_EN 0x0713 -#define RJ54N1_CLK_RST 0x0717 -#define RJ54N1_RESET_STANDBY 0x0718 -#define RJ54N1_FWFLG 0x07fe - -#define E_EXCLK (1 << 7) -#define SOFT_STDBY (1 << 4) -#define SEN_RSTX (1 << 2) -#define TG_RSTX (1 << 1) -#define DSP_RSTX (1 << 0) - -#define RESIZE_HOLD_SEL (1 << 2) -#define RESIZE_GO (1 << 1) - -/* - * When cropping, the camera automatically centers the cropped region, there - * doesn't seem to be a way to specify an explicit location of the rectangle. - */ -#define RJ54N1_COLUMN_SKIP 0 -#define RJ54N1_ROW_SKIP 0 -#define RJ54N1_MAX_WIDTH 1600 -#define RJ54N1_MAX_HEIGHT 1200 - -#define PLL_L 2 -#define PLL_N 0x31 - -/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */ - -/* RJ54N1CB0C has only one fixed colorspace per pixelcode */ -struct rj54n1_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -/* Find a data format by a pixel code in an array */ -static const struct rj54n1_datafmt *rj54n1_find_datafmt( - u32 code, const struct rj54n1_datafmt *fmt, - int n) -{ - int i; - for (i = 0; i < n; i++) - if (fmt[i].code == code) - return fmt + i; - - return NULL; -} - -static const struct rj54n1_datafmt rj54n1_colour_fmts[] = { - {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, -}; - -struct rj54n1_clock_div { - u8 ratio_tg; /* can be 0 or an odd number */ - u8 ratio_t; - u8 ratio_r; - u8 ratio_op; - u8 ratio_o; -}; - -struct rj54n1 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; - struct rj54n1_clock_div clk_div; - const struct rj54n1_datafmt *fmt; - struct v4l2_rect rect; /* Sensor window */ - unsigned int tgclk_mhz; - bool auto_wb; - unsigned short width; /* Output window */ - unsigned short height; - unsigned short resize; /* Sensor * 1024 / resize = Output */ - unsigned short scale; - u8 bank; -}; - -struct rj54n1_reg_val { - u16 reg; - u8 val; -}; - -static const struct rj54n1_reg_val bank_4[] = { - {0x417, 0}, - {0x42c, 0}, - {0x42d, 0xf0}, - {0x42e, 0}, - {0x42f, 0x50}, - {0x430, 0xf5}, - {0x431, 0x16}, - {0x432, 0x20}, - {0x433, 0}, - {0x434, 0xc8}, - {0x43c, 8}, - {0x43e, 0x90}, - {0x445, 0x83}, - {0x4ba, 0x58}, - {0x4bb, 4}, - {0x4bc, 0x20}, - {0x4db, 4}, - {0x4fe, 2}, -}; - -static const struct rj54n1_reg_val bank_5[] = { - {0x514, 0}, - {0x516, 0}, - {0x518, 0}, - {0x51a, 0}, - {0x51d, 0xff}, - {0x56f, 0x28}, - {0x575, 0x40}, - {0x5bc, 0x48}, - {0x5c1, 6}, - {0x5e5, 0x11}, - {0x5e6, 0x43}, - {0x5e7, 0x33}, - {0x5e8, 0x21}, - {0x5e9, 0x30}, - {0x5ea, 0x0}, - {0x5eb, 0xa5}, - {0x5ec, 0xff}, - {0x5fe, 2}, -}; - -static const struct rj54n1_reg_val bank_7[] = { - {0x70a, 0}, - {0x714, 0xff}, - {0x715, 0xff}, - {0x716, 0x1f}, - {0x7FE, 2}, -}; - -static const struct rj54n1_reg_val bank_8[] = { - {0x800, 0x00}, - {0x801, 0x01}, - {0x802, 0x61}, - {0x805, 0x00}, - {0x806, 0x00}, - {0x807, 0x00}, - {0x808, 0x00}, - {0x809, 0x01}, - {0x80A, 0x61}, - {0x80B, 0x00}, - {0x80C, 0x01}, - {0x80D, 0x00}, - {0x80E, 0x00}, - {0x80F, 0x00}, - {0x810, 0x00}, - {0x811, 0x01}, - {0x812, 0x61}, - {0x813, 0x00}, - {0x814, 0x11}, - {0x815, 0x00}, - {0x816, 0x41}, - {0x817, 0x00}, - {0x818, 0x51}, - {0x819, 0x01}, - {0x81A, 0x1F}, - {0x81B, 0x00}, - {0x81C, 0x01}, - {0x81D, 0x00}, - {0x81E, 0x11}, - {0x81F, 0x00}, - {0x820, 0x41}, - {0x821, 0x00}, - {0x822, 0x51}, - {0x823, 0x00}, - {0x824, 0x00}, - {0x825, 0x00}, - {0x826, 0x47}, - {0x827, 0x01}, - {0x828, 0x4F}, - {0x829, 0x00}, - {0x82A, 0x00}, - {0x82B, 0x00}, - {0x82C, 0x30}, - {0x82D, 0x00}, - {0x82E, 0x40}, - {0x82F, 0x00}, - {0x830, 0xB3}, - {0x831, 0x00}, - {0x832, 0xE3}, - {0x833, 0x00}, - {0x834, 0x00}, - {0x835, 0x00}, - {0x836, 0x00}, - {0x837, 0x00}, - {0x838, 0x00}, - {0x839, 0x01}, - {0x83A, 0x61}, - {0x83B, 0x00}, - {0x83C, 0x01}, - {0x83D, 0x00}, - {0x83E, 0x00}, - {0x83F, 0x00}, - {0x840, 0x00}, - {0x841, 0x01}, - {0x842, 0x61}, - {0x843, 0x00}, - {0x844, 0x1D}, - {0x845, 0x00}, - {0x846, 0x00}, - {0x847, 0x00}, - {0x848, 0x00}, - {0x849, 0x01}, - {0x84A, 0x1F}, - {0x84B, 0x00}, - {0x84C, 0x05}, - {0x84D, 0x00}, - {0x84E, 0x19}, - {0x84F, 0x01}, - {0x850, 0x21}, - {0x851, 0x01}, - {0x852, 0x5D}, - {0x853, 0x00}, - {0x854, 0x00}, - {0x855, 0x00}, - {0x856, 0x19}, - {0x857, 0x01}, - {0x858, 0x21}, - {0x859, 0x00}, - {0x85A, 0x00}, - {0x85B, 0x00}, - {0x85C, 0x00}, - {0x85D, 0x00}, - {0x85E, 0x00}, - {0x85F, 0x00}, - {0x860, 0xB3}, - {0x861, 0x00}, - {0x862, 0xE3}, - {0x863, 0x00}, - {0x864, 0x00}, - {0x865, 0x00}, - {0x866, 0x00}, - {0x867, 0x00}, - {0x868, 0x00}, - {0x869, 0xE2}, - {0x86A, 0x00}, - {0x86B, 0x01}, - {0x86C, 0x06}, - {0x86D, 0x00}, - {0x86E, 0x00}, - {0x86F, 0x00}, - {0x870, 0x60}, - {0x871, 0x8C}, - {0x872, 0x10}, - {0x873, 0x00}, - {0x874, 0xE0}, - {0x875, 0x00}, - {0x876, 0x27}, - {0x877, 0x01}, - {0x878, 0x00}, - {0x879, 0x00}, - {0x87A, 0x00}, - {0x87B, 0x03}, - {0x87C, 0x00}, - {0x87D, 0x00}, - {0x87E, 0x00}, - {0x87F, 0x00}, - {0x880, 0x00}, - {0x881, 0x00}, - {0x882, 0x00}, - {0x883, 0x00}, - {0x884, 0x00}, - {0x885, 0x00}, - {0x886, 0xF8}, - {0x887, 0x00}, - {0x888, 0x03}, - {0x889, 0x00}, - {0x88A, 0x64}, - {0x88B, 0x00}, - {0x88C, 0x03}, - {0x88D, 0x00}, - {0x88E, 0xB1}, - {0x88F, 0x00}, - {0x890, 0x03}, - {0x891, 0x01}, - {0x892, 0x1D}, - {0x893, 0x00}, - {0x894, 0x03}, - {0x895, 0x01}, - {0x896, 0x4B}, - {0x897, 0x00}, - {0x898, 0xE5}, - {0x899, 0x00}, - {0x89A, 0x01}, - {0x89B, 0x00}, - {0x89C, 0x01}, - {0x89D, 0x04}, - {0x89E, 0xC8}, - {0x89F, 0x00}, - {0x8A0, 0x01}, - {0x8A1, 0x01}, - {0x8A2, 0x61}, - {0x8A3, 0x00}, - {0x8A4, 0x01}, - {0x8A5, 0x00}, - {0x8A6, 0x00}, - {0x8A7, 0x00}, - {0x8A8, 0x00}, - {0x8A9, 0x00}, - {0x8AA, 0x7F}, - {0x8AB, 0x03}, - {0x8AC, 0x00}, - {0x8AD, 0x00}, - {0x8AE, 0x00}, - {0x8AF, 0x00}, - {0x8B0, 0x00}, - {0x8B1, 0x00}, - {0x8B6, 0x00}, - {0x8B7, 0x01}, - {0x8B8, 0x00}, - {0x8B9, 0x00}, - {0x8BA, 0x02}, - {0x8BB, 0x00}, - {0x8BC, 0xFF}, - {0x8BD, 0x00}, - {0x8FE, 2}, -}; - -static const struct rj54n1_reg_val bank_10[] = { - {0x10bf, 0x69} -}; - -/* Clock dividers - these are default register values, divider = register + 1 */ -static const struct rj54n1_clock_div clk_div = { - .ratio_tg = 3 /* default: 5 */, - .ratio_t = 4 /* default: 1 */, - .ratio_r = 4 /* default: 0 */, - .ratio_op = 1 /* default: 5 */, - .ratio_o = 9 /* default: 0 */, -}; - -static struct rj54n1 *to_rj54n1(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct rj54n1, subdev); -} - -static int reg_read(struct i2c_client *client, const u16 reg) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - int ret; - - /* set bank */ - if (rj54n1->bank != reg >> 8) { - dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); - ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); - if (ret < 0) - return ret; - rj54n1->bank = reg >> 8; - } - return i2c_smbus_read_byte_data(client, reg & 0xff); -} - -static int reg_write(struct i2c_client *client, const u16 reg, - const u8 data) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - int ret; - - /* set bank */ - if (rj54n1->bank != reg >> 8) { - dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); - ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); - if (ret < 0) - return ret; - rj54n1->bank = reg >> 8; - } - dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data); - return i2c_smbus_write_byte_data(client, reg & 0xff, data); -} - -static int reg_set(struct i2c_client *client, const u16 reg, - const u8 data, const u8 mask) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, (ret & ~mask) | (data & mask)); -} - -static int reg_write_multiple(struct i2c_client *client, - const struct rj54n1_reg_val *rv, const int n) -{ - int i, ret; - - for (i = 0; i < n; i++) { - ret = reg_write(client, rv->reg, rv->val); - if (ret < 0) - return ret; - rv++; - } - - return 0; -} - -static int rj54n1_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(rj54n1_colour_fmts)) - return -EINVAL; - - code->code = rj54n1_colour_fmts[code->index].code; - return 0; -} - -static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* Switch between preview and still shot modes */ - return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80); -} - -static int rj54n1_set_rect(struct i2c_client *client, - u16 reg_x, u16 reg_y, u16 reg_xy, - u32 width, u32 height) -{ - int ret; - - ret = reg_write(client, reg_xy, - ((width >> 4) & 0x70) | - ((height >> 8) & 7)); - - if (!ret) - ret = reg_write(client, reg_x, width & 0xff); - if (!ret) - ret = reg_write(client, reg_y, height & 0xff); - - return ret; -} - -/* - * Some commands, specifically certain initialisation sequences, require - * a commit operation. - */ -static int rj54n1_commit(struct i2c_client *client) -{ - int ret = reg_write(client, RJ54N1_INIT_START, 1); - msleep(10); - if (!ret) - ret = reg_write(client, RJ54N1_INIT_START, 0); - return ret; -} - -static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, - s32 *out_w, s32 *out_h); - -static int rj54n1_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - const struct v4l2_rect *rect = &sel->r; - int dummy = 0, output_w, output_h, - input_w = rect->width, input_h = rect->height; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - /* arbitrary minimum width and height, edges unimportant */ - soc_camera_limit_side(&dummy, &input_w, - RJ54N1_COLUMN_SKIP, 8, RJ54N1_MAX_WIDTH); - - soc_camera_limit_side(&dummy, &input_h, - RJ54N1_ROW_SKIP, 8, RJ54N1_MAX_HEIGHT); - - output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize; - output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize; - - dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n", - input_w, input_h, rj54n1->resize, output_w, output_h); - - ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); - if (ret < 0) - return ret; - - rj54n1->width = output_w; - rj54n1->height = output_h; - rj54n1->resize = ret; - rj54n1->rect.width = input_w; - rj54n1->rect.height = input_h; - - return 0; -} - -static int rj54n1_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = RJ54N1_COLUMN_SKIP; - sel->r.top = RJ54N1_ROW_SKIP; - sel->r.width = RJ54N1_MAX_WIDTH; - sel->r.height = RJ54N1_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = rj54n1->rect; - return 0; - default: - return -EINVAL; - } -} - -static int rj54n1_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - - if (format->pad) - return -EINVAL; - - mf->code = rj54n1->fmt->code; - mf->colorspace = rj54n1->fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - mf->width = rj54n1->width; - mf->height = rj54n1->height; - - return 0; -} - -/* - * The actual geometry configuration routine. It scales the input window into - * the output one, updates the window sizes and returns an error or the resize - * coefficient on success. Note: we only use the "Fixed Scaling" on this camera. - */ -static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, - s32 *out_w, s32 *out_h) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - unsigned int skip, resize, input_w = *in_w, input_h = *in_h, - output_w = *out_w, output_h = *out_h; - u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom; - unsigned int peak, peak_50, peak_60; - int ret; - - /* - * We have a problem with crops, where the window is larger than 512x384 - * and output window is larger than a half of the input one. In this - * case we have to either reduce the input window to equal or below - * 512x384 or the output window to equal or below 1/2 of the input. - */ - if (output_w > max(512U, input_w / 2)) { - if (2 * output_w > RJ54N1_MAX_WIDTH) { - input_w = RJ54N1_MAX_WIDTH; - output_w = RJ54N1_MAX_WIDTH / 2; - } else { - input_w = output_w * 2; - } - - dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n", - input_w, output_w); - } - - if (output_h > max(384U, input_h / 2)) { - if (2 * output_h > RJ54N1_MAX_HEIGHT) { - input_h = RJ54N1_MAX_HEIGHT; - output_h = RJ54N1_MAX_HEIGHT / 2; - } else { - input_h = output_h * 2; - } - - dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n", - input_h, output_h); - } - - /* Idea: use the read mode for snapshots, handle separate geometries */ - ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L, - RJ54N1_Y_OUTPUT_SIZE_S_L, - RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h); - if (!ret) - ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L, - RJ54N1_Y_OUTPUT_SIZE_P_L, - RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h); - - if (ret < 0) - return ret; - - if (output_w > input_w && output_h > input_h) { - input_w = output_w; - input_h = output_h; - - resize = 1024; - } else { - unsigned int resize_x, resize_y; - resize_x = (input_w * 1024 + output_w / 2) / output_w; - resize_y = (input_h * 1024 + output_h / 2) / output_h; - - /* We want max(resize_x, resize_y), check if it still fits */ - if (resize_x > resize_y && - (output_h * resize_x + 512) / 1024 > RJ54N1_MAX_HEIGHT) - resize = (RJ54N1_MAX_HEIGHT * 1024 + output_h / 2) / - output_h; - else if (resize_y > resize_x && - (output_w * resize_y + 512) / 1024 > RJ54N1_MAX_WIDTH) - resize = (RJ54N1_MAX_WIDTH * 1024 + output_w / 2) / - output_w; - else - resize = max(resize_x, resize_y); - - /* Prohibited value ranges */ - switch (resize) { - case 2040 ... 2047: - resize = 2039; - break; - case 4080 ... 4095: - resize = 4079; - break; - case 8160 ... 8191: - resize = 8159; - break; - case 16320 ... 16384: - resize = 16319; - } - } - - /* Set scaling */ - ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff); - if (!ret) - ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8); - - if (ret < 0) - return ret; - - /* - * Configure a skipping bitmask. The sensor will select a skipping value - * among set bits automatically. This is very unclear in the datasheet - * too. I was told, in this register one enables all skipping values, - * that are required for a specific resize, and the camera selects - * automatically, which ones to use. But it is unclear how to identify, - * which cropping values are needed. Secondly, why don't we just set all - * bits and let the camera choose? Would it increase processing time and - * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to - * improve the image quality or stability for larger frames (see comment - * above), but I didn't check the framerate. - */ - skip = min(resize / 1024, 15U); - - inc_sel = 1 << skip; - - if (inc_sel <= 2) - inc_sel = 0xc; - else if (resize & 1023 && skip < 15) - inc_sel |= 1 << (skip + 1); - - ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc); - if (!ret) - ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8); - - if (!rj54n1->auto_wb) { - /* Auto white balance window */ - wb_left = output_w / 16; - wb_right = (3 * output_w / 4 - 3) / 4; - wb_top = output_h / 16; - wb_bottom = (3 * output_h / 4 - 3) / 4; - wb_bit8 = ((wb_left >> 2) & 0x40) | ((wb_top >> 4) & 0x10) | - ((wb_right >> 6) & 4) | ((wb_bottom >> 8) & 1); - - if (!ret) - ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8); - if (!ret) - ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left); - if (!ret) - ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top); - if (!ret) - ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right); - if (!ret) - ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom); - } - - /* Antiflicker */ - peak = 12 * RJ54N1_MAX_WIDTH * (1 << 14) * resize / rj54n1->tgclk_mhz / - 10000; - peak_50 = peak / 6; - peak_60 = peak / 5; - - if (!ret) - ret = reg_write(client, RJ54N1_PEAK_H, - ((peak_50 >> 4) & 0xf0) | (peak_60 >> 8)); - if (!ret) - ret = reg_write(client, RJ54N1_PEAK_50, peak_50); - if (!ret) - ret = reg_write(client, RJ54N1_PEAK_60, peak_60); - if (!ret) - ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150); - - /* Start resizing */ - if (!ret) - ret = reg_write(client, RJ54N1_RESIZE_CONTROL, - RESIZE_HOLD_SEL | RESIZE_GO | 1); - - if (ret < 0) - return ret; - - /* Constant taken from manufacturer's example */ - msleep(230); - - ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1); - if (ret < 0) - return ret; - - *in_w = (output_w * resize + 512) / 1024; - *in_h = (output_h * resize + 512) / 1024; - *out_w = output_w; - *out_h = output_h; - - dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n", - *in_w, *in_h, resize, output_w, output_h, skip); - - return resize; -} - -static int rj54n1_set_clock(struct i2c_client *client) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - int ret; - - /* Enable external clock */ - ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY); - /* Leave stand-by. Note: use this when implementing suspend / resume */ - if (!ret) - ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK); - - if (!ret) - ret = reg_write(client, RJ54N1_PLL_L, PLL_L); - if (!ret) - ret = reg_write(client, RJ54N1_PLL_N, PLL_N); - - /* TGCLK dividers */ - if (!ret) - ret = reg_write(client, RJ54N1_RATIO_TG, - rj54n1->clk_div.ratio_tg); - if (!ret) - ret = reg_write(client, RJ54N1_RATIO_T, - rj54n1->clk_div.ratio_t); - if (!ret) - ret = reg_write(client, RJ54N1_RATIO_R, - rj54n1->clk_div.ratio_r); - - /* Enable TGCLK & RAMP */ - if (!ret) - ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3); - - /* Disable clock output */ - if (!ret) - ret = reg_write(client, RJ54N1_OCLK_DSP, 0); - - /* Set divisors */ - if (!ret) - ret = reg_write(client, RJ54N1_RATIO_OP, - rj54n1->clk_div.ratio_op); - if (!ret) - ret = reg_write(client, RJ54N1_RATIO_O, - rj54n1->clk_div.ratio_o); - - /* Enable OCLK */ - if (!ret) - ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); - - /* Use PLL for Timing Generator, write 2 to reserved bits */ - if (!ret) - ret = reg_write(client, RJ54N1_TG_BYPASS, 2); - - /* Take sensor out of reset */ - if (!ret) - ret = reg_write(client, RJ54N1_RESET_STANDBY, - E_EXCLK | SEN_RSTX); - /* Enable PLL */ - if (!ret) - ret = reg_write(client, RJ54N1_PLL_EN, 1); - - /* Wait for PLL to stabilise */ - msleep(10); - - /* Enable clock to frequency divider */ - if (!ret) - ret = reg_write(client, RJ54N1_CLK_RST, 1); - - if (!ret) - ret = reg_read(client, RJ54N1_CLK_RST); - if (ret != 1) { - dev_err(&client->dev, - "Resetting RJ54N1CB0C clock failed: %d!\n", ret); - return -EIO; - } - - /* Start the PLL */ - ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1); - - /* Enable OCLK */ - if (!ret) - ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); - - return ret; -} - -static int rj54n1_reg_init(struct i2c_client *client) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - int ret = rj54n1_set_clock(client); - - if (!ret) - ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7)); - if (!ret) - ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10)); - - /* Set binning divisors */ - if (!ret) - ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4)); - if (!ret) - ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf); - - /* Switch to fixed resize mode */ - if (!ret) - ret = reg_write(client, RJ54N1_RESIZE_CONTROL, - RESIZE_HOLD_SEL | 1); - - /* Set gain */ - if (!ret) - ret = reg_write(client, RJ54N1_Y_GAIN, 0x84); - - /* - * Mirror the image back: default is upside down and left-to-right... - * Set manual preview / still shot switching - */ - if (!ret) - ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27); - - if (!ret) - ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4)); - - /* Auto exposure area */ - if (!ret) - ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80); - /* Check current auto WB config */ - if (!ret) - ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I); - if (ret >= 0) { - rj54n1->auto_wb = ret & 0x80; - ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5)); - } - if (!ret) - ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8)); - - if (!ret) - ret = reg_write(client, RJ54N1_RESET_STANDBY, - E_EXCLK | DSP_RSTX | SEN_RSTX); - - /* Commit init */ - if (!ret) - ret = rj54n1_commit(client); - - /* Take DSP, TG, sensor out of reset */ - if (!ret) - ret = reg_write(client, RJ54N1_RESET_STANDBY, - E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX); - - /* Start register update? Same register as 0x?FE in many bank_* sets */ - if (!ret) - ret = reg_write(client, RJ54N1_FWFLG, 2); - - /* Constant taken from manufacturer's example */ - msleep(700); - - return ret; -} - -static int rj54n1_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct rj54n1 *rj54n1 = to_rj54n1(client); - const struct rj54n1_datafmt *fmt; - int output_w, output_h, max_w, max_h, - input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; - int align = mf->code == MEDIA_BUS_FMT_SBGGR10_1X10 || - mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE || - mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE || - mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE || - mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE; - int ret; - - if (format->pad) - return -EINVAL; - - dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n", - __func__, mf->code, mf->width, mf->height); - - fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, - ARRAY_SIZE(rj54n1_colour_fmts)); - if (!fmt) { - fmt = rj54n1->fmt; - mf->code = fmt->code; - } - - mf->field = V4L2_FIELD_NONE; - mf->colorspace = fmt->colorspace; - - v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align, - &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; - return 0; - } - - /* - * Verify if the sensor has just been powered on. TODO: replace this - * with proper PM, when a suitable API is available. - */ - ret = reg_read(client, RJ54N1_RESET_STANDBY); - if (ret < 0) - return ret; - - if (!(ret & E_EXCLK)) { - ret = rj54n1_reg_init(client); - if (ret < 0) - return ret; - } - - /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ - switch (mf->code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - ret = reg_write(client, RJ54N1_OUT_SEL, 0); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - ret = reg_write(client, RJ54N1_OUT_SEL, 0); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); - break; - case MEDIA_BUS_FMT_RGB565_2X8_LE: - ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); - break; - case MEDIA_BUS_FMT_RGB565_2X8_BE: - ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); - break; - case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: - ret = reg_write(client, RJ54N1_OUT_SEL, 4); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); - if (!ret) - ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); - break; - case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: - ret = reg_write(client, RJ54N1_OUT_SEL, 4); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); - if (!ret) - ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); - break; - case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: - ret = reg_write(client, RJ54N1_OUT_SEL, 4); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); - if (!ret) - ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); - break; - case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: - ret = reg_write(client, RJ54N1_OUT_SEL, 4); - if (!ret) - ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); - if (!ret) - ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); - break; - case MEDIA_BUS_FMT_SBGGR10_1X10: - ret = reg_write(client, RJ54N1_OUT_SEL, 5); - break; - default: - ret = -EINVAL; - } - - /* Special case: a raw mode with 10 bits of data per clock tick */ - if (!ret) - ret = reg_set(client, RJ54N1_OCLK_SEL_EN, - (mf->code == MEDIA_BUS_FMT_SBGGR10_1X10) << 1, 2); - - if (ret < 0) - return ret; - - /* Supported scales 1:1 >= scale > 1:16 */ - max_w = mf->width * (16 * 1024 - 1) / 1024; - if (input_w > max_w) - input_w = max_w; - max_h = mf->height * (16 * 1024 - 1) / 1024; - if (input_h > max_h) - input_h = max_h; - - output_w = mf->width; - output_h = mf->height; - - ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); - if (ret < 0) - return ret; - - fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, - ARRAY_SIZE(rj54n1_colour_fmts)); - - rj54n1->fmt = fmt; - rj54n1->resize = ret; - rj54n1->rect.width = input_w; - rj54n1->rect.height = input_h; - rj54n1->width = output_w; - rj54n1->height = output_h; - - mf->width = output_w; - mf->height = output_h; - mf->field = V4L2_FIELD_NONE; - mf->colorspace = fmt->colorspace; - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int rj54n1_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg < 0x400 || reg->reg > 0x1fff) - /* Registers > 0x0800 are only available from Sharp support */ - return -EINVAL; - - reg->size = 1; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xff) - return -EIO; - - return 0; -} - -static int rj54n1_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg < 0x400 || reg->reg > 0x1fff) - /* Registers >= 0x0800 are only available from Sharp support */ - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int rj54n1_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct rj54n1 *rj54n1 = to_rj54n1(client); - - return soc_camera_set_power(&client->dev, ssdd, rj54n1->clk, on); -} - -static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl); - struct v4l2_subdev *sd = &rj54n1->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1); - else - data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_HFLIP: - if (ctrl->val) - data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2); - else - data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_GAIN: - if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0) - return -EIO; - return 0; - case V4L2_CID_AUTO_WHITE_BALANCE: - /* Auto WB area - whole image */ - if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7, - 0x80) < 0) - return -EIO; - rj54n1->auto_wb = ctrl->val; - return 0; - } - - return -EINVAL; -} - -static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = { - .s_ctrl = rj54n1_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = rj54n1_g_register, - .s_register = rj54n1_s_register, -#endif - .s_power = rj54n1_s_power, -}; - -static int rj54n1_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ - if (soc_camera_apply_board_flags(ssdd, cfg) & - V4L2_MBUS_PCLK_SAMPLE_RISING) - return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); - else - return reg_write(client, RJ54N1_OUT_SIGPO, 0); -} - -static const struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { - .s_stream = rj54n1_s_stream, - .g_mbus_config = rj54n1_g_mbus_config, - .s_mbus_config = rj54n1_s_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops rj54n1_subdev_pad_ops = { - .enum_mbus_code = rj54n1_enum_mbus_code, - .get_selection = rj54n1_get_selection, - .set_selection = rj54n1_set_selection, - .get_fmt = rj54n1_get_fmt, - .set_fmt = rj54n1_set_fmt, -}; - -static const struct v4l2_subdev_ops rj54n1_subdev_ops = { - .core = &rj54n1_subdev_core_ops, - .video = &rj54n1_subdev_video_ops, - .pad = &rj54n1_subdev_pad_ops, -}; - -/* - * Interface active, can use i2c. If it fails, it can indeed mean, that - * this wasn't our capture interface, so, we wait for the right one - */ -static int rj54n1_video_probe(struct i2c_client *client, - struct rj54n1_pdata *priv) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - int data1, data2; - int ret; - - ret = rj54n1_s_power(&rj54n1->subdev, 1); - if (ret < 0) - return ret; - - /* Read out the chip version register */ - data1 = reg_read(client, RJ54N1_DEV_CODE); - data2 = reg_read(client, RJ54N1_DEV_CODE2); - - if (data1 != 0x51 || data2 != 0x10) { - ret = -ENODEV; - dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n", - data1, data2); - goto done; - } - - /* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */ - ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7); - if (ret < 0) - goto done; - - dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n", - data1, data2); - - ret = v4l2_ctrl_handler_setup(&rj54n1->hdl); - -done: - rj54n1_s_power(&rj54n1->subdev, 0); - return ret; -} - -static int rj54n1_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct rj54n1 *rj54n1; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct rj54n1_pdata *rj54n1_priv; - int ret; - - if (!ssdd || !ssdd->drv_priv) { - dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); - return -EINVAL; - } - - rj54n1_priv = ssdd->drv_priv; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); - return -EIO; - } - - rj54n1 = devm_kzalloc(&client->dev, sizeof(struct rj54n1), GFP_KERNEL); - if (!rj54n1) - return -ENOMEM; - - v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops); - v4l2_ctrl_handler_init(&rj54n1->hdl, 4); - v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 66); - v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, - V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - rj54n1->subdev.ctrl_handler = &rj54n1->hdl; - if (rj54n1->hdl.error) - return rj54n1->hdl.error; - - rj54n1->clk_div = clk_div; - rj54n1->rect.left = RJ54N1_COLUMN_SKIP; - rj54n1->rect.top = RJ54N1_ROW_SKIP; - rj54n1->rect.width = RJ54N1_MAX_WIDTH; - rj54n1->rect.height = RJ54N1_MAX_HEIGHT; - rj54n1->width = RJ54N1_MAX_WIDTH; - rj54n1->height = RJ54N1_MAX_HEIGHT; - rj54n1->fmt = &rj54n1_colour_fmts[0]; - rj54n1->resize = 1024; - rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) / - (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); - - rj54n1->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(rj54n1->clk)) { - ret = PTR_ERR(rj54n1->clk); - goto eclkget; - } - - ret = rj54n1_video_probe(client, rj54n1_priv); - if (ret < 0) { - v4l2_clk_put(rj54n1->clk); -eclkget: - v4l2_ctrl_handler_free(&rj54n1->hdl); - } - - return ret; -} - -static int rj54n1_remove(struct i2c_client *client) -{ - struct rj54n1 *rj54n1 = to_rj54n1(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - v4l2_clk_put(rj54n1->clk); - v4l2_device_unregister_subdev(&rj54n1->subdev); - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - v4l2_ctrl_handler_free(&rj54n1->hdl); - - return 0; -} - -static const struct i2c_device_id rj54n1_id[] = { - { "rj54n1cb0c", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, rj54n1_id); - -static struct i2c_driver rj54n1_i2c_driver = { - .driver = { - .name = "rj54n1cb0c", - }, - .probe = rj54n1_probe, - .remove = rj54n1_remove, - .id_table = rj54n1_id, -}; - -module_i2c_driver(rj54n1_i2c_driver); - -MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 280de94a651945905cb8337626c40025e4cea56d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 7 Feb 2019 08:43:47 -0500 Subject: media: soc_camera: Move to the staging tree The SoC camera framework has no functional drivers left, something that has not changed for years. Move the leftovers to the staging tree. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 8 - drivers/media/i2c/Makefile | 1 - drivers/media/i2c/soc_camera/Kconfig | 29 - drivers/media/i2c/soc_camera/Makefile | 4 - drivers/media/i2c/soc_camera/soc_mt9v022.c | 1012 ---------- drivers/media/i2c/soc_camera/soc_ov5642.c | 1087 ----------- drivers/media/i2c/soc_camera/soc_ov9740.c | 996 ---------- drivers/media/platform/Kconfig | 1 - drivers/media/platform/Makefile | 2 - drivers/media/platform/soc_camera/Kconfig | 8 - drivers/media/platform/soc_camera/Makefile | 1 - drivers/media/platform/soc_camera/soc_camera.c | 2170 ---------------------- drivers/media/platform/soc_camera/soc_mediabus.c | 533 ------ drivers/staging/media/Kconfig | 2 + drivers/staging/media/Makefile | 1 + drivers/staging/media/soc_camera/Kconfig | 37 + drivers/staging/media/soc_camera/Makefile | 5 + drivers/staging/media/soc_camera/soc_camera.c | 2170 ++++++++++++++++++++++ drivers/staging/media/soc_camera/soc_mediabus.c | 533 ++++++ drivers/staging/media/soc_camera/soc_mt9v022.c | 1012 ++++++++++ drivers/staging/media/soc_camera/soc_ov5642.c | 1087 +++++++++++ drivers/staging/media/soc_camera/soc_ov9740.c | 996 ++++++++++ 22 files changed, 5843 insertions(+), 5852 deletions(-) delete mode 100644 drivers/media/i2c/soc_camera/Kconfig delete mode 100644 drivers/media/i2c/soc_camera/Makefile delete mode 100644 drivers/media/i2c/soc_camera/soc_mt9v022.c delete mode 100644 drivers/media/i2c/soc_camera/soc_ov5642.c delete mode 100644 drivers/media/i2c/soc_camera/soc_ov9740.c delete mode 100644 drivers/media/platform/soc_camera/Kconfig delete mode 100644 drivers/media/platform/soc_camera/Makefile delete mode 100644 drivers/media/platform/soc_camera/soc_camera.c delete mode 100644 drivers/media/platform/soc_camera/soc_mediabus.c create mode 100644 drivers/staging/media/soc_camera/Kconfig create mode 100644 drivers/staging/media/soc_camera/Makefile create mode 100644 drivers/staging/media/soc_camera/soc_camera.c create mode 100644 drivers/staging/media/soc_camera/soc_mediabus.c create mode 100644 drivers/staging/media/soc_camera/soc_mt9v022.c create mode 100644 drivers/staging/media/soc_camera/soc_ov5642.c create mode 100644 drivers/staging/media/soc_camera/soc_ov9740.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 19c112cda078..6d32f8dcf83b 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1134,12 +1134,4 @@ config VIDEO_I2C endmenu -menu "Sensors used on soc_camera driver" - -if SOC_CAMERA - source "drivers/media/i2c/soc_camera/Kconfig" -endif - -endmenu - endif diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 2e5e4b0bf7f3..a64fca82e0c4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ obj-$(CONFIG_VIDEO_CX25840) += cx25840/ obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ -obj-y += soc_camera/ obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig deleted file mode 100644 index bcd9ef86f40b..000000000000 --- a/drivers/media/i2c/soc_camera/Kconfig +++ /dev/null @@ -1,29 +0,0 @@ -comment "soc_camera sensor drivers" - -config SOC_CAMERA_MT9M111 - tristate "legacy soc_camera mt9m111, mt9m112 and mt9m131 support" - depends on SOC_CAMERA && I2C - select VIDEO_MT9M111 - help - This driver supports MT9M111, MT9M112 and MT9M131 cameras from - Micron/Aptina. - This is the legacy configuration which shouldn't be used anymore, - while VIDEO_MT9M111 should be used instead. - -config SOC_CAMERA_MT9V022 - tristate "mt9v022 and mt9v024 support" - depends on SOC_CAMERA && I2C - help - This driver supports MT9V022 cameras from Micron - -config SOC_CAMERA_OV5642 - tristate "ov5642 camera support" - depends on SOC_CAMERA && I2C - help - This is a V4L2 camera driver for the OmniVision OV5642 sensor - -config SOC_CAMERA_OV9740 - tristate "ov9740 camera support" - depends on SOC_CAMERA && I2C - help - This is a ov9740 camera driver diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile deleted file mode 100644 index 6d63eb31c3b7..000000000000 --- a/drivers/media/i2c/soc_camera/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o -obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o -obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o diff --git a/drivers/media/i2c/soc_camera/soc_mt9v022.c b/drivers/media/i2c/soc_camera/soc_mt9v022.c deleted file mode 100644 index 6d922b17ea94..000000000000 --- a/drivers/media/i2c/soc_camera/soc_mt9v022.c +++ /dev/null @@ -1,1012 +0,0 @@ -/* - * Driver for MT9V022 CMOS Image Sensor from Micron - * - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* - * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc - */ - -static char *sensor_type; -module_param(sensor_type, charp, S_IRUGO); -MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); - -/* mt9v022 selected register addresses */ -#define MT9V022_CHIP_VERSION 0x00 -#define MT9V022_COLUMN_START 0x01 -#define MT9V022_ROW_START 0x02 -#define MT9V022_WINDOW_HEIGHT 0x03 -#define MT9V022_WINDOW_WIDTH 0x04 -#define MT9V022_HORIZONTAL_BLANKING 0x05 -#define MT9V022_VERTICAL_BLANKING 0x06 -#define MT9V022_CHIP_CONTROL 0x07 -#define MT9V022_SHUTTER_WIDTH1 0x08 -#define MT9V022_SHUTTER_WIDTH2 0x09 -#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a -#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b -#define MT9V022_RESET 0x0c -#define MT9V022_READ_MODE 0x0d -#define MT9V022_MONITOR_MODE 0x0e -#define MT9V022_PIXEL_OPERATION_MODE 0x0f -#define MT9V022_LED_OUT_CONTROL 0x1b -#define MT9V022_ADC_MODE_CONTROL 0x1c -#define MT9V022_REG32 0x20 -#define MT9V022_ANALOG_GAIN 0x35 -#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47 -#define MT9V022_PIXCLK_FV_LV 0x74 -#define MT9V022_DIGITAL_TEST_PATTERN 0x7f -#define MT9V022_AEC_AGC_ENABLE 0xAF -#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD - -/* mt9v024 partial list register addresses changes with respect to mt9v022 */ -#define MT9V024_PIXCLK_FV_LV 0x72 -#define MT9V024_MAX_TOTAL_SHUTTER_WIDTH 0xAD - -/* Progressive scan, master, defaults */ -#define MT9V022_CHIP_CONTROL_DEFAULT 0x188 - -#define MT9V022_MAX_WIDTH 752 -#define MT9V022_MAX_HEIGHT 480 -#define MT9V022_MIN_WIDTH 48 -#define MT9V022_MIN_HEIGHT 32 -#define MT9V022_COLUMN_SKIP 1 -#define MT9V022_ROW_SKIP 4 - -#define MT9V022_HORIZONTAL_BLANKING_MIN 43 -#define MT9V022_HORIZONTAL_BLANKING_MAX 1023 -#define MT9V022_HORIZONTAL_BLANKING_DEF 94 -#define MT9V022_VERTICAL_BLANKING_MIN 2 -#define MT9V022_VERTICAL_BLANKING_MAX 3000 -#define MT9V022_VERTICAL_BLANKING_DEF 45 - -#define is_mt9v022_rev3(id) (id == 0x1313) -#define is_mt9v024(id) (id == 0x1324) - -/* MT9V022 has only one fixed colorspace per pixelcode */ -struct mt9v022_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -/* Find a data format by a pixel code in an array */ -static const struct mt9v022_datafmt *mt9v022_find_datafmt( - u32 code, const struct mt9v022_datafmt *fmt, - int n) -{ - int i; - for (i = 0; i < n; i++) - if (fmt[i].code == code) - return fmt + i; - - return NULL; -} - -static const struct mt9v022_datafmt mt9v022_colour_fmts[] = { - /* - * Order important: first natively supported, - * second supported with a GPIO extender - */ - {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, -}; - -static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = { - /* Order important - see above */ - {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, -}; - -/* only registers with different addresses on different mt9v02x sensors */ -struct mt9v02x_register { - u8 max_total_shutter_width; - u8 pixclk_fv_lv; -}; - -static const struct mt9v02x_register mt9v022_register = { - .max_total_shutter_width = MT9V022_MAX_TOTAL_SHUTTER_WIDTH, - .pixclk_fv_lv = MT9V022_PIXCLK_FV_LV, -}; - -static const struct mt9v02x_register mt9v024_register = { - .max_total_shutter_width = MT9V024_MAX_TOTAL_SHUTTER_WIDTH, - .pixclk_fv_lv = MT9V024_PIXCLK_FV_LV, -}; - -enum mt9v022_model { - MT9V022IX7ATM, - MT9V022IX7ATC, -}; - -struct mt9v022 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/auto-exposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct { - /* gain/auto-gain cluster */ - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *gain; - }; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *vblank; - struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; - const struct mt9v022_datafmt *fmt; - const struct mt9v022_datafmt *fmts; - const struct mt9v02x_register *reg; - int num_fmts; - enum mt9v022_model model; - u16 chip_control; - u16 chip_version; - unsigned short y_skip_top; /* Lines to skip at the top */ -}; - -static struct mt9v022 *to_mt9v022(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct mt9v022, subdev); -} - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret | data); -} - -static int reg_clear(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret & ~data); -} - -static int mt9v022_init(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - int ret; - - /* - * Almost the default mode: master, parallel, simultaneous, and an - * undocumented bit 0x200, which is present in table 7, but not in 8, - * plus snapshot mode to disable scan for now - */ - mt9v022->chip_control |= 0x10; - ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (!ret) - ret = reg_write(client, MT9V022_READ_MODE, 0x300); - - /* All defaults */ - if (!ret) - /* AEC, AGC on */ - ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3); - if (!ret) - ret = reg_write(client, MT9V022_ANALOG_GAIN, 16); - if (!ret) - ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480); - if (!ret) - ret = reg_write(client, mt9v022->reg->max_total_shutter_width, 480); - if (!ret) - /* default - auto */ - ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); - if (!ret) - ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0); - if (!ret) - return v4l2_ctrl_handler_setup(&mt9v022->hdl); - - return ret; -} - -static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (enable) { - /* Switch to master "normal" mode */ - mt9v022->chip_control &= ~0x10; - if (is_mt9v022_rev3(mt9v022->chip_version) || - is_mt9v024(mt9v022->chip_version)) { - /* - * Unset snapshot mode specific settings: clear bit 9 - * and bit 2 in reg. 0x20 when in normal mode. - */ - if (reg_clear(client, MT9V022_REG32, 0x204)) - return -EIO; - } - } else { - /* Switch to snapshot mode */ - mt9v022->chip_control |= 0x10; - if (is_mt9v022_rev3(mt9v022->chip_version) || - is_mt9v024(mt9v022->chip_version)) { - /* - * Required settings for snapshot mode: set bit 9 - * (RST enable) and bit 2 (CR enable) in reg. 0x20 - * See TechNote TN0960 or TN-09-225. - */ - if (reg_set(client, MT9V022_REG32, 0x204)) - return -EIO; - } - } - - if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) - return -EIO; - return 0; -} - -static int mt9v022_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct v4l2_rect rect = sel->r; - int min_row, min_blank; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - /* Bayer format - even size lengths */ - if (mt9v022->fmts == mt9v022_colour_fmts) { - rect.width = ALIGN(rect.width, 2); - rect.height = ALIGN(rect.height, 2); - /* Let the user play with the starting pixel */ - } - - soc_camera_limit_side(&rect.left, &rect.width, - MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT); - - /* Like in example app. Contradicts the datasheet though */ - ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); - if (ret >= 0) { - if (ret & 1) /* Autoexposure */ - ret = reg_write(client, mt9v022->reg->max_total_shutter_width, - rect.height + mt9v022->y_skip_top + 43); - /* - * If autoexposure is off, there is no need to set - * MT9V022_TOTAL_SHUTTER_WIDTH here. Autoexposure can be off - * only if the user has set exposure manually, using the - * V4L2_CID_EXPOSURE_AUTO with the value V4L2_EXPOSURE_MANUAL. - * In this case the register MT9V022_TOTAL_SHUTTER_WIDTH - * already contains the correct value. - */ - } - /* Setup frame format: defaults apart from width and height */ - if (!ret) - ret = reg_write(client, MT9V022_COLUMN_START, rect.left); - if (!ret) - ret = reg_write(client, MT9V022_ROW_START, rect.top); - /* - * mt9v022: min total row time is 660 columns, min blanking is 43 - * mt9v024: min total row time is 690 columns, min blanking is 61 - */ - if (is_mt9v024(mt9v022->chip_version)) { - min_row = 690; - min_blank = 61; - } else { - min_row = 660; - min_blank = 43; - } - if (!ret) - ret = v4l2_ctrl_s_ctrl(mt9v022->hblank, - rect.width > min_row - min_blank ? - min_blank : min_row - rect.width); - if (!ret) - ret = v4l2_ctrl_s_ctrl(mt9v022->vblank, 45); - if (!ret) - ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width); - if (!ret) - ret = reg_write(client, MT9V022_WINDOW_HEIGHT, - rect.height + mt9v022->y_skip_top); - - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height); - - mt9v022->rect = rect; - - return 0; -} - -static int mt9v022_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = MT9V022_COLUMN_SKIP; - sel->r.top = MT9V022_ROW_SKIP; - sel->r.width = MT9V022_MAX_WIDTH; - sel->r.height = MT9V022_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = mt9v022->rect; - return 0; - default: - return -EINVAL; - } -} - -static int mt9v022_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (format->pad) - return -EINVAL; - - mf->width = mt9v022->rect.width; - mf->height = mt9v022->rect.height; - mf->code = mt9v022->fmt->code; - mf->colorspace = mt9v022->fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int mt9v022_s_fmt(struct v4l2_subdev *sd, - const struct mt9v022_datafmt *fmt, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct v4l2_subdev_selection sel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP, - .r.left = mt9v022->rect.left, - .r.top = mt9v022->rect.top, - .r.width = mf->width, - .r.height = mf->height, - }; - int ret; - - /* - * The caller provides a supported format, as verified per call to - * .set_fmt(FORMAT_TRY), datawidth is from our supported format list - */ - switch (mf->code) { - case MEDIA_BUS_FMT_Y8_1X8: - case MEDIA_BUS_FMT_Y10_1X10: - if (mt9v022->model != MT9V022IX7ATM) - return -EINVAL; - break; - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SBGGR10_1X10: - if (mt9v022->model != MT9V022IX7ATC) - return -EINVAL; - break; - default: - return -EINVAL; - } - - /* No support for scaling on this camera, just crop. */ - ret = mt9v022_set_selection(sd, NULL, &sel); - if (!ret) { - mf->width = mt9v022->rect.width; - mf->height = mt9v022->rect.height; - mt9v022->fmt = fmt; - mf->colorspace = fmt->colorspace; - } - - return ret; -} - -static int mt9v022_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - const struct mt9v022_datafmt *fmt; - int align = mf->code == MEDIA_BUS_FMT_SBGGR8_1X8 || - mf->code == MEDIA_BUS_FMT_SBGGR10_1X10; - - if (format->pad) - return -EINVAL; - - v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH, - MT9V022_MAX_WIDTH, align, - &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top, - MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0); - - fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts, - mt9v022->num_fmts); - if (!fmt) { - fmt = mt9v022->fmt; - mf->code = fmt->code; - } - - mf->colorspace = fmt->colorspace; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return mt9v022_s_fmt(sd, fmt, mf); - cfg->try_fmt = *mf; - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9v022_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 2; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int mt9v022_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int mt9v022_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - return soc_camera_set_power(&client->dev, ssdd, mt9v022->clk, on); -} - -static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9v022 *mt9v022 = container_of(ctrl->handler, - struct mt9v022, hdl); - struct v4l2_subdev *sd = &mt9v022->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct v4l2_ctrl *gain = mt9v022->gain; - struct v4l2_ctrl *exp = mt9v022->exposure; - unsigned long range; - int data; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - data = reg_read(client, MT9V022_ANALOG_GAIN); - if (data < 0) - return -EIO; - - range = gain->maximum - gain->minimum; - gain->val = ((data - 16) * range + 24) / 48 + gain->minimum; - return 0; - case V4L2_CID_EXPOSURE_AUTO: - data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); - if (data < 0) - return -EIO; - - range = exp->maximum - exp->minimum; - exp->val = ((data - 1) * range + 239) / 479 + exp->minimum; - return 0; - case V4L2_CID_HBLANK: - data = reg_read(client, MT9V022_HORIZONTAL_BLANKING); - if (data < 0) - return -EIO; - ctrl->val = data; - return 0; - case V4L2_CID_VBLANK: - data = reg_read(client, MT9V022_VERTICAL_BLANKING); - if (data < 0) - return -EIO; - ctrl->val = data; - return 0; - } - return -EINVAL; -} - -static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9v022 *mt9v022 = container_of(ctrl->handler, - struct mt9v022, hdl); - struct v4l2_subdev *sd = &mt9v022->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, MT9V022_READ_MODE, 0x10); - else - data = reg_clear(client, MT9V022_READ_MODE, 0x10); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_HFLIP: - if (ctrl->val) - data = reg_set(client, MT9V022_READ_MODE, 0x20); - else - data = reg_clear(client, MT9V022_READ_MODE, 0x20); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_AUTOGAIN: - if (ctrl->val) { - if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) - return -EIO; - } else { - struct v4l2_ctrl *gain = mt9v022->gain; - /* mt9v022 has minimum == default */ - unsigned long range = gain->maximum - gain->minimum; - /* Valid values 16 to 64, 32 to 64 must be even. */ - unsigned long gain_val = ((gain->val - (s32)gain->minimum) * - 48 + range / 2) / range + 16; - - if (gain_val >= 32) - gain_val &= ~1; - - /* - * The user wants to set gain manually, hope, she - * knows, what she's doing... Switch AGC off. - */ - if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) - return -EIO; - - dev_dbg(&client->dev, "Setting gain from %d to %lu\n", - reg_read(client, MT9V022_ANALOG_GAIN), gain_val); - if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0) - return -EIO; - } - return 0; - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_AUTO) { - data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); - } else { - struct v4l2_ctrl *exp = mt9v022->exposure; - unsigned long range = exp->maximum - exp->minimum; - unsigned long shutter = ((exp->val - (s32)exp->minimum) * - 479 + range / 2) / range + 1; - - /* - * The user wants to set shutter width manually, hope, - * she knows, what she's doing... Switch AEC off. - */ - data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); - if (data < 0) - return -EIO; - dev_dbg(&client->dev, "Shutter width from %d to %lu\n", - reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), - shutter); - if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, - shutter) < 0) - return -EIO; - } - return 0; - case V4L2_CID_HBLANK: - if (reg_write(client, MT9V022_HORIZONTAL_BLANKING, - ctrl->val) < 0) - return -EIO; - return 0; - case V4L2_CID_VBLANK: - if (reg_write(client, MT9V022_VERTICAL_BLANKING, - ctrl->val) < 0) - return -EIO; - return 0; - } - return -EINVAL; -} - -/* - * Interface active, can use i2c. If it fails, it can indeed mean, that - * this wasn't our capture interface, so, we wait for the right one - */ -static int mt9v022_video_probe(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - s32 data; - int ret; - unsigned long flags; - - ret = mt9v022_s_power(&mt9v022->subdev, 1); - if (ret < 0) - return ret; - - /* Read out the chip version register */ - data = reg_read(client, MT9V022_CHIP_VERSION); - - /* must be 0x1311, 0x1313 or 0x1324 */ - if (data != 0x1311 && data != 0x1313 && data != 0x1324) { - ret = -ENODEV; - dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n", - data); - goto ei2c; - } - - mt9v022->chip_version = data; - - mt9v022->reg = is_mt9v024(data) ? &mt9v024_register : - &mt9v022_register; - - /* Soft reset */ - ret = reg_write(client, MT9V022_RESET, 1); - if (ret < 0) - goto ei2c; - /* 15 clock cycles */ - udelay(200); - if (reg_read(client, MT9V022_RESET)) { - dev_err(&client->dev, "Resetting MT9V022 failed!\n"); - if (ret > 0) - ret = -EIO; - goto ei2c; - } - - /* Set monochrome or colour sensor type */ - if (sensor_type && (!strcmp("colour", sensor_type) || - !strcmp("color", sensor_type))) { - ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); - mt9v022->model = MT9V022IX7ATC; - mt9v022->fmts = mt9v022_colour_fmts; - } else { - ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11); - mt9v022->model = MT9V022IX7ATM; - mt9v022->fmts = mt9v022_monochrome_fmts; - } - - if (ret < 0) - goto ei2c; - - mt9v022->num_fmts = 0; - - /* - * This is a 10bit sensor, so by default we only allow 10bit. - * The platform may support different bus widths due to - * different routing of the data lines. - */ - if (ssdd->query_bus_param) - flags = ssdd->query_bus_param(ssdd); - else - flags = SOCAM_DATAWIDTH_10; - - if (flags & SOCAM_DATAWIDTH_10) - mt9v022->num_fmts++; - else - mt9v022->fmts++; - - if (flags & SOCAM_DATAWIDTH_8) - mt9v022->num_fmts++; - - mt9v022->fmt = &mt9v022->fmts[0]; - - dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", - data, mt9v022->model == MT9V022IX7ATM ? - "monochrome" : "colour"); - - ret = mt9v022_init(client); - if (ret < 0) - dev_err(&client->dev, "Failed to initialise the camera\n"); - -ei2c: - mt9v022_s_power(&mt9v022->subdev, 0); - return ret; -} - -static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - *lines = mt9v022->y_skip_top; - - return 0; -} - -static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = { - .g_volatile_ctrl = mt9v022_g_volatile_ctrl, - .s_ctrl = mt9v022_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9v022_g_register, - .s_register = mt9v022_s_register, -#endif - .s_power = mt9v022_s_power, -}; - -static int mt9v022_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (code->pad || code->index >= mt9v022->num_fmts) - return -EINVAL; - - code->code = mt9v022->fmts[code->index].code; - return 0; -} - -static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE | - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9v022 *mt9v022 = to_mt9v022(client); - unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); - unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; - int ret; - u16 pixclk = 0; - - if (ssdd->set_bus_param) { - ret = ssdd->set_bus_param(ssdd, 1 << (bps - 1)); - if (ret) - return ret; - } else if (bps != 10) { - /* - * Without board specific bus width settings we only support the - * sensors native bus width - */ - return -EINVAL; - } - - if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - pixclk |= 0x10; - - if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)) - pixclk |= 0x1; - - if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)) - pixclk |= 0x2; - - ret = reg_write(client, mt9v022->reg->pixclk_fv_lv, pixclk); - if (ret < 0) - return ret; - - if (!(flags & V4L2_MBUS_MASTER)) - mt9v022->chip_control &= ~0x8; - - ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", - pixclk, mt9v022->chip_control); - - return 0; -} - -static const struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { - .s_stream = mt9v022_s_stream, - .g_mbus_config = mt9v022_g_mbus_config, - .s_mbus_config = mt9v022_s_mbus_config, -}; - -static const struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = { - .g_skip_top_lines = mt9v022_g_skip_top_lines, -}; - -static const struct v4l2_subdev_pad_ops mt9v022_subdev_pad_ops = { - .enum_mbus_code = mt9v022_enum_mbus_code, - .get_selection = mt9v022_get_selection, - .set_selection = mt9v022_set_selection, - .get_fmt = mt9v022_get_fmt, - .set_fmt = mt9v022_set_fmt, -}; - -static const struct v4l2_subdev_ops mt9v022_subdev_ops = { - .core = &mt9v022_subdev_core_ops, - .video = &mt9v022_subdev_video_ops, - .sensor = &mt9v022_subdev_sensor_ops, - .pad = &mt9v022_subdev_pad_ops, -}; - -static int mt9v022_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9v022 *mt9v022; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct mt9v022_platform_data *pdata; - int ret; - - if (!ssdd) { - dev_err(&client->dev, "MT9V022 driver needs platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9v022 = devm_kzalloc(&client->dev, sizeof(struct mt9v022), GFP_KERNEL); - if (!mt9v022) - return -ENOMEM; - - pdata = ssdd->drv_priv; - v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); - v4l2_ctrl_handler_init(&mt9v022->hdl, 6); - v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl, - &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, - V4L2_EXPOSURE_AUTO); - mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 255, 1, 255); - - mt9v022->hblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_HBLANK, MT9V022_HORIZONTAL_BLANKING_MIN, - MT9V022_HORIZONTAL_BLANKING_MAX, 1, - MT9V022_HORIZONTAL_BLANKING_DEF); - - mt9v022->vblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_VBLANK, MT9V022_VERTICAL_BLANKING_MIN, - MT9V022_VERTICAL_BLANKING_MAX, 1, - MT9V022_VERTICAL_BLANKING_DEF); - - mt9v022->subdev.ctrl_handler = &mt9v022->hdl; - if (mt9v022->hdl.error) { - int err = mt9v022->hdl.error; - - dev_err(&client->dev, "control initialisation err %d\n", err); - return err; - } - v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true); - - mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; - - /* - * On some platforms the first read out line is corrupted. - * Workaround it by skipping if indicated by platform data. - */ - mt9v022->y_skip_top = pdata ? pdata->y_skip_top : 0; - mt9v022->rect.left = MT9V022_COLUMN_SKIP; - mt9v022->rect.top = MT9V022_ROW_SKIP; - mt9v022->rect.width = MT9V022_MAX_WIDTH; - mt9v022->rect.height = MT9V022_MAX_HEIGHT; - - mt9v022->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9v022->clk)) { - ret = PTR_ERR(mt9v022->clk); - goto eclkget; - } - - ret = mt9v022_video_probe(client); - if (ret) { - v4l2_clk_put(mt9v022->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9v022->hdl); - } - - return ret; -} - -static int mt9v022_remove(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - v4l2_clk_put(mt9v022->clk); - v4l2_device_unregister_subdev(&mt9v022->subdev); - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - v4l2_ctrl_handler_free(&mt9v022->hdl); - - return 0; -} -static const struct i2c_device_id mt9v022_id[] = { - { "mt9v022", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9v022_id); - -static struct i2c_driver mt9v022_i2c_driver = { - .driver = { - .name = "mt9v022", - }, - .probe = mt9v022_probe, - .remove = mt9v022_remove, - .id_table = mt9v022_id, -}; - -module_i2c_driver(mt9v022_i2c_driver); - -MODULE_DESCRIPTION("Micron MT9V022 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/soc_camera/soc_ov5642.c b/drivers/media/i2c/soc_camera/soc_ov5642.c deleted file mode 100644 index 0931898c79dd..000000000000 --- a/drivers/media/i2c/soc_camera/soc_ov5642.c +++ /dev/null @@ -1,1087 +0,0 @@ -/* - * Driver for OV5642 CMOS Image Sensor from Omnivision - * - * Copyright (C) 2011, Bastian Hecht - * - * Based on Sony IMX074 Camera Driver - * Copyright (C) 2010, Guennadi Liakhovetski - * - * Based on Omnivision OV7670 Camera Driver - * Copyright (C) 2006-7 Jonathan Corbet - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* OV5642 registers */ -#define REG_CHIP_ID_HIGH 0x300a -#define REG_CHIP_ID_LOW 0x300b - -#define REG_WINDOW_START_X_HIGH 0x3800 -#define REG_WINDOW_START_X_LOW 0x3801 -#define REG_WINDOW_START_Y_HIGH 0x3802 -#define REG_WINDOW_START_Y_LOW 0x3803 -#define REG_WINDOW_WIDTH_HIGH 0x3804 -#define REG_WINDOW_WIDTH_LOW 0x3805 -#define REG_WINDOW_HEIGHT_HIGH 0x3806 -#define REG_WINDOW_HEIGHT_LOW 0x3807 -#define REG_OUT_WIDTH_HIGH 0x3808 -#define REG_OUT_WIDTH_LOW 0x3809 -#define REG_OUT_HEIGHT_HIGH 0x380a -#define REG_OUT_HEIGHT_LOW 0x380b -#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c -#define REG_OUT_TOTAL_WIDTH_LOW 0x380d -#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e -#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f -#define REG_OUTPUT_FORMAT 0x4300 -#define REG_ISP_CTRL_01 0x5001 -#define REG_AVG_WINDOW_END_X_HIGH 0x5682 -#define REG_AVG_WINDOW_END_X_LOW 0x5683 -#define REG_AVG_WINDOW_END_Y_HIGH 0x5686 -#define REG_AVG_WINDOW_END_Y_LOW 0x5687 - -/* active pixel array size */ -#define OV5642_SENSOR_SIZE_X 2592 -#define OV5642_SENSOR_SIZE_Y 1944 - -/* - * About OV5642 resolution, cropping and binning: - * This sensor supports it all, at least in the feature description. - * Unfortunately, no combination of appropriate registers settings could make - * the chip work the intended way. As it works with predefined register lists, - * some undocumented registers are presumably changed there to achieve their - * goals. - * This driver currently only works for resolutions up to 720 lines with a - * 1:1 scale. Hopefully these restrictions will be removed in the future. - */ -#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X -#define OV5642_MAX_HEIGHT 720 - -/* default sizes */ -#define OV5642_DEFAULT_WIDTH 1280 -#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT - -/* minimum extra blanking */ -#define BLANKING_EXTRA_WIDTH 500 -#define BLANKING_EXTRA_HEIGHT 20 - -/* - * the sensor's autoexposure is buggy when setting total_height low. - * It tries to expose longer than 1 frame period without taking care of it - * and this leads to weird output. So we set 1000 lines as minimum. - */ -#define BLANKING_MIN_HEIGHT 1000 - -struct regval_list { - u16 reg_num; - u8 value; -}; - -static struct regval_list ov5642_default_regs_init[] = { - { 0x3103, 0x93 }, - { 0x3008, 0x82 }, - { 0x3017, 0x7f }, - { 0x3018, 0xfc }, - { 0x3810, 0xc2 }, - { 0x3615, 0xf0 }, - { 0x3000, 0x0 }, - { 0x3001, 0x0 }, - { 0x3002, 0x0 }, - { 0x3003, 0x0 }, - { 0x3004, 0xff }, - { 0x3030, 0x2b }, - { 0x3011, 0x8 }, - { 0x3010, 0x10 }, - { 0x3604, 0x60 }, - { 0x3622, 0x60 }, - { 0x3621, 0x9 }, - { 0x3709, 0x0 }, - { 0x4000, 0x21 }, - { 0x401d, 0x22 }, - { 0x3600, 0x54 }, - { 0x3605, 0x4 }, - { 0x3606, 0x3f }, - { 0x3c01, 0x80 }, - { 0x300d, 0x22 }, - { 0x3623, 0x22 }, - { 0x5000, 0x4f }, - { 0x5020, 0x4 }, - { 0x5181, 0x79 }, - { 0x5182, 0x0 }, - { 0x5185, 0x22 }, - { 0x5197, 0x1 }, - { 0x5500, 0xa }, - { 0x5504, 0x0 }, - { 0x5505, 0x7f }, - { 0x5080, 0x8 }, - { 0x300e, 0x18 }, - { 0x4610, 0x0 }, - { 0x471d, 0x5 }, - { 0x4708, 0x6 }, - { 0x370c, 0xa0 }, - { 0x5687, 0x94 }, - { 0x501f, 0x0 }, - { 0x5000, 0x4f }, - { 0x5001, 0xcf }, - { 0x4300, 0x30 }, - { 0x4300, 0x30 }, - { 0x460b, 0x35 }, - { 0x471d, 0x0 }, - { 0x3002, 0xc }, - { 0x3002, 0x0 }, - { 0x4713, 0x3 }, - { 0x471c, 0x50 }, - { 0x4721, 0x2 }, - { 0x4402, 0x90 }, - { 0x460c, 0x22 }, - { 0x3815, 0x44 }, - { 0x3503, 0x7 }, - { 0x3501, 0x73 }, - { 0x3502, 0x80 }, - { 0x350b, 0x0 }, - { 0x3818, 0xc8 }, - { 0x3824, 0x11 }, - { 0x3a00, 0x78 }, - { 0x3a1a, 0x4 }, - { 0x3a13, 0x30 }, - { 0x3a18, 0x0 }, - { 0x3a19, 0x7c }, - { 0x3a08, 0x12 }, - { 0x3a09, 0xc0 }, - { 0x3a0a, 0xf }, - { 0x3a0b, 0xa0 }, - { 0x350c, 0x7 }, - { 0x350d, 0xd0 }, - { 0x3a0d, 0x8 }, - { 0x3a0e, 0x6 }, - { 0x3500, 0x0 }, - { 0x3501, 0x0 }, - { 0x3502, 0x0 }, - { 0x350a, 0x0 }, - { 0x350b, 0x0 }, - { 0x3503, 0x0 }, - { 0x3a0f, 0x3c }, - { 0x3a10, 0x32 }, - { 0x3a1b, 0x3c }, - { 0x3a1e, 0x32 }, - { 0x3a11, 0x80 }, - { 0x3a1f, 0x20 }, - { 0x3030, 0x2b }, - { 0x3a02, 0x0 }, - { 0x3a03, 0x7d }, - { 0x3a04, 0x0 }, - { 0x3a14, 0x0 }, - { 0x3a15, 0x7d }, - { 0x3a16, 0x0 }, - { 0x3a00, 0x78 }, - { 0x3a08, 0x9 }, - { 0x3a09, 0x60 }, - { 0x3a0a, 0x7 }, - { 0x3a0b, 0xd0 }, - { 0x3a0d, 0x10 }, - { 0x3a0e, 0xd }, - { 0x4407, 0x4 }, - { 0x5193, 0x70 }, - { 0x589b, 0x0 }, - { 0x589a, 0xc0 }, - { 0x401e, 0x20 }, - { 0x4001, 0x42 }, - { 0x401c, 0x6 }, - { 0x3825, 0xac }, - { 0x3827, 0xc }, - { 0x528a, 0x1 }, - { 0x528b, 0x4 }, - { 0x528c, 0x8 }, - { 0x528d, 0x10 }, - { 0x528e, 0x20 }, - { 0x528f, 0x28 }, - { 0x5290, 0x30 }, - { 0x5292, 0x0 }, - { 0x5293, 0x1 }, - { 0x5294, 0x0 }, - { 0x5295, 0x4 }, - { 0x5296, 0x0 }, - { 0x5297, 0x8 }, - { 0x5298, 0x0 }, - { 0x5299, 0x10 }, - { 0x529a, 0x0 }, - { 0x529b, 0x20 }, - { 0x529c, 0x0 }, - { 0x529d, 0x28 }, - { 0x529e, 0x0 }, - { 0x529f, 0x30 }, - { 0x5282, 0x0 }, - { 0x5300, 0x0 }, - { 0x5301, 0x20 }, - { 0x5302, 0x0 }, - { 0x5303, 0x7c }, - { 0x530c, 0x0 }, - { 0x530d, 0xc }, - { 0x530e, 0x20 }, - { 0x530f, 0x80 }, - { 0x5310, 0x20 }, - { 0x5311, 0x80 }, - { 0x5308, 0x20 }, - { 0x5309, 0x40 }, - { 0x5304, 0x0 }, - { 0x5305, 0x30 }, - { 0x5306, 0x0 }, - { 0x5307, 0x80 }, - { 0x5314, 0x8 }, - { 0x5315, 0x20 }, - { 0x5319, 0x30 }, - { 0x5316, 0x10 }, - { 0x5317, 0x0 }, - { 0x5318, 0x2 }, - { 0x5380, 0x1 }, - { 0x5381, 0x0 }, - { 0x5382, 0x0 }, - { 0x5383, 0x4e }, - { 0x5384, 0x0 }, - { 0x5385, 0xf }, - { 0x5386, 0x0 }, - { 0x5387, 0x0 }, - { 0x5388, 0x1 }, - { 0x5389, 0x15 }, - { 0x538a, 0x0 }, - { 0x538b, 0x31 }, - { 0x538c, 0x0 }, - { 0x538d, 0x0 }, - { 0x538e, 0x0 }, - { 0x538f, 0xf }, - { 0x5390, 0x0 }, - { 0x5391, 0xab }, - { 0x5392, 0x0 }, - { 0x5393, 0xa2 }, - { 0x5394, 0x8 }, - { 0x5480, 0x14 }, - { 0x5481, 0x21 }, - { 0x5482, 0x36 }, - { 0x5483, 0x57 }, - { 0x5484, 0x65 }, - { 0x5485, 0x71 }, - { 0x5486, 0x7d }, - { 0x5487, 0x87 }, - { 0x5488, 0x91 }, - { 0x5489, 0x9a }, - { 0x548a, 0xaa }, - { 0x548b, 0xb8 }, - { 0x548c, 0xcd }, - { 0x548d, 0xdd }, - { 0x548e, 0xea }, - { 0x548f, 0x1d }, - { 0x5490, 0x5 }, - { 0x5491, 0x0 }, - { 0x5492, 0x4 }, - { 0x5493, 0x20 }, - { 0x5494, 0x3 }, - { 0x5495, 0x60 }, - { 0x5496, 0x2 }, - { 0x5497, 0xb8 }, - { 0x5498, 0x2 }, - { 0x5499, 0x86 }, - { 0x549a, 0x2 }, - { 0x549b, 0x5b }, - { 0x549c, 0x2 }, - { 0x549d, 0x3b }, - { 0x549e, 0x2 }, - { 0x549f, 0x1c }, - { 0x54a0, 0x2 }, - { 0x54a1, 0x4 }, - { 0x54a2, 0x1 }, - { 0x54a3, 0xed }, - { 0x54a4, 0x1 }, - { 0x54a5, 0xc5 }, - { 0x54a6, 0x1 }, - { 0x54a7, 0xa5 }, - { 0x54a8, 0x1 }, - { 0x54a9, 0x6c }, - { 0x54aa, 0x1 }, - { 0x54ab, 0x41 }, - { 0x54ac, 0x1 }, - { 0x54ad, 0x20 }, - { 0x54ae, 0x0 }, - { 0x54af, 0x16 }, - { 0x54b0, 0x1 }, - { 0x54b1, 0x20 }, - { 0x54b2, 0x0 }, - { 0x54b3, 0x10 }, - { 0x54b4, 0x0 }, - { 0x54b5, 0xf0 }, - { 0x54b6, 0x0 }, - { 0x54b7, 0xdf }, - { 0x5402, 0x3f }, - { 0x5403, 0x0 }, - { 0x3406, 0x0 }, - { 0x5180, 0xff }, - { 0x5181, 0x52 }, - { 0x5182, 0x11 }, - { 0x5183, 0x14 }, - { 0x5184, 0x25 }, - { 0x5185, 0x24 }, - { 0x5186, 0x6 }, - { 0x5187, 0x8 }, - { 0x5188, 0x8 }, - { 0x5189, 0x7c }, - { 0x518a, 0x60 }, - { 0x518b, 0xb2 }, - { 0x518c, 0xb2 }, - { 0x518d, 0x44 }, - { 0x518e, 0x3d }, - { 0x518f, 0x58 }, - { 0x5190, 0x46 }, - { 0x5191, 0xf8 }, - { 0x5192, 0x4 }, - { 0x5193, 0x70 }, - { 0x5194, 0xf0 }, - { 0x5195, 0xf0 }, - { 0x5196, 0x3 }, - { 0x5197, 0x1 }, - { 0x5198, 0x4 }, - { 0x5199, 0x12 }, - { 0x519a, 0x4 }, - { 0x519b, 0x0 }, - { 0x519c, 0x6 }, - { 0x519d, 0x82 }, - { 0x519e, 0x0 }, - { 0x5025, 0x80 }, - { 0x3a0f, 0x38 }, - { 0x3a10, 0x30 }, - { 0x3a1b, 0x3a }, - { 0x3a1e, 0x2e }, - { 0x3a11, 0x60 }, - { 0x3a1f, 0x10 }, - { 0x5688, 0xa6 }, - { 0x5689, 0x6a }, - { 0x568a, 0xea }, - { 0x568b, 0xae }, - { 0x568c, 0xa6 }, - { 0x568d, 0x6a }, - { 0x568e, 0x62 }, - { 0x568f, 0x26 }, - { 0x5583, 0x40 }, - { 0x5584, 0x40 }, - { 0x5580, 0x2 }, - { 0x5000, 0xcf }, - { 0x5800, 0x27 }, - { 0x5801, 0x19 }, - { 0x5802, 0x12 }, - { 0x5803, 0xf }, - { 0x5804, 0x10 }, - { 0x5805, 0x15 }, - { 0x5806, 0x1e }, - { 0x5807, 0x2f }, - { 0x5808, 0x15 }, - { 0x5809, 0xd }, - { 0x580a, 0xa }, - { 0x580b, 0x9 }, - { 0x580c, 0xa }, - { 0x580d, 0xc }, - { 0x580e, 0x12 }, - { 0x580f, 0x19 }, - { 0x5810, 0xb }, - { 0x5811, 0x7 }, - { 0x5812, 0x4 }, - { 0x5813, 0x3 }, - { 0x5814, 0x3 }, - { 0x5815, 0x6 }, - { 0x5816, 0xa }, - { 0x5817, 0xf }, - { 0x5818, 0xa }, - { 0x5819, 0x5 }, - { 0x581a, 0x1 }, - { 0x581b, 0x0 }, - { 0x581c, 0x0 }, - { 0x581d, 0x3 }, - { 0x581e, 0x8 }, - { 0x581f, 0xc }, - { 0x5820, 0xa }, - { 0x5821, 0x5 }, - { 0x5822, 0x1 }, - { 0x5823, 0x0 }, - { 0x5824, 0x0 }, - { 0x5825, 0x3 }, - { 0x5826, 0x8 }, - { 0x5827, 0xc }, - { 0x5828, 0xe }, - { 0x5829, 0x8 }, - { 0x582a, 0x6 }, - { 0x582b, 0x4 }, - { 0x582c, 0x5 }, - { 0x582d, 0x7 }, - { 0x582e, 0xb }, - { 0x582f, 0x12 }, - { 0x5830, 0x18 }, - { 0x5831, 0x10 }, - { 0x5832, 0xc }, - { 0x5833, 0xa }, - { 0x5834, 0xb }, - { 0x5835, 0xe }, - { 0x5836, 0x15 }, - { 0x5837, 0x19 }, - { 0x5838, 0x32 }, - { 0x5839, 0x1f }, - { 0x583a, 0x18 }, - { 0x583b, 0x16 }, - { 0x583c, 0x17 }, - { 0x583d, 0x1e }, - { 0x583e, 0x26 }, - { 0x583f, 0x53 }, - { 0x5840, 0x10 }, - { 0x5841, 0xf }, - { 0x5842, 0xd }, - { 0x5843, 0xc }, - { 0x5844, 0xe }, - { 0x5845, 0x9 }, - { 0x5846, 0x11 }, - { 0x5847, 0x10 }, - { 0x5848, 0x10 }, - { 0x5849, 0x10 }, - { 0x584a, 0x10 }, - { 0x584b, 0xe }, - { 0x584c, 0x10 }, - { 0x584d, 0x10 }, - { 0x584e, 0x11 }, - { 0x584f, 0x10 }, - { 0x5850, 0xf }, - { 0x5851, 0xc }, - { 0x5852, 0xf }, - { 0x5853, 0x10 }, - { 0x5854, 0x10 }, - { 0x5855, 0xf }, - { 0x5856, 0xe }, - { 0x5857, 0xb }, - { 0x5858, 0x10 }, - { 0x5859, 0xd }, - { 0x585a, 0xd }, - { 0x585b, 0xc }, - { 0x585c, 0xc }, - { 0x585d, 0xc }, - { 0x585e, 0xb }, - { 0x585f, 0xc }, - { 0x5860, 0xc }, - { 0x5861, 0xc }, - { 0x5862, 0xd }, - { 0x5863, 0x8 }, - { 0x5864, 0x11 }, - { 0x5865, 0x18 }, - { 0x5866, 0x18 }, - { 0x5867, 0x19 }, - { 0x5868, 0x17 }, - { 0x5869, 0x19 }, - { 0x586a, 0x16 }, - { 0x586b, 0x13 }, - { 0x586c, 0x13 }, - { 0x586d, 0x12 }, - { 0x586e, 0x13 }, - { 0x586f, 0x16 }, - { 0x5870, 0x14 }, - { 0x5871, 0x12 }, - { 0x5872, 0x10 }, - { 0x5873, 0x11 }, - { 0x5874, 0x11 }, - { 0x5875, 0x16 }, - { 0x5876, 0x14 }, - { 0x5877, 0x11 }, - { 0x5878, 0x10 }, - { 0x5879, 0xf }, - { 0x587a, 0x10 }, - { 0x587b, 0x14 }, - { 0x587c, 0x13 }, - { 0x587d, 0x12 }, - { 0x587e, 0x11 }, - { 0x587f, 0x11 }, - { 0x5880, 0x12 }, - { 0x5881, 0x15 }, - { 0x5882, 0x14 }, - { 0x5883, 0x15 }, - { 0x5884, 0x15 }, - { 0x5885, 0x15 }, - { 0x5886, 0x13 }, - { 0x5887, 0x17 }, - { 0x3710, 0x10 }, - { 0x3632, 0x51 }, - { 0x3702, 0x10 }, - { 0x3703, 0xb2 }, - { 0x3704, 0x18 }, - { 0x370b, 0x40 }, - { 0x370d, 0x3 }, - { 0x3631, 0x1 }, - { 0x3632, 0x52 }, - { 0x3606, 0x24 }, - { 0x3620, 0x96 }, - { 0x5785, 0x7 }, - { 0x3a13, 0x30 }, - { 0x3600, 0x52 }, - { 0x3604, 0x48 }, - { 0x3606, 0x1b }, - { 0x370d, 0xb }, - { 0x370f, 0xc0 }, - { 0x3709, 0x1 }, - { 0x3823, 0x0 }, - { 0x5007, 0x0 }, - { 0x5009, 0x0 }, - { 0x5011, 0x0 }, - { 0x5013, 0x0 }, - { 0x519e, 0x0 }, - { 0x5086, 0x0 }, - { 0x5087, 0x0 }, - { 0x5088, 0x0 }, - { 0x5089, 0x0 }, - { 0x302b, 0x0 }, - { 0x3503, 0x7 }, - { 0x3011, 0x8 }, - { 0x350c, 0x2 }, - { 0x350d, 0xe4 }, - { 0x3621, 0xc9 }, - { 0x370a, 0x81 }, - { 0xffff, 0xff }, -}; - -static struct regval_list ov5642_default_regs_finalise[] = { - { 0x3810, 0xc2 }, - { 0x3818, 0xc9 }, - { 0x381c, 0x10 }, - { 0x381d, 0xa0 }, - { 0x381e, 0x5 }, - { 0x381f, 0xb0 }, - { 0x3820, 0x0 }, - { 0x3821, 0x0 }, - { 0x3824, 0x11 }, - { 0x3a08, 0x1b }, - { 0x3a09, 0xc0 }, - { 0x3a0a, 0x17 }, - { 0x3a0b, 0x20 }, - { 0x3a0d, 0x2 }, - { 0x3a0e, 0x1 }, - { 0x401c, 0x4 }, - { 0x5682, 0x5 }, - { 0x5683, 0x0 }, - { 0x5686, 0x2 }, - { 0x5687, 0xcc }, - { 0x5001, 0x4f }, - { 0x589b, 0x6 }, - { 0x589a, 0xc5 }, - { 0x3503, 0x0 }, - { 0x460c, 0x20 }, - { 0x460b, 0x37 }, - { 0x471c, 0xd0 }, - { 0x471d, 0x5 }, - { 0x3815, 0x1 }, - { 0x3818, 0xc1 }, - { 0x501f, 0x0 }, - { 0x5002, 0xe0 }, - { 0x4300, 0x32 }, /* UYVY */ - { 0x3002, 0x1c }, - { 0x4800, 0x14 }, - { 0x4801, 0xf }, - { 0x3007, 0x3b }, - { 0x300e, 0x4 }, - { 0x4803, 0x50 }, - { 0x3815, 0x1 }, - { 0x4713, 0x2 }, - { 0x4842, 0x1 }, - { 0x300f, 0xe }, - { 0x3003, 0x3 }, - { 0x3003, 0x1 }, - { 0xffff, 0xff }, -}; - -struct ov5642_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -struct ov5642 { - struct v4l2_subdev subdev; - const struct ov5642_datafmt *fmt; - struct v4l2_rect crop_rect; - struct v4l2_clk *clk; - - /* blanking information */ - int total_width; - int total_height; -}; - -static const struct ov5642_datafmt ov5642_colour_fmts[] = { - {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, -}; - -static struct ov5642 *to_ov5642(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct ov5642, subdev); -} - -/* Find a data format by a pixel code in an array */ -static const struct ov5642_datafmt - *ov5642_find_datafmt(u32 code) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++) - if (ov5642_colour_fmts[i].code == code) - return ov5642_colour_fmts + i; - - return NULL; -} - -static int reg_read(struct i2c_client *client, u16 reg, u8 *val) -{ - int ret; - /* We have 16-bit i2c addresses - care for endianness */ - unsigned char data[2] = { reg >> 8, reg & 0xff }; - - ret = i2c_master_send(client, data, 2); - if (ret < 2) { - dev_err(&client->dev, "%s: i2c read error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - - ret = i2c_master_recv(client, val, 1); - if (ret < 1) { - dev_err(&client->dev, "%s: i2c read error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - return 0; -} - -static int reg_write(struct i2c_client *client, u16 reg, u8 val) -{ - int ret; - unsigned char data[3] = { reg >> 8, reg & 0xff, val }; - - ret = i2c_master_send(client, data, 3); - if (ret < 3) { - dev_err(&client->dev, "%s: i2c write error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - - return 0; -} - -/* - * convenience function to write 16 bit register values that are split up - * into two consecutive high and low parts - */ -static int reg_write16(struct i2c_client *client, u16 reg, u16 val16) -{ - int ret; - - ret = reg_write(client, reg, val16 >> 8); - if (ret) - return ret; - return reg_write(client, reg + 1, val16 & 0x00ff); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - u8 val; - - if (reg->reg & ~0xffff) - return -EINVAL; - - reg->size = 1; - - ret = reg_read(client, reg->reg, &val); - if (!ret) - reg->val = (__u64)val; - - return ret; -} - -static int ov5642_set_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg & ~0xffff || reg->val & ~0xff) - return -EINVAL; - - return reg_write(client, reg->reg, reg->val); -} -#endif - -static int ov5642_write_array(struct i2c_client *client, - struct regval_list *vals) -{ - while (vals->reg_num != 0xffff || vals->value != 0xff) { - int ret = reg_write(client, vals->reg_num, vals->value); - if (ret < 0) - return ret; - vals++; - } - dev_dbg(&client->dev, "Register list loaded\n"); - return 0; -} - -static int ov5642_set_resolution(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - int width = priv->crop_rect.width; - int height = priv->crop_rect.height; - int total_width = priv->total_width; - int total_height = priv->total_height; - int start_x = (OV5642_SENSOR_SIZE_X - width) / 2; - int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2; - int ret; - - /* - * This should set the starting point for cropping. - * Doesn't work so far. - */ - ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x); - if (!ret) - ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y); - if (!ret) { - priv->crop_rect.left = start_x; - priv->crop_rect.top = start_y; - } - - if (!ret) - ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height); - if (ret) - return ret; - priv->crop_rect.width = width; - priv->crop_rect.height = height; - - /* Set the output window size. Only 1:1 scale is supported so far. */ - ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height); - - /* Total width = output size + blanking */ - if (!ret) - ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width); - if (!ret) - ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height); - - /* Sets the window for AWB calculations */ - if (!ret) - ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height); - - return ret; -} - -static int ov5642_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code); - - if (format->pad) - return -EINVAL; - - mf->width = priv->crop_rect.width; - mf->height = priv->crop_rect.height; - - if (!fmt) { - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - mf->code = ov5642_colour_fmts[0].code; - mf->colorspace = ov5642_colour_fmts[0].colorspace; - } - - mf->field = V4L2_FIELD_NONE; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - priv->fmt = fmt; - else - cfg->try_fmt = *mf; - return 0; -} - -static int ov5642_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - - const struct ov5642_datafmt *fmt = priv->fmt; - - if (format->pad) - return -EINVAL; - - mf->code = fmt->code; - mf->colorspace = fmt->colorspace; - mf->width = priv->crop_rect.width; - mf->height = priv->crop_rect.height; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int ov5642_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(ov5642_colour_fmts)) - return -EINVAL; - - code->code = ov5642_colour_fmts[code->index].code; - return 0; -} - -static int ov5642_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - struct v4l2_rect rect = sel->r; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - v4l_bound_align_image(&rect.width, 48, OV5642_MAX_WIDTH, 1, - &rect.height, 32, OV5642_MAX_HEIGHT, 1, 0); - - priv->crop_rect.width = rect.width; - priv->crop_rect.height = rect.height; - priv->total_width = rect.width + BLANKING_EXTRA_WIDTH; - priv->total_height = max_t(int, rect.height + - BLANKING_EXTRA_HEIGHT, - BLANKING_MIN_HEIGHT); - priv->crop_rect.width = rect.width; - priv->crop_rect.height = rect.height; - - ret = ov5642_write_array(client, ov5642_default_regs_init); - if (!ret) - ret = ov5642_set_resolution(sd); - if (!ret) - ret = ov5642_write_array(client, ov5642_default_regs_finalise); - - return ret; -} - -static int ov5642_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = OV5642_MAX_WIDTH; - sel->r.height = OV5642_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = priv->crop_rect; - return 0; - default: - return -EINVAL; - } -} - -static int ov5642_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - cfg->type = V4L2_MBUS_CSI2_DPHY; - cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - - return 0; -} - -static int ov5642_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov5642 *priv = to_ov5642(client); - int ret; - - if (!on) - return soc_camera_power_off(&client->dev, ssdd, priv->clk); - - ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); - if (ret < 0) - return ret; - - ret = ov5642_write_array(client, ov5642_default_regs_init); - if (!ret) - ret = ov5642_set_resolution(sd); - if (!ret) - ret = ov5642_write_array(client, ov5642_default_regs_finalise); - - return ret; -} - -static const struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { - .g_mbus_config = ov5642_g_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops ov5642_subdev_pad_ops = { - .enum_mbus_code = ov5642_enum_mbus_code, - .get_selection = ov5642_get_selection, - .set_selection = ov5642_set_selection, - .get_fmt = ov5642_get_fmt, - .set_fmt = ov5642_set_fmt, -}; - -static const struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { - .s_power = ov5642_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov5642_get_register, - .s_register = ov5642_set_register, -#endif -}; - -static const struct v4l2_subdev_ops ov5642_subdev_ops = { - .core = &ov5642_subdev_core_ops, - .video = &ov5642_subdev_video_ops, - .pad = &ov5642_subdev_pad_ops, -}; - -static int ov5642_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - int ret; - u8 id_high, id_low; - u16 id; - - ret = ov5642_s_power(subdev, 1); - if (ret < 0) - return ret; - - /* Read sensor Model ID */ - ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high); - if (ret < 0) - goto done; - - id = id_high << 8; - - ret = reg_read(client, REG_CHIP_ID_LOW, &id_low); - if (ret < 0) - goto done; - - id |= id_low; - - dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); - - if (id != 0x5642) { - ret = -ENODEV; - goto done; - } - - ret = 0; - -done: - ov5642_s_power(subdev, 0); - return ret; -} - -static int ov5642_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov5642 *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "OV5642: missing platform data!\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(struct ov5642), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops); - - priv->fmt = &ov5642_colour_fmts[0]; - - priv->crop_rect.width = OV5642_DEFAULT_WIDTH; - priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; - priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; - priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; - priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; - priv->total_height = BLANKING_MIN_HEIGHT; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); - - ret = ov5642_video_probe(client); - if (ret < 0) - v4l2_clk_put(priv->clk); - - return ret; -} - -static int ov5642_remove(struct i2c_client *client) -{ - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov5642 *priv = to_ov5642(client); - - v4l2_clk_put(priv->clk); - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - - return 0; -} - -static const struct i2c_device_id ov5642_id[] = { - { "ov5642", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov5642_id); - -#if IS_ENABLED(CONFIG_OF) -static const struct of_device_id ov5642_of_match[] = { - { .compatible = "ovti,ov5642" }, - { }, -}; -MODULE_DEVICE_TABLE(of, ov5642_of_match); -#endif - -static struct i2c_driver ov5642_i2c_driver = { - .driver = { - .name = "ov5642", - .of_match_table = of_match_ptr(ov5642_of_match), - }, - .probe = ov5642_probe, - .remove = ov5642_remove, - .id_table = ov5642_id, -}; - -module_i2c_driver(ov5642_i2c_driver); - -MODULE_DESCRIPTION("Omnivision OV5642 Camera driver"); -MODULE_AUTHOR("Bastian Hecht "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/soc_camera/soc_ov9740.c b/drivers/media/i2c/soc_camera/soc_ov9740.c deleted file mode 100644 index a07d3145d1b4..000000000000 --- a/drivers/media/i2c/soc_camera/soc_ov9740.c +++ /dev/null @@ -1,996 +0,0 @@ -/* - * OmniVision OV9740 Camera Driver - * - * Copyright (C) 2011 NVIDIA Corporation - * - * Based on ov9640 camera driver. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev) - -/* General Status Registers */ -#define OV9740_MODEL_ID_HI 0x0000 -#define OV9740_MODEL_ID_LO 0x0001 -#define OV9740_REVISION_NUMBER 0x0002 -#define OV9740_MANUFACTURER_ID 0x0003 -#define OV9740_SMIA_VERSION 0x0004 - -/* General Setup Registers */ -#define OV9740_MODE_SELECT 0x0100 -#define OV9740_IMAGE_ORT 0x0101 -#define OV9740_SOFTWARE_RESET 0x0103 -#define OV9740_GRP_PARAM_HOLD 0x0104 -#define OV9740_MSK_CORRUP_FM 0x0105 - -/* Timing Setting */ -#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */ -#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */ -#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */ -#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */ -#define OV9740_X_ADDR_START_HI 0x0344 -#define OV9740_X_ADDR_START_LO 0x0345 -#define OV9740_Y_ADDR_START_HI 0x0346 -#define OV9740_Y_ADDR_START_LO 0x0347 -#define OV9740_X_ADDR_END_HI 0x0348 -#define OV9740_X_ADDR_END_LO 0x0349 -#define OV9740_Y_ADDR_END_HI 0x034a -#define OV9740_Y_ADDR_END_LO 0x034b -#define OV9740_X_OUTPUT_SIZE_HI 0x034c -#define OV9740_X_OUTPUT_SIZE_LO 0x034d -#define OV9740_Y_OUTPUT_SIZE_HI 0x034e -#define OV9740_Y_OUTPUT_SIZE_LO 0x034f - -/* IO Control Registers */ -#define OV9740_IO_CREL00 0x3002 -#define OV9740_IO_CREL01 0x3004 -#define OV9740_IO_CREL02 0x3005 -#define OV9740_IO_OUTPUT_SEL01 0x3026 -#define OV9740_IO_OUTPUT_SEL02 0x3027 - -/* AWB Registers */ -#define OV9740_AWB_MANUAL_CTRL 0x3406 - -/* Analog Control Registers */ -#define OV9740_ANALOG_CTRL01 0x3601 -#define OV9740_ANALOG_CTRL02 0x3602 -#define OV9740_ANALOG_CTRL03 0x3603 -#define OV9740_ANALOG_CTRL04 0x3604 -#define OV9740_ANALOG_CTRL10 0x3610 -#define OV9740_ANALOG_CTRL12 0x3612 -#define OV9740_ANALOG_CTRL15 0x3615 -#define OV9740_ANALOG_CTRL20 0x3620 -#define OV9740_ANALOG_CTRL21 0x3621 -#define OV9740_ANALOG_CTRL22 0x3622 -#define OV9740_ANALOG_CTRL30 0x3630 -#define OV9740_ANALOG_CTRL31 0x3631 -#define OV9740_ANALOG_CTRL32 0x3632 -#define OV9740_ANALOG_CTRL33 0x3633 - -/* Sensor Control */ -#define OV9740_SENSOR_CTRL03 0x3703 -#define OV9740_SENSOR_CTRL04 0x3704 -#define OV9740_SENSOR_CTRL05 0x3705 -#define OV9740_SENSOR_CTRL07 0x3707 - -/* Timing Control */ -#define OV9740_TIMING_CTRL17 0x3817 -#define OV9740_TIMING_CTRL19 0x3819 -#define OV9740_TIMING_CTRL33 0x3833 -#define OV9740_TIMING_CTRL35 0x3835 - -/* Banding Filter */ -#define OV9740_AEC_MAXEXPO_60_H 0x3a02 -#define OV9740_AEC_MAXEXPO_60_L 0x3a03 -#define OV9740_AEC_B50_STEP_HI 0x3a08 -#define OV9740_AEC_B50_STEP_LO 0x3a09 -#define OV9740_AEC_B60_STEP_HI 0x3a0a -#define OV9740_AEC_B60_STEP_LO 0x3a0b -#define OV9740_AEC_CTRL0D 0x3a0d -#define OV9740_AEC_CTRL0E 0x3a0e -#define OV9740_AEC_MAXEXPO_50_H 0x3a14 -#define OV9740_AEC_MAXEXPO_50_L 0x3a15 - -/* AEC/AGC Control */ -#define OV9740_AEC_ENABLE 0x3503 -#define OV9740_GAIN_CEILING_01 0x3a18 -#define OV9740_GAIN_CEILING_02 0x3a19 -#define OV9740_AEC_HI_THRESHOLD 0x3a11 -#define OV9740_AEC_3A1A 0x3a1a -#define OV9740_AEC_CTRL1B_WPT2 0x3a1b -#define OV9740_AEC_CTRL0F_WPT 0x3a0f -#define OV9740_AEC_CTRL10_BPT 0x3a10 -#define OV9740_AEC_CTRL1E_BPT2 0x3a1e -#define OV9740_AEC_LO_THRESHOLD 0x3a1f - -/* BLC Control */ -#define OV9740_BLC_AUTO_ENABLE 0x4002 -#define OV9740_BLC_MODE 0x4005 - -/* VFIFO */ -#define OV9740_VFIFO_READ_START_HI 0x4608 -#define OV9740_VFIFO_READ_START_LO 0x4609 - -/* DVP Control */ -#define OV9740_DVP_VSYNC_CTRL02 0x4702 -#define OV9740_DVP_VSYNC_MODE 0x4704 -#define OV9740_DVP_VSYNC_CTRL06 0x4706 - -/* PLL Setting */ -#define OV9740_PLL_MODE_CTRL01 0x3104 -#define OV9740_PRE_PLL_CLK_DIV 0x0305 -#define OV9740_PLL_MULTIPLIER 0x0307 -#define OV9740_VT_SYS_CLK_DIV 0x0303 -#define OV9740_VT_PIX_CLK_DIV 0x0301 -#define OV9740_PLL_CTRL3010 0x3010 -#define OV9740_VFIFO_CTRL00 0x460e - -/* ISP Control */ -#define OV9740_ISP_CTRL00 0x5000 -#define OV9740_ISP_CTRL01 0x5001 -#define OV9740_ISP_CTRL03 0x5003 -#define OV9740_ISP_CTRL05 0x5005 -#define OV9740_ISP_CTRL12 0x5012 -#define OV9740_ISP_CTRL19 0x5019 -#define OV9740_ISP_CTRL1A 0x501a -#define OV9740_ISP_CTRL1E 0x501e -#define OV9740_ISP_CTRL1F 0x501f -#define OV9740_ISP_CTRL20 0x5020 -#define OV9740_ISP_CTRL21 0x5021 - -/* AWB */ -#define OV9740_AWB_CTRL00 0x5180 -#define OV9740_AWB_CTRL01 0x5181 -#define OV9740_AWB_CTRL02 0x5182 -#define OV9740_AWB_CTRL03 0x5183 -#define OV9740_AWB_ADV_CTRL01 0x5184 -#define OV9740_AWB_ADV_CTRL02 0x5185 -#define OV9740_AWB_ADV_CTRL03 0x5186 -#define OV9740_AWB_ADV_CTRL04 0x5187 -#define OV9740_AWB_ADV_CTRL05 0x5188 -#define OV9740_AWB_ADV_CTRL06 0x5189 -#define OV9740_AWB_ADV_CTRL07 0x518a -#define OV9740_AWB_ADV_CTRL08 0x518b -#define OV9740_AWB_ADV_CTRL09 0x518c -#define OV9740_AWB_ADV_CTRL10 0x518d -#define OV9740_AWB_ADV_CTRL11 0x518e -#define OV9740_AWB_CTRL0F 0x518f -#define OV9740_AWB_CTRL10 0x5190 -#define OV9740_AWB_CTRL11 0x5191 -#define OV9740_AWB_CTRL12 0x5192 -#define OV9740_AWB_CTRL13 0x5193 -#define OV9740_AWB_CTRL14 0x5194 - -/* MIPI Control */ -#define OV9740_MIPI_CTRL00 0x4800 -#define OV9740_MIPI_3837 0x3837 -#define OV9740_MIPI_CTRL01 0x4801 -#define OV9740_MIPI_CTRL03 0x4803 -#define OV9740_MIPI_CTRL05 0x4805 -#define OV9740_VFIFO_RD_CTRL 0x4601 -#define OV9740_MIPI_CTRL_3012 0x3012 -#define OV9740_SC_CMMM_MIPI_CTR 0x3014 - -#define OV9740_MAX_WIDTH 1280 -#define OV9740_MAX_HEIGHT 720 - -/* Misc. structures */ -struct ov9740_reg { - u16 reg; - u8 val; -}; - -struct ov9740_priv { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; - - u16 model; - u8 revision; - u8 manid; - u8 smiaver; - - bool flag_vflip; - bool flag_hflip; - - /* For suspend/resume. */ - struct v4l2_mbus_framefmt current_mf; - bool current_enable; -}; - -static const struct ov9740_reg ov9740_defaults[] = { - /* Software Reset */ - { OV9740_SOFTWARE_RESET, 0x01 }, - - /* Banding Filter */ - { OV9740_AEC_B50_STEP_HI, 0x00 }, - { OV9740_AEC_B50_STEP_LO, 0xe8 }, - { OV9740_AEC_CTRL0E, 0x03 }, - { OV9740_AEC_MAXEXPO_50_H, 0x15 }, - { OV9740_AEC_MAXEXPO_50_L, 0xc6 }, - { OV9740_AEC_B60_STEP_HI, 0x00 }, - { OV9740_AEC_B60_STEP_LO, 0xc0 }, - { OV9740_AEC_CTRL0D, 0x04 }, - { OV9740_AEC_MAXEXPO_60_H, 0x18 }, - { OV9740_AEC_MAXEXPO_60_L, 0x20 }, - - /* LC */ - { 0x5842, 0x02 }, { 0x5843, 0x5e }, { 0x5844, 0x04 }, { 0x5845, 0x32 }, - { 0x5846, 0x03 }, { 0x5847, 0x29 }, { 0x5848, 0x02 }, { 0x5849, 0xcc }, - - /* Un-documented OV9740 registers */ - { 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 }, - { 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c }, - { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580a, 0x0e }, { 0x580b, 0x16 }, - { 0x580c, 0x06 }, { 0x580d, 0x02 }, { 0x580e, 0x00 }, { 0x580f, 0x00 }, - { 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 }, - { 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 }, - { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581a, 0x07 }, { 0x581b, 0x08 }, - { 0x581c, 0x0b }, { 0x581d, 0x14 }, { 0x581e, 0x28 }, { 0x581f, 0x23 }, - { 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a }, - { 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f }, - { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582a, 0x8f }, { 0x582b, 0x9e }, - { 0x582c, 0x8f }, { 0x582d, 0x9f }, { 0x582e, 0x4f }, { 0x582f, 0x87 }, - { 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f }, - { 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf }, - { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583a, 0x9f }, { 0x583b, 0x7f }, - { 0x583c, 0x5f }, - - /* Y Gamma */ - { 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e }, - { 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 }, - { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548a, 0xa4 }, { 0x548b, 0xb1 }, - { 0x548c, 0xc6 }, { 0x548d, 0xd8 }, { 0x548e, 0xe9 }, - - /* UV Gamma */ - { 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 }, - { 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 }, - { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549a, 0x02 }, { 0x549b, 0xeb }, - { 0x549c, 0x02 }, { 0x549d, 0xa0 }, { 0x549e, 0x02 }, { 0x549f, 0x67 }, - { 0x54a0, 0x02 }, { 0x54a1, 0x3b }, { 0x54a2, 0x02 }, { 0x54a3, 0x18 }, - { 0x54a4, 0x01 }, { 0x54a5, 0xe7 }, { 0x54a6, 0x01 }, { 0x54a7, 0xc3 }, - { 0x54a8, 0x01 }, { 0x54a9, 0x94 }, { 0x54aa, 0x01 }, { 0x54ab, 0x72 }, - { 0x54ac, 0x01 }, { 0x54ad, 0x57 }, - - /* AWB */ - { OV9740_AWB_CTRL00, 0xf0 }, - { OV9740_AWB_CTRL01, 0x00 }, - { OV9740_AWB_CTRL02, 0x41 }, - { OV9740_AWB_CTRL03, 0x42 }, - { OV9740_AWB_ADV_CTRL01, 0x8a }, - { OV9740_AWB_ADV_CTRL02, 0x61 }, - { OV9740_AWB_ADV_CTRL03, 0xce }, - { OV9740_AWB_ADV_CTRL04, 0xa8 }, - { OV9740_AWB_ADV_CTRL05, 0x17 }, - { OV9740_AWB_ADV_CTRL06, 0x1f }, - { OV9740_AWB_ADV_CTRL07, 0x27 }, - { OV9740_AWB_ADV_CTRL08, 0x41 }, - { OV9740_AWB_ADV_CTRL09, 0x34 }, - { OV9740_AWB_ADV_CTRL10, 0xf0 }, - { OV9740_AWB_ADV_CTRL11, 0x10 }, - { OV9740_AWB_CTRL0F, 0xff }, - { OV9740_AWB_CTRL10, 0x00 }, - { OV9740_AWB_CTRL11, 0xff }, - { OV9740_AWB_CTRL12, 0x00 }, - { OV9740_AWB_CTRL13, 0xff }, - { OV9740_AWB_CTRL14, 0x00 }, - - /* CIP */ - { 0x530d, 0x12 }, - - /* CMX */ - { 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 }, - { 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 }, - { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538a, 0x00 }, { 0x538b, 0x20 }, - { 0x538c, 0x00 }, { 0x538d, 0x00 }, { 0x538e, 0x00 }, { 0x538f, 0x16 }, - { 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 }, - { 0x5394, 0x18 }, - - /* 50/60 Detection */ - { 0x3c0a, 0x9c }, { 0x3c0b, 0x3f }, - - /* Output Select */ - { OV9740_IO_OUTPUT_SEL01, 0x00 }, - { OV9740_IO_OUTPUT_SEL02, 0x00 }, - { OV9740_IO_CREL00, 0x00 }, - { OV9740_IO_CREL01, 0x00 }, - { OV9740_IO_CREL02, 0x00 }, - - /* AWB Control */ - { OV9740_AWB_MANUAL_CTRL, 0x00 }, - - /* Analog Control */ - { OV9740_ANALOG_CTRL03, 0xaa }, - { OV9740_ANALOG_CTRL32, 0x2f }, - { OV9740_ANALOG_CTRL20, 0x66 }, - { OV9740_ANALOG_CTRL21, 0xc0 }, - { OV9740_ANALOG_CTRL31, 0x52 }, - { OV9740_ANALOG_CTRL33, 0x50 }, - { OV9740_ANALOG_CTRL30, 0xca }, - { OV9740_ANALOG_CTRL04, 0x0c }, - { OV9740_ANALOG_CTRL01, 0x40 }, - { OV9740_ANALOG_CTRL02, 0x16 }, - { OV9740_ANALOG_CTRL10, 0xa1 }, - { OV9740_ANALOG_CTRL12, 0x24 }, - { OV9740_ANALOG_CTRL22, 0x9f }, - { OV9740_ANALOG_CTRL15, 0xf0 }, - - /* Sensor Control */ - { OV9740_SENSOR_CTRL03, 0x42 }, - { OV9740_SENSOR_CTRL04, 0x10 }, - { OV9740_SENSOR_CTRL05, 0x45 }, - { OV9740_SENSOR_CTRL07, 0x14 }, - - /* Timing Control */ - { OV9740_TIMING_CTRL33, 0x04 }, - { OV9740_TIMING_CTRL35, 0x02 }, - { OV9740_TIMING_CTRL19, 0x6e }, - { OV9740_TIMING_CTRL17, 0x94 }, - - /* AEC/AGC Control */ - { OV9740_AEC_ENABLE, 0x10 }, - { OV9740_GAIN_CEILING_01, 0x00 }, - { OV9740_GAIN_CEILING_02, 0x7f }, - { OV9740_AEC_HI_THRESHOLD, 0xa0 }, - { OV9740_AEC_3A1A, 0x05 }, - { OV9740_AEC_CTRL1B_WPT2, 0x50 }, - { OV9740_AEC_CTRL0F_WPT, 0x50 }, - { OV9740_AEC_CTRL10_BPT, 0x4c }, - { OV9740_AEC_CTRL1E_BPT2, 0x4c }, - { OV9740_AEC_LO_THRESHOLD, 0x26 }, - - /* BLC Control */ - { OV9740_BLC_AUTO_ENABLE, 0x45 }, - { OV9740_BLC_MODE, 0x18 }, - - /* DVP Control */ - { OV9740_DVP_VSYNC_CTRL02, 0x04 }, - { OV9740_DVP_VSYNC_MODE, 0x00 }, - { OV9740_DVP_VSYNC_CTRL06, 0x08 }, - - /* PLL Setting */ - { OV9740_PLL_MODE_CTRL01, 0x20 }, - { OV9740_PRE_PLL_CLK_DIV, 0x03 }, - { OV9740_PLL_MULTIPLIER, 0x4c }, - { OV9740_VT_SYS_CLK_DIV, 0x01 }, - { OV9740_VT_PIX_CLK_DIV, 0x08 }, - { OV9740_PLL_CTRL3010, 0x01 }, - { OV9740_VFIFO_CTRL00, 0x82 }, - - /* Timing Setting */ - /* VTS */ - { OV9740_FRM_LENGTH_LN_HI, 0x03 }, - { OV9740_FRM_LENGTH_LN_LO, 0x07 }, - /* HTS */ - { OV9740_LN_LENGTH_PCK_HI, 0x06 }, - { OV9740_LN_LENGTH_PCK_LO, 0x62 }, - - /* MIPI Control */ - { OV9740_MIPI_CTRL00, 0x44 }, /* 0x64 for discontinuous clk */ - { OV9740_MIPI_3837, 0x01 }, - { OV9740_MIPI_CTRL01, 0x0f }, - { OV9740_MIPI_CTRL03, 0x05 }, - { OV9740_MIPI_CTRL05, 0x10 }, - { OV9740_VFIFO_RD_CTRL, 0x16 }, - { OV9740_MIPI_CTRL_3012, 0x70 }, - { OV9740_SC_CMMM_MIPI_CTR, 0x01 }, - - /* YUYV order */ - { OV9740_ISP_CTRL19, 0x02 }, -}; - -static u32 ov9740_codes[] = { - MEDIA_BUS_FMT_YUYV8_2X8, -}; - -/* read a register */ -static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val) -{ - int ret; - struct i2c_msg msg[] = { - { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = (u8 *)®, - }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .len = 1, - .buf = val, - }, - }; - - reg = swab16(reg); - - ret = i2c_transfer(client->adapter, msg, 2); - if (ret < 0) { - dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg); - return ret; - } - - return 0; -} - -/* write a register */ -static int ov9740_reg_write(struct i2c_client *client, u16 reg, u8 val) -{ - struct i2c_msg msg; - struct { - u16 reg; - u8 val; - } __packed buf; - int ret; - - reg = swab16(reg); - - buf.reg = reg; - buf.val = val; - - msg.addr = client->addr; - msg.flags = 0; - msg.len = 3; - msg.buf = (u8 *)&buf; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg); - return ret; - } - - return 0; -} - - -/* Read a register, alter its bits, write it back */ -static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset) -{ - u8 val; - int ret; - - ret = ov9740_reg_read(client, reg, &val); - if (ret < 0) { - dev_err(&client->dev, - "[Read]-Modify-Write of register 0x%04x failed!\n", - reg); - return ret; - } - - val |= set; - val &= ~unset; - - ret = ov9740_reg_write(client, reg, val); - if (ret < 0) { - dev_err(&client->dev, - "Read-Modify-[Write] of register 0x%04x failed!\n", - reg); - return ret; - } - - return 0; -} - -static int ov9740_reg_write_array(struct i2c_client *client, - const struct ov9740_reg *regarray, - int regarraylen) -{ - int i; - int ret; - - for (i = 0; i < regarraylen; i++) { - ret = ov9740_reg_write(client, - regarray[i].reg, regarray[i].val); - if (ret < 0) - return ret; - } - - return 0; -} - -/* Start/Stop streaming from the device */ -static int ov9740_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov9740_priv *priv = to_ov9740(sd); - int ret; - - /* Program orientation register. */ - if (priv->flag_vflip) - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x2, 0); - else - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x2); - if (ret < 0) - return ret; - - if (priv->flag_hflip) - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x1, 0); - else - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x1); - if (ret < 0) - return ret; - - if (enable) { - dev_dbg(&client->dev, "Enabling Streaming\n"); - /* Start Streaming */ - ret = ov9740_reg_write(client, OV9740_MODE_SELECT, 0x01); - - } else { - dev_dbg(&client->dev, "Disabling Streaming\n"); - /* Software Reset */ - ret = ov9740_reg_write(client, OV9740_SOFTWARE_RESET, 0x01); - if (!ret) - /* Setting Streaming to Standby */ - ret = ov9740_reg_write(client, OV9740_MODE_SELECT, - 0x00); - } - - priv->current_enable = enable; - - return ret; -} - -/* select nearest higher resolution for capture */ -static void ov9740_res_roundup(u32 *width, u32 *height) -{ - /* Width must be a multiple of 4 pixels. */ - *width = ALIGN(*width, 4); - - /* Max resolution is 1280x720 (720p). */ - if (*width > OV9740_MAX_WIDTH) - *width = OV9740_MAX_WIDTH; - - if (*height > OV9740_MAX_HEIGHT) - *height = OV9740_MAX_HEIGHT; -} - -/* Setup registers according to resolution and color encoding */ -static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height) -{ - u32 x_start; - u32 y_start; - u32 x_end; - u32 y_end; - bool scaling = false; - u32 scale_input_x; - u32 scale_input_y; - int ret; - - if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT)) - scaling = true; - - /* - * Try to use as much of the sensor area as possible when supporting - * smaller resolutions. Depending on the aspect ratio of the - * chosen resolution, we can either use the full width of the sensor, - * or the full height of the sensor (or both if the aspect ratio is - * the same as 1280x720. - */ - if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) { - scale_input_x = (OV9740_MAX_HEIGHT * width) / height; - scale_input_y = OV9740_MAX_HEIGHT; - } else { - scale_input_x = OV9740_MAX_WIDTH; - scale_input_y = (OV9740_MAX_WIDTH * height) / width; - } - - /* These describe the area of the sensor to use. */ - x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2; - y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2; - x_end = x_start + scale_input_x - 1; - y_end = y_start + scale_input_y - 1; - - ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI, - (scale_input_x - width) >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO, - (scale_input_x - width) & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef | - (scaling << 4)); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff); - -done: - return ret; -} - -/* set the format we will capture in */ -static int ov9740_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov9740_priv *priv = to_ov9740(sd); - int ret; - - ret = ov9740_reg_write_array(client, ov9740_defaults, - ARRAY_SIZE(ov9740_defaults)); - if (ret < 0) - return ret; - - ret = ov9740_set_res(client, mf->width, mf->height); - if (ret < 0) - return ret; - - priv->current_mf = *mf; - return ret; -} - -static int ov9740_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - - if (format->pad) - return -EINVAL; - - ov9740_res_roundup(&mf->width, &mf->height); - - mf->field = V4L2_FIELD_NONE; - mf->code = MEDIA_BUS_FMT_YUYV8_2X8; - mf->colorspace = V4L2_COLORSPACE_SRGB; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov9740_s_fmt(sd, mf); - cfg->try_fmt = *mf; - return 0; -} - -static int ov9740_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(ov9740_codes)) - return -EINVAL; - - code->code = ov9740_codes[code->index]; - - return 0; -} - -static int ov9740_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = OV9740_MAX_WIDTH; - sel->r.height = OV9740_MAX_HEIGHT; - return 0; - default: - return -EINVAL; - } -} - -/* Set status of additional camera capabilities */ -static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct ov9740_priv *priv = - container_of(ctrl->handler, struct ov9740_priv, hdl); - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - priv->flag_vflip = ctrl->val; - break; - case V4L2_CID_HFLIP: - priv->flag_hflip = ctrl->val; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int ov9740_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov9740_priv *priv = to_ov9740(sd); - int ret; - - if (on) { - ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); - if (ret < 0) - return ret; - - if (priv->current_enable) { - ov9740_s_fmt(sd, &priv->current_mf); - ov9740_s_stream(sd, 1); - } - } else { - if (priv->current_enable) { - ov9740_s_stream(sd, 0); - priv->current_enable = true; - } - - soc_camera_power_off(&client->dev, ssdd, priv->clk); - } - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov9740_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - u8 val; - - if (reg->reg & ~0xffff) - return -EINVAL; - - reg->size = 2; - - ret = ov9740_reg_read(client, reg->reg, &val); - if (ret) - return ret; - - reg->val = (__u64)val; - - return ret; -} - -static int ov9740_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg & ~0xffff || reg->val & ~0xff) - return -EINVAL; - - return ov9740_reg_write(client, reg->reg, reg->val); -} -#endif - -static int ov9740_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov9740_priv *priv = to_ov9740(sd); - u8 modelhi, modello; - int ret; - - ret = ov9740_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show product ID and manufacturer ID - */ - ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello); - if (ret < 0) - goto done; - - priv->model = (modelhi << 8) | modello; - - ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver); - if (ret < 0) - goto done; - - if (priv->model != 0x9740) { - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, Manufacturer 0x%02x, SMIA Version 0x%02x\n", - priv->model, priv->revision, priv->manid, priv->smiaver); - - ret = v4l2_ctrl_handler_setup(&priv->hdl); - -done: - ov9740_s_power(&priv->subdev, 0); - return ret; -} - -/* Request bus settings on camera side */ -static int ov9740_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static const struct v4l2_subdev_video_ops ov9740_video_ops = { - .s_stream = ov9740_s_stream, - .g_mbus_config = ov9740_g_mbus_config, -}; - -static const struct v4l2_subdev_core_ops ov9740_core_ops = { - .s_power = ov9740_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov9740_get_register, - .s_register = ov9740_set_register, -#endif -}; - -static const struct v4l2_subdev_pad_ops ov9740_pad_ops = { - .enum_mbus_code = ov9740_enum_mbus_code, - .get_selection = ov9740_get_selection, - .set_fmt = ov9740_set_fmt, -}; - -static const struct v4l2_subdev_ops ov9740_subdev_ops = { - .core = &ov9740_core_ops, - .video = &ov9740_video_ops, - .pad = &ov9740_pad_ops, -}; - -static const struct v4l2_ctrl_ops ov9740_ctrl_ops = { - .s_ctrl = ov9740_s_ctrl, -}; - -/* - * i2c_driver function - */ -static int ov9740_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov9740_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "Missing platform_data for driver\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops); - v4l2_ctrl_handler_init(&priv->hdl, 13); - v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - goto eclkget; - } - - ret = ov9740_video_probe(client); - if (ret < 0) { - v4l2_clk_put(priv->clk); -eclkget: - v4l2_ctrl_handler_free(&priv->hdl); - } - - return ret; -} - -static int ov9740_remove(struct i2c_client *client) -{ - struct ov9740_priv *priv = i2c_get_clientdata(client); - - v4l2_clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); - v4l2_ctrl_handler_free(&priv->hdl); - return 0; -} - -static const struct i2c_device_id ov9740_id[] = { - { "ov9740", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov9740_id); - -static struct i2c_driver ov9740_i2c_driver = { - .driver = { - .name = "ov9740", - }, - .probe = ov9740_probe, - .remove = ov9740_remove, - .id_table = ov9740_id, -}; - -module_i2c_driver(ov9740_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740"); -MODULE_AUTHOR("Andrew Chew "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index b5ccb60cf664..6cff26b29a38 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -141,7 +141,6 @@ config VIDEO_RENESAS_CEU ---help--- This is a v4l2 driver for the Renesas CEU Interface -source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/am437x/Kconfig" source "drivers/media/platform/xilinx/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index e6deb2597738..7cbbd925124c 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -62,8 +62,6 @@ obj-y += davinci/ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o -obj-$(CONFIG_SOC_CAMERA) += soc_camera/ - obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o obj-$(CONFIG_VIDEO_RENESAS_CEU) += renesas-ceu.o obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig deleted file mode 100644 index 8f9b3bac5450..000000000000 --- a/drivers/media/platform/soc_camera/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -config SOC_CAMERA - tristate "SoC camera support" - depends on VIDEO_V4L2 && HAS_DMA && I2C - select VIDEOBUF2_CORE - help - SoC Camera is a common API to several cameras, not connecting - over a bus like PCI or USB. For example some i2c camera connected - directly to the data bus of an SoC. diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile deleted file mode 100644 index 85d5e74f3b2b..000000000000 --- a/drivers/media/platform/soc_camera/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c deleted file mode 100644 index 21034339cdcb..000000000000 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ /dev/null @@ -1,2170 +0,0 @@ -/* - * camera image capture (abstract) bus driver - * - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This driver provides an interface between platform-specific camera - * busses and camera devices. It should be used if the camera is - * connected not over a "proper" bus like PCI or USB, but over a - * special bus, like, for example, the Quick Capture interface on PXA270 - * SoCs. Later it should also be used for i.MX31 SoCs from Freescale. - * It can handle multiple cameras and / or multiple busses, which can - * be used, e.g., in stereo-vision applications. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Default to VGA resolution */ -#define DEFAULT_WIDTH 640 -#define DEFAULT_HEIGHT 480 - -#define MAP_MAX_NUM 32 -static DECLARE_BITMAP(device_map, MAP_MAX_NUM); -static LIST_HEAD(hosts); -static LIST_HEAD(devices); -/* - * Protects lists and bitmaps of hosts and devices. - * Lock nesting: Ok to take ->host_lock under list_lock. - */ -static DEFINE_MUTEX(list_lock); - -struct soc_camera_async_client { - struct v4l2_async_subdev *sensor; - struct v4l2_async_notifier notifier; - struct platform_device *pdev; - struct list_head list; /* needed for clean up */ -}; - -static int soc_camera_video_start(struct soc_camera_device *icd); -static int video_dev_create(struct soc_camera_device *icd); - -int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk) -{ - int ret; - bool clock_toggle; - - if (clk && (!ssdd->unbalanced_power || - !test_and_set_bit(0, &ssdd->clock_state))) { - ret = v4l2_clk_enable(clk); - if (ret < 0) { - dev_err(dev, "Cannot enable clock: %d\n", ret); - return ret; - } - clock_toggle = true; - } else { - clock_toggle = false; - } - - ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (ret < 0) { - dev_err(dev, "Cannot enable regulators\n"); - goto eregenable; - } - - if (ssdd->power) { - ret = ssdd->power(dev, 1); - if (ret < 0) { - dev_err(dev, - "Platform failed to power-on the camera.\n"); - goto epwron; - } - } - - return 0; - -epwron: - regulator_bulk_disable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); -eregenable: - if (clock_toggle) - v4l2_clk_disable(clk); - - return ret; -} -EXPORT_SYMBOL(soc_camera_power_on); - -int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk) -{ - int ret = 0; - int err; - - if (ssdd->power) { - err = ssdd->power(dev, 0); - if (err < 0) { - dev_err(dev, - "Platform failed to power-off the camera.\n"); - ret = err; - } - } - - err = regulator_bulk_disable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (err < 0) { - dev_err(dev, "Cannot disable regulators\n"); - ret = ret ? : err; - } - - if (clk && (!ssdd->unbalanced_power || test_and_clear_bit(0, &ssdd->clock_state))) - v4l2_clk_disable(clk); - - return ret; -} -EXPORT_SYMBOL(soc_camera_power_off); - -int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd) -{ - /* Should not have any effect in synchronous case */ - return devm_regulator_bulk_get(dev, ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); -} -EXPORT_SYMBOL(soc_camera_power_init); - -static int __soc_camera_power_on(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - ret = v4l2_subdev_call(sd, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - return 0; -} - -static int __soc_camera_power_off(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - ret = v4l2_subdev_call(sd, core, s_power, 0); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - return 0; -} - -static int soc_camera_clock_start(struct soc_camera_host *ici) -{ - int ret; - - if (!ici->ops->clock_start) - return 0; - - mutex_lock(&ici->clk_lock); - ret = ici->ops->clock_start(ici); - mutex_unlock(&ici->clk_lock); - - return ret; -} - -static void soc_camera_clock_stop(struct soc_camera_host *ici) -{ - if (!ici->ops->clock_stop) - return; - - mutex_lock(&ici->clk_lock); - ici->ops->clock_stop(ici); - mutex_unlock(&ici->clk_lock); -} - -const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( - struct soc_camera_device *icd, unsigned int fourcc) -{ - unsigned int i; - - for (i = 0; i < icd->num_user_formats; i++) - if (icd->user_formats[i].host_fmt->fourcc == fourcc) - return icd->user_formats + i; - return NULL; -} -EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); - -/** - * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags - * @ssdd: camera platform parameters - * @cfg: media bus configuration - * @return: resulting flags - */ -unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, - const struct v4l2_mbus_config *cfg) -{ - unsigned long f, flags = cfg->flags; - - /* If only one of the two polarities is supported, switch to the opposite */ - if (ssdd->flags & SOCAM_SENSOR_INVERT_HSYNC) { - f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW); - if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW) - flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW; - } - - if (ssdd->flags & SOCAM_SENSOR_INVERT_VSYNC) { - f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW); - if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW) - flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW; - } - - if (ssdd->flags & SOCAM_SENSOR_INVERT_PCLK) { - f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING); - if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING) - flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; - } - - return flags; -} -EXPORT_SYMBOL(soc_camera_apply_board_flags); - -#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \ - ((x) >> 24) & 0xff - -static int soc_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n", - pixfmtstr(pix->pixelformat), pix->width, pix->height); - - if (pix->pixelformat != V4L2_PIX_FMT_JPEG && - !(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) { - pix->bytesperline = 0; - pix->sizeimage = 0; - } - - ret = ici->ops->try_fmt(icd, f); - if (ret < 0) - return ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) - return -EINVAL; - - ret = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt); - if (ret < 0) - return ret; - - pix->bytesperline = max_t(u32, pix->bytesperline, ret); - - ret = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline, - pix->height); - if (ret < 0) - return ret; - - pix->sizeimage = max_t(u32, pix->sizeimage, ret); - - return 0; -} - -static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - /* Only single-plane capture is supported so far */ - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* limit format to hardware capabilities */ - return soc_camera_try_fmt(icd, f); -} - -static int soc_camera_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct soc_camera_device *icd = file->private_data; - - if (inp->index != 0) - return -EINVAL; - - /* default is camera */ - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = icd->vdev->tvnorms; - strscpy(inp->name, "Camera", sizeof(inp->name)); - - return 0; -} - -static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - - return 0; -} - -static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i > 0) - return -EINVAL; - - return 0; -} - -static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_subdev_call(sd, video, s_std, a); -} - -static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_subdev_call(sd, video, g_std, a); -} - -static int soc_camera_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - return ici->ops->enum_framesizes(icd, fsize); -} - -static int soc_camera_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - int ret; - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - ret = vb2_reqbufs(&icd->vb2_vidq, p); - if (!ret) - icd->streamer = p->count ? file : NULL; - return ret; -} - -static int soc_camera_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - return vb2_querybuf(&icd->vb2_vidq, p); -} - -static int soc_camera_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer != file) - return -EBUSY; - - return vb2_qbuf(&icd->vb2_vidq, NULL, p); -} - -static int soc_camera_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer != file) - return -EBUSY; - - return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK); -} - -static int soc_camera_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *create) -{ - struct soc_camera_device *icd = file->private_data; - int ret; - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - ret = vb2_create_bufs(&icd->vb2_vidq, create); - if (!ret) - icd->streamer = file; - return ret; -} - -static int soc_camera_prepare_buf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_prepare_buf(&icd->vb2_vidq, NULL, b); -} - -static int soc_camera_expbuf(struct file *file, void *priv, - struct v4l2_exportbuffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - return vb2_expbuf(&icd->vb2_vidq, p); -} - -/* Always entered with .host_lock held */ -static int soc_camera_init_user_formats(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - unsigned int i, fmts = 0, raw_fmts = 0; - int ret; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { - raw_fmts++; - code.index++; - } - - if (!ici->ops->get_formats) - /* - * Fallback mode - the host will have to serve all - * sensor-provided formats one-to-one to the user - */ - fmts = raw_fmts; - else - /* - * First pass - only count formats this host-sensor - * configuration can provide - */ - for (i = 0; i < raw_fmts; i++) { - ret = ici->ops->get_formats(icd, i, NULL); - if (ret < 0) - return ret; - fmts += ret; - } - - if (!fmts) - return -ENXIO; - - icd->user_formats = - vmalloc(array_size(fmts, - sizeof(struct soc_camera_format_xlate))); - if (!icd->user_formats) - return -ENOMEM; - - dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts); - - /* Second pass - actually fill data formats */ - fmts = 0; - for (i = 0; i < raw_fmts; i++) - if (!ici->ops->get_formats) { - code.index = i; - v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - icd->user_formats[fmts].host_fmt = - soc_mbus_get_fmtdesc(code.code); - if (icd->user_formats[fmts].host_fmt) - icd->user_formats[fmts++].code = code.code; - } else { - ret = ici->ops->get_formats(icd, i, - &icd->user_formats[fmts]); - if (ret < 0) - goto egfmt; - fmts += ret; - } - - icd->num_user_formats = fmts; - icd->current_fmt = &icd->user_formats[0]; - - return 0; - -egfmt: - vfree(icd->user_formats); - return ret; -} - -/* Always entered with .host_lock held */ -static void soc_camera_free_user_formats(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->put_formats) - ici->ops->put_formats(icd); - icd->current_fmt = NULL; - icd->num_user_formats = 0; - vfree(icd->user_formats); - icd->user_formats = NULL; -} - -/* Called with .vb_lock held, or from the first open(2), see comment there */ -static int soc_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n", - pixfmtstr(pix->pixelformat), pix->width, pix->height); - - /* We always call try_fmt() before set_fmt() or set_selection() */ - ret = soc_camera_try_fmt(icd, f); - if (ret < 0) - return ret; - - ret = ici->ops->set_fmt(icd, f); - if (ret < 0) { - return ret; - } else if (!icd->current_fmt || - icd->current_fmt->host_fmt->fourcc != pix->pixelformat) { - dev_err(icd->pdev, - "Host driver hasn't set up current format correctly!\n"); - return -EINVAL; - } - - icd->user_width = pix->width; - icd->user_height = pix->height; - icd->bytesperline = pix->bytesperline; - icd->sizeimage = pix->sizeimage; - icd->colorspace = pix->colorspace; - icd->field = pix->field; - - dev_dbg(icd->pdev, "set width: %d height: %d\n", - icd->user_width, icd->user_height); - - /* set physical bus parameters */ - return ici->ops->set_bus_param(icd); -} - -static int soc_camera_add_device(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - if (ici->icd) - return -EBUSY; - - if (!icd->clk) { - ret = soc_camera_clock_start(ici); - if (ret < 0) - return ret; - } - - if (ici->ops->add) { - ret = ici->ops->add(icd); - if (ret < 0) - goto eadd; - } - - ici->icd = icd; - - return 0; - -eadd: - if (!icd->clk) - soc_camera_clock_stop(ici); - return ret; -} - -static void soc_camera_remove_device(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (WARN_ON(icd != ici->icd)) - return; - - if (ici->ops->remove) - ici->ops->remove(icd); - if (!icd->clk) - soc_camera_clock_stop(ici); - ici->icd = NULL; -} - -static int soc_camera_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct soc_camera_device *icd; - struct soc_camera_host *ici; - int ret; - - /* - * Don't mess with the host during probe: wait until the loop in - * scan_add_host() completes. Also protect against a race with - * soc_camera_host_unregister(). - */ - if (mutex_lock_interruptible(&list_lock)) - return -ERESTARTSYS; - - if (!vdev || !video_is_registered(vdev)) { - mutex_unlock(&list_lock); - return -ENODEV; - } - - icd = video_get_drvdata(vdev); - ici = to_soc_camera_host(icd->parent); - - ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV; - mutex_unlock(&list_lock); - - if (ret < 0) { - dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); - return ret; - } - - if (!to_soc_camera_control(icd)) { - /* No device driver attached */ - ret = -ENODEV; - goto econtrol; - } - - if (mutex_lock_interruptible(&ici->host_lock)) { - ret = -ERESTARTSYS; - goto elockhost; - } - icd->use_count++; - - /* Now we really have to activate the camera */ - if (icd->use_count == 1) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - /* Restore parameters before the last close() per V4L2 API */ - struct v4l2_format f = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt.pix = { - .width = icd->user_width, - .height = icd->user_height, - .field = icd->field, - .colorspace = icd->colorspace, - .pixelformat = - icd->current_fmt->host_fmt->fourcc, - }, - }; - - /* The camera could have been already on, try to reset */ - if (sdesc->subdev_desc.reset) - if (icd->control) - sdesc->subdev_desc.reset(icd->control); - - ret = soc_camera_add_device(icd); - if (ret < 0) { - dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); - goto eiciadd; - } - - ret = __soc_camera_power_on(icd); - if (ret < 0) - goto epower; - - pm_runtime_enable(&icd->vdev->dev); - ret = pm_runtime_resume(&icd->vdev->dev); - if (ret < 0 && ret != -ENOSYS) - goto eresume; - - /* - * Try to configure with default parameters. Notice: this is the - * very first open, so, we cannot race against other calls, - * apart from someone else calling open() simultaneously, but - * .host_lock is protecting us against it. - */ - ret = soc_camera_set_fmt(icd, &f); - if (ret < 0) - goto esfmt; - - ret = ici->ops->init_videobuf2(&icd->vb2_vidq, icd); - if (ret < 0) - goto einitvb; - v4l2_ctrl_handler_setup(&icd->ctrl_handler); - } - mutex_unlock(&ici->host_lock); - - file->private_data = icd; - dev_dbg(icd->pdev, "camera device open\n"); - - return 0; - - /* - * All errors are entered with the .host_lock held, first four also - * with use_count == 1 - */ -einitvb: -esfmt: - pm_runtime_disable(&icd->vdev->dev); -eresume: - __soc_camera_power_off(icd); -epower: - soc_camera_remove_device(icd); -eiciadd: - icd->use_count--; - mutex_unlock(&ici->host_lock); -elockhost: -econtrol: - module_put(ici->ops->owner); - - return ret; -} - -static int soc_camera_close(struct file *file) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - mutex_lock(&ici->host_lock); - if (icd->streamer == file) { - if (ici->ops->init_videobuf2) - vb2_queue_release(&icd->vb2_vidq); - icd->streamer = NULL; - } - icd->use_count--; - if (!icd->use_count) { - pm_runtime_suspend(&icd->vdev->dev); - pm_runtime_disable(&icd->vdev->dev); - - __soc_camera_power_off(icd); - - soc_camera_remove_device(icd); - } - - mutex_unlock(&ici->host_lock); - - module_put(ici->ops->owner); - - dev_dbg(icd->pdev, "camera device close\n"); - - return 0; -} - -static ssize_t soc_camera_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - dev_dbg(icd->pdev, "read called, buf %p\n", buf); - - if (ici->ops->init_videobuf2 && icd->vb2_vidq.io_modes & VB2_READ) - return vb2_read(&icd->vb2_vidq, buf, count, ppos, - file->f_flags & O_NONBLOCK); - - dev_err(icd->pdev, "camera device read not implemented\n"); - - return -EINVAL; -} - -static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int err; - - dev_dbg(icd->pdev, "mmap called, vma=%p\n", vma); - - if (icd->streamer != file) - return -EBUSY; - - if (mutex_lock_interruptible(&ici->host_lock)) - return -ERESTARTSYS; - err = vb2_mmap(&icd->vb2_vidq, vma); - mutex_unlock(&ici->host_lock); - - dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, - err); - - return err; -} - -static __poll_t soc_camera_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - __poll_t res = EPOLLERR; - - if (icd->streamer != file) - return EPOLLERR; - - mutex_lock(&ici->host_lock); - res = ici->ops->poll(file, pt); - mutex_unlock(&ici->host_lock); - return res; -} - -static const struct v4l2_file_operations soc_camera_fops = { - .owner = THIS_MODULE, - .open = soc_camera_open, - .release = soc_camera_close, - .unlocked_ioctl = video_ioctl2, - .read = soc_camera_read, - .mmap = soc_camera_mmap, - .poll = soc_camera_poll, -}; - -static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - int ret; - - WARN_ON(priv != file->private_data); - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type); - return -EINVAL; - } - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - if (vb2_is_streaming(&icd->vb2_vidq)) { - dev_err(icd->pdev, "S_FMT denied: queue initialised\n"); - return -EBUSY; - } - - ret = soc_camera_set_fmt(icd, f); - - if (!ret && !icd->streamer) - icd->streamer = file; - - return ret; -} - -static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct soc_camera_device *icd = file->private_data; - const struct soc_mbus_pixelfmt *format; - - WARN_ON(priv != file->private_data); - - if (f->index >= icd->num_user_formats) - return -EINVAL; - - format = icd->user_formats[f->index].host_fmt; - - if (format->name) - strscpy(f->description, format->name, sizeof(f->description)); - f->pixelformat = format->fourcc; - return 0; -} - -static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_pix_format *pix = &f->fmt.pix; - - WARN_ON(priv != file->private_data); - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pix->width = icd->user_width; - pix->height = icd->user_height; - pix->bytesperline = icd->bytesperline; - pix->sizeimage = icd->sizeimage; - pix->field = icd->field; - pix->pixelformat = icd->current_fmt->host_fmt->fourcc; - pix->colorspace = icd->colorspace; - dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n", - icd->current_fmt->host_fmt->fourcc); - return 0; -} - -static int soc_camera_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - WARN_ON(priv != file->private_data); - - strscpy(cap->driver, ici->drv_name, sizeof(cap->driver)); - return ici->ops->querycap(ici, cap); -} - -static int soc_camera_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (icd->streamer != file) - return -EBUSY; - - /* This calls buf_queue from host driver's videobuf2_queue_ops */ - ret = vb2_streamon(&icd->vb2_vidq, i); - if (!ret) - v4l2_subdev_call(sd, video, s_stream, 1); - - return ret; -} - -static int soc_camera_streamoff(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (icd->streamer != file) - return -EBUSY; - - /* - * This calls buf_release from host driver's videobuf2_queue_ops for all - * remaining buffers. When the last buffer is freed, stop capture - */ - ret = vb2_streamoff(&icd->vb2_vidq, i); - - v4l2_subdev_call(sd, video, s_stream, 0); - - return ret; -} - -static int soc_camera_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - /* With a wrong type no need to try to fall back to cropping */ - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return ici->ops->get_selection(icd, s); -} - -static int soc_camera_s_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - /* In all these cases cropping emulation will not help */ - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - (s->target != V4L2_SEL_TGT_COMPOSE && - s->target != V4L2_SEL_TGT_CROP)) - return -EINVAL; - - if (s->target == V4L2_SEL_TGT_COMPOSE) { - /* No output size change during a running capture! */ - if (vb2_is_streaming(&icd->vb2_vidq) && - (icd->user_width != s->r.width || - icd->user_height != s->r.height)) - return -EBUSY; - - /* - * Only one user is allowed to change the output format, touch - * buffers, start / stop streaming, poll for data - */ - if (icd->streamer && icd->streamer != file) - return -EBUSY; - } - - if (s->target == V4L2_SEL_TGT_CROP && - vb2_is_streaming(&icd->vb2_vidq) && - ici->ops->set_liveselection) - ret = ici->ops->set_liveselection(icd, s); - else - ret = ici->ops->set_selection(icd, s); - if (!ret && - s->target == V4L2_SEL_TGT_COMPOSE) { - icd->user_width = s->r.width; - icd->user_height = s->r.height; - if (!icd->streamer) - icd->streamer = file; - } - - return ret; -} - -static int soc_camera_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->get_parm) - return ici->ops->get_parm(icd, a); - - return -ENOIOCTLCMD; -} - -static int soc_camera_s_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->set_parm) - return ici->ops->set_parm(icd, a); - - return -ENOIOCTLCMD; -} - -static int soc_camera_probe(struct soc_camera_host *ici, - struct soc_camera_device *icd); - -/* So far this function cannot fail */ -static void scan_add_host(struct soc_camera_host *ici) -{ - struct soc_camera_device *icd; - - mutex_lock(&list_lock); - - list_for_each_entry(icd, &devices, list) - if (icd->iface == ici->nr) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; - - /* The camera could have been already on, try to reset */ - if (ssdd->reset) - if (icd->control) - ssdd->reset(icd->control); - - icd->parent = ici->v4l2_dev.dev; - - /* Ignore errors */ - soc_camera_probe(ici, icd); - } - - mutex_unlock(&list_lock); -} - -/* - * It is invalid to call v4l2_clk_enable() after a successful probing - * asynchronously outside of V4L2 operations, i.e. with .host_lock not held. - */ -static int soc_camera_clk_enable(struct v4l2_clk *clk) -{ - struct soc_camera_device *icd = clk->priv; - struct soc_camera_host *ici; - - if (!icd || !icd->parent) - return -ENODEV; - - ici = to_soc_camera_host(icd->parent); - - if (!try_module_get(ici->ops->owner)) - return -ENODEV; - - /* - * If a different client is currently being probed, the host will tell - * you to go - */ - return soc_camera_clock_start(ici); -} - -static void soc_camera_clk_disable(struct v4l2_clk *clk) -{ - struct soc_camera_device *icd = clk->priv; - struct soc_camera_host *ici; - - if (!icd || !icd->parent) - return; - - ici = to_soc_camera_host(icd->parent); - - soc_camera_clock_stop(ici); - - module_put(ici->ops->owner); -} - -/* - * Eventually, it would be more logical to make the respective host the clock - * owner, but then we would have to copy this struct for each ici. Besides, it - * would introduce the circular dependency problem, unless we port all client - * drivers to release the clock, when not in use. - */ -static const struct v4l2_clk_ops soc_camera_clk_ops = { - .owner = THIS_MODULE, - .enable = soc_camera_clk_enable, - .disable = soc_camera_clk_disable, -}; - -static int soc_camera_dyn_pdev(struct soc_camera_desc *sdesc, - struct soc_camera_async_client *sasc) -{ - struct platform_device *pdev; - int ret, i; - - mutex_lock(&list_lock); - i = find_first_zero_bit(device_map, MAP_MAX_NUM); - if (i < MAP_MAX_NUM) - set_bit(i, device_map); - mutex_unlock(&list_lock); - if (i >= MAP_MAX_NUM) - return -ENOMEM; - - pdev = platform_device_alloc("soc-camera-pdrv", i); - if (!pdev) - return -ENOMEM; - - ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc)); - if (ret < 0) { - platform_device_put(pdev); - return ret; - } - - sasc->pdev = pdev; - - return 0; -} - -static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_client *sasc) -{ - struct platform_device *pdev = sasc->pdev; - int ret; - - ret = platform_device_add(pdev); - if (ret < 0 || !pdev->dev.driver) - return NULL; - - return platform_get_drvdata(pdev); -} - -/* Locking: called with .host_lock held */ -static int soc_camera_probe_finish(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - sd->grp_id = soc_camera_grp_id(icd); - v4l2_set_subdev_hostdata(sd, icd); - - v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms); - - ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, - NULL, true); - if (ret < 0) - return ret; - - ret = soc_camera_add_device(icd); - if (ret < 0) { - dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); - return ret; - } - - /* At this point client .probe() should have run already */ - ret = soc_camera_init_user_formats(icd); - if (ret < 0) - goto eusrfmt; - - icd->field = V4L2_FIELD_ANY; - - ret = soc_camera_video_start(icd); - if (ret < 0) - goto evidstart; - - /* Try to improve our guess of a reasonable window format */ - if (!v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt)) { - icd->user_width = mf->width; - icd->user_height = mf->height; - icd->colorspace = mf->colorspace; - icd->field = mf->field; - } - soc_camera_remove_device(icd); - - return 0; - -evidstart: - soc_camera_free_user_formats(icd); -eusrfmt: - soc_camera_remove_device(icd); - - return ret; -} - -#ifdef CONFIG_I2C_BOARDINFO -static int soc_camera_i2c_init(struct soc_camera_device *icd, - struct soc_camera_desc *sdesc) -{ - struct soc_camera_subdev_desc *ssdd; - struct i2c_client *client; - struct soc_camera_host *ici; - struct soc_camera_host_desc *shd = &sdesc->host_desc; - struct i2c_adapter *adap; - struct v4l2_subdev *subdev; - char clk_name[V4L2_CLK_NAME_SIZE]; - int ret; - - /* First find out how we link the main client */ - if (icd->sasc) { - /* Async non-OF probing handled by the subdevice list */ - return -EPROBE_DEFER; - } - - ici = to_soc_camera_host(icd->parent); - adap = i2c_get_adapter(shd->i2c_adapter_id); - if (!adap) { - dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", - shd->i2c_adapter_id); - return -ENODEV; - } - - ssdd = kmemdup(&sdesc->subdev_desc, sizeof(*ssdd), GFP_KERNEL); - if (!ssdd) { - ret = -ENOMEM; - goto ealloc; - } - /* - * In synchronous case we request regulators ourselves in - * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try - * to allocate them again. - */ - ssdd->sd_pdata.num_regulators = 0; - ssdd->sd_pdata.regulators = NULL; - shd->board_info->platform_data = ssdd; - - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - shd->i2c_adapter_id, shd->board_info->addr); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, - shd->board_info, NULL); - if (!subdev) { - ret = -ENODEV; - goto ei2cnd; - } - - client = v4l2_get_subdevdata(subdev); - - /* Use to_i2c_client(dev) to recover the i2c client */ - icd->control = &client->dev; - - return 0; -ei2cnd: - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; -eclkreg: - kfree(ssdd); -ealloc: - i2c_put_adapter(adap); - return ret; -} - -static void soc_camera_i2c_free(struct soc_camera_device *icd) -{ - struct i2c_client *client = - to_i2c_client(to_soc_camera_control(icd)); - struct i2c_adapter *adap; - struct soc_camera_subdev_desc *ssdd; - - icd->control = NULL; - if (icd->sasc) - return; - - adap = client->adapter; - ssdd = client->dev.platform_data; - v4l2_device_unregister_subdev(i2c_get_clientdata(client)); - i2c_unregister_device(client); - i2c_put_adapter(adap); - kfree(ssdd); - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; -} - -/* - * V4L2 asynchronous notifier callbacks. They are all called under a v4l2-async - * internal global mutex, therefore cannot race against other asynchronous - * events. Until notifier->complete() (soc_camera_async_complete()) is called, - * the video device node is not registered and no V4L fops can occur. Unloading - * of the host driver also calls a v4l2-async function, so also there we're - * protected. - */ -static int soc_camera_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - if (asd == sasc->sensor && !WARN_ON(icd->control)) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* - * Only now we get subdevice-specific information like - * regulators, flags, callbacks, etc. - */ - if (client) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_subdev_desc *ssdd = - soc_camera_i2c_to_desc(client); - if (ssdd) { - memcpy(&sdesc->subdev_desc, ssdd, - sizeof(sdesc->subdev_desc)); - if (ssdd->reset) - ssdd->reset(&client->dev); - } - - icd->control = &client->dev; - } - } - - return 0; -} - -static void soc_camera_async_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - icd->control = NULL; - - if (icd->clk) { - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; - } -} - -static int soc_camera_async_complete(struct v4l2_async_notifier *notifier) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - if (to_soc_camera_control(icd)) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - mutex_lock(&list_lock); - ret = soc_camera_probe(ici, icd); - mutex_unlock(&list_lock); - if (ret < 0) - return ret; - } - - return 0; -} - -static const struct v4l2_async_notifier_operations soc_camera_async_ops = { - .bound = soc_camera_async_bound, - .unbind = soc_camera_async_unbind, - .complete = soc_camera_async_complete, -}; - -static int scan_async_group(struct soc_camera_host *ici, - struct v4l2_async_subdev **asd, unsigned int size) -{ - struct soc_camera_async_subdev *sasd; - struct soc_camera_async_client *sasc; - struct soc_camera_device *icd; - struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; - char clk_name[V4L2_CLK_NAME_SIZE]; - unsigned int i; - int ret; - - /* First look for a sensor */ - for (i = 0; i < size; i++) { - sasd = container_of(asd[i], struct soc_camera_async_subdev, asd); - if (sasd->role == SOCAM_SUBDEV_DATA_SOURCE) - break; - } - - if (i >= size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) { - /* All useless */ - dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n"); - return -ENODEV; - } - - /* Or shall this be managed by the soc-camera device? */ - sasc = devm_kzalloc(ici->v4l2_dev.dev, sizeof(*sasc), GFP_KERNEL); - if (!sasc) - return -ENOMEM; - - /* HACK: just need a != NULL */ - sdesc.host_desc.board_info = ERR_PTR(-ENODATA); - - ret = soc_camera_dyn_pdev(&sdesc, sasc); - if (ret < 0) - goto eallocpdev; - - sasc->sensor = &sasd->asd; - - icd = soc_camera_add_pdev(sasc); - if (!icd) { - ret = -ENOMEM; - goto eaddpdev; - } - - v4l2_async_notifier_init(&sasc->notifier); - - for (i = 0; i < size; i++) { - ret = v4l2_async_notifier_add_subdev(&sasc->notifier, asd[i]); - if (ret) - goto eaddasd; - } - - sasc->notifier.ops = &soc_camera_async_ops; - - icd->sasc = sasc; - icd->parent = ici->v4l2_dev.dev; - - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - sasd->asd.match.i2c.adapter_id, - sasd->asd.match.i2c.address); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); - if (!ret) - return 0; - - v4l2_clk_unregister(icd->clk); -eclkreg: - icd->clk = NULL; -eaddasd: - v4l2_async_notifier_cleanup(&sasc->notifier); - platform_device_del(sasc->pdev); -eaddpdev: - platform_device_put(sasc->pdev); -eallocpdev: - devm_kfree(ici->v4l2_dev.dev, sasc); - dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); - - return ret; -} - -static void scan_async_host(struct soc_camera_host *ici) -{ - struct v4l2_async_subdev **asd; - int j; - - for (j = 0, asd = ici->asd; ici->asd_sizes[j]; j++) { - scan_async_group(ici, asd, ici->asd_sizes[j]); - asd += ici->asd_sizes[j]; - } -} -#else -#define soc_camera_i2c_init(icd, sdesc) (-ENODEV) -#define soc_camera_i2c_free(icd) do {} while (0) -#define scan_async_host(ici) do {} while (0) -#endif - -#ifdef CONFIG_OF - -struct soc_of_info { - struct soc_camera_async_subdev sasd; - struct soc_camera_async_client sasc; - struct v4l2_async_subdev *subdev; -}; - -static int soc_of_bind(struct soc_camera_host *ici, - struct device_node *ep, - struct device_node *remote) -{ - struct soc_camera_device *icd; - struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; - struct soc_camera_async_client *sasc; - struct soc_of_info *info; - struct i2c_client *client; - char clk_name[V4L2_CLK_NAME_SIZE]; - int ret; - - /* allocate a new subdev and add match info to it */ - info = devm_kzalloc(ici->v4l2_dev.dev, sizeof(struct soc_of_info), - GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->sasd.asd.match.fwnode = of_fwnode_handle(remote); - info->sasd.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - info->subdev = &info->sasd.asd; - - /* Or shall this be managed by the soc-camera device? */ - sasc = &info->sasc; - - /* HACK: just need a != NULL */ - sdesc.host_desc.board_info = ERR_PTR(-ENODATA); - - ret = soc_camera_dyn_pdev(&sdesc, sasc); - if (ret < 0) - goto eallocpdev; - - sasc->sensor = &info->sasd.asd; - - icd = soc_camera_add_pdev(sasc); - if (!icd) { - ret = -ENOMEM; - goto eaddpdev; - } - - v4l2_async_notifier_init(&sasc->notifier); - - ret = v4l2_async_notifier_add_subdev(&sasc->notifier, info->subdev); - if (ret) { - of_node_put(remote); - goto eaddasd; - } - - sasc->notifier.ops = &soc_camera_async_ops; - - icd->sasc = sasc; - icd->parent = ici->v4l2_dev.dev; - - client = of_find_i2c_device_by_node(remote); - - if (client) - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - client->adapter->nr, client->addr); - else - v4l2_clk_name_of(clk_name, sizeof(clk_name), remote); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); - if (!ret) - return 0; - - v4l2_clk_unregister(icd->clk); -eclkreg: - icd->clk = NULL; -eaddasd: - v4l2_async_notifier_cleanup(&sasc->notifier); - platform_device_del(sasc->pdev); -eaddpdev: - platform_device_put(sasc->pdev); -eallocpdev: - devm_kfree(ici->v4l2_dev.dev, info); - dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); - - return ret; -} - -static void scan_of_host(struct soc_camera_host *ici) -{ - struct device *dev = ici->v4l2_dev.dev; - struct device_node *np = dev->of_node; - struct device_node *epn = NULL, *rem; - unsigned int i; - - for (i = 0; ; i++) { - epn = of_graph_get_next_endpoint(np, epn); - if (!epn) - break; - - rem = of_graph_get_remote_port_parent(epn); - if (!rem) { - dev_notice(dev, "no remote for %pOF\n", epn); - continue; - } - - /* so we now have a remote node to connect */ - if (!i) - soc_of_bind(ici, epn, rem); - - if (i) { - dev_err(dev, "multiple subdevices aren't supported yet!\n"); - break; - } - } - - of_node_put(epn); -} - -#else -static inline void scan_of_host(struct soc_camera_host *ici) { } -#endif - -/* Called during host-driver probe */ -static int soc_camera_probe(struct soc_camera_host *ici, - struct soc_camera_device *icd) -{ - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_host_desc *shd = &sdesc->host_desc; - struct device *control = NULL; - int ret; - - dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev)); - - /* - * Currently the subdev with the largest number of controls (13) is - * ov6550. So let's pick 16 as a hint for the control handler. Note - * that this is a hint only: too large and you waste some memory, too - * small and there is a (very) small performance hit when looking up - * controls in the internal hash. - */ - ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16); - if (ret < 0) - return ret; - - /* Must have icd->vdev before registering the device */ - ret = video_dev_create(icd); - if (ret < 0) - goto evdc; - - /* - * ..._video_start() will create a device node, video_register_device() - * itself is protected against concurrent open() calls, but we also have - * to protect our data also during client probing. - */ - - /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ - if (shd->board_info) { - ret = soc_camera_i2c_init(icd, sdesc); - if (ret < 0 && ret != -EPROBE_DEFER) - goto eadd; - } else if (!shd->add_device || !shd->del_device) { - ret = -EINVAL; - goto eadd; - } else { - ret = soc_camera_clock_start(ici); - if (ret < 0) - goto eadd; - - if (shd->module_name) - ret = request_module(shd->module_name); - - ret = shd->add_device(icd); - if (ret < 0) - goto eadddev; - - /* - * FIXME: this is racy, have to use driver-binding notification, - * when it is available - */ - control = to_soc_camera_control(icd); - if (!control || !control->driver || !dev_get_drvdata(control) || - !try_module_get(control->driver->owner)) { - shd->del_device(icd); - ret = -ENODEV; - goto enodrv; - } - } - - mutex_lock(&ici->host_lock); - ret = soc_camera_probe_finish(icd); - mutex_unlock(&ici->host_lock); - if (ret < 0) - goto efinish; - - return 0; - -efinish: - if (shd->board_info) { - soc_camera_i2c_free(icd); - } else { - shd->del_device(icd); - module_put(control->driver->owner); -enodrv: -eadddev: - soc_camera_clock_stop(ici); - } -eadd: - if (icd->vdev) { - video_device_release(icd->vdev); - icd->vdev = NULL; - } -evdc: - v4l2_ctrl_handler_free(&icd->ctrl_handler); - return ret; -} - -/* - * This is called on device_unregister, which only means we have to disconnect - * from the host, but not remove ourselves from the device list. With - * asynchronous client probing this can also be called without - * soc_camera_probe_finish() having run. Careful with clean up. - */ -static int soc_camera_remove(struct soc_camera_device *icd) -{ - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct video_device *vdev = icd->vdev; - - v4l2_ctrl_handler_free(&icd->ctrl_handler); - if (vdev) { - video_unregister_device(vdev); - icd->vdev = NULL; - } - - if (sdesc->host_desc.board_info) { - soc_camera_i2c_free(icd); - } else { - struct device *dev = to_soc_camera_control(icd); - struct device_driver *drv = dev ? dev->driver : NULL; - if (drv) { - sdesc->host_desc.del_device(icd); - module_put(drv->owner); - } - } - - if (icd->num_user_formats) - soc_camera_free_user_formats(icd); - - if (icd->clk) { - /* For the synchronous case */ - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; - } - - if (icd->sasc) - platform_device_unregister(icd->sasc->pdev); - - return 0; -} - -static int default_g_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - }; - int ret; - - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); - if (ret) - return ret; - sel->r = sdsel.r; - return 0; -} - -static int default_s_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - .flags = sel->flags, - .r = sel->r, - }; - int ret; - - ret = v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); - if (ret) - return ret; - sel->r = sdsel.r; - return 0; -} - -static int default_g_parm(struct soc_camera_device *icd, - struct v4l2_streamparm *a) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_g_parm_cap(icd->vdev, sd, a); -} - -static int default_s_parm(struct soc_camera_device *icd, - struct v4l2_streamparm *a) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_s_parm_cap(icd->vdev, sd, a); -} - -static int default_enum_framesizes(struct soc_camera_device *icd, - struct v4l2_frmsizeenum *fsize) -{ - int ret; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_subdev_frame_size_enum fse = { - .index = fsize->index, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - xlate = soc_camera_xlate_by_fourcc(icd, fsize->pixel_format); - if (!xlate) - return -EINVAL; - fse.code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); - if (ret < 0) - return ret; - - if (fse.min_width == fse.max_width && - fse.min_height == fse.max_height) { - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.min_width; - fsize->discrete.height = fse.min_height; - return 0; - } - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = fse.min_width; - fsize->stepwise.max_width = fse.max_width; - fsize->stepwise.min_height = fse.min_height; - fsize->stepwise.max_height = fse.max_height; - fsize->stepwise.step_width = 1; - fsize->stepwise.step_height = 1; - return 0; -} - -int soc_camera_host_register(struct soc_camera_host *ici) -{ - struct soc_camera_host *ix; - int ret; - - if (!ici || !ici->ops || - !ici->ops->try_fmt || - !ici->ops->set_fmt || - !ici->ops->set_bus_param || - !ici->ops->querycap || - !ici->ops->init_videobuf2 || - !ici->ops->poll || - !ici->v4l2_dev.dev) - return -EINVAL; - - if (!ici->ops->set_selection) - ici->ops->set_selection = default_s_selection; - if (!ici->ops->get_selection) - ici->ops->get_selection = default_g_selection; - if (!ici->ops->set_parm) - ici->ops->set_parm = default_s_parm; - if (!ici->ops->get_parm) - ici->ops->get_parm = default_g_parm; - if (!ici->ops->enum_framesizes) - ici->ops->enum_framesizes = default_enum_framesizes; - - mutex_lock(&list_lock); - list_for_each_entry(ix, &hosts, list) { - if (ix->nr == ici->nr) { - ret = -EBUSY; - goto edevreg; - } - } - - ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev); - if (ret < 0) - goto edevreg; - - list_add_tail(&ici->list, &hosts); - mutex_unlock(&list_lock); - - mutex_init(&ici->host_lock); - mutex_init(&ici->clk_lock); - - if (ici->v4l2_dev.dev->of_node) - scan_of_host(ici); - else if (ici->asd_sizes) - /* - * No OF, host with a list of subdevices. Don't try to mix - * modes by initialising some groups statically and some - * dynamically! - */ - scan_async_host(ici); - else - /* Legacy: static platform devices from board data */ - scan_add_host(ici); - - return 0; - -edevreg: - mutex_unlock(&list_lock); - return ret; -} -EXPORT_SYMBOL(soc_camera_host_register); - -/* Unregister all clients! */ -void soc_camera_host_unregister(struct soc_camera_host *ici) -{ - struct soc_camera_device *icd, *tmp; - struct soc_camera_async_client *sasc; - LIST_HEAD(notifiers); - - mutex_lock(&list_lock); - list_del(&ici->list); - list_for_each_entry(icd, &devices, list) - if (icd->iface == ici->nr && icd->sasc) { - /* as long as we hold the device, sasc won't be freed */ - get_device(icd->pdev); - list_add(&icd->sasc->list, ¬ifiers); - } - mutex_unlock(&list_lock); - - list_for_each_entry(sasc, ¬ifiers, list) { - /* Must call unlocked to avoid AB-BA dead-lock */ - v4l2_async_notifier_unregister(&sasc->notifier); - v4l2_async_notifier_cleanup(&sasc->notifier); - put_device(&sasc->pdev->dev); - } - - mutex_lock(&list_lock); - - list_for_each_entry_safe(icd, tmp, &devices, list) - if (icd->iface == ici->nr) - soc_camera_remove(icd); - - mutex_unlock(&list_lock); - - v4l2_device_unregister(&ici->v4l2_dev); -} -EXPORT_SYMBOL(soc_camera_host_unregister); - -/* Image capture device */ -static int soc_camera_device_register(struct soc_camera_device *icd) -{ - struct soc_camera_device *ix; - int num = -1, i; - - mutex_lock(&list_lock); - for (i = 0; i < 256 && num < 0; i++) { - num = i; - /* Check if this index is available on this interface */ - list_for_each_entry(ix, &devices, list) { - if (ix->iface == icd->iface && ix->devnum == i) { - num = -1; - break; - } - } - } - - if (num < 0) { - /* - * ok, we have 256 cameras on this host... - * man, stay reasonable... - */ - mutex_unlock(&list_lock); - return -ENOMEM; - } - - icd->devnum = num; - icd->use_count = 0; - icd->host_priv = NULL; - - /* - * Dynamically allocated devices set the bit earlier, but it doesn't hurt setting - * it again - */ - i = to_platform_device(icd->pdev)->id; - if (i < 0) - /* One static (legacy) soc-camera platform device */ - i = 0; - if (i >= MAP_MAX_NUM) { - mutex_unlock(&list_lock); - return -EBUSY; - } - set_bit(i, device_map); - list_add_tail(&icd->list, &devices); - mutex_unlock(&list_lock); - - return 0; -} - -static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { - .vidioc_querycap = soc_camera_querycap, - .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap, - .vidioc_enum_input = soc_camera_enum_input, - .vidioc_g_input = soc_camera_g_input, - .vidioc_s_input = soc_camera_s_input, - .vidioc_s_std = soc_camera_s_std, - .vidioc_g_std = soc_camera_g_std, - .vidioc_enum_framesizes = soc_camera_enum_framesizes, - .vidioc_reqbufs = soc_camera_reqbufs, - .vidioc_querybuf = soc_camera_querybuf, - .vidioc_qbuf = soc_camera_qbuf, - .vidioc_dqbuf = soc_camera_dqbuf, - .vidioc_create_bufs = soc_camera_create_bufs, - .vidioc_prepare_buf = soc_camera_prepare_buf, - .vidioc_expbuf = soc_camera_expbuf, - .vidioc_streamon = soc_camera_streamon, - .vidioc_streamoff = soc_camera_streamoff, - .vidioc_g_selection = soc_camera_g_selection, - .vidioc_s_selection = soc_camera_s_selection, - .vidioc_g_parm = soc_camera_g_parm, - .vidioc_s_parm = soc_camera_s_parm, -}; - -static int video_dev_create(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct video_device *vdev = video_device_alloc(); - - if (!vdev) - return -ENOMEM; - - strscpy(vdev->name, ici->drv_name, sizeof(vdev->name)); - - vdev->v4l2_dev = &ici->v4l2_dev; - vdev->fops = &soc_camera_fops; - vdev->ioctl_ops = &soc_camera_ioctl_ops; - vdev->release = video_device_release; - vdev->ctrl_handler = &icd->ctrl_handler; - vdev->lock = &ici->host_lock; - - icd->vdev = vdev; - - return 0; -} - -/* - * Called from soc_camera_probe() above with .host_lock held - */ -static int soc_camera_video_start(struct soc_camera_device *icd) -{ - const struct device_type *type = icd->vdev->dev.type; - int ret; - - if (!icd->parent) - return -ENODEV; - - video_set_drvdata(icd->vdev, icd); - if (icd->vdev->tvnorms == 0) { - /* disable the STD API if there are no tvnorms defined */ - v4l2_disable_ioctl(icd->vdev, VIDIOC_G_STD); - v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD); - v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD); - } - ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1); - if (ret < 0) { - dev_err(icd->pdev, "video_register_device failed: %d\n", ret); - return ret; - } - - /* Restore device type, possibly set by the subdevice driver */ - icd->vdev->dev.type = type; - - return 0; -} - -static int soc_camera_pdrv_probe(struct platform_device *pdev) -{ - struct soc_camera_desc *sdesc = pdev->dev.platform_data; - struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; - struct soc_camera_device *icd; - int ret; - - if (!sdesc) - return -EINVAL; - - icd = devm_kzalloc(&pdev->dev, sizeof(*icd), GFP_KERNEL); - if (!icd) - return -ENOMEM; - - /* - * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below - * regulator allocation is a dummy. They are actually requested by the - * subdevice driver, using soc_camera_power_init(). Also note, that in - * that case regulators are attached to the I2C device and not to the - * camera platform device. - */ - ret = devm_regulator_bulk_get(&pdev->dev, ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (ret < 0) - return ret; - - icd->iface = sdesc->host_desc.bus_id; - icd->sdesc = sdesc; - icd->pdev = &pdev->dev; - platform_set_drvdata(pdev, icd); - - icd->user_width = DEFAULT_WIDTH; - icd->user_height = DEFAULT_HEIGHT; - - return soc_camera_device_register(icd); -} - -/* - * Only called on rmmod for each platform device, since they are not - * hot-pluggable. Now we know, that all our users - hosts and devices have - * been unloaded already - */ -static int soc_camera_pdrv_remove(struct platform_device *pdev) -{ - struct soc_camera_device *icd = platform_get_drvdata(pdev); - int i; - - if (!icd) - return -EINVAL; - - i = pdev->id; - if (i < 0) - i = 0; - - /* - * In synchronous mode with static platform devices this is called in a - * loop from drivers/base/dd.c::driver_detach(), no parallel execution, - * no need to lock. In asynchronous case the caller - - * soc_camera_host_unregister() - already holds the lock - */ - if (test_bit(i, device_map)) { - clear_bit(i, device_map); - list_del(&icd->list); - } - - return 0; -} - -static struct platform_driver __refdata soc_camera_pdrv = { - .probe = soc_camera_pdrv_probe, - .remove = soc_camera_pdrv_remove, - .driver = { - .name = "soc-camera-pdrv", - }, -}; - -module_platform_driver(soc_camera_pdrv); - -MODULE_DESCRIPTION("Image capture bus driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:soc-camera-pdrv"); diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c deleted file mode 100644 index be74008ec0ca..000000000000 --- a/drivers/media/platform/soc_camera/soc_mediabus.c +++ /dev/null @@ -1,533 +0,0 @@ -/* - * soc-camera media bus helper routines - * - * Copyright (C) 2009, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include - -#include -#include -#include - -static const struct soc_mbus_lookup mbus_fmt[] = { -{ - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVYU, - .name = "YVYU", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_VYUY, - .name = "VYUY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555, - .name = "RGB555", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555X, - .name = "RGB555X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565, - .name = "RGB565", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565X, - .name = "RGB565X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB666_1X18, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB666/32bpp", - .bits_per_sample = 18, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_1X24, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 24, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_BE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .name = "Bayer 8 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_Y8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_GREY, - .name = "Grey", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_Y10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_Y10, - .name = "Grey 10bit", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADLO, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADLO, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_JPEG_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_JPEG, - .name = "JPEG", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_VARIABLE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB444, - .name = "RGB444", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YUYV8_1_5X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUV420, - .name = "YUYV 4:2:0", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_1_5X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVU420, - .name = "YVYU 4:2:0", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_VYUY8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_VYUY, - .name = "VYUY 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YUYV8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVYU, - .name = "YVYU 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .name = "Bayer 8 GRBG", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG10DPCM8, - .name = "Bayer 10 BGGR DPCM 8", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .name = "Bayer 10 GBRG", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .name = "Bayer 10 GRBG", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .name = "Bayer 10 RGGB", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .name = "Bayer 12 BGGR", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .name = "Bayer 12 GBRG", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .name = "Bayer 12 GRBG", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .name = "Bayer 12 RGGB", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, -}; - -int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, - unsigned int *numerator, unsigned int *denominator) -{ - switch (mf->packing) { - case SOC_MBUS_PACKING_NONE: - case SOC_MBUS_PACKING_EXTEND16: - *numerator = 1; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_EXTEND32: - *numerator = 1; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - *numerator = 2; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_1_5X8: - *numerator = 3; - *denominator = 2; - return 0; - case SOC_MBUS_PACKING_VARIABLE: - *numerator = 0; - *denominator = 1; - return 0; - } - return -EINVAL; -} -EXPORT_SYMBOL(soc_mbus_samples_per_pixel); - -s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) -{ - if (mf->layout != SOC_MBUS_LAYOUT_PACKED) - return width * mf->bits_per_sample / 8; - - switch (mf->packing) { - case SOC_MBUS_PACKING_NONE: - return width * mf->bits_per_sample / 8; - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - case SOC_MBUS_PACKING_EXTEND16: - return width * 2; - case SOC_MBUS_PACKING_1_5X8: - return width * 3 / 2; - case SOC_MBUS_PACKING_VARIABLE: - return 0; - case SOC_MBUS_PACKING_EXTEND32: - return width * 4; - } - return -EINVAL; -} -EXPORT_SYMBOL(soc_mbus_bytes_per_line); - -s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, - u32 bytes_per_line, u32 height) -{ - if (mf->layout == SOC_MBUS_LAYOUT_PACKED) - return bytes_per_line * height; - - switch (mf->packing) { - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - return bytes_per_line * height * 2; - case SOC_MBUS_PACKING_1_5X8: - return bytes_per_line * height * 3 / 2; - default: - return -EINVAL; - } -} -EXPORT_SYMBOL(soc_mbus_image_size); - -const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( - u32 code, - const struct soc_mbus_lookup *lookup, - int n) -{ - int i; - - for (i = 0; i < n; i++) - if (lookup[i].code == code) - return &lookup[i].fmt; - - return NULL; -} -EXPORT_SYMBOL(soc_mbus_find_fmtdesc); - -const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( - u32 code) -{ - return soc_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt)); -} -EXPORT_SYMBOL(soc_mbus_get_fmtdesc); - -unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, - unsigned int flags) -{ - unsigned long common_flags; - bool hsync = true, vsync = true, pclk, data, mode; - bool mipi_lanes, mipi_clock; - - common_flags = cfg->flags & flags; - - switch (cfg->type) { - case V4L2_MBUS_PARALLEL: - hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_LOW); - vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_LOW); - /* fall through */ - case V4L2_MBUS_BT656: - pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING); - data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_LOW); - mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE); - return (!hsync || !vsync || !pclk || !data || !mode) ? - 0 : common_flags; - case V4L2_MBUS_CSI2_DPHY: - mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES; - mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK); - return (!mipi_lanes || !mipi_clock) ? 0 : common_flags; - default: - WARN_ON(1); - return -EINVAL; - } - return 0; -} -EXPORT_SYMBOL(soc_mbus_config_compatible); - -static int __init soc_mbus_init(void) -{ - return 0; -} - -static void __exit soc_mbus_exit(void) -{ -} - -module_init(soc_mbus_init); -module_exit(soc_mbus_exit); - -MODULE_DESCRIPTION("soc-camera media bus interface"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 19cadd17e542..7c3f443f2735 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -41,4 +41,6 @@ source "drivers/staging/media/zoran/Kconfig" source "drivers/staging/media/ipu3/Kconfig" +source "drivers/staging/media/soc_camera/Kconfig" + endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index edde1960b030..9c1bb862f5c9 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip/vpu/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ +obj-$(CONFIG_SOC_CAMERA) += soc_camera/ diff --git a/drivers/staging/media/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig new file mode 100644 index 000000000000..ebd78cebd4ec --- /dev/null +++ b/drivers/staging/media/soc_camera/Kconfig @@ -0,0 +1,37 @@ +config SOC_CAMERA + tristate "SoC camera support" + depends on VIDEO_V4L2 && HAS_DMA && I2C + select VIDEOBUF2_CORE + help + SoC Camera is a common API to several cameras, not connecting + over a bus like PCI or USB. For example some i2c camera connected + directly to the data bus of an SoC. +comment "soc_camera sensor drivers" + +config SOC_CAMERA_MT9M111 + tristate "legacy soc_camera mt9m111, mt9m112 and mt9m131 support" + depends on SOC_CAMERA && I2C + select VIDEO_MT9M111 + help + This driver supports MT9M111, MT9M112 and MT9M131 cameras from + Micron/Aptina. + This is the legacy configuration which shouldn't be used anymore, + while VIDEO_MT9M111 should be used instead. + +config SOC_CAMERA_MT9V022 + tristate "mt9v022 and mt9v024 support" + depends on SOC_CAMERA && I2C + help + This driver supports MT9V022 cameras from Micron + +config SOC_CAMERA_OV5642 + tristate "ov5642 camera support" + depends on SOC_CAMERA && I2C + help + This is a V4L2 camera driver for the OmniVision OV5642 sensor + +config SOC_CAMERA_OV9740 + tristate "ov9740 camera support" + depends on SOC_CAMERA && I2C + help + This is a ov9740 camera driver diff --git a/drivers/staging/media/soc_camera/Makefile b/drivers/staging/media/soc_camera/Makefile new file mode 100644 index 000000000000..e03450cee524 --- /dev/null +++ b/drivers/staging/media/soc_camera/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o +obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o +obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o +obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o diff --git a/drivers/staging/media/soc_camera/soc_camera.c b/drivers/staging/media/soc_camera/soc_camera.c new file mode 100644 index 000000000000..21034339cdcb --- /dev/null +++ b/drivers/staging/media/soc_camera/soc_camera.c @@ -0,0 +1,2170 @@ +/* + * camera image capture (abstract) bus driver + * + * Copyright (C) 2008, Guennadi Liakhovetski + * + * This driver provides an interface between platform-specific camera + * busses and camera devices. It should be used if the camera is + * connected not over a "proper" bus like PCI or USB, but over a + * special bus, like, for example, the Quick Capture interface on PXA270 + * SoCs. Later it should also be used for i.MX31 SoCs from Freescale. + * It can handle multiple cameras and / or multiple busses, which can + * be used, e.g., in stereo-vision applications. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Default to VGA resolution */ +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + +#define MAP_MAX_NUM 32 +static DECLARE_BITMAP(device_map, MAP_MAX_NUM); +static LIST_HEAD(hosts); +static LIST_HEAD(devices); +/* + * Protects lists and bitmaps of hosts and devices. + * Lock nesting: Ok to take ->host_lock under list_lock. + */ +static DEFINE_MUTEX(list_lock); + +struct soc_camera_async_client { + struct v4l2_async_subdev *sensor; + struct v4l2_async_notifier notifier; + struct platform_device *pdev; + struct list_head list; /* needed for clean up */ +}; + +static int soc_camera_video_start(struct soc_camera_device *icd); +static int video_dev_create(struct soc_camera_device *icd); + +int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd, + struct v4l2_clk *clk) +{ + int ret; + bool clock_toggle; + + if (clk && (!ssdd->unbalanced_power || + !test_and_set_bit(0, &ssdd->clock_state))) { + ret = v4l2_clk_enable(clk); + if (ret < 0) { + dev_err(dev, "Cannot enable clock: %d\n", ret); + return ret; + } + clock_toggle = true; + } else { + clock_toggle = false; + } + + ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators, + ssdd->sd_pdata.regulators); + if (ret < 0) { + dev_err(dev, "Cannot enable regulators\n"); + goto eregenable; + } + + if (ssdd->power) { + ret = ssdd->power(dev, 1); + if (ret < 0) { + dev_err(dev, + "Platform failed to power-on the camera.\n"); + goto epwron; + } + } + + return 0; + +epwron: + regulator_bulk_disable(ssdd->sd_pdata.num_regulators, + ssdd->sd_pdata.regulators); +eregenable: + if (clock_toggle) + v4l2_clk_disable(clk); + + return ret; +} +EXPORT_SYMBOL(soc_camera_power_on); + +int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd, + struct v4l2_clk *clk) +{ + int ret = 0; + int err; + + if (ssdd->power) { + err = ssdd->power(dev, 0); + if (err < 0) { + dev_err(dev, + "Platform failed to power-off the camera.\n"); + ret = err; + } + } + + err = regulator_bulk_disable(ssdd->sd_pdata.num_regulators, + ssdd->sd_pdata.regulators); + if (err < 0) { + dev_err(dev, "Cannot disable regulators\n"); + ret = ret ? : err; + } + + if (clk && (!ssdd->unbalanced_power || test_and_clear_bit(0, &ssdd->clock_state))) + v4l2_clk_disable(clk); + + return ret; +} +EXPORT_SYMBOL(soc_camera_power_off); + +int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd) +{ + /* Should not have any effect in synchronous case */ + return devm_regulator_bulk_get(dev, ssdd->sd_pdata.num_regulators, + ssdd->sd_pdata.regulators); +} +EXPORT_SYMBOL(soc_camera_power_init); + +static int __soc_camera_power_on(struct soc_camera_device *icd) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; + + ret = v4l2_subdev_call(sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + return 0; +} + +static int __soc_camera_power_off(struct soc_camera_device *icd) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; + + ret = v4l2_subdev_call(sd, core, s_power, 0); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + return 0; +} + +static int soc_camera_clock_start(struct soc_camera_host *ici) +{ + int ret; + + if (!ici->ops->clock_start) + return 0; + + mutex_lock(&ici->clk_lock); + ret = ici->ops->clock_start(ici); + mutex_unlock(&ici->clk_lock); + + return ret; +} + +static void soc_camera_clock_stop(struct soc_camera_host *ici) +{ + if (!ici->ops->clock_stop) + return; + + mutex_lock(&ici->clk_lock); + ici->ops->clock_stop(ici); + mutex_unlock(&ici->clk_lock); +} + +const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < icd->num_user_formats; i++) + if (icd->user_formats[i].host_fmt->fourcc == fourcc) + return icd->user_formats + i; + return NULL; +} +EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); + +/** + * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags + * @ssdd: camera platform parameters + * @cfg: media bus configuration + * @return: resulting flags + */ +unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, + const struct v4l2_mbus_config *cfg) +{ + unsigned long f, flags = cfg->flags; + + /* If only one of the two polarities is supported, switch to the opposite */ + if (ssdd->flags & SOCAM_SENSOR_INVERT_HSYNC) { + f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW); + if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW) + flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW; + } + + if (ssdd->flags & SOCAM_SENSOR_INVERT_VSYNC) { + f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW); + if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW) + flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW; + } + + if (ssdd->flags & SOCAM_SENSOR_INVERT_PCLK) { + f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING); + if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING) + flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; + } + + return flags; +} +EXPORT_SYMBOL(soc_camera_apply_board_flags); + +#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \ + ((x) >> 24) & 0xff + +static int soc_camera_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n", + pixfmtstr(pix->pixelformat), pix->width, pix->height); + + if (pix->pixelformat != V4L2_PIX_FMT_JPEG && + !(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) { + pix->bytesperline = 0; + pix->sizeimage = 0; + } + + ret = ici->ops->try_fmt(icd, f); + if (ret < 0) + return ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) + return -EINVAL; + + ret = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt); + if (ret < 0) + return ret; + + pix->bytesperline = max_t(u32, pix->bytesperline, ret); + + ret = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline, + pix->height); + if (ret < 0) + return ret; + + pix->sizeimage = max_t(u32, pix->sizeimage, ret); + + return 0; +} + +static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_device *icd = file->private_data; + + WARN_ON(priv != file->private_data); + + /* Only single-plane capture is supported so far */ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* limit format to hardware capabilities */ + return soc_camera_try_fmt(icd, f); +} + +static int soc_camera_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct soc_camera_device *icd = file->private_data; + + if (inp->index != 0) + return -EINVAL; + + /* default is camera */ + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = icd->vdev->tvnorms; + strscpy(inp->name, "Camera", sizeof(inp->name)); + + return 0; +} + +static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + + return 0; +} + +static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_subdev_call(sd, video, s_std, a); +} + +static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_subdev_call(sd, video, g_std, a); +} + +static int soc_camera_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + return ici->ops->enum_framesizes(icd, fsize); +} + +static int soc_camera_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + int ret; + struct soc_camera_device *icd = file->private_data; + + WARN_ON(priv != file->private_data); + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + + ret = vb2_reqbufs(&icd->vb2_vidq, p); + if (!ret) + icd->streamer = p->count ? file : NULL; + return ret; +} + +static int soc_camera_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_device *icd = file->private_data; + + WARN_ON(priv != file->private_data); + + return vb2_querybuf(&icd->vb2_vidq, p); +} + +static int soc_camera_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_device *icd = file->private_data; + + WARN_ON(priv != file->private_data); + + if (icd->streamer != file) + return -EBUSY; + + return vb2_qbuf(&icd->vb2_vidq, NULL, p); +} + +static int soc_camera_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_device *icd = file->private_data; + + WARN_ON(priv != file->private_data); + + if (icd->streamer != file) + return -EBUSY; + + return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK); +} + +static int soc_camera_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *create) +{ + struct soc_camera_device *icd = file->private_data; + int ret; + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + + ret = vb2_create_bufs(&icd->vb2_vidq, create); + if (!ret) + icd->streamer = file; + return ret; +} + +static int soc_camera_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct soc_camera_device *icd = file->private_data; + + return vb2_prepare_buf(&icd->vb2_vidq, NULL, b); +} + +static int soc_camera_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *p) +{ + struct soc_camera_device *icd = file->private_data; + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + return vb2_expbuf(&icd->vb2_vidq, p); +} + +/* Always entered with .host_lock held */ +static int soc_camera_init_user_formats(struct soc_camera_device *icd) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + unsigned int i, fmts = 0, raw_fmts = 0; + int ret; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { + raw_fmts++; + code.index++; + } + + if (!ici->ops->get_formats) + /* + * Fallback mode - the host will have to serve all + * sensor-provided formats one-to-one to the user + */ + fmts = raw_fmts; + else + /* + * First pass - only count formats this host-sensor + * configuration can provide + */ + for (i = 0; i < raw_fmts; i++) { + ret = ici->ops->get_formats(icd, i, NULL); + if (ret < 0) + return ret; + fmts += ret; + } + + if (!fmts) + return -ENXIO; + + icd->user_formats = + vmalloc(array_size(fmts, + sizeof(struct soc_camera_format_xlate))); + if (!icd->user_formats) + return -ENOMEM; + + dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts); + + /* Second pass - actually fill data formats */ + fmts = 0; + for (i = 0; i < raw_fmts; i++) + if (!ici->ops->get_formats) { + code.index = i; + v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); + icd->user_formats[fmts].host_fmt = + soc_mbus_get_fmtdesc(code.code); + if (icd->user_formats[fmts].host_fmt) + icd->user_formats[fmts++].code = code.code; + } else { + ret = ici->ops->get_formats(icd, i, + &icd->user_formats[fmts]); + if (ret < 0) + goto egfmt; + fmts += ret; + } + + icd->num_user_formats = fmts; + icd->current_fmt = &icd->user_formats[0]; + + return 0; + +egfmt: + vfree(icd->user_formats); + return ret; +} + +/* Always entered with .host_lock held */ +static void soc_camera_free_user_formats(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + if (ici->ops->put_formats) + ici->ops->put_formats(icd); + icd->current_fmt = NULL; + icd->num_user_formats = 0; + vfree(icd->user_formats); + icd->user_formats = NULL; +} + +/* Called with .vb_lock held, or from the first open(2), see comment there */ +static int soc_camera_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n", + pixfmtstr(pix->pixelformat), pix->width, pix->height); + + /* We always call try_fmt() before set_fmt() or set_selection() */ + ret = soc_camera_try_fmt(icd, f); + if (ret < 0) + return ret; + + ret = ici->ops->set_fmt(icd, f); + if (ret < 0) { + return ret; + } else if (!icd->current_fmt || + icd->current_fmt->host_fmt->fourcc != pix->pixelformat) { + dev_err(icd->pdev, + "Host driver hasn't set up current format correctly!\n"); + return -EINVAL; + } + + icd->user_width = pix->width; + icd->user_height = pix->height; + icd->bytesperline = pix->bytesperline; + icd->sizeimage = pix->sizeimage; + icd->colorspace = pix->colorspace; + icd->field = pix->field; + + dev_dbg(icd->pdev, "set width: %d height: %d\n", + icd->user_width, icd->user_height); + + /* set physical bus parameters */ + return ici->ops->set_bus_param(icd); +} + +static int soc_camera_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; + + if (ici->icd) + return -EBUSY; + + if (!icd->clk) { + ret = soc_camera_clock_start(ici); + if (ret < 0) + return ret; + } + + if (ici->ops->add) { + ret = ici->ops->add(icd); + if (ret < 0) + goto eadd; + } + + ici->icd = icd; + + return 0; + +eadd: + if (!icd->clk) + soc_camera_clock_stop(ici); + return ret; +} + +static void soc_camera_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + if (WARN_ON(icd != ici->icd)) + return; + + if (ici->ops->remove) + ici->ops->remove(icd); + if (!icd->clk) + soc_camera_clock_stop(ici); + ici->icd = NULL; +} + +static int soc_camera_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct soc_camera_device *icd; + struct soc_camera_host *ici; + int ret; + + /* + * Don't mess with the host during probe: wait until the loop in + * scan_add_host() completes. Also protect against a race with + * soc_camera_host_unregister(). + */ + if (mutex_lock_interruptible(&list_lock)) + return -ERESTARTSYS; + + if (!vdev || !video_is_registered(vdev)) { + mutex_unlock(&list_lock); + return -ENODEV; + } + + icd = video_get_drvdata(vdev); + ici = to_soc_camera_host(icd->parent); + + ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV; + mutex_unlock(&list_lock); + + if (ret < 0) { + dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); + return ret; + } + + if (!to_soc_camera_control(icd)) { + /* No device driver attached */ + ret = -ENODEV; + goto econtrol; + } + + if (mutex_lock_interruptible(&ici->host_lock)) { + ret = -ERESTARTSYS; + goto elockhost; + } + icd->use_count++; + + /* Now we really have to activate the camera */ + if (icd->use_count == 1) { + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + /* Restore parameters before the last close() per V4L2 API */ + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = icd->user_width, + .height = icd->user_height, + .field = icd->field, + .colorspace = icd->colorspace, + .pixelformat = + icd->current_fmt->host_fmt->fourcc, + }, + }; + + /* The camera could have been already on, try to reset */ + if (sdesc->subdev_desc.reset) + if (icd->control) + sdesc->subdev_desc.reset(icd->control); + + ret = soc_camera_add_device(icd); + if (ret < 0) { + dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); + goto eiciadd; + } + + ret = __soc_camera_power_on(icd); + if (ret < 0) + goto epower; + + pm_runtime_enable(&icd->vdev->dev); + ret = pm_runtime_resume(&icd->vdev->dev); + if (ret < 0 && ret != -ENOSYS) + goto eresume; + + /* + * Try to configure with default parameters. Notice: this is the + * very first open, so, we cannot race against other calls, + * apart from someone else calling open() simultaneously, but + * .host_lock is protecting us against it. + */ + ret = soc_camera_set_fmt(icd, &f); + if (ret < 0) + goto esfmt; + + ret = ici->ops->init_videobuf2(&icd->vb2_vidq, icd); + if (ret < 0) + goto einitvb; + v4l2_ctrl_handler_setup(&icd->ctrl_handler); + } + mutex_unlock(&ici->host_lock); + + file->private_data = icd; + dev_dbg(icd->pdev, "camera device open\n"); + + return 0; + + /* + * All errors are entered with the .host_lock held, first four also + * with use_count == 1 + */ +einitvb: +esfmt: + pm_runtime_disable(&icd->vdev->dev); +eresume: + __soc_camera_power_off(icd); +epower: + soc_camera_remove_device(icd); +eiciadd: + icd->use_count--; + mutex_unlock(&ici->host_lock); +elockhost: +econtrol: + module_put(ici->ops->owner); + + return ret; +} + +static int soc_camera_close(struct file *file) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + mutex_lock(&ici->host_lock); + if (icd->streamer == file) { + if (ici->ops->init_videobuf2) + vb2_queue_release(&icd->vb2_vidq); + icd->streamer = NULL; + } + icd->use_count--; + if (!icd->use_count) { + pm_runtime_suspend(&icd->vdev->dev); + pm_runtime_disable(&icd->vdev->dev); + + __soc_camera_power_off(icd); + + soc_camera_remove_device(icd); + } + + mutex_unlock(&ici->host_lock); + + module_put(ici->ops->owner); + + dev_dbg(icd->pdev, "camera device close\n"); + + return 0; +} + +static ssize_t soc_camera_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + dev_dbg(icd->pdev, "read called, buf %p\n", buf); + + if (ici->ops->init_videobuf2 && icd->vb2_vidq.io_modes & VB2_READ) + return vb2_read(&icd->vb2_vidq, buf, count, ppos, + file->f_flags & O_NONBLOCK); + + dev_err(icd->pdev, "camera device read not implemented\n"); + + return -EINVAL; +} + +static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int err; + + dev_dbg(icd->pdev, "mmap called, vma=%p\n", vma); + + if (icd->streamer != file) + return -EBUSY; + + if (mutex_lock_interruptible(&ici->host_lock)) + return -ERESTARTSYS; + err = vb2_mmap(&icd->vb2_vidq, vma); + mutex_unlock(&ici->host_lock); + + dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, + err); + + return err; +} + +static __poll_t soc_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + __poll_t res = EPOLLERR; + + if (icd->streamer != file) + return EPOLLERR; + + mutex_lock(&ici->host_lock); + res = ici->ops->poll(file, pt); + mutex_unlock(&ici->host_lock); + return res; +} + +static const struct v4l2_file_operations soc_camera_fops = { + .owner = THIS_MODULE, + .open = soc_camera_open, + .release = soc_camera_close, + .unlocked_ioctl = video_ioctl2, + .read = soc_camera_read, + .mmap = soc_camera_mmap, + .poll = soc_camera_poll, +}; + +static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_device *icd = file->private_data; + int ret; + + WARN_ON(priv != file->private_data); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type); + return -EINVAL; + } + + if (icd->streamer && icd->streamer != file) + return -EBUSY; + + if (vb2_is_streaming(&icd->vb2_vidq)) { + dev_err(icd->pdev, "S_FMT denied: queue initialised\n"); + return -EBUSY; + } + + ret = soc_camera_set_fmt(icd, f); + + if (!ret && !icd->streamer) + icd->streamer = file; + + return ret; +} + +static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct soc_camera_device *icd = file->private_data; + const struct soc_mbus_pixelfmt *format; + + WARN_ON(priv != file->private_data); + + if (f->index >= icd->num_user_formats) + return -EINVAL; + + format = icd->user_formats[f->index].host_fmt; + + if (format->name) + strscpy(f->description, format->name, sizeof(f->description)); + f->pixelformat = format->fourcc; + return 0; +} + +static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_pix_format *pix = &f->fmt.pix; + + WARN_ON(priv != file->private_data); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pix->width = icd->user_width; + pix->height = icd->user_height; + pix->bytesperline = icd->bytesperline; + pix->sizeimage = icd->sizeimage; + pix->field = icd->field; + pix->pixelformat = icd->current_fmt->host_fmt->fourcc; + pix->colorspace = icd->colorspace; + dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n", + icd->current_fmt->host_fmt->fourcc); + return 0; +} + +static int soc_camera_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + WARN_ON(priv != file->private_data); + + strscpy(cap->driver, ici->drv_name, sizeof(cap->driver)); + return ici->ops->querycap(ici, cap); +} + +static int soc_camera_streamon(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; + + WARN_ON(priv != file->private_data); + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (icd->streamer != file) + return -EBUSY; + + /* This calls buf_queue from host driver's videobuf2_queue_ops */ + ret = vb2_streamon(&icd->vb2_vidq, i); + if (!ret) + v4l2_subdev_call(sd, video, s_stream, 1); + + return ret; +} + +static int soc_camera_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; + + WARN_ON(priv != file->private_data); + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (icd->streamer != file) + return -EBUSY; + + /* + * This calls buf_release from host driver's videobuf2_queue_ops for all + * remaining buffers. When the last buffer is freed, stop capture + */ + ret = vb2_streamoff(&icd->vb2_vidq, i); + + v4l2_subdev_call(sd, video, s_stream, 0); + + return ret; +} + +static int soc_camera_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + /* With a wrong type no need to try to fall back to cropping */ + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return ici->ops->get_selection(icd, s); +} + +static int soc_camera_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; + + /* In all these cases cropping emulation will not help */ + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + (s->target != V4L2_SEL_TGT_COMPOSE && + s->target != V4L2_SEL_TGT_CROP)) + return -EINVAL; + + if (s->target == V4L2_SEL_TGT_COMPOSE) { + /* No output size change during a running capture! */ + if (vb2_is_streaming(&icd->vb2_vidq) && + (icd->user_width != s->r.width || + icd->user_height != s->r.height)) + return -EBUSY; + + /* + * Only one user is allowed to change the output format, touch + * buffers, start / stop streaming, poll for data + */ + if (icd->streamer && icd->streamer != file) + return -EBUSY; + } + + if (s->target == V4L2_SEL_TGT_CROP && + vb2_is_streaming(&icd->vb2_vidq) && + ici->ops->set_liveselection) + ret = ici->ops->set_liveselection(icd, s); + else + ret = ici->ops->set_selection(icd, s); + if (!ret && + s->target == V4L2_SEL_TGT_COMPOSE) { + icd->user_width = s->r.width; + icd->user_height = s->r.height; + if (!icd->streamer) + icd->streamer = file; + } + + return ret; +} + +static int soc_camera_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + if (ici->ops->get_parm) + return ici->ops->get_parm(icd, a); + + return -ENOIOCTLCMD; +} + +static int soc_camera_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct soc_camera_device *icd = file->private_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + if (ici->ops->set_parm) + return ici->ops->set_parm(icd, a); + + return -ENOIOCTLCMD; +} + +static int soc_camera_probe(struct soc_camera_host *ici, + struct soc_camera_device *icd); + +/* So far this function cannot fail */ +static void scan_add_host(struct soc_camera_host *ici) +{ + struct soc_camera_device *icd; + + mutex_lock(&list_lock); + + list_for_each_entry(icd, &devices, list) + if (icd->iface == ici->nr) { + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; + + /* The camera could have been already on, try to reset */ + if (ssdd->reset) + if (icd->control) + ssdd->reset(icd->control); + + icd->parent = ici->v4l2_dev.dev; + + /* Ignore errors */ + soc_camera_probe(ici, icd); + } + + mutex_unlock(&list_lock); +} + +/* + * It is invalid to call v4l2_clk_enable() after a successful probing + * asynchronously outside of V4L2 operations, i.e. with .host_lock not held. + */ +static int soc_camera_clk_enable(struct v4l2_clk *clk) +{ + struct soc_camera_device *icd = clk->priv; + struct soc_camera_host *ici; + + if (!icd || !icd->parent) + return -ENODEV; + + ici = to_soc_camera_host(icd->parent); + + if (!try_module_get(ici->ops->owner)) + return -ENODEV; + + /* + * If a different client is currently being probed, the host will tell + * you to go + */ + return soc_camera_clock_start(ici); +} + +static void soc_camera_clk_disable(struct v4l2_clk *clk) +{ + struct soc_camera_device *icd = clk->priv; + struct soc_camera_host *ici; + + if (!icd || !icd->parent) + return; + + ici = to_soc_camera_host(icd->parent); + + soc_camera_clock_stop(ici); + + module_put(ici->ops->owner); +} + +/* + * Eventually, it would be more logical to make the respective host the clock + * owner, but then we would have to copy this struct for each ici. Besides, it + * would introduce the circular dependency problem, unless we port all client + * drivers to release the clock, when not in use. + */ +static const struct v4l2_clk_ops soc_camera_clk_ops = { + .owner = THIS_MODULE, + .enable = soc_camera_clk_enable, + .disable = soc_camera_clk_disable, +}; + +static int soc_camera_dyn_pdev(struct soc_camera_desc *sdesc, + struct soc_camera_async_client *sasc) +{ + struct platform_device *pdev; + int ret, i; + + mutex_lock(&list_lock); + i = find_first_zero_bit(device_map, MAP_MAX_NUM); + if (i < MAP_MAX_NUM) + set_bit(i, device_map); + mutex_unlock(&list_lock); + if (i >= MAP_MAX_NUM) + return -ENOMEM; + + pdev = platform_device_alloc("soc-camera-pdrv", i); + if (!pdev) + return -ENOMEM; + + ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc)); + if (ret < 0) { + platform_device_put(pdev); + return ret; + } + + sasc->pdev = pdev; + + return 0; +} + +static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_client *sasc) +{ + struct platform_device *pdev = sasc->pdev; + int ret; + + ret = platform_device_add(pdev); + if (ret < 0 || !pdev->dev.driver) + return NULL; + + return platform_get_drvdata(pdev); +} + +/* Locking: called with .host_lock held */ +static int soc_camera_probe_finish(struct soc_camera_device *icd) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + int ret; + + sd->grp_id = soc_camera_grp_id(icd); + v4l2_set_subdev_hostdata(sd, icd); + + v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms); + + ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, + NULL, true); + if (ret < 0) + return ret; + + ret = soc_camera_add_device(icd); + if (ret < 0) { + dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); + return ret; + } + + /* At this point client .probe() should have run already */ + ret = soc_camera_init_user_formats(icd); + if (ret < 0) + goto eusrfmt; + + icd->field = V4L2_FIELD_ANY; + + ret = soc_camera_video_start(icd); + if (ret < 0) + goto evidstart; + + /* Try to improve our guess of a reasonable window format */ + if (!v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt)) { + icd->user_width = mf->width; + icd->user_height = mf->height; + icd->colorspace = mf->colorspace; + icd->field = mf->field; + } + soc_camera_remove_device(icd); + + return 0; + +evidstart: + soc_camera_free_user_formats(icd); +eusrfmt: + soc_camera_remove_device(icd); + + return ret; +} + +#ifdef CONFIG_I2C_BOARDINFO +static int soc_camera_i2c_init(struct soc_camera_device *icd, + struct soc_camera_desc *sdesc) +{ + struct soc_camera_subdev_desc *ssdd; + struct i2c_client *client; + struct soc_camera_host *ici; + struct soc_camera_host_desc *shd = &sdesc->host_desc; + struct i2c_adapter *adap; + struct v4l2_subdev *subdev; + char clk_name[V4L2_CLK_NAME_SIZE]; + int ret; + + /* First find out how we link the main client */ + if (icd->sasc) { + /* Async non-OF probing handled by the subdevice list */ + return -EPROBE_DEFER; + } + + ici = to_soc_camera_host(icd->parent); + adap = i2c_get_adapter(shd->i2c_adapter_id); + if (!adap) { + dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", + shd->i2c_adapter_id); + return -ENODEV; + } + + ssdd = kmemdup(&sdesc->subdev_desc, sizeof(*ssdd), GFP_KERNEL); + if (!ssdd) { + ret = -ENOMEM; + goto ealloc; + } + /* + * In synchronous case we request regulators ourselves in + * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try + * to allocate them again. + */ + ssdd->sd_pdata.num_regulators = 0; + ssdd->sd_pdata.regulators = NULL; + shd->board_info->platform_data = ssdd; + + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), + shd->i2c_adapter_id, shd->board_info->addr); + + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); + if (IS_ERR(icd->clk)) { + ret = PTR_ERR(icd->clk); + goto eclkreg; + } + + subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, + shd->board_info, NULL); + if (!subdev) { + ret = -ENODEV; + goto ei2cnd; + } + + client = v4l2_get_subdevdata(subdev); + + /* Use to_i2c_client(dev) to recover the i2c client */ + icd->control = &client->dev; + + return 0; +ei2cnd: + v4l2_clk_unregister(icd->clk); + icd->clk = NULL; +eclkreg: + kfree(ssdd); +ealloc: + i2c_put_adapter(adap); + return ret; +} + +static void soc_camera_i2c_free(struct soc_camera_device *icd) +{ + struct i2c_client *client = + to_i2c_client(to_soc_camera_control(icd)); + struct i2c_adapter *adap; + struct soc_camera_subdev_desc *ssdd; + + icd->control = NULL; + if (icd->sasc) + return; + + adap = client->adapter; + ssdd = client->dev.platform_data; + v4l2_device_unregister_subdev(i2c_get_clientdata(client)); + i2c_unregister_device(client); + i2c_put_adapter(adap); + kfree(ssdd); + v4l2_clk_unregister(icd->clk); + icd->clk = NULL; +} + +/* + * V4L2 asynchronous notifier callbacks. They are all called under a v4l2-async + * internal global mutex, therefore cannot race against other asynchronous + * events. Until notifier->complete() (soc_camera_async_complete()) is called, + * the video device node is not registered and no V4L fops can occur. Unloading + * of the host driver also calls a v4l2-async function, so also there we're + * protected. + */ +static int soc_camera_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct soc_camera_async_client *sasc = container_of(notifier, + struct soc_camera_async_client, notifier); + struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); + + if (asd == sasc->sensor && !WARN_ON(icd->control)) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* + * Only now we get subdevice-specific information like + * regulators, flags, callbacks, etc. + */ + if (client) { + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_subdev_desc *ssdd = + soc_camera_i2c_to_desc(client); + if (ssdd) { + memcpy(&sdesc->subdev_desc, ssdd, + sizeof(sdesc->subdev_desc)); + if (ssdd->reset) + ssdd->reset(&client->dev); + } + + icd->control = &client->dev; + } + } + + return 0; +} + +static void soc_camera_async_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct soc_camera_async_client *sasc = container_of(notifier, + struct soc_camera_async_client, notifier); + struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); + + icd->control = NULL; + + if (icd->clk) { + v4l2_clk_unregister(icd->clk); + icd->clk = NULL; + } +} + +static int soc_camera_async_complete(struct v4l2_async_notifier *notifier) +{ + struct soc_camera_async_client *sasc = container_of(notifier, + struct soc_camera_async_client, notifier); + struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); + + if (to_soc_camera_control(icd)) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; + + mutex_lock(&list_lock); + ret = soc_camera_probe(ici, icd); + mutex_unlock(&list_lock); + if (ret < 0) + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations soc_camera_async_ops = { + .bound = soc_camera_async_bound, + .unbind = soc_camera_async_unbind, + .complete = soc_camera_async_complete, +}; + +static int scan_async_group(struct soc_camera_host *ici, + struct v4l2_async_subdev **asd, unsigned int size) +{ + struct soc_camera_async_subdev *sasd; + struct soc_camera_async_client *sasc; + struct soc_camera_device *icd; + struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; + char clk_name[V4L2_CLK_NAME_SIZE]; + unsigned int i; + int ret; + + /* First look for a sensor */ + for (i = 0; i < size; i++) { + sasd = container_of(asd[i], struct soc_camera_async_subdev, asd); + if (sasd->role == SOCAM_SUBDEV_DATA_SOURCE) + break; + } + + if (i >= size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) { + /* All useless */ + dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n"); + return -ENODEV; + } + + /* Or shall this be managed by the soc-camera device? */ + sasc = devm_kzalloc(ici->v4l2_dev.dev, sizeof(*sasc), GFP_KERNEL); + if (!sasc) + return -ENOMEM; + + /* HACK: just need a != NULL */ + sdesc.host_desc.board_info = ERR_PTR(-ENODATA); + + ret = soc_camera_dyn_pdev(&sdesc, sasc); + if (ret < 0) + goto eallocpdev; + + sasc->sensor = &sasd->asd; + + icd = soc_camera_add_pdev(sasc); + if (!icd) { + ret = -ENOMEM; + goto eaddpdev; + } + + v4l2_async_notifier_init(&sasc->notifier); + + for (i = 0; i < size; i++) { + ret = v4l2_async_notifier_add_subdev(&sasc->notifier, asd[i]); + if (ret) + goto eaddasd; + } + + sasc->notifier.ops = &soc_camera_async_ops; + + icd->sasc = sasc; + icd->parent = ici->v4l2_dev.dev; + + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), + sasd->asd.match.i2c.adapter_id, + sasd->asd.match.i2c.address); + + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); + if (IS_ERR(icd->clk)) { + ret = PTR_ERR(icd->clk); + goto eclkreg; + } + + ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); + if (!ret) + return 0; + + v4l2_clk_unregister(icd->clk); +eclkreg: + icd->clk = NULL; +eaddasd: + v4l2_async_notifier_cleanup(&sasc->notifier); + platform_device_del(sasc->pdev); +eaddpdev: + platform_device_put(sasc->pdev); +eallocpdev: + devm_kfree(ici->v4l2_dev.dev, sasc); + dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); + + return ret; +} + +static void scan_async_host(struct soc_camera_host *ici) +{ + struct v4l2_async_subdev **asd; + int j; + + for (j = 0, asd = ici->asd; ici->asd_sizes[j]; j++) { + scan_async_group(ici, asd, ici->asd_sizes[j]); + asd += ici->asd_sizes[j]; + } +} +#else +#define soc_camera_i2c_init(icd, sdesc) (-ENODEV) +#define soc_camera_i2c_free(icd) do {} while (0) +#define scan_async_host(ici) do {} while (0) +#endif + +#ifdef CONFIG_OF + +struct soc_of_info { + struct soc_camera_async_subdev sasd; + struct soc_camera_async_client sasc; + struct v4l2_async_subdev *subdev; +}; + +static int soc_of_bind(struct soc_camera_host *ici, + struct device_node *ep, + struct device_node *remote) +{ + struct soc_camera_device *icd; + struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; + struct soc_camera_async_client *sasc; + struct soc_of_info *info; + struct i2c_client *client; + char clk_name[V4L2_CLK_NAME_SIZE]; + int ret; + + /* allocate a new subdev and add match info to it */ + info = devm_kzalloc(ici->v4l2_dev.dev, sizeof(struct soc_of_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->sasd.asd.match.fwnode = of_fwnode_handle(remote); + info->sasd.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + info->subdev = &info->sasd.asd; + + /* Or shall this be managed by the soc-camera device? */ + sasc = &info->sasc; + + /* HACK: just need a != NULL */ + sdesc.host_desc.board_info = ERR_PTR(-ENODATA); + + ret = soc_camera_dyn_pdev(&sdesc, sasc); + if (ret < 0) + goto eallocpdev; + + sasc->sensor = &info->sasd.asd; + + icd = soc_camera_add_pdev(sasc); + if (!icd) { + ret = -ENOMEM; + goto eaddpdev; + } + + v4l2_async_notifier_init(&sasc->notifier); + + ret = v4l2_async_notifier_add_subdev(&sasc->notifier, info->subdev); + if (ret) { + of_node_put(remote); + goto eaddasd; + } + + sasc->notifier.ops = &soc_camera_async_ops; + + icd->sasc = sasc; + icd->parent = ici->v4l2_dev.dev; + + client = of_find_i2c_device_by_node(remote); + + if (client) + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), + client->adapter->nr, client->addr); + else + v4l2_clk_name_of(clk_name, sizeof(clk_name), remote); + + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); + if (IS_ERR(icd->clk)) { + ret = PTR_ERR(icd->clk); + goto eclkreg; + } + + ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); + if (!ret) + return 0; + + v4l2_clk_unregister(icd->clk); +eclkreg: + icd->clk = NULL; +eaddasd: + v4l2_async_notifier_cleanup(&sasc->notifier); + platform_device_del(sasc->pdev); +eaddpdev: + platform_device_put(sasc->pdev); +eallocpdev: + devm_kfree(ici->v4l2_dev.dev, info); + dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); + + return ret; +} + +static void scan_of_host(struct soc_camera_host *ici) +{ + struct device *dev = ici->v4l2_dev.dev; + struct device_node *np = dev->of_node; + struct device_node *epn = NULL, *rem; + unsigned int i; + + for (i = 0; ; i++) { + epn = of_graph_get_next_endpoint(np, epn); + if (!epn) + break; + + rem = of_graph_get_remote_port_parent(epn); + if (!rem) { + dev_notice(dev, "no remote for %pOF\n", epn); + continue; + } + + /* so we now have a remote node to connect */ + if (!i) + soc_of_bind(ici, epn, rem); + + if (i) { + dev_err(dev, "multiple subdevices aren't supported yet!\n"); + break; + } + } + + of_node_put(epn); +} + +#else +static inline void scan_of_host(struct soc_camera_host *ici) { } +#endif + +/* Called during host-driver probe */ +static int soc_camera_probe(struct soc_camera_host *ici, + struct soc_camera_device *icd) +{ + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_host_desc *shd = &sdesc->host_desc; + struct device *control = NULL; + int ret; + + dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev)); + + /* + * Currently the subdev with the largest number of controls (13) is + * ov6550. So let's pick 16 as a hint for the control handler. Note + * that this is a hint only: too large and you waste some memory, too + * small and there is a (very) small performance hit when looking up + * controls in the internal hash. + */ + ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16); + if (ret < 0) + return ret; + + /* Must have icd->vdev before registering the device */ + ret = video_dev_create(icd); + if (ret < 0) + goto evdc; + + /* + * ..._video_start() will create a device node, video_register_device() + * itself is protected against concurrent open() calls, but we also have + * to protect our data also during client probing. + */ + + /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ + if (shd->board_info) { + ret = soc_camera_i2c_init(icd, sdesc); + if (ret < 0 && ret != -EPROBE_DEFER) + goto eadd; + } else if (!shd->add_device || !shd->del_device) { + ret = -EINVAL; + goto eadd; + } else { + ret = soc_camera_clock_start(ici); + if (ret < 0) + goto eadd; + + if (shd->module_name) + ret = request_module(shd->module_name); + + ret = shd->add_device(icd); + if (ret < 0) + goto eadddev; + + /* + * FIXME: this is racy, have to use driver-binding notification, + * when it is available + */ + control = to_soc_camera_control(icd); + if (!control || !control->driver || !dev_get_drvdata(control) || + !try_module_get(control->driver->owner)) { + shd->del_device(icd); + ret = -ENODEV; + goto enodrv; + } + } + + mutex_lock(&ici->host_lock); + ret = soc_camera_probe_finish(icd); + mutex_unlock(&ici->host_lock); + if (ret < 0) + goto efinish; + + return 0; + +efinish: + if (shd->board_info) { + soc_camera_i2c_free(icd); + } else { + shd->del_device(icd); + module_put(control->driver->owner); +enodrv: +eadddev: + soc_camera_clock_stop(ici); + } +eadd: + if (icd->vdev) { + video_device_release(icd->vdev); + icd->vdev = NULL; + } +evdc: + v4l2_ctrl_handler_free(&icd->ctrl_handler); + return ret; +} + +/* + * This is called on device_unregister, which only means we have to disconnect + * from the host, but not remove ourselves from the device list. With + * asynchronous client probing this can also be called without + * soc_camera_probe_finish() having run. Careful with clean up. + */ +static int soc_camera_remove(struct soc_camera_device *icd) +{ + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct video_device *vdev = icd->vdev; + + v4l2_ctrl_handler_free(&icd->ctrl_handler); + if (vdev) { + video_unregister_device(vdev); + icd->vdev = NULL; + } + + if (sdesc->host_desc.board_info) { + soc_camera_i2c_free(icd); + } else { + struct device *dev = to_soc_camera_control(icd); + struct device_driver *drv = dev ? dev->driver : NULL; + if (drv) { + sdesc->host_desc.del_device(icd); + module_put(drv->owner); + } + } + + if (icd->num_user_formats) + soc_camera_free_user_formats(icd); + + if (icd->clk) { + /* For the synchronous case */ + v4l2_clk_unregister(icd->clk); + icd->clk = NULL; + } + + if (icd->sasc) + platform_device_unregister(icd->sasc->pdev); + + return 0; +} + +static int default_g_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + }; + int ret; + + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); + if (ret) + return ret; + sel->r = sdsel.r; + return 0; +} + +static int default_s_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + .flags = sel->flags, + .r = sel->r, + }; + int ret; + + ret = v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); + if (ret) + return ret; + sel->r = sdsel.r; + return 0; +} + +static int default_g_parm(struct soc_camera_device *icd, + struct v4l2_streamparm *a) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_g_parm_cap(icd->vdev, sd, a); +} + +static int default_s_parm(struct soc_camera_device *icd, + struct v4l2_streamparm *a) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_s_parm_cap(icd->vdev, sd, a); +} + +static int default_enum_framesizes(struct soc_camera_device *icd, + struct v4l2_frmsizeenum *fsize) +{ + int ret; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + const struct soc_camera_format_xlate *xlate; + struct v4l2_subdev_frame_size_enum fse = { + .index = fsize->index, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + xlate = soc_camera_xlate_by_fourcc(icd, fsize->pixel_format); + if (!xlate) + return -EINVAL; + fse.code = xlate->code; + + ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); + if (ret < 0) + return ret; + + if (fse.min_width == fse.max_width && + fse.min_height == fse.max_height) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.min_width; + fsize->discrete.height = fse.min_height; + return 0; + } + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = fse.min_width; + fsize->stepwise.max_width = fse.max_width; + fsize->stepwise.min_height = fse.min_height; + fsize->stepwise.max_height = fse.max_height; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + return 0; +} + +int soc_camera_host_register(struct soc_camera_host *ici) +{ + struct soc_camera_host *ix; + int ret; + + if (!ici || !ici->ops || + !ici->ops->try_fmt || + !ici->ops->set_fmt || + !ici->ops->set_bus_param || + !ici->ops->querycap || + !ici->ops->init_videobuf2 || + !ici->ops->poll || + !ici->v4l2_dev.dev) + return -EINVAL; + + if (!ici->ops->set_selection) + ici->ops->set_selection = default_s_selection; + if (!ici->ops->get_selection) + ici->ops->get_selection = default_g_selection; + if (!ici->ops->set_parm) + ici->ops->set_parm = default_s_parm; + if (!ici->ops->get_parm) + ici->ops->get_parm = default_g_parm; + if (!ici->ops->enum_framesizes) + ici->ops->enum_framesizes = default_enum_framesizes; + + mutex_lock(&list_lock); + list_for_each_entry(ix, &hosts, list) { + if (ix->nr == ici->nr) { + ret = -EBUSY; + goto edevreg; + } + } + + ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev); + if (ret < 0) + goto edevreg; + + list_add_tail(&ici->list, &hosts); + mutex_unlock(&list_lock); + + mutex_init(&ici->host_lock); + mutex_init(&ici->clk_lock); + + if (ici->v4l2_dev.dev->of_node) + scan_of_host(ici); + else if (ici->asd_sizes) + /* + * No OF, host with a list of subdevices. Don't try to mix + * modes by initialising some groups statically and some + * dynamically! + */ + scan_async_host(ici); + else + /* Legacy: static platform devices from board data */ + scan_add_host(ici); + + return 0; + +edevreg: + mutex_unlock(&list_lock); + return ret; +} +EXPORT_SYMBOL(soc_camera_host_register); + +/* Unregister all clients! */ +void soc_camera_host_unregister(struct soc_camera_host *ici) +{ + struct soc_camera_device *icd, *tmp; + struct soc_camera_async_client *sasc; + LIST_HEAD(notifiers); + + mutex_lock(&list_lock); + list_del(&ici->list); + list_for_each_entry(icd, &devices, list) + if (icd->iface == ici->nr && icd->sasc) { + /* as long as we hold the device, sasc won't be freed */ + get_device(icd->pdev); + list_add(&icd->sasc->list, ¬ifiers); + } + mutex_unlock(&list_lock); + + list_for_each_entry(sasc, ¬ifiers, list) { + /* Must call unlocked to avoid AB-BA dead-lock */ + v4l2_async_notifier_unregister(&sasc->notifier); + v4l2_async_notifier_cleanup(&sasc->notifier); + put_device(&sasc->pdev->dev); + } + + mutex_lock(&list_lock); + + list_for_each_entry_safe(icd, tmp, &devices, list) + if (icd->iface == ici->nr) + soc_camera_remove(icd); + + mutex_unlock(&list_lock); + + v4l2_device_unregister(&ici->v4l2_dev); +} +EXPORT_SYMBOL(soc_camera_host_unregister); + +/* Image capture device */ +static int soc_camera_device_register(struct soc_camera_device *icd) +{ + struct soc_camera_device *ix; + int num = -1, i; + + mutex_lock(&list_lock); + for (i = 0; i < 256 && num < 0; i++) { + num = i; + /* Check if this index is available on this interface */ + list_for_each_entry(ix, &devices, list) { + if (ix->iface == icd->iface && ix->devnum == i) { + num = -1; + break; + } + } + } + + if (num < 0) { + /* + * ok, we have 256 cameras on this host... + * man, stay reasonable... + */ + mutex_unlock(&list_lock); + return -ENOMEM; + } + + icd->devnum = num; + icd->use_count = 0; + icd->host_priv = NULL; + + /* + * Dynamically allocated devices set the bit earlier, but it doesn't hurt setting + * it again + */ + i = to_platform_device(icd->pdev)->id; + if (i < 0) + /* One static (legacy) soc-camera platform device */ + i = 0; + if (i >= MAP_MAX_NUM) { + mutex_unlock(&list_lock); + return -EBUSY; + } + set_bit(i, device_map); + list_add_tail(&icd->list, &devices); + mutex_unlock(&list_lock); + + return 0; +} + +static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { + .vidioc_querycap = soc_camera_querycap, + .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap, + .vidioc_enum_input = soc_camera_enum_input, + .vidioc_g_input = soc_camera_g_input, + .vidioc_s_input = soc_camera_s_input, + .vidioc_s_std = soc_camera_s_std, + .vidioc_g_std = soc_camera_g_std, + .vidioc_enum_framesizes = soc_camera_enum_framesizes, + .vidioc_reqbufs = soc_camera_reqbufs, + .vidioc_querybuf = soc_camera_querybuf, + .vidioc_qbuf = soc_camera_qbuf, + .vidioc_dqbuf = soc_camera_dqbuf, + .vidioc_create_bufs = soc_camera_create_bufs, + .vidioc_prepare_buf = soc_camera_prepare_buf, + .vidioc_expbuf = soc_camera_expbuf, + .vidioc_streamon = soc_camera_streamon, + .vidioc_streamoff = soc_camera_streamoff, + .vidioc_g_selection = soc_camera_g_selection, + .vidioc_s_selection = soc_camera_s_selection, + .vidioc_g_parm = soc_camera_g_parm, + .vidioc_s_parm = soc_camera_s_parm, +}; + +static int video_dev_create(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct video_device *vdev = video_device_alloc(); + + if (!vdev) + return -ENOMEM; + + strscpy(vdev->name, ici->drv_name, sizeof(vdev->name)); + + vdev->v4l2_dev = &ici->v4l2_dev; + vdev->fops = &soc_camera_fops; + vdev->ioctl_ops = &soc_camera_ioctl_ops; + vdev->release = video_device_release; + vdev->ctrl_handler = &icd->ctrl_handler; + vdev->lock = &ici->host_lock; + + icd->vdev = vdev; + + return 0; +} + +/* + * Called from soc_camera_probe() above with .host_lock held + */ +static int soc_camera_video_start(struct soc_camera_device *icd) +{ + const struct device_type *type = icd->vdev->dev.type; + int ret; + + if (!icd->parent) + return -ENODEV; + + video_set_drvdata(icd->vdev, icd); + if (icd->vdev->tvnorms == 0) { + /* disable the STD API if there are no tvnorms defined */ + v4l2_disable_ioctl(icd->vdev, VIDIOC_G_STD); + v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD); + v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD); + } + ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + dev_err(icd->pdev, "video_register_device failed: %d\n", ret); + return ret; + } + + /* Restore device type, possibly set by the subdevice driver */ + icd->vdev->dev.type = type; + + return 0; +} + +static int soc_camera_pdrv_probe(struct platform_device *pdev) +{ + struct soc_camera_desc *sdesc = pdev->dev.platform_data; + struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; + struct soc_camera_device *icd; + int ret; + + if (!sdesc) + return -EINVAL; + + icd = devm_kzalloc(&pdev->dev, sizeof(*icd), GFP_KERNEL); + if (!icd) + return -ENOMEM; + + /* + * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below + * regulator allocation is a dummy. They are actually requested by the + * subdevice driver, using soc_camera_power_init(). Also note, that in + * that case regulators are attached to the I2C device and not to the + * camera platform device. + */ + ret = devm_regulator_bulk_get(&pdev->dev, ssdd->sd_pdata.num_regulators, + ssdd->sd_pdata.regulators); + if (ret < 0) + return ret; + + icd->iface = sdesc->host_desc.bus_id; + icd->sdesc = sdesc; + icd->pdev = &pdev->dev; + platform_set_drvdata(pdev, icd); + + icd->user_width = DEFAULT_WIDTH; + icd->user_height = DEFAULT_HEIGHT; + + return soc_camera_device_register(icd); +} + +/* + * Only called on rmmod for each platform device, since they are not + * hot-pluggable. Now we know, that all our users - hosts and devices have + * been unloaded already + */ +static int soc_camera_pdrv_remove(struct platform_device *pdev) +{ + struct soc_camera_device *icd = platform_get_drvdata(pdev); + int i; + + if (!icd) + return -EINVAL; + + i = pdev->id; + if (i < 0) + i = 0; + + /* + * In synchronous mode with static platform devices this is called in a + * loop from drivers/base/dd.c::driver_detach(), no parallel execution, + * no need to lock. In asynchronous case the caller - + * soc_camera_host_unregister() - already holds the lock + */ + if (test_bit(i, device_map)) { + clear_bit(i, device_map); + list_del(&icd->list); + } + + return 0; +} + +static struct platform_driver __refdata soc_camera_pdrv = { + .probe = soc_camera_pdrv_probe, + .remove = soc_camera_pdrv_remove, + .driver = { + .name = "soc-camera-pdrv", + }, +}; + +module_platform_driver(soc_camera_pdrv); + +MODULE_DESCRIPTION("Image capture bus driver"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:soc-camera-pdrv"); diff --git a/drivers/staging/media/soc_camera/soc_mediabus.c b/drivers/staging/media/soc_camera/soc_mediabus.c new file mode 100644 index 000000000000..be74008ec0ca --- /dev/null +++ b/drivers/staging/media/soc_camera/soc_mediabus.c @@ -0,0 +1,533 @@ +/* + * soc-camera media bus helper routines + * + * Copyright (C) 2009, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include +#include +#include + +static const struct soc_mbus_lookup mbus_fmt[] = { +{ + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUYV", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVYU, + .name = "YVYU", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_VYUY, + .name = "VYUY", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB555, + .name = "RGB555", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB555X, + .name = "RGB555X", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB565, + .name = "RGB565", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB565X, + .name = "RGB565X", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB666_1X18, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB666/32bpp", + .bits_per_sample = 18, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = MEDIA_BUS_FMT_RGB888_1X24, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 24, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = MEDIA_BUS_FMT_RGB888_2X12_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_BE, + }, +}, { + .code = MEDIA_BUS_FMT_RGB888_2X12_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888/32bpp", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND32, + .order = SOC_MBUS_ORDER_LE, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .name = "Bayer 8 BGGR", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_Y8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_GREY, + .name = "Grey", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_Y10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_Y10, + .name = "Grey 10bit", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADLO, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADLO, + .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_JPEG_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_JPEG, + .name = "JPEG", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_VARIABLE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB444, + .name = "RGB444", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YUYV8_1_5X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUV420, + .name = "YUYV 4:2:0", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_1_5X8, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YVYU8_1_5X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVU420, + .name = "YVYU 4:2:0", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_1_5X8, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_VYUY, + .name = "VYUY 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUYV 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVYU, + .name = "YVYU 16bit", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .name = "Bayer 8 GRBG", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG10DPCM8, + .name = "Bayer 10 BGGR DPCM 8", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .name = "Bayer 10 GBRG", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .name = "Bayer 10 GRBG", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .name = "Bayer 10 RGGB", + .bits_per_sample = 10, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .name = "Bayer 12 BGGR", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .name = "Bayer 12 GBRG", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .name = "Bayer 12 GRBG", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .name = "Bayer 12 RGGB", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}, +}; + +int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, + unsigned int *numerator, unsigned int *denominator) +{ + switch (mf->packing) { + case SOC_MBUS_PACKING_NONE: + case SOC_MBUS_PACKING_EXTEND16: + *numerator = 1; + *denominator = 1; + return 0; + case SOC_MBUS_PACKING_EXTEND32: + *numerator = 1; + *denominator = 1; + return 0; + case SOC_MBUS_PACKING_2X8_PADHI: + case SOC_MBUS_PACKING_2X8_PADLO: + *numerator = 2; + *denominator = 1; + return 0; + case SOC_MBUS_PACKING_1_5X8: + *numerator = 3; + *denominator = 2; + return 0; + case SOC_MBUS_PACKING_VARIABLE: + *numerator = 0; + *denominator = 1; + return 0; + } + return -EINVAL; +} +EXPORT_SYMBOL(soc_mbus_samples_per_pixel); + +s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) +{ + if (mf->layout != SOC_MBUS_LAYOUT_PACKED) + return width * mf->bits_per_sample / 8; + + switch (mf->packing) { + case SOC_MBUS_PACKING_NONE: + return width * mf->bits_per_sample / 8; + case SOC_MBUS_PACKING_2X8_PADHI: + case SOC_MBUS_PACKING_2X8_PADLO: + case SOC_MBUS_PACKING_EXTEND16: + return width * 2; + case SOC_MBUS_PACKING_1_5X8: + return width * 3 / 2; + case SOC_MBUS_PACKING_VARIABLE: + return 0; + case SOC_MBUS_PACKING_EXTEND32: + return width * 4; + } + return -EINVAL; +} +EXPORT_SYMBOL(soc_mbus_bytes_per_line); + +s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, + u32 bytes_per_line, u32 height) +{ + if (mf->layout == SOC_MBUS_LAYOUT_PACKED) + return bytes_per_line * height; + + switch (mf->packing) { + case SOC_MBUS_PACKING_2X8_PADHI: + case SOC_MBUS_PACKING_2X8_PADLO: + return bytes_per_line * height * 2; + case SOC_MBUS_PACKING_1_5X8: + return bytes_per_line * height * 3 / 2; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL(soc_mbus_image_size); + +const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( + u32 code, + const struct soc_mbus_lookup *lookup, + int n) +{ + int i; + + for (i = 0; i < n; i++) + if (lookup[i].code == code) + return &lookup[i].fmt; + + return NULL; +} +EXPORT_SYMBOL(soc_mbus_find_fmtdesc); + +const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( + u32 code) +{ + return soc_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt)); +} +EXPORT_SYMBOL(soc_mbus_get_fmtdesc); + +unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, + unsigned int flags) +{ + unsigned long common_flags; + bool hsync = true, vsync = true, pclk, data, mode; + bool mipi_lanes, mipi_clock; + + common_flags = cfg->flags & flags; + + switch (cfg->type) { + case V4L2_MBUS_PARALLEL: + hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_HSYNC_ACTIVE_LOW); + vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_LOW); + /* fall through */ + case V4L2_MBUS_BT656: + pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_PCLK_SAMPLE_FALLING); + data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_LOW); + mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE); + return (!hsync || !vsync || !pclk || !data || !mode) ? + 0 : common_flags; + case V4L2_MBUS_CSI2_DPHY: + mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES; + mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK); + return (!mipi_lanes || !mipi_clock) ? 0 : common_flags; + default: + WARN_ON(1); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(soc_mbus_config_compatible); + +static int __init soc_mbus_init(void) +{ + return 0; +} + +static void __exit soc_mbus_exit(void) +{ +} + +module_init(soc_mbus_init); +module_exit(soc_mbus_exit); + +MODULE_DESCRIPTION("soc-camera media bus interface"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc_mt9v022.c b/drivers/staging/media/soc_camera/soc_mt9v022.c new file mode 100644 index 000000000000..6d922b17ea94 --- /dev/null +++ b/drivers/staging/media/soc_camera/soc_mt9v022.c @@ -0,0 +1,1012 @@ +/* + * Driver for MT9V022 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c + * The platform has to define struct i2c_board_info objects and link to them + * from struct soc_camera_host_desc + */ + +static char *sensor_type; +module_param(sensor_type, charp, S_IRUGO); +MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); + +/* mt9v022 selected register addresses */ +#define MT9V022_CHIP_VERSION 0x00 +#define MT9V022_COLUMN_START 0x01 +#define MT9V022_ROW_START 0x02 +#define MT9V022_WINDOW_HEIGHT 0x03 +#define MT9V022_WINDOW_WIDTH 0x04 +#define MT9V022_HORIZONTAL_BLANKING 0x05 +#define MT9V022_VERTICAL_BLANKING 0x06 +#define MT9V022_CHIP_CONTROL 0x07 +#define MT9V022_SHUTTER_WIDTH1 0x08 +#define MT9V022_SHUTTER_WIDTH2 0x09 +#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a +#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b +#define MT9V022_RESET 0x0c +#define MT9V022_READ_MODE 0x0d +#define MT9V022_MONITOR_MODE 0x0e +#define MT9V022_PIXEL_OPERATION_MODE 0x0f +#define MT9V022_LED_OUT_CONTROL 0x1b +#define MT9V022_ADC_MODE_CONTROL 0x1c +#define MT9V022_REG32 0x20 +#define MT9V022_ANALOG_GAIN 0x35 +#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47 +#define MT9V022_PIXCLK_FV_LV 0x74 +#define MT9V022_DIGITAL_TEST_PATTERN 0x7f +#define MT9V022_AEC_AGC_ENABLE 0xAF +#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD + +/* mt9v024 partial list register addresses changes with respect to mt9v022 */ +#define MT9V024_PIXCLK_FV_LV 0x72 +#define MT9V024_MAX_TOTAL_SHUTTER_WIDTH 0xAD + +/* Progressive scan, master, defaults */ +#define MT9V022_CHIP_CONTROL_DEFAULT 0x188 + +#define MT9V022_MAX_WIDTH 752 +#define MT9V022_MAX_HEIGHT 480 +#define MT9V022_MIN_WIDTH 48 +#define MT9V022_MIN_HEIGHT 32 +#define MT9V022_COLUMN_SKIP 1 +#define MT9V022_ROW_SKIP 4 + +#define MT9V022_HORIZONTAL_BLANKING_MIN 43 +#define MT9V022_HORIZONTAL_BLANKING_MAX 1023 +#define MT9V022_HORIZONTAL_BLANKING_DEF 94 +#define MT9V022_VERTICAL_BLANKING_MIN 2 +#define MT9V022_VERTICAL_BLANKING_MAX 3000 +#define MT9V022_VERTICAL_BLANKING_DEF 45 + +#define is_mt9v022_rev3(id) (id == 0x1313) +#define is_mt9v024(id) (id == 0x1324) + +/* MT9V022 has only one fixed colorspace per pixelcode */ +struct mt9v022_datafmt { + u32 code; + enum v4l2_colorspace colorspace; +}; + +/* Find a data format by a pixel code in an array */ +static const struct mt9v022_datafmt *mt9v022_find_datafmt( + u32 code, const struct mt9v022_datafmt *fmt, + int n) +{ + int i; + for (i = 0; i < n; i++) + if (fmt[i].code == code) + return fmt + i; + + return NULL; +} + +static const struct mt9v022_datafmt mt9v022_colour_fmts[] = { + /* + * Order important: first natively supported, + * second supported with a GPIO extender + */ + {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, +}; + +static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = { + /* Order important - see above */ + {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, + {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, +}; + +/* only registers with different addresses on different mt9v02x sensors */ +struct mt9v02x_register { + u8 max_total_shutter_width; + u8 pixclk_fv_lv; +}; + +static const struct mt9v02x_register mt9v022_register = { + .max_total_shutter_width = MT9V022_MAX_TOTAL_SHUTTER_WIDTH, + .pixclk_fv_lv = MT9V022_PIXCLK_FV_LV, +}; + +static const struct mt9v02x_register mt9v024_register = { + .max_total_shutter_width = MT9V024_MAX_TOTAL_SHUTTER_WIDTH, + .pixclk_fv_lv = MT9V024_PIXCLK_FV_LV, +}; + +enum mt9v022_model { + MT9V022IX7ATM, + MT9V022IX7ATC, +}; + +struct mt9v022 { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct { + /* exposure/auto-exposure cluster */ + struct v4l2_ctrl *autoexposure; + struct v4l2_ctrl *exposure; + }; + struct { + /* gain/auto-gain cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + }; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_rect rect; /* Sensor window */ + struct v4l2_clk *clk; + const struct mt9v022_datafmt *fmt; + const struct mt9v022_datafmt *fmts; + const struct mt9v02x_register *reg; + int num_fmts; + enum mt9v022_model model; + u16 chip_control; + u16 chip_version; + unsigned short y_skip_top; /* Lines to skip at the top */ +}; + +static struct mt9v022 *to_mt9v022(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9v022, subdev); +} + +static int reg_read(struct i2c_client *client, const u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int reg_write(struct i2c_client *client, const u8 reg, + const u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static int reg_set(struct i2c_client *client, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, ret | data); +} + +static int reg_clear(struct i2c_client *client, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, ret & ~data); +} + +static int mt9v022_init(struct i2c_client *client) +{ + struct mt9v022 *mt9v022 = to_mt9v022(client); + int ret; + + /* + * Almost the default mode: master, parallel, simultaneous, and an + * undocumented bit 0x200, which is present in table 7, but not in 8, + * plus snapshot mode to disable scan for now + */ + mt9v022->chip_control |= 0x10; + ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + if (!ret) + ret = reg_write(client, MT9V022_READ_MODE, 0x300); + + /* All defaults */ + if (!ret) + /* AEC, AGC on */ + ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3); + if (!ret) + ret = reg_write(client, MT9V022_ANALOG_GAIN, 16); + if (!ret) + ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480); + if (!ret) + ret = reg_write(client, mt9v022->reg->max_total_shutter_width, 480); + if (!ret) + /* default - auto */ + ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); + if (!ret) + ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0); + if (!ret) + return v4l2_ctrl_handler_setup(&mt9v022->hdl); + + return ret; +} + +static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + if (enable) { + /* Switch to master "normal" mode */ + mt9v022->chip_control &= ~0x10; + if (is_mt9v022_rev3(mt9v022->chip_version) || + is_mt9v024(mt9v022->chip_version)) { + /* + * Unset snapshot mode specific settings: clear bit 9 + * and bit 2 in reg. 0x20 when in normal mode. + */ + if (reg_clear(client, MT9V022_REG32, 0x204)) + return -EIO; + } + } else { + /* Switch to snapshot mode */ + mt9v022->chip_control |= 0x10; + if (is_mt9v022_rev3(mt9v022->chip_version) || + is_mt9v024(mt9v022->chip_version)) { + /* + * Required settings for snapshot mode: set bit 9 + * (RST enable) and bit 2 (CR enable) in reg. 0x20 + * See TechNote TN0960 or TN-09-225. + */ + if (reg_set(client, MT9V022_REG32, 0x204)) + return -EIO; + } + } + + if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) + return -EIO; + return 0; +} + +static int mt9v022_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct v4l2_rect rect = sel->r; + int min_row, min_blank; + int ret; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* Bayer format - even size lengths */ + if (mt9v022->fmts == mt9v022_colour_fmts) { + rect.width = ALIGN(rect.width, 2); + rect.height = ALIGN(rect.height, 2); + /* Let the user play with the starting pixel */ + } + + soc_camera_limit_side(&rect.left, &rect.width, + MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT); + + /* Like in example app. Contradicts the datasheet though */ + ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); + if (ret >= 0) { + if (ret & 1) /* Autoexposure */ + ret = reg_write(client, mt9v022->reg->max_total_shutter_width, + rect.height + mt9v022->y_skip_top + 43); + /* + * If autoexposure is off, there is no need to set + * MT9V022_TOTAL_SHUTTER_WIDTH here. Autoexposure can be off + * only if the user has set exposure manually, using the + * V4L2_CID_EXPOSURE_AUTO with the value V4L2_EXPOSURE_MANUAL. + * In this case the register MT9V022_TOTAL_SHUTTER_WIDTH + * already contains the correct value. + */ + } + /* Setup frame format: defaults apart from width and height */ + if (!ret) + ret = reg_write(client, MT9V022_COLUMN_START, rect.left); + if (!ret) + ret = reg_write(client, MT9V022_ROW_START, rect.top); + /* + * mt9v022: min total row time is 660 columns, min blanking is 43 + * mt9v024: min total row time is 690 columns, min blanking is 61 + */ + if (is_mt9v024(mt9v022->chip_version)) { + min_row = 690; + min_blank = 61; + } else { + min_row = 660; + min_blank = 43; + } + if (!ret) + ret = v4l2_ctrl_s_ctrl(mt9v022->hblank, + rect.width > min_row - min_blank ? + min_blank : min_row - rect.width); + if (!ret) + ret = v4l2_ctrl_s_ctrl(mt9v022->vblank, 45); + if (!ret) + ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width); + if (!ret) + ret = reg_write(client, MT9V022_WINDOW_HEIGHT, + rect.height + mt9v022->y_skip_top); + + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height); + + mt9v022->rect = rect; + + return 0; +} + +static int mt9v022_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = MT9V022_COLUMN_SKIP; + sel->r.top = MT9V022_ROW_SKIP; + sel->r.width = MT9V022_MAX_WIDTH; + sel->r.height = MT9V022_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = mt9v022->rect; + return 0; + default: + return -EINVAL; + } +} + +static int mt9v022_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + if (format->pad) + return -EINVAL; + + mf->width = mt9v022->rect.width; + mf->height = mt9v022->rect.height; + mf->code = mt9v022->fmt->code; + mf->colorspace = mt9v022->fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int mt9v022_s_fmt(struct v4l2_subdev *sd, + const struct mt9v022_datafmt *fmt, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP, + .r.left = mt9v022->rect.left, + .r.top = mt9v022->rect.top, + .r.width = mf->width, + .r.height = mf->height, + }; + int ret; + + /* + * The caller provides a supported format, as verified per call to + * .set_fmt(FORMAT_TRY), datawidth is from our supported format list + */ + switch (mf->code) { + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_Y10_1X10: + if (mt9v022->model != MT9V022IX7ATM) + return -EINVAL; + break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SBGGR10_1X10: + if (mt9v022->model != MT9V022IX7ATC) + return -EINVAL; + break; + default: + return -EINVAL; + } + + /* No support for scaling on this camera, just crop. */ + ret = mt9v022_set_selection(sd, NULL, &sel); + if (!ret) { + mf->width = mt9v022->rect.width; + mf->height = mt9v022->rect.height; + mt9v022->fmt = fmt; + mf->colorspace = fmt->colorspace; + } + + return ret; +} + +static int mt9v022_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + const struct mt9v022_datafmt *fmt; + int align = mf->code == MEDIA_BUS_FMT_SBGGR8_1X8 || + mf->code == MEDIA_BUS_FMT_SBGGR10_1X10; + + if (format->pad) + return -EINVAL; + + v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH, + MT9V022_MAX_WIDTH, align, + &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top, + MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0); + + fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts, + mt9v022->num_fmts); + if (!fmt) { + fmt = mt9v022->fmt; + mf->code = fmt->code; + } + + mf->colorspace = fmt->colorspace; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return mt9v022_s_fmt(sd, fmt, mf); + cfg->try_fmt = *mf; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9v022_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff) + return -EINVAL; + + reg->size = 2; + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9v022_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff) + return -EINVAL; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static int mt9v022_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + return soc_camera_set_power(&client->dev, ssdd, mt9v022->clk, on); +} + +static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9v022 *mt9v022 = container_of(ctrl->handler, + struct mt9v022, hdl); + struct v4l2_subdev *sd = &mt9v022->subdev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_ctrl *gain = mt9v022->gain; + struct v4l2_ctrl *exp = mt9v022->exposure; + unsigned long range; + int data; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + data = reg_read(client, MT9V022_ANALOG_GAIN); + if (data < 0) + return -EIO; + + range = gain->maximum - gain->minimum; + gain->val = ((data - 16) * range + 24) / 48 + gain->minimum; + return 0; + case V4L2_CID_EXPOSURE_AUTO: + data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); + if (data < 0) + return -EIO; + + range = exp->maximum - exp->minimum; + exp->val = ((data - 1) * range + 239) / 479 + exp->minimum; + return 0; + case V4L2_CID_HBLANK: + data = reg_read(client, MT9V022_HORIZONTAL_BLANKING); + if (data < 0) + return -EIO; + ctrl->val = data; + return 0; + case V4L2_CID_VBLANK: + data = reg_read(client, MT9V022_VERTICAL_BLANKING); + if (data < 0) + return -EIO; + ctrl->val = data; + return 0; + } + return -EINVAL; +} + +static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9v022 *mt9v022 = container_of(ctrl->handler, + struct mt9v022, hdl); + struct v4l2_subdev *sd = &mt9v022->subdev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->val) + data = reg_set(client, MT9V022_READ_MODE, 0x10); + else + data = reg_clear(client, MT9V022_READ_MODE, 0x10); + if (data < 0) + return -EIO; + return 0; + case V4L2_CID_HFLIP: + if (ctrl->val) + data = reg_set(client, MT9V022_READ_MODE, 0x20); + else + data = reg_clear(client, MT9V022_READ_MODE, 0x20); + if (data < 0) + return -EIO; + return 0; + case V4L2_CID_AUTOGAIN: + if (ctrl->val) { + if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) + return -EIO; + } else { + struct v4l2_ctrl *gain = mt9v022->gain; + /* mt9v022 has minimum == default */ + unsigned long range = gain->maximum - gain->minimum; + /* Valid values 16 to 64, 32 to 64 must be even. */ + unsigned long gain_val = ((gain->val - (s32)gain->minimum) * + 48 + range / 2) / range + 16; + + if (gain_val >= 32) + gain_val &= ~1; + + /* + * The user wants to set gain manually, hope, she + * knows, what she's doing... Switch AGC off. + */ + if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) + return -EIO; + + dev_dbg(&client->dev, "Setting gain from %d to %lu\n", + reg_read(client, MT9V022_ANALOG_GAIN), gain_val); + if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0) + return -EIO; + } + return 0; + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_AUTO) { + data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); + } else { + struct v4l2_ctrl *exp = mt9v022->exposure; + unsigned long range = exp->maximum - exp->minimum; + unsigned long shutter = ((exp->val - (s32)exp->minimum) * + 479 + range / 2) / range + 1; + + /* + * The user wants to set shutter width manually, hope, + * she knows, what she's doing... Switch AEC off. + */ + data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); + if (data < 0) + return -EIO; + dev_dbg(&client->dev, "Shutter width from %d to %lu\n", + reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), + shutter); + if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, + shutter) < 0) + return -EIO; + } + return 0; + case V4L2_CID_HBLANK: + if (reg_write(client, MT9V022_HORIZONTAL_BLANKING, + ctrl->val) < 0) + return -EIO; + return 0; + case V4L2_CID_VBLANK: + if (reg_write(client, MT9V022_VERTICAL_BLANKING, + ctrl->val) < 0) + return -EIO; + return 0; + } + return -EINVAL; +} + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int mt9v022_video_probe(struct i2c_client *client) +{ + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + s32 data; + int ret; + unsigned long flags; + + ret = mt9v022_s_power(&mt9v022->subdev, 1); + if (ret < 0) + return ret; + + /* Read out the chip version register */ + data = reg_read(client, MT9V022_CHIP_VERSION); + + /* must be 0x1311, 0x1313 or 0x1324 */ + if (data != 0x1311 && data != 0x1313 && data != 0x1324) { + ret = -ENODEV; + dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n", + data); + goto ei2c; + } + + mt9v022->chip_version = data; + + mt9v022->reg = is_mt9v024(data) ? &mt9v024_register : + &mt9v022_register; + + /* Soft reset */ + ret = reg_write(client, MT9V022_RESET, 1); + if (ret < 0) + goto ei2c; + /* 15 clock cycles */ + udelay(200); + if (reg_read(client, MT9V022_RESET)) { + dev_err(&client->dev, "Resetting MT9V022 failed!\n"); + if (ret > 0) + ret = -EIO; + goto ei2c; + } + + /* Set monochrome or colour sensor type */ + if (sensor_type && (!strcmp("colour", sensor_type) || + !strcmp("color", sensor_type))) { + ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); + mt9v022->model = MT9V022IX7ATC; + mt9v022->fmts = mt9v022_colour_fmts; + } else { + ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11); + mt9v022->model = MT9V022IX7ATM; + mt9v022->fmts = mt9v022_monochrome_fmts; + } + + if (ret < 0) + goto ei2c; + + mt9v022->num_fmts = 0; + + /* + * This is a 10bit sensor, so by default we only allow 10bit. + * The platform may support different bus widths due to + * different routing of the data lines. + */ + if (ssdd->query_bus_param) + flags = ssdd->query_bus_param(ssdd); + else + flags = SOCAM_DATAWIDTH_10; + + if (flags & SOCAM_DATAWIDTH_10) + mt9v022->num_fmts++; + else + mt9v022->fmts++; + + if (flags & SOCAM_DATAWIDTH_8) + mt9v022->num_fmts++; + + mt9v022->fmt = &mt9v022->fmts[0]; + + dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", + data, mt9v022->model == MT9V022IX7ATM ? + "monochrome" : "colour"); + + ret = mt9v022_init(client); + if (ret < 0) + dev_err(&client->dev, "Failed to initialise the camera\n"); + +ei2c: + mt9v022_s_power(&mt9v022->subdev, 0); + return ret; +} + +static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + *lines = mt9v022->y_skip_top; + + return 0; +} + +static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = { + .g_volatile_ctrl = mt9v022_g_volatile_ctrl, + .s_ctrl = mt9v022_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9v022_g_register, + .s_register = mt9v022_s_register, +#endif + .s_power = mt9v022_s_power, +}; + +static int mt9v022_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9v022 *mt9v022 = to_mt9v022(client); + + if (code->pad || code->index >= mt9v022->num_fmts) + return -EINVAL; + + code->code = mt9v022->fmts[code->index].code; + return 0; +} + +static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE | + V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); + + return 0; +} + +static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct mt9v022 *mt9v022 = to_mt9v022(client); + unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); + unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; + int ret; + u16 pixclk = 0; + + if (ssdd->set_bus_param) { + ret = ssdd->set_bus_param(ssdd, 1 << (bps - 1)); + if (ret) + return ret; + } else if (bps != 10) { + /* + * Without board specific bus width settings we only support the + * sensors native bus width + */ + return -EINVAL; + } + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + pixclk |= 0x10; + + if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)) + pixclk |= 0x1; + + if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)) + pixclk |= 0x2; + + ret = reg_write(client, mt9v022->reg->pixclk_fv_lv, pixclk); + if (ret < 0) + return ret; + + if (!(flags & V4L2_MBUS_MASTER)) + mt9v022->chip_control &= ~0x8; + + ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", + pixclk, mt9v022->chip_control); + + return 0; +} + +static const struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { + .s_stream = mt9v022_s_stream, + .g_mbus_config = mt9v022_g_mbus_config, + .s_mbus_config = mt9v022_s_mbus_config, +}; + +static const struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = { + .g_skip_top_lines = mt9v022_g_skip_top_lines, +}; + +static const struct v4l2_subdev_pad_ops mt9v022_subdev_pad_ops = { + .enum_mbus_code = mt9v022_enum_mbus_code, + .get_selection = mt9v022_get_selection, + .set_selection = mt9v022_set_selection, + .get_fmt = mt9v022_get_fmt, + .set_fmt = mt9v022_set_fmt, +}; + +static const struct v4l2_subdev_ops mt9v022_subdev_ops = { + .core = &mt9v022_subdev_core_ops, + .video = &mt9v022_subdev_video_ops, + .sensor = &mt9v022_subdev_sensor_ops, + .pad = &mt9v022_subdev_pad_ops, +}; + +static int mt9v022_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9v022 *mt9v022; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct mt9v022_platform_data *pdata; + int ret; + + if (!ssdd) { + dev_err(&client->dev, "MT9V022 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9v022 = devm_kzalloc(&client->dev, sizeof(struct mt9v022), GFP_KERNEL); + if (!mt9v022) + return -ENOMEM; + + pdata = ssdd->drv_priv; + v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); + v4l2_ctrl_handler_init(&mt9v022->hdl, 6); + v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + + /* + * Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width + */ + mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl, + &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, + V4L2_EXPOSURE_AUTO); + mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 255, 1, 255); + + mt9v022->hblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_HBLANK, MT9V022_HORIZONTAL_BLANKING_MIN, + MT9V022_HORIZONTAL_BLANKING_MAX, 1, + MT9V022_HORIZONTAL_BLANKING_DEF); + + mt9v022->vblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, + V4L2_CID_VBLANK, MT9V022_VERTICAL_BLANKING_MIN, + MT9V022_VERTICAL_BLANKING_MAX, 1, + MT9V022_VERTICAL_BLANKING_DEF); + + mt9v022->subdev.ctrl_handler = &mt9v022->hdl; + if (mt9v022->hdl.error) { + int err = mt9v022->hdl.error; + + dev_err(&client->dev, "control initialisation err %d\n", err); + return err; + } + v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, + V4L2_EXPOSURE_MANUAL, true); + v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true); + + mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; + + /* + * On some platforms the first read out line is corrupted. + * Workaround it by skipping if indicated by platform data. + */ + mt9v022->y_skip_top = pdata ? pdata->y_skip_top : 0; + mt9v022->rect.left = MT9V022_COLUMN_SKIP; + mt9v022->rect.top = MT9V022_ROW_SKIP; + mt9v022->rect.width = MT9V022_MAX_WIDTH; + mt9v022->rect.height = MT9V022_MAX_HEIGHT; + + mt9v022->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(mt9v022->clk)) { + ret = PTR_ERR(mt9v022->clk); + goto eclkget; + } + + ret = mt9v022_video_probe(client); + if (ret) { + v4l2_clk_put(mt9v022->clk); +eclkget: + v4l2_ctrl_handler_free(&mt9v022->hdl); + } + + return ret; +} + +static int mt9v022_remove(struct i2c_client *client) +{ + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + v4l2_clk_put(mt9v022->clk); + v4l2_device_unregister_subdev(&mt9v022->subdev); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); + v4l2_ctrl_handler_free(&mt9v022->hdl); + + return 0; +} +static const struct i2c_device_id mt9v022_id[] = { + { "mt9v022", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9v022_id); + +static struct i2c_driver mt9v022_i2c_driver = { + .driver = { + .name = "mt9v022", + }, + .probe = mt9v022_probe, + .remove = mt9v022_remove, + .id_table = mt9v022_id, +}; + +module_i2c_driver(mt9v022_i2c_driver); + +MODULE_DESCRIPTION("Micron MT9V022 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/soc_camera/soc_ov5642.c b/drivers/staging/media/soc_camera/soc_ov5642.c new file mode 100644 index 000000000000..0931898c79dd --- /dev/null +++ b/drivers/staging/media/soc_camera/soc_ov5642.c @@ -0,0 +1,1087 @@ +/* + * Driver for OV5642 CMOS Image Sensor from Omnivision + * + * Copyright (C) 2011, Bastian Hecht + * + * Based on Sony IMX074 Camera Driver + * Copyright (C) 2010, Guennadi Liakhovetski + * + * Based on Omnivision OV7670 Camera Driver + * Copyright (C) 2006-7 Jonathan Corbet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* OV5642 registers */ +#define REG_CHIP_ID_HIGH 0x300a +#define REG_CHIP_ID_LOW 0x300b + +#define REG_WINDOW_START_X_HIGH 0x3800 +#define REG_WINDOW_START_X_LOW 0x3801 +#define REG_WINDOW_START_Y_HIGH 0x3802 +#define REG_WINDOW_START_Y_LOW 0x3803 +#define REG_WINDOW_WIDTH_HIGH 0x3804 +#define REG_WINDOW_WIDTH_LOW 0x3805 +#define REG_WINDOW_HEIGHT_HIGH 0x3806 +#define REG_WINDOW_HEIGHT_LOW 0x3807 +#define REG_OUT_WIDTH_HIGH 0x3808 +#define REG_OUT_WIDTH_LOW 0x3809 +#define REG_OUT_HEIGHT_HIGH 0x380a +#define REG_OUT_HEIGHT_LOW 0x380b +#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c +#define REG_OUT_TOTAL_WIDTH_LOW 0x380d +#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e +#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f +#define REG_OUTPUT_FORMAT 0x4300 +#define REG_ISP_CTRL_01 0x5001 +#define REG_AVG_WINDOW_END_X_HIGH 0x5682 +#define REG_AVG_WINDOW_END_X_LOW 0x5683 +#define REG_AVG_WINDOW_END_Y_HIGH 0x5686 +#define REG_AVG_WINDOW_END_Y_LOW 0x5687 + +/* active pixel array size */ +#define OV5642_SENSOR_SIZE_X 2592 +#define OV5642_SENSOR_SIZE_Y 1944 + +/* + * About OV5642 resolution, cropping and binning: + * This sensor supports it all, at least in the feature description. + * Unfortunately, no combination of appropriate registers settings could make + * the chip work the intended way. As it works with predefined register lists, + * some undocumented registers are presumably changed there to achieve their + * goals. + * This driver currently only works for resolutions up to 720 lines with a + * 1:1 scale. Hopefully these restrictions will be removed in the future. + */ +#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X +#define OV5642_MAX_HEIGHT 720 + +/* default sizes */ +#define OV5642_DEFAULT_WIDTH 1280 +#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT + +/* minimum extra blanking */ +#define BLANKING_EXTRA_WIDTH 500 +#define BLANKING_EXTRA_HEIGHT 20 + +/* + * the sensor's autoexposure is buggy when setting total_height low. + * It tries to expose longer than 1 frame period without taking care of it + * and this leads to weird output. So we set 1000 lines as minimum. + */ +#define BLANKING_MIN_HEIGHT 1000 + +struct regval_list { + u16 reg_num; + u8 value; +}; + +static struct regval_list ov5642_default_regs_init[] = { + { 0x3103, 0x93 }, + { 0x3008, 0x82 }, + { 0x3017, 0x7f }, + { 0x3018, 0xfc }, + { 0x3810, 0xc2 }, + { 0x3615, 0xf0 }, + { 0x3000, 0x0 }, + { 0x3001, 0x0 }, + { 0x3002, 0x0 }, + { 0x3003, 0x0 }, + { 0x3004, 0xff }, + { 0x3030, 0x2b }, + { 0x3011, 0x8 }, + { 0x3010, 0x10 }, + { 0x3604, 0x60 }, + { 0x3622, 0x60 }, + { 0x3621, 0x9 }, + { 0x3709, 0x0 }, + { 0x4000, 0x21 }, + { 0x401d, 0x22 }, + { 0x3600, 0x54 }, + { 0x3605, 0x4 }, + { 0x3606, 0x3f }, + { 0x3c01, 0x80 }, + { 0x300d, 0x22 }, + { 0x3623, 0x22 }, + { 0x5000, 0x4f }, + { 0x5020, 0x4 }, + { 0x5181, 0x79 }, + { 0x5182, 0x0 }, + { 0x5185, 0x22 }, + { 0x5197, 0x1 }, + { 0x5500, 0xa }, + { 0x5504, 0x0 }, + { 0x5505, 0x7f }, + { 0x5080, 0x8 }, + { 0x300e, 0x18 }, + { 0x4610, 0x0 }, + { 0x471d, 0x5 }, + { 0x4708, 0x6 }, + { 0x370c, 0xa0 }, + { 0x5687, 0x94 }, + { 0x501f, 0x0 }, + { 0x5000, 0x4f }, + { 0x5001, 0xcf }, + { 0x4300, 0x30 }, + { 0x4300, 0x30 }, + { 0x460b, 0x35 }, + { 0x471d, 0x0 }, + { 0x3002, 0xc }, + { 0x3002, 0x0 }, + { 0x4713, 0x3 }, + { 0x471c, 0x50 }, + { 0x4721, 0x2 }, + { 0x4402, 0x90 }, + { 0x460c, 0x22 }, + { 0x3815, 0x44 }, + { 0x3503, 0x7 }, + { 0x3501, 0x73 }, + { 0x3502, 0x80 }, + { 0x350b, 0x0 }, + { 0x3818, 0xc8 }, + { 0x3824, 0x11 }, + { 0x3a00, 0x78 }, + { 0x3a1a, 0x4 }, + { 0x3a13, 0x30 }, + { 0x3a18, 0x0 }, + { 0x3a19, 0x7c }, + { 0x3a08, 0x12 }, + { 0x3a09, 0xc0 }, + { 0x3a0a, 0xf }, + { 0x3a0b, 0xa0 }, + { 0x350c, 0x7 }, + { 0x350d, 0xd0 }, + { 0x3a0d, 0x8 }, + { 0x3a0e, 0x6 }, + { 0x3500, 0x0 }, + { 0x3501, 0x0 }, + { 0x3502, 0x0 }, + { 0x350a, 0x0 }, + { 0x350b, 0x0 }, + { 0x3503, 0x0 }, + { 0x3a0f, 0x3c }, + { 0x3a10, 0x32 }, + { 0x3a1b, 0x3c }, + { 0x3a1e, 0x32 }, + { 0x3a11, 0x80 }, + { 0x3a1f, 0x20 }, + { 0x3030, 0x2b }, + { 0x3a02, 0x0 }, + { 0x3a03, 0x7d }, + { 0x3a04, 0x0 }, + { 0x3a14, 0x0 }, + { 0x3a15, 0x7d }, + { 0x3a16, 0x0 }, + { 0x3a00, 0x78 }, + { 0x3a08, 0x9 }, + { 0x3a09, 0x60 }, + { 0x3a0a, 0x7 }, + { 0x3a0b, 0xd0 }, + { 0x3a0d, 0x10 }, + { 0x3a0e, 0xd }, + { 0x4407, 0x4 }, + { 0x5193, 0x70 }, + { 0x589b, 0x0 }, + { 0x589a, 0xc0 }, + { 0x401e, 0x20 }, + { 0x4001, 0x42 }, + { 0x401c, 0x6 }, + { 0x3825, 0xac }, + { 0x3827, 0xc }, + { 0x528a, 0x1 }, + { 0x528b, 0x4 }, + { 0x528c, 0x8 }, + { 0x528d, 0x10 }, + { 0x528e, 0x20 }, + { 0x528f, 0x28 }, + { 0x5290, 0x30 }, + { 0x5292, 0x0 }, + { 0x5293, 0x1 }, + { 0x5294, 0x0 }, + { 0x5295, 0x4 }, + { 0x5296, 0x0 }, + { 0x5297, 0x8 }, + { 0x5298, 0x0 }, + { 0x5299, 0x10 }, + { 0x529a, 0x0 }, + { 0x529b, 0x20 }, + { 0x529c, 0x0 }, + { 0x529d, 0x28 }, + { 0x529e, 0x0 }, + { 0x529f, 0x30 }, + { 0x5282, 0x0 }, + { 0x5300, 0x0 }, + { 0x5301, 0x20 }, + { 0x5302, 0x0 }, + { 0x5303, 0x7c }, + { 0x530c, 0x0 }, + { 0x530d, 0xc }, + { 0x530e, 0x20 }, + { 0x530f, 0x80 }, + { 0x5310, 0x20 }, + { 0x5311, 0x80 }, + { 0x5308, 0x20 }, + { 0x5309, 0x40 }, + { 0x5304, 0x0 }, + { 0x5305, 0x30 }, + { 0x5306, 0x0 }, + { 0x5307, 0x80 }, + { 0x5314, 0x8 }, + { 0x5315, 0x20 }, + { 0x5319, 0x30 }, + { 0x5316, 0x10 }, + { 0x5317, 0x0 }, + { 0x5318, 0x2 }, + { 0x5380, 0x1 }, + { 0x5381, 0x0 }, + { 0x5382, 0x0 }, + { 0x5383, 0x4e }, + { 0x5384, 0x0 }, + { 0x5385, 0xf }, + { 0x5386, 0x0 }, + { 0x5387, 0x0 }, + { 0x5388, 0x1 }, + { 0x5389, 0x15 }, + { 0x538a, 0x0 }, + { 0x538b, 0x31 }, + { 0x538c, 0x0 }, + { 0x538d, 0x0 }, + { 0x538e, 0x0 }, + { 0x538f, 0xf }, + { 0x5390, 0x0 }, + { 0x5391, 0xab }, + { 0x5392, 0x0 }, + { 0x5393, 0xa2 }, + { 0x5394, 0x8 }, + { 0x5480, 0x14 }, + { 0x5481, 0x21 }, + { 0x5482, 0x36 }, + { 0x5483, 0x57 }, + { 0x5484, 0x65 }, + { 0x5485, 0x71 }, + { 0x5486, 0x7d }, + { 0x5487, 0x87 }, + { 0x5488, 0x91 }, + { 0x5489, 0x9a }, + { 0x548a, 0xaa }, + { 0x548b, 0xb8 }, + { 0x548c, 0xcd }, + { 0x548d, 0xdd }, + { 0x548e, 0xea }, + { 0x548f, 0x1d }, + { 0x5490, 0x5 }, + { 0x5491, 0x0 }, + { 0x5492, 0x4 }, + { 0x5493, 0x20 }, + { 0x5494, 0x3 }, + { 0x5495, 0x60 }, + { 0x5496, 0x2 }, + { 0x5497, 0xb8 }, + { 0x5498, 0x2 }, + { 0x5499, 0x86 }, + { 0x549a, 0x2 }, + { 0x549b, 0x5b }, + { 0x549c, 0x2 }, + { 0x549d, 0x3b }, + { 0x549e, 0x2 }, + { 0x549f, 0x1c }, + { 0x54a0, 0x2 }, + { 0x54a1, 0x4 }, + { 0x54a2, 0x1 }, + { 0x54a3, 0xed }, + { 0x54a4, 0x1 }, + { 0x54a5, 0xc5 }, + { 0x54a6, 0x1 }, + { 0x54a7, 0xa5 }, + { 0x54a8, 0x1 }, + { 0x54a9, 0x6c }, + { 0x54aa, 0x1 }, + { 0x54ab, 0x41 }, + { 0x54ac, 0x1 }, + { 0x54ad, 0x20 }, + { 0x54ae, 0x0 }, + { 0x54af, 0x16 }, + { 0x54b0, 0x1 }, + { 0x54b1, 0x20 }, + { 0x54b2, 0x0 }, + { 0x54b3, 0x10 }, + { 0x54b4, 0x0 }, + { 0x54b5, 0xf0 }, + { 0x54b6, 0x0 }, + { 0x54b7, 0xdf }, + { 0x5402, 0x3f }, + { 0x5403, 0x0 }, + { 0x3406, 0x0 }, + { 0x5180, 0xff }, + { 0x5181, 0x52 }, + { 0x5182, 0x11 }, + { 0x5183, 0x14 }, + { 0x5184, 0x25 }, + { 0x5185, 0x24 }, + { 0x5186, 0x6 }, + { 0x5187, 0x8 }, + { 0x5188, 0x8 }, + { 0x5189, 0x7c }, + { 0x518a, 0x60 }, + { 0x518b, 0xb2 }, + { 0x518c, 0xb2 }, + { 0x518d, 0x44 }, + { 0x518e, 0x3d }, + { 0x518f, 0x58 }, + { 0x5190, 0x46 }, + { 0x5191, 0xf8 }, + { 0x5192, 0x4 }, + { 0x5193, 0x70 }, + { 0x5194, 0xf0 }, + { 0x5195, 0xf0 }, + { 0x5196, 0x3 }, + { 0x5197, 0x1 }, + { 0x5198, 0x4 }, + { 0x5199, 0x12 }, + { 0x519a, 0x4 }, + { 0x519b, 0x0 }, + { 0x519c, 0x6 }, + { 0x519d, 0x82 }, + { 0x519e, 0x0 }, + { 0x5025, 0x80 }, + { 0x3a0f, 0x38 }, + { 0x3a10, 0x30 }, + { 0x3a1b, 0x3a }, + { 0x3a1e, 0x2e }, + { 0x3a11, 0x60 }, + { 0x3a1f, 0x10 }, + { 0x5688, 0xa6 }, + { 0x5689, 0x6a }, + { 0x568a, 0xea }, + { 0x568b, 0xae }, + { 0x568c, 0xa6 }, + { 0x568d, 0x6a }, + { 0x568e, 0x62 }, + { 0x568f, 0x26 }, + { 0x5583, 0x40 }, + { 0x5584, 0x40 }, + { 0x5580, 0x2 }, + { 0x5000, 0xcf }, + { 0x5800, 0x27 }, + { 0x5801, 0x19 }, + { 0x5802, 0x12 }, + { 0x5803, 0xf }, + { 0x5804, 0x10 }, + { 0x5805, 0x15 }, + { 0x5806, 0x1e }, + { 0x5807, 0x2f }, + { 0x5808, 0x15 }, + { 0x5809, 0xd }, + { 0x580a, 0xa }, + { 0x580b, 0x9 }, + { 0x580c, 0xa }, + { 0x580d, 0xc }, + { 0x580e, 0x12 }, + { 0x580f, 0x19 }, + { 0x5810, 0xb }, + { 0x5811, 0x7 }, + { 0x5812, 0x4 }, + { 0x5813, 0x3 }, + { 0x5814, 0x3 }, + { 0x5815, 0x6 }, + { 0x5816, 0xa }, + { 0x5817, 0xf }, + { 0x5818, 0xa }, + { 0x5819, 0x5 }, + { 0x581a, 0x1 }, + { 0x581b, 0x0 }, + { 0x581c, 0x0 }, + { 0x581d, 0x3 }, + { 0x581e, 0x8 }, + { 0x581f, 0xc }, + { 0x5820, 0xa }, + { 0x5821, 0x5 }, + { 0x5822, 0x1 }, + { 0x5823, 0x0 }, + { 0x5824, 0x0 }, + { 0x5825, 0x3 }, + { 0x5826, 0x8 }, + { 0x5827, 0xc }, + { 0x5828, 0xe }, + { 0x5829, 0x8 }, + { 0x582a, 0x6 }, + { 0x582b, 0x4 }, + { 0x582c, 0x5 }, + { 0x582d, 0x7 }, + { 0x582e, 0xb }, + { 0x582f, 0x12 }, + { 0x5830, 0x18 }, + { 0x5831, 0x10 }, + { 0x5832, 0xc }, + { 0x5833, 0xa }, + { 0x5834, 0xb }, + { 0x5835, 0xe }, + { 0x5836, 0x15 }, + { 0x5837, 0x19 }, + { 0x5838, 0x32 }, + { 0x5839, 0x1f }, + { 0x583a, 0x18 }, + { 0x583b, 0x16 }, + { 0x583c, 0x17 }, + { 0x583d, 0x1e }, + { 0x583e, 0x26 }, + { 0x583f, 0x53 }, + { 0x5840, 0x10 }, + { 0x5841, 0xf }, + { 0x5842, 0xd }, + { 0x5843, 0xc }, + { 0x5844, 0xe }, + { 0x5845, 0x9 }, + { 0x5846, 0x11 }, + { 0x5847, 0x10 }, + { 0x5848, 0x10 }, + { 0x5849, 0x10 }, + { 0x584a, 0x10 }, + { 0x584b, 0xe }, + { 0x584c, 0x10 }, + { 0x584d, 0x10 }, + { 0x584e, 0x11 }, + { 0x584f, 0x10 }, + { 0x5850, 0xf }, + { 0x5851, 0xc }, + { 0x5852, 0xf }, + { 0x5853, 0x10 }, + { 0x5854, 0x10 }, + { 0x5855, 0xf }, + { 0x5856, 0xe }, + { 0x5857, 0xb }, + { 0x5858, 0x10 }, + { 0x5859, 0xd }, + { 0x585a, 0xd }, + { 0x585b, 0xc }, + { 0x585c, 0xc }, + { 0x585d, 0xc }, + { 0x585e, 0xb }, + { 0x585f, 0xc }, + { 0x5860, 0xc }, + { 0x5861, 0xc }, + { 0x5862, 0xd }, + { 0x5863, 0x8 }, + { 0x5864, 0x11 }, + { 0x5865, 0x18 }, + { 0x5866, 0x18 }, + { 0x5867, 0x19 }, + { 0x5868, 0x17 }, + { 0x5869, 0x19 }, + { 0x586a, 0x16 }, + { 0x586b, 0x13 }, + { 0x586c, 0x13 }, + { 0x586d, 0x12 }, + { 0x586e, 0x13 }, + { 0x586f, 0x16 }, + { 0x5870, 0x14 }, + { 0x5871, 0x12 }, + { 0x5872, 0x10 }, + { 0x5873, 0x11 }, + { 0x5874, 0x11 }, + { 0x5875, 0x16 }, + { 0x5876, 0x14 }, + { 0x5877, 0x11 }, + { 0x5878, 0x10 }, + { 0x5879, 0xf }, + { 0x587a, 0x10 }, + { 0x587b, 0x14 }, + { 0x587c, 0x13 }, + { 0x587d, 0x12 }, + { 0x587e, 0x11 }, + { 0x587f, 0x11 }, + { 0x5880, 0x12 }, + { 0x5881, 0x15 }, + { 0x5882, 0x14 }, + { 0x5883, 0x15 }, + { 0x5884, 0x15 }, + { 0x5885, 0x15 }, + { 0x5886, 0x13 }, + { 0x5887, 0x17 }, + { 0x3710, 0x10 }, + { 0x3632, 0x51 }, + { 0x3702, 0x10 }, + { 0x3703, 0xb2 }, + { 0x3704, 0x18 }, + { 0x370b, 0x40 }, + { 0x370d, 0x3 }, + { 0x3631, 0x1 }, + { 0x3632, 0x52 }, + { 0x3606, 0x24 }, + { 0x3620, 0x96 }, + { 0x5785, 0x7 }, + { 0x3a13, 0x30 }, + { 0x3600, 0x52 }, + { 0x3604, 0x48 }, + { 0x3606, 0x1b }, + { 0x370d, 0xb }, + { 0x370f, 0xc0 }, + { 0x3709, 0x1 }, + { 0x3823, 0x0 }, + { 0x5007, 0x0 }, + { 0x5009, 0x0 }, + { 0x5011, 0x0 }, + { 0x5013, 0x0 }, + { 0x519e, 0x0 }, + { 0x5086, 0x0 }, + { 0x5087, 0x0 }, + { 0x5088, 0x0 }, + { 0x5089, 0x0 }, + { 0x302b, 0x0 }, + { 0x3503, 0x7 }, + { 0x3011, 0x8 }, + { 0x350c, 0x2 }, + { 0x350d, 0xe4 }, + { 0x3621, 0xc9 }, + { 0x370a, 0x81 }, + { 0xffff, 0xff }, +}; + +static struct regval_list ov5642_default_regs_finalise[] = { + { 0x3810, 0xc2 }, + { 0x3818, 0xc9 }, + { 0x381c, 0x10 }, + { 0x381d, 0xa0 }, + { 0x381e, 0x5 }, + { 0x381f, 0xb0 }, + { 0x3820, 0x0 }, + { 0x3821, 0x0 }, + { 0x3824, 0x11 }, + { 0x3a08, 0x1b }, + { 0x3a09, 0xc0 }, + { 0x3a0a, 0x17 }, + { 0x3a0b, 0x20 }, + { 0x3a0d, 0x2 }, + { 0x3a0e, 0x1 }, + { 0x401c, 0x4 }, + { 0x5682, 0x5 }, + { 0x5683, 0x0 }, + { 0x5686, 0x2 }, + { 0x5687, 0xcc }, + { 0x5001, 0x4f }, + { 0x589b, 0x6 }, + { 0x589a, 0xc5 }, + { 0x3503, 0x0 }, + { 0x460c, 0x20 }, + { 0x460b, 0x37 }, + { 0x471c, 0xd0 }, + { 0x471d, 0x5 }, + { 0x3815, 0x1 }, + { 0x3818, 0xc1 }, + { 0x501f, 0x0 }, + { 0x5002, 0xe0 }, + { 0x4300, 0x32 }, /* UYVY */ + { 0x3002, 0x1c }, + { 0x4800, 0x14 }, + { 0x4801, 0xf }, + { 0x3007, 0x3b }, + { 0x300e, 0x4 }, + { 0x4803, 0x50 }, + { 0x3815, 0x1 }, + { 0x4713, 0x2 }, + { 0x4842, 0x1 }, + { 0x300f, 0xe }, + { 0x3003, 0x3 }, + { 0x3003, 0x1 }, + { 0xffff, 0xff }, +}; + +struct ov5642_datafmt { + u32 code; + enum v4l2_colorspace colorspace; +}; + +struct ov5642 { + struct v4l2_subdev subdev; + const struct ov5642_datafmt *fmt; + struct v4l2_rect crop_rect; + struct v4l2_clk *clk; + + /* blanking information */ + int total_width; + int total_height; +}; + +static const struct ov5642_datafmt ov5642_colour_fmts[] = { + {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, +}; + +static struct ov5642 *to_ov5642(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct ov5642, subdev); +} + +/* Find a data format by a pixel code in an array */ +static const struct ov5642_datafmt + *ov5642_find_datafmt(u32 code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++) + if (ov5642_colour_fmts[i].code == code) + return ov5642_colour_fmts + i; + + return NULL; +} + +static int reg_read(struct i2c_client *client, u16 reg, u8 *val) +{ + int ret; + /* We have 16-bit i2c addresses - care for endianness */ + unsigned char data[2] = { reg >> 8, reg & 0xff }; + + ret = i2c_master_send(client, data, 2); + if (ret < 2) { + dev_err(&client->dev, "%s: i2c read error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + + ret = i2c_master_recv(client, val, 1); + if (ret < 1) { + dev_err(&client->dev, "%s: i2c read error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + return 0; +} + +static int reg_write(struct i2c_client *client, u16 reg, u8 val) +{ + int ret; + unsigned char data[3] = { reg >> 8, reg & 0xff, val }; + + ret = i2c_master_send(client, data, 3); + if (ret < 3) { + dev_err(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +/* + * convenience function to write 16 bit register values that are split up + * into two consecutive high and low parts + */ +static int reg_write16(struct i2c_client *client, u16 reg, u16 val16) +{ + int ret; + + ret = reg_write(client, reg, val16 >> 8); + if (ret) + return ret; + return reg_write(client, reg + 1, val16 & 0x00ff); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u8 val; + + if (reg->reg & ~0xffff) + return -EINVAL; + + reg->size = 1; + + ret = reg_read(client, reg->reg, &val); + if (!ret) + reg->val = (__u64)val; + + return ret; +} + +static int ov5642_set_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg & ~0xffff || reg->val & ~0xff) + return -EINVAL; + + return reg_write(client, reg->reg, reg->val); +} +#endif + +static int ov5642_write_array(struct i2c_client *client, + struct regval_list *vals) +{ + while (vals->reg_num != 0xffff || vals->value != 0xff) { + int ret = reg_write(client, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + } + dev_dbg(&client->dev, "Register list loaded\n"); + return 0; +} + +static int ov5642_set_resolution(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + int width = priv->crop_rect.width; + int height = priv->crop_rect.height; + int total_width = priv->total_width; + int total_height = priv->total_height; + int start_x = (OV5642_SENSOR_SIZE_X - width) / 2; + int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2; + int ret; + + /* + * This should set the starting point for cropping. + * Doesn't work so far. + */ + ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x); + if (!ret) + ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y); + if (!ret) { + priv->crop_rect.left = start_x; + priv->crop_rect.top = start_y; + } + + if (!ret) + ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width); + if (!ret) + ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height); + if (ret) + return ret; + priv->crop_rect.width = width; + priv->crop_rect.height = height; + + /* Set the output window size. Only 1:1 scale is supported so far. */ + ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width); + if (!ret) + ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height); + + /* Total width = output size + blanking */ + if (!ret) + ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width); + if (!ret) + ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height); + + /* Sets the window for AWB calculations */ + if (!ret) + ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width); + if (!ret) + ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height); + + return ret; +} + +static int ov5642_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code); + + if (format->pad) + return -EINVAL; + + mf->width = priv->crop_rect.width; + mf->height = priv->crop_rect.height; + + if (!fmt) { + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mf->code = ov5642_colour_fmts[0].code; + mf->colorspace = ov5642_colour_fmts[0].colorspace; + } + + mf->field = V4L2_FIELD_NONE; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + priv->fmt = fmt; + else + cfg->try_fmt = *mf; + return 0; +} + +static int ov5642_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + + const struct ov5642_datafmt *fmt = priv->fmt; + + if (format->pad) + return -EINVAL; + + mf->code = fmt->code; + mf->colorspace = fmt->colorspace; + mf->width = priv->crop_rect.width; + mf->height = priv->crop_rect.height; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov5642_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index >= ARRAY_SIZE(ov5642_colour_fmts)) + return -EINVAL; + + code->code = ov5642_colour_fmts[code->index].code; + return 0; +} + +static int ov5642_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + struct v4l2_rect rect = sel->r; + int ret; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + v4l_bound_align_image(&rect.width, 48, OV5642_MAX_WIDTH, 1, + &rect.height, 32, OV5642_MAX_HEIGHT, 1, 0); + + priv->crop_rect.width = rect.width; + priv->crop_rect.height = rect.height; + priv->total_width = rect.width + BLANKING_EXTRA_WIDTH; + priv->total_height = max_t(int, rect.height + + BLANKING_EXTRA_HEIGHT, + BLANKING_MIN_HEIGHT); + priv->crop_rect.width = rect.width; + priv->crop_rect.height = rect.height; + + ret = ov5642_write_array(client, ov5642_default_regs_init); + if (!ret) + ret = ov5642_set_resolution(sd); + if (!ret) + ret = ov5642_write_array(client, ov5642_default_regs_finalise); + + return ret; +} + +static int ov5642_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5642 *priv = to_ov5642(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = OV5642_MAX_WIDTH; + sel->r.height = OV5642_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = priv->crop_rect; + return 0; + default: + return -EINVAL; + } +} + +static int ov5642_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + cfg->type = V4L2_MBUS_CSI2_DPHY; + cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + return 0; +} + +static int ov5642_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct ov5642 *priv = to_ov5642(client); + int ret; + + if (!on) + return soc_camera_power_off(&client->dev, ssdd, priv->clk); + + ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); + if (ret < 0) + return ret; + + ret = ov5642_write_array(client, ov5642_default_regs_init); + if (!ret) + ret = ov5642_set_resolution(sd); + if (!ret) + ret = ov5642_write_array(client, ov5642_default_regs_finalise); + + return ret; +} + +static const struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { + .g_mbus_config = ov5642_g_mbus_config, +}; + +static const struct v4l2_subdev_pad_ops ov5642_subdev_pad_ops = { + .enum_mbus_code = ov5642_enum_mbus_code, + .get_selection = ov5642_get_selection, + .set_selection = ov5642_set_selection, + .get_fmt = ov5642_get_fmt, + .set_fmt = ov5642_set_fmt, +}; + +static const struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { + .s_power = ov5642_s_power, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov5642_get_register, + .s_register = ov5642_set_register, +#endif +}; + +static const struct v4l2_subdev_ops ov5642_subdev_ops = { + .core = &ov5642_subdev_core_ops, + .video = &ov5642_subdev_video_ops, + .pad = &ov5642_subdev_pad_ops, +}; + +static int ov5642_video_probe(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + int ret; + u8 id_high, id_low; + u16 id; + + ret = ov5642_s_power(subdev, 1); + if (ret < 0) + return ret; + + /* Read sensor Model ID */ + ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high); + if (ret < 0) + goto done; + + id = id_high << 8; + + ret = reg_read(client, REG_CHIP_ID_LOW, &id_low); + if (ret < 0) + goto done; + + id |= id_low; + + dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); + + if (id != 0x5642) { + ret = -ENODEV; + goto done; + } + + ret = 0; + +done: + ov5642_s_power(subdev, 0); + return ret; +} + +static int ov5642_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov5642 *priv; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + int ret; + + if (!ssdd) { + dev_err(&client->dev, "OV5642: missing platform data!\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&client->dev, sizeof(struct ov5642), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops); + + priv->fmt = &ov5642_colour_fmts[0]; + + priv->crop_rect.width = OV5642_DEFAULT_WIDTH; + priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; + priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; + priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; + priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; + priv->total_height = BLANKING_MIN_HEIGHT; + + priv->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + ret = ov5642_video_probe(client); + if (ret < 0) + v4l2_clk_put(priv->clk); + + return ret; +} + +static int ov5642_remove(struct i2c_client *client) +{ + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct ov5642 *priv = to_ov5642(client); + + v4l2_clk_put(priv->clk); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); + + return 0; +} + +static const struct i2c_device_id ov5642_id[] = { + { "ov5642", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov5642_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov5642_of_match[] = { + { .compatible = "ovti,ov5642" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ov5642_of_match); +#endif + +static struct i2c_driver ov5642_i2c_driver = { + .driver = { + .name = "ov5642", + .of_match_table = of_match_ptr(ov5642_of_match), + }, + .probe = ov5642_probe, + .remove = ov5642_remove, + .id_table = ov5642_id, +}; + +module_i2c_driver(ov5642_i2c_driver); + +MODULE_DESCRIPTION("Omnivision OV5642 Camera driver"); +MODULE_AUTHOR("Bastian Hecht "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc_ov9740.c b/drivers/staging/media/soc_camera/soc_ov9740.c new file mode 100644 index 000000000000..a07d3145d1b4 --- /dev/null +++ b/drivers/staging/media/soc_camera/soc_ov9740.c @@ -0,0 +1,996 @@ +/* + * OmniVision OV9740 Camera Driver + * + * Copyright (C) 2011 NVIDIA Corporation + * + * Based on ov9640 camera driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev) + +/* General Status Registers */ +#define OV9740_MODEL_ID_HI 0x0000 +#define OV9740_MODEL_ID_LO 0x0001 +#define OV9740_REVISION_NUMBER 0x0002 +#define OV9740_MANUFACTURER_ID 0x0003 +#define OV9740_SMIA_VERSION 0x0004 + +/* General Setup Registers */ +#define OV9740_MODE_SELECT 0x0100 +#define OV9740_IMAGE_ORT 0x0101 +#define OV9740_SOFTWARE_RESET 0x0103 +#define OV9740_GRP_PARAM_HOLD 0x0104 +#define OV9740_MSK_CORRUP_FM 0x0105 + +/* Timing Setting */ +#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */ +#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */ +#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */ +#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */ +#define OV9740_X_ADDR_START_HI 0x0344 +#define OV9740_X_ADDR_START_LO 0x0345 +#define OV9740_Y_ADDR_START_HI 0x0346 +#define OV9740_Y_ADDR_START_LO 0x0347 +#define OV9740_X_ADDR_END_HI 0x0348 +#define OV9740_X_ADDR_END_LO 0x0349 +#define OV9740_Y_ADDR_END_HI 0x034a +#define OV9740_Y_ADDR_END_LO 0x034b +#define OV9740_X_OUTPUT_SIZE_HI 0x034c +#define OV9740_X_OUTPUT_SIZE_LO 0x034d +#define OV9740_Y_OUTPUT_SIZE_HI 0x034e +#define OV9740_Y_OUTPUT_SIZE_LO 0x034f + +/* IO Control Registers */ +#define OV9740_IO_CREL00 0x3002 +#define OV9740_IO_CREL01 0x3004 +#define OV9740_IO_CREL02 0x3005 +#define OV9740_IO_OUTPUT_SEL01 0x3026 +#define OV9740_IO_OUTPUT_SEL02 0x3027 + +/* AWB Registers */ +#define OV9740_AWB_MANUAL_CTRL 0x3406 + +/* Analog Control Registers */ +#define OV9740_ANALOG_CTRL01 0x3601 +#define OV9740_ANALOG_CTRL02 0x3602 +#define OV9740_ANALOG_CTRL03 0x3603 +#define OV9740_ANALOG_CTRL04 0x3604 +#define OV9740_ANALOG_CTRL10 0x3610 +#define OV9740_ANALOG_CTRL12 0x3612 +#define OV9740_ANALOG_CTRL15 0x3615 +#define OV9740_ANALOG_CTRL20 0x3620 +#define OV9740_ANALOG_CTRL21 0x3621 +#define OV9740_ANALOG_CTRL22 0x3622 +#define OV9740_ANALOG_CTRL30 0x3630 +#define OV9740_ANALOG_CTRL31 0x3631 +#define OV9740_ANALOG_CTRL32 0x3632 +#define OV9740_ANALOG_CTRL33 0x3633 + +/* Sensor Control */ +#define OV9740_SENSOR_CTRL03 0x3703 +#define OV9740_SENSOR_CTRL04 0x3704 +#define OV9740_SENSOR_CTRL05 0x3705 +#define OV9740_SENSOR_CTRL07 0x3707 + +/* Timing Control */ +#define OV9740_TIMING_CTRL17 0x3817 +#define OV9740_TIMING_CTRL19 0x3819 +#define OV9740_TIMING_CTRL33 0x3833 +#define OV9740_TIMING_CTRL35 0x3835 + +/* Banding Filter */ +#define OV9740_AEC_MAXEXPO_60_H 0x3a02 +#define OV9740_AEC_MAXEXPO_60_L 0x3a03 +#define OV9740_AEC_B50_STEP_HI 0x3a08 +#define OV9740_AEC_B50_STEP_LO 0x3a09 +#define OV9740_AEC_B60_STEP_HI 0x3a0a +#define OV9740_AEC_B60_STEP_LO 0x3a0b +#define OV9740_AEC_CTRL0D 0x3a0d +#define OV9740_AEC_CTRL0E 0x3a0e +#define OV9740_AEC_MAXEXPO_50_H 0x3a14 +#define OV9740_AEC_MAXEXPO_50_L 0x3a15 + +/* AEC/AGC Control */ +#define OV9740_AEC_ENABLE 0x3503 +#define OV9740_GAIN_CEILING_01 0x3a18 +#define OV9740_GAIN_CEILING_02 0x3a19 +#define OV9740_AEC_HI_THRESHOLD 0x3a11 +#define OV9740_AEC_3A1A 0x3a1a +#define OV9740_AEC_CTRL1B_WPT2 0x3a1b +#define OV9740_AEC_CTRL0F_WPT 0x3a0f +#define OV9740_AEC_CTRL10_BPT 0x3a10 +#define OV9740_AEC_CTRL1E_BPT2 0x3a1e +#define OV9740_AEC_LO_THRESHOLD 0x3a1f + +/* BLC Control */ +#define OV9740_BLC_AUTO_ENABLE 0x4002 +#define OV9740_BLC_MODE 0x4005 + +/* VFIFO */ +#define OV9740_VFIFO_READ_START_HI 0x4608 +#define OV9740_VFIFO_READ_START_LO 0x4609 + +/* DVP Control */ +#define OV9740_DVP_VSYNC_CTRL02 0x4702 +#define OV9740_DVP_VSYNC_MODE 0x4704 +#define OV9740_DVP_VSYNC_CTRL06 0x4706 + +/* PLL Setting */ +#define OV9740_PLL_MODE_CTRL01 0x3104 +#define OV9740_PRE_PLL_CLK_DIV 0x0305 +#define OV9740_PLL_MULTIPLIER 0x0307 +#define OV9740_VT_SYS_CLK_DIV 0x0303 +#define OV9740_VT_PIX_CLK_DIV 0x0301 +#define OV9740_PLL_CTRL3010 0x3010 +#define OV9740_VFIFO_CTRL00 0x460e + +/* ISP Control */ +#define OV9740_ISP_CTRL00 0x5000 +#define OV9740_ISP_CTRL01 0x5001 +#define OV9740_ISP_CTRL03 0x5003 +#define OV9740_ISP_CTRL05 0x5005 +#define OV9740_ISP_CTRL12 0x5012 +#define OV9740_ISP_CTRL19 0x5019 +#define OV9740_ISP_CTRL1A 0x501a +#define OV9740_ISP_CTRL1E 0x501e +#define OV9740_ISP_CTRL1F 0x501f +#define OV9740_ISP_CTRL20 0x5020 +#define OV9740_ISP_CTRL21 0x5021 + +/* AWB */ +#define OV9740_AWB_CTRL00 0x5180 +#define OV9740_AWB_CTRL01 0x5181 +#define OV9740_AWB_CTRL02 0x5182 +#define OV9740_AWB_CTRL03 0x5183 +#define OV9740_AWB_ADV_CTRL01 0x5184 +#define OV9740_AWB_ADV_CTRL02 0x5185 +#define OV9740_AWB_ADV_CTRL03 0x5186 +#define OV9740_AWB_ADV_CTRL04 0x5187 +#define OV9740_AWB_ADV_CTRL05 0x5188 +#define OV9740_AWB_ADV_CTRL06 0x5189 +#define OV9740_AWB_ADV_CTRL07 0x518a +#define OV9740_AWB_ADV_CTRL08 0x518b +#define OV9740_AWB_ADV_CTRL09 0x518c +#define OV9740_AWB_ADV_CTRL10 0x518d +#define OV9740_AWB_ADV_CTRL11 0x518e +#define OV9740_AWB_CTRL0F 0x518f +#define OV9740_AWB_CTRL10 0x5190 +#define OV9740_AWB_CTRL11 0x5191 +#define OV9740_AWB_CTRL12 0x5192 +#define OV9740_AWB_CTRL13 0x5193 +#define OV9740_AWB_CTRL14 0x5194 + +/* MIPI Control */ +#define OV9740_MIPI_CTRL00 0x4800 +#define OV9740_MIPI_3837 0x3837 +#define OV9740_MIPI_CTRL01 0x4801 +#define OV9740_MIPI_CTRL03 0x4803 +#define OV9740_MIPI_CTRL05 0x4805 +#define OV9740_VFIFO_RD_CTRL 0x4601 +#define OV9740_MIPI_CTRL_3012 0x3012 +#define OV9740_SC_CMMM_MIPI_CTR 0x3014 + +#define OV9740_MAX_WIDTH 1280 +#define OV9740_MAX_HEIGHT 720 + +/* Misc. structures */ +struct ov9740_reg { + u16 reg; + u8 val; +}; + +struct ov9740_priv { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct v4l2_clk *clk; + + u16 model; + u8 revision; + u8 manid; + u8 smiaver; + + bool flag_vflip; + bool flag_hflip; + + /* For suspend/resume. */ + struct v4l2_mbus_framefmt current_mf; + bool current_enable; +}; + +static const struct ov9740_reg ov9740_defaults[] = { + /* Software Reset */ + { OV9740_SOFTWARE_RESET, 0x01 }, + + /* Banding Filter */ + { OV9740_AEC_B50_STEP_HI, 0x00 }, + { OV9740_AEC_B50_STEP_LO, 0xe8 }, + { OV9740_AEC_CTRL0E, 0x03 }, + { OV9740_AEC_MAXEXPO_50_H, 0x15 }, + { OV9740_AEC_MAXEXPO_50_L, 0xc6 }, + { OV9740_AEC_B60_STEP_HI, 0x00 }, + { OV9740_AEC_B60_STEP_LO, 0xc0 }, + { OV9740_AEC_CTRL0D, 0x04 }, + { OV9740_AEC_MAXEXPO_60_H, 0x18 }, + { OV9740_AEC_MAXEXPO_60_L, 0x20 }, + + /* LC */ + { 0x5842, 0x02 }, { 0x5843, 0x5e }, { 0x5844, 0x04 }, { 0x5845, 0x32 }, + { 0x5846, 0x03 }, { 0x5847, 0x29 }, { 0x5848, 0x02 }, { 0x5849, 0xcc }, + + /* Un-documented OV9740 registers */ + { 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 }, + { 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c }, + { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580a, 0x0e }, { 0x580b, 0x16 }, + { 0x580c, 0x06 }, { 0x580d, 0x02 }, { 0x580e, 0x00 }, { 0x580f, 0x00 }, + { 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 }, + { 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 }, + { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581a, 0x07 }, { 0x581b, 0x08 }, + { 0x581c, 0x0b }, { 0x581d, 0x14 }, { 0x581e, 0x28 }, { 0x581f, 0x23 }, + { 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a }, + { 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f }, + { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582a, 0x8f }, { 0x582b, 0x9e }, + { 0x582c, 0x8f }, { 0x582d, 0x9f }, { 0x582e, 0x4f }, { 0x582f, 0x87 }, + { 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f }, + { 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf }, + { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583a, 0x9f }, { 0x583b, 0x7f }, + { 0x583c, 0x5f }, + + /* Y Gamma */ + { 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e }, + { 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 }, + { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548a, 0xa4 }, { 0x548b, 0xb1 }, + { 0x548c, 0xc6 }, { 0x548d, 0xd8 }, { 0x548e, 0xe9 }, + + /* UV Gamma */ + { 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 }, + { 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 }, + { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549a, 0x02 }, { 0x549b, 0xeb }, + { 0x549c, 0x02 }, { 0x549d, 0xa0 }, { 0x549e, 0x02 }, { 0x549f, 0x67 }, + { 0x54a0, 0x02 }, { 0x54a1, 0x3b }, { 0x54a2, 0x02 }, { 0x54a3, 0x18 }, + { 0x54a4, 0x01 }, { 0x54a5, 0xe7 }, { 0x54a6, 0x01 }, { 0x54a7, 0xc3 }, + { 0x54a8, 0x01 }, { 0x54a9, 0x94 }, { 0x54aa, 0x01 }, { 0x54ab, 0x72 }, + { 0x54ac, 0x01 }, { 0x54ad, 0x57 }, + + /* AWB */ + { OV9740_AWB_CTRL00, 0xf0 }, + { OV9740_AWB_CTRL01, 0x00 }, + { OV9740_AWB_CTRL02, 0x41 }, + { OV9740_AWB_CTRL03, 0x42 }, + { OV9740_AWB_ADV_CTRL01, 0x8a }, + { OV9740_AWB_ADV_CTRL02, 0x61 }, + { OV9740_AWB_ADV_CTRL03, 0xce }, + { OV9740_AWB_ADV_CTRL04, 0xa8 }, + { OV9740_AWB_ADV_CTRL05, 0x17 }, + { OV9740_AWB_ADV_CTRL06, 0x1f }, + { OV9740_AWB_ADV_CTRL07, 0x27 }, + { OV9740_AWB_ADV_CTRL08, 0x41 }, + { OV9740_AWB_ADV_CTRL09, 0x34 }, + { OV9740_AWB_ADV_CTRL10, 0xf0 }, + { OV9740_AWB_ADV_CTRL11, 0x10 }, + { OV9740_AWB_CTRL0F, 0xff }, + { OV9740_AWB_CTRL10, 0x00 }, + { OV9740_AWB_CTRL11, 0xff }, + { OV9740_AWB_CTRL12, 0x00 }, + { OV9740_AWB_CTRL13, 0xff }, + { OV9740_AWB_CTRL14, 0x00 }, + + /* CIP */ + { 0x530d, 0x12 }, + + /* CMX */ + { 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 }, + { 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 }, + { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538a, 0x00 }, { 0x538b, 0x20 }, + { 0x538c, 0x00 }, { 0x538d, 0x00 }, { 0x538e, 0x00 }, { 0x538f, 0x16 }, + { 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 }, + { 0x5394, 0x18 }, + + /* 50/60 Detection */ + { 0x3c0a, 0x9c }, { 0x3c0b, 0x3f }, + + /* Output Select */ + { OV9740_IO_OUTPUT_SEL01, 0x00 }, + { OV9740_IO_OUTPUT_SEL02, 0x00 }, + { OV9740_IO_CREL00, 0x00 }, + { OV9740_IO_CREL01, 0x00 }, + { OV9740_IO_CREL02, 0x00 }, + + /* AWB Control */ + { OV9740_AWB_MANUAL_CTRL, 0x00 }, + + /* Analog Control */ + { OV9740_ANALOG_CTRL03, 0xaa }, + { OV9740_ANALOG_CTRL32, 0x2f }, + { OV9740_ANALOG_CTRL20, 0x66 }, + { OV9740_ANALOG_CTRL21, 0xc0 }, + { OV9740_ANALOG_CTRL31, 0x52 }, + { OV9740_ANALOG_CTRL33, 0x50 }, + { OV9740_ANALOG_CTRL30, 0xca }, + { OV9740_ANALOG_CTRL04, 0x0c }, + { OV9740_ANALOG_CTRL01, 0x40 }, + { OV9740_ANALOG_CTRL02, 0x16 }, + { OV9740_ANALOG_CTRL10, 0xa1 }, + { OV9740_ANALOG_CTRL12, 0x24 }, + { OV9740_ANALOG_CTRL22, 0x9f }, + { OV9740_ANALOG_CTRL15, 0xf0 }, + + /* Sensor Control */ + { OV9740_SENSOR_CTRL03, 0x42 }, + { OV9740_SENSOR_CTRL04, 0x10 }, + { OV9740_SENSOR_CTRL05, 0x45 }, + { OV9740_SENSOR_CTRL07, 0x14 }, + + /* Timing Control */ + { OV9740_TIMING_CTRL33, 0x04 }, + { OV9740_TIMING_CTRL35, 0x02 }, + { OV9740_TIMING_CTRL19, 0x6e }, + { OV9740_TIMING_CTRL17, 0x94 }, + + /* AEC/AGC Control */ + { OV9740_AEC_ENABLE, 0x10 }, + { OV9740_GAIN_CEILING_01, 0x00 }, + { OV9740_GAIN_CEILING_02, 0x7f }, + { OV9740_AEC_HI_THRESHOLD, 0xa0 }, + { OV9740_AEC_3A1A, 0x05 }, + { OV9740_AEC_CTRL1B_WPT2, 0x50 }, + { OV9740_AEC_CTRL0F_WPT, 0x50 }, + { OV9740_AEC_CTRL10_BPT, 0x4c }, + { OV9740_AEC_CTRL1E_BPT2, 0x4c }, + { OV9740_AEC_LO_THRESHOLD, 0x26 }, + + /* BLC Control */ + { OV9740_BLC_AUTO_ENABLE, 0x45 }, + { OV9740_BLC_MODE, 0x18 }, + + /* DVP Control */ + { OV9740_DVP_VSYNC_CTRL02, 0x04 }, + { OV9740_DVP_VSYNC_MODE, 0x00 }, + { OV9740_DVP_VSYNC_CTRL06, 0x08 }, + + /* PLL Setting */ + { OV9740_PLL_MODE_CTRL01, 0x20 }, + { OV9740_PRE_PLL_CLK_DIV, 0x03 }, + { OV9740_PLL_MULTIPLIER, 0x4c }, + { OV9740_VT_SYS_CLK_DIV, 0x01 }, + { OV9740_VT_PIX_CLK_DIV, 0x08 }, + { OV9740_PLL_CTRL3010, 0x01 }, + { OV9740_VFIFO_CTRL00, 0x82 }, + + /* Timing Setting */ + /* VTS */ + { OV9740_FRM_LENGTH_LN_HI, 0x03 }, + { OV9740_FRM_LENGTH_LN_LO, 0x07 }, + /* HTS */ + { OV9740_LN_LENGTH_PCK_HI, 0x06 }, + { OV9740_LN_LENGTH_PCK_LO, 0x62 }, + + /* MIPI Control */ + { OV9740_MIPI_CTRL00, 0x44 }, /* 0x64 for discontinuous clk */ + { OV9740_MIPI_3837, 0x01 }, + { OV9740_MIPI_CTRL01, 0x0f }, + { OV9740_MIPI_CTRL03, 0x05 }, + { OV9740_MIPI_CTRL05, 0x10 }, + { OV9740_VFIFO_RD_CTRL, 0x16 }, + { OV9740_MIPI_CTRL_3012, 0x70 }, + { OV9740_SC_CMMM_MIPI_CTR, 0x01 }, + + /* YUYV order */ + { OV9740_ISP_CTRL19, 0x02 }, +}; + +static u32 ov9740_codes[] = { + MEDIA_BUS_FMT_YUYV8_2X8, +}; + +/* read a register */ +static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val) +{ + int ret; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = (u8 *)®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = val, + }, + }; + + reg = swab16(reg); + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) { + dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg); + return ret; + } + + return 0; +} + +/* write a register */ +static int ov9740_reg_write(struct i2c_client *client, u16 reg, u8 val) +{ + struct i2c_msg msg; + struct { + u16 reg; + u8 val; + } __packed buf; + int ret; + + reg = swab16(reg); + + buf.reg = reg; + buf.val = val; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = (u8 *)&buf; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg); + return ret; + } + + return 0; +} + + +/* Read a register, alter its bits, write it back */ +static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset) +{ + u8 val; + int ret; + + ret = ov9740_reg_read(client, reg, &val); + if (ret < 0) { + dev_err(&client->dev, + "[Read]-Modify-Write of register 0x%04x failed!\n", + reg); + return ret; + } + + val |= set; + val &= ~unset; + + ret = ov9740_reg_write(client, reg, val); + if (ret < 0) { + dev_err(&client->dev, + "Read-Modify-[Write] of register 0x%04x failed!\n", + reg); + return ret; + } + + return 0; +} + +static int ov9740_reg_write_array(struct i2c_client *client, + const struct ov9740_reg *regarray, + int regarraylen) +{ + int i; + int ret; + + for (i = 0; i < regarraylen; i++) { + ret = ov9740_reg_write(client, + regarray[i].reg, regarray[i].val); + if (ret < 0) + return ret; + } + + return 0; +} + +/* Start/Stop streaming from the device */ +static int ov9740_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov9740_priv *priv = to_ov9740(sd); + int ret; + + /* Program orientation register. */ + if (priv->flag_vflip) + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x2, 0); + else + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x2); + if (ret < 0) + return ret; + + if (priv->flag_hflip) + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x1, 0); + else + ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x1); + if (ret < 0) + return ret; + + if (enable) { + dev_dbg(&client->dev, "Enabling Streaming\n"); + /* Start Streaming */ + ret = ov9740_reg_write(client, OV9740_MODE_SELECT, 0x01); + + } else { + dev_dbg(&client->dev, "Disabling Streaming\n"); + /* Software Reset */ + ret = ov9740_reg_write(client, OV9740_SOFTWARE_RESET, 0x01); + if (!ret) + /* Setting Streaming to Standby */ + ret = ov9740_reg_write(client, OV9740_MODE_SELECT, + 0x00); + } + + priv->current_enable = enable; + + return ret; +} + +/* select nearest higher resolution for capture */ +static void ov9740_res_roundup(u32 *width, u32 *height) +{ + /* Width must be a multiple of 4 pixels. */ + *width = ALIGN(*width, 4); + + /* Max resolution is 1280x720 (720p). */ + if (*width > OV9740_MAX_WIDTH) + *width = OV9740_MAX_WIDTH; + + if (*height > OV9740_MAX_HEIGHT) + *height = OV9740_MAX_HEIGHT; +} + +/* Setup registers according to resolution and color encoding */ +static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height) +{ + u32 x_start; + u32 y_start; + u32 x_end; + u32 y_end; + bool scaling = false; + u32 scale_input_x; + u32 scale_input_y; + int ret; + + if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT)) + scaling = true; + + /* + * Try to use as much of the sensor area as possible when supporting + * smaller resolutions. Depending on the aspect ratio of the + * chosen resolution, we can either use the full width of the sensor, + * or the full height of the sensor (or both if the aspect ratio is + * the same as 1280x720. + */ + if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) { + scale_input_x = (OV9740_MAX_HEIGHT * width) / height; + scale_input_y = OV9740_MAX_HEIGHT; + } else { + scale_input_x = OV9740_MAX_WIDTH; + scale_input_y = (OV9740_MAX_WIDTH * height) / width; + } + + /* These describe the area of the sensor to use. */ + x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2; + y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2; + x_end = x_start + scale_input_x - 1; + y_end = y_start + scale_input_y - 1; + + ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff); + if (ret) + goto done; + + ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff); + if (ret) + goto done; + + ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff); + if (ret) + goto done; + + ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff); + if (ret) + goto done; + + ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI, + (scale_input_x - width) >> 8); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO, + (scale_input_x - width) & 0xff); + if (ret) + goto done; + + ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef | + (scaling << 4)); + if (ret) + goto done; + ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff); + +done: + return ret; +} + +/* set the format we will capture in */ +static int ov9740_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov9740_priv *priv = to_ov9740(sd); + int ret; + + ret = ov9740_reg_write_array(client, ov9740_defaults, + ARRAY_SIZE(ov9740_defaults)); + if (ret < 0) + return ret; + + ret = ov9740_set_res(client, mf->width, mf->height); + if (ret < 0) + return ret; + + priv->current_mf = *mf; + return ret; +} + +static int ov9740_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; + + ov9740_res_roundup(&mf->width, &mf->height); + + mf->field = V4L2_FIELD_NONE; + mf->code = MEDIA_BUS_FMT_YUYV8_2X8; + mf->colorspace = V4L2_COLORSPACE_SRGB; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return ov9740_s_fmt(sd, mf); + cfg->try_fmt = *mf; + return 0; +} + +static int ov9740_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index >= ARRAY_SIZE(ov9740_codes)) + return -EINVAL; + + code->code = ov9740_codes[code->index]; + + return 0; +} + +static int ov9740_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = OV9740_MAX_WIDTH; + sel->r.height = OV9740_MAX_HEIGHT; + return 0; + default: + return -EINVAL; + } +} + +/* Set status of additional camera capabilities */ +static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov9740_priv *priv = + container_of(ctrl->handler, struct ov9740_priv, hdl); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + priv->flag_vflip = ctrl->val; + break; + case V4L2_CID_HFLIP: + priv->flag_hflip = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ov9740_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct ov9740_priv *priv = to_ov9740(sd); + int ret; + + if (on) { + ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); + if (ret < 0) + return ret; + + if (priv->current_enable) { + ov9740_s_fmt(sd, &priv->current_mf); + ov9740_s_stream(sd, 1); + } + } else { + if (priv->current_enable) { + ov9740_s_stream(sd, 0); + priv->current_enable = true; + } + + soc_camera_power_off(&client->dev, ssdd, priv->clk); + } + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov9740_get_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u8 val; + + if (reg->reg & ~0xffff) + return -EINVAL; + + reg->size = 2; + + ret = ov9740_reg_read(client, reg->reg, &val); + if (ret) + return ret; + + reg->val = (__u64)val; + + return ret; +} + +static int ov9740_set_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg & ~0xffff || reg->val & ~0xff) + return -EINVAL; + + return ov9740_reg_write(client, reg->reg, reg->val); +} +#endif + +static int ov9740_video_probe(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9740_priv *priv = to_ov9740(sd); + u8 modelhi, modello; + int ret; + + ret = ov9740_s_power(&priv->subdev, 1); + if (ret < 0) + return ret; + + /* + * check and show product ID and manufacturer ID + */ + ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi); + if (ret < 0) + goto done; + + ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello); + if (ret < 0) + goto done; + + priv->model = (modelhi << 8) | modello; + + ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision); + if (ret < 0) + goto done; + + ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid); + if (ret < 0) + goto done; + + ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver); + if (ret < 0) + goto done; + + if (priv->model != 0x9740) { + ret = -ENODEV; + goto done; + } + + dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, Manufacturer 0x%02x, SMIA Version 0x%02x\n", + priv->model, priv->revision, priv->manid, priv->smiaver); + + ret = v4l2_ctrl_handler_setup(&priv->hdl); + +done: + ov9740_s_power(&priv->subdev, 0); + return ret; +} + +/* Request bus settings on camera side */ +static int ov9740_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov9740_video_ops = { + .s_stream = ov9740_s_stream, + .g_mbus_config = ov9740_g_mbus_config, +}; + +static const struct v4l2_subdev_core_ops ov9740_core_ops = { + .s_power = ov9740_s_power, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov9740_get_register, + .s_register = ov9740_set_register, +#endif +}; + +static const struct v4l2_subdev_pad_ops ov9740_pad_ops = { + .enum_mbus_code = ov9740_enum_mbus_code, + .get_selection = ov9740_get_selection, + .set_fmt = ov9740_set_fmt, +}; + +static const struct v4l2_subdev_ops ov9740_subdev_ops = { + .core = &ov9740_core_ops, + .video = &ov9740_video_ops, + .pad = &ov9740_pad_ops, +}; + +static const struct v4l2_ctrl_ops ov9740_ctrl_ops = { + .s_ctrl = ov9740_s_ctrl, +}; + +/* + * i2c_driver function + */ +static int ov9740_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov9740_priv *priv; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + int ret; + + if (!ssdd) { + dev_err(&client->dev, "Missing platform_data for driver\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops); + v4l2_ctrl_handler_init(&priv->hdl, 13); + v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + priv->subdev.ctrl_handler = &priv->hdl; + if (priv->hdl.error) + return priv->hdl.error; + + priv->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + goto eclkget; + } + + ret = ov9740_video_probe(client); + if (ret < 0) { + v4l2_clk_put(priv->clk); +eclkget: + v4l2_ctrl_handler_free(&priv->hdl); + } + + return ret; +} + +static int ov9740_remove(struct i2c_client *client) +{ + struct ov9740_priv *priv = i2c_get_clientdata(client); + + v4l2_clk_put(priv->clk); + v4l2_device_unregister_subdev(&priv->subdev); + v4l2_ctrl_handler_free(&priv->hdl); + return 0; +} + +static const struct i2c_device_id ov9740_id[] = { + { "ov9740", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov9740_id); + +static struct i2c_driver ov9740_i2c_driver = { + .driver = { + .name = "ov9740", + }, + .probe = ov9740_probe, + .remove = ov9740_remove, + .id_table = ov9740_id, +}; + +module_i2c_driver(ov9740_i2c_driver); + +MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740"); +MODULE_AUTHOR("Andrew Chew "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 045f95e773335f5b2bdd9e261ba5b28ba4083de0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 7 Feb 2019 08:47:01 -0500 Subject: media: soc_camera: Move the imx074 under soc_camera directory Move the imx074 driver to the soc_camera directory in the media staging tree. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/imx074/Kconfig | 5 - drivers/staging/media/imx074/Makefile | 1 - drivers/staging/media/imx074/TODO | 5 - drivers/staging/media/imx074/imx074.c | 496 ------------------------------ drivers/staging/media/soc_camera/Kconfig | 7 + drivers/staging/media/soc_camera/Makefile | 1 + drivers/staging/media/soc_camera/imx074.c | 496 ++++++++++++++++++++++++++++++ 9 files changed, 504 insertions(+), 510 deletions(-) delete mode 100644 drivers/staging/media/imx074/Kconfig delete mode 100644 drivers/staging/media/imx074/Makefile delete mode 100644 drivers/staging/media/imx074/TODO delete mode 100644 drivers/staging/media/imx074/imx074.c create mode 100644 drivers/staging/media/soc_camera/imx074.c diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 7c3f443f2735..fce893321624 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -25,8 +25,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/imx/Kconfig" -source "drivers/staging/media/imx074/Kconfig" - source "drivers/staging/media/mt9t031/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 9c1bb862f5c9..74920289b0d9 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ -obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074/ obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ diff --git a/drivers/staging/media/imx074/Kconfig b/drivers/staging/media/imx074/Kconfig deleted file mode 100644 index 229cbeea580b..000000000000 --- a/drivers/staging/media/imx074/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -config SOC_CAMERA_IMX074 - tristate "imx074 support (DEPRECATED)" - depends on SOC_CAMERA && I2C - help - This driver supports IMX074 cameras from Sony diff --git a/drivers/staging/media/imx074/Makefile b/drivers/staging/media/imx074/Makefile deleted file mode 100644 index 7d183574aa84..000000000000 --- a/drivers/staging/media/imx074/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o diff --git a/drivers/staging/media/imx074/TODO b/drivers/staging/media/imx074/TODO deleted file mode 100644 index 15580a4f950c..000000000000 --- a/drivers/staging/media/imx074/TODO +++ /dev/null @@ -1,5 +0,0 @@ -This sensor driver needs to be converted to a regular -v4l2 subdev driver. The soc_camera framework is deprecated and -will be removed in the future. Unless someone does this work this -sensor driver will be deleted when the soc_camera framework is -deleted. diff --git a/drivers/staging/media/imx074/imx074.c b/drivers/staging/media/imx074/imx074.c deleted file mode 100644 index 1676c166dc83..000000000000 --- a/drivers/staging/media/imx074/imx074.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Driver for IMX074 CMOS Image Sensor from Sony - * - * Copyright (C) 2010, Guennadi Liakhovetski - * - * Partially inspired by the IMX074 driver from the Android / MSM tree - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* IMX074 registers */ - -#define MODE_SELECT 0x0100 -#define IMAGE_ORIENTATION 0x0101 -#define GROUPED_PARAMETER_HOLD 0x0104 - -/* Integration Time */ -#define COARSE_INTEGRATION_TIME_HI 0x0202 -#define COARSE_INTEGRATION_TIME_LO 0x0203 -/* Gain */ -#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204 -#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205 - -/* PLL registers */ -#define PRE_PLL_CLK_DIV 0x0305 -#define PLL_MULTIPLIER 0x0307 -#define PLSTATIM 0x302b -#define VNDMY_ABLMGSHLMT 0x300a -#define Y_OPBADDR_START_DI 0x3014 -/* mode setting */ -#define FRAME_LENGTH_LINES_HI 0x0340 -#define FRAME_LENGTH_LINES_LO 0x0341 -#define LINE_LENGTH_PCK_HI 0x0342 -#define LINE_LENGTH_PCK_LO 0x0343 -#define YADDR_START 0x0347 -#define YADDR_END 0x034b -#define X_OUTPUT_SIZE_MSB 0x034c -#define X_OUTPUT_SIZE_LSB 0x034d -#define Y_OUTPUT_SIZE_MSB 0x034e -#define Y_OUTPUT_SIZE_LSB 0x034f -#define X_EVEN_INC 0x0381 -#define X_ODD_INC 0x0383 -#define Y_EVEN_INC 0x0385 -#define Y_ODD_INC 0x0387 - -#define HMODEADD 0x3001 -#define VMODEADD 0x3016 -#define VAPPLINE_START 0x3069 -#define VAPPLINE_END 0x306b -#define SHUTTER 0x3086 -#define HADDAVE 0x30e8 -#define LANESEL 0x3301 - -/* IMX074 supported geometry */ -#define IMX074_WIDTH 1052 -#define IMX074_HEIGHT 780 - -/* IMX074 has only one fixed colorspace per pixelcode */ -struct imx074_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -struct imx074 { - struct v4l2_subdev subdev; - const struct imx074_datafmt *fmt; - struct v4l2_clk *clk; -}; - -static const struct imx074_datafmt imx074_colour_fmts[] = { - {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, -}; - -static struct imx074 *to_imx074(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct imx074, subdev); -} - -/* Find a data format by a pixel code in an array */ -static const struct imx074_datafmt *imx074_find_datafmt(u32 code) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++) - if (imx074_colour_fmts[i].code == code) - return imx074_colour_fmts + i; - - return NULL; -} - -static int reg_write(struct i2c_client *client, const u16 addr, const u8 data) -{ - struct i2c_adapter *adap = client->adapter; - struct i2c_msg msg; - unsigned char tx[3]; - int ret; - - msg.addr = client->addr; - msg.buf = tx; - msg.len = 3; - msg.flags = 0; - - tx[0] = addr >> 8; - tx[1] = addr & 0xff; - tx[2] = data; - - ret = i2c_transfer(adap, &msg, 1); - - mdelay(2); - - return ret == 1 ? 0 : -EIO; -} - -static int reg_read(struct i2c_client *client, const u16 addr) -{ - u8 buf[2] = {addr >> 8, addr & 0xff}; - int ret; - struct i2c_msg msgs[] = { - { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = buf, - }, { - .addr = client->addr, - .flags = I2C_M_RD, - .len = 2, - .buf = buf, - }, - }; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret < 0) { - dev_warn(&client->dev, "Reading register %x from %x failed\n", - addr, client->addr); - return ret; - } - - return buf[0] & 0xff; /* no sign-extension */ -} - -static int imx074_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct imx074 *priv = to_imx074(client); - - if (format->pad) - return -EINVAL; - - dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); - - if (!fmt) { - /* MIPI CSI could have changed the format, double-check */ - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - mf->code = imx074_colour_fmts[0].code; - mf->colorspace = imx074_colour_fmts[0].colorspace; - } - - mf->width = IMX074_WIDTH; - mf->height = IMX074_HEIGHT; - mf->field = V4L2_FIELD_NONE; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - priv->fmt = fmt; - else - cfg->try_fmt = *mf; - - return 0; -} - -static int imx074_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct imx074 *priv = to_imx074(client); - - const struct imx074_datafmt *fmt = priv->fmt; - - if (format->pad) - return -EINVAL; - - mf->code = fmt->code; - mf->colorspace = fmt->colorspace; - mf->width = IMX074_WIDTH; - mf->height = IMX074_HEIGHT; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int imx074_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = IMX074_WIDTH; - sel->r.height = IMX074_HEIGHT; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP: - return 0; - default: - return -EINVAL; - } -} - -static int imx074_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || - (unsigned int)code->index >= ARRAY_SIZE(imx074_colour_fmts)) - return -EINVAL; - - code->code = imx074_colour_fmts[code->index].code; - return 0; -} - -static int imx074_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* MODE_SELECT: stream or standby */ - return reg_write(client, MODE_SELECT, !!enable); -} - -static int imx074_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct imx074 *priv = to_imx074(client); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -static int imx074_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - cfg->type = V4L2_MBUS_CSI2_DPHY; - cfg->flags = V4L2_MBUS_CSI2_2_LANE | - V4L2_MBUS_CSI2_CHANNEL_0 | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - - return 0; -} - -static const struct v4l2_subdev_video_ops imx074_subdev_video_ops = { - .s_stream = imx074_s_stream, - .g_mbus_config = imx074_g_mbus_config, -}; - -static const struct v4l2_subdev_core_ops imx074_subdev_core_ops = { - .s_power = imx074_s_power, -}; - -static const struct v4l2_subdev_pad_ops imx074_subdev_pad_ops = { - .enum_mbus_code = imx074_enum_mbus_code, - .get_selection = imx074_get_selection, - .get_fmt = imx074_get_fmt, - .set_fmt = imx074_set_fmt, -}; - -static const struct v4l2_subdev_ops imx074_subdev_ops = { - .core = &imx074_subdev_core_ops, - .video = &imx074_subdev_video_ops, - .pad = &imx074_subdev_pad_ops, -}; - -static int imx074_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - int ret; - u16 id; - - ret = imx074_s_power(subdev, 1); - if (ret < 0) - return ret; - - /* Read sensor Model ID */ - ret = reg_read(client, 0); - if (ret < 0) - goto done; - - id = ret << 8; - - ret = reg_read(client, 1); - if (ret < 0) - goto done; - - id |= ret; - - dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); - - if (id != 0x74) { - ret = -ENODEV; - goto done; - } - - /* PLL Setting EXTCLK=24MHz, 22.5times */ - reg_write(client, PLL_MULTIPLIER, 0x2D); - reg_write(client, PRE_PLL_CLK_DIV, 0x02); - reg_write(client, PLSTATIM, 0x4B); - - /* 2-lane mode */ - reg_write(client, 0x3024, 0x00); - - reg_write(client, IMAGE_ORIENTATION, 0x00); - - /* select RAW mode: - * 0x08+0x08 = top 8 bits - * 0x0a+0x08 = compressed 8-bits - * 0x0a+0x0a = 10 bits - */ - reg_write(client, 0x0112, 0x08); - reg_write(client, 0x0113, 0x08); - - /* Base setting for High frame mode */ - reg_write(client, VNDMY_ABLMGSHLMT, 0x80); - reg_write(client, Y_OPBADDR_START_DI, 0x08); - reg_write(client, 0x3015, 0x37); - reg_write(client, 0x301C, 0x01); - reg_write(client, 0x302C, 0x05); - reg_write(client, 0x3031, 0x26); - reg_write(client, 0x3041, 0x60); - reg_write(client, 0x3051, 0x24); - reg_write(client, 0x3053, 0x34); - reg_write(client, 0x3057, 0xC0); - reg_write(client, 0x305C, 0x09); - reg_write(client, 0x305D, 0x07); - reg_write(client, 0x3060, 0x30); - reg_write(client, 0x3065, 0x00); - reg_write(client, 0x30AA, 0x08); - reg_write(client, 0x30AB, 0x1C); - reg_write(client, 0x30B0, 0x32); - reg_write(client, 0x30B2, 0x83); - reg_write(client, 0x30D3, 0x04); - reg_write(client, 0x3106, 0x78); - reg_write(client, 0x310C, 0x82); - reg_write(client, 0x3304, 0x05); - reg_write(client, 0x3305, 0x04); - reg_write(client, 0x3306, 0x11); - reg_write(client, 0x3307, 0x02); - reg_write(client, 0x3308, 0x0C); - reg_write(client, 0x3309, 0x06); - reg_write(client, 0x330A, 0x08); - reg_write(client, 0x330B, 0x04); - reg_write(client, 0x330C, 0x08); - reg_write(client, 0x330D, 0x06); - reg_write(client, 0x330E, 0x01); - reg_write(client, 0x3381, 0x00); - - /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */ - /* 1608 = 1560 + 48 (black lines) */ - reg_write(client, FRAME_LENGTH_LINES_HI, 0x06); - reg_write(client, FRAME_LENGTH_LINES_LO, 0x48); - reg_write(client, YADDR_START, 0x00); - reg_write(client, YADDR_END, 0x2F); - /* 0x838 == 2104 */ - reg_write(client, X_OUTPUT_SIZE_MSB, 0x08); - reg_write(client, X_OUTPUT_SIZE_LSB, 0x38); - /* 0x618 == 1560 */ - reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06); - reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18); - reg_write(client, X_EVEN_INC, 0x01); - reg_write(client, X_ODD_INC, 0x03); - reg_write(client, Y_EVEN_INC, 0x01); - reg_write(client, Y_ODD_INC, 0x03); - reg_write(client, HMODEADD, 0x00); - reg_write(client, VMODEADD, 0x16); - reg_write(client, VAPPLINE_START, 0x24); - reg_write(client, VAPPLINE_END, 0x53); - reg_write(client, SHUTTER, 0x00); - reg_write(client, HADDAVE, 0x80); - - reg_write(client, LANESEL, 0x00); - - reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */ - - ret = 0; - -done: - imx074_s_power(subdev, 0); - return ret; -} - -static int imx074_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct imx074 *priv; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "IMX074: missing platform data!\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); - return -EIO; - } - - priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops); - - priv->fmt = &imx074_colour_fmts[0]; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk)); - return -EPROBE_DEFER; - } - - ret = soc_camera_power_init(&client->dev, ssdd); - if (ret < 0) - goto epwrinit; - - ret = imx074_video_probe(client); - if (ret < 0) - goto eprobe; - - ret = v4l2_async_register_subdev(&priv->subdev); - if (!ret) - return 0; - -epwrinit: -eprobe: - v4l2_clk_put(priv->clk); - return ret; -} - -static int imx074_remove(struct i2c_client *client) -{ - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct imx074 *priv = to_imx074(client); - - v4l2_async_unregister_subdev(&priv->subdev); - v4l2_clk_put(priv->clk); - - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - - return 0; -} - -static const struct i2c_device_id imx074_id[] = { - { "imx074", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, imx074_id); - -static struct i2c_driver imx074_i2c_driver = { - .driver = { - .name = "imx074", - }, - .probe = imx074_probe, - .remove = imx074_remove, - .id_table = imx074_id, -}; - -module_i2c_driver(imx074_i2c_driver); - -MODULE_DESCRIPTION("Sony IMX074 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig index ebd78cebd4ec..e6bd04840971 100644 --- a/drivers/staging/media/soc_camera/Kconfig +++ b/drivers/staging/media/soc_camera/Kconfig @@ -6,6 +6,7 @@ config SOC_CAMERA SoC Camera is a common API to several cameras, not connecting over a bus like PCI or USB. For example some i2c camera connected directly to the data bus of an SoC. + comment "soc_camera sensor drivers" config SOC_CAMERA_MT9M111 @@ -35,3 +36,9 @@ config SOC_CAMERA_OV9740 depends on SOC_CAMERA && I2C help This is a ov9740 camera driver + +config SOC_CAMERA_IMX074 + tristate "imx074 support (DEPRECATED)" + depends on SOC_CAMERA && I2C + help + This driver supports IMX074 cameras from Sony diff --git a/drivers/staging/media/soc_camera/Makefile b/drivers/staging/media/soc_camera/Makefile index e03450cee524..09560dc32c4c 100644 --- a/drivers/staging/media/soc_camera/Makefile +++ b/drivers/staging/media/soc_camera/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o +obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o diff --git a/drivers/staging/media/soc_camera/imx074.c b/drivers/staging/media/soc_camera/imx074.c new file mode 100644 index 000000000000..1676c166dc83 --- /dev/null +++ b/drivers/staging/media/soc_camera/imx074.c @@ -0,0 +1,496 @@ +/* + * Driver for IMX074 CMOS Image Sensor from Sony + * + * Copyright (C) 2010, Guennadi Liakhovetski + * + * Partially inspired by the IMX074 driver from the Android / MSM tree + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* IMX074 registers */ + +#define MODE_SELECT 0x0100 +#define IMAGE_ORIENTATION 0x0101 +#define GROUPED_PARAMETER_HOLD 0x0104 + +/* Integration Time */ +#define COARSE_INTEGRATION_TIME_HI 0x0202 +#define COARSE_INTEGRATION_TIME_LO 0x0203 +/* Gain */ +#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204 +#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205 + +/* PLL registers */ +#define PRE_PLL_CLK_DIV 0x0305 +#define PLL_MULTIPLIER 0x0307 +#define PLSTATIM 0x302b +#define VNDMY_ABLMGSHLMT 0x300a +#define Y_OPBADDR_START_DI 0x3014 +/* mode setting */ +#define FRAME_LENGTH_LINES_HI 0x0340 +#define FRAME_LENGTH_LINES_LO 0x0341 +#define LINE_LENGTH_PCK_HI 0x0342 +#define LINE_LENGTH_PCK_LO 0x0343 +#define YADDR_START 0x0347 +#define YADDR_END 0x034b +#define X_OUTPUT_SIZE_MSB 0x034c +#define X_OUTPUT_SIZE_LSB 0x034d +#define Y_OUTPUT_SIZE_MSB 0x034e +#define Y_OUTPUT_SIZE_LSB 0x034f +#define X_EVEN_INC 0x0381 +#define X_ODD_INC 0x0383 +#define Y_EVEN_INC 0x0385 +#define Y_ODD_INC 0x0387 + +#define HMODEADD 0x3001 +#define VMODEADD 0x3016 +#define VAPPLINE_START 0x3069 +#define VAPPLINE_END 0x306b +#define SHUTTER 0x3086 +#define HADDAVE 0x30e8 +#define LANESEL 0x3301 + +/* IMX074 supported geometry */ +#define IMX074_WIDTH 1052 +#define IMX074_HEIGHT 780 + +/* IMX074 has only one fixed colorspace per pixelcode */ +struct imx074_datafmt { + u32 code; + enum v4l2_colorspace colorspace; +}; + +struct imx074 { + struct v4l2_subdev subdev; + const struct imx074_datafmt *fmt; + struct v4l2_clk *clk; +}; + +static const struct imx074_datafmt imx074_colour_fmts[] = { + {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, +}; + +static struct imx074 *to_imx074(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct imx074, subdev); +} + +/* Find a data format by a pixel code in an array */ +static const struct imx074_datafmt *imx074_find_datafmt(u32 code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++) + if (imx074_colour_fmts[i].code == code) + return imx074_colour_fmts + i; + + return NULL; +} + +static int reg_write(struct i2c_client *client, const u16 addr, const u8 data) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg; + unsigned char tx[3]; + int ret; + + msg.addr = client->addr; + msg.buf = tx; + msg.len = 3; + msg.flags = 0; + + tx[0] = addr >> 8; + tx[1] = addr & 0xff; + tx[2] = data; + + ret = i2c_transfer(adap, &msg, 1); + + mdelay(2); + + return ret == 1 ? 0 : -EIO; +} + +static int reg_read(struct i2c_client *client, const u16 addr) +{ + u8 buf[2] = {addr >> 8, addr & 0xff}; + int ret; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = buf, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 2, + .buf = buf, + }, + }; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + dev_warn(&client->dev, "Reading register %x from %x failed\n", + addr, client->addr); + return ret; + } + + return buf[0] & 0xff; /* no sign-extension */ +} + +static int imx074_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct imx074 *priv = to_imx074(client); + + if (format->pad) + return -EINVAL; + + dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); + + if (!fmt) { + /* MIPI CSI could have changed the format, double-check */ + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + mf->code = imx074_colour_fmts[0].code; + mf->colorspace = imx074_colour_fmts[0].colorspace; + } + + mf->width = IMX074_WIDTH; + mf->height = IMX074_HEIGHT; + mf->field = V4L2_FIELD_NONE; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + priv->fmt = fmt; + else + cfg->try_fmt = *mf; + + return 0; +} + +static int imx074_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct imx074 *priv = to_imx074(client); + + const struct imx074_datafmt *fmt = priv->fmt; + + if (format->pad) + return -EINVAL; + + mf->code = fmt->code; + mf->colorspace = fmt->colorspace; + mf->width = IMX074_WIDTH; + mf->height = IMX074_HEIGHT; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int imx074_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = IMX074_WIDTH; + sel->r.height = IMX074_HEIGHT; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP: + return 0; + default: + return -EINVAL; + } +} + +static int imx074_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || + (unsigned int)code->index >= ARRAY_SIZE(imx074_colour_fmts)) + return -EINVAL; + + code->code = imx074_colour_fmts[code->index].code; + return 0; +} + +static int imx074_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* MODE_SELECT: stream or standby */ + return reg_write(client, MODE_SELECT, !!enable); +} + +static int imx074_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct imx074 *priv = to_imx074(client); + + return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); +} + +static int imx074_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + cfg->type = V4L2_MBUS_CSI2_DPHY; + cfg->flags = V4L2_MBUS_CSI2_2_LANE | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + return 0; +} + +static const struct v4l2_subdev_video_ops imx074_subdev_video_ops = { + .s_stream = imx074_s_stream, + .g_mbus_config = imx074_g_mbus_config, +}; + +static const struct v4l2_subdev_core_ops imx074_subdev_core_ops = { + .s_power = imx074_s_power, +}; + +static const struct v4l2_subdev_pad_ops imx074_subdev_pad_ops = { + .enum_mbus_code = imx074_enum_mbus_code, + .get_selection = imx074_get_selection, + .get_fmt = imx074_get_fmt, + .set_fmt = imx074_set_fmt, +}; + +static const struct v4l2_subdev_ops imx074_subdev_ops = { + .core = &imx074_subdev_core_ops, + .video = &imx074_subdev_video_ops, + .pad = &imx074_subdev_pad_ops, +}; + +static int imx074_video_probe(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + int ret; + u16 id; + + ret = imx074_s_power(subdev, 1); + if (ret < 0) + return ret; + + /* Read sensor Model ID */ + ret = reg_read(client, 0); + if (ret < 0) + goto done; + + id = ret << 8; + + ret = reg_read(client, 1); + if (ret < 0) + goto done; + + id |= ret; + + dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); + + if (id != 0x74) { + ret = -ENODEV; + goto done; + } + + /* PLL Setting EXTCLK=24MHz, 22.5times */ + reg_write(client, PLL_MULTIPLIER, 0x2D); + reg_write(client, PRE_PLL_CLK_DIV, 0x02); + reg_write(client, PLSTATIM, 0x4B); + + /* 2-lane mode */ + reg_write(client, 0x3024, 0x00); + + reg_write(client, IMAGE_ORIENTATION, 0x00); + + /* select RAW mode: + * 0x08+0x08 = top 8 bits + * 0x0a+0x08 = compressed 8-bits + * 0x0a+0x0a = 10 bits + */ + reg_write(client, 0x0112, 0x08); + reg_write(client, 0x0113, 0x08); + + /* Base setting for High frame mode */ + reg_write(client, VNDMY_ABLMGSHLMT, 0x80); + reg_write(client, Y_OPBADDR_START_DI, 0x08); + reg_write(client, 0x3015, 0x37); + reg_write(client, 0x301C, 0x01); + reg_write(client, 0x302C, 0x05); + reg_write(client, 0x3031, 0x26); + reg_write(client, 0x3041, 0x60); + reg_write(client, 0x3051, 0x24); + reg_write(client, 0x3053, 0x34); + reg_write(client, 0x3057, 0xC0); + reg_write(client, 0x305C, 0x09); + reg_write(client, 0x305D, 0x07); + reg_write(client, 0x3060, 0x30); + reg_write(client, 0x3065, 0x00); + reg_write(client, 0x30AA, 0x08); + reg_write(client, 0x30AB, 0x1C); + reg_write(client, 0x30B0, 0x32); + reg_write(client, 0x30B2, 0x83); + reg_write(client, 0x30D3, 0x04); + reg_write(client, 0x3106, 0x78); + reg_write(client, 0x310C, 0x82); + reg_write(client, 0x3304, 0x05); + reg_write(client, 0x3305, 0x04); + reg_write(client, 0x3306, 0x11); + reg_write(client, 0x3307, 0x02); + reg_write(client, 0x3308, 0x0C); + reg_write(client, 0x3309, 0x06); + reg_write(client, 0x330A, 0x08); + reg_write(client, 0x330B, 0x04); + reg_write(client, 0x330C, 0x08); + reg_write(client, 0x330D, 0x06); + reg_write(client, 0x330E, 0x01); + reg_write(client, 0x3381, 0x00); + + /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */ + /* 1608 = 1560 + 48 (black lines) */ + reg_write(client, FRAME_LENGTH_LINES_HI, 0x06); + reg_write(client, FRAME_LENGTH_LINES_LO, 0x48); + reg_write(client, YADDR_START, 0x00); + reg_write(client, YADDR_END, 0x2F); + /* 0x838 == 2104 */ + reg_write(client, X_OUTPUT_SIZE_MSB, 0x08); + reg_write(client, X_OUTPUT_SIZE_LSB, 0x38); + /* 0x618 == 1560 */ + reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06); + reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18); + reg_write(client, X_EVEN_INC, 0x01); + reg_write(client, X_ODD_INC, 0x03); + reg_write(client, Y_EVEN_INC, 0x01); + reg_write(client, Y_ODD_INC, 0x03); + reg_write(client, HMODEADD, 0x00); + reg_write(client, VMODEADD, 0x16); + reg_write(client, VAPPLINE_START, 0x24); + reg_write(client, VAPPLINE_END, 0x53); + reg_write(client, SHUTTER, 0x00); + reg_write(client, HADDAVE, 0x80); + + reg_write(client, LANESEL, 0x00); + + reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */ + + ret = 0; + +done: + imx074_s_power(subdev, 0); + return ret; +} + +static int imx074_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct imx074 *priv; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + int ret; + + if (!ssdd) { + dev_err(&client->dev, "IMX074: missing platform data!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); + return -EIO; + } + + priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops); + + priv->fmt = &imx074_colour_fmts[0]; + + priv->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(priv->clk)) { + dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk)); + return -EPROBE_DEFER; + } + + ret = soc_camera_power_init(&client->dev, ssdd); + if (ret < 0) + goto epwrinit; + + ret = imx074_video_probe(client); + if (ret < 0) + goto eprobe; + + ret = v4l2_async_register_subdev(&priv->subdev); + if (!ret) + return 0; + +epwrinit: +eprobe: + v4l2_clk_put(priv->clk); + return ret; +} + +static int imx074_remove(struct i2c_client *client) +{ + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct imx074 *priv = to_imx074(client); + + v4l2_async_unregister_subdev(&priv->subdev); + v4l2_clk_put(priv->clk); + + if (ssdd->free_bus) + ssdd->free_bus(ssdd); + + return 0; +} + +static const struct i2c_device_id imx074_id[] = { + { "imx074", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, imx074_id); + +static struct i2c_driver imx074_i2c_driver = { + .driver = { + .name = "imx074", + }, + .probe = imx074_probe, + .remove = imx074_remove, + .id_table = imx074_id, +}; + +module_i2c_driver(imx074_i2c_driver); + +MODULE_DESCRIPTION("Sony IMX074 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From dc548ee0773bedf74a162e761b3bf487bb84270b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 7 Feb 2019 08:48:28 -0500 Subject: media: soc_camera: Move the mt9t031 under soc_camera directory Move the mt9t031 driver to the soc_camera directory in the media staging tree. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/mt9t031/mt9t031.c | 857 ----------------------------- drivers/staging/media/soc_camera/Kconfig | 6 + drivers/staging/media/soc_camera/Makefile | 1 + drivers/staging/media/soc_camera/mt9t031.c | 857 +++++++++++++++++++++++++++++ 6 files changed, 864 insertions(+), 860 deletions(-) delete mode 100644 drivers/staging/media/mt9t031/mt9t031.c create mode 100644 drivers/staging/media/soc_camera/mt9t031.c diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index fce893321624..1da5c20d65c0 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -25,8 +25,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/imx/Kconfig" -source "drivers/staging/media/mt9t031/Kconfig" - source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/rockchip/vpu/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 74920289b0d9..0355e3030504 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ -obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ diff --git a/drivers/staging/media/mt9t031/mt9t031.c b/drivers/staging/media/mt9t031/mt9t031.c deleted file mode 100644 index 4ff179302b4f..000000000000 --- a/drivers/staging/media/mt9t031/mt9t031.c +++ /dev/null @@ -1,857 +0,0 @@ -/* - * Driver for MT9T031 CMOS Image Sensor from Micron - * - * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* - * ATTENTION: this driver still cannot be used outside of the soc-camera - * framework because of its PM implementation, using the video_device node. - * If hardware becomes available for testing, alternative PM approaches shall - * be considered and tested. - */ - -/* - * mt9t031 i2c address 0x5d - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc - */ - -/* mt9t031 selected register addresses */ -#define MT9T031_CHIP_VERSION 0x00 -#define MT9T031_ROW_START 0x01 -#define MT9T031_COLUMN_START 0x02 -#define MT9T031_WINDOW_HEIGHT 0x03 -#define MT9T031_WINDOW_WIDTH 0x04 -#define MT9T031_HORIZONTAL_BLANKING 0x05 -#define MT9T031_VERTICAL_BLANKING 0x06 -#define MT9T031_OUTPUT_CONTROL 0x07 -#define MT9T031_SHUTTER_WIDTH_UPPER 0x08 -#define MT9T031_SHUTTER_WIDTH 0x09 -#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a -#define MT9T031_FRAME_RESTART 0x0b -#define MT9T031_SHUTTER_DELAY 0x0c -#define MT9T031_RESET 0x0d -#define MT9T031_READ_MODE_1 0x1e -#define MT9T031_READ_MODE_2 0x20 -#define MT9T031_READ_MODE_3 0x21 -#define MT9T031_ROW_ADDRESS_MODE 0x22 -#define MT9T031_COLUMN_ADDRESS_MODE 0x23 -#define MT9T031_GLOBAL_GAIN 0x35 -#define MT9T031_CHIP_ENABLE 0xF8 - -#define MT9T031_MAX_HEIGHT 1536 -#define MT9T031_MAX_WIDTH 2048 -#define MT9T031_MIN_HEIGHT 2 -#define MT9T031_MIN_WIDTH 18 -#define MT9T031_HORIZONTAL_BLANK 142 -#define MT9T031_VERTICAL_BLANK 25 -#define MT9T031_COLUMN_SKIP 32 -#define MT9T031_ROW_SKIP 20 - -struct mt9t031 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/auto-exposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; - u16 xskip; - u16 yskip; - unsigned int total_h; - unsigned short y_skip_top; /* Lines to skip at the top */ -}; - -static struct mt9t031 *to_mt9t031(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct mt9t031, subdev); -} - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret | data); -} - -static int reg_clear(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret & ~data); -} - -static int set_shutter(struct i2c_client *client, const u32 data) -{ - int ret; - - ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16); - - if (ret >= 0) - ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff); - - return ret; -} - -static int get_shutter(struct i2c_client *client, u32 *data) -{ - int ret; - - ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER); - *data = ret << 16; - - if (ret >= 0) - ret = reg_read(client, MT9T031_SHUTTER_WIDTH); - *data |= ret & 0xffff; - - return ret < 0 ? ret : 0; -} - -static int mt9t031_idle(struct i2c_client *client) -{ - int ret; - - /* Disable chip output, synchronous option update */ - ret = reg_write(client, MT9T031_RESET, 1); - if (ret >= 0) - ret = reg_write(client, MT9T031_RESET, 0); - if (ret >= 0) - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - - return ret >= 0 ? 0 : -EIO; -} - -static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (enable) - /* Switch to master "normal" mode */ - ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2); - else - /* Stop sensor readout */ - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - - if (ret < 0) - return -EIO; - - return 0; -} - -/* target must be _even_ */ -static u16 mt9t031_skip(s32 *source, s32 target, s32 max) -{ - unsigned int skip; - - if (*source < target + target / 2) { - *source = target; - return 1; - } - - skip = min(max, *source + target / 2) / target; - if (skip > 8) - skip = 8; - *source = target * skip; - - return skip; -} - -/* rect is the sensor rectangle, the caller guarantees parameter validity */ -static int mt9t031_set_params(struct i2c_client *client, - struct v4l2_rect *rect, u16 xskip, u16 yskip) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - int ret; - u16 xbin, ybin; - const u16 hblank = MT9T031_HORIZONTAL_BLANK, - vblank = MT9T031_VERTICAL_BLANK; - - xbin = min(xskip, (u16)3); - ybin = min(yskip, (u16)3); - - /* - * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper. - * There is always a valid suitably aligned value. The worst case is - * xbin = 3, width = 2048. Then we will start at 36, the last read out - * pixel will be 2083, which is < 2085 - first black pixel. - * - * MT9T031 datasheet imposes window left border alignment, depending on - * the selected xskip. Failing to conform to this requirement produces - * dark horizontal stripes in the image. However, even obeying to this - * requirement doesn't eliminate the stripes in all configurations. They - * appear "locally reproducibly," but can differ between tests under - * different lighting conditions. - */ - switch (xbin) { - case 1: - rect->left &= ~1; - break; - case 2: - rect->left &= ~3; - break; - case 3: - rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ? - (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6); - } - - rect->top &= ~1; - - dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n", - xskip, yskip, rect->width, rect->height, rect->left, rect->top); - - /* Disable register update, reconfigure atomically */ - ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1); - if (ret < 0) - return ret; - - /* Blanking and start values - default... */ - ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank); - if (ret >= 0) - ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank); - - if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) { - /* Binning, skipping */ - if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (xskip - 1)); - if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (yskip - 1)); - } - dev_dbg(&client->dev, "new physical left %u, top %u\n", - rect->left, rect->top); - - /* - * The caller provides a supported format, as guaranteed by - * .set_fmt(FORMAT_TRY), soc_camera_s_selection() and soc_camera_cropcap() - */ - if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_START, rect->left); - if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_START, rect->top); - if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1); - if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_HEIGHT, - rect->height + mt9t031->y_skip_top - 1); - if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) { - mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank; - - ret = set_shutter(client, mt9t031->total_h); - } - - /* Re-enable register update, commit all changes */ - if (ret >= 0) - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); - - if (ret >= 0) { - mt9t031->rect = *rect; - mt9t031->xskip = xskip; - mt9t031->yskip = yskip; - } - - return ret < 0 ? ret : 0; -} - -static int mt9t031_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - struct v4l2_rect rect = sel->r; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - rect.width = ALIGN(rect.width, 2); - rect.height = ALIGN(rect.height, 2); - - soc_camera_limit_side(&rect.left, &rect.width, - MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT); - - return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip); -} - -static int mt9t031_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = MT9T031_COLUMN_SKIP; - sel->r.top = MT9T031_ROW_SKIP; - sel->r.width = MT9T031_MAX_WIDTH; - sel->r.height = MT9T031_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = mt9t031->rect; - return 0; - default: - return -EINVAL; - } -} - -static int mt9t031_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - if (format->pad) - return -EINVAL; - - mf->width = mt9t031->rect.width / mt9t031->xskip; - mf->height = mt9t031->rect.height / mt9t031->yskip; - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -/* - * If a user window larger than sensor window is requested, we'll increase the - * sensor window. - */ -static int mt9t031_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - u16 xskip, yskip; - struct v4l2_rect rect = mt9t031->rect; - - if (format->pad) - return -EINVAL; - - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - v4l_bound_align_image( - &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1, - &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; - return 0; - } - - /* - * Width and height are within limits. - * S_FMT: use binning and skipping for scaling - */ - xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH); - yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT); - - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - - /* mt9t031_set_params() doesn't change width and height */ - return mt9t031_set_params(client, &rect, xskip, yskip); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9t031_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 1; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int mt9t031_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9t031 *mt9t031 = container_of(ctrl->handler, - struct mt9t031, hdl); - const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK; - s32 min, max; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE_AUTO: - min = mt9t031->exposure->minimum; - max = mt9t031->exposure->maximum; - mt9t031->exposure->val = - (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min)) - / shutter_max + min; - break; - } - return 0; -} - -static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9t031 *mt9t031 = container_of(ctrl->handler, - struct mt9t031, hdl); - struct v4l2_subdev *sd = &mt9t031->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct v4l2_ctrl *exp = mt9t031->exposure; - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, MT9T031_READ_MODE_2, 0x8000); - else - data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_HFLIP: - if (ctrl->val) - data = reg_set(client, MT9T031_READ_MODE_2, 0x4000); - else - data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_GAIN: - /* See Datasheet Table 7, Gain settings. */ - if (ctrl->val <= ctrl->default_value) { - /* Pack it into 0..1 step 0.125, register values 0..8 */ - unsigned long range = ctrl->default_value - ctrl->minimum; - data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; - - dev_dbg(&client->dev, "Setting gain %d\n", data); - data = reg_write(client, MT9T031_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } else { - /* Pack it into 1.125..128 variable step, register values 9..0x7860 */ - /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ - unsigned long range = ctrl->maximum - ctrl->default_value - 1; - /* calculated gain: map 65..127 to 9..1024 step 0.125 */ - unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * - 1015 + range / 2) / range + 9; - - if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ - data = gain; - else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */ - data = ((gain - 32) * 16 + 16) / 32 + 80; - else - /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ - data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; - - dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n", - reg_read(client, MT9T031_GLOBAL_GAIN), data); - data = reg_write(client, MT9T031_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } - return 0; - - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_MANUAL) { - unsigned int range = exp->maximum - exp->minimum; - unsigned int shutter = ((exp->val - (s32)exp->minimum) * 1048 + - range / 2) / range + 1; - u32 old; - - get_shutter(client, &old); - dev_dbg(&client->dev, "Set shutter from %u to %u\n", - old, shutter); - if (set_shutter(client, shutter) < 0) - return -EIO; - } else { - const u16 vblank = MT9T031_VERTICAL_BLANK; - mt9t031->total_h = mt9t031->rect.height + - mt9t031->y_skip_top + vblank; - - if (set_shutter(client, mt9t031->total_h) < 0) - return -EIO; - } - return 0; - default: - return -EINVAL; - } - return 0; -} - -/* - * Power Management: - * This function does nothing for now but must be present for pm to work - */ -static int mt9t031_runtime_suspend(struct device *dev) -{ - return 0; -} - -/* - * Power Management: - * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged - * they are however changed at reset if the platform hook is present - * thus we rewrite them with the values stored by the driver - */ -static int mt9t031_runtime_resume(struct device *dev) -{ - struct video_device *vdev = to_video_device(dev); - struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - int ret; - u16 xbin, ybin; - - xbin = min(mt9t031->xskip, (u16)3); - ybin = min(mt9t031->yskip, (u16)3); - - ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (mt9t031->xskip - 1)); - if (ret < 0) - return ret; - - ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (mt9t031->yskip - 1)); - if (ret < 0) - return ret; - - return 0; -} - -static const struct dev_pm_ops mt9t031_dev_pm_ops = { - .runtime_suspend = mt9t031_runtime_suspend, - .runtime_resume = mt9t031_runtime_resume, -}; - -static const struct device_type mt9t031_dev_type = { - .name = "MT9T031", - .pm = &mt9t031_dev_pm_ops, -}; - -static int mt9t031_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct video_device *vdev = soc_camera_i2c_to_vdev(client); - struct mt9t031 *mt9t031 = to_mt9t031(client); - int ret; - - if (on) { - ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk); - if (ret < 0) - return ret; - if (vdev) - /* Not needed during probing, when vdev isn't available yet */ - vdev->dev.type = &mt9t031_dev_type; - } else { - if (vdev) - vdev->dev.type = NULL; - soc_camera_power_off(&client->dev, ssdd, mt9t031->clk); - } - - return 0; -} - -/* - * Interface active, can use i2c. If it fails, it can indeed mean, that - * this wasn't our capture interface, so, we wait for the right one - */ -static int mt9t031_video_probe(struct i2c_client *client) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - s32 data; - int ret; - - ret = mt9t031_s_power(&mt9t031->subdev, 1); - if (ret < 0) - return ret; - - ret = mt9t031_idle(client); - if (ret < 0) { - dev_err(&client->dev, "Failed to initialise the camera\n"); - goto done; - } - - /* Read out the chip version register */ - data = reg_read(client, MT9T031_CHIP_VERSION); - - switch (data) { - case 0x1621: - break; - default: - dev_err(&client->dev, - "No MT9T031 chip detected, register read %x\n", data); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data); - - ret = v4l2_ctrl_handler_setup(&mt9t031->hdl); - -done: - mt9t031_s_power(&mt9t031->subdev, 0); - - return ret; -} - -static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - *lines = mt9t031->y_skip_top; - - return 0; -} - -static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = { - .g_volatile_ctrl = mt9t031_g_volatile_ctrl, - .s_ctrl = mt9t031_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { - .s_power = mt9t031_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9t031_g_register, - .s_register = mt9t031_s_register, -#endif -}; - -static int mt9t031_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SBGGR10_1X10; - return 0; -} - -static int mt9t031_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - if (soc_camera_apply_board_flags(ssdd, cfg) & - V4L2_MBUS_PCLK_SAMPLE_FALLING) - return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); - else - return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); -} - -static const struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { - .s_stream = mt9t031_s_stream, - .g_mbus_config = mt9t031_g_mbus_config, - .s_mbus_config = mt9t031_s_mbus_config, -}; - -static const struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = { - .g_skip_top_lines = mt9t031_g_skip_top_lines, -}; - -static const struct v4l2_subdev_pad_ops mt9t031_subdev_pad_ops = { - .enum_mbus_code = mt9t031_enum_mbus_code, - .get_selection = mt9t031_get_selection, - .set_selection = mt9t031_set_selection, - .get_fmt = mt9t031_get_fmt, - .set_fmt = mt9t031_set_fmt, -}; - -static const struct v4l2_subdev_ops mt9t031_subdev_ops = { - .core = &mt9t031_subdev_core_ops, - .video = &mt9t031_subdev_video_ops, - .sensor = &mt9t031_subdev_sensor_ops, - .pad = &mt9t031_subdev_pad_ops, -}; - -static int mt9t031_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9t031 *mt9t031; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "MT9T031 driver needs platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9t031 = devm_kzalloc(&client->dev, sizeof(struct mt9t031), GFP_KERNEL); - if (!mt9t031) - return -ENOMEM; - - v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops); - v4l2_ctrl_handler_init(&mt9t031->hdl, 5); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl, - &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, - V4L2_EXPOSURE_AUTO); - mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 255, 1, 255); - - mt9t031->subdev.ctrl_handler = &mt9t031->hdl; - if (mt9t031->hdl.error) - return mt9t031->hdl.error; - - v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - - mt9t031->y_skip_top = 0; - mt9t031->rect.left = MT9T031_COLUMN_SKIP; - mt9t031->rect.top = MT9T031_ROW_SKIP; - mt9t031->rect.width = MT9T031_MAX_WIDTH; - mt9t031->rect.height = MT9T031_MAX_HEIGHT; - - mt9t031->xskip = 1; - mt9t031->yskip = 1; - - mt9t031->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9t031->clk)) { - ret = PTR_ERR(mt9t031->clk); - goto eclkget; - } - - ret = mt9t031_video_probe(client); - if (ret) { - v4l2_clk_put(mt9t031->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9t031->hdl); - } - - return ret; -} - -static int mt9t031_remove(struct i2c_client *client) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - - v4l2_clk_put(mt9t031->clk); - v4l2_device_unregister_subdev(&mt9t031->subdev); - v4l2_ctrl_handler_free(&mt9t031->hdl); - - return 0; -} - -static const struct i2c_device_id mt9t031_id[] = { - { "mt9t031", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9t031_id); - -static struct i2c_driver mt9t031_i2c_driver = { - .driver = { - .name = "mt9t031", - }, - .probe = mt9t031_probe, - .remove = mt9t031_remove, - .id_table = mt9t031_id, -}; - -module_i2c_driver(mt9t031_i2c_driver); - -MODULE_DESCRIPTION("Micron MT9T031 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig index e6bd04840971..6a6aa6d2d150 100644 --- a/drivers/staging/media/soc_camera/Kconfig +++ b/drivers/staging/media/soc_camera/Kconfig @@ -42,3 +42,9 @@ config SOC_CAMERA_IMX074 depends on SOC_CAMERA && I2C help This driver supports IMX074 cameras from Sony + +config SOC_CAMERA_MT9T031 + tristate "mt9t031 support (DEPRECATED)" + depends on SOC_CAMERA && I2C + help + This driver supports MT9T031 cameras from Micron. diff --git a/drivers/staging/media/soc_camera/Makefile b/drivers/staging/media/soc_camera/Makefile index 09560dc32c4c..3a351bd629f5 100644 --- a/drivers/staging/media/soc_camera/Makefile +++ b/drivers/staging/media/soc_camera/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o +obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o diff --git a/drivers/staging/media/soc_camera/mt9t031.c b/drivers/staging/media/soc_camera/mt9t031.c new file mode 100644 index 000000000000..4ff179302b4f --- /dev/null +++ b/drivers/staging/media/soc_camera/mt9t031.c @@ -0,0 +1,857 @@ +/* + * Driver for MT9T031 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * ATTENTION: this driver still cannot be used outside of the soc-camera + * framework because of its PM implementation, using the video_device node. + * If hardware becomes available for testing, alternative PM approaches shall + * be considered and tested. + */ + +/* + * mt9t031 i2c address 0x5d + * The platform has to define struct i2c_board_info objects and link to them + * from struct soc_camera_host_desc + */ + +/* mt9t031 selected register addresses */ +#define MT9T031_CHIP_VERSION 0x00 +#define MT9T031_ROW_START 0x01 +#define MT9T031_COLUMN_START 0x02 +#define MT9T031_WINDOW_HEIGHT 0x03 +#define MT9T031_WINDOW_WIDTH 0x04 +#define MT9T031_HORIZONTAL_BLANKING 0x05 +#define MT9T031_VERTICAL_BLANKING 0x06 +#define MT9T031_OUTPUT_CONTROL 0x07 +#define MT9T031_SHUTTER_WIDTH_UPPER 0x08 +#define MT9T031_SHUTTER_WIDTH 0x09 +#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a +#define MT9T031_FRAME_RESTART 0x0b +#define MT9T031_SHUTTER_DELAY 0x0c +#define MT9T031_RESET 0x0d +#define MT9T031_READ_MODE_1 0x1e +#define MT9T031_READ_MODE_2 0x20 +#define MT9T031_READ_MODE_3 0x21 +#define MT9T031_ROW_ADDRESS_MODE 0x22 +#define MT9T031_COLUMN_ADDRESS_MODE 0x23 +#define MT9T031_GLOBAL_GAIN 0x35 +#define MT9T031_CHIP_ENABLE 0xF8 + +#define MT9T031_MAX_HEIGHT 1536 +#define MT9T031_MAX_WIDTH 2048 +#define MT9T031_MIN_HEIGHT 2 +#define MT9T031_MIN_WIDTH 18 +#define MT9T031_HORIZONTAL_BLANK 142 +#define MT9T031_VERTICAL_BLANK 25 +#define MT9T031_COLUMN_SKIP 32 +#define MT9T031_ROW_SKIP 20 + +struct mt9t031 { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct { + /* exposure/auto-exposure cluster */ + struct v4l2_ctrl *autoexposure; + struct v4l2_ctrl *exposure; + }; + struct v4l2_rect rect; /* Sensor window */ + struct v4l2_clk *clk; + u16 xskip; + u16 yskip; + unsigned int total_h; + unsigned short y_skip_top; /* Lines to skip at the top */ +}; + +static struct mt9t031 *to_mt9t031(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9t031, subdev); +} + +static int reg_read(struct i2c_client *client, const u8 reg) +{ + return i2c_smbus_read_word_swapped(client, reg); +} + +static int reg_write(struct i2c_client *client, const u8 reg, + const u16 data) +{ + return i2c_smbus_write_word_swapped(client, reg, data); +} + +static int reg_set(struct i2c_client *client, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, ret | data); +} + +static int reg_clear(struct i2c_client *client, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, ret & ~data); +} + +static int set_shutter(struct i2c_client *client, const u32 data) +{ + int ret; + + ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16); + + if (ret >= 0) + ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff); + + return ret; +} + +static int get_shutter(struct i2c_client *client, u32 *data) +{ + int ret; + + ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER); + *data = ret << 16; + + if (ret >= 0) + ret = reg_read(client, MT9T031_SHUTTER_WIDTH); + *data |= ret & 0xffff; + + return ret < 0 ? ret : 0; +} + +static int mt9t031_idle(struct i2c_client *client) +{ + int ret; + + /* Disable chip output, synchronous option update */ + ret = reg_write(client, MT9T031_RESET, 1); + if (ret >= 0) + ret = reg_write(client, MT9T031_RESET, 0); + if (ret >= 0) + ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); + + return ret >= 0 ? 0 : -EIO; +} + +static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + if (enable) + /* Switch to master "normal" mode */ + ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2); + else + /* Stop sensor readout */ + ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); + + if (ret < 0) + return -EIO; + + return 0; +} + +/* target must be _even_ */ +static u16 mt9t031_skip(s32 *source, s32 target, s32 max) +{ + unsigned int skip; + + if (*source < target + target / 2) { + *source = target; + return 1; + } + + skip = min(max, *source + target / 2) / target; + if (skip > 8) + skip = 8; + *source = target * skip; + + return skip; +} + +/* rect is the sensor rectangle, the caller guarantees parameter validity */ +static int mt9t031_set_params(struct i2c_client *client, + struct v4l2_rect *rect, u16 xskip, u16 yskip) +{ + struct mt9t031 *mt9t031 = to_mt9t031(client); + int ret; + u16 xbin, ybin; + const u16 hblank = MT9T031_HORIZONTAL_BLANK, + vblank = MT9T031_VERTICAL_BLANK; + + xbin = min(xskip, (u16)3); + ybin = min(yskip, (u16)3); + + /* + * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper. + * There is always a valid suitably aligned value. The worst case is + * xbin = 3, width = 2048. Then we will start at 36, the last read out + * pixel will be 2083, which is < 2085 - first black pixel. + * + * MT9T031 datasheet imposes window left border alignment, depending on + * the selected xskip. Failing to conform to this requirement produces + * dark horizontal stripes in the image. However, even obeying to this + * requirement doesn't eliminate the stripes in all configurations. They + * appear "locally reproducibly," but can differ between tests under + * different lighting conditions. + */ + switch (xbin) { + case 1: + rect->left &= ~1; + break; + case 2: + rect->left &= ~3; + break; + case 3: + rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ? + (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6); + } + + rect->top &= ~1; + + dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n", + xskip, yskip, rect->width, rect->height, rect->left, rect->top); + + /* Disable register update, reconfigure atomically */ + ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1); + if (ret < 0) + return ret; + + /* Blanking and start values - default... */ + ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank); + if (ret >= 0) + ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank); + + if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) { + /* Binning, skipping */ + if (ret >= 0) + ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, + ((xbin - 1) << 4) | (xskip - 1)); + if (ret >= 0) + ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, + ((ybin - 1) << 4) | (yskip - 1)); + } + dev_dbg(&client->dev, "new physical left %u, top %u\n", + rect->left, rect->top); + + /* + * The caller provides a supported format, as guaranteed by + * .set_fmt(FORMAT_TRY), soc_camera_s_selection() and soc_camera_cropcap() + */ + if (ret >= 0) + ret = reg_write(client, MT9T031_COLUMN_START, rect->left); + if (ret >= 0) + ret = reg_write(client, MT9T031_ROW_START, rect->top); + if (ret >= 0) + ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1); + if (ret >= 0) + ret = reg_write(client, MT9T031_WINDOW_HEIGHT, + rect->height + mt9t031->y_skip_top - 1); + if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) { + mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank; + + ret = set_shutter(client, mt9t031->total_h); + } + + /* Re-enable register update, commit all changes */ + if (ret >= 0) + ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); + + if (ret >= 0) { + mt9t031->rect = *rect; + mt9t031->xskip = xskip; + mt9t031->yskip = yskip; + } + + return ret < 0 ? ret : 0; +} + +static int mt9t031_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct v4l2_rect rect = sel->r; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + rect.width = ALIGN(rect.width, 2); + rect.height = ALIGN(rect.height, 2); + + soc_camera_limit_side(&rect.left, &rect.width, + MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT); + + return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip); +} + +static int mt9t031_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t031 *mt9t031 = to_mt9t031(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = MT9T031_COLUMN_SKIP; + sel->r.top = MT9T031_ROW_SKIP; + sel->r.width = MT9T031_MAX_WIDTH; + sel->r.height = MT9T031_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = mt9t031->rect; + return 0; + default: + return -EINVAL; + } +} + +static int mt9t031_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t031 *mt9t031 = to_mt9t031(client); + + if (format->pad) + return -EINVAL; + + mf->width = mt9t031->rect.width / mt9t031->xskip; + mf->height = mt9t031->rect.height / mt9t031->yskip; + mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; + mf->colorspace = V4L2_COLORSPACE_SRGB; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +/* + * If a user window larger than sensor window is requested, we'll increase the + * sensor window. + */ +static int mt9t031_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t031 *mt9t031 = to_mt9t031(client); + u16 xskip, yskip; + struct v4l2_rect rect = mt9t031->rect; + + if (format->pad) + return -EINVAL; + + mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; + mf->colorspace = V4L2_COLORSPACE_SRGB; + v4l_bound_align_image( + &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1, + &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *mf; + return 0; + } + + /* + * Width and height are within limits. + * S_FMT: use binning and skipping for scaling + */ + xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH); + yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT); + + mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; + mf->colorspace = V4L2_COLORSPACE_SRGB; + + /* mt9t031_set_params() doesn't change width and height */ + return mt9t031_set_params(client, &rect, xskip, yskip); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9t031_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff) + return -EINVAL; + + reg->size = 1; + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9t031_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff) + return -EINVAL; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9t031 *mt9t031 = container_of(ctrl->handler, + struct mt9t031, hdl); + const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK; + s32 min, max; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_AUTO: + min = mt9t031->exposure->minimum; + max = mt9t031->exposure->maximum; + mt9t031->exposure->val = + (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min)) + / shutter_max + min; + break; + } + return 0; +} + +static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9t031 *mt9t031 = container_of(ctrl->handler, + struct mt9t031, hdl); + struct v4l2_subdev *sd = &mt9t031->subdev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_ctrl *exp = mt9t031->exposure; + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->val) + data = reg_set(client, MT9T031_READ_MODE_2, 0x8000); + else + data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000); + if (data < 0) + return -EIO; + return 0; + case V4L2_CID_HFLIP: + if (ctrl->val) + data = reg_set(client, MT9T031_READ_MODE_2, 0x4000); + else + data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000); + if (data < 0) + return -EIO; + return 0; + case V4L2_CID_GAIN: + /* See Datasheet Table 7, Gain settings. */ + if (ctrl->val <= ctrl->default_value) { + /* Pack it into 0..1 step 0.125, register values 0..8 */ + unsigned long range = ctrl->default_value - ctrl->minimum; + data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; + + dev_dbg(&client->dev, "Setting gain %d\n", data); + data = reg_write(client, MT9T031_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } else { + /* Pack it into 1.125..128 variable step, register values 9..0x7860 */ + /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ + unsigned long range = ctrl->maximum - ctrl->default_value - 1; + /* calculated gain: map 65..127 to 9..1024 step 0.125 */ + unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * + 1015 + range / 2) / range + 9; + + if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ + data = gain; + else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */ + data = ((gain - 32) * 16 + 16) / 32 + 80; + else + /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ + data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; + + dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n", + reg_read(client, MT9T031_GLOBAL_GAIN), data); + data = reg_write(client, MT9T031_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } + return 0; + + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_MANUAL) { + unsigned int range = exp->maximum - exp->minimum; + unsigned int shutter = ((exp->val - (s32)exp->minimum) * 1048 + + range / 2) / range + 1; + u32 old; + + get_shutter(client, &old); + dev_dbg(&client->dev, "Set shutter from %u to %u\n", + old, shutter); + if (set_shutter(client, shutter) < 0) + return -EIO; + } else { + const u16 vblank = MT9T031_VERTICAL_BLANK; + mt9t031->total_h = mt9t031->rect.height + + mt9t031->y_skip_top + vblank; + + if (set_shutter(client, mt9t031->total_h) < 0) + return -EIO; + } + return 0; + default: + return -EINVAL; + } + return 0; +} + +/* + * Power Management: + * This function does nothing for now but must be present for pm to work + */ +static int mt9t031_runtime_suspend(struct device *dev) +{ + return 0; +} + +/* + * Power Management: + * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged + * they are however changed at reset if the platform hook is present + * thus we rewrite them with the values stored by the driver + */ +static int mt9t031_runtime_resume(struct device *dev) +{ + struct video_device *vdev = to_video_device(dev); + struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t031 *mt9t031 = to_mt9t031(client); + + int ret; + u16 xbin, ybin; + + xbin = min(mt9t031->xskip, (u16)3); + ybin = min(mt9t031->yskip, (u16)3); + + ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, + ((xbin - 1) << 4) | (mt9t031->xskip - 1)); + if (ret < 0) + return ret; + + ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, + ((ybin - 1) << 4) | (mt9t031->yskip - 1)); + if (ret < 0) + return ret; + + return 0; +} + +static const struct dev_pm_ops mt9t031_dev_pm_ops = { + .runtime_suspend = mt9t031_runtime_suspend, + .runtime_resume = mt9t031_runtime_resume, +}; + +static const struct device_type mt9t031_dev_type = { + .name = "MT9T031", + .pm = &mt9t031_dev_pm_ops, +}; + +static int mt9t031_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct video_device *vdev = soc_camera_i2c_to_vdev(client); + struct mt9t031 *mt9t031 = to_mt9t031(client); + int ret; + + if (on) { + ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk); + if (ret < 0) + return ret; + if (vdev) + /* Not needed during probing, when vdev isn't available yet */ + vdev->dev.type = &mt9t031_dev_type; + } else { + if (vdev) + vdev->dev.type = NULL; + soc_camera_power_off(&client->dev, ssdd, mt9t031->clk); + } + + return 0; +} + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int mt9t031_video_probe(struct i2c_client *client) +{ + struct mt9t031 *mt9t031 = to_mt9t031(client); + s32 data; + int ret; + + ret = mt9t031_s_power(&mt9t031->subdev, 1); + if (ret < 0) + return ret; + + ret = mt9t031_idle(client); + if (ret < 0) { + dev_err(&client->dev, "Failed to initialise the camera\n"); + goto done; + } + + /* Read out the chip version register */ + data = reg_read(client, MT9T031_CHIP_VERSION); + + switch (data) { + case 0x1621: + break; + default: + dev_err(&client->dev, + "No MT9T031 chip detected, register read %x\n", data); + ret = -ENODEV; + goto done; + } + + dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data); + + ret = v4l2_ctrl_handler_setup(&mt9t031->hdl); + +done: + mt9t031_s_power(&mt9t031->subdev, 0); + + return ret; +} + +static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t031 *mt9t031 = to_mt9t031(client); + + *lines = mt9t031->y_skip_top; + + return 0; +} + +static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = { + .g_volatile_ctrl = mt9t031_g_volatile_ctrl, + .s_ctrl = mt9t031_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { + .s_power = mt9t031_s_power, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9t031_g_register, + .s_register = mt9t031_s_register, +#endif +}; + +static int mt9t031_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + return 0; +} + +static int mt9t031_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); + + return 0; +} + +static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, + const struct v4l2_mbus_config *cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + + if (soc_camera_apply_board_flags(ssdd, cfg) & + V4L2_MBUS_PCLK_SAMPLE_FALLING) + return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); + else + return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); +} + +static const struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { + .s_stream = mt9t031_s_stream, + .g_mbus_config = mt9t031_g_mbus_config, + .s_mbus_config = mt9t031_s_mbus_config, +}; + +static const struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = { + .g_skip_top_lines = mt9t031_g_skip_top_lines, +}; + +static const struct v4l2_subdev_pad_ops mt9t031_subdev_pad_ops = { + .enum_mbus_code = mt9t031_enum_mbus_code, + .get_selection = mt9t031_get_selection, + .set_selection = mt9t031_set_selection, + .get_fmt = mt9t031_get_fmt, + .set_fmt = mt9t031_set_fmt, +}; + +static const struct v4l2_subdev_ops mt9t031_subdev_ops = { + .core = &mt9t031_subdev_core_ops, + .video = &mt9t031_subdev_video_ops, + .sensor = &mt9t031_subdev_sensor_ops, + .pad = &mt9t031_subdev_pad_ops, +}; + +static int mt9t031_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct mt9t031 *mt9t031; + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int ret; + + if (!ssdd) { + dev_err(&client->dev, "MT9T031 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9t031 = devm_kzalloc(&client->dev, sizeof(struct mt9t031), GFP_KERNEL); + if (!mt9t031) + return -ENOMEM; + + v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops); + v4l2_ctrl_handler_init(&mt9t031->hdl, 5); + v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 64); + + /* + * Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width + */ + mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl, + &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, + V4L2_EXPOSURE_AUTO); + mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 255, 1, 255); + + mt9t031->subdev.ctrl_handler = &mt9t031->hdl; + if (mt9t031->hdl.error) + return mt9t031->hdl.error; + + v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure, + V4L2_EXPOSURE_MANUAL, true); + + mt9t031->y_skip_top = 0; + mt9t031->rect.left = MT9T031_COLUMN_SKIP; + mt9t031->rect.top = MT9T031_ROW_SKIP; + mt9t031->rect.width = MT9T031_MAX_WIDTH; + mt9t031->rect.height = MT9T031_MAX_HEIGHT; + + mt9t031->xskip = 1; + mt9t031->yskip = 1; + + mt9t031->clk = v4l2_clk_get(&client->dev, "mclk"); + if (IS_ERR(mt9t031->clk)) { + ret = PTR_ERR(mt9t031->clk); + goto eclkget; + } + + ret = mt9t031_video_probe(client); + if (ret) { + v4l2_clk_put(mt9t031->clk); +eclkget: + v4l2_ctrl_handler_free(&mt9t031->hdl); + } + + return ret; +} + +static int mt9t031_remove(struct i2c_client *client) +{ + struct mt9t031 *mt9t031 = to_mt9t031(client); + + v4l2_clk_put(mt9t031->clk); + v4l2_device_unregister_subdev(&mt9t031->subdev); + v4l2_ctrl_handler_free(&mt9t031->hdl); + + return 0; +} + +static const struct i2c_device_id mt9t031_id[] = { + { "mt9t031", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9t031_id); + +static struct i2c_driver mt9t031_i2c_driver = { + .driver = { + .name = "mt9t031", + }, + .probe = mt9t031_probe, + .remove = mt9t031_remove, + .id_table = mt9t031_id, +}; + +module_i2c_driver(mt9t031_i2c_driver); + +MODULE_DESCRIPTION("Micron MT9T031 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From dd1df35dd0f6172537271e533fe329a165e56265 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 7 Feb 2019 08:50:48 -0500 Subject: media: soc_camera: Depend on BROKEN This patch makes the SoC camera framework as well as effectively the few remaining sensor drivers depend on BROKEN, rendering it uncompileable without changes in Kconfig. The purpose is to leave the code around if someone wishes to convert the drivers to the modern day V4L2 sub-device framework without having to go to see development history in git. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/soc_camera/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig index 6a6aa6d2d150..bacd30f0348d 100644 --- a/drivers/staging/media/soc_camera/Kconfig +++ b/drivers/staging/media/soc_camera/Kconfig @@ -1,6 +1,6 @@ config SOC_CAMERA tristate "SoC camera support" - depends on VIDEO_V4L2 && HAS_DMA && I2C + depends on VIDEO_V4L2 && HAS_DMA && I2C && BROKEN select VIDEOBUF2_CORE help SoC Camera is a common API to several cameras, not connecting -- cgit v1.2.3-59-g8ed1b From 959bba134238452b0a2f5c7d107d7d05b358b41f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 17 Feb 2019 09:12:41 -0500 Subject: media: vivid: two unregistration fixes When the media device registration fails, don't call media_device_unregister since the device was never actually registered. When removing the module also call media_device_cleanup() to avoid a memory leak. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 29e7b14fa704..342e0e6c103b 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -1478,9 +1478,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) return 0; unreg_dev: -#ifdef CONFIG_MEDIA_CONTROLLER - media_device_unregister(&dev->mdev); -#endif video_unregister_device(&dev->radio_tx_dev); video_unregister_device(&dev->radio_rx_dev); video_unregister_device(&dev->sdr_cap_dev); @@ -1553,6 +1550,7 @@ static int vivid_remove(struct platform_device *pdev) #ifdef CONFIG_MEDIA_CONTROLLER media_device_unregister(&dev->mdev); + media_device_cleanup(&dev->mdev); #endif if (dev->has_vid_cap) { -- cgit v1.2.3-59-g8ed1b From cdabfa84025946b0eae4fefb5f6af3f9e99ce88b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 17 Feb 2019 09:38:19 -0500 Subject: media: vimc: fix memory leak media_device_cleanup() wasn't called, which caused a small memory leak. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index c2fdf3ea67ed..0fbb7914098f 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -220,6 +220,7 @@ static int vimc_comp_bind(struct device *master) err_mdev_unregister: media_device_unregister(&vimc->mdev); + media_device_cleanup(&vimc->mdev); err_comp_unbind_all: component_unbind_all(master, NULL); err_v4l2_unregister: @@ -236,6 +237,7 @@ static void vimc_comp_unbind(struct device *master) dev_dbg(master, "unbind"); media_device_unregister(&vimc->mdev); + media_device_cleanup(&vimc->mdev); component_unbind_all(master, NULL); v4l2_device_unregister(&vimc->v4l2_dev); } -- cgit v1.2.3-59-g8ed1b From e3e71be056df8309c80ed7457122b726172c45a1 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 17 Feb 2019 17:26:05 -0500 Subject: media: wl128x: fix spelling mistake: "Swtich" -> "Switch" There is a spelling mistake in a fmdbg debug message. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/wl128x/fmdrv_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 800d69c3f80b..e1c218b23d9e 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -908,7 +908,7 @@ static void fm_irq_afjump_setfreq(struct fmdev *fmdev) u16 frq_index; u16 payload; - fmdbg("Swtich to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]); + fmdbg("Switch to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]); frq_index = (fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx] - fmdev->rx.region.bot_freq) / FM_FREQ_MUL; -- cgit v1.2.3-59-g8ed1b From 46c039d06b6ecabb94bd16c3a999b28dc83b79ce Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Mon, 14 Jan 2019 19:14:24 -0500 Subject: media: cx25840: mark pad sig_types to fix cx231xx init Without this, we get failures like this when the kernel attempts to initialize a cx231xx device: [16046.153653] cx231xx 3-1.2:1.1: New device Hauppauge Hauppauge Device @ 480 Mbps (2040:c200) with 6 interfaces [16046.153900] cx231xx 3-1.2:1.1: can't change interface 3 alt no. to 3: Max. Pkt size = 0 [16046.153907] cx231xx 3-1.2:1.1: Identified as Hauppauge USB Live 2 (card=9) [16046.154350] i2c i2c-11: Added multiplexed i2c bus 13 [16046.154379] i2c i2c-11: Added multiplexed i2c bus 14 [16046.267194] cx25840 10-0044: cx23102 A/V decoder found @ 0x88 (cx231xx #0-0) [16048.424551] cx25840 10-0044: loaded v4l-cx231xx-avcore-01.fw firmware (16382 bytes) [16048.463224] cx231xx 3-1.2:1.1: v4l2 driver version 0.0.3 [16048.567878] cx231xx 3-1.2:1.1: Registered video device video2 [v4l2] [16048.568001] cx231xx 3-1.2:1.1: Registered VBI device vbi0 [16048.568419] cx231xx 3-1.2:1.1: audio EndPoint Addr 0x83, Alternate settings: 3 [16048.568425] cx231xx 3-1.2:1.1: video EndPoint Addr 0x84, Alternate settings: 5 [16048.568431] cx231xx 3-1.2:1.1: VBI EndPoint Addr 0x85, Alternate settings: 2 [16048.568436] cx231xx 3-1.2:1.1: sliced CC EndPoint Addr 0x86, Alternate settings: 2 [16048.568448] usb 3-1.2: couldn't get decoder output pad for V4L I/O [16048.568453] cx231xx 3-1.2:1.1: V4L2 device vbi0 deregistered [16048.568579] cx231xx 3-1.2:1.1: V4L2 device video2 deregistered [16048.569001] cx231xx: probe of 3-1.2:1.1 failed with error -22 Likely a regession since Commit 9d6d20e652c0 ("media: v4l2-mc: switch it to use the new approach to setup pipelines") (v4.19-rc1-100-g9d6d20e652c0), which introduced the use of PAD_SIGNAL_DV within v4l2_mc_create_media_graph(). This also modifies cx25840 to remove the VBI pad, matching the action taken in Commit 092a37875a22 ("media: v4l2: remove VBI output pad"). Fixes: 9d6d20e652c0 ("media: v4l2-mc: switch it to use the new approach to setup pipelines") Cc: stable@vger.kernel.org Signed-off-by: Cody P Schafer Tested-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/cx25840/cx25840-core.c | 3 ++- drivers/media/i2c/cx25840/cx25840-core.h | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index b168bf3635b6..8b0b8b5aa531 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -5216,8 +5216,9 @@ static int cx25840_probe(struct i2c_client *client, * those extra inputs. So, let's add it only when needed. */ state->pads[CX25840_PAD_INPUT].flags = MEDIA_PAD_FL_SINK; + state->pads[CX25840_PAD_INPUT].sig_type = PAD_SIGNAL_ANALOG; state->pads[CX25840_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; - state->pads[CX25840_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[CX25840_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV; sd->entity.function = MEDIA_ENT_F_ATV_DECODER; ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads), diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h index c323b1af1f83..9efefa15d090 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.h +++ b/drivers/media/i2c/cx25840/cx25840-core.h @@ -40,7 +40,6 @@ enum cx25840_model { enum cx25840_media_pads { CX25840_PAD_INPUT, CX25840_PAD_VID_OUT, - CX25840_PAD_VBI_OUT, CX25840_NUM_PADS }; -- cgit v1.2.3-59-g8ed1b From a7fe4ca72b1fe7877de5672640d0b4e023d0fdca Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Thu, 7 Feb 2019 22:18:43 -0500 Subject: media: v4l: Add 32-bit packed YUV formats The formats added in this patch include: V4L2_PIX_FMT_AYUV32 V4L2_PIX_FMT_XYUV32 V4L2_PIX_FMT_VUYA32 V4L2_PIX_FMT_VUYX32 These formats enable the trasmission of alpha channel data to other drivers and userspace applications in addition to YUV data. For example, buffers generated by drivers in one of these formats can be used by the Weston compositor to display as a texture or flipped directly onto the overlay planes with the help of a DRM driver. Signed-off-by: Vivek Kasireddy Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/pixfmt-packed-yuv.rst | 170 ++++++++++++++++++++- drivers/media/v4l2-core/v4l2-ioctl.c | 4 + include/uapi/linux/videodev2.h | 4 + 3 files changed, 177 insertions(+), 1 deletion(-) diff --git a/Documentation/media/uapi/v4l/pixfmt-packed-yuv.rst b/Documentation/media/uapi/v4l/pixfmt-packed-yuv.rst index f53e8f57a003..7fcee1c11ac4 100644 --- a/Documentation/media/uapi/v4l/pixfmt-packed-yuv.rst +++ b/Documentation/media/uapi/v4l/pixfmt-packed-yuv.rst @@ -190,6 +190,170 @@ component of each pixel in one 16 or 32 bit word. - Cr\ :sub:`2` - Cr\ :sub:`1` - Cr\ :sub:`0` + - + * .. _V4L2-PIX-FMT-AYUV32: + + - ``V4L2_PIX_FMT_AYUV32`` + - 'AYUV' + + - a\ :sub:`7` + - a\ :sub:`6` + - a\ :sub:`5` + - a\ :sub:`4` + - a\ :sub:`3` + - a\ :sub:`2` + - a\ :sub:`1` + - a\ :sub:`0` + + - Y'\ :sub:`7` + - Y'\ :sub:`6` + - Y'\ :sub:`5` + - Y'\ :sub:`4` + - Y'\ :sub:`3` + - Y'\ :sub:`2` + - Y'\ :sub:`1` + - Y'\ :sub:`0` + + - Cb\ :sub:`7` + - Cb\ :sub:`6` + - Cb\ :sub:`5` + - Cb\ :sub:`4` + - Cb\ :sub:`3` + - Cb\ :sub:`2` + - Cb\ :sub:`1` + - Cb\ :sub:`0` + + - Cr\ :sub:`7` + - Cr\ :sub:`6` + - Cr\ :sub:`5` + - Cr\ :sub:`4` + - Cr\ :sub:`3` + - Cr\ :sub:`2` + - Cr\ :sub:`1` + - Cr\ :sub:`0` + - + * .. _V4L2-PIX-FMT-XYUV32: + + - ``V4L2_PIX_FMT_XYUV32`` + - 'XYUV' + + - + - + - + - + - + - + - + - + + - Y'\ :sub:`7` + - Y'\ :sub:`6` + - Y'\ :sub:`5` + - Y'\ :sub:`4` + - Y'\ :sub:`3` + - Y'\ :sub:`2` + - Y'\ :sub:`1` + - Y'\ :sub:`0` + + - Cb\ :sub:`7` + - Cb\ :sub:`6` + - Cb\ :sub:`5` + - Cb\ :sub:`4` + - Cb\ :sub:`3` + - Cb\ :sub:`2` + - Cb\ :sub:`1` + - Cb\ :sub:`0` + + - Cr\ :sub:`7` + - Cr\ :sub:`6` + - Cr\ :sub:`5` + - Cr\ :sub:`4` + - Cr\ :sub:`3` + - Cr\ :sub:`2` + - Cr\ :sub:`1` + - Cr\ :sub:`0` + - + * .. _V4L2-PIX-FMT-VUYA32: + + - ``V4L2_PIX_FMT_VUYA32`` + - 'VUYA' + + - Cr\ :sub:`7` + - Cr\ :sub:`6` + - Cr\ :sub:`5` + - Cr\ :sub:`4` + - Cr\ :sub:`3` + - Cr\ :sub:`2` + - Cr\ :sub:`1` + - Cr\ :sub:`0` + + - Cb\ :sub:`7` + - Cb\ :sub:`6` + - Cb\ :sub:`5` + - Cb\ :sub:`4` + - Cb\ :sub:`3` + - Cb\ :sub:`2` + - Cb\ :sub:`1` + - Cb\ :sub:`0` + + - Y'\ :sub:`7` + - Y'\ :sub:`6` + - Y'\ :sub:`5` + - Y'\ :sub:`4` + - Y'\ :sub:`3` + - Y'\ :sub:`2` + - Y'\ :sub:`1` + - Y'\ :sub:`0` + + - a\ :sub:`7` + - a\ :sub:`6` + - a\ :sub:`5` + - a\ :sub:`4` + - a\ :sub:`3` + - a\ :sub:`2` + - a\ :sub:`1` + - a\ :sub:`0` + - + * .. _V4L2-PIX-FMT-VUYX32: + + - ``V4L2_PIX_FMT_VUYX32`` + - 'VUYX' + + - Cr\ :sub:`7` + - Cr\ :sub:`6` + - Cr\ :sub:`5` + - Cr\ :sub:`4` + - Cr\ :sub:`3` + - Cr\ :sub:`2` + - Cr\ :sub:`1` + - Cr\ :sub:`0` + + - Cb\ :sub:`7` + - Cb\ :sub:`6` + - Cb\ :sub:`5` + - Cb\ :sub:`4` + - Cb\ :sub:`3` + - Cb\ :sub:`2` + - Cb\ :sub:`1` + - Cb\ :sub:`0` + + - Y'\ :sub:`7` + - Y'\ :sub:`6` + - Y'\ :sub:`5` + - Y'\ :sub:`4` + - Y'\ :sub:`3` + - Y'\ :sub:`2` + - Y'\ :sub:`1` + - Y'\ :sub:`0` + + - + - + - + - + - + - + - + - .. raw:: latex @@ -202,4 +366,8 @@ component of each pixel in one 16 or 32 bit word. #) The value of a = alpha bits is undefined when reading from the driver, ignored when writing to the driver, except when alpha blending has been negotiated for a :ref:`Video Overlay ` or - :ref:`Video Output Overlay `. + :ref:`Video Output Overlay ` for the formats Y444, YUV555 and + YUV4. However, for formats AYUV32 and VUYA32, the alpha component is + expected to contain a meaningful value that can be used by drivers + and applications. And, the formats XYUV32 and VUYX32 contain undefined + alpha values that must be ignored by all applications and drivers. diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 206b7348797e..6f707466b5d2 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1220,6 +1220,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_YUV555: descr = "16-bit A/XYUV 1-5-5-5"; break; case V4L2_PIX_FMT_YUV565: descr = "16-bit YUV 5-6-5"; break; case V4L2_PIX_FMT_YUV32: descr = "32-bit A/XYUV 8-8-8-8"; break; + case V4L2_PIX_FMT_AYUV32: descr = "32-bit AYUV 8-8-8-8"; break; + case V4L2_PIX_FMT_XYUV32: descr = "32-bit XYUV 8-8-8-8"; break; + case V4L2_PIX_FMT_VUYA32: descr = "32-bit VUYA 8-8-8-8"; break; + case V4L2_PIX_FMT_VUYX32: descr = "32-bit VUYX 8-8-8-8"; break; case V4L2_PIX_FMT_YUV410: descr = "Planar YUV 4:1:0"; break; case V4L2_PIX_FMT_YUV420: descr = "Planar YUV 4:2:0"; break; case V4L2_PIX_FMT_HI240: descr = "8-bit Dithered RGB (BTTV)"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 9a920f071ff9..1db220da3bcc 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -562,6 +562,10 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_YUV555 v4l2_fourcc('Y', 'U', 'V', 'O') /* 16 YUV-5-5-5 */ #define V4L2_PIX_FMT_YUV565 v4l2_fourcc('Y', 'U', 'V', 'P') /* 16 YUV-5-6-5 */ #define V4L2_PIX_FMT_YUV32 v4l2_fourcc('Y', 'U', 'V', '4') /* 32 YUV-8-8-8-8 */ +#define V4L2_PIX_FMT_AYUV32 v4l2_fourcc('A', 'Y', 'U', 'V') /* 32 AYUV-8-8-8-8 */ +#define V4L2_PIX_FMT_XYUV32 v4l2_fourcc('X', 'Y', 'U', 'V') /* 32 XYUV-8-8-8-8 */ +#define V4L2_PIX_FMT_VUYA32 v4l2_fourcc('V', 'U', 'Y', 'A') /* 32 VUYA-8-8-8-8 */ +#define V4L2_PIX_FMT_VUYX32 v4l2_fourcc('V', 'U', 'Y', 'X') /* 32 VUYX-8-8-8-8 */ #define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* 8 8-bit color */ #define V4L2_PIX_FMT_HM12 v4l2_fourcc('H', 'M', '1', '2') /* 8 YUV 4:2:0 16x16 macroblocks */ #define V4L2_PIX_FMT_M420 v4l2_fourcc('M', '4', '2', '0') /* 12 YUV 4:2:0 2 lines y, 1 line uv interleaved */ -- cgit v1.2.3-59-g8ed1b From 10a2bc7e63f9fc0a7255a301130cad02fa521326 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Fri, 8 Feb 2019 20:29:32 -0500 Subject: media: v4l2-tpg-core: Add support for 32-bit packed YUV formats (v2) Add support for the following formats to tpg: V4L2_PIX_FMT_AYUV32 V4L2_PIX_FMT_XYUV32 V4L2_PIX_FMT_VUYA32 V4L2_PIX_FMT_VUYX32 Changes from v1: Remove the duplicate case value Signed-off-by: Vivek Kasireddy Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/v4l2-tpg/v4l2-tpg-core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index d9a590ae7545..07e0629af8ed 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -246,6 +246,10 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) case V4L2_PIX_FMT_YUV555: case V4L2_PIX_FMT_YUV565: case V4L2_PIX_FMT_YUV32: + case V4L2_PIX_FMT_AYUV32: + case V4L2_PIX_FMT_XYUV32: + case V4L2_PIX_FMT_VUYA32: + case V4L2_PIX_FMT_VUYX32: tpg->color_enc = TGP_COLOR_ENC_YCBCR; break; case V4L2_PIX_FMT_YUV420M: @@ -372,6 +376,10 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) case V4L2_PIX_FMT_ARGB32: case V4L2_PIX_FMT_ABGR32: case V4L2_PIX_FMT_YUV32: + case V4L2_PIX_FMT_AYUV32: + case V4L2_PIX_FMT_XYUV32: + case V4L2_PIX_FMT_VUYA32: + case V4L2_PIX_FMT_VUYX32: case V4L2_PIX_FMT_HSV32: tpg->twopixelsize[0] = 2 * 4; break; @@ -1267,10 +1275,12 @@ static void gen_twopix(struct tpg_data *tpg, case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_XRGB32: case V4L2_PIX_FMT_HSV32: + case V4L2_PIX_FMT_XYUV32: alpha = 0; /* fall through */ case V4L2_PIX_FMT_YUV32: case V4L2_PIX_FMT_ARGB32: + case V4L2_PIX_FMT_AYUV32: buf[0][offset] = alpha; buf[0][offset + 1] = r_y_h; buf[0][offset + 2] = g_u_s; @@ -1278,9 +1288,11 @@ static void gen_twopix(struct tpg_data *tpg, break; case V4L2_PIX_FMT_BGR32: case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_VUYX32: alpha = 0; /* fall through */ case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_VUYA32: buf[0][offset] = b_v; buf[0][offset + 1] = g_u_s; buf[0][offset + 2] = r_y_h; -- cgit v1.2.3-59-g8ed1b From 6377bb7d18cc2d5892aa8f8f67b3226331847ec3 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Thu, 7 Feb 2019 22:18:45 -0500 Subject: media: vivid: Add definitions for the 32-bit packed YUV formats Enable vivid to make use of the following formats: V4L2_PIX_FMT_AYUV32 V4L2_PIX_FMT_XYUV32 V4L2_PIX_FMT_VUYA32 V4L2_PIX_FMT_VUYX32 Signed-off-by: Vivek Kasireddy Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-vid-common.c | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c index 661f4015fba1..74b83bcc6119 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.c +++ b/drivers/media/platform/vivid/vivid-vid-common.c @@ -168,6 +168,36 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, .alpha_mask = 0x000000ff, }, + { + .fourcc = V4L2_PIX_FMT_AYUV32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, + .alpha_mask = 0x000000ff, + }, + { + .fourcc = V4L2_PIX_FMT_XYUV32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, + }, + { + .fourcc = V4L2_PIX_FMT_VUYA32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, + .alpha_mask = 0xff000000, + }, + { + .fourcc = V4L2_PIX_FMT_VUYX32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, + }, { .fourcc = V4L2_PIX_FMT_GREY, .vdownsampling = { 1 }, -- cgit v1.2.3-59-g8ed1b From 98617b45674113d241ec3264019217108d3a87a4 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Fri, 8 Feb 2019 20:38:18 -0500 Subject: media: imx-pxp: Start using the format VUYA32 instead of YUV32 (v2) Buffers generated with YUV32 format seems to be incorrect, hence use VUYA32 instead. Changes from v1: Add both formats VUYA32 and VUYX32 but associate only VUYX32 to the output queue as the alpha channel of buffers is ignored on this queue. Signed-off-by: Vivek Kasireddy Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/imx-pxp.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/imx-pxp.c b/drivers/media/platform/imx-pxp.c index f087dc4fc729..0bcfc5aa8f3d 100644 --- a/drivers/media/platform/imx-pxp.c +++ b/drivers/media/platform/imx-pxp.c @@ -90,7 +90,11 @@ static struct pxp_fmt formats[] = { .depth = 16, .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, }, { - .fourcc = V4L2_PIX_FMT_YUV32, + .fourcc = V4L2_PIX_FMT_VUYA32, + .depth = 32, + .types = MEM2MEM_CAPTURE, + }, { + .fourcc = V4L2_PIX_FMT_VUYX32, .depth = 32, .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, }, { @@ -236,7 +240,7 @@ static u32 pxp_v4l2_pix_fmt_to_ps_format(u32 v4l2_pix_fmt) case V4L2_PIX_FMT_RGB555: return BV_PXP_PS_CTRL_FORMAT__RGB555; case V4L2_PIX_FMT_RGB444: return BV_PXP_PS_CTRL_FORMAT__RGB444; case V4L2_PIX_FMT_RGB565: return BV_PXP_PS_CTRL_FORMAT__RGB565; - case V4L2_PIX_FMT_YUV32: return BV_PXP_PS_CTRL_FORMAT__YUV1P444; + case V4L2_PIX_FMT_VUYX32: return BV_PXP_PS_CTRL_FORMAT__YUV1P444; case V4L2_PIX_FMT_UYVY: return BV_PXP_PS_CTRL_FORMAT__UYVY1P422; case V4L2_PIX_FMT_YUYV: return BM_PXP_PS_CTRL_WB_SWAP | BV_PXP_PS_CTRL_FORMAT__UYVY1P422; @@ -265,7 +269,8 @@ static u32 pxp_v4l2_pix_fmt_to_out_format(u32 v4l2_pix_fmt) case V4L2_PIX_FMT_RGB555: return BV_PXP_OUT_CTRL_FORMAT__RGB555; case V4L2_PIX_FMT_RGB444: return BV_PXP_OUT_CTRL_FORMAT__RGB444; case V4L2_PIX_FMT_RGB565: return BV_PXP_OUT_CTRL_FORMAT__RGB565; - case V4L2_PIX_FMT_YUV32: return BV_PXP_OUT_CTRL_FORMAT__YUV1P444; + case V4L2_PIX_FMT_VUYA32: + case V4L2_PIX_FMT_VUYX32: return BV_PXP_OUT_CTRL_FORMAT__YUV1P444; case V4L2_PIX_FMT_UYVY: return BV_PXP_OUT_CTRL_FORMAT__UYVY1P422; case V4L2_PIX_FMT_VYUY: return BV_PXP_OUT_CTRL_FORMAT__VYUY1P422; case V4L2_PIX_FMT_GREY: return BV_PXP_OUT_CTRL_FORMAT__Y8; @@ -281,7 +286,8 @@ static u32 pxp_v4l2_pix_fmt_to_out_format(u32 v4l2_pix_fmt) static bool pxp_v4l2_pix_fmt_is_yuv(u32 v4l2_pix_fmt) { switch (v4l2_pix_fmt) { - case V4L2_PIX_FMT_YUV32: + case V4L2_PIX_FMT_VUYA32: + case V4L2_PIX_FMT_VUYX32: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_VYUY: -- cgit v1.2.3-59-g8ed1b From cfc7740835d09b98a304a4793c93b6074c04379e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 4 Feb 2019 05:11:32 -0500 Subject: media: vb2: replace bool by bitfield in vb2_buffer The bool type is not recommended for use in structs, so replace these by bitfields. Signed-off-by: Hans Verkuil Reviewed-by: Paul Kocialkowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 12 ++++++------ include/media/videobuf2-core.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 34cc87ca8d59..b4457edc3245 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -934,7 +934,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) /* sync buffers */ for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, finish, vb->planes[plane].mem_priv); - vb->synced = false; + vb->synced = 0; } spin_lock_irqsave(&q->done_lock, flags); @@ -1313,8 +1313,8 @@ static int __buf_prepare(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, prepare, vb->planes[plane].mem_priv); - vb->synced = true; - vb->prepared = true; + vb->synced = 1; + vb->prepared = 1; vb->state = orig_state; return 0; @@ -1802,7 +1802,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, } call_void_vb_qop(vb, buf_finish, vb); - vb->prepared = false; + vb->prepared = 0; if (pindex) *pindex = vb->index; @@ -1926,12 +1926,12 @@ static void __vb2_queue_cancel(struct vb2_queue *q) for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, finish, vb->planes[plane].mem_priv); - vb->synced = false; + vb->synced = 0; } if (vb->prepared) { call_void_vb_qop(vb, buf_finish, vb); - vb->prepared = false; + vb->prepared = 0; } __vb2_dqbuf(vb); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 06142c1469cc..6d5490bb91d3 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -269,8 +269,8 @@ struct vb2_buffer { * vb2_plane: per-plane information; do not change */ enum vb2_buffer_state state; - bool synced; - bool prepared; + unsigned int synced:1; + unsigned int prepared:1; struct vb2_plane planes[VB2_MAX_PLANES]; struct list_head queued_entry; -- cgit v1.2.3-59-g8ed1b From 7e4e71624491d8a8befe62b43138beb0ab696006 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 4 Feb 2019 05:11:33 -0500 Subject: media: vb2: keep track of timestamp status If a stream is stopped, or if a USERPTR/DMABUF buffer is queued backed by a different user address or dmabuf fd, then the timestamp should be skipped by vb2_find_timestamp since the memory it refers to is no longer valid. So keep track of a 'copied_timestamp' state: it is set when the timestamp is copied from an output to a capture buffer, and is cleared when it is no longer valid. Signed-off-by: Hans Verkuil Reviewed-by: Paul Kocialkowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 3 +++ drivers/media/common/videobuf2/videobuf2-v4l2.c | 3 ++- drivers/media/v4l2-core/v4l2-mem2mem.c | 1 + include/media/videobuf2-core.h | 3 +++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index b4457edc3245..275cddd8808c 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -1041,6 +1041,7 @@ static int __prepare_userptr(struct vb2_buffer *vb) if (vb->planes[plane].mem_priv) { if (!reacquired) { reacquired = true; + vb->copied_timestamp = 0; call_void_vb_qop(vb, buf_cleanup, vb); } call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv); @@ -1165,6 +1166,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) if (!reacquired) { reacquired = true; + vb->copied_timestamp = 0; call_void_vb_qop(vb, buf_cleanup, vb); } @@ -1942,6 +1944,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) if (vb->request) media_request_put(vb->request); vb->request = NULL; + vb->copied_timestamp = 0; } } diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 3aeaea3af42a..55277370c313 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -604,7 +604,8 @@ int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp, unsigned int i; for (i = start_idx; i < q->num_buffers; i++) - if (q->bufs[i]->timestamp == timestamp) + if (q->bufs[i]->copied_timestamp && + q->bufs[i]->timestamp == timestamp) return i; return -1; } diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 1494d0d5951a..add228c98a9a 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -992,6 +992,7 @@ void v4l2_m2m_buf_copy_metadata(const struct vb2_v4l2_buffer *out_vb, cap_vb->field = out_vb->field; cap_vb->flags &= ~mask; cap_vb->flags |= out_vb->flags & mask; + cap_vb->vb2_buf.copied_timestamp = 1; } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_copy_metadata); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 6d5490bb91d3..a844abcae71e 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -262,6 +262,8 @@ struct vb2_buffer { * prepared: this buffer has been prepared, i.e. the * buf_prepare op was called. It is cleared again * after the 'buf_finish' op is called. + * copied_timestamp: the timestamp of this capture buffer was copied + * from an output buffer. * queued_entry: entry on the queued buffers list, which holds * all buffers queued from userspace * done_entry: entry on the list that stores all buffers ready @@ -271,6 +273,7 @@ struct vb2_buffer { enum vb2_buffer_state state; unsigned int synced:1; unsigned int prepared:1; + unsigned int copied_timestamp:1; struct vb2_plane planes[VB2_MAX_PLANES]; struct list_head queued_entry; -- cgit v1.2.3-59-g8ed1b From b7990bcfb644c53511cca97dca6e39d1bd70732a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:41 -0500 Subject: media: cec: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in cec_poll(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Reviewed-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c index 391b6fd483e1..156a0d76ab2a 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/cec-api.c @@ -38,6 +38,7 @@ static __poll_t cec_poll(struct file *filp, struct cec_adapter *adap = fh->adap; __poll_t res = 0; + poll_wait(filp, &fh->wait, poll); if (!cec_is_registered(adap)) return EPOLLERR | EPOLLHUP; mutex_lock(&adap->lock); @@ -48,7 +49,6 @@ static __poll_t cec_poll(struct file *filp, res |= EPOLLIN | EPOLLRDNORM; if (fh->total_queued_events) res |= EPOLLPRI; - poll_wait(filp, &fh->wait, poll); mutex_unlock(&adap->lock); return res; } -- cgit v1.2.3-59-g8ed1b From f4dd471b5c368c396e8e03a65fec64bced949639 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:42 -0500 Subject: media: media-request: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in media_request_poll(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-request.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c index c71a34ae6383..eec2e2b2f6ec 100644 --- a/drivers/media/media-request.c +++ b/drivers/media/media-request.c @@ -100,6 +100,7 @@ static __poll_t media_request_poll(struct file *filp, if (!(poll_requested_events(wait) & EPOLLPRI)) return 0; + poll_wait(filp, &req->poll_wait, wait); spin_lock_irqsave(&req->lock, flags); if (req->state == MEDIA_REQUEST_STATE_COMPLETE) { ret = EPOLLPRI; @@ -110,8 +111,6 @@ static __poll_t media_request_poll(struct file *filp, goto unlock; } - poll_wait(filp, &req->poll_wait, wait); - unlock: spin_unlock_irqrestore(&req->lock, flags); return ret; -- cgit v1.2.3-59-g8ed1b From 914c68686441c93f6954267dabef6c936ebb479f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:43 -0500 Subject: media: vb2: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in vb2_core_poll() and vb2_poll(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Reported-by: Yi Qingliang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 4 ++-- drivers/media/common/videobuf2/videobuf2-v4l2.c | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 275cddd8808c..15b6b9c0a2e4 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -2291,6 +2291,8 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, if (q->is_output && !(req_events & (EPOLLOUT | EPOLLWRNORM))) return 0; + poll_wait(file, &q->done_wq, wait); + /* * Start file I/O emulator only if streaming API has not been used yet. */ @@ -2342,8 +2344,6 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, */ if (q->last_buffer_dequeued) return EPOLLIN | EPOLLRDNORM; - - poll_wait(file, &q->done_wq, wait); } /* diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 55277370c313..d09dee20e421 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -868,16 +868,14 @@ EXPORT_SYMBOL_GPL(vb2_queue_release); __poll_t vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) { struct video_device *vfd = video_devdata(file); - __poll_t req_events = poll_requested_events(wait); __poll_t res = 0; if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { struct v4l2_fh *fh = file->private_data; + poll_wait(file, &fh->wait, wait); if (v4l2_event_pending(fh)) res = EPOLLPRI; - else if (req_events & EPOLLPRI) - poll_wait(file, &fh->wait, wait); } return res | vb2_core_poll(q, file, wait); -- cgit v1.2.3-59-g8ed1b From 398d768025c171245918828b330697f9442d0fc1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:44 -0500 Subject: media: v4l2-ctrls.c: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in v4l2_ctrl_poll(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 99308dac2daa..b79d3bbd8350 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -4166,9 +4166,9 @@ __poll_t v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait) { struct v4l2_fh *fh = file->private_data; + poll_wait(file, &fh->wait, wait); if (v4l2_event_pending(fh)) return EPOLLPRI; - poll_wait(file, &fh->wait, wait); return 0; } EXPORT_SYMBOL(v4l2_ctrl_poll); -- cgit v1.2.3-59-g8ed1b From dd8695e4e1e39136968339ceed5680327c174a31 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:45 -0500 Subject: media: v4l2-mem2mem: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in v4l2_m2m_poll(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index add228c98a9a..36ae6123dcef 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -617,20 +617,22 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, __poll_t rc = 0; unsigned long flags; + src_q = v4l2_m2m_get_src_vq(m2m_ctx); + dst_q = v4l2_m2m_get_dst_vq(m2m_ctx); + + poll_wait(file, &src_q->done_wq, wait); + poll_wait(file, &dst_q->done_wq, wait); + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { struct v4l2_fh *fh = file->private_data; + poll_wait(file, &fh->wait, wait); if (v4l2_event_pending(fh)) rc = EPOLLPRI; - else if (req_events & EPOLLPRI) - poll_wait(file, &fh->wait, wait); if (!(req_events & (EPOLLOUT | EPOLLWRNORM | EPOLLIN | EPOLLRDNORM))) return rc; } - src_q = v4l2_m2m_get_src_vq(m2m_ctx); - dst_q = v4l2_m2m_get_dst_vq(m2m_ctx); - /* * There has to be at least one buffer queued on each queued_list, which * means either in driver already or waiting for driver to claim it @@ -642,11 +644,6 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, goto end; } - spin_lock_irqsave(&src_q->done_lock, flags); - if (list_empty(&src_q->done_list)) - poll_wait(file, &src_q->done_wq, wait); - spin_unlock_irqrestore(&src_q->done_lock, flags); - spin_lock_irqsave(&dst_q->done_lock, flags); if (list_empty(&dst_q->done_list)) { /* @@ -657,8 +654,6 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, spin_unlock_irqrestore(&dst_q->done_lock, flags); return rc | EPOLLIN | EPOLLRDNORM; } - - poll_wait(file, &dst_q->done_wq, wait); } spin_unlock_irqrestore(&dst_q->done_lock, flags); -- cgit v1.2.3-59-g8ed1b From 96ebc0ca6f6d0978b1a016e6daa29817a812ce55 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:46 -0500 Subject: media: v4l2-mem2mem: add q->error check to v4l2_m2m_poll() The v4l2_m2m_poll function didn't check whether q->error was set for either of the two queues. Add support for this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 36ae6123dcef..57eccbaa52de 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -638,8 +638,10 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, * means either in driver already or waiting for driver to claim it * and start processing. */ - if ((!src_q->streaming || list_empty(&src_q->queued_list)) - && (!dst_q->streaming || list_empty(&dst_q->queued_list))) { + if ((!src_q->streaming || src_q->error || + list_empty(&src_q->queued_list)) && + (!dst_q->streaming || dst_q->error || + list_empty(&dst_q->queued_list))) { rc |= EPOLLERR; goto end; } -- cgit v1.2.3-59-g8ed1b From bb436cbeb918740638278217c241d81a306c1bc3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:47 -0500 Subject: media: videobuf: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in videobuf_poll_stream(). Fix this, otherwise epoll() would timeout when it shouldn't. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c index d1bcfa91aaf8..ab849052b6dc 100644 --- a/drivers/media/v4l2-core/videobuf-core.c +++ b/drivers/media/v4l2-core/videobuf-core.c @@ -1119,13 +1119,14 @@ done: EXPORT_SYMBOL_GPL(videobuf_read_stream); __poll_t videobuf_poll_stream(struct file *file, - struct videobuf_queue *q, - poll_table *wait) + struct videobuf_queue *q, + poll_table *wait) { __poll_t req_events = poll_requested_events(wait); struct videobuf_buffer *buf = NULL; __poll_t rc = 0; + poll_wait(file, &buf->done, wait); videobuf_queue_lock(q); if (q->streaming) { if (!list_empty(&q->stream)) @@ -1149,7 +1150,6 @@ __poll_t videobuf_poll_stream(struct file *file, rc = EPOLLERR; if (0 == rc) { - poll_wait(file, &buf->done, wait); if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) { switch (q->type) { -- cgit v1.2.3-59-g8ed1b From c6f5c7c237fb7179f3652a46108086c9178eb419 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 06:49:48 -0500 Subject: media: dvb-core: fix epoll() by calling poll_wait first The epoll function expects that whenever the poll file op is called, the poll_wait function is also called. That didn't always happen in dvb_demux_poll(), dvb_dvr_poll() and dvb_ca_en50221_io_poll(). Fix this, otherwise epoll() can timeout when it shouldn't. Signed-off-by: Hans Verkuil Reviewed-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dmxdev.c | 8 ++++---- drivers/media/dvb-core/dvb_ca_en50221.c | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index 1544e8cef564..f14a872d1268 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -1195,13 +1195,13 @@ static __poll_t dvb_demux_poll(struct file *file, poll_table *wait) struct dmxdev_filter *dmxdevfilter = file->private_data; __poll_t mask = 0; + poll_wait(file, &dmxdevfilter->buffer.queue, wait); + if ((!dmxdevfilter) || dmxdevfilter->dev->exit) return EPOLLERR; if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx)) return dvb_vb2_poll(&dmxdevfilter->vb2_ctx, file, wait); - poll_wait(file, &dmxdevfilter->buffer.queue, wait); - if (dmxdevfilter->state != DMXDEV_STATE_GO && dmxdevfilter->state != DMXDEV_STATE_DONE && dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT) @@ -1346,13 +1346,13 @@ static __poll_t dvb_dvr_poll(struct file *file, poll_table *wait) dprintk("%s\n", __func__); + poll_wait(file, &dmxdev->dvr_buffer.queue, wait); + if (dmxdev->exit) return EPOLLERR; if (dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx)) return dvb_vb2_poll(&dmxdev->dvr_vb2_ctx, file, wait); - poll_wait(file, &dmxdev->dvr_buffer.queue, wait); - if (((file->f_flags & O_ACCMODE) == O_RDONLY) || dmxdev->may_do_mmap) { if (dmxdev->dvr_buffer.error) diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 4d371cea0d5d..ebf1e3b03819 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -1797,6 +1797,8 @@ static __poll_t dvb_ca_en50221_io_poll(struct file *file, poll_table *wait) dprintk("%s\n", __func__); + poll_wait(file, &ca->wait_queue, wait); + if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) mask |= EPOLLIN; @@ -1804,9 +1806,6 @@ static __poll_t dvb_ca_en50221_io_poll(struct file *file, poll_table *wait) if (mask) return mask; - /* wait for something to happen */ - poll_wait(file, &ca->wait_queue, wait); - if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) mask |= EPOLLIN; -- cgit v1.2.3-59-g8ed1b From 4f14e3272f1ca1b317447cb77242cd382854fcca Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 8 Feb 2019 10:53:50 +0100 Subject: extended-controls.rst: split up per control class The extended-controls.rst file had become too big. Split it up: each control class reference gets its own rst file, and this file just describes the Extended Control API. Each control class reference is also moved up one level into the table of contents to make it easier to find e.g. the codec control reference. Finally I rearranged the order so that all camera-related control classes are grouped together, ditto for codec/jpeg and fm-rx/tx. The ext-ctrls-codec.rst is still pretty big and it is a candidate to split up further in the future, possibly per codec. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/common.rst | 11 + Documentation/media/uapi/v4l/ext-ctrls-camera.rst | 508 +++ Documentation/media/uapi/v4l/ext-ctrls-codec.rst | 2451 ++++++++++++ Documentation/media/uapi/v4l/ext-ctrls-detect.rst | 71 + Documentation/media/uapi/v4l/ext-ctrls-dv.rst | 166 + Documentation/media/uapi/v4l/ext-ctrls-flash.rst | 192 + Documentation/media/uapi/v4l/ext-ctrls-fm-rx.rst | 95 + Documentation/media/uapi/v4l/ext-ctrls-fm-tx.rst | 188 + .../media/uapi/v4l/ext-ctrls-image-process.rst | 63 + .../media/uapi/v4l/ext-ctrls-image-source.rst | 57 + Documentation/media/uapi/v4l/ext-ctrls-jpeg.rst | 113 + .../media/uapi/v4l/ext-ctrls-rf-tuner.rst | 96 + Documentation/media/uapi/v4l/extended-controls.rst | 3920 +------------------- 13 files changed, 4014 insertions(+), 3917 deletions(-) create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-camera.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-codec.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-detect.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-dv.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-flash.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-fm-rx.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-fm-tx.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-image-process.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-image-source.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-jpeg.rst create mode 100644 Documentation/media/uapi/v4l/ext-ctrls-rf-tuner.rst diff --git a/Documentation/media/uapi/v4l/common.rst b/Documentation/media/uapi/v4l/common.rst index 889f2f2632a1..5e87ae24e4b4 100644 --- a/Documentation/media/uapi/v4l/common.rst +++ b/Documentation/media/uapi/v4l/common.rst @@ -46,6 +46,17 @@ applicable to all devices. dv-timings control extended-controls + ext-ctrls-camera + ext-ctrls-flash + ext-ctrls-image-source + ext-ctrls-image-process + ext-ctrls-codec + ext-ctrls-jpeg + ext-ctrls-dv + ext-ctrls-rf-tuner + ext-ctrls-fm-tx + ext-ctrls-fm-rx + ext-ctrls-detect format planar-apis selection-api diff --git a/Documentation/media/uapi/v4l/ext-ctrls-camera.rst b/Documentation/media/uapi/v4l/ext-ctrls-camera.rst new file mode 100644 index 000000000000..d3a553cd86c9 --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-camera.rst @@ -0,0 +1,508 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _camera-controls: + +************************ +Camera Control Reference +************************ + +The Camera class includes controls for mechanical (or equivalent +digital) features of a device such as controllable lenses or sensors. + + +.. _camera-control-id: + +Camera Control IDs +================== + +``V4L2_CID_CAMERA_CLASS (class)`` + The Camera class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +.. _v4l2-exposure-auto-type: + +``V4L2_CID_EXPOSURE_AUTO`` + (enum) + +enum v4l2_exposure_auto_type - + Enables automatic adjustments of the exposure time and/or iris + aperture. The effect of manual changes of the exposure time or iris + aperture while these features are enabled is undefined, drivers + should ignore such requests. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_EXPOSURE_AUTO`` + - Automatic exposure time, automatic iris aperture. + * - ``V4L2_EXPOSURE_MANUAL`` + - Manual exposure time, manual iris. + * - ``V4L2_EXPOSURE_SHUTTER_PRIORITY`` + - Manual exposure time, auto iris. + * - ``V4L2_EXPOSURE_APERTURE_PRIORITY`` + - Auto exposure time, manual iris. + + + +``V4L2_CID_EXPOSURE_ABSOLUTE (integer)`` + Determines the exposure time of the camera sensor. The exposure time + is limited by the frame interval. Drivers should interpret the + values as 100 µs units, where the value 1 stands for 1/10000th of a + second, 10000 for 1 second and 100000 for 10 seconds. + +``V4L2_CID_EXPOSURE_AUTO_PRIORITY (boolean)`` + When ``V4L2_CID_EXPOSURE_AUTO`` is set to ``AUTO`` or + ``APERTURE_PRIORITY``, this control determines if the device may + dynamically vary the frame rate. By default this feature is disabled + (0) and the frame rate must remain constant. + +``V4L2_CID_AUTO_EXPOSURE_BIAS (integer menu)`` + Determines the automatic exposure compensation, it is effective only + when ``V4L2_CID_EXPOSURE_AUTO`` control is set to ``AUTO``, + ``SHUTTER_PRIORITY`` or ``APERTURE_PRIORITY``. It is expressed in + terms of EV, drivers should interpret the values as 0.001 EV units, + where the value 1000 stands for +1 EV. + + Increasing the exposure compensation value is equivalent to + decreasing the exposure value (EV) and will increase the amount of + light at the image sensor. The camera performs the exposure + compensation by adjusting absolute exposure time and/or aperture. + +.. _v4l2-exposure-metering: + +``V4L2_CID_EXPOSURE_METERING`` + (enum) + +enum v4l2_exposure_metering - + Determines how the camera measures the amount of light available for + the frame exposure. Possible values are: + +.. tabularcolumns:: |p{8.5cm}|p{9.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_EXPOSURE_METERING_AVERAGE`` + - Use the light information coming from the entire frame and average + giving no weighting to any particular portion of the metered area. + * - ``V4L2_EXPOSURE_METERING_CENTER_WEIGHTED`` + - Average the light information coming from the entire frame giving + priority to the center of the metered area. + * - ``V4L2_EXPOSURE_METERING_SPOT`` + - Measure only very small area at the center of the frame. + * - ``V4L2_EXPOSURE_METERING_MATRIX`` + - A multi-zone metering. The light intensity is measured in several + points of the frame and the results are combined. The algorithm of + the zones selection and their significance in calculating the + final value is device dependent. + + + +``V4L2_CID_PAN_RELATIVE (integer)`` + This control turns the camera horizontally by the specified amount. + The unit is undefined. A positive value moves the camera to the + right (clockwise when viewed from above), a negative value to the + left. A value of zero does not cause motion. This is a write-only + control. + +``V4L2_CID_TILT_RELATIVE (integer)`` + This control turns the camera vertically by the specified amount. + The unit is undefined. A positive value moves the camera up, a + negative value down. A value of zero does not cause motion. This is + a write-only control. + +``V4L2_CID_PAN_RESET (button)`` + When this control is set, the camera moves horizontally to the + default position. + +``V4L2_CID_TILT_RESET (button)`` + When this control is set, the camera moves vertically to the default + position. + +``V4L2_CID_PAN_ABSOLUTE (integer)`` + This control turns the camera horizontally to the specified + position. Positive values move the camera to the right (clockwise + when viewed from above), negative values to the left. Drivers should + interpret the values as arc seconds, with valid values between -180 + * 3600 and +180 * 3600 inclusive. + +``V4L2_CID_TILT_ABSOLUTE (integer)`` + This control turns the camera vertically to the specified position. + Positive values move the camera up, negative values down. Drivers + should interpret the values as arc seconds, with valid values + between -180 * 3600 and +180 * 3600 inclusive. + +``V4L2_CID_FOCUS_ABSOLUTE (integer)`` + This control sets the focal point of the camera to the specified + position. The unit is undefined. Positive values set the focus + closer to the camera, negative values towards infinity. + +``V4L2_CID_FOCUS_RELATIVE (integer)`` + This control moves the focal point of the camera by the specified + amount. The unit is undefined. Positive values move the focus closer + to the camera, negative values towards infinity. This is a + write-only control. + +``V4L2_CID_FOCUS_AUTO (boolean)`` + Enables continuous automatic focus adjustments. The effect of manual + focus adjustments while this feature is enabled is undefined, + drivers should ignore such requests. + +``V4L2_CID_AUTO_FOCUS_START (button)`` + Starts single auto focus process. The effect of setting this control + when ``V4L2_CID_FOCUS_AUTO`` is set to ``TRUE`` (1) is undefined, + drivers should ignore such requests. + +``V4L2_CID_AUTO_FOCUS_STOP (button)`` + Aborts automatic focusing started with ``V4L2_CID_AUTO_FOCUS_START`` + control. It is effective only when the continuous autofocus is + disabled, that is when ``V4L2_CID_FOCUS_AUTO`` control is set to + ``FALSE`` (0). + +.. _v4l2-auto-focus-status: + +``V4L2_CID_AUTO_FOCUS_STATUS (bitmask)`` + The automatic focus status. This is a read-only control. + + Setting ``V4L2_LOCK_FOCUS`` lock bit of the ``V4L2_CID_3A_LOCK`` + control may stop updates of the ``V4L2_CID_AUTO_FOCUS_STATUS`` + control value. + +.. tabularcolumns:: |p{6.5cm}|p{11.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_AUTO_FOCUS_STATUS_IDLE`` + - Automatic focus is not active. + * - ``V4L2_AUTO_FOCUS_STATUS_BUSY`` + - Automatic focusing is in progress. + * - ``V4L2_AUTO_FOCUS_STATUS_REACHED`` + - Focus has been reached. + * - ``V4L2_AUTO_FOCUS_STATUS_FAILED`` + - Automatic focus has failed, the driver will not transition from + this state until another action is performed by an application. + + + +.. _v4l2-auto-focus-range: + +``V4L2_CID_AUTO_FOCUS_RANGE`` + (enum) + +enum v4l2_auto_focus_range - + Determines auto focus distance range for which lens may be adjusted. + +.. tabularcolumns:: |p{6.5cm}|p{11.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_AUTO_FOCUS_RANGE_AUTO`` + - The camera automatically selects the focus range. + * - ``V4L2_AUTO_FOCUS_RANGE_NORMAL`` + - Normal distance range, limited for best automatic focus + performance. + * - ``V4L2_AUTO_FOCUS_RANGE_MACRO`` + - Macro (close-up) auto focus. The camera will use its minimum + possible distance for auto focus. + * - ``V4L2_AUTO_FOCUS_RANGE_INFINITY`` + - The lens is set to focus on an object at infinite distance. + + + +``V4L2_CID_ZOOM_ABSOLUTE (integer)`` + Specify the objective lens focal length as an absolute value. The + zoom unit is driver-specific and its value should be a positive + integer. + +``V4L2_CID_ZOOM_RELATIVE (integer)`` + Specify the objective lens focal length relatively to the current + value. Positive values move the zoom lens group towards the + telephoto direction, negative values towards the wide-angle + direction. The zoom unit is driver-specific. This is a write-only + control. + +``V4L2_CID_ZOOM_CONTINUOUS (integer)`` + Move the objective lens group at the specified speed until it + reaches physical device limits or until an explicit request to stop + the movement. A positive value moves the zoom lens group towards the + telephoto direction. A value of zero stops the zoom lens group + movement. A negative value moves the zoom lens group towards the + wide-angle direction. The zoom speed unit is driver-specific. + +``V4L2_CID_IRIS_ABSOLUTE (integer)`` + This control sets the camera's aperture to the specified value. The + unit is undefined. Larger values open the iris wider, smaller values + close it. + +``V4L2_CID_IRIS_RELATIVE (integer)`` + This control modifies the camera's aperture by the specified amount. + The unit is undefined. Positive values open the iris one step + further, negative values close it one step further. This is a + write-only control. + +``V4L2_CID_PRIVACY (boolean)`` + Prevent video from being acquired by the camera. When this control + is set to ``TRUE`` (1), no image can be captured by the camera. + Common means to enforce privacy are mechanical obturation of the + sensor and firmware image processing, but the device is not + restricted to these methods. Devices that implement the privacy + control must support read access and may support write access. + +``V4L2_CID_BAND_STOP_FILTER (integer)`` + Switch the band-stop filter of a camera sensor on or off, or specify + its strength. Such band-stop filters can be used, for example, to + filter out the fluorescent light component. + +.. _v4l2-auto-n-preset-white-balance: + +``V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE`` + (enum) + +enum v4l2_auto_n_preset_white_balance - + Sets white balance to automatic, manual or a preset. The presets + determine color temperature of the light as a hint to the camera for + white balance adjustments resulting in most accurate color + representation. The following white balance presets are listed in + order of increasing color temperature. + +.. tabularcolumns:: |p{7.0 cm}|p{10.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_WHITE_BALANCE_MANUAL`` + - Manual white balance. + * - ``V4L2_WHITE_BALANCE_AUTO`` + - Automatic white balance adjustments. + * - ``V4L2_WHITE_BALANCE_INCANDESCENT`` + - White balance setting for incandescent (tungsten) lighting. It + generally cools down the colors and corresponds approximately to + 2500...3500 K color temperature range. + * - ``V4L2_WHITE_BALANCE_FLUORESCENT`` + - White balance preset for fluorescent lighting. It corresponds + approximately to 4000...5000 K color temperature. + * - ``V4L2_WHITE_BALANCE_FLUORESCENT_H`` + - With this setting the camera will compensate for fluorescent H + lighting. + * - ``V4L2_WHITE_BALANCE_HORIZON`` + - White balance setting for horizon daylight. It corresponds + approximately to 5000 K color temperature. + * - ``V4L2_WHITE_BALANCE_DAYLIGHT`` + - White balance preset for daylight (with clear sky). It corresponds + approximately to 5000...6500 K color temperature. + * - ``V4L2_WHITE_BALANCE_FLASH`` + - With this setting the camera will compensate for the flash light. + It slightly warms up the colors and corresponds roughly to + 5000...5500 K color temperature. + * - ``V4L2_WHITE_BALANCE_CLOUDY`` + - White balance preset for moderately overcast sky. This option + corresponds approximately to 6500...8000 K color temperature + range. + * - ``V4L2_WHITE_BALANCE_SHADE`` + - White balance preset for shade or heavily overcast sky. It + corresponds approximately to 9000...10000 K color temperature. + + + +.. _v4l2-wide-dynamic-range: + +``V4L2_CID_WIDE_DYNAMIC_RANGE (boolean)`` + Enables or disables the camera's wide dynamic range feature. This + feature allows to obtain clear images in situations where intensity + of the illumination varies significantly throughout the scene, i.e. + there are simultaneously very dark and very bright areas. It is most + commonly realized in cameras by combining two subsequent frames with + different exposure times. [#f1]_ + +.. _v4l2-image-stabilization: + +``V4L2_CID_IMAGE_STABILIZATION (boolean)`` + Enables or disables image stabilization. + +``V4L2_CID_ISO_SENSITIVITY (integer menu)`` + Determines ISO equivalent of an image sensor indicating the sensor's + sensitivity to light. The numbers are expressed in arithmetic scale, + as per :ref:`iso12232` standard, where doubling the sensor + sensitivity is represented by doubling the numerical ISO value. + Applications should interpret the values as standard ISO values + multiplied by 1000, e.g. control value 800 stands for ISO 0.8. + Drivers will usually support only a subset of standard ISO values. + The effect of setting this control while the + ``V4L2_CID_ISO_SENSITIVITY_AUTO`` control is set to a value other + than ``V4L2_CID_ISO_SENSITIVITY_MANUAL`` is undefined, drivers + should ignore such requests. + +.. _v4l2-iso-sensitivity-auto-type: + +``V4L2_CID_ISO_SENSITIVITY_AUTO`` + (enum) + +enum v4l2_iso_sensitivity_type - + Enables or disables automatic ISO sensitivity adjustments. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_CID_ISO_SENSITIVITY_MANUAL`` + - Manual ISO sensitivity. + * - ``V4L2_CID_ISO_SENSITIVITY_AUTO`` + - Automatic ISO sensitivity adjustments. + + + +.. _v4l2-scene-mode: + +``V4L2_CID_SCENE_MODE`` + (enum) + +enum v4l2_scene_mode - + This control allows to select scene programs as the camera automatic + modes optimized for common shooting scenes. Within these modes the + camera determines best exposure, aperture, focusing, light metering, + white balance and equivalent sensitivity. The controls of those + parameters are influenced by the scene mode control. An exact + behavior in each mode is subject to the camera specification. + + When the scene mode feature is not used, this control should be set + to ``V4L2_SCENE_MODE_NONE`` to make sure the other possibly related + controls are accessible. The following scene programs are defined: + +.. tabularcolumns:: |p{6.0cm}|p{11.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_SCENE_MODE_NONE`` + - The scene mode feature is disabled. + * - ``V4L2_SCENE_MODE_BACKLIGHT`` + - Backlight. Compensates for dark shadows when light is coming from + behind a subject, also by automatically turning on the flash. + * - ``V4L2_SCENE_MODE_BEACH_SNOW`` + - Beach and snow. This mode compensates for all-white or bright + scenes, which tend to look gray and low contrast, when camera's + automatic exposure is based on an average scene brightness. To + compensate, this mode automatically slightly overexposes the + frames. The white balance may also be adjusted to compensate for + the fact that reflected snow looks bluish rather than white. + * - ``V4L2_SCENE_MODE_CANDLELIGHT`` + - Candle light. The camera generally raises the ISO sensitivity and + lowers the shutter speed. This mode compensates for relatively + close subject in the scene. The flash is disabled in order to + preserve the ambiance of the light. + * - ``V4L2_SCENE_MODE_DAWN_DUSK`` + - Dawn and dusk. Preserves the colors seen in low natural light + before dusk and after down. The camera may turn off the flash, and + automatically focus at infinity. It will usually boost saturation + and lower the shutter speed. + * - ``V4L2_SCENE_MODE_FALL_COLORS`` + - Fall colors. Increases saturation and adjusts white balance for + color enhancement. Pictures of autumn leaves get saturated reds + and yellows. + * - ``V4L2_SCENE_MODE_FIREWORKS`` + - Fireworks. Long exposure times are used to capture the expanding + burst of light from a firework. The camera may invoke image + stabilization. + * - ``V4L2_SCENE_MODE_LANDSCAPE`` + - Landscape. The camera may choose a small aperture to provide deep + depth of field and long exposure duration to help capture detail + in dim light conditions. The focus is fixed at infinity. Suitable + for distant and wide scenery. + * - ``V4L2_SCENE_MODE_NIGHT`` + - Night, also known as Night Landscape. Designed for low light + conditions, it preserves detail in the dark areas without blowing + out bright objects. The camera generally sets itself to a + medium-to-high ISO sensitivity, with a relatively long exposure + time, and turns flash off. As such, there will be increased image + noise and the possibility of blurred image. + * - ``V4L2_SCENE_MODE_PARTY_INDOOR`` + - Party and indoor. Designed to capture indoor scenes that are lit + by indoor background lighting as well as the flash. The camera + usually increases ISO sensitivity, and adjusts exposure for the + low light conditions. + * - ``V4L2_SCENE_MODE_PORTRAIT`` + - Portrait. The camera adjusts the aperture so that the depth of + field is reduced, which helps to isolate the subject against a + smooth background. Most cameras recognize the presence of faces in + the scene and focus on them. The color hue is adjusted to enhance + skin tones. The intensity of the flash is often reduced. + * - ``V4L2_SCENE_MODE_SPORTS`` + - Sports. Significantly increases ISO and uses a fast shutter speed + to freeze motion of rapidly-moving subjects. Increased image noise + may be seen in this mode. + * - ``V4L2_SCENE_MODE_SUNSET`` + - Sunset. Preserves deep hues seen in sunsets and sunrises. It bumps + up the saturation. + * - ``V4L2_SCENE_MODE_TEXT`` + - Text. It applies extra contrast and sharpness, it is typically a + black-and-white mode optimized for readability. Automatic focus + may be switched to close-up mode and this setting may also involve + some lens-distortion correction. + + + +``V4L2_CID_3A_LOCK (bitmask)`` + This control locks or unlocks the automatic focus, exposure and + white balance. The automatic adjustments can be paused independently + by setting the corresponding lock bit to 1. The camera then retains + the settings until the lock bit is cleared. The following lock bits + are defined: + + When a given algorithm is not enabled, drivers should ignore + requests to lock it and should return no error. An example might be + an application setting bit ``V4L2_LOCK_WHITE_BALANCE`` when the + ``V4L2_CID_AUTO_WHITE_BALANCE`` control is set to ``FALSE``. The + value of this control may be changed by exposure, white balance or + focus controls. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_LOCK_EXPOSURE`` + - Automatic exposure adjustments lock. + * - ``V4L2_LOCK_WHITE_BALANCE`` + - Automatic white balance adjustments lock. + * - ``V4L2_LOCK_FOCUS`` + - Automatic focus lock. + + + +``V4L2_CID_PAN_SPEED (integer)`` + This control turns the camera horizontally at the specific speed. + The unit is undefined. A positive value moves the camera to the + right (clockwise when viewed from above), a negative value to the + left. A value of zero stops the motion if one is in progress and has + no effect otherwise. + +``V4L2_CID_TILT_SPEED (integer)`` + This control turns the camera vertically at the specified speed. The + unit is undefined. A positive value moves the camera up, a negative + value down. A value of zero stops the motion if one is in progress + and has no effect otherwise. + +.. [#f1] + This control may be changed to a menu control in the future, if more + options are required. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst new file mode 100644 index 000000000000..54b3797b67dd --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst @@ -0,0 +1,2451 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _mpeg-controls: + +*********************** +Codec Control Reference +*********************** + +Below all controls within the Codec control class are described. First +the generic controls, then controls specific for certain hardware. + +.. note:: + + These controls are applicable to all codecs and not just MPEG. The + defines are prefixed with V4L2_CID_MPEG/V4L2_MPEG as the controls + were originally made for MPEG codecs and later extended to cover all + encoding formats. + + +Generic Codec Controls +====================== + + +.. _mpeg-control-id: + +Codec Control IDs +----------------- + +``V4L2_CID_MPEG_CLASS (class)`` + The Codec class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. This description can be + used as the caption of a Tab page in a GUI, for example. + +.. _v4l2-mpeg-stream-type: + +``V4L2_CID_MPEG_STREAM_TYPE`` + (enum) + +enum v4l2_mpeg_stream_type - + The MPEG-1, -2 or -4 output stream type. One cannot assume anything + here. Each hardware MPEG encoder tends to support different subsets + of the available MPEG stream types. This control is specific to + multiplexed MPEG streams. The currently defined stream types are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_PS`` + - MPEG-2 program stream + * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_TS`` + - MPEG-2 transport stream + * - ``V4L2_MPEG_STREAM_TYPE_MPEG1_SS`` + - MPEG-1 system stream + * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_DVD`` + - MPEG-2 DVD-compatible stream + * - ``V4L2_MPEG_STREAM_TYPE_MPEG1_VCD`` + - MPEG-1 VCD-compatible stream + * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD`` + - MPEG-2 SVCD-compatible stream + + + +``V4L2_CID_MPEG_STREAM_PID_PMT (integer)`` + Program Map Table Packet ID for the MPEG transport stream (default + 16) + +``V4L2_CID_MPEG_STREAM_PID_AUDIO (integer)`` + Audio Packet ID for the MPEG transport stream (default 256) + +``V4L2_CID_MPEG_STREAM_PID_VIDEO (integer)`` + Video Packet ID for the MPEG transport stream (default 260) + +``V4L2_CID_MPEG_STREAM_PID_PCR (integer)`` + Packet ID for the MPEG transport stream carrying PCR fields (default + 259) + +``V4L2_CID_MPEG_STREAM_PES_ID_AUDIO (integer)`` + Audio ID for MPEG PES + +``V4L2_CID_MPEG_STREAM_PES_ID_VIDEO (integer)`` + Video ID for MPEG PES + +.. _v4l2-mpeg-stream-vbi-fmt: + +``V4L2_CID_MPEG_STREAM_VBI_FMT`` + (enum) + +enum v4l2_mpeg_stream_vbi_fmt - + Some cards can embed VBI data (e. g. Closed Caption, Teletext) into + the MPEG stream. This control selects whether VBI data should be + embedded, and if so, what embedding method should be used. The list + of possible VBI formats depends on the driver. The currently defined + VBI format types are: + + + +.. tabularcolumns:: |p{6 cm}|p{11.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_STREAM_VBI_FMT_NONE`` + - No VBI in the MPEG stream + * - ``V4L2_MPEG_STREAM_VBI_FMT_IVTV`` + - VBI in private packets, IVTV format (documented in the kernel + sources in the file + ``Documentation/media/v4l-drivers/cx2341x.rst``) + + + +.. _v4l2-mpeg-audio-sampling-freq: + +``V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ`` + (enum) + +enum v4l2_mpeg_audio_sampling_freq - + MPEG Audio sampling frequency. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100`` + - 44.1 kHz + * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000`` + - 48 kHz + * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000`` + - 32 kHz + + + +.. _v4l2-mpeg-audio-encoding: + +``V4L2_CID_MPEG_AUDIO_ENCODING`` + (enum) + +enum v4l2_mpeg_audio_encoding - + MPEG Audio encoding. This control is specific to multiplexed MPEG + streams. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_1`` + - MPEG-1/2 Layer I encoding + * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_2`` + - MPEG-1/2 Layer II encoding + * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_3`` + - MPEG-1/2 Layer III encoding + * - ``V4L2_MPEG_AUDIO_ENCODING_AAC`` + - MPEG-2/4 AAC (Advanced Audio Coding) + * - ``V4L2_MPEG_AUDIO_ENCODING_AC3`` + - AC-3 aka ATSC A/52 encoding + + + +.. _v4l2-mpeg-audio-l1-bitrate: + +``V4L2_CID_MPEG_AUDIO_L1_BITRATE`` + (enum) + +enum v4l2_mpeg_audio_l1_bitrate - + MPEG-1/2 Layer I bitrate. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_32K`` + - 32 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_64K`` + - 64 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_96K`` + - 96 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_128K`` + - 128 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_160K`` + - 160 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_192K`` + - 192 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_224K`` + - 224 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_256K`` + - 256 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_288K`` + - 288 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_320K`` + - 320 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_352K`` + - 352 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_384K`` + - 384 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_416K`` + - 416 kbit/s + * - ``V4L2_MPEG_AUDIO_L1_BITRATE_448K`` + - 448 kbit/s + + + +.. _v4l2-mpeg-audio-l2-bitrate: + +``V4L2_CID_MPEG_AUDIO_L2_BITRATE`` + (enum) + +enum v4l2_mpeg_audio_l2_bitrate - + MPEG-1/2 Layer II bitrate. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_32K`` + - 32 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_48K`` + - 48 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_56K`` + - 56 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_64K`` + - 64 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_80K`` + - 80 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_96K`` + - 96 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_112K`` + - 112 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_128K`` + - 128 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_160K`` + - 160 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_192K`` + - 192 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_224K`` + - 224 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_256K`` + - 256 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_320K`` + - 320 kbit/s + * - ``V4L2_MPEG_AUDIO_L2_BITRATE_384K`` + - 384 kbit/s + + + +.. _v4l2-mpeg-audio-l3-bitrate: + +``V4L2_CID_MPEG_AUDIO_L3_BITRATE`` + (enum) + +enum v4l2_mpeg_audio_l3_bitrate - + MPEG-1/2 Layer III bitrate. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_32K`` + - 32 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_40K`` + - 40 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_48K`` + - 48 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_56K`` + - 56 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_64K`` + - 64 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_80K`` + - 80 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_96K`` + - 96 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_112K`` + - 112 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_128K`` + - 128 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_160K`` + - 160 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_192K`` + - 192 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_224K`` + - 224 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_256K`` + - 256 kbit/s + * - ``V4L2_MPEG_AUDIO_L3_BITRATE_320K`` + - 320 kbit/s + + + +``V4L2_CID_MPEG_AUDIO_AAC_BITRATE (integer)`` + AAC bitrate in bits per second. + +.. _v4l2-mpeg-audio-ac3-bitrate: + +``V4L2_CID_MPEG_AUDIO_AC3_BITRATE`` + (enum) + +enum v4l2_mpeg_audio_ac3_bitrate - + AC-3 bitrate. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_32K`` + - 32 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_40K`` + - 40 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_48K`` + - 48 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_56K`` + - 56 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_64K`` + - 64 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_80K`` + - 80 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_96K`` + - 96 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_112K`` + - 112 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_128K`` + - 128 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_160K`` + - 160 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_192K`` + - 192 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_224K`` + - 224 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_256K`` + - 256 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_320K`` + - 320 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_384K`` + - 384 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_448K`` + - 448 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_512K`` + - 512 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_576K`` + - 576 kbit/s + * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_640K`` + - 640 kbit/s + + + +.. _v4l2-mpeg-audio-mode: + +``V4L2_CID_MPEG_AUDIO_MODE`` + (enum) + +enum v4l2_mpeg_audio_mode - + MPEG Audio mode. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_MODE_STEREO`` + - Stereo + * - ``V4L2_MPEG_AUDIO_MODE_JOINT_STEREO`` + - Joint Stereo + * - ``V4L2_MPEG_AUDIO_MODE_DUAL`` + - Bilingual + * - ``V4L2_MPEG_AUDIO_MODE_MONO`` + - Mono + + + +.. _v4l2-mpeg-audio-mode-extension: + +``V4L2_CID_MPEG_AUDIO_MODE_EXTENSION`` + (enum) + +enum v4l2_mpeg_audio_mode_extension - + Joint Stereo audio mode extension. In Layer I and II they indicate + which subbands are in intensity stereo. All other subbands are coded + in stereo. Layer III is not (yet) supported. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4`` + - Subbands 4-31 in intensity stereo + * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8`` + - Subbands 8-31 in intensity stereo + * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12`` + - Subbands 12-31 in intensity stereo + * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16`` + - Subbands 16-31 in intensity stereo + + + +.. _v4l2-mpeg-audio-emphasis: + +``V4L2_CID_MPEG_AUDIO_EMPHASIS`` + (enum) + +enum v4l2_mpeg_audio_emphasis - + Audio Emphasis. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_EMPHASIS_NONE`` + - None + * - ``V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS`` + - 50/15 microsecond emphasis + * - ``V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17`` + - CCITT J.17 + + + +.. _v4l2-mpeg-audio-crc: + +``V4L2_CID_MPEG_AUDIO_CRC`` + (enum) + +enum v4l2_mpeg_audio_crc - + CRC method. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_CRC_NONE`` + - None + * - ``V4L2_MPEG_AUDIO_CRC_CRC16`` + - 16 bit parity check + + + +``V4L2_CID_MPEG_AUDIO_MUTE (boolean)`` + Mutes the audio when capturing. This is not done by muting audio + hardware, which can still produce a slight hiss, but in the encoder + itself, guaranteeing a fixed and reproducible audio bitstream. 0 = + unmuted, 1 = muted. + +.. _v4l2-mpeg-audio-dec-playback: + +``V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK`` + (enum) + +enum v4l2_mpeg_audio_dec_playback - + Determines how monolingual audio should be played back. Possible + values are: + + + +.. tabularcolumns:: |p{9.0cm}|p{8.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO`` + - Automatically determines the best playback mode. + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO`` + - Stereo playback. + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT`` + - Left channel playback. + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_RIGHT`` + - Right channel playback. + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_MONO`` + - Mono playback. + * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO`` + - Stereo playback with swapped left and right channels. + + + +.. _v4l2-mpeg-audio-dec-multilingual-playback: + +``V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK`` + (enum) + +enum v4l2_mpeg_audio_dec_playback - + Determines how multilingual audio should be played back. + +.. _v4l2-mpeg-video-encoding: + +``V4L2_CID_MPEG_VIDEO_ENCODING`` + (enum) + +enum v4l2_mpeg_video_encoding - + MPEG Video encoding method. This control is specific to multiplexed + MPEG streams. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_1`` + - MPEG-1 Video encoding + * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_2`` + - MPEG-2 Video encoding + * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC`` + - MPEG-4 AVC (H.264) Video encoding + + + +.. _v4l2-mpeg-video-aspect: + +``V4L2_CID_MPEG_VIDEO_ASPECT`` + (enum) + +enum v4l2_mpeg_video_aspect - + Video aspect. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_ASPECT_1x1`` + * - ``V4L2_MPEG_VIDEO_ASPECT_4x3`` + * - ``V4L2_MPEG_VIDEO_ASPECT_16x9`` + * - ``V4L2_MPEG_VIDEO_ASPECT_221x100`` + + + +``V4L2_CID_MPEG_VIDEO_B_FRAMES (integer)`` + Number of B-Frames (default 2) + +``V4L2_CID_MPEG_VIDEO_GOP_SIZE (integer)`` + GOP size (default 12) + +``V4L2_CID_MPEG_VIDEO_GOP_CLOSURE (boolean)`` + GOP closure (default 1) + +``V4L2_CID_MPEG_VIDEO_PULLDOWN (boolean)`` + Enable 3:2 pulldown (default 0) + +.. _v4l2-mpeg-video-bitrate-mode: + +``V4L2_CID_MPEG_VIDEO_BITRATE_MODE`` + (enum) + +enum v4l2_mpeg_video_bitrate_mode - + Video bitrate mode. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_BITRATE_MODE_VBR`` + - Variable bitrate + * - ``V4L2_MPEG_VIDEO_BITRATE_MODE_CBR`` + - Constant bitrate + + + +``V4L2_CID_MPEG_VIDEO_BITRATE (integer)`` + Video bitrate in bits per second. + +``V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (integer)`` + Peak video bitrate in bits per second. Must be larger or equal to + the average video bitrate. It is ignored if the video bitrate mode + is set to constant bitrate. + +``V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (integer)`` + For every captured frame, skip this many subsequent frames (default + 0). + +``V4L2_CID_MPEG_VIDEO_MUTE (boolean)`` + "Mutes" the video to a fixed color when capturing. This is useful + for testing, to produce a fixed video bitstream. 0 = unmuted, 1 = + muted. + +``V4L2_CID_MPEG_VIDEO_MUTE_YUV (integer)`` + Sets the "mute" color of the video. The supplied 32-bit integer is + interpreted as follows (bit 0 = least significant bit): + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - Bit 0:7 + - V chrominance information + * - Bit 8:15 + - U chrominance information + * - Bit 16:23 + - Y luminance information + * - Bit 24:31 + - Must be zero. + + + +.. _v4l2-mpeg-video-dec-pts: + +``V4L2_CID_MPEG_VIDEO_DEC_PTS (integer64)`` + This read-only control returns the 33-bit video Presentation Time + Stamp as defined in ITU T-REC-H.222.0 and ISO/IEC 13818-1 of the + currently displayed frame. This is the same PTS as is used in + :ref:`VIDIOC_DECODER_CMD`. + +.. _v4l2-mpeg-video-dec-frame: + +``V4L2_CID_MPEG_VIDEO_DEC_FRAME (integer64)`` + This read-only control returns the frame counter of the frame that + is currently displayed (decoded). This value is reset to 0 whenever + the decoder is started. + +``V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE (boolean)`` + If enabled the decoder expects to receive a single slice per buffer, + otherwise the decoder expects a single frame in per buffer. + Applicable to the decoder, all codecs. + +``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE (boolean)`` + Enable writing sample aspect ratio in the Video Usability + Information. Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-vui-sar-idc: + +``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC`` + (enum) + +enum v4l2_mpeg_video_h264_vui_sar_idc - + VUI sample aspect ratio indicator for H.264 encoding. The value is + defined in the table E-1 in the standard. Applicable to the H264 + encoder. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED`` + - Unspecified + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1`` + - 1x1 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11`` + - 12x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11`` + - 10x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11`` + - 16x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33`` + - 40x33 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11`` + - 24x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11`` + - 20x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11`` + - 32x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33`` + - 80x33 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11`` + - 18x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11`` + - 15x11 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33`` + - 64x33 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99`` + - 160x99 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3`` + - 4x3 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2`` + - 3x2 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1`` + - 2x1 + * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED`` + - Extended SAR + + + +``V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH (integer)`` + Extended sample aspect ratio width for H.264 VUI encoding. + Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT (integer)`` + Extended sample aspect ratio height for H.264 VUI encoding. + Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-level: + +``V4L2_CID_MPEG_VIDEO_H264_LEVEL`` + (enum) + +enum v4l2_mpeg_video_h264_level - + The level information for the H264 video elementary stream. + Applicable to the H264 encoder. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_0`` + - Level 1.0 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1B`` + - Level 1B + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_1`` + - Level 1.1 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_2`` + - Level 1.2 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_3`` + - Level 1.3 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_0`` + - Level 2.0 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_1`` + - Level 2.1 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_2`` + - Level 2.2 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_0`` + - Level 3.0 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_1`` + - Level 3.1 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_2`` + - Level 3.2 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_0`` + - Level 4.0 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_1`` + - Level 4.1 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_2`` + - Level 4.2 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_5_0`` + - Level 5.0 + * - ``V4L2_MPEG_VIDEO_H264_LEVEL_5_1`` + - Level 5.1 + + + +.. _v4l2-mpeg-video-mpeg4-level: + +``V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL`` + (enum) + +enum v4l2_mpeg_video_mpeg4_level - + The level information for the MPEG4 elementary stream. Applicable to + the MPEG4 encoder. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_0`` + - Level 0 + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B`` + - Level 0b + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_1`` + - Level 1 + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_2`` + - Level 2 + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_3`` + - Level 3 + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B`` + - Level 3b + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_4`` + - Level 4 + * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_5`` + - Level 5 + + + +.. _v4l2-mpeg-video-h264-profile: + +``V4L2_CID_MPEG_VIDEO_H264_PROFILE`` + (enum) + +enum v4l2_mpeg_video_h264_profile - + The profile information for H264. Applicable to the H264 encoder. + Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE`` + - Baseline profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE`` + - Constrained Baseline profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_MAIN`` + - Main profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED`` + - Extended profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH`` + - High profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10`` + - High 10 profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422`` + - High 422 profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE`` + - High 444 Predictive profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA`` + - High 10 Intra profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA`` + - High 422 Intra profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA`` + - High 444 Intra profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA`` + - CAVLC 444 Intra profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE`` + - Scalable Baseline profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH`` + - Scalable High profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA`` + - Scalable High Intra profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH`` + - Stereo High profile + * - ``V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH`` + - Multiview High profile + + + +.. _v4l2-mpeg-video-mpeg4-profile: + +``V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE`` + (enum) + +enum v4l2_mpeg_video_mpeg4_profile - + The profile information for MPEG4. Applicable to the MPEG4 encoder. + Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE`` + - Simple profile + * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE`` + - Advanced Simple profile + * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE`` + - Core profile + * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE`` + - Simple Scalable profile + * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY`` + - + + + +``V4L2_CID_MPEG_VIDEO_MAX_REF_PIC (integer)`` + The maximum number of reference pictures used for encoding. + Applicable to the encoder. + +.. _v4l2-mpeg-video-multi-slice-mode: + +``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` + (enum) + +enum v4l2_mpeg_video_multi_slice_mode - + Determines how the encoder should handle division of frame into + slices. Applicable to the encoder. Possible values are: + + + +.. tabularcolumns:: |p{8.7cm}|p{8.8cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE`` + - Single slice per frame. + * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB`` + - Multiple slices with set maximum number of macroblocks per slice. + * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES`` + - Multiple slice with set maximum size in bytes per slice. + + + +``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB (integer)`` + The maximum number of macroblocks in a slice. Used when + ``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` is set to + ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB``. Applicable to the + encoder. + +``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES (integer)`` + The maximum size of a slice in bytes. Used when + ``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` is set to + ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES``. Applicable to the + encoder. + +.. _v4l2-mpeg-video-h264-loop-filter-mode: + +``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE`` + (enum) + +enum v4l2_mpeg_video_h264_loop_filter_mode - + Loop filter mode for H264 encoder. Possible values are: + + + +.. tabularcolumns:: |p{14.0cm}|p{3.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED`` + - Loop filter is enabled. + * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED`` + - Loop filter is disabled. + * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY`` + - Loop filter is disabled at the slice boundary. + + + +``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA (integer)`` + Loop filter alpha coefficient, defined in the H264 standard. + This value corresponds to the slice_alpha_c0_offset_div2 slice header + field, and should be in the range of -6 to +6, inclusive. The actual alpha + offset FilterOffsetA is twice this value. + Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA (integer)`` + Loop filter beta coefficient, defined in the H264 standard. + This corresponds to the slice_beta_offset_div2 slice header field, and + should be in the range of -6 to +6, inclusive. The actual beta offset + FilterOffsetB is twice this value. + Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-entropy-mode: + +``V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE`` + (enum) + +enum v4l2_mpeg_video_h264_entropy_mode - + Entropy coding mode for H264 - CABAC/CAVALC. Applicable to the H264 + encoder. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC`` + - Use CAVLC entropy coding. + * - ``V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC`` + - Use CABAC entropy coding. + + + +``V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM (boolean)`` + Enable 8X8 transform for H264. Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (boolean)`` + Enable constrained intra prediction for H264. Applicable to the H264 + encoder. + +``V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET (integer)`` + Specify the offset that should be added to the luma quantization + parameter to determine the chroma quantization parameter. Applicable + to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (integer)`` + Cyclic intra macroblock refresh. This is the number of continuous + macroblocks refreshed every frame. Each frame a successive set of + macroblocks is refreshed until the cycle completes and starts from + the top of the frame. Applicable to H264, H263 and MPEG4 encoder. + +``V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE (boolean)`` + Frame level rate control enable. If this control is disabled then + the quantization parameter for each frame type is constant and set + with appropriate controls (e.g. + ``V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP``). If frame rate control is + enabled then quantization parameter is adjusted to meet the chosen + bitrate. Minimum and maximum value for the quantization parameter + can be set with appropriate controls (e.g. + ``V4L2_CID_MPEG_VIDEO_H263_MIN_QP``). Applicable to encoders. + +``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE (boolean)`` + Macroblock level rate control enable. Applicable to the MPEG4 and + H264 encoders. + +``V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (boolean)`` + Quarter pixel motion estimation for MPEG4. Applicable to the MPEG4 + encoder. + +``V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (integer)`` + Quantization parameter for an I frame for H263. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_H263_MIN_QP (integer)`` + Minimum quantization parameter for H263. Valid range: from 1 to 31. + +``V4L2_CID_MPEG_VIDEO_H263_MAX_QP (integer)`` + Maximum quantization parameter for H263. Valid range: from 1 to 31. + +``V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (integer)`` + Quantization parameter for an P frame for H263. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP (integer)`` + Quantization parameter for an B frame for H263. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP (integer)`` + Quantization parameter for an I frame for H264. Valid range: from 0 + to 51. + +``V4L2_CID_MPEG_VIDEO_H264_MIN_QP (integer)`` + Minimum quantization parameter for H264. Valid range: from 0 to 51. + +``V4L2_CID_MPEG_VIDEO_H264_MAX_QP (integer)`` + Maximum quantization parameter for H264. Valid range: from 0 to 51. + +``V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP (integer)`` + Quantization parameter for an P frame for H264. Valid range: from 0 + to 51. + +``V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP (integer)`` + Quantization parameter for an B frame for H264. Valid range: from 0 + to 51. + +``V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (integer)`` + Quantization parameter for an I frame for MPEG4. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP (integer)`` + Minimum quantization parameter for MPEG4. Valid range: from 1 to 31. + +``V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP (integer)`` + Maximum quantization parameter for MPEG4. Valid range: from 1 to 31. + +``V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (integer)`` + Quantization parameter for an P frame for MPEG4. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (integer)`` + Quantization parameter for an B frame for MPEG4. Valid range: from 1 + to 31. + +``V4L2_CID_MPEG_VIDEO_VBV_SIZE (integer)`` + The Video Buffer Verifier size in kilobytes, it is used as a + limitation of frame skip. The VBV is defined in the standard as a + mean to verify that the produced stream will be successfully + decoded. The standard describes it as "Part of a hypothetical + decoder that is conceptually connected to the output of the encoder. + Its purpose is to provide a constraint on the variability of the + data rate that an encoder or editing process may produce.". + Applicable to the MPEG1, MPEG2, MPEG4 encoders. + +.. _v4l2-mpeg-video-vbv-delay: + +``V4L2_CID_MPEG_VIDEO_VBV_DELAY (integer)`` + Sets the initial delay in milliseconds for VBV buffer control. + +.. _v4l2-mpeg-video-hor-search-range: + +``V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE (integer)`` + Horizontal search range defines maximum horizontal search area in + pixels to search and match for the present Macroblock (MB) in the + reference picture. This V4L2 control macro is used to set horizontal + search range for motion estimation module in video encoder. + +.. _v4l2-mpeg-video-vert-search-range: + +``V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (integer)`` + Vertical search range defines maximum vertical search area in pixels + to search and match for the present Macroblock (MB) in the reference + picture. This V4L2 control macro is used to set vertical search + range for motion estimation module in video encoder. + +.. _v4l2-mpeg-video-force-key-frame: + +``V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (button)`` + Force a key frame for the next queued buffer. Applicable to + encoders. This is a general, codec-agnostic keyframe control. + +``V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE (integer)`` + The Coded Picture Buffer size in kilobytes, it is used as a + limitation of frame skip. The CPB is defined in the H264 standard as + a mean to verify that the produced stream will be successfully + decoded. Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_I_PERIOD (integer)`` + Period between I-frames in the open GOP for H264. In case of an open + GOP this is the period between two I-frames. The period between IDR + (Instantaneous Decoding Refresh) frames is taken from the GOP_SIZE + control. An IDR frame, which stands for Instantaneous Decoding + Refresh is an I-frame after which no prior frames are referenced. + This means that a stream can be restarted from an IDR frame without + the need to store or decode any previous frames. Applicable to the + H264 encoder. + +.. _v4l2-mpeg-video-header-mode: + +``V4L2_CID_MPEG_VIDEO_HEADER_MODE`` + (enum) + +enum v4l2_mpeg_video_header_mode - + Determines whether the header is returned as the first buffer or is + it returned together with the first frame. Applicable to encoders. + Possible values are: + + + +.. tabularcolumns:: |p{10.3cm}|p{7.2cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE`` + - The stream header is returned separately in the first buffer. + * - ``V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME`` + - The stream header is returned together with the first encoded + frame. + + + +``V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER (boolean)`` + Repeat the video sequence headers. Repeating these headers makes + random access to the video stream easier. Applicable to the MPEG1, 2 + and 4 encoder. + +``V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER (boolean)`` + Enabled the deblocking post processing filter for MPEG4 decoder. + Applicable to the MPEG4 decoder. + +``V4L2_CID_MPEG_VIDEO_MPEG4_VOP_TIME_RES (integer)`` + vop_time_increment_resolution value for MPEG4. Applicable to the + MPEG4 encoder. + +``V4L2_CID_MPEG_VIDEO_MPEG4_VOP_TIME_INC (integer)`` + vop_time_increment value for MPEG4. Applicable to the MPEG4 + encoder. + +``V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING (boolean)`` + Enable generation of frame packing supplemental enhancement + information in the encoded bitstream. The frame packing SEI message + contains the arrangement of L and R planes for 3D viewing. + Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_SEI_FP_CURRENT_FRAME_0 (boolean)`` + Sets current frame as frame0 in frame packing SEI. Applicable to the + H264 encoder. + +.. _v4l2-mpeg-video-h264-sei-fp-arrangement-type: + +``V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE`` + (enum) + +enum v4l2_mpeg_video_h264_sei_fp_arrangement_type - + Frame packing arrangement type for H264 SEI. Applicable to the H264 + encoder. Possible values are: + +.. tabularcolumns:: |p{12cm}|p{5.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_CHEKERBOARD`` + - Pixels are alternatively from L and R. + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_COLUMN`` + - L and R are interlaced by column. + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_ROW`` + - L and R are interlaced by row. + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_SIDE_BY_SIDE`` + - L is on the left, R on the right. + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TOP_BOTTOM`` + - L is on top, R on bottom. + * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TEMPORAL`` + - One view per frame. + + + +``V4L2_CID_MPEG_VIDEO_H264_FMO (boolean)`` + Enables flexible macroblock ordering in the encoded bitstream. It is + a technique used for restructuring the ordering of macroblocks in + pictures. Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-fmo-map-type: + +``V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE`` + (enum) + +enum v4l2_mpeg_video_h264_fmo_map_type - + When using FMO, the map type divides the image in different scan + patterns of macroblocks. Applicable to the H264 encoder. Possible + values are: + +.. tabularcolumns:: |p{12.5cm}|p{5.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES`` + - Slices are interleaved one after other with macroblocks in run + length order. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES`` + - Scatters the macroblocks based on a mathematical function known to + both encoder and decoder. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_FOREGROUND_WITH_LEFT_OVER`` + - Macroblocks arranged in rectangular areas or regions of interest. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_BOX_OUT`` + - Slice groups grow in a cyclic way from centre to outwards. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_RASTER_SCAN`` + - Slice groups grow in raster scan pattern from left to right. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN`` + - Slice groups grow in wipe scan pattern from top to bottom. + * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_EXPLICIT`` + - User defined map type. + + + +``V4L2_CID_MPEG_VIDEO_H264_FMO_SLICE_GROUP (integer)`` + Number of slice groups in FMO. Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-fmo-change-direction: + +``V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_DIRECTION`` + (enum) + +enum v4l2_mpeg_video_h264_fmo_change_dir - + Specifies a direction of the slice group change for raster and wipe + maps. Applicable to the H264 encoder. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_RIGHT`` + - Raster scan or wipe right. + * - ``V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_LEFT`` + - Reverse raster scan or wipe left. + + + +``V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_RATE (integer)`` + Specifies the size of the first slice group for raster and wipe map. + Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_FMO_RUN_LENGTH (integer)`` + Specifies the number of consecutive macroblocks for the interleaved + map. Applicable to the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_ASO (boolean)`` + Enables arbitrary slice ordering in encoded bitstream. Applicable to + the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_ASO_SLICE_ORDER (integer)`` + Specifies the slice order in ASO. Applicable to the H264 encoder. + The supplied 32-bit integer is interpreted as follows (bit 0 = least + significant bit): + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - Bit 0:15 + - Slice ID + * - Bit 16:32 + - Slice position or order + + + +``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING (boolean)`` + Enables H264 hierarchical coding. Applicable to the H264 encoder. + +.. _v4l2-mpeg-video-h264-hierarchical-coding-type: + +``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE`` + (enum) + +enum v4l2_mpeg_video_h264_hierarchical_coding_type - + Specifies the hierarchical coding type. Applicable to the H264 + encoder. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B`` + - Hierarchical B coding. + * - ``V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P`` + - Hierarchical P coding. + + + +``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER (integer)`` + Specifies the number of hierarchical coding layers. Applicable to + the H264 encoder. + +``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP (integer)`` + Specifies a user defined QP for each layer. Applicable to the H264 + encoder. The supplied 32-bit integer is interpreted as follows (bit + 0 = least significant bit): + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - Bit 0:15 + - QP value + * - Bit 16:32 + - Layer number + + + +.. _v4l2-mpeg-mpeg2: + +``V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS (struct)`` + Specifies the slice parameters (as extracted from the bitstream) for the + associated MPEG-2 slice data. This includes the necessary parameters for + configuring a stateless hardware decoding pipeline for MPEG-2. + The bitstream parameters are defined according to :ref:`mpeg2part2`. + + .. note:: + + This compound control is not yet part of the public kernel API and + it is expected to change. + +.. c:type:: v4l2_ctrl_mpeg2_slice_params + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_mpeg2_slice_params + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u32 + - ``bit_size`` + - Size (in bits) of the current slice data. + * - __u32 + - ``data_bit_offset`` + - Offset (in bits) to the video data in the current slice data. + * - struct :c:type:`v4l2_mpeg2_sequence` + - ``sequence`` + - Structure with MPEG-2 sequence metadata, merging relevant fields from + the sequence header and sequence extension parts of the bitstream. + * - struct :c:type:`v4l2_mpeg2_picture` + - ``picture`` + - Structure with MPEG-2 picture metadata, merging relevant fields from + the picture header and picture coding extension parts of the bitstream. + * - __u64 + - ``backward_ref_ts`` + - Timestamp of the V4L2 capture buffer to use as backward reference, used + with B-coded and P-coded frames. The timestamp refers to the + ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the + :c:func:`v4l2_timeval_to_ns()` function to convert the struct + :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. + * - __u64 + - ``forward_ref_ts`` + - Timestamp for the V4L2 capture buffer to use as forward reference, used + with B-coded frames. The timestamp refers to the ``timestamp`` field in + struct :c:type:`v4l2_buffer`. Use the :c:func:`v4l2_timeval_to_ns()` + function to convert the struct :c:type:`timeval` in struct + :c:type:`v4l2_buffer` to a __u64. + * - __u32 + - ``quantiser_scale_code`` + - Code used to determine the quantization scale to use for the IDCT. + +.. c:type:: v4l2_mpeg2_sequence + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_mpeg2_sequence + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u16 + - ``horizontal_size`` + - The width of the displayable part of the frame's luminance component. + * - __u16 + - ``vertical_size`` + - The height of the displayable part of the frame's luminance component. + * - __u32 + - ``vbv_buffer_size`` + - Used to calculate the required size of the video buffering verifier, + defined (in bits) as: 16 * 1024 * vbv_buffer_size. + * - __u16 + - ``profile_and_level_indication`` + - The current profile and level indication as extracted from the + bitstream. + * - __u8 + - ``progressive_sequence`` + - Indication that all the frames for the sequence are progressive instead + of interlaced. + * - __u8 + - ``chroma_format`` + - The chrominance sub-sampling format (1: 4:2:0, 2: 4:2:2, 3: 4:4:4). + +.. c:type:: v4l2_mpeg2_picture + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_mpeg2_picture + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``picture_coding_type`` + - Picture coding type for the frame covered by the current slice + (V4L2_MPEG2_PICTURE_CODING_TYPE_I, V4L2_MPEG2_PICTURE_CODING_TYPE_P or + V4L2_MPEG2_PICTURE_CODING_TYPE_B). + * - __u8 + - ``f_code[2][2]`` + - Motion vector codes. + * - __u8 + - ``intra_dc_precision`` + - Precision of Discrete Cosine transform (0: 8 bits precision, + 1: 9 bits precision, 2: 10 bits precision, 3: 11 bits precision). + * - __u8 + - ``picture_structure`` + - Picture structure (1: interlaced top field, 2: interlaced bottom field, + 3: progressive frame). + * - __u8 + - ``top_field_first`` + - If set to 1 and interlaced stream, top field is output first. + * - __u8 + - ``frame_pred_frame_dct`` + - If set to 1, only frame-DCT and frame prediction are used. + * - __u8 + - ``concealment_motion_vectors`` + - If set to 1, motion vectors are coded for intra macroblocks. + * - __u8 + - ``q_scale_type`` + - This flag affects the inverse quantization process. + * - __u8 + - ``intra_vlc_format`` + - This flag affects the decoding of transform coefficient data. + * - __u8 + - ``alternate_scan`` + - This flag affects the decoding of transform coefficient data. + * - __u8 + - ``repeat_first_field`` + - This flag affects the decoding process of progressive frames. + * - __u16 + - ``progressive_frame`` + - Indicates whether the current frame is progressive. + +``V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION (struct)`` + Specifies quantization matrices (as extracted from the bitstream) for the + associated MPEG-2 slice data. + + .. note:: + + This compound control is not yet part of the public kernel API and + it is expected to change. + +.. c:type:: v4l2_ctrl_mpeg2_quantization + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_mpeg2_quantization + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``load_intra_quantiser_matrix`` + - One bit to indicate whether to load the ``intra_quantiser_matrix`` data. + * - __u8 + - ``load_non_intra_quantiser_matrix`` + - One bit to indicate whether to load the ``non_intra_quantiser_matrix`` + data. + * - __u8 + - ``load_chroma_intra_quantiser_matrix`` + - One bit to indicate whether to load the + ``chroma_intra_quantiser_matrix`` data, only relevant for non-4:2:0 YUV + formats. + * - __u8 + - ``load_chroma_non_intra_quantiser_matrix`` + - One bit to indicate whether to load the + ``chroma_non_intra_quantiser_matrix`` data, only relevant for non-4:2:0 + YUV formats. + * - __u8 + - ``intra_quantiser_matrix[64]`` + - The quantization matrix coefficients for intra-coded frames, in zigzag + scanning order. It is relevant for both luma and chroma components, + although it can be superseded by the chroma-specific matrix for + non-4:2:0 YUV formats. + * - __u8 + - ``non_intra_quantiser_matrix[64]`` + - The quantization matrix coefficients for non-intra-coded frames, in + zigzag scanning order. It is relevant for both luma and chroma + components, although it can be superseded by the chroma-specific matrix + for non-4:2:0 YUV formats. + * - __u8 + - ``chroma_intra_quantiser_matrix[64]`` + - The quantization matrix coefficients for the chominance component of + intra-coded frames, in zigzag scanning order. Only relevant for + non-4:2:0 YUV formats. + * - __u8 + - ``chroma_non_intra_quantiser_matrix[64]`` + - The quantization matrix coefficients for the chrominance component of + non-intra-coded frames, in zigzag scanning order. Only relevant for + non-4:2:0 YUV formats. + +MFC 5.1 MPEG Controls +===================== + +The following MPEG class controls deal with MPEG decoding and encoding +settings that are specific to the Multi Format Codec 5.1 device present +in the S5P family of SoCs by Samsung. + + +.. _mfc51-control-id: + +MFC 5.1 Control IDs +------------------- + +``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE (boolean)`` + If the display delay is enabled then the decoder is forced to return + a CAPTURE buffer (decoded frame) after processing a certain number + of OUTPUT buffers. The delay can be set through + ``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY``. This + feature can be used for example for generating thumbnails of videos. + Applicable to the H264 decoder. + +``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY (integer)`` + Display delay value for H264 decoder. The decoder is forced to + return a decoded frame after the set 'display delay' number of + frames. If this number is low it may result in frames returned out + of dispaly order, in addition the hardware may still be using the + returned buffer as a reference picture for subsequent frames. + +``V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P (integer)`` + The number of reference pictures used for encoding a P picture. + Applicable to the H264 encoder. + +``V4L2_CID_MPEG_MFC51_VIDEO_PADDING (boolean)`` + Padding enable in the encoder - use a color instead of repeating + border pixels. Applicable to encoders. + +``V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV (integer)`` + Padding color in the encoder. Applicable to encoders. The supplied + 32-bit integer is interpreted as follows (bit 0 = least significant + bit): + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - Bit 0:7 + - V chrominance information + * - Bit 8:15 + - U chrominance information + * - Bit 16:23 + - Y luminance information + * - Bit 24:31 + - Must be zero. + + + +``V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF (integer)`` + Reaction coefficient for MFC rate control. Applicable to encoders. + + .. note:: + + #. Valid only when the frame level RC is enabled. + + #. For tight CBR, this field must be small (ex. 2 ~ 10). For + VBR, this field must be large (ex. 100 ~ 1000). + + #. It is not recommended to use the greater number than + FRAME_RATE * (10^9 / BIT_RATE). + +``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK (boolean)`` + Adaptive rate control for dark region. Valid only when H.264 and + macroblock level RC is enabled + (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 + encoder. + +``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH (boolean)`` + Adaptive rate control for smooth region. Valid only when H.264 and + macroblock level RC is enabled + (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 + encoder. + +``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC (boolean)`` + Adaptive rate control for static region. Valid only when H.264 and + macroblock level RC is enabled + (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 + encoder. + +``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY (boolean)`` + Adaptive rate control for activity region. Valid only when H.264 and + macroblock level RC is enabled + (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 + encoder. + +.. _v4l2-mpeg-mfc51-video-frame-skip-mode: + +``V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE`` + (enum) + +enum v4l2_mpeg_mfc51_video_frame_skip_mode - + Indicates in what conditions the encoder should skip frames. If + encoding a frame would cause the encoded stream to be larger then a + chosen data limit then the frame will be skipped. Possible values + are: + + +.. tabularcolumns:: |p{9.0cm}|p{8.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_DISABLED`` + - Frame skip mode is disabled. + * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_LEVEL_LIMIT`` + - Frame skip mode enabled and buffer limit is set by the chosen + level and is defined by the standard. + * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_BUF_LIMIT`` + - Frame skip mode enabled and buffer limit is set by the VBV + (MPEG1/2/4) or CPB (H264) buffer size control. + + + +``V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT (integer)`` + Enable rate-control with fixed target bit. If this setting is + enabled, then the rate control logic of the encoder will calculate + the average bitrate for a GOP and keep it below or equal the set + bitrate target. Otherwise the rate control logic calculates the + overall average bitrate for the stream and keeps it below or equal + to the set bitrate. In the first case the average bitrate for the + whole stream will be smaller then the set bitrate. This is caused + because the average is calculated for smaller number of frames, on + the other hand enabling this setting will ensure that the stream + will meet tight bandwidth constraints. Applicable to encoders. + +.. _v4l2-mpeg-mfc51-video-force-frame-type: + +``V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE`` + (enum) + +enum v4l2_mpeg_mfc51_video_force_frame_type - + Force a frame type for the next queued buffer. Applicable to + encoders. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_DISABLED`` + - Forcing a specific frame type disabled. + * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_I_FRAME`` + - Force an I-frame. + * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_NOT_CODED`` + - Force a non-coded frame. + + + + +CX2341x MPEG Controls +===================== + +The following MPEG class controls deal with MPEG encoding settings that +are specific to the Conexant CX23415 and CX23416 MPEG encoding chips. + + +.. _cx2341x-control-id: + +CX2341x Control IDs +------------------- + +.. _v4l2-mpeg-cx2341x-video-spatial-filter-mode: + +``V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE`` + (enum) + +enum v4l2_mpeg_cx2341x_video_spatial_filter_mode - + Sets the Spatial Filter mode (default ``MANUAL``). Possible values + are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL`` + - Choose the filter manually + * - ``V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO`` + - Choose the filter automatically + + + +``V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER (integer (0-15))`` + The setting for the Spatial Filter. 0 = off, 15 = maximum. (Default + is 0.) + +.. _luma-spatial-filter-type: + +``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE`` + (enum) + +enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type - + Select the algorithm to use for the Luma Spatial Filter (default + ``1D_HOR``). Possible values: + + + +.. tabularcolumns:: |p{14.5cm}|p{3.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF`` + - No filter + * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR`` + - One-dimensional horizontal + * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT`` + - One-dimensional vertical + * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE`` + - Two-dimensional separable + * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE`` + - Two-dimensional symmetrical non-separable + + + +.. _chroma-spatial-filter-type: + +``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE`` + (enum) + +enum v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type - + Select the algorithm for the Chroma Spatial Filter (default + ``1D_HOR``). Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF`` + - No filter + * - ``V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR`` + - One-dimensional horizontal + + + +.. _v4l2-mpeg-cx2341x-video-temporal-filter-mode: + +``V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE`` + (enum) + +enum v4l2_mpeg_cx2341x_video_temporal_filter_mode - + Sets the Temporal Filter mode (default ``MANUAL``). Possible values + are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL`` + - Choose the filter manually + * - ``V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO`` + - Choose the filter automatically + + + +``V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER (integer (0-31))`` + The setting for the Temporal Filter. 0 = off, 31 = maximum. (Default + is 8 for full-scale capturing and 0 for scaled capturing.) + +.. _v4l2-mpeg-cx2341x-video-median-filter-type: + +``V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE`` + (enum) + +enum v4l2_mpeg_cx2341x_video_median_filter_type - + Median Filter Type (default ``OFF``). Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF`` + - No filter + * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR`` + - Horizontal filter + * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT`` + - Vertical filter + * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT`` + - Horizontal and vertical filter + * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG`` + - Diagonal filter + + + +``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM (integer (0-255))`` + Threshold above which the luminance median filter is enabled + (default 0) + +``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP (integer (0-255))`` + Threshold below which the luminance median filter is enabled + (default 255) + +``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (integer (0-255))`` + Threshold above which the chroma median filter is enabled (default + 0) + +``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (integer (0-255))`` + Threshold below which the chroma median filter is enabled (default + 255) + +``V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS (boolean)`` + The CX2341X MPEG encoder can insert one empty MPEG-2 PES packet into + the stream between every four video frames. The packet size is 2048 + bytes, including the packet_start_code_prefix and stream_id + fields. The stream_id is 0xBF (private stream 2). The payload + consists of 0x00 bytes, to be filled in by the application. 0 = do + not insert, 1 = insert packets. + + +VPX Control Reference +===================== + +The VPX controls include controls for encoding parameters of VPx video +codec. + + +.. _vpx-control-id: + +VPX Control IDs +--------------- + +.. _v4l2-vpx-num-partitions: + +``V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS`` + (enum) + +enum v4l2_vp8_num_partitions - + The number of token partitions to use in VP8 encoder. Possible + values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION`` + - 1 coefficient partition + * - ``V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS`` + - 2 coefficient partitions + * - ``V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS`` + - 4 coefficient partitions + * - ``V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS`` + - 8 coefficient partitions + + + +``V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 (boolean)`` + Setting this prevents intra 4x4 mode in the intra mode decision. + +.. _v4l2-vpx-num-ref-frames: + +``V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES`` + (enum) + +enum v4l2_vp8_num_ref_frames - + The number of reference pictures for encoding P frames. Possible + values are: + +.. tabularcolumns:: |p{7.9cm}|p{9.6cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME`` + - Last encoded frame will be searched + * - ``V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME`` + - Two frames will be searched among the last encoded frame, the + golden frame and the alternate reference (altref) frame. The + encoder implementation will decide which two are chosen. + * - ``V4L2_CID_MPEG_VIDEO_VPX_3_REF_FRAME`` + - The last encoded frame, the golden frame and the altref frame will + be searched. + + + +``V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL (integer)`` + Indicates the loop filter level. The adjustment of the loop filter + level is done via a delta value against a baseline loop filter + value. + +``V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS (integer)`` + This parameter affects the loop filter. Anything above zero weakens + the deblocking effect on the loop filter. + +``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD (integer)`` + Sets the refresh period for the golden frame. The period is defined + in number of frames. For a value of 'n', every nth frame starting + from the first key frame will be taken as a golden frame. For eg. + for encoding sequence of 0, 1, 2, 3, 4, 5, 6, 7 where the golden + frame refresh period is set as 4, the frames 0, 4, 8 etc will be + taken as the golden frames as frame 0 is always a key frame. + +.. _v4l2-vpx-golden-frame-sel: + +``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL`` + (enum) + +enum v4l2_vp8_golden_frame_sel - + Selects the golden frame for encoding. Possible values are: + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV`` + - Use the (n-2)th frame as a golden frame, current frame index being + 'n'. + * - ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD`` + - Use the previous specific frame indicated by + ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD`` as a + golden frame. + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_VPX_MIN_QP (integer)`` + Minimum quantization parameter for VP8. + +``V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (integer)`` + Maximum quantization parameter for VP8. + +``V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (integer)`` + Quantization parameter for an I frame for VP8. + +``V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (integer)`` + Quantization parameter for a P frame for VP8. + +.. _v4l2-mpeg-video-vp8-profile: + +``V4L2_CID_MPEG_VIDEO_VP8_PROFILE`` + (enum) + +enum v4l2_mpeg_video_vp8_profile - + This control allows selecting the profile for VP8 encoder. + This is also used to enumerate supported profiles by VP8 encoder or decoder. + Possible values are: + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_0`` + - Profile 0 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_1`` + - Profile 1 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_2`` + - Profile 2 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_3`` + - Profile 3 + +.. _v4l2-mpeg-video-vp9-profile: + +``V4L2_CID_MPEG_VIDEO_VP9_PROFILE`` + (enum) + +enum v4l2_mpeg_video_vp9_profile - + This control allows selecting the profile for VP9 encoder. + This is also used to enumerate supported profiles by VP9 encoder or decoder. + Possible values are: + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_0`` + - Profile 0 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_1`` + - Profile 1 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_2`` + - Profile 2 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_3`` + - Profile 3 + + +High Efficiency Video Coding (HEVC/H.265) Control Reference +=========================================================== + +The HEVC/H.265 controls include controls for encoding parameters of HEVC/H.265 +video codec. + + +.. _hevc-control-id: + +HEVC/H.265 Control IDs +---------------------- + +``V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP (integer)`` + Minimum quantization parameter for HEVC. + Valid range: from 0 to 51. + +``V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP (integer)`` + Maximum quantization parameter for HEVC. + Valid range: from 0 to 51. + +``V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP (integer)`` + Quantization parameter for an I frame for HEVC. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP (integer)`` + Quantization parameter for a P frame for HEVC. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP (integer)`` + Quantization parameter for a B frame for HEVC. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP (boolean)`` + HIERARCHICAL_QP allows the host to specify the quantization parameter + values for each temporal layer through HIERARCHICAL_QP_LAYER. This is + valid only if HIERARCHICAL_CODING_LAYER is greater than 1. Setting the + control value to 1 enables setting of the QP values for the layers. + +.. _v4l2-hevc-hier-coding-type: + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE`` + (enum) + +enum v4l2_mpeg_video_hevc_hier_coding_type - + Selects the hierarchical coding type for encoding. Possible values are: + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B`` + - Use the B frame for hierarchical coding. + * - ``V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P`` + - Use the P frame for hierarchical coding. + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER (integer)`` + Selects the hierarchical coding layer. In normal encoding + (non-hierarchial coding), it should be zero. Possible values are [0, 6]. + 0 indicates HIERARCHICAL CODING LAYER 0, 1 indicates HIERARCHICAL CODING + LAYER 1 and so on. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 0. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 1. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 2. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 3. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 4. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 5. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP (integer)`` + Indicates quantization parameter for hierarchical coding layer 6. + Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. + +.. _v4l2-hevc-profile: + +``V4L2_CID_MPEG_VIDEO_HEVC_PROFILE`` + (enum) + +enum v4l2_mpeg_video_hevc_profile - + Select the desired profile for HEVC encoder. + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN`` + - Main profile. + * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE`` + - Main still picture profile. + * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10`` + - Main 10 profile. + +.. raw:: latex + + \normalsize + + +.. _v4l2-hevc-level: + +``V4L2_CID_MPEG_VIDEO_HEVC_LEVEL`` + (enum) + +enum v4l2_mpeg_video_hevc_level - + Selects the desired level for HEVC encoder. + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_1`` + - Level 1.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_2`` + - Level 2.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1`` + - Level 2.1 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_3`` + - Level 3.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1`` + - Level 3.1 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_4`` + - Level 4.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1`` + - Level 4.1 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5`` + - Level 5.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1`` + - Level 5.1 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2`` + - Level 5.2 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6`` + - Level 6.0 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1`` + - Level 6.1 + * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2`` + - Level 6.2 + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION (integer)`` + Indicates the number of evenly spaced subintervals, called ticks, within + one second. This is a 16 bit unsigned integer and has a maximum value up to + 0xffff and a minimum value of 1. + +.. _v4l2-hevc-tier: + +``V4L2_CID_MPEG_VIDEO_HEVC_TIER`` + (enum) + +enum v4l2_mpeg_video_hevc_tier - + TIER_FLAG specifies tiers information of the HEVC encoded picture. Tier + were made to deal with applications that differ in terms of maximum bit + rate. Setting the flag to 0 selects HEVC tier as Main tier and setting + this flag to 1 indicates High tier. High tier is for applications requiring + high bit rates. + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_TIER_MAIN`` + - Main tier. + * - ``V4L2_MPEG_VIDEO_HEVC_TIER_HIGH`` + - High tier. + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH (integer)`` + Selects HEVC maximum coding unit depth. + +.. _v4l2-hevc-loop-filter-mode: + +``V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE`` + (enum) + +enum v4l2_mpeg_video_hevc_loop_filter_mode - + Loop filter mode for HEVC encoder. Possible values are: + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{10.7cm}|p{6.3cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED`` + - Loop filter is disabled. + * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED`` + - Loop filter is enabled. + * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY`` + - Loop filter is disabled at the slice boundary. + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2 (integer)`` + Selects HEVC loop filter beta offset. The valid range is [-6, +6]. + +``V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2 (integer)`` + Selects HEVC loop filter tc offset. The valid range is [-6, +6]. + +.. _v4l2-hevc-refresh-type: + +``V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE`` + (enum) + +enum v4l2_mpeg_video_hevc_hier_refresh_type - + Selects refresh type for HEVC encoder. + Host has to specify the period into + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD. + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{8.0cm}|p{9.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE`` + - Use the B frame for hierarchical coding. + * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA`` + - Use CRA (Clean Random Access Unit) picture encoding. + * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR`` + - Use IDR (Instantaneous Decoding Refresh) picture encoding. + +.. raw:: latex + + \normalsize + + +``V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD (integer)`` + Selects the refresh period for HEVC encoder. + This specifies the number of I pictures between two CRA/IDR pictures. + This is valid only if REFRESH_TYPE is not 0. + +``V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU (boolean)`` + Indicates HEVC lossless encoding. Setting it to 0 disables lossless + encoding. Setting it to 1 enables lossless encoding. + +``V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED (boolean)`` + Indicates constant intra prediction for HEVC encoder. Specifies the + constrained intra prediction in which intra largest coding unit (LCU) + prediction is performed by using residual data and decoded samples of + neighboring intra LCU only. Setting the value to 1 enables constant intra + prediction and setting the value to 0 disables constant intra prediction. + +``V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT (boolean)`` + Indicates wavefront parallel processing for HEVC encoder. Setting it to 0 + disables the feature and setting it to 1 enables the wavefront parallel + processing. + +``V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB (boolean)`` + Setting the value to 1 enables combination of P and B frame for HEVC + encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID (boolean)`` + Indicates temporal identifier for HEVC encoder which is enabled by + setting the value to 1. + +``V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING (boolean)`` + Indicates bi-linear interpolation is conditionally used in the intra + prediction filtering process in the CVS when set to 1. Indicates bi-linear + interpolation is not used in the CVS when set to 0. + +``V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1 (integer)`` + Indicates maximum number of merge candidate motion vectors. + Values are from 0 to 4. + +``V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION (boolean)`` + Indicates temporal motion vector prediction for HEVC encoder. Setting it to + 1 enables the prediction. Setting it to 0 disables the prediction. + +``V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE (boolean)`` + Specifies if HEVC generates a stream with a size of the length field + instead of start code pattern. The size of the length field is configurable + through the V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD control. Setting + the value to 0 disables encoding without startcode pattern. Setting the + value to 1 will enables encoding without startcode pattern. + +.. _v4l2-hevc-size-of-length-field: + +``V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD`` +(enum) + +enum v4l2_mpeg_video_hevc_size_of_length_field - + Indicates the size of length field. + This is valid when encoding WITHOUT_STARTCODE_ENABLE is enabled. + +.. raw:: latex + + \footnotesize + +.. tabularcolumns:: |p{6.0cm}|p{11.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_0`` + - Generate start code pattern (Normal). + * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_1`` + - Generate size of length field instead of start code pattern and length is 1. + * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_2`` + - Generate size of length field instead of start code pattern and length is 2. + * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_4`` + - Generate size of length field instead of start code pattern and length is 4. + +.. raw:: latex + + \normalsize + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR (integer)`` + Indicates bit rate for hierarchical coding layer 0 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR (integer)`` + Indicates bit rate for hierarchical coding layer 1 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR (integer)`` + Indicates bit rate for hierarchical coding layer 2 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR (integer)`` + Indicates bit rate for hierarchical coding layer 3 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR (integer)`` + Indicates bit rate for hierarchical coding layer 4 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR (integer)`` + Indicates bit rate for hierarchical coding layer 5 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR (integer)`` + Indicates bit rate for hierarchical coding layer 6 for HEVC encoder. + +``V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES (integer)`` + Selects number of P reference pictures required for HEVC encoder. + P-Frame can use 1 or 2 frames for reference. + +``V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR (integer)`` + Indicates whether to generate SPS and PPS at every IDR. Setting it to 0 + disables generating SPS and PPS at every IDR. Setting it to one enables + generating SPS and PPS at every IDR. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-detect.rst b/Documentation/media/uapi/v4l/ext-ctrls-detect.rst new file mode 100644 index 000000000000..8a45ce642829 --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-detect.rst @@ -0,0 +1,71 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _detect-controls: + +************************ +Detect Control Reference +************************ + +The Detect class includes controls for common features of various motion +or object detection capable devices. + + +.. _detect-control-id: + +Detect Control IDs +================== + +``V4L2_CID_DETECT_CLASS (class)`` + The Detect class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +``V4L2_CID_DETECT_MD_MODE (menu)`` + Sets the motion detection mode. + +.. tabularcolumns:: |p{7.5cm}|p{10.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_DETECT_MD_MODE_DISABLED`` + - Disable motion detection. + * - ``V4L2_DETECT_MD_MODE_GLOBAL`` + - Use a single motion detection threshold. + * - ``V4L2_DETECT_MD_MODE_THRESHOLD_GRID`` + - The image is divided into a grid, each cell with its own motion + detection threshold. These thresholds are set through the + ``V4L2_CID_DETECT_MD_THRESHOLD_GRID`` matrix control. + * - ``V4L2_DETECT_MD_MODE_REGION_GRID`` + - The image is divided into a grid, each cell with its own region + value that specifies which per-region motion detection thresholds + should be used. Each region has its own thresholds. How these + per-region thresholds are set up is driver-specific. The region + values for the grid are set through the + ``V4L2_CID_DETECT_MD_REGION_GRID`` matrix control. + + + +``V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD (integer)`` + Sets the global motion detection threshold to be used with the + ``V4L2_DETECT_MD_MODE_GLOBAL`` motion detection mode. + +``V4L2_CID_DETECT_MD_THRESHOLD_GRID (__u16 matrix)`` + Sets the motion detection thresholds for each cell in the grid. To + be used with the ``V4L2_DETECT_MD_MODE_THRESHOLD_GRID`` motion + detection mode. Matrix element (0, 0) represents the cell at the + top-left of the grid. + +``V4L2_CID_DETECT_MD_REGION_GRID (__u8 matrix)`` + Sets the motion detection region value for each cell in the grid. To + be used with the ``V4L2_DETECT_MD_MODE_REGION_GRID`` motion + detection mode. Matrix element (0, 0) represents the cell at the + top-left of the grid. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-dv.rst b/Documentation/media/uapi/v4l/ext-ctrls-dv.rst new file mode 100644 index 000000000000..57edf211875c --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-dv.rst @@ -0,0 +1,166 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _dv-controls: + +******************************* +Digital Video Control Reference +******************************* + +The Digital Video control class is intended to control receivers and +transmitters for `VGA `__, +`DVI `__ +(Digital Visual Interface), HDMI (:ref:`hdmi`) and DisplayPort +(:ref:`dp`). These controls are generally expected to be private to +the receiver or transmitter subdevice that implements them, so they are +only exposed on the ``/dev/v4l-subdev*`` device node. + +.. note:: + + Note that these devices can have multiple input or output pads which are + hooked up to e.g. HDMI connectors. Even though the subdevice will + receive or transmit video from/to only one of those pads, the other pads + can still be active when it comes to EDID (Extended Display + Identification Data, :ref:`vesaedid`) and HDCP (High-bandwidth Digital + Content Protection System, :ref:`hdcp`) processing, allowing the + device to do the fairly slow EDID/HDCP handling in advance. This allows + for quick switching between connectors. + +These pads appear in several of the controls in this section as +bitmasks, one bit for each pad. Bit 0 corresponds to pad 0, bit 1 to pad +1, etc. The maximum value of the control is the set of valid pads. + + +.. _dv-control-id: + +Digital Video Control IDs +========================= + +``V4L2_CID_DV_CLASS (class)`` + The Digital Video class descriptor. + +``V4L2_CID_DV_TX_HOTPLUG (bitmask)`` + Many connectors have a hotplug pin which is high if EDID information + is available from the source. This control shows the state of the + hotplug pin as seen by the transmitter. Each bit corresponds to an + output pad on the transmitter. If an output pad does not have an + associated hotplug pin, then the bit for that pad will be 0. This + read-only control is applicable to DVI-D, HDMI and DisplayPort + connectors. + +``V4L2_CID_DV_TX_RXSENSE (bitmask)`` + Rx Sense is the detection of pull-ups on the TMDS clock lines. This + normally means that the sink has left/entered standby (i.e. the + transmitter can sense that the receiver is ready to receive video). + Each bit corresponds to an output pad on the transmitter. If an + output pad does not have an associated Rx Sense, then the bit for + that pad will be 0. This read-only control is applicable to DVI-D + and HDMI devices. + +``V4L2_CID_DV_TX_EDID_PRESENT (bitmask)`` + When the transmitter sees the hotplug signal from the receiver it + will attempt to read the EDID. If set, then the transmitter has read + at least the first block (= 128 bytes). Each bit corresponds to an + output pad on the transmitter. If an output pad does not support + EDIDs, then the bit for that pad will be 0. This read-only control + is applicable to VGA, DVI-A/D, HDMI and DisplayPort connectors. + +``V4L2_CID_DV_TX_MODE`` + (enum) + +enum v4l2_dv_tx_mode - + HDMI transmitters can transmit in DVI-D mode (just video) or in HDMI + mode (video + audio + auxiliary data). This control selects which + mode to use: V4L2_DV_TX_MODE_DVI_D or V4L2_DV_TX_MODE_HDMI. + This control is applicable to HDMI connectors. + +``V4L2_CID_DV_TX_RGB_RANGE`` + (enum) + +enum v4l2_dv_rgb_range - + Select the quantization range for RGB output. V4L2_DV_RANGE_AUTO + follows the RGB quantization range specified in the standard for the + video interface (ie. :ref:`cea861` for HDMI). + V4L2_DV_RANGE_LIMITED and V4L2_DV_RANGE_FULL override the + standard to be compatible with sinks that have not implemented the + standard correctly (unfortunately quite common for HDMI and DVI-D). + Full range allows all possible values to be used whereas limited + range sets the range to (16 << (N-8)) - (235 << (N-8)) where N is + the number of bits per component. This control is applicable to VGA, + DVI-A/D, HDMI and DisplayPort connectors. + +``V4L2_CID_DV_TX_IT_CONTENT_TYPE`` + (enum) + +enum v4l2_dv_it_content_type - + Configures the IT Content Type of the transmitted video. This + information is sent over HDMI and DisplayPort connectors as part of + the AVI InfoFrame. The term 'IT Content' is used for content that + originates from a computer as opposed to content from a TV broadcast + or an analog source. The enum v4l2_dv_it_content_type defines + the possible content types: + +.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_DV_IT_CONTENT_TYPE_GRAPHICS`` + - Graphics content. Pixel data should be passed unfiltered and + without analog reconstruction. + * - ``V4L2_DV_IT_CONTENT_TYPE_PHOTO`` + - Photo content. The content is derived from digital still pictures. + The content should be passed through with minimal scaling and + picture enhancements. + * - ``V4L2_DV_IT_CONTENT_TYPE_CINEMA`` + - Cinema content. + * - ``V4L2_DV_IT_CONTENT_TYPE_GAME`` + - Game content. Audio and video latency should be minimized. + * - ``V4L2_DV_IT_CONTENT_TYPE_NO_ITC`` + - No IT Content information is available and the ITC bit in the AVI + InfoFrame is set to 0. + + + +``V4L2_CID_DV_RX_POWER_PRESENT (bitmask)`` + Detects whether the receiver receives power from the source (e.g. + HDMI carries 5V on one of the pins). This is often used to power an + eeprom which contains EDID information, such that the source can + read the EDID even if the sink is in standby/power off. Each bit + corresponds to an input pad on the receiver. If an input pad + cannot detect whether power is present, then the bit for that pad + will be 0. This read-only control is applicable to DVI-D, HDMI and + DisplayPort connectors. + +``V4L2_CID_DV_RX_RGB_RANGE`` + (enum) + +enum v4l2_dv_rgb_range - + Select the quantization range for RGB input. V4L2_DV_RANGE_AUTO + follows the RGB quantization range specified in the standard for the + video interface (ie. :ref:`cea861` for HDMI). + V4L2_DV_RANGE_LIMITED and V4L2_DV_RANGE_FULL override the + standard to be compatible with sources that have not implemented the + standard correctly (unfortunately quite common for HDMI and DVI-D). + Full range allows all possible values to be used whereas limited + range sets the range to (16 << (N-8)) - (235 << (N-8)) where N is + the number of bits per component. This control is applicable to VGA, + DVI-A/D, HDMI and DisplayPort connectors. + +``V4L2_CID_DV_RX_IT_CONTENT_TYPE`` + (enum) + +enum v4l2_dv_it_content_type - + Reads the IT Content Type of the received video. This information is + sent over HDMI and DisplayPort connectors as part of the AVI + InfoFrame. The term 'IT Content' is used for content that originates + from a computer as opposed to content from a TV broadcast or an + analog source. See ``V4L2_CID_DV_TX_IT_CONTENT_TYPE`` for the + available content types. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-flash.rst b/Documentation/media/uapi/v4l/ext-ctrls-flash.rst new file mode 100644 index 000000000000..5f30791c35b5 --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-flash.rst @@ -0,0 +1,192 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _flash-controls: + +*********************** +Flash Control Reference +*********************** + +The V4L2 flash controls are intended to provide generic access to flash +controller devices. Flash controller devices are typically used in +digital cameras. + +The interface can support both LED and xenon flash devices. As of +writing this, there is no xenon flash driver using this interface. + + +.. _flash-controls-use-cases: + +Supported use cases +=================== + + +Unsynchronised LED flash (software strobe) +------------------------------------------ + +Unsynchronised LED flash is controlled directly by the host as the +sensor. The flash must be enabled by the host before the exposure of the +image starts and disabled once it ends. The host is fully responsible +for the timing of the flash. + +Example of such device: Nokia N900. + + +Synchronised LED flash (hardware strobe) +---------------------------------------- + +The synchronised LED flash is pre-programmed by the host (power and +timeout) but controlled by the sensor through a strobe signal from the +sensor to the flash. + +The sensor controls the flash duration and timing. This information +typically must be made available to the sensor. + + +LED flash as torch +------------------ + +LED flash may be used as torch in conjunction with another use case +involving camera or individually. + + +.. _flash-control-id: + +Flash Control IDs +----------------- + +``V4L2_CID_FLASH_CLASS (class)`` + The FLASH class descriptor. + +``V4L2_CID_FLASH_LED_MODE (menu)`` + Defines the mode of the flash LED, the high-power white LED attached + to the flash controller. Setting this control may not be possible in + presence of some faults. See V4L2_CID_FLASH_FAULT. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_FLASH_LED_MODE_NONE`` + - Off. + * - ``V4L2_FLASH_LED_MODE_FLASH`` + - Flash mode. + * - ``V4L2_FLASH_LED_MODE_TORCH`` + - Torch mode. See V4L2_CID_FLASH_TORCH_INTENSITY. + + + +``V4L2_CID_FLASH_STROBE_SOURCE (menu)`` + Defines the source of the flash LED strobe. + +.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_FLASH_STROBE_SOURCE_SOFTWARE`` + - The flash strobe is triggered by using the + V4L2_CID_FLASH_STROBE control. + * - ``V4L2_FLASH_STROBE_SOURCE_EXTERNAL`` + - The flash strobe is triggered by an external source. Typically + this is a sensor, which makes it possible to synchronises the + flash strobe start to exposure start. + + + +``V4L2_CID_FLASH_STROBE (button)`` + Strobe flash. Valid when V4L2_CID_FLASH_LED_MODE is set to + V4L2_FLASH_LED_MODE_FLASH and V4L2_CID_FLASH_STROBE_SOURCE + is set to V4L2_FLASH_STROBE_SOURCE_SOFTWARE. Setting this + control may not be possible in presence of some faults. See + V4L2_CID_FLASH_FAULT. + +``V4L2_CID_FLASH_STROBE_STOP (button)`` + Stop flash strobe immediately. + +``V4L2_CID_FLASH_STROBE_STATUS (boolean)`` + Strobe status: whether the flash is strobing at the moment or not. + This is a read-only control. + +``V4L2_CID_FLASH_TIMEOUT (integer)`` + Hardware timeout for flash. The flash strobe is stopped after this + period of time has passed from the start of the strobe. + +``V4L2_CID_FLASH_INTENSITY (integer)`` + Intensity of the flash strobe when the flash LED is in flash mode + (V4L2_FLASH_LED_MODE_FLASH). The unit should be milliamps (mA) + if possible. + +``V4L2_CID_FLASH_TORCH_INTENSITY (integer)`` + Intensity of the flash LED in torch mode + (V4L2_FLASH_LED_MODE_TORCH). The unit should be milliamps (mA) + if possible. Setting this control may not be possible in presence of + some faults. See V4L2_CID_FLASH_FAULT. + +``V4L2_CID_FLASH_INDICATOR_INTENSITY (integer)`` + Intensity of the indicator LED. The indicator LED may be fully + independent of the flash LED. The unit should be microamps (uA) if + possible. + +``V4L2_CID_FLASH_FAULT (bitmask)`` + Faults related to the flash. The faults tell about specific problems + in the flash chip itself or the LEDs attached to it. Faults may + prevent further use of some of the flash controls. In particular, + V4L2_CID_FLASH_LED_MODE is set to V4L2_FLASH_LED_MODE_NONE + if the fault affects the flash LED. Exactly which faults have such + an effect is chip dependent. Reading the faults resets the control + and returns the chip to a usable state if possible. + +.. tabularcolumns:: |p{8.0cm}|p{9.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_FLASH_FAULT_OVER_VOLTAGE`` + - Flash controller voltage to the flash LED has exceeded the limit + specific to the flash controller. + * - ``V4L2_FLASH_FAULT_TIMEOUT`` + - The flash strobe was still on when the timeout set by the user --- + V4L2_CID_FLASH_TIMEOUT control --- has expired. Not all flash + controllers may set this in all such conditions. + * - ``V4L2_FLASH_FAULT_OVER_TEMPERATURE`` + - The flash controller has overheated. + * - ``V4L2_FLASH_FAULT_SHORT_CIRCUIT`` + - The short circuit protection of the flash controller has been + triggered. + * - ``V4L2_FLASH_FAULT_OVER_CURRENT`` + - Current in the LED power supply has exceeded the limit specific to + the flash controller. + * - ``V4L2_FLASH_FAULT_INDICATOR`` + - The flash controller has detected a short or open circuit + condition on the indicator LED. + * - ``V4L2_FLASH_FAULT_UNDER_VOLTAGE`` + - Flash controller voltage to the flash LED has been below the + minimum limit specific to the flash controller. + * - ``V4L2_FLASH_FAULT_INPUT_VOLTAGE`` + - The input voltage of the flash controller is below the limit under + which strobing the flash at full current will not be possible.The + condition persists until this flag is no longer set. + * - ``V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE`` + - The temperature of the LED has exceeded its allowed upper limit. + + + +``V4L2_CID_FLASH_CHARGE (boolean)`` + Enable or disable charging of the xenon flash capacitor. + +``V4L2_CID_FLASH_READY (boolean)`` + Is the flash ready to strobe? Xenon flashes require their capacitors + charged before strobing. LED flashes often require a cooldown period + after strobe during which another strobe will not be possible. This + is a read-only control. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-fm-rx.rst b/Documentation/media/uapi/v4l/ext-ctrls-fm-rx.rst new file mode 100644 index 000000000000..3ed6dd7f586d --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-fm-rx.rst @@ -0,0 +1,95 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _fm-rx-controls: + +***************************** +FM Receiver Control Reference +***************************** + +The FM Receiver (FM_RX) class includes controls for common features of +FM Reception capable devices. + + +.. _fm-rx-control-id: + +FM_RX Control IDs +================= + +``V4L2_CID_FM_RX_CLASS (class)`` + The FM_RX class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +``V4L2_CID_RDS_RECEPTION (boolean)`` + Enables/disables RDS reception by the radio tuner + +``V4L2_CID_RDS_RX_PTY (integer)`` + Gets RDS Programme Type field. This encodes up to 31 pre-defined + programme types. + +``V4L2_CID_RDS_RX_PS_NAME (string)`` + Gets the Programme Service name (PS_NAME). It is intended for + static display on a receiver. It is the primary aid to listeners in + programme service identification and selection. In Annex E of + :ref:`iec62106`, the RDS specification, there is a full + description of the correct character encoding for Programme Service + name strings. Also from RDS specification, PS is usually a single + eight character text. However, it is also possible to find receivers + which can scroll strings sized as 8 x N characters. So, this control + must be configured with steps of 8 characters. The result is it must + always contain a string with size multiple of 8. + +``V4L2_CID_RDS_RX_RADIO_TEXT (string)`` + Gets the Radio Text info. It is a textual description of what is + being broadcasted. RDS Radio Text can be applied when broadcaster + wishes to transmit longer PS names, programme-related information or + any other text. In these cases, RadioText can be used in addition to + ``V4L2_CID_RDS_RX_PS_NAME``. The encoding for Radio Text strings is + also fully described in Annex E of :ref:`iec62106`. The length of + Radio Text strings depends on which RDS Block is being used to + transmit it, either 32 (2A block) or 64 (2B block). However, it is + also possible to find receivers which can scroll strings sized as 32 + x N or 64 x N characters. So, this control must be configured with + steps of 32 or 64 characters. The result is it must always contain a + string with size multiple of 32 or 64. + +``V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT (boolean)`` + If set, then a traffic announcement is in progress. + +``V4L2_CID_RDS_RX_TRAFFIC_PROGRAM (boolean)`` + If set, then the tuned programme carries traffic announcements. + +``V4L2_CID_RDS_RX_MUSIC_SPEECH (boolean)`` + If set, then this channel broadcasts music. If cleared, then it + broadcasts speech. If the transmitter doesn't make this distinction, + then it will be set. + +``V4L2_CID_TUNE_DEEMPHASIS`` + (enum) + +enum v4l2_deemphasis - + Configures the de-emphasis value for reception. A de-emphasis filter + is applied to the broadcast to accentuate the high audio + frequencies. Depending on the region, a time constant of either 50 + or 75 useconds is used. The enum v4l2_deemphasis defines possible + values for de-emphasis. Here they are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_DEEMPHASIS_DISABLED`` + - No de-emphasis is applied. + * - ``V4L2_DEEMPHASIS_50_uS`` + - A de-emphasis of 50 uS is used. + * - ``V4L2_DEEMPHASIS_75_uS`` + - A de-emphasis of 75 uS is used. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-fm-tx.rst b/Documentation/media/uapi/v4l/ext-ctrls-fm-tx.rst new file mode 100644 index 000000000000..db88346d99fd --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-fm-tx.rst @@ -0,0 +1,188 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _fm-tx-controls: + +******************************** +FM Transmitter Control Reference +******************************** + +The FM Transmitter (FM_TX) class includes controls for common features +of FM transmissions capable devices. Currently this class includes +parameters for audio compression, pilot tone generation, audio deviation +limiter, RDS transmission and tuning power features. + + +.. _fm-tx-control-id: + +FM_TX Control IDs +================= + +``V4L2_CID_FM_TX_CLASS (class)`` + The FM_TX class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +``V4L2_CID_RDS_TX_DEVIATION (integer)`` + Configures RDS signal frequency deviation level in Hz. The range and + step are driver-specific. + +``V4L2_CID_RDS_TX_PI (integer)`` + Sets the RDS Programme Identification field for transmission. + +``V4L2_CID_RDS_TX_PTY (integer)`` + Sets the RDS Programme Type field for transmission. This encodes up + to 31 pre-defined programme types. + +``V4L2_CID_RDS_TX_PS_NAME (string)`` + Sets the Programme Service name (PS_NAME) for transmission. It is + intended for static display on a receiver. It is the primary aid to + listeners in programme service identification and selection. In + Annex E of :ref:`iec62106`, the RDS specification, there is a full + description of the correct character encoding for Programme Service + name strings. Also from RDS specification, PS is usually a single + eight character text. However, it is also possible to find receivers + which can scroll strings sized as 8 x N characters. So, this control + must be configured with steps of 8 characters. The result is it must + always contain a string with size multiple of 8. + +``V4L2_CID_RDS_TX_RADIO_TEXT (string)`` + Sets the Radio Text info for transmission. It is a textual + description of what is being broadcasted. RDS Radio Text can be + applied when broadcaster wishes to transmit longer PS names, + programme-related information or any other text. In these cases, + RadioText should be used in addition to ``V4L2_CID_RDS_TX_PS_NAME``. + The encoding for Radio Text strings is also fully described in Annex + E of :ref:`iec62106`. The length of Radio Text strings depends on + which RDS Block is being used to transmit it, either 32 (2A block) + or 64 (2B block). However, it is also possible to find receivers + which can scroll strings sized as 32 x N or 64 x N characters. So, + this control must be configured with steps of 32 or 64 characters. + The result is it must always contain a string with size multiple of + 32 or 64. + +``V4L2_CID_RDS_TX_MONO_STEREO (boolean)`` + Sets the Mono/Stereo bit of the Decoder Identification code. If set, + then the audio was recorded as stereo. + +``V4L2_CID_RDS_TX_ARTIFICIAL_HEAD (boolean)`` + Sets the + `Artificial Head `__ + bit of the Decoder Identification code. If set, then the audio was + recorded using an artificial head. + +``V4L2_CID_RDS_TX_COMPRESSED (boolean)`` + Sets the Compressed bit of the Decoder Identification code. If set, + then the audio is compressed. + +``V4L2_CID_RDS_TX_DYNAMIC_PTY (boolean)`` + Sets the Dynamic PTY bit of the Decoder Identification code. If set, + then the PTY code is dynamically switched. + +``V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT (boolean)`` + If set, then a traffic announcement is in progress. + +``V4L2_CID_RDS_TX_TRAFFIC_PROGRAM (boolean)`` + If set, then the tuned programme carries traffic announcements. + +``V4L2_CID_RDS_TX_MUSIC_SPEECH (boolean)`` + If set, then this channel broadcasts music. If cleared, then it + broadcasts speech. If the transmitter doesn't make this distinction, + then it should be set. + +``V4L2_CID_RDS_TX_ALT_FREQS_ENABLE (boolean)`` + If set, then transmit alternate frequencies. + +``V4L2_CID_RDS_TX_ALT_FREQS (__u32 array)`` + The alternate frequencies in kHz units. The RDS standard allows for + up to 25 frequencies to be defined. Drivers may support fewer + frequencies so check the array size. + +``V4L2_CID_AUDIO_LIMITER_ENABLED (boolean)`` + Enables or disables the audio deviation limiter feature. The limiter + is useful when trying to maximize the audio volume, minimize + receiver-generated distortion and prevent overmodulation. + +``V4L2_CID_AUDIO_LIMITER_RELEASE_TIME (integer)`` + Sets the audio deviation limiter feature release time. Unit is in + useconds. Step and range are driver-specific. + +``V4L2_CID_AUDIO_LIMITER_DEVIATION (integer)`` + Configures audio frequency deviation level in Hz. The range and step + are driver-specific. + +``V4L2_CID_AUDIO_COMPRESSION_ENABLED (boolean)`` + Enables or disables the audio compression feature. This feature + amplifies signals below the threshold by a fixed gain and compresses + audio signals above the threshold by the ratio of Threshold/(Gain + + Threshold). + +``V4L2_CID_AUDIO_COMPRESSION_GAIN (integer)`` + Sets the gain for audio compression feature. It is a dB value. The + range and step are driver-specific. + +``V4L2_CID_AUDIO_COMPRESSION_THRESHOLD (integer)`` + Sets the threshold level for audio compression freature. It is a dB + value. The range and step are driver-specific. + +``V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME (integer)`` + Sets the attack time for audio compression feature. It is a useconds + value. The range and step are driver-specific. + +``V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME (integer)`` + Sets the release time for audio compression feature. It is a + useconds value. The range and step are driver-specific. + +``V4L2_CID_PILOT_TONE_ENABLED (boolean)`` + Enables or disables the pilot tone generation feature. + +``V4L2_CID_PILOT_TONE_DEVIATION (integer)`` + Configures pilot tone frequency deviation level. Unit is in Hz. The + range and step are driver-specific. + +``V4L2_CID_PILOT_TONE_FREQUENCY (integer)`` + Configures pilot tone frequency value. Unit is in Hz. The range and + step are driver-specific. + +``V4L2_CID_TUNE_PREEMPHASIS`` + (enum) + +enum v4l2_preemphasis - + Configures the pre-emphasis value for broadcasting. A pre-emphasis + filter is applied to the broadcast to accentuate the high audio + frequencies. Depending on the region, a time constant of either 50 + or 75 useconds is used. The enum v4l2_preemphasis defines possible + values for pre-emphasis. Here they are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_PREEMPHASIS_DISABLED`` + - No pre-emphasis is applied. + * - ``V4L2_PREEMPHASIS_50_uS`` + - A pre-emphasis of 50 uS is used. + * - ``V4L2_PREEMPHASIS_75_uS`` + - A pre-emphasis of 75 uS is used. + + + +``V4L2_CID_TUNE_POWER_LEVEL (integer)`` + Sets the output power level for signal transmission. Unit is in + dBuV. Range and step are driver-specific. + +``V4L2_CID_TUNE_ANTENNA_CAPACITOR (integer)`` + This selects the value of antenna tuning capacitor manually or + automatically if set to zero. Unit, range and step are + driver-specific. + +For more details about RDS specification, refer to :ref:`iec62106` +document, from CENELEC. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-image-process.rst b/Documentation/media/uapi/v4l/ext-ctrls-image-process.rst new file mode 100644 index 000000000000..22fc2d3e433d --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-image-process.rst @@ -0,0 +1,63 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _image-process-controls: + +******************************* +Image Process Control Reference +******************************* + +The Image Process control class is intended for low-level control of +image processing functions. Unlike ``V4L2_CID_IMAGE_SOURCE_CLASS``, the +controls in this class affect processing the image, and do not control +capturing of it. + + +.. _image-process-control-id: + +Image Process Control IDs +========================= + +``V4L2_CID_IMAGE_PROC_CLASS (class)`` + The IMAGE_PROC class descriptor. + +``V4L2_CID_LINK_FREQ (integer menu)`` + Data bus frequency. Together with the media bus pixel code, bus type + (clock cycles per sample), the data bus frequency defines the pixel + rate (``V4L2_CID_PIXEL_RATE``) in the pixel array (or possibly + elsewhere, if the device is not an image sensor). The frame rate can + be calculated from the pixel clock, image width and height and + horizontal and vertical blanking. While the pixel rate control may + be defined elsewhere than in the subdev containing the pixel array, + the frame rate cannot be obtained from that information. This is + because only on the pixel array it can be assumed that the vertical + and horizontal blanking information is exact: no other blanking is + allowed in the pixel array. The selection of frame rate is performed + by selecting the desired horizontal and vertical blanking. The unit + of this control is Hz. + +``V4L2_CID_PIXEL_RATE (64-bit integer)`` + Pixel rate in the source pads of the subdev. This control is + read-only and its unit is pixels / second. + +``V4L2_CID_TEST_PATTERN (menu)`` + Some capture/display/sensor devices have the capability to generate + test pattern images. These hardware specific test patterns can be + used to test if a device is working properly. + +``V4L2_CID_DEINTERLACING_MODE (menu)`` + The video deinterlacing mode (such as Bob, Weave, ...). The menu items are + driver specific and are documented in :ref:`v4l-drivers`. + +``V4L2_CID_DIGITAL_GAIN (integer)`` + Digital gain is the value by which all colour components + are multiplied by. Typically the digital gain applied is the + control value divided by e.g. 0x100, meaning that to get no + digital gain the control value needs to be 0x100. The no-gain + configuration is also typically the default. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-image-source.rst b/Documentation/media/uapi/v4l/ext-ctrls-image-source.rst new file mode 100644 index 000000000000..2c3ab5796d76 --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-image-source.rst @@ -0,0 +1,57 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _image-source-controls: + +****************************** +Image Source Control Reference +****************************** + +The Image Source control class is intended for low-level control of +image source devices such as image sensors. The devices feature an +analogue to digital converter and a bus transmitter to transmit the +image data out of the device. + + +.. _image-source-control-id: + +Image Source Control IDs +======================== + +``V4L2_CID_IMAGE_SOURCE_CLASS (class)`` + The IMAGE_SOURCE class descriptor. + +``V4L2_CID_VBLANK (integer)`` + Vertical blanking. The idle period after every frame during which no + image data is produced. The unit of vertical blanking is a line. + Every line has length of the image width plus horizontal blanking at + the pixel rate defined by ``V4L2_CID_PIXEL_RATE`` control in the + same sub-device. + +``V4L2_CID_HBLANK (integer)`` + Horizontal blanking. The idle period after every line of image data + during which no image data is produced. The unit of horizontal + blanking is pixels. + +``V4L2_CID_ANALOGUE_GAIN (integer)`` + Analogue gain is gain affecting all colour components in the pixel + matrix. The gain operation is performed in the analogue domain + before A/D conversion. + +``V4L2_CID_TEST_PATTERN_RED (integer)`` + Test pattern red colour component. + +``V4L2_CID_TEST_PATTERN_GREENR (integer)`` + Test pattern green (next to red) colour component. + +``V4L2_CID_TEST_PATTERN_BLUE (integer)`` + Test pattern blue colour component. + +``V4L2_CID_TEST_PATTERN_GREENB (integer)`` + Test pattern green (next to blue) colour component. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-jpeg.rst b/Documentation/media/uapi/v4l/ext-ctrls-jpeg.rst new file mode 100644 index 000000000000..cf9cd8a9f9b4 --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-jpeg.rst @@ -0,0 +1,113 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _jpeg-controls: + +********************** +JPEG Control Reference +********************** + +The JPEG class includes controls for common features of JPEG encoders +and decoders. Currently it includes features for codecs implementing +progressive baseline DCT compression process with Huffman entrophy +coding. + + +.. _jpeg-control-id: + +JPEG Control IDs +================ + +``V4L2_CID_JPEG_CLASS (class)`` + The JPEG class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +``V4L2_CID_JPEG_CHROMA_SUBSAMPLING (menu)`` + The chroma subsampling factors describe how each component of an + input image is sampled, in respect to maximum sample rate in each + spatial dimension. See :ref:`itu-t81`, clause A.1.1. for more + details. The ``V4L2_CID_JPEG_CHROMA_SUBSAMPLING`` control determines + how Cb and Cr components are downsampled after converting an input + image from RGB to Y'CbCr color space. + +.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_444`` + - No chroma subsampling, each pixel has Y, Cr and Cb values. + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_422`` + - Horizontally subsample Cr, Cb components by a factor of 2. + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_420`` + - Subsample Cr, Cb components horizontally and vertically by 2. + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_411`` + - Horizontally subsample Cr, Cb components by a factor of 4. + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_410`` + - Subsample Cr, Cb components horizontally by 4 and vertically by 2. + * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY`` + - Use only luminance component. + + + +``V4L2_CID_JPEG_RESTART_INTERVAL (integer)`` + The restart interval determines an interval of inserting RSTm + markers (m = 0..7). The purpose of these markers is to additionally + reinitialize the encoder process, in order to process blocks of an + image independently. For the lossy compression processes the restart + interval unit is MCU (Minimum Coded Unit) and its value is contained + in DRI (Define Restart Interval) marker. If + ``V4L2_CID_JPEG_RESTART_INTERVAL`` control is set to 0, DRI and RSTm + markers will not be inserted. + +.. _jpeg-quality-control: + +``V4L2_CID_JPEG_COMPRESSION_QUALITY (integer)`` + ``V4L2_CID_JPEG_COMPRESSION_QUALITY`` control determines trade-off + between image quality and size. It provides simpler method for + applications to control image quality, without a need for direct + reconfiguration of luminance and chrominance quantization tables. In + cases where a driver uses quantization tables configured directly by + an application, using interfaces defined elsewhere, + ``V4L2_CID_JPEG_COMPRESSION_QUALITY`` control should be set by + driver to 0. + + The value range of this control is driver-specific. Only positive, + non-zero values are meaningful. The recommended range is 1 - 100, + where larger values correspond to better image quality. + +.. _jpeg-active-marker-control: + +``V4L2_CID_JPEG_ACTIVE_MARKER (bitmask)`` + Specify which JPEG markers are included in compressed stream. This + control is valid only for encoders. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_JPEG_ACTIVE_MARKER_APP0`` + - Application data segment APP\ :sub:`0`. + * - ``V4L2_JPEG_ACTIVE_MARKER_APP1`` + - Application data segment APP\ :sub:`1`. + * - ``V4L2_JPEG_ACTIVE_MARKER_COM`` + - Comment segment. + * - ``V4L2_JPEG_ACTIVE_MARKER_DQT`` + - Quantization tables segment. + * - ``V4L2_JPEG_ACTIVE_MARKER_DHT`` + - Huffman tables segment. + + + +For more details about JPEG specification, refer to :ref:`itu-t81`, +:ref:`jfif`, :ref:`w3c-jpeg-jfif`. diff --git a/Documentation/media/uapi/v4l/ext-ctrls-rf-tuner.rst b/Documentation/media/uapi/v4l/ext-ctrls-rf-tuner.rst new file mode 100644 index 000000000000..0fb85ba878dd --- /dev/null +++ b/Documentation/media/uapi/v4l/ext-ctrls-rf-tuner.rst @@ -0,0 +1,96 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _rf-tuner-controls: + +************************** +RF Tuner Control Reference +************************** + +The RF Tuner (RF_TUNER) class includes controls for common features of +devices having RF tuner. + +In this context, RF tuner is radio receiver circuit between antenna and +demodulator. It receives radio frequency (RF) from the antenna and +converts that received signal to lower intermediate frequency (IF) or +baseband frequency (BB). Tuners that could do baseband output are often +called Zero-IF tuners. Older tuners were typically simple PLL tuners +inside a metal box, while newer ones are highly integrated chips +without a metal box "silicon tuners". These controls are mostly +applicable for new feature rich silicon tuners, just because older +tuners does not have much adjustable features. + +For more information about RF tuners see +`Tuner (radio) `__ +and `RF front end `__ +from Wikipedia. + + +.. _rf-tuner-control-id: + +RF_TUNER Control IDs +==================== + +``V4L2_CID_RF_TUNER_CLASS (class)`` + The RF_TUNER class descriptor. Calling + :ref:`VIDIOC_QUERYCTRL` for this control will + return a description of this control class. + +``V4L2_CID_RF_TUNER_BANDWIDTH_AUTO (boolean)`` + Enables/disables tuner radio channel bandwidth configuration. In + automatic mode bandwidth configuration is performed by the driver. + +``V4L2_CID_RF_TUNER_BANDWIDTH (integer)`` + Filter(s) on tuner signal path are used to filter signal according + to receiving party needs. Driver configures filters to fulfill + desired bandwidth requirement. Used when + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO is not set. Unit is in Hz. The + range and step are driver-specific. + +``V4L2_CID_RF_TUNER_LNA_GAIN_AUTO (boolean)`` + Enables/disables LNA automatic gain control (AGC) + +``V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO (boolean)`` + Enables/disables mixer automatic gain control (AGC) + +``V4L2_CID_RF_TUNER_IF_GAIN_AUTO (boolean)`` + Enables/disables IF automatic gain control (AGC) + +``V4L2_CID_RF_TUNER_RF_GAIN (integer)`` + The RF amplifier is the very first amplifier on the receiver signal + path, just right after the antenna input. The difference between the + LNA gain and the RF gain in this document is that the LNA gain is + integrated in the tuner chip while the RF gain is a separate chip. + There may be both RF and LNA gain controls in the same device. The + range and step are driver-specific. + +``V4L2_CID_RF_TUNER_LNA_GAIN (integer)`` + LNA (low noise amplifier) gain is first gain stage on the RF tuner + signal path. It is located very close to tuner antenna input. Used + when ``V4L2_CID_RF_TUNER_LNA_GAIN_AUTO`` is not set. See + ``V4L2_CID_RF_TUNER_RF_GAIN`` to understand how RF gain and LNA gain + differs from the each others. The range and step are + driver-specific. + +``V4L2_CID_RF_TUNER_MIXER_GAIN (integer)`` + Mixer gain is second gain stage on the RF tuner signal path. It is + located inside mixer block, where RF signal is down-converted by the + mixer. Used when ``V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO`` is not set. + The range and step are driver-specific. + +``V4L2_CID_RF_TUNER_IF_GAIN (integer)`` + IF gain is last gain stage on the RF tuner signal path. It is + located on output of RF tuner. It controls signal level of + intermediate frequency output or baseband output. Used when + ``V4L2_CID_RF_TUNER_IF_GAIN_AUTO`` is not set. The range and step + are driver-specific. + +``V4L2_CID_RF_TUNER_PLL_LOCK (boolean)`` + Is synthesizer PLL locked? RF tuner is receiving given frequency + when that control is set. This is a read-only control. diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index 00934efdc9e4..24274b398e63 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -9,9 +9,9 @@ .. _extended-controls: -***************** -Extended Controls -***************** +********************* +Extended Controls API +********************* Introduction @@ -181,3917 +181,3 @@ The flags field of struct :ref:`v4l2_queryctrl ` also contains hints on the behavior of the control. See the :ref:`VIDIOC_QUERYCTRL` documentation for more details. - - -.. _mpeg-controls: - -Codec Control Reference -======================= - -Below all controls within the Codec control class are described. First -the generic controls, then controls specific for certain hardware. - -.. note:: - - These controls are applicable to all codecs and not just MPEG. The - defines are prefixed with V4L2_CID_MPEG/V4L2_MPEG as the controls - were originally made for MPEG codecs and later extended to cover all - encoding formats. - - -Generic Codec Controls ----------------------- - - -.. _mpeg-control-id: - -Codec Control IDs -^^^^^^^^^^^^^^^^^ - -``V4L2_CID_MPEG_CLASS (class)`` - The Codec class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. This description can be - used as the caption of a Tab page in a GUI, for example. - -.. _v4l2-mpeg-stream-type: - -``V4L2_CID_MPEG_STREAM_TYPE`` - (enum) - -enum v4l2_mpeg_stream_type - - The MPEG-1, -2 or -4 output stream type. One cannot assume anything - here. Each hardware MPEG encoder tends to support different subsets - of the available MPEG stream types. This control is specific to - multiplexed MPEG streams. The currently defined stream types are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_PS`` - - MPEG-2 program stream - * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_TS`` - - MPEG-2 transport stream - * - ``V4L2_MPEG_STREAM_TYPE_MPEG1_SS`` - - MPEG-1 system stream - * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_DVD`` - - MPEG-2 DVD-compatible stream - * - ``V4L2_MPEG_STREAM_TYPE_MPEG1_VCD`` - - MPEG-1 VCD-compatible stream - * - ``V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD`` - - MPEG-2 SVCD-compatible stream - - - -``V4L2_CID_MPEG_STREAM_PID_PMT (integer)`` - Program Map Table Packet ID for the MPEG transport stream (default - 16) - -``V4L2_CID_MPEG_STREAM_PID_AUDIO (integer)`` - Audio Packet ID for the MPEG transport stream (default 256) - -``V4L2_CID_MPEG_STREAM_PID_VIDEO (integer)`` - Video Packet ID for the MPEG transport stream (default 260) - -``V4L2_CID_MPEG_STREAM_PID_PCR (integer)`` - Packet ID for the MPEG transport stream carrying PCR fields (default - 259) - -``V4L2_CID_MPEG_STREAM_PES_ID_AUDIO (integer)`` - Audio ID for MPEG PES - -``V4L2_CID_MPEG_STREAM_PES_ID_VIDEO (integer)`` - Video ID for MPEG PES - -.. _v4l2-mpeg-stream-vbi-fmt: - -``V4L2_CID_MPEG_STREAM_VBI_FMT`` - (enum) - -enum v4l2_mpeg_stream_vbi_fmt - - Some cards can embed VBI data (e. g. Closed Caption, Teletext) into - the MPEG stream. This control selects whether VBI data should be - embedded, and if so, what embedding method should be used. The list - of possible VBI formats depends on the driver. The currently defined - VBI format types are: - - - -.. tabularcolumns:: |p{6 cm}|p{11.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_STREAM_VBI_FMT_NONE`` - - No VBI in the MPEG stream - * - ``V4L2_MPEG_STREAM_VBI_FMT_IVTV`` - - VBI in private packets, IVTV format (documented in the kernel - sources in the file - ``Documentation/media/v4l-drivers/cx2341x.rst``) - - - -.. _v4l2-mpeg-audio-sampling-freq: - -``V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ`` - (enum) - -enum v4l2_mpeg_audio_sampling_freq - - MPEG Audio sampling frequency. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100`` - - 44.1 kHz - * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000`` - - 48 kHz - * - ``V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000`` - - 32 kHz - - - -.. _v4l2-mpeg-audio-encoding: - -``V4L2_CID_MPEG_AUDIO_ENCODING`` - (enum) - -enum v4l2_mpeg_audio_encoding - - MPEG Audio encoding. This control is specific to multiplexed MPEG - streams. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_1`` - - MPEG-1/2 Layer I encoding - * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_2`` - - MPEG-1/2 Layer II encoding - * - ``V4L2_MPEG_AUDIO_ENCODING_LAYER_3`` - - MPEG-1/2 Layer III encoding - * - ``V4L2_MPEG_AUDIO_ENCODING_AAC`` - - MPEG-2/4 AAC (Advanced Audio Coding) - * - ``V4L2_MPEG_AUDIO_ENCODING_AC3`` - - AC-3 aka ATSC A/52 encoding - - - -.. _v4l2-mpeg-audio-l1-bitrate: - -``V4L2_CID_MPEG_AUDIO_L1_BITRATE`` - (enum) - -enum v4l2_mpeg_audio_l1_bitrate - - MPEG-1/2 Layer I bitrate. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_32K`` - - 32 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_64K`` - - 64 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_96K`` - - 96 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_128K`` - - 128 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_160K`` - - 160 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_192K`` - - 192 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_224K`` - - 224 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_256K`` - - 256 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_288K`` - - 288 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_320K`` - - 320 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_352K`` - - 352 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_384K`` - - 384 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_416K`` - - 416 kbit/s - * - ``V4L2_MPEG_AUDIO_L1_BITRATE_448K`` - - 448 kbit/s - - - -.. _v4l2-mpeg-audio-l2-bitrate: - -``V4L2_CID_MPEG_AUDIO_L2_BITRATE`` - (enum) - -enum v4l2_mpeg_audio_l2_bitrate - - MPEG-1/2 Layer II bitrate. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_32K`` - - 32 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_48K`` - - 48 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_56K`` - - 56 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_64K`` - - 64 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_80K`` - - 80 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_96K`` - - 96 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_112K`` - - 112 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_128K`` - - 128 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_160K`` - - 160 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_192K`` - - 192 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_224K`` - - 224 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_256K`` - - 256 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_320K`` - - 320 kbit/s - * - ``V4L2_MPEG_AUDIO_L2_BITRATE_384K`` - - 384 kbit/s - - - -.. _v4l2-mpeg-audio-l3-bitrate: - -``V4L2_CID_MPEG_AUDIO_L3_BITRATE`` - (enum) - -enum v4l2_mpeg_audio_l3_bitrate - - MPEG-1/2 Layer III bitrate. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_32K`` - - 32 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_40K`` - - 40 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_48K`` - - 48 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_56K`` - - 56 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_64K`` - - 64 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_80K`` - - 80 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_96K`` - - 96 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_112K`` - - 112 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_128K`` - - 128 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_160K`` - - 160 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_192K`` - - 192 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_224K`` - - 224 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_256K`` - - 256 kbit/s - * - ``V4L2_MPEG_AUDIO_L3_BITRATE_320K`` - - 320 kbit/s - - - -``V4L2_CID_MPEG_AUDIO_AAC_BITRATE (integer)`` - AAC bitrate in bits per second. - -.. _v4l2-mpeg-audio-ac3-bitrate: - -``V4L2_CID_MPEG_AUDIO_AC3_BITRATE`` - (enum) - -enum v4l2_mpeg_audio_ac3_bitrate - - AC-3 bitrate. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_32K`` - - 32 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_40K`` - - 40 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_48K`` - - 48 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_56K`` - - 56 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_64K`` - - 64 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_80K`` - - 80 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_96K`` - - 96 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_112K`` - - 112 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_128K`` - - 128 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_160K`` - - 160 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_192K`` - - 192 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_224K`` - - 224 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_256K`` - - 256 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_320K`` - - 320 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_384K`` - - 384 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_448K`` - - 448 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_512K`` - - 512 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_576K`` - - 576 kbit/s - * - ``V4L2_MPEG_AUDIO_AC3_BITRATE_640K`` - - 640 kbit/s - - - -.. _v4l2-mpeg-audio-mode: - -``V4L2_CID_MPEG_AUDIO_MODE`` - (enum) - -enum v4l2_mpeg_audio_mode - - MPEG Audio mode. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_MODE_STEREO`` - - Stereo - * - ``V4L2_MPEG_AUDIO_MODE_JOINT_STEREO`` - - Joint Stereo - * - ``V4L2_MPEG_AUDIO_MODE_DUAL`` - - Bilingual - * - ``V4L2_MPEG_AUDIO_MODE_MONO`` - - Mono - - - -.. _v4l2-mpeg-audio-mode-extension: - -``V4L2_CID_MPEG_AUDIO_MODE_EXTENSION`` - (enum) - -enum v4l2_mpeg_audio_mode_extension - - Joint Stereo audio mode extension. In Layer I and II they indicate - which subbands are in intensity stereo. All other subbands are coded - in stereo. Layer III is not (yet) supported. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4`` - - Subbands 4-31 in intensity stereo - * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8`` - - Subbands 8-31 in intensity stereo - * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12`` - - Subbands 12-31 in intensity stereo - * - ``V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16`` - - Subbands 16-31 in intensity stereo - - - -.. _v4l2-mpeg-audio-emphasis: - -``V4L2_CID_MPEG_AUDIO_EMPHASIS`` - (enum) - -enum v4l2_mpeg_audio_emphasis - - Audio Emphasis. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_EMPHASIS_NONE`` - - None - * - ``V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS`` - - 50/15 microsecond emphasis - * - ``V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17`` - - CCITT J.17 - - - -.. _v4l2-mpeg-audio-crc: - -``V4L2_CID_MPEG_AUDIO_CRC`` - (enum) - -enum v4l2_mpeg_audio_crc - - CRC method. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_CRC_NONE`` - - None - * - ``V4L2_MPEG_AUDIO_CRC_CRC16`` - - 16 bit parity check - - - -``V4L2_CID_MPEG_AUDIO_MUTE (boolean)`` - Mutes the audio when capturing. This is not done by muting audio - hardware, which can still produce a slight hiss, but in the encoder - itself, guaranteeing a fixed and reproducible audio bitstream. 0 = - unmuted, 1 = muted. - -.. _v4l2-mpeg-audio-dec-playback: - -``V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK`` - (enum) - -enum v4l2_mpeg_audio_dec_playback - - Determines how monolingual audio should be played back. Possible - values are: - - - -.. tabularcolumns:: |p{9.0cm}|p{8.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO`` - - Automatically determines the best playback mode. - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO`` - - Stereo playback. - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT`` - - Left channel playback. - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_RIGHT`` - - Right channel playback. - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_MONO`` - - Mono playback. - * - ``V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO`` - - Stereo playback with swapped left and right channels. - - - -.. _v4l2-mpeg-audio-dec-multilingual-playback: - -``V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK`` - (enum) - -enum v4l2_mpeg_audio_dec_playback - - Determines how multilingual audio should be played back. - -.. _v4l2-mpeg-video-encoding: - -``V4L2_CID_MPEG_VIDEO_ENCODING`` - (enum) - -enum v4l2_mpeg_video_encoding - - MPEG Video encoding method. This control is specific to multiplexed - MPEG streams. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_1`` - - MPEG-1 Video encoding - * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_2`` - - MPEG-2 Video encoding - * - ``V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC`` - - MPEG-4 AVC (H.264) Video encoding - - - -.. _v4l2-mpeg-video-aspect: - -``V4L2_CID_MPEG_VIDEO_ASPECT`` - (enum) - -enum v4l2_mpeg_video_aspect - - Video aspect. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_ASPECT_1x1`` - * - ``V4L2_MPEG_VIDEO_ASPECT_4x3`` - * - ``V4L2_MPEG_VIDEO_ASPECT_16x9`` - * - ``V4L2_MPEG_VIDEO_ASPECT_221x100`` - - - -``V4L2_CID_MPEG_VIDEO_B_FRAMES (integer)`` - Number of B-Frames (default 2) - -``V4L2_CID_MPEG_VIDEO_GOP_SIZE (integer)`` - GOP size (default 12) - -``V4L2_CID_MPEG_VIDEO_GOP_CLOSURE (boolean)`` - GOP closure (default 1) - -``V4L2_CID_MPEG_VIDEO_PULLDOWN (boolean)`` - Enable 3:2 pulldown (default 0) - -.. _v4l2-mpeg-video-bitrate-mode: - -``V4L2_CID_MPEG_VIDEO_BITRATE_MODE`` - (enum) - -enum v4l2_mpeg_video_bitrate_mode - - Video bitrate mode. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_BITRATE_MODE_VBR`` - - Variable bitrate - * - ``V4L2_MPEG_VIDEO_BITRATE_MODE_CBR`` - - Constant bitrate - - - -``V4L2_CID_MPEG_VIDEO_BITRATE (integer)`` - Video bitrate in bits per second. - -``V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (integer)`` - Peak video bitrate in bits per second. Must be larger or equal to - the average video bitrate. It is ignored if the video bitrate mode - is set to constant bitrate. - -``V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (integer)`` - For every captured frame, skip this many subsequent frames (default - 0). - -``V4L2_CID_MPEG_VIDEO_MUTE (boolean)`` - "Mutes" the video to a fixed color when capturing. This is useful - for testing, to produce a fixed video bitstream. 0 = unmuted, 1 = - muted. - -``V4L2_CID_MPEG_VIDEO_MUTE_YUV (integer)`` - Sets the "mute" color of the video. The supplied 32-bit integer is - interpreted as follows (bit 0 = least significant bit): - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - Bit 0:7 - - V chrominance information - * - Bit 8:15 - - U chrominance information - * - Bit 16:23 - - Y luminance information - * - Bit 24:31 - - Must be zero. - - - -.. _v4l2-mpeg-video-dec-pts: - -``V4L2_CID_MPEG_VIDEO_DEC_PTS (integer64)`` - This read-only control returns the 33-bit video Presentation Time - Stamp as defined in ITU T-REC-H.222.0 and ISO/IEC 13818-1 of the - currently displayed frame. This is the same PTS as is used in - :ref:`VIDIOC_DECODER_CMD`. - -.. _v4l2-mpeg-video-dec-frame: - -``V4L2_CID_MPEG_VIDEO_DEC_FRAME (integer64)`` - This read-only control returns the frame counter of the frame that - is currently displayed (decoded). This value is reset to 0 whenever - the decoder is started. - -``V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE (boolean)`` - If enabled the decoder expects to receive a single slice per buffer, - otherwise the decoder expects a single frame in per buffer. - Applicable to the decoder, all codecs. - -``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE (boolean)`` - Enable writing sample aspect ratio in the Video Usability - Information. Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-vui-sar-idc: - -``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC`` - (enum) - -enum v4l2_mpeg_video_h264_vui_sar_idc - - VUI sample aspect ratio indicator for H.264 encoding. The value is - defined in the table E-1 in the standard. Applicable to the H264 - encoder. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED`` - - Unspecified - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1`` - - 1x1 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11`` - - 12x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11`` - - 10x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11`` - - 16x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33`` - - 40x33 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11`` - - 24x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11`` - - 20x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11`` - - 32x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33`` - - 80x33 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11`` - - 18x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11`` - - 15x11 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33`` - - 64x33 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99`` - - 160x99 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3`` - - 4x3 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2`` - - 3x2 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1`` - - 2x1 - * - ``V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED`` - - Extended SAR - - - -``V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH (integer)`` - Extended sample aspect ratio width for H.264 VUI encoding. - Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT (integer)`` - Extended sample aspect ratio height for H.264 VUI encoding. - Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-level: - -``V4L2_CID_MPEG_VIDEO_H264_LEVEL`` - (enum) - -enum v4l2_mpeg_video_h264_level - - The level information for the H264 video elementary stream. - Applicable to the H264 encoder. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_0`` - - Level 1.0 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1B`` - - Level 1B - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_1`` - - Level 1.1 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_2`` - - Level 1.2 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_1_3`` - - Level 1.3 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_0`` - - Level 2.0 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_1`` - - Level 2.1 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_2_2`` - - Level 2.2 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_0`` - - Level 3.0 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_1`` - - Level 3.1 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_3_2`` - - Level 3.2 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_0`` - - Level 4.0 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_1`` - - Level 4.1 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_4_2`` - - Level 4.2 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_5_0`` - - Level 5.0 - * - ``V4L2_MPEG_VIDEO_H264_LEVEL_5_1`` - - Level 5.1 - - - -.. _v4l2-mpeg-video-mpeg4-level: - -``V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL`` - (enum) - -enum v4l2_mpeg_video_mpeg4_level - - The level information for the MPEG4 elementary stream. Applicable to - the MPEG4 encoder. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_0`` - - Level 0 - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B`` - - Level 0b - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_1`` - - Level 1 - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_2`` - - Level 2 - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_3`` - - Level 3 - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B`` - - Level 3b - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_4`` - - Level 4 - * - ``V4L2_MPEG_VIDEO_MPEG4_LEVEL_5`` - - Level 5 - - - -.. _v4l2-mpeg-video-h264-profile: - -``V4L2_CID_MPEG_VIDEO_H264_PROFILE`` - (enum) - -enum v4l2_mpeg_video_h264_profile - - The profile information for H264. Applicable to the H264 encoder. - Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE`` - - Baseline profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE`` - - Constrained Baseline profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_MAIN`` - - Main profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED`` - - Extended profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH`` - - High profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10`` - - High 10 profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422`` - - High 422 profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE`` - - High 444 Predictive profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA`` - - High 10 Intra profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA`` - - High 422 Intra profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA`` - - High 444 Intra profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA`` - - CAVLC 444 Intra profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE`` - - Scalable Baseline profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH`` - - Scalable High profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA`` - - Scalable High Intra profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH`` - - Stereo High profile - * - ``V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH`` - - Multiview High profile - - - -.. _v4l2-mpeg-video-mpeg4-profile: - -``V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE`` - (enum) - -enum v4l2_mpeg_video_mpeg4_profile - - The profile information for MPEG4. Applicable to the MPEG4 encoder. - Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE`` - - Simple profile - * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE`` - - Advanced Simple profile - * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE`` - - Core profile - * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE`` - - Simple Scalable profile - * - ``V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY`` - - - - - -``V4L2_CID_MPEG_VIDEO_MAX_REF_PIC (integer)`` - The maximum number of reference pictures used for encoding. - Applicable to the encoder. - -.. _v4l2-mpeg-video-multi-slice-mode: - -``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` - (enum) - -enum v4l2_mpeg_video_multi_slice_mode - - Determines how the encoder should handle division of frame into - slices. Applicable to the encoder. Possible values are: - - - -.. tabularcolumns:: |p{8.7cm}|p{8.8cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE`` - - Single slice per frame. - * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB`` - - Multiple slices with set maximum number of macroblocks per slice. - * - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES`` - - Multiple slice with set maximum size in bytes per slice. - - - -``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB (integer)`` - The maximum number of macroblocks in a slice. Used when - ``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` is set to - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB``. Applicable to the - encoder. - -``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES (integer)`` - The maximum size of a slice in bytes. Used when - ``V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE`` is set to - ``V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES``. Applicable to the - encoder. - -.. _v4l2-mpeg-video-h264-loop-filter-mode: - -``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE`` - (enum) - -enum v4l2_mpeg_video_h264_loop_filter_mode - - Loop filter mode for H264 encoder. Possible values are: - - - -.. tabularcolumns:: |p{14.0cm}|p{3.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED`` - - Loop filter is enabled. - * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED`` - - Loop filter is disabled. - * - ``V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY`` - - Loop filter is disabled at the slice boundary. - - - -``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA (integer)`` - Loop filter alpha coefficient, defined in the H264 standard. - This value corresponds to the slice_alpha_c0_offset_div2 slice header - field, and should be in the range of -6 to +6, inclusive. The actual alpha - offset FilterOffsetA is twice this value. - Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA (integer)`` - Loop filter beta coefficient, defined in the H264 standard. - This corresponds to the slice_beta_offset_div2 slice header field, and - should be in the range of -6 to +6, inclusive. The actual beta offset - FilterOffsetB is twice this value. - Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-entropy-mode: - -``V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE`` - (enum) - -enum v4l2_mpeg_video_h264_entropy_mode - - Entropy coding mode for H264 - CABAC/CAVALC. Applicable to the H264 - encoder. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC`` - - Use CAVLC entropy coding. - * - ``V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC`` - - Use CABAC entropy coding. - - - -``V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM (boolean)`` - Enable 8X8 transform for H264. Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (boolean)`` - Enable constrained intra prediction for H264. Applicable to the H264 - encoder. - -``V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET (integer)`` - Specify the offset that should be added to the luma quantization - parameter to determine the chroma quantization parameter. Applicable - to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (integer)`` - Cyclic intra macroblock refresh. This is the number of continuous - macroblocks refreshed every frame. Each frame a successive set of - macroblocks is refreshed until the cycle completes and starts from - the top of the frame. Applicable to H264, H263 and MPEG4 encoder. - -``V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE (boolean)`` - Frame level rate control enable. If this control is disabled then - the quantization parameter for each frame type is constant and set - with appropriate controls (e.g. - ``V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP``). If frame rate control is - enabled then quantization parameter is adjusted to meet the chosen - bitrate. Minimum and maximum value for the quantization parameter - can be set with appropriate controls (e.g. - ``V4L2_CID_MPEG_VIDEO_H263_MIN_QP``). Applicable to encoders. - -``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE (boolean)`` - Macroblock level rate control enable. Applicable to the MPEG4 and - H264 encoders. - -``V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (boolean)`` - Quarter pixel motion estimation for MPEG4. Applicable to the MPEG4 - encoder. - -``V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (integer)`` - Quantization parameter for an I frame for H263. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_H263_MIN_QP (integer)`` - Minimum quantization parameter for H263. Valid range: from 1 to 31. - -``V4L2_CID_MPEG_VIDEO_H263_MAX_QP (integer)`` - Maximum quantization parameter for H263. Valid range: from 1 to 31. - -``V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (integer)`` - Quantization parameter for an P frame for H263. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP (integer)`` - Quantization parameter for an B frame for H263. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP (integer)`` - Quantization parameter for an I frame for H264. Valid range: from 0 - to 51. - -``V4L2_CID_MPEG_VIDEO_H264_MIN_QP (integer)`` - Minimum quantization parameter for H264. Valid range: from 0 to 51. - -``V4L2_CID_MPEG_VIDEO_H264_MAX_QP (integer)`` - Maximum quantization parameter for H264. Valid range: from 0 to 51. - -``V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP (integer)`` - Quantization parameter for an P frame for H264. Valid range: from 0 - to 51. - -``V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP (integer)`` - Quantization parameter for an B frame for H264. Valid range: from 0 - to 51. - -``V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (integer)`` - Quantization parameter for an I frame for MPEG4. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP (integer)`` - Minimum quantization parameter for MPEG4. Valid range: from 1 to 31. - -``V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP (integer)`` - Maximum quantization parameter for MPEG4. Valid range: from 1 to 31. - -``V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (integer)`` - Quantization parameter for an P frame for MPEG4. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (integer)`` - Quantization parameter for an B frame for MPEG4. Valid range: from 1 - to 31. - -``V4L2_CID_MPEG_VIDEO_VBV_SIZE (integer)`` - The Video Buffer Verifier size in kilobytes, it is used as a - limitation of frame skip. The VBV is defined in the standard as a - mean to verify that the produced stream will be successfully - decoded. The standard describes it as "Part of a hypothetical - decoder that is conceptually connected to the output of the encoder. - Its purpose is to provide a constraint on the variability of the - data rate that an encoder or editing process may produce.". - Applicable to the MPEG1, MPEG2, MPEG4 encoders. - -.. _v4l2-mpeg-video-vbv-delay: - -``V4L2_CID_MPEG_VIDEO_VBV_DELAY (integer)`` - Sets the initial delay in milliseconds for VBV buffer control. - -.. _v4l2-mpeg-video-hor-search-range: - -``V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE (integer)`` - Horizontal search range defines maximum horizontal search area in - pixels to search and match for the present Macroblock (MB) in the - reference picture. This V4L2 control macro is used to set horizontal - search range for motion estimation module in video encoder. - -.. _v4l2-mpeg-video-vert-search-range: - -``V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (integer)`` - Vertical search range defines maximum vertical search area in pixels - to search and match for the present Macroblock (MB) in the reference - picture. This V4L2 control macro is used to set vertical search - range for motion estimation module in video encoder. - -.. _v4l2-mpeg-video-force-key-frame: - -``V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (button)`` - Force a key frame for the next queued buffer. Applicable to - encoders. This is a general, codec-agnostic keyframe control. - -``V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE (integer)`` - The Coded Picture Buffer size in kilobytes, it is used as a - limitation of frame skip. The CPB is defined in the H264 standard as - a mean to verify that the produced stream will be successfully - decoded. Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_I_PERIOD (integer)`` - Period between I-frames in the open GOP for H264. In case of an open - GOP this is the period between two I-frames. The period between IDR - (Instantaneous Decoding Refresh) frames is taken from the GOP_SIZE - control. An IDR frame, which stands for Instantaneous Decoding - Refresh is an I-frame after which no prior frames are referenced. - This means that a stream can be restarted from an IDR frame without - the need to store or decode any previous frames. Applicable to the - H264 encoder. - -.. _v4l2-mpeg-video-header-mode: - -``V4L2_CID_MPEG_VIDEO_HEADER_MODE`` - (enum) - -enum v4l2_mpeg_video_header_mode - - Determines whether the header is returned as the first buffer or is - it returned together with the first frame. Applicable to encoders. - Possible values are: - - - -.. tabularcolumns:: |p{10.3cm}|p{7.2cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE`` - - The stream header is returned separately in the first buffer. - * - ``V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME`` - - The stream header is returned together with the first encoded - frame. - - - -``V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER (boolean)`` - Repeat the video sequence headers. Repeating these headers makes - random access to the video stream easier. Applicable to the MPEG1, 2 - and 4 encoder. - -``V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER (boolean)`` - Enabled the deblocking post processing filter for MPEG4 decoder. - Applicable to the MPEG4 decoder. - -``V4L2_CID_MPEG_VIDEO_MPEG4_VOP_TIME_RES (integer)`` - vop_time_increment_resolution value for MPEG4. Applicable to the - MPEG4 encoder. - -``V4L2_CID_MPEG_VIDEO_MPEG4_VOP_TIME_INC (integer)`` - vop_time_increment value for MPEG4. Applicable to the MPEG4 - encoder. - -``V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING (boolean)`` - Enable generation of frame packing supplemental enhancement - information in the encoded bitstream. The frame packing SEI message - contains the arrangement of L and R planes for 3D viewing. - Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_SEI_FP_CURRENT_FRAME_0 (boolean)`` - Sets current frame as frame0 in frame packing SEI. Applicable to the - H264 encoder. - -.. _v4l2-mpeg-video-h264-sei-fp-arrangement-type: - -``V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE`` - (enum) - -enum v4l2_mpeg_video_h264_sei_fp_arrangement_type - - Frame packing arrangement type for H264 SEI. Applicable to the H264 - encoder. Possible values are: - -.. tabularcolumns:: |p{12cm}|p{5.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_CHEKERBOARD`` - - Pixels are alternatively from L and R. - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_COLUMN`` - - L and R are interlaced by column. - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_ROW`` - - L and R are interlaced by row. - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_SIDE_BY_SIDE`` - - L is on the left, R on the right. - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TOP_BOTTOM`` - - L is on top, R on bottom. - * - ``V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TEMPORAL`` - - One view per frame. - - - -``V4L2_CID_MPEG_VIDEO_H264_FMO (boolean)`` - Enables flexible macroblock ordering in the encoded bitstream. It is - a technique used for restructuring the ordering of macroblocks in - pictures. Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-fmo-map-type: - -``V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE`` - (enum) - -enum v4l2_mpeg_video_h264_fmo_map_type - - When using FMO, the map type divides the image in different scan - patterns of macroblocks. Applicable to the H264 encoder. Possible - values are: - -.. tabularcolumns:: |p{12.5cm}|p{5.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES`` - - Slices are interleaved one after other with macroblocks in run - length order. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES`` - - Scatters the macroblocks based on a mathematical function known to - both encoder and decoder. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_FOREGROUND_WITH_LEFT_OVER`` - - Macroblocks arranged in rectangular areas or regions of interest. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_BOX_OUT`` - - Slice groups grow in a cyclic way from centre to outwards. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_RASTER_SCAN`` - - Slice groups grow in raster scan pattern from left to right. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN`` - - Slice groups grow in wipe scan pattern from top to bottom. - * - ``V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_EXPLICIT`` - - User defined map type. - - - -``V4L2_CID_MPEG_VIDEO_H264_FMO_SLICE_GROUP (integer)`` - Number of slice groups in FMO. Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-fmo-change-direction: - -``V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_DIRECTION`` - (enum) - -enum v4l2_mpeg_video_h264_fmo_change_dir - - Specifies a direction of the slice group change for raster and wipe - maps. Applicable to the H264 encoder. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_RIGHT`` - - Raster scan or wipe right. - * - ``V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_LEFT`` - - Reverse raster scan or wipe left. - - - -``V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_RATE (integer)`` - Specifies the size of the first slice group for raster and wipe map. - Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_FMO_RUN_LENGTH (integer)`` - Specifies the number of consecutive macroblocks for the interleaved - map. Applicable to the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_ASO (boolean)`` - Enables arbitrary slice ordering in encoded bitstream. Applicable to - the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_ASO_SLICE_ORDER (integer)`` - Specifies the slice order in ASO. Applicable to the H264 encoder. - The supplied 32-bit integer is interpreted as follows (bit 0 = least - significant bit): - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - Bit 0:15 - - Slice ID - * - Bit 16:32 - - Slice position or order - - - -``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING (boolean)`` - Enables H264 hierarchical coding. Applicable to the H264 encoder. - -.. _v4l2-mpeg-video-h264-hierarchical-coding-type: - -``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE`` - (enum) - -enum v4l2_mpeg_video_h264_hierarchical_coding_type - - Specifies the hierarchical coding type. Applicable to the H264 - encoder. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B`` - - Hierarchical B coding. - * - ``V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P`` - - Hierarchical P coding. - - - -``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER (integer)`` - Specifies the number of hierarchical coding layers. Applicable to - the H264 encoder. - -``V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP (integer)`` - Specifies a user defined QP for each layer. Applicable to the H264 - encoder. The supplied 32-bit integer is interpreted as follows (bit - 0 = least significant bit): - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - Bit 0:15 - - QP value - * - Bit 16:32 - - Layer number - - - -.. _v4l2-mpeg-mpeg2: - -``V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS (struct)`` - Specifies the slice parameters (as extracted from the bitstream) for the - associated MPEG-2 slice data. This includes the necessary parameters for - configuring a stateless hardware decoding pipeline for MPEG-2. - The bitstream parameters are defined according to :ref:`mpeg2part2`. - - .. note:: - - This compound control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_ctrl_mpeg2_slice_params - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_ctrl_mpeg2_slice_params - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u32 - - ``bit_size`` - - Size (in bits) of the current slice data. - * - __u32 - - ``data_bit_offset`` - - Offset (in bits) to the video data in the current slice data. - * - struct :c:type:`v4l2_mpeg2_sequence` - - ``sequence`` - - Structure with MPEG-2 sequence metadata, merging relevant fields from - the sequence header and sequence extension parts of the bitstream. - * - struct :c:type:`v4l2_mpeg2_picture` - - ``picture`` - - Structure with MPEG-2 picture metadata, merging relevant fields from - the picture header and picture coding extension parts of the bitstream. - * - __u64 - - ``backward_ref_ts`` - - Timestamp of the V4L2 capture buffer to use as backward reference, used - with B-coded and P-coded frames. The timestamp refers to the - ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the - :c:func:`v4l2_timeval_to_ns()` function to convert the struct - :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. - * - __u64 - - ``forward_ref_ts`` - - Timestamp for the V4L2 capture buffer to use as forward reference, used - with B-coded frames. The timestamp refers to the ``timestamp`` field in - struct :c:type:`v4l2_buffer`. Use the :c:func:`v4l2_timeval_to_ns()` - function to convert the struct :c:type:`timeval` in struct - :c:type:`v4l2_buffer` to a __u64. - * - __u32 - - ``quantiser_scale_code`` - - Code used to determine the quantization scale to use for the IDCT. - -.. c:type:: v4l2_mpeg2_sequence - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_mpeg2_sequence - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u16 - - ``horizontal_size`` - - The width of the displayable part of the frame's luminance component. - * - __u16 - - ``vertical_size`` - - The height of the displayable part of the frame's luminance component. - * - __u32 - - ``vbv_buffer_size`` - - Used to calculate the required size of the video buffering verifier, - defined (in bits) as: 16 * 1024 * vbv_buffer_size. - * - __u16 - - ``profile_and_level_indication`` - - The current profile and level indication as extracted from the - bitstream. - * - __u8 - - ``progressive_sequence`` - - Indication that all the frames for the sequence are progressive instead - of interlaced. - * - __u8 - - ``chroma_format`` - - The chrominance sub-sampling format (1: 4:2:0, 2: 4:2:2, 3: 4:4:4). - -.. c:type:: v4l2_mpeg2_picture - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_mpeg2_picture - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u8 - - ``picture_coding_type`` - - Picture coding type for the frame covered by the current slice - (V4L2_MPEG2_PICTURE_CODING_TYPE_I, V4L2_MPEG2_PICTURE_CODING_TYPE_P or - V4L2_MPEG2_PICTURE_CODING_TYPE_B). - * - __u8 - - ``f_code[2][2]`` - - Motion vector codes. - * - __u8 - - ``intra_dc_precision`` - - Precision of Discrete Cosine transform (0: 8 bits precision, - 1: 9 bits precision, 2: 10 bits precision, 3: 11 bits precision). - * - __u8 - - ``picture_structure`` - - Picture structure (1: interlaced top field, 2: interlaced bottom field, - 3: progressive frame). - * - __u8 - - ``top_field_first`` - - If set to 1 and interlaced stream, top field is output first. - * - __u8 - - ``frame_pred_frame_dct`` - - If set to 1, only frame-DCT and frame prediction are used. - * - __u8 - - ``concealment_motion_vectors`` - - If set to 1, motion vectors are coded for intra macroblocks. - * - __u8 - - ``q_scale_type`` - - This flag affects the inverse quantization process. - * - __u8 - - ``intra_vlc_format`` - - This flag affects the decoding of transform coefficient data. - * - __u8 - - ``alternate_scan`` - - This flag affects the decoding of transform coefficient data. - * - __u8 - - ``repeat_first_field`` - - This flag affects the decoding process of progressive frames. - * - __u16 - - ``progressive_frame`` - - Indicates whether the current frame is progressive. - -``V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION (struct)`` - Specifies quantization matrices (as extracted from the bitstream) for the - associated MPEG-2 slice data. - - .. note:: - - This compound control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_ctrl_mpeg2_quantization - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_ctrl_mpeg2_quantization - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u8 - - ``load_intra_quantiser_matrix`` - - One bit to indicate whether to load the ``intra_quantiser_matrix`` data. - * - __u8 - - ``load_non_intra_quantiser_matrix`` - - One bit to indicate whether to load the ``non_intra_quantiser_matrix`` - data. - * - __u8 - - ``load_chroma_intra_quantiser_matrix`` - - One bit to indicate whether to load the - ``chroma_intra_quantiser_matrix`` data, only relevant for non-4:2:0 YUV - formats. - * - __u8 - - ``load_chroma_non_intra_quantiser_matrix`` - - One bit to indicate whether to load the - ``chroma_non_intra_quantiser_matrix`` data, only relevant for non-4:2:0 - YUV formats. - * - __u8 - - ``intra_quantiser_matrix[64]`` - - The quantization matrix coefficients for intra-coded frames, in zigzag - scanning order. It is relevant for both luma and chroma components, - although it can be superseded by the chroma-specific matrix for - non-4:2:0 YUV formats. - * - __u8 - - ``non_intra_quantiser_matrix[64]`` - - The quantization matrix coefficients for non-intra-coded frames, in - zigzag scanning order. It is relevant for both luma and chroma - components, although it can be superseded by the chroma-specific matrix - for non-4:2:0 YUV formats. - * - __u8 - - ``chroma_intra_quantiser_matrix[64]`` - - The quantization matrix coefficients for the chominance component of - intra-coded frames, in zigzag scanning order. Only relevant for - non-4:2:0 YUV formats. - * - __u8 - - ``chroma_non_intra_quantiser_matrix[64]`` - - The quantization matrix coefficients for the chrominance component of - non-intra-coded frames, in zigzag scanning order. Only relevant for - non-4:2:0 YUV formats. - -MFC 5.1 MPEG Controls ---------------------- - -The following MPEG class controls deal with MPEG decoding and encoding -settings that are specific to the Multi Format Codec 5.1 device present -in the S5P family of SoCs by Samsung. - - -.. _mfc51-control-id: - -MFC 5.1 Control IDs -^^^^^^^^^^^^^^^^^^^ - -``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE (boolean)`` - If the display delay is enabled then the decoder is forced to return - a CAPTURE buffer (decoded frame) after processing a certain number - of OUTPUT buffers. The delay can be set through - ``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY``. This - feature can be used for example for generating thumbnails of videos. - Applicable to the H264 decoder. - -``V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY (integer)`` - Display delay value for H264 decoder. The decoder is forced to - return a decoded frame after the set 'display delay' number of - frames. If this number is low it may result in frames returned out - of dispaly order, in addition the hardware may still be using the - returned buffer as a reference picture for subsequent frames. - -``V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P (integer)`` - The number of reference pictures used for encoding a P picture. - Applicable to the H264 encoder. - -``V4L2_CID_MPEG_MFC51_VIDEO_PADDING (boolean)`` - Padding enable in the encoder - use a color instead of repeating - border pixels. Applicable to encoders. - -``V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV (integer)`` - Padding color in the encoder. Applicable to encoders. The supplied - 32-bit integer is interpreted as follows (bit 0 = least significant - bit): - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - Bit 0:7 - - V chrominance information - * - Bit 8:15 - - U chrominance information - * - Bit 16:23 - - Y luminance information - * - Bit 24:31 - - Must be zero. - - - -``V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF (integer)`` - Reaction coefficient for MFC rate control. Applicable to encoders. - - .. note:: - - #. Valid only when the frame level RC is enabled. - - #. For tight CBR, this field must be small (ex. 2 ~ 10). For - VBR, this field must be large (ex. 100 ~ 1000). - - #. It is not recommended to use the greater number than - FRAME_RATE * (10^9 / BIT_RATE). - -``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK (boolean)`` - Adaptive rate control for dark region. Valid only when H.264 and - macroblock level RC is enabled - (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 - encoder. - -``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH (boolean)`` - Adaptive rate control for smooth region. Valid only when H.264 and - macroblock level RC is enabled - (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 - encoder. - -``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC (boolean)`` - Adaptive rate control for static region. Valid only when H.264 and - macroblock level RC is enabled - (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 - encoder. - -``V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY (boolean)`` - Adaptive rate control for activity region. Valid only when H.264 and - macroblock level RC is enabled - (``V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE``). Applicable to the H264 - encoder. - -.. _v4l2-mpeg-mfc51-video-frame-skip-mode: - -``V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE`` - (enum) - -enum v4l2_mpeg_mfc51_video_frame_skip_mode - - Indicates in what conditions the encoder should skip frames. If - encoding a frame would cause the encoded stream to be larger then a - chosen data limit then the frame will be skipped. Possible values - are: - - -.. tabularcolumns:: |p{9.0cm}|p{8.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_DISABLED`` - - Frame skip mode is disabled. - * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_LEVEL_LIMIT`` - - Frame skip mode enabled and buffer limit is set by the chosen - level and is defined by the standard. - * - ``V4L2_MPEG_MFC51_FRAME_SKIP_MODE_BUF_LIMIT`` - - Frame skip mode enabled and buffer limit is set by the VBV - (MPEG1/2/4) or CPB (H264) buffer size control. - - - -``V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT (integer)`` - Enable rate-control with fixed target bit. If this setting is - enabled, then the rate control logic of the encoder will calculate - the average bitrate for a GOP and keep it below or equal the set - bitrate target. Otherwise the rate control logic calculates the - overall average bitrate for the stream and keeps it below or equal - to the set bitrate. In the first case the average bitrate for the - whole stream will be smaller then the set bitrate. This is caused - because the average is calculated for smaller number of frames, on - the other hand enabling this setting will ensure that the stream - will meet tight bandwidth constraints. Applicable to encoders. - -.. _v4l2-mpeg-mfc51-video-force-frame-type: - -``V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE`` - (enum) - -enum v4l2_mpeg_mfc51_video_force_frame_type - - Force a frame type for the next queued buffer. Applicable to - encoders. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_DISABLED`` - - Forcing a specific frame type disabled. - * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_I_FRAME`` - - Force an I-frame. - * - ``V4L2_MPEG_MFC51_FORCE_FRAME_TYPE_NOT_CODED`` - - Force a non-coded frame. - - - - -CX2341x MPEG Controls ---------------------- - -The following MPEG class controls deal with MPEG encoding settings that -are specific to the Conexant CX23415 and CX23416 MPEG encoding chips. - - -.. _cx2341x-control-id: - -CX2341x Control IDs -^^^^^^^^^^^^^^^^^^^ - -.. _v4l2-mpeg-cx2341x-video-spatial-filter-mode: - -``V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE`` - (enum) - -enum v4l2_mpeg_cx2341x_video_spatial_filter_mode - - Sets the Spatial Filter mode (default ``MANUAL``). Possible values - are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL`` - - Choose the filter manually - * - ``V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO`` - - Choose the filter automatically - - - -``V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER (integer (0-15))`` - The setting for the Spatial Filter. 0 = off, 15 = maximum. (Default - is 0.) - -.. _luma-spatial-filter-type: - -``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE`` - (enum) - -enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type - - Select the algorithm to use for the Luma Spatial Filter (default - ``1D_HOR``). Possible values: - - - -.. tabularcolumns:: |p{14.5cm}|p{3.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF`` - - No filter - * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR`` - - One-dimensional horizontal - * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT`` - - One-dimensional vertical - * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE`` - - Two-dimensional separable - * - ``V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE`` - - Two-dimensional symmetrical non-separable - - - -.. _chroma-spatial-filter-type: - -``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE`` - (enum) - -enum v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type - - Select the algorithm for the Chroma Spatial Filter (default - ``1D_HOR``). Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF`` - - No filter - * - ``V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR`` - - One-dimensional horizontal - - - -.. _v4l2-mpeg-cx2341x-video-temporal-filter-mode: - -``V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE`` - (enum) - -enum v4l2_mpeg_cx2341x_video_temporal_filter_mode - - Sets the Temporal Filter mode (default ``MANUAL``). Possible values - are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL`` - - Choose the filter manually - * - ``V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO`` - - Choose the filter automatically - - - -``V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER (integer (0-31))`` - The setting for the Temporal Filter. 0 = off, 31 = maximum. (Default - is 8 for full-scale capturing and 0 for scaled capturing.) - -.. _v4l2-mpeg-cx2341x-video-median-filter-type: - -``V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE`` - (enum) - -enum v4l2_mpeg_cx2341x_video_median_filter_type - - Median Filter Type (default ``OFF``). Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF`` - - No filter - * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR`` - - Horizontal filter - * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT`` - - Vertical filter - * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT`` - - Horizontal and vertical filter - * - ``V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG`` - - Diagonal filter - - - -``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM (integer (0-255))`` - Threshold above which the luminance median filter is enabled - (default 0) - -``V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP (integer (0-255))`` - Threshold below which the luminance median filter is enabled - (default 255) - -``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (integer (0-255))`` - Threshold above which the chroma median filter is enabled (default - 0) - -``V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (integer (0-255))`` - Threshold below which the chroma median filter is enabled (default - 255) - -``V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS (boolean)`` - The CX2341X MPEG encoder can insert one empty MPEG-2 PES packet into - the stream between every four video frames. The packet size is 2048 - bytes, including the packet_start_code_prefix and stream_id - fields. The stream_id is 0xBF (private stream 2). The payload - consists of 0x00 bytes, to be filled in by the application. 0 = do - not insert, 1 = insert packets. - - -VPX Control Reference ---------------------- - -The VPX controls include controls for encoding parameters of VPx video -codec. - - -.. _vpx-control-id: - -VPX Control IDs -^^^^^^^^^^^^^^^ - -.. _v4l2-vpx-num-partitions: - -``V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS`` - (enum) - -enum v4l2_vp8_num_partitions - - The number of token partitions to use in VP8 encoder. Possible - values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION`` - - 1 coefficient partition - * - ``V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS`` - - 2 coefficient partitions - * - ``V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS`` - - 4 coefficient partitions - * - ``V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS`` - - 8 coefficient partitions - - - -``V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 (boolean)`` - Setting this prevents intra 4x4 mode in the intra mode decision. - -.. _v4l2-vpx-num-ref-frames: - -``V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES`` - (enum) - -enum v4l2_vp8_num_ref_frames - - The number of reference pictures for encoding P frames. Possible - values are: - -.. tabularcolumns:: |p{7.9cm}|p{9.6cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME`` - - Last encoded frame will be searched - * - ``V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME`` - - Two frames will be searched among the last encoded frame, the - golden frame and the alternate reference (altref) frame. The - encoder implementation will decide which two are chosen. - * - ``V4L2_CID_MPEG_VIDEO_VPX_3_REF_FRAME`` - - The last encoded frame, the golden frame and the altref frame will - be searched. - - - -``V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL (integer)`` - Indicates the loop filter level. The adjustment of the loop filter - level is done via a delta value against a baseline loop filter - value. - -``V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS (integer)`` - This parameter affects the loop filter. Anything above zero weakens - the deblocking effect on the loop filter. - -``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD (integer)`` - Sets the refresh period for the golden frame. The period is defined - in number of frames. For a value of 'n', every nth frame starting - from the first key frame will be taken as a golden frame. For eg. - for encoding sequence of 0, 1, 2, 3, 4, 5, 6, 7 where the golden - frame refresh period is set as 4, the frames 0, 4, 8 etc will be - taken as the golden frames as frame 0 is always a key frame. - -.. _v4l2-vpx-golden-frame-sel: - -``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL`` - (enum) - -enum v4l2_vp8_golden_frame_sel - - Selects the golden frame for encoding. Possible values are: - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV`` - - Use the (n-2)th frame as a golden frame, current frame index being - 'n'. - * - ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD`` - - Use the previous specific frame indicated by - ``V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD`` as a - golden frame. - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_VPX_MIN_QP (integer)`` - Minimum quantization parameter for VP8. - -``V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (integer)`` - Maximum quantization parameter for VP8. - -``V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (integer)`` - Quantization parameter for an I frame for VP8. - -``V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (integer)`` - Quantization parameter for a P frame for VP8. - -.. _v4l2-mpeg-video-vp8-profile: - -``V4L2_CID_MPEG_VIDEO_VP8_PROFILE`` - (enum) - -enum v4l2_mpeg_video_vp8_profile - - This control allows selecting the profile for VP8 encoder. - This is also used to enumerate supported profiles by VP8 encoder or decoder. - Possible values are: - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_0`` - - Profile 0 - * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_1`` - - Profile 1 - * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_2`` - - Profile 2 - * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_3`` - - Profile 3 - -.. _v4l2-mpeg-video-vp9-profile: - -``V4L2_CID_MPEG_VIDEO_VP9_PROFILE`` - (enum) - -enum v4l2_mpeg_video_vp9_profile - - This control allows selecting the profile for VP9 encoder. - This is also used to enumerate supported profiles by VP9 encoder or decoder. - Possible values are: - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_0`` - - Profile 0 - * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_1`` - - Profile 1 - * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_2`` - - Profile 2 - * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_3`` - - Profile 3 - - -High Efficiency Video Coding (HEVC/H.265) Control Reference ------------------------------------------------------------ - -The HEVC/H.265 controls include controls for encoding parameters of HEVC/H.265 -video codec. - - -.. _hevc-control-id: - -HEVC/H.265 Control IDs -^^^^^^^^^^^^^^^^^^^^^^ - -``V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP (integer)`` - Minimum quantization parameter for HEVC. - Valid range: from 0 to 51. - -``V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP (integer)`` - Maximum quantization parameter for HEVC. - Valid range: from 0 to 51. - -``V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP (integer)`` - Quantization parameter for an I frame for HEVC. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP (integer)`` - Quantization parameter for a P frame for HEVC. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP (integer)`` - Quantization parameter for a B frame for HEVC. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP (boolean)`` - HIERARCHICAL_QP allows the host to specify the quantization parameter - values for each temporal layer through HIERARCHICAL_QP_LAYER. This is - valid only if HIERARCHICAL_CODING_LAYER is greater than 1. Setting the - control value to 1 enables setting of the QP values for the layers. - -.. _v4l2-hevc-hier-coding-type: - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE`` - (enum) - -enum v4l2_mpeg_video_hevc_hier_coding_type - - Selects the hierarchical coding type for encoding. Possible values are: - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B`` - - Use the B frame for hierarchical coding. - * - ``V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P`` - - Use the P frame for hierarchical coding. - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER (integer)`` - Selects the hierarchical coding layer. In normal encoding - (non-hierarchial coding), it should be zero. Possible values are [0, 6]. - 0 indicates HIERARCHICAL CODING LAYER 0, 1 indicates HIERARCHICAL CODING - LAYER 1 and so on. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 0. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 1. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 2. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 3. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 4. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 5. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP (integer)`` - Indicates quantization parameter for hierarchical coding layer 6. - Valid range: [V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, - V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP]. - -.. _v4l2-hevc-profile: - -``V4L2_CID_MPEG_VIDEO_HEVC_PROFILE`` - (enum) - -enum v4l2_mpeg_video_hevc_profile - - Select the desired profile for HEVC encoder. - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN`` - - Main profile. - * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE`` - - Main still picture profile. - * - ``V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10`` - - Main 10 profile. - -.. raw:: latex - - \normalsize - - -.. _v4l2-hevc-level: - -``V4L2_CID_MPEG_VIDEO_HEVC_LEVEL`` - (enum) - -enum v4l2_mpeg_video_hevc_level - - Selects the desired level for HEVC encoder. - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_1`` - - Level 1.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_2`` - - Level 2.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1`` - - Level 2.1 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_3`` - - Level 3.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1`` - - Level 3.1 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_4`` - - Level 4.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1`` - - Level 4.1 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5`` - - Level 5.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1`` - - Level 5.1 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2`` - - Level 5.2 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6`` - - Level 6.0 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1`` - - Level 6.1 - * - ``V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2`` - - Level 6.2 - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION (integer)`` - Indicates the number of evenly spaced subintervals, called ticks, within - one second. This is a 16 bit unsigned integer and has a maximum value up to - 0xffff and a minimum value of 1. - -.. _v4l2-hevc-tier: - -``V4L2_CID_MPEG_VIDEO_HEVC_TIER`` - (enum) - -enum v4l2_mpeg_video_hevc_tier - - TIER_FLAG specifies tiers information of the HEVC encoded picture. Tier - were made to deal with applications that differ in terms of maximum bit - rate. Setting the flag to 0 selects HEVC tier as Main tier and setting - this flag to 1 indicates High tier. High tier is for applications requiring - high bit rates. - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{9.0cm}|p{8.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_TIER_MAIN`` - - Main tier. - * - ``V4L2_MPEG_VIDEO_HEVC_TIER_HIGH`` - - High tier. - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH (integer)`` - Selects HEVC maximum coding unit depth. - -.. _v4l2-hevc-loop-filter-mode: - -``V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE`` - (enum) - -enum v4l2_mpeg_video_hevc_loop_filter_mode - - Loop filter mode for HEVC encoder. Possible values are: - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{10.7cm}|p{6.3cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED`` - - Loop filter is disabled. - * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED`` - - Loop filter is enabled. - * - ``V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY`` - - Loop filter is disabled at the slice boundary. - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2 (integer)`` - Selects HEVC loop filter beta offset. The valid range is [-6, +6]. - -``V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2 (integer)`` - Selects HEVC loop filter tc offset. The valid range is [-6, +6]. - -.. _v4l2-hevc-refresh-type: - -``V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE`` - (enum) - -enum v4l2_mpeg_video_hevc_hier_refresh_type - - Selects refresh type for HEVC encoder. - Host has to specify the period into - V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD. - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{8.0cm}|p{9.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE`` - - Use the B frame for hierarchical coding. - * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA`` - - Use CRA (Clean Random Access Unit) picture encoding. - * - ``V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR`` - - Use IDR (Instantaneous Decoding Refresh) picture encoding. - -.. raw:: latex - - \normalsize - - -``V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD (integer)`` - Selects the refresh period for HEVC encoder. - This specifies the number of I pictures between two CRA/IDR pictures. - This is valid only if REFRESH_TYPE is not 0. - -``V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU (boolean)`` - Indicates HEVC lossless encoding. Setting it to 0 disables lossless - encoding. Setting it to 1 enables lossless encoding. - -``V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED (boolean)`` - Indicates constant intra prediction for HEVC encoder. Specifies the - constrained intra prediction in which intra largest coding unit (LCU) - prediction is performed by using residual data and decoded samples of - neighboring intra LCU only. Setting the value to 1 enables constant intra - prediction and setting the value to 0 disables constant intra prediction. - -``V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT (boolean)`` - Indicates wavefront parallel processing for HEVC encoder. Setting it to 0 - disables the feature and setting it to 1 enables the wavefront parallel - processing. - -``V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB (boolean)`` - Setting the value to 1 enables combination of P and B frame for HEVC - encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID (boolean)`` - Indicates temporal identifier for HEVC encoder which is enabled by - setting the value to 1. - -``V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING (boolean)`` - Indicates bi-linear interpolation is conditionally used in the intra - prediction filtering process in the CVS when set to 1. Indicates bi-linear - interpolation is not used in the CVS when set to 0. - -``V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1 (integer)`` - Indicates maximum number of merge candidate motion vectors. - Values are from 0 to 4. - -``V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION (boolean)`` - Indicates temporal motion vector prediction for HEVC encoder. Setting it to - 1 enables the prediction. Setting it to 0 disables the prediction. - -``V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE (boolean)`` - Specifies if HEVC generates a stream with a size of the length field - instead of start code pattern. The size of the length field is configurable - through the V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD control. Setting - the value to 0 disables encoding without startcode pattern. Setting the - value to 1 will enables encoding without startcode pattern. - -.. _v4l2-hevc-size-of-length-field: - -``V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD`` -(enum) - -enum v4l2_mpeg_video_hevc_size_of_length_field - - Indicates the size of length field. - This is valid when encoding WITHOUT_STARTCODE_ENABLE is enabled. - -.. raw:: latex - - \footnotesize - -.. tabularcolumns:: |p{6.0cm}|p{11.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_0`` - - Generate start code pattern (Normal). - * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_1`` - - Generate size of length field instead of start code pattern and length is 1. - * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_2`` - - Generate size of length field instead of start code pattern and length is 2. - * - ``V4L2_MPEG_VIDEO_HEVC_SIZE_4`` - - Generate size of length field instead of start code pattern and length is 4. - -.. raw:: latex - - \normalsize - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR (integer)`` - Indicates bit rate for hierarchical coding layer 0 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR (integer)`` - Indicates bit rate for hierarchical coding layer 1 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR (integer)`` - Indicates bit rate for hierarchical coding layer 2 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR (integer)`` - Indicates bit rate for hierarchical coding layer 3 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR (integer)`` - Indicates bit rate for hierarchical coding layer 4 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR (integer)`` - Indicates bit rate for hierarchical coding layer 5 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR (integer)`` - Indicates bit rate for hierarchical coding layer 6 for HEVC encoder. - -``V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES (integer)`` - Selects number of P reference pictures required for HEVC encoder. - P-Frame can use 1 or 2 frames for reference. - -``V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR (integer)`` - Indicates whether to generate SPS and PPS at every IDR. Setting it to 0 - disables generating SPS and PPS at every IDR. Setting it to one enables - generating SPS and PPS at every IDR. - - -.. _camera-controls: - -Camera Control Reference -======================== - -The Camera class includes controls for mechanical (or equivalent -digital) features of a device such as controllable lenses or sensors. - - -.. _camera-control-id: - -Camera Control IDs ------------------- - -``V4L2_CID_CAMERA_CLASS (class)`` - The Camera class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -.. _v4l2-exposure-auto-type: - -``V4L2_CID_EXPOSURE_AUTO`` - (enum) - -enum v4l2_exposure_auto_type - - Enables automatic adjustments of the exposure time and/or iris - aperture. The effect of manual changes of the exposure time or iris - aperture while these features are enabled is undefined, drivers - should ignore such requests. Possible values are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_EXPOSURE_AUTO`` - - Automatic exposure time, automatic iris aperture. - * - ``V4L2_EXPOSURE_MANUAL`` - - Manual exposure time, manual iris. - * - ``V4L2_EXPOSURE_SHUTTER_PRIORITY`` - - Manual exposure time, auto iris. - * - ``V4L2_EXPOSURE_APERTURE_PRIORITY`` - - Auto exposure time, manual iris. - - - -``V4L2_CID_EXPOSURE_ABSOLUTE (integer)`` - Determines the exposure time of the camera sensor. The exposure time - is limited by the frame interval. Drivers should interpret the - values as 100 µs units, where the value 1 stands for 1/10000th of a - second, 10000 for 1 second and 100000 for 10 seconds. - -``V4L2_CID_EXPOSURE_AUTO_PRIORITY (boolean)`` - When ``V4L2_CID_EXPOSURE_AUTO`` is set to ``AUTO`` or - ``APERTURE_PRIORITY``, this control determines if the device may - dynamically vary the frame rate. By default this feature is disabled - (0) and the frame rate must remain constant. - -``V4L2_CID_AUTO_EXPOSURE_BIAS (integer menu)`` - Determines the automatic exposure compensation, it is effective only - when ``V4L2_CID_EXPOSURE_AUTO`` control is set to ``AUTO``, - ``SHUTTER_PRIORITY`` or ``APERTURE_PRIORITY``. It is expressed in - terms of EV, drivers should interpret the values as 0.001 EV units, - where the value 1000 stands for +1 EV. - - Increasing the exposure compensation value is equivalent to - decreasing the exposure value (EV) and will increase the amount of - light at the image sensor. The camera performs the exposure - compensation by adjusting absolute exposure time and/or aperture. - -.. _v4l2-exposure-metering: - -``V4L2_CID_EXPOSURE_METERING`` - (enum) - -enum v4l2_exposure_metering - - Determines how the camera measures the amount of light available for - the frame exposure. Possible values are: - -.. tabularcolumns:: |p{8.5cm}|p{9.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_EXPOSURE_METERING_AVERAGE`` - - Use the light information coming from the entire frame and average - giving no weighting to any particular portion of the metered area. - * - ``V4L2_EXPOSURE_METERING_CENTER_WEIGHTED`` - - Average the light information coming from the entire frame giving - priority to the center of the metered area. - * - ``V4L2_EXPOSURE_METERING_SPOT`` - - Measure only very small area at the center of the frame. - * - ``V4L2_EXPOSURE_METERING_MATRIX`` - - A multi-zone metering. The light intensity is measured in several - points of the frame and the results are combined. The algorithm of - the zones selection and their significance in calculating the - final value is device dependent. - - - -``V4L2_CID_PAN_RELATIVE (integer)`` - This control turns the camera horizontally by the specified amount. - The unit is undefined. A positive value moves the camera to the - right (clockwise when viewed from above), a negative value to the - left. A value of zero does not cause motion. This is a write-only - control. - -``V4L2_CID_TILT_RELATIVE (integer)`` - This control turns the camera vertically by the specified amount. - The unit is undefined. A positive value moves the camera up, a - negative value down. A value of zero does not cause motion. This is - a write-only control. - -``V4L2_CID_PAN_RESET (button)`` - When this control is set, the camera moves horizontally to the - default position. - -``V4L2_CID_TILT_RESET (button)`` - When this control is set, the camera moves vertically to the default - position. - -``V4L2_CID_PAN_ABSOLUTE (integer)`` - This control turns the camera horizontally to the specified - position. Positive values move the camera to the right (clockwise - when viewed from above), negative values to the left. Drivers should - interpret the values as arc seconds, with valid values between -180 - * 3600 and +180 * 3600 inclusive. - -``V4L2_CID_TILT_ABSOLUTE (integer)`` - This control turns the camera vertically to the specified position. - Positive values move the camera up, negative values down. Drivers - should interpret the values as arc seconds, with valid values - between -180 * 3600 and +180 * 3600 inclusive. - -``V4L2_CID_FOCUS_ABSOLUTE (integer)`` - This control sets the focal point of the camera to the specified - position. The unit is undefined. Positive values set the focus - closer to the camera, negative values towards infinity. - -``V4L2_CID_FOCUS_RELATIVE (integer)`` - This control moves the focal point of the camera by the specified - amount. The unit is undefined. Positive values move the focus closer - to the camera, negative values towards infinity. This is a - write-only control. - -``V4L2_CID_FOCUS_AUTO (boolean)`` - Enables continuous automatic focus adjustments. The effect of manual - focus adjustments while this feature is enabled is undefined, - drivers should ignore such requests. - -``V4L2_CID_AUTO_FOCUS_START (button)`` - Starts single auto focus process. The effect of setting this control - when ``V4L2_CID_FOCUS_AUTO`` is set to ``TRUE`` (1) is undefined, - drivers should ignore such requests. - -``V4L2_CID_AUTO_FOCUS_STOP (button)`` - Aborts automatic focusing started with ``V4L2_CID_AUTO_FOCUS_START`` - control. It is effective only when the continuous autofocus is - disabled, that is when ``V4L2_CID_FOCUS_AUTO`` control is set to - ``FALSE`` (0). - -.. _v4l2-auto-focus-status: - -``V4L2_CID_AUTO_FOCUS_STATUS (bitmask)`` - The automatic focus status. This is a read-only control. - - Setting ``V4L2_LOCK_FOCUS`` lock bit of the ``V4L2_CID_3A_LOCK`` - control may stop updates of the ``V4L2_CID_AUTO_FOCUS_STATUS`` - control value. - -.. tabularcolumns:: |p{6.5cm}|p{11.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_AUTO_FOCUS_STATUS_IDLE`` - - Automatic focus is not active. - * - ``V4L2_AUTO_FOCUS_STATUS_BUSY`` - - Automatic focusing is in progress. - * - ``V4L2_AUTO_FOCUS_STATUS_REACHED`` - - Focus has been reached. - * - ``V4L2_AUTO_FOCUS_STATUS_FAILED`` - - Automatic focus has failed, the driver will not transition from - this state until another action is performed by an application. - - - -.. _v4l2-auto-focus-range: - -``V4L2_CID_AUTO_FOCUS_RANGE`` - (enum) - -enum v4l2_auto_focus_range - - Determines auto focus distance range for which lens may be adjusted. - -.. tabularcolumns:: |p{6.5cm}|p{11.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_AUTO_FOCUS_RANGE_AUTO`` - - The camera automatically selects the focus range. - * - ``V4L2_AUTO_FOCUS_RANGE_NORMAL`` - - Normal distance range, limited for best automatic focus - performance. - * - ``V4L2_AUTO_FOCUS_RANGE_MACRO`` - - Macro (close-up) auto focus. The camera will use its minimum - possible distance for auto focus. - * - ``V4L2_AUTO_FOCUS_RANGE_INFINITY`` - - The lens is set to focus on an object at infinite distance. - - - -``V4L2_CID_ZOOM_ABSOLUTE (integer)`` - Specify the objective lens focal length as an absolute value. The - zoom unit is driver-specific and its value should be a positive - integer. - -``V4L2_CID_ZOOM_RELATIVE (integer)`` - Specify the objective lens focal length relatively to the current - value. Positive values move the zoom lens group towards the - telephoto direction, negative values towards the wide-angle - direction. The zoom unit is driver-specific. This is a write-only - control. - -``V4L2_CID_ZOOM_CONTINUOUS (integer)`` - Move the objective lens group at the specified speed until it - reaches physical device limits or until an explicit request to stop - the movement. A positive value moves the zoom lens group towards the - telephoto direction. A value of zero stops the zoom lens group - movement. A negative value moves the zoom lens group towards the - wide-angle direction. The zoom speed unit is driver-specific. - -``V4L2_CID_IRIS_ABSOLUTE (integer)`` - This control sets the camera's aperture to the specified value. The - unit is undefined. Larger values open the iris wider, smaller values - close it. - -``V4L2_CID_IRIS_RELATIVE (integer)`` - This control modifies the camera's aperture by the specified amount. - The unit is undefined. Positive values open the iris one step - further, negative values close it one step further. This is a - write-only control. - -``V4L2_CID_PRIVACY (boolean)`` - Prevent video from being acquired by the camera. When this control - is set to ``TRUE`` (1), no image can be captured by the camera. - Common means to enforce privacy are mechanical obturation of the - sensor and firmware image processing, but the device is not - restricted to these methods. Devices that implement the privacy - control must support read access and may support write access. - -``V4L2_CID_BAND_STOP_FILTER (integer)`` - Switch the band-stop filter of a camera sensor on or off, or specify - its strength. Such band-stop filters can be used, for example, to - filter out the fluorescent light component. - -.. _v4l2-auto-n-preset-white-balance: - -``V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE`` - (enum) - -enum v4l2_auto_n_preset_white_balance - - Sets white balance to automatic, manual or a preset. The presets - determine color temperature of the light as a hint to the camera for - white balance adjustments resulting in most accurate color - representation. The following white balance presets are listed in - order of increasing color temperature. - -.. tabularcolumns:: |p{7.0 cm}|p{10.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_WHITE_BALANCE_MANUAL`` - - Manual white balance. - * - ``V4L2_WHITE_BALANCE_AUTO`` - - Automatic white balance adjustments. - * - ``V4L2_WHITE_BALANCE_INCANDESCENT`` - - White balance setting for incandescent (tungsten) lighting. It - generally cools down the colors and corresponds approximately to - 2500...3500 K color temperature range. - * - ``V4L2_WHITE_BALANCE_FLUORESCENT`` - - White balance preset for fluorescent lighting. It corresponds - approximately to 4000...5000 K color temperature. - * - ``V4L2_WHITE_BALANCE_FLUORESCENT_H`` - - With this setting the camera will compensate for fluorescent H - lighting. - * - ``V4L2_WHITE_BALANCE_HORIZON`` - - White balance setting for horizon daylight. It corresponds - approximately to 5000 K color temperature. - * - ``V4L2_WHITE_BALANCE_DAYLIGHT`` - - White balance preset for daylight (with clear sky). It corresponds - approximately to 5000...6500 K color temperature. - * - ``V4L2_WHITE_BALANCE_FLASH`` - - With this setting the camera will compensate for the flash light. - It slightly warms up the colors and corresponds roughly to - 5000...5500 K color temperature. - * - ``V4L2_WHITE_BALANCE_CLOUDY`` - - White balance preset for moderately overcast sky. This option - corresponds approximately to 6500...8000 K color temperature - range. - * - ``V4L2_WHITE_BALANCE_SHADE`` - - White balance preset for shade or heavily overcast sky. It - corresponds approximately to 9000...10000 K color temperature. - - - -.. _v4l2-wide-dynamic-range: - -``V4L2_CID_WIDE_DYNAMIC_RANGE (boolean)`` - Enables or disables the camera's wide dynamic range feature. This - feature allows to obtain clear images in situations where intensity - of the illumination varies significantly throughout the scene, i.e. - there are simultaneously very dark and very bright areas. It is most - commonly realized in cameras by combining two subsequent frames with - different exposure times. [#f1]_ - -.. _v4l2-image-stabilization: - -``V4L2_CID_IMAGE_STABILIZATION (boolean)`` - Enables or disables image stabilization. - -``V4L2_CID_ISO_SENSITIVITY (integer menu)`` - Determines ISO equivalent of an image sensor indicating the sensor's - sensitivity to light. The numbers are expressed in arithmetic scale, - as per :ref:`iso12232` standard, where doubling the sensor - sensitivity is represented by doubling the numerical ISO value. - Applications should interpret the values as standard ISO values - multiplied by 1000, e.g. control value 800 stands for ISO 0.8. - Drivers will usually support only a subset of standard ISO values. - The effect of setting this control while the - ``V4L2_CID_ISO_SENSITIVITY_AUTO`` control is set to a value other - than ``V4L2_CID_ISO_SENSITIVITY_MANUAL`` is undefined, drivers - should ignore such requests. - -.. _v4l2-iso-sensitivity-auto-type: - -``V4L2_CID_ISO_SENSITIVITY_AUTO`` - (enum) - -enum v4l2_iso_sensitivity_type - - Enables or disables automatic ISO sensitivity adjustments. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_CID_ISO_SENSITIVITY_MANUAL`` - - Manual ISO sensitivity. - * - ``V4L2_CID_ISO_SENSITIVITY_AUTO`` - - Automatic ISO sensitivity adjustments. - - - -.. _v4l2-scene-mode: - -``V4L2_CID_SCENE_MODE`` - (enum) - -enum v4l2_scene_mode - - This control allows to select scene programs as the camera automatic - modes optimized for common shooting scenes. Within these modes the - camera determines best exposure, aperture, focusing, light metering, - white balance and equivalent sensitivity. The controls of those - parameters are influenced by the scene mode control. An exact - behavior in each mode is subject to the camera specification. - - When the scene mode feature is not used, this control should be set - to ``V4L2_SCENE_MODE_NONE`` to make sure the other possibly related - controls are accessible. The following scene programs are defined: - -.. tabularcolumns:: |p{6.0cm}|p{11.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_SCENE_MODE_NONE`` - - The scene mode feature is disabled. - * - ``V4L2_SCENE_MODE_BACKLIGHT`` - - Backlight. Compensates for dark shadows when light is coming from - behind a subject, also by automatically turning on the flash. - * - ``V4L2_SCENE_MODE_BEACH_SNOW`` - - Beach and snow. This mode compensates for all-white or bright - scenes, which tend to look gray and low contrast, when camera's - automatic exposure is based on an average scene brightness. To - compensate, this mode automatically slightly overexposes the - frames. The white balance may also be adjusted to compensate for - the fact that reflected snow looks bluish rather than white. - * - ``V4L2_SCENE_MODE_CANDLELIGHT`` - - Candle light. The camera generally raises the ISO sensitivity and - lowers the shutter speed. This mode compensates for relatively - close subject in the scene. The flash is disabled in order to - preserve the ambiance of the light. - * - ``V4L2_SCENE_MODE_DAWN_DUSK`` - - Dawn and dusk. Preserves the colors seen in low natural light - before dusk and after down. The camera may turn off the flash, and - automatically focus at infinity. It will usually boost saturation - and lower the shutter speed. - * - ``V4L2_SCENE_MODE_FALL_COLORS`` - - Fall colors. Increases saturation and adjusts white balance for - color enhancement. Pictures of autumn leaves get saturated reds - and yellows. - * - ``V4L2_SCENE_MODE_FIREWORKS`` - - Fireworks. Long exposure times are used to capture the expanding - burst of light from a firework. The camera may invoke image - stabilization. - * - ``V4L2_SCENE_MODE_LANDSCAPE`` - - Landscape. The camera may choose a small aperture to provide deep - depth of field and long exposure duration to help capture detail - in dim light conditions. The focus is fixed at infinity. Suitable - for distant and wide scenery. - * - ``V4L2_SCENE_MODE_NIGHT`` - - Night, also known as Night Landscape. Designed for low light - conditions, it preserves detail in the dark areas without blowing - out bright objects. The camera generally sets itself to a - medium-to-high ISO sensitivity, with a relatively long exposure - time, and turns flash off. As such, there will be increased image - noise and the possibility of blurred image. - * - ``V4L2_SCENE_MODE_PARTY_INDOOR`` - - Party and indoor. Designed to capture indoor scenes that are lit - by indoor background lighting as well as the flash. The camera - usually increases ISO sensitivity, and adjusts exposure for the - low light conditions. - * - ``V4L2_SCENE_MODE_PORTRAIT`` - - Portrait. The camera adjusts the aperture so that the depth of - field is reduced, which helps to isolate the subject against a - smooth background. Most cameras recognize the presence of faces in - the scene and focus on them. The color hue is adjusted to enhance - skin tones. The intensity of the flash is often reduced. - * - ``V4L2_SCENE_MODE_SPORTS`` - - Sports. Significantly increases ISO and uses a fast shutter speed - to freeze motion of rapidly-moving subjects. Increased image noise - may be seen in this mode. - * - ``V4L2_SCENE_MODE_SUNSET`` - - Sunset. Preserves deep hues seen in sunsets and sunrises. It bumps - up the saturation. - * - ``V4L2_SCENE_MODE_TEXT`` - - Text. It applies extra contrast and sharpness, it is typically a - black-and-white mode optimized for readability. Automatic focus - may be switched to close-up mode and this setting may also involve - some lens-distortion correction. - - - -``V4L2_CID_3A_LOCK (bitmask)`` - This control locks or unlocks the automatic focus, exposure and - white balance. The automatic adjustments can be paused independently - by setting the corresponding lock bit to 1. The camera then retains - the settings until the lock bit is cleared. The following lock bits - are defined: - - When a given algorithm is not enabled, drivers should ignore - requests to lock it and should return no error. An example might be - an application setting bit ``V4L2_LOCK_WHITE_BALANCE`` when the - ``V4L2_CID_AUTO_WHITE_BALANCE`` control is set to ``FALSE``. The - value of this control may be changed by exposure, white balance or - focus controls. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_LOCK_EXPOSURE`` - - Automatic exposure adjustments lock. - * - ``V4L2_LOCK_WHITE_BALANCE`` - - Automatic white balance adjustments lock. - * - ``V4L2_LOCK_FOCUS`` - - Automatic focus lock. - - - -``V4L2_CID_PAN_SPEED (integer)`` - This control turns the camera horizontally at the specific speed. - The unit is undefined. A positive value moves the camera to the - right (clockwise when viewed from above), a negative value to the - left. A value of zero stops the motion if one is in progress and has - no effect otherwise. - -``V4L2_CID_TILT_SPEED (integer)`` - This control turns the camera vertically at the specified speed. The - unit is undefined. A positive value moves the camera up, a negative - value down. A value of zero stops the motion if one is in progress - and has no effect otherwise. - - -.. _fm-tx-controls: - -FM Transmitter Control Reference -================================ - -The FM Transmitter (FM_TX) class includes controls for common features -of FM transmissions capable devices. Currently this class includes -parameters for audio compression, pilot tone generation, audio deviation -limiter, RDS transmission and tuning power features. - - -.. _fm-tx-control-id: - -FM_TX Control IDs ------------------ - -``V4L2_CID_FM_TX_CLASS (class)`` - The FM_TX class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -``V4L2_CID_RDS_TX_DEVIATION (integer)`` - Configures RDS signal frequency deviation level in Hz. The range and - step are driver-specific. - -``V4L2_CID_RDS_TX_PI (integer)`` - Sets the RDS Programme Identification field for transmission. - -``V4L2_CID_RDS_TX_PTY (integer)`` - Sets the RDS Programme Type field for transmission. This encodes up - to 31 pre-defined programme types. - -``V4L2_CID_RDS_TX_PS_NAME (string)`` - Sets the Programme Service name (PS_NAME) for transmission. It is - intended for static display on a receiver. It is the primary aid to - listeners in programme service identification and selection. In - Annex E of :ref:`iec62106`, the RDS specification, there is a full - description of the correct character encoding for Programme Service - name strings. Also from RDS specification, PS is usually a single - eight character text. However, it is also possible to find receivers - which can scroll strings sized as 8 x N characters. So, this control - must be configured with steps of 8 characters. The result is it must - always contain a string with size multiple of 8. - -``V4L2_CID_RDS_TX_RADIO_TEXT (string)`` - Sets the Radio Text info for transmission. It is a textual - description of what is being broadcasted. RDS Radio Text can be - applied when broadcaster wishes to transmit longer PS names, - programme-related information or any other text. In these cases, - RadioText should be used in addition to ``V4L2_CID_RDS_TX_PS_NAME``. - The encoding for Radio Text strings is also fully described in Annex - E of :ref:`iec62106`. The length of Radio Text strings depends on - which RDS Block is being used to transmit it, either 32 (2A block) - or 64 (2B block). However, it is also possible to find receivers - which can scroll strings sized as 32 x N or 64 x N characters. So, - this control must be configured with steps of 32 or 64 characters. - The result is it must always contain a string with size multiple of - 32 or 64. - -``V4L2_CID_RDS_TX_MONO_STEREO (boolean)`` - Sets the Mono/Stereo bit of the Decoder Identification code. If set, - then the audio was recorded as stereo. - -``V4L2_CID_RDS_TX_ARTIFICIAL_HEAD (boolean)`` - Sets the - `Artificial Head `__ - bit of the Decoder Identification code. If set, then the audio was - recorded using an artificial head. - -``V4L2_CID_RDS_TX_COMPRESSED (boolean)`` - Sets the Compressed bit of the Decoder Identification code. If set, - then the audio is compressed. - -``V4L2_CID_RDS_TX_DYNAMIC_PTY (boolean)`` - Sets the Dynamic PTY bit of the Decoder Identification code. If set, - then the PTY code is dynamically switched. - -``V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT (boolean)`` - If set, then a traffic announcement is in progress. - -``V4L2_CID_RDS_TX_TRAFFIC_PROGRAM (boolean)`` - If set, then the tuned programme carries traffic announcements. - -``V4L2_CID_RDS_TX_MUSIC_SPEECH (boolean)`` - If set, then this channel broadcasts music. If cleared, then it - broadcasts speech. If the transmitter doesn't make this distinction, - then it should be set. - -``V4L2_CID_RDS_TX_ALT_FREQS_ENABLE (boolean)`` - If set, then transmit alternate frequencies. - -``V4L2_CID_RDS_TX_ALT_FREQS (__u32 array)`` - The alternate frequencies in kHz units. The RDS standard allows for - up to 25 frequencies to be defined. Drivers may support fewer - frequencies so check the array size. - -``V4L2_CID_AUDIO_LIMITER_ENABLED (boolean)`` - Enables or disables the audio deviation limiter feature. The limiter - is useful when trying to maximize the audio volume, minimize - receiver-generated distortion and prevent overmodulation. - -``V4L2_CID_AUDIO_LIMITER_RELEASE_TIME (integer)`` - Sets the audio deviation limiter feature release time. Unit is in - useconds. Step and range are driver-specific. - -``V4L2_CID_AUDIO_LIMITER_DEVIATION (integer)`` - Configures audio frequency deviation level in Hz. The range and step - are driver-specific. - -``V4L2_CID_AUDIO_COMPRESSION_ENABLED (boolean)`` - Enables or disables the audio compression feature. This feature - amplifies signals below the threshold by a fixed gain and compresses - audio signals above the threshold by the ratio of Threshold/(Gain + - Threshold). - -``V4L2_CID_AUDIO_COMPRESSION_GAIN (integer)`` - Sets the gain for audio compression feature. It is a dB value. The - range and step are driver-specific. - -``V4L2_CID_AUDIO_COMPRESSION_THRESHOLD (integer)`` - Sets the threshold level for audio compression freature. It is a dB - value. The range and step are driver-specific. - -``V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME (integer)`` - Sets the attack time for audio compression feature. It is a useconds - value. The range and step are driver-specific. - -``V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME (integer)`` - Sets the release time for audio compression feature. It is a - useconds value. The range and step are driver-specific. - -``V4L2_CID_PILOT_TONE_ENABLED (boolean)`` - Enables or disables the pilot tone generation feature. - -``V4L2_CID_PILOT_TONE_DEVIATION (integer)`` - Configures pilot tone frequency deviation level. Unit is in Hz. The - range and step are driver-specific. - -``V4L2_CID_PILOT_TONE_FREQUENCY (integer)`` - Configures pilot tone frequency value. Unit is in Hz. The range and - step are driver-specific. - -``V4L2_CID_TUNE_PREEMPHASIS`` - (enum) - -enum v4l2_preemphasis - - Configures the pre-emphasis value for broadcasting. A pre-emphasis - filter is applied to the broadcast to accentuate the high audio - frequencies. Depending on the region, a time constant of either 50 - or 75 useconds is used. The enum v4l2_preemphasis defines possible - values for pre-emphasis. Here they are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_PREEMPHASIS_DISABLED`` - - No pre-emphasis is applied. - * - ``V4L2_PREEMPHASIS_50_uS`` - - A pre-emphasis of 50 uS is used. - * - ``V4L2_PREEMPHASIS_75_uS`` - - A pre-emphasis of 75 uS is used. - - - -``V4L2_CID_TUNE_POWER_LEVEL (integer)`` - Sets the output power level for signal transmission. Unit is in - dBuV. Range and step are driver-specific. - -``V4L2_CID_TUNE_ANTENNA_CAPACITOR (integer)`` - This selects the value of antenna tuning capacitor manually or - automatically if set to zero. Unit, range and step are - driver-specific. - -For more details about RDS specification, refer to :ref:`iec62106` -document, from CENELEC. - - -.. _flash-controls: - -Flash Control Reference -======================= - -The V4L2 flash controls are intended to provide generic access to flash -controller devices. Flash controller devices are typically used in -digital cameras. - -The interface can support both LED and xenon flash devices. As of -writing this, there is no xenon flash driver using this interface. - - -.. _flash-controls-use-cases: - -Supported use cases -------------------- - - -Unsynchronised LED flash (software strobe) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Unsynchronised LED flash is controlled directly by the host as the -sensor. The flash must be enabled by the host before the exposure of the -image starts and disabled once it ends. The host is fully responsible -for the timing of the flash. - -Example of such device: Nokia N900. - - -Synchronised LED flash (hardware strobe) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The synchronised LED flash is pre-programmed by the host (power and -timeout) but controlled by the sensor through a strobe signal from the -sensor to the flash. - -The sensor controls the flash duration and timing. This information -typically must be made available to the sensor. - - -LED flash as torch -^^^^^^^^^^^^^^^^^^ - -LED flash may be used as torch in conjunction with another use case -involving camera or individually. - - -.. _flash-control-id: - -Flash Control IDs -""""""""""""""""" - -``V4L2_CID_FLASH_CLASS (class)`` - The FLASH class descriptor. - -``V4L2_CID_FLASH_LED_MODE (menu)`` - Defines the mode of the flash LED, the high-power white LED attached - to the flash controller. Setting this control may not be possible in - presence of some faults. See V4L2_CID_FLASH_FAULT. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_FLASH_LED_MODE_NONE`` - - Off. - * - ``V4L2_FLASH_LED_MODE_FLASH`` - - Flash mode. - * - ``V4L2_FLASH_LED_MODE_TORCH`` - - Torch mode. See V4L2_CID_FLASH_TORCH_INTENSITY. - - - -``V4L2_CID_FLASH_STROBE_SOURCE (menu)`` - Defines the source of the flash LED strobe. - -.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_FLASH_STROBE_SOURCE_SOFTWARE`` - - The flash strobe is triggered by using the - V4L2_CID_FLASH_STROBE control. - * - ``V4L2_FLASH_STROBE_SOURCE_EXTERNAL`` - - The flash strobe is triggered by an external source. Typically - this is a sensor, which makes it possible to synchronises the - flash strobe start to exposure start. - - - -``V4L2_CID_FLASH_STROBE (button)`` - Strobe flash. Valid when V4L2_CID_FLASH_LED_MODE is set to - V4L2_FLASH_LED_MODE_FLASH and V4L2_CID_FLASH_STROBE_SOURCE - is set to V4L2_FLASH_STROBE_SOURCE_SOFTWARE. Setting this - control may not be possible in presence of some faults. See - V4L2_CID_FLASH_FAULT. - -``V4L2_CID_FLASH_STROBE_STOP (button)`` - Stop flash strobe immediately. - -``V4L2_CID_FLASH_STROBE_STATUS (boolean)`` - Strobe status: whether the flash is strobing at the moment or not. - This is a read-only control. - -``V4L2_CID_FLASH_TIMEOUT (integer)`` - Hardware timeout for flash. The flash strobe is stopped after this - period of time has passed from the start of the strobe. - -``V4L2_CID_FLASH_INTENSITY (integer)`` - Intensity of the flash strobe when the flash LED is in flash mode - (V4L2_FLASH_LED_MODE_FLASH). The unit should be milliamps (mA) - if possible. - -``V4L2_CID_FLASH_TORCH_INTENSITY (integer)`` - Intensity of the flash LED in torch mode - (V4L2_FLASH_LED_MODE_TORCH). The unit should be milliamps (mA) - if possible. Setting this control may not be possible in presence of - some faults. See V4L2_CID_FLASH_FAULT. - -``V4L2_CID_FLASH_INDICATOR_INTENSITY (integer)`` - Intensity of the indicator LED. The indicator LED may be fully - independent of the flash LED. The unit should be microamps (uA) if - possible. - -``V4L2_CID_FLASH_FAULT (bitmask)`` - Faults related to the flash. The faults tell about specific problems - in the flash chip itself or the LEDs attached to it. Faults may - prevent further use of some of the flash controls. In particular, - V4L2_CID_FLASH_LED_MODE is set to V4L2_FLASH_LED_MODE_NONE - if the fault affects the flash LED. Exactly which faults have such - an effect is chip dependent. Reading the faults resets the control - and returns the chip to a usable state if possible. - -.. tabularcolumns:: |p{8.0cm}|p{9.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_FLASH_FAULT_OVER_VOLTAGE`` - - Flash controller voltage to the flash LED has exceeded the limit - specific to the flash controller. - * - ``V4L2_FLASH_FAULT_TIMEOUT`` - - The flash strobe was still on when the timeout set by the user --- - V4L2_CID_FLASH_TIMEOUT control --- has expired. Not all flash - controllers may set this in all such conditions. - * - ``V4L2_FLASH_FAULT_OVER_TEMPERATURE`` - - The flash controller has overheated. - * - ``V4L2_FLASH_FAULT_SHORT_CIRCUIT`` - - The short circuit protection of the flash controller has been - triggered. - * - ``V4L2_FLASH_FAULT_OVER_CURRENT`` - - Current in the LED power supply has exceeded the limit specific to - the flash controller. - * - ``V4L2_FLASH_FAULT_INDICATOR`` - - The flash controller has detected a short or open circuit - condition on the indicator LED. - * - ``V4L2_FLASH_FAULT_UNDER_VOLTAGE`` - - Flash controller voltage to the flash LED has been below the - minimum limit specific to the flash controller. - * - ``V4L2_FLASH_FAULT_INPUT_VOLTAGE`` - - The input voltage of the flash controller is below the limit under - which strobing the flash at full current will not be possible.The - condition persists until this flag is no longer set. - * - ``V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE`` - - The temperature of the LED has exceeded its allowed upper limit. - - - -``V4L2_CID_FLASH_CHARGE (boolean)`` - Enable or disable charging of the xenon flash capacitor. - -``V4L2_CID_FLASH_READY (boolean)`` - Is the flash ready to strobe? Xenon flashes require their capacitors - charged before strobing. LED flashes often require a cooldown period - after strobe during which another strobe will not be possible. This - is a read-only control. - - -.. _jpeg-controls: - -JPEG Control Reference -====================== - -The JPEG class includes controls for common features of JPEG encoders -and decoders. Currently it includes features for codecs implementing -progressive baseline DCT compression process with Huffman entrophy -coding. - - -.. _jpeg-control-id: - -JPEG Control IDs ----------------- - -``V4L2_CID_JPEG_CLASS (class)`` - The JPEG class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -``V4L2_CID_JPEG_CHROMA_SUBSAMPLING (menu)`` - The chroma subsampling factors describe how each component of an - input image is sampled, in respect to maximum sample rate in each - spatial dimension. See :ref:`itu-t81`, clause A.1.1. for more - details. The ``V4L2_CID_JPEG_CHROMA_SUBSAMPLING`` control determines - how Cb and Cr components are downsampled after converting an input - image from RGB to Y'CbCr color space. - -.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_444`` - - No chroma subsampling, each pixel has Y, Cr and Cb values. - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_422`` - - Horizontally subsample Cr, Cb components by a factor of 2. - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_420`` - - Subsample Cr, Cb components horizontally and vertically by 2. - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_411`` - - Horizontally subsample Cr, Cb components by a factor of 4. - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_410`` - - Subsample Cr, Cb components horizontally by 4 and vertically by 2. - * - ``V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY`` - - Use only luminance component. - - - -``V4L2_CID_JPEG_RESTART_INTERVAL (integer)`` - The restart interval determines an interval of inserting RSTm - markers (m = 0..7). The purpose of these markers is to additionally - reinitialize the encoder process, in order to process blocks of an - image independently. For the lossy compression processes the restart - interval unit is MCU (Minimum Coded Unit) and its value is contained - in DRI (Define Restart Interval) marker. If - ``V4L2_CID_JPEG_RESTART_INTERVAL`` control is set to 0, DRI and RSTm - markers will not be inserted. - -.. _jpeg-quality-control: - -``V4L2_CID_JPEG_COMPRESSION_QUALITY (integer)`` - ``V4L2_CID_JPEG_COMPRESSION_QUALITY`` control determines trade-off - between image quality and size. It provides simpler method for - applications to control image quality, without a need for direct - reconfiguration of luminance and chrominance quantization tables. In - cases where a driver uses quantization tables configured directly by - an application, using interfaces defined elsewhere, - ``V4L2_CID_JPEG_COMPRESSION_QUALITY`` control should be set by - driver to 0. - - The value range of this control is driver-specific. Only positive, - non-zero values are meaningful. The recommended range is 1 - 100, - where larger values correspond to better image quality. - -.. _jpeg-active-marker-control: - -``V4L2_CID_JPEG_ACTIVE_MARKER (bitmask)`` - Specify which JPEG markers are included in compressed stream. This - control is valid only for encoders. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_JPEG_ACTIVE_MARKER_APP0`` - - Application data segment APP\ :sub:`0`. - * - ``V4L2_JPEG_ACTIVE_MARKER_APP1`` - - Application data segment APP\ :sub:`1`. - * - ``V4L2_JPEG_ACTIVE_MARKER_COM`` - - Comment segment. - * - ``V4L2_JPEG_ACTIVE_MARKER_DQT`` - - Quantization tables segment. - * - ``V4L2_JPEG_ACTIVE_MARKER_DHT`` - - Huffman tables segment. - - - -For more details about JPEG specification, refer to :ref:`itu-t81`, -:ref:`jfif`, :ref:`w3c-jpeg-jfif`. - - -.. _image-source-controls: - -Image Source Control Reference -============================== - -The Image Source control class is intended for low-level control of -image source devices such as image sensors. The devices feature an -analogue to digital converter and a bus transmitter to transmit the -image data out of the device. - - -.. _image-source-control-id: - -Image Source Control IDs ------------------------- - -``V4L2_CID_IMAGE_SOURCE_CLASS (class)`` - The IMAGE_SOURCE class descriptor. - -``V4L2_CID_VBLANK (integer)`` - Vertical blanking. The idle period after every frame during which no - image data is produced. The unit of vertical blanking is a line. - Every line has length of the image width plus horizontal blanking at - the pixel rate defined by ``V4L2_CID_PIXEL_RATE`` control in the - same sub-device. - -``V4L2_CID_HBLANK (integer)`` - Horizontal blanking. The idle period after every line of image data - during which no image data is produced. The unit of horizontal - blanking is pixels. - -``V4L2_CID_ANALOGUE_GAIN (integer)`` - Analogue gain is gain affecting all colour components in the pixel - matrix. The gain operation is performed in the analogue domain - before A/D conversion. - -``V4L2_CID_TEST_PATTERN_RED (integer)`` - Test pattern red colour component. - -``V4L2_CID_TEST_PATTERN_GREENR (integer)`` - Test pattern green (next to red) colour component. - -``V4L2_CID_TEST_PATTERN_BLUE (integer)`` - Test pattern blue colour component. - -``V4L2_CID_TEST_PATTERN_GREENB (integer)`` - Test pattern green (next to blue) colour component. - - -.. _image-process-controls: - -Image Process Control Reference -=============================== - -The Image Process control class is intended for low-level control of -image processing functions. Unlike ``V4L2_CID_IMAGE_SOURCE_CLASS``, the -controls in this class affect processing the image, and do not control -capturing of it. - - -.. _image-process-control-id: - -Image Process Control IDs -------------------------- - -``V4L2_CID_IMAGE_PROC_CLASS (class)`` - The IMAGE_PROC class descriptor. - -``V4L2_CID_LINK_FREQ (integer menu)`` - Data bus frequency. Together with the media bus pixel code, bus type - (clock cycles per sample), the data bus frequency defines the pixel - rate (``V4L2_CID_PIXEL_RATE``) in the pixel array (or possibly - elsewhere, if the device is not an image sensor). The frame rate can - be calculated from the pixel clock, image width and height and - horizontal and vertical blanking. While the pixel rate control may - be defined elsewhere than in the subdev containing the pixel array, - the frame rate cannot be obtained from that information. This is - because only on the pixel array it can be assumed that the vertical - and horizontal blanking information is exact: no other blanking is - allowed in the pixel array. The selection of frame rate is performed - by selecting the desired horizontal and vertical blanking. The unit - of this control is Hz. - -``V4L2_CID_PIXEL_RATE (64-bit integer)`` - Pixel rate in the source pads of the subdev. This control is - read-only and its unit is pixels / second. - -``V4L2_CID_TEST_PATTERN (menu)`` - Some capture/display/sensor devices have the capability to generate - test pattern images. These hardware specific test patterns can be - used to test if a device is working properly. - -``V4L2_CID_DEINTERLACING_MODE (menu)`` - The video deinterlacing mode (such as Bob, Weave, ...). The menu items are - driver specific and are documented in :ref:`v4l-drivers`. - -``V4L2_CID_DIGITAL_GAIN (integer)`` - Digital gain is the value by which all colour components - are multiplied by. Typically the digital gain applied is the - control value divided by e.g. 0x100, meaning that to get no - digital gain the control value needs to be 0x100. The no-gain - configuration is also typically the default. - - -.. _dv-controls: - -Digital Video Control Reference -=============================== - -The Digital Video control class is intended to control receivers and -transmitters for `VGA `__, -`DVI `__ -(Digital Visual Interface), HDMI (:ref:`hdmi`) and DisplayPort -(:ref:`dp`). These controls are generally expected to be private to -the receiver or transmitter subdevice that implements them, so they are -only exposed on the ``/dev/v4l-subdev*`` device node. - -.. note:: - - Note that these devices can have multiple input or output pads which are - hooked up to e.g. HDMI connectors. Even though the subdevice will - receive or transmit video from/to only one of those pads, the other pads - can still be active when it comes to EDID (Extended Display - Identification Data, :ref:`vesaedid`) and HDCP (High-bandwidth Digital - Content Protection System, :ref:`hdcp`) processing, allowing the - device to do the fairly slow EDID/HDCP handling in advance. This allows - for quick switching between connectors. - -These pads appear in several of the controls in this section as -bitmasks, one bit for each pad. Bit 0 corresponds to pad 0, bit 1 to pad -1, etc. The maximum value of the control is the set of valid pads. - - -.. _dv-control-id: - -Digital Video Control IDs -------------------------- - -``V4L2_CID_DV_CLASS (class)`` - The Digital Video class descriptor. - -``V4L2_CID_DV_TX_HOTPLUG (bitmask)`` - Many connectors have a hotplug pin which is high if EDID information - is available from the source. This control shows the state of the - hotplug pin as seen by the transmitter. Each bit corresponds to an - output pad on the transmitter. If an output pad does not have an - associated hotplug pin, then the bit for that pad will be 0. This - read-only control is applicable to DVI-D, HDMI and DisplayPort - connectors. - -``V4L2_CID_DV_TX_RXSENSE (bitmask)`` - Rx Sense is the detection of pull-ups on the TMDS clock lines. This - normally means that the sink has left/entered standby (i.e. the - transmitter can sense that the receiver is ready to receive video). - Each bit corresponds to an output pad on the transmitter. If an - output pad does not have an associated Rx Sense, then the bit for - that pad will be 0. This read-only control is applicable to DVI-D - and HDMI devices. - -``V4L2_CID_DV_TX_EDID_PRESENT (bitmask)`` - When the transmitter sees the hotplug signal from the receiver it - will attempt to read the EDID. If set, then the transmitter has read - at least the first block (= 128 bytes). Each bit corresponds to an - output pad on the transmitter. If an output pad does not support - EDIDs, then the bit for that pad will be 0. This read-only control - is applicable to VGA, DVI-A/D, HDMI and DisplayPort connectors. - -``V4L2_CID_DV_TX_MODE`` - (enum) - -enum v4l2_dv_tx_mode - - HDMI transmitters can transmit in DVI-D mode (just video) or in HDMI - mode (video + audio + auxiliary data). This control selects which - mode to use: V4L2_DV_TX_MODE_DVI_D or V4L2_DV_TX_MODE_HDMI. - This control is applicable to HDMI connectors. - -``V4L2_CID_DV_TX_RGB_RANGE`` - (enum) - -enum v4l2_dv_rgb_range - - Select the quantization range for RGB output. V4L2_DV_RANGE_AUTO - follows the RGB quantization range specified in the standard for the - video interface (ie. :ref:`cea861` for HDMI). - V4L2_DV_RANGE_LIMITED and V4L2_DV_RANGE_FULL override the - standard to be compatible with sinks that have not implemented the - standard correctly (unfortunately quite common for HDMI and DVI-D). - Full range allows all possible values to be used whereas limited - range sets the range to (16 << (N-8)) - (235 << (N-8)) where N is - the number of bits per component. This control is applicable to VGA, - DVI-A/D, HDMI and DisplayPort connectors. - -``V4L2_CID_DV_TX_IT_CONTENT_TYPE`` - (enum) - -enum v4l2_dv_it_content_type - - Configures the IT Content Type of the transmitted video. This - information is sent over HDMI and DisplayPort connectors as part of - the AVI InfoFrame. The term 'IT Content' is used for content that - originates from a computer as opposed to content from a TV broadcast - or an analog source. The enum v4l2_dv_it_content_type defines - the possible content types: - -.. tabularcolumns:: |p{7.0cm}|p{10.5cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_DV_IT_CONTENT_TYPE_GRAPHICS`` - - Graphics content. Pixel data should be passed unfiltered and - without analog reconstruction. - * - ``V4L2_DV_IT_CONTENT_TYPE_PHOTO`` - - Photo content. The content is derived from digital still pictures. - The content should be passed through with minimal scaling and - picture enhancements. - * - ``V4L2_DV_IT_CONTENT_TYPE_CINEMA`` - - Cinema content. - * - ``V4L2_DV_IT_CONTENT_TYPE_GAME`` - - Game content. Audio and video latency should be minimized. - * - ``V4L2_DV_IT_CONTENT_TYPE_NO_ITC`` - - No IT Content information is available and the ITC bit in the AVI - InfoFrame is set to 0. - - - -``V4L2_CID_DV_RX_POWER_PRESENT (bitmask)`` - Detects whether the receiver receives power from the source (e.g. - HDMI carries 5V on one of the pins). This is often used to power an - eeprom which contains EDID information, such that the source can - read the EDID even if the sink is in standby/power off. Each bit - corresponds to an input pad on the receiver. If an input pad - cannot detect whether power is present, then the bit for that pad - will be 0. This read-only control is applicable to DVI-D, HDMI and - DisplayPort connectors. - -``V4L2_CID_DV_RX_RGB_RANGE`` - (enum) - -enum v4l2_dv_rgb_range - - Select the quantization range for RGB input. V4L2_DV_RANGE_AUTO - follows the RGB quantization range specified in the standard for the - video interface (ie. :ref:`cea861` for HDMI). - V4L2_DV_RANGE_LIMITED and V4L2_DV_RANGE_FULL override the - standard to be compatible with sources that have not implemented the - standard correctly (unfortunately quite common for HDMI and DVI-D). - Full range allows all possible values to be used whereas limited - range sets the range to (16 << (N-8)) - (235 << (N-8)) where N is - the number of bits per component. This control is applicable to VGA, - DVI-A/D, HDMI and DisplayPort connectors. - -``V4L2_CID_DV_RX_IT_CONTENT_TYPE`` - (enum) - -enum v4l2_dv_it_content_type - - Reads the IT Content Type of the received video. This information is - sent over HDMI and DisplayPort connectors as part of the AVI - InfoFrame. The term 'IT Content' is used for content that originates - from a computer as opposed to content from a TV broadcast or an - analog source. See ``V4L2_CID_DV_TX_IT_CONTENT_TYPE`` for the - available content types. - - -.. _fm-rx-controls: - -FM Receiver Control Reference -============================= - -The FM Receiver (FM_RX) class includes controls for common features of -FM Reception capable devices. - - -.. _fm-rx-control-id: - -FM_RX Control IDs ------------------ - -``V4L2_CID_FM_RX_CLASS (class)`` - The FM_RX class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -``V4L2_CID_RDS_RECEPTION (boolean)`` - Enables/disables RDS reception by the radio tuner - -``V4L2_CID_RDS_RX_PTY (integer)`` - Gets RDS Programme Type field. This encodes up to 31 pre-defined - programme types. - -``V4L2_CID_RDS_RX_PS_NAME (string)`` - Gets the Programme Service name (PS_NAME). It is intended for - static display on a receiver. It is the primary aid to listeners in - programme service identification and selection. In Annex E of - :ref:`iec62106`, the RDS specification, there is a full - description of the correct character encoding for Programme Service - name strings. Also from RDS specification, PS is usually a single - eight character text. However, it is also possible to find receivers - which can scroll strings sized as 8 x N characters. So, this control - must be configured with steps of 8 characters. The result is it must - always contain a string with size multiple of 8. - -``V4L2_CID_RDS_RX_RADIO_TEXT (string)`` - Gets the Radio Text info. It is a textual description of what is - being broadcasted. RDS Radio Text can be applied when broadcaster - wishes to transmit longer PS names, programme-related information or - any other text. In these cases, RadioText can be used in addition to - ``V4L2_CID_RDS_RX_PS_NAME``. The encoding for Radio Text strings is - also fully described in Annex E of :ref:`iec62106`. The length of - Radio Text strings depends on which RDS Block is being used to - transmit it, either 32 (2A block) or 64 (2B block). However, it is - also possible to find receivers which can scroll strings sized as 32 - x N or 64 x N characters. So, this control must be configured with - steps of 32 or 64 characters. The result is it must always contain a - string with size multiple of 32 or 64. - -``V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT (boolean)`` - If set, then a traffic announcement is in progress. - -``V4L2_CID_RDS_RX_TRAFFIC_PROGRAM (boolean)`` - If set, then the tuned programme carries traffic announcements. - -``V4L2_CID_RDS_RX_MUSIC_SPEECH (boolean)`` - If set, then this channel broadcasts music. If cleared, then it - broadcasts speech. If the transmitter doesn't make this distinction, - then it will be set. - -``V4L2_CID_TUNE_DEEMPHASIS`` - (enum) - -enum v4l2_deemphasis - - Configures the de-emphasis value for reception. A de-emphasis filter - is applied to the broadcast to accentuate the high audio - frequencies. Depending on the region, a time constant of either 50 - or 75 useconds is used. The enum v4l2_deemphasis defines possible - values for de-emphasis. Here they are: - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_DEEMPHASIS_DISABLED`` - - No de-emphasis is applied. - * - ``V4L2_DEEMPHASIS_50_uS`` - - A de-emphasis of 50 uS is used. - * - ``V4L2_DEEMPHASIS_75_uS`` - - A de-emphasis of 75 uS is used. - - - - -.. _detect-controls: - -Detect Control Reference -======================== - -The Detect class includes controls for common features of various motion -or object detection capable devices. - - -.. _detect-control-id: - -Detect Control IDs ------------------- - -``V4L2_CID_DETECT_CLASS (class)`` - The Detect class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -``V4L2_CID_DETECT_MD_MODE (menu)`` - Sets the motion detection mode. - -.. tabularcolumns:: |p{7.5cm}|p{10.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - ``V4L2_DETECT_MD_MODE_DISABLED`` - - Disable motion detection. - * - ``V4L2_DETECT_MD_MODE_GLOBAL`` - - Use a single motion detection threshold. - * - ``V4L2_DETECT_MD_MODE_THRESHOLD_GRID`` - - The image is divided into a grid, each cell with its own motion - detection threshold. These thresholds are set through the - ``V4L2_CID_DETECT_MD_THRESHOLD_GRID`` matrix control. - * - ``V4L2_DETECT_MD_MODE_REGION_GRID`` - - The image is divided into a grid, each cell with its own region - value that specifies which per-region motion detection thresholds - should be used. Each region has its own thresholds. How these - per-region thresholds are set up is driver-specific. The region - values for the grid are set through the - ``V4L2_CID_DETECT_MD_REGION_GRID`` matrix control. - - - -``V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD (integer)`` - Sets the global motion detection threshold to be used with the - ``V4L2_DETECT_MD_MODE_GLOBAL`` motion detection mode. - -``V4L2_CID_DETECT_MD_THRESHOLD_GRID (__u16 matrix)`` - Sets the motion detection thresholds for each cell in the grid. To - be used with the ``V4L2_DETECT_MD_MODE_THRESHOLD_GRID`` motion - detection mode. Matrix element (0, 0) represents the cell at the - top-left of the grid. - -``V4L2_CID_DETECT_MD_REGION_GRID (__u8 matrix)`` - Sets the motion detection region value for each cell in the grid. To - be used with the ``V4L2_DETECT_MD_MODE_REGION_GRID`` motion - detection mode. Matrix element (0, 0) represents the cell at the - top-left of the grid. - - -.. _rf-tuner-controls: - -RF Tuner Control Reference -========================== - -The RF Tuner (RF_TUNER) class includes controls for common features of -devices having RF tuner. - -In this context, RF tuner is radio receiver circuit between antenna and -demodulator. It receives radio frequency (RF) from the antenna and -converts that received signal to lower intermediate frequency (IF) or -baseband frequency (BB). Tuners that could do baseband output are often -called Zero-IF tuners. Older tuners were typically simple PLL tuners -inside a metal box, while newer ones are highly integrated chips -without a metal box "silicon tuners". These controls are mostly -applicable for new feature rich silicon tuners, just because older -tuners does not have much adjustable features. - -For more information about RF tuners see -`Tuner (radio) `__ -and `RF front end `__ -from Wikipedia. - - -.. _rf-tuner-control-id: - -RF_TUNER Control IDs --------------------- - -``V4L2_CID_RF_TUNER_CLASS (class)`` - The RF_TUNER class descriptor. Calling - :ref:`VIDIOC_QUERYCTRL` for this control will - return a description of this control class. - -``V4L2_CID_RF_TUNER_BANDWIDTH_AUTO (boolean)`` - Enables/disables tuner radio channel bandwidth configuration. In - automatic mode bandwidth configuration is performed by the driver. - -``V4L2_CID_RF_TUNER_BANDWIDTH (integer)`` - Filter(s) on tuner signal path are used to filter signal according - to receiving party needs. Driver configures filters to fulfill - desired bandwidth requirement. Used when - V4L2_CID_RF_TUNER_BANDWIDTH_AUTO is not set. Unit is in Hz. The - range and step are driver-specific. - -``V4L2_CID_RF_TUNER_LNA_GAIN_AUTO (boolean)`` - Enables/disables LNA automatic gain control (AGC) - -``V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO (boolean)`` - Enables/disables mixer automatic gain control (AGC) - -``V4L2_CID_RF_TUNER_IF_GAIN_AUTO (boolean)`` - Enables/disables IF automatic gain control (AGC) - -``V4L2_CID_RF_TUNER_RF_GAIN (integer)`` - The RF amplifier is the very first amplifier on the receiver signal - path, just right after the antenna input. The difference between the - LNA gain and the RF gain in this document is that the LNA gain is - integrated in the tuner chip while the RF gain is a separate chip. - There may be both RF and LNA gain controls in the same device. The - range and step are driver-specific. - -``V4L2_CID_RF_TUNER_LNA_GAIN (integer)`` - LNA (low noise amplifier) gain is first gain stage on the RF tuner - signal path. It is located very close to tuner antenna input. Used - when ``V4L2_CID_RF_TUNER_LNA_GAIN_AUTO`` is not set. See - ``V4L2_CID_RF_TUNER_RF_GAIN`` to understand how RF gain and LNA gain - differs from the each others. The range and step are - driver-specific. - -``V4L2_CID_RF_TUNER_MIXER_GAIN (integer)`` - Mixer gain is second gain stage on the RF tuner signal path. It is - located inside mixer block, where RF signal is down-converted by the - mixer. Used when ``V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO`` is not set. - The range and step are driver-specific. - -``V4L2_CID_RF_TUNER_IF_GAIN (integer)`` - IF gain is last gain stage on the RF tuner signal path. It is - located on output of RF tuner. It controls signal level of - intermediate frequency output or baseband output. Used when - ``V4L2_CID_RF_TUNER_IF_GAIN_AUTO`` is not set. The range and step - are driver-specific. - -``V4L2_CID_RF_TUNER_PLL_LOCK (boolean)`` - Is synthesizer PLL locked? RF tuner is receiving given frequency - when that control is set. This is a read-only control. - -.. [#f1] - This control may be changed to a menu control in the future, if more - options are required. -- cgit v1.2.3-59-g8ed1b From 076d792b17468632cb104b6de8d2cf6cd426fed1 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 28 Jan 2019 03:58:43 -0500 Subject: media: dt-bindings: media: sun6i: Add A64 CSI compatible Allwinner A64 CSI is a single channel time-multiplexed BT.656 protocol interface. Add separate compatible string for A64 since it require explicit change in sun6i_csi driver to update default CSI_SCLK rate. Signed-off-by: Jagan Teki Reviewed-by: Rob Herring Acked-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/sun6i-csi.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/sun6i-csi.txt b/Documentation/devicetree/bindings/media/sun6i-csi.txt index cc37cf7fd051..0dd540bb03db 100644 --- a/Documentation/devicetree/bindings/media/sun6i-csi.txt +++ b/Documentation/devicetree/bindings/media/sun6i-csi.txt @@ -8,6 +8,7 @@ Required properties: * "allwinner,sun6i-a31-csi" * "allwinner,sun8i-h3-csi" * "allwinner,sun8i-v3s-csi" + * "allwinner,sun50i-a64-csi" - reg: base address and size of the memory-mapped region. - interrupts: interrupt associated to this IP - clocks: phandles to the clocks feeding the CSI -- cgit v1.2.3-59-g8ed1b From 27e2add8ae8fcae07e2e8d3ea5b3699572290ef3 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 28 Jan 2019 03:58:44 -0500 Subject: media: sun6i: Add A64 CSI block support CSI block in Allwinner A64 has similar features as like in H3, but the default CSI_SCLK rate cannot work properly to drive the connected sensor interface. The tested mod cock rate is 300 MHz and BSP vfe media driver is also using the same rate. Unfortunately there is no valid information about clock rate in manual or any other sources except the BSP driver. so more faith on BSP code, because same has tested in mainline. So, add support for A64 CSI block by setting updated mod clock rate. Signed-off-by: Jagan Teki Acked-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index ee882b66a5ea..5ecdfbf9f6ae 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -154,6 +155,7 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi, int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) { struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); + struct device *dev = sdev->dev; struct regmap *regmap = sdev->regmap; int ret; @@ -161,6 +163,9 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0); clk_disable_unprepare(sdev->clk_ram); + if (of_device_is_compatible(dev->of_node, + "allwinner,sun50i-a64-csi")) + clk_rate_exclusive_put(sdev->clk_mod); clk_disable_unprepare(sdev->clk_mod); reset_control_assert(sdev->rstc_bus); return 0; @@ -172,6 +177,9 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) return ret; } + if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi")) + clk_set_rate_exclusive(sdev->clk_mod, 300000000); + ret = clk_prepare_enable(sdev->clk_ram); if (ret) { dev_err(sdev->dev, "Enable clk_dram_csi clk err %d\n", ret); @@ -191,6 +199,8 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) clk_ram_disable: clk_disable_unprepare(sdev->clk_ram); clk_mod_disable: + if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi")) + clk_rate_exclusive_put(sdev->clk_mod); clk_disable_unprepare(sdev->clk_mod); return ret; } @@ -895,6 +905,7 @@ static const struct of_device_id sun6i_csi_of_match[] = { { .compatible = "allwinner,sun6i-a31-csi", }, { .compatible = "allwinner,sun8i-h3-csi", }, { .compatible = "allwinner,sun8i-v3s-csi", }, + { .compatible = "allwinner,sun50i-a64-csi", }, {}, }; MODULE_DEVICE_TABLE(of, sun6i_csi_of_match); -- cgit v1.2.3-59-g8ed1b From b8726aea59de780a2eb95897f133284d742d6c83 Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Thu, 24 Jan 2019 19:05:31 -0500 Subject: media: ipu3: update meta format documentation Language improvements, fix entity naming, make pipeline a graph and move device usage documentation to device documentation ipu3.rst. Signed-off-by: Yong Zhi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/meta-formats.rst | 2 +- .../media/uapi/v4l/pixfmt-meta-intel-ipu3.rst | 119 ++--------------- Documentation/media/v4l-drivers/ipu3.rst | 147 +++++++++++++++++++++ 3 files changed, 159 insertions(+), 109 deletions(-) diff --git a/Documentation/media/uapi/v4l/meta-formats.rst b/Documentation/media/uapi/v4l/meta-formats.rst index 5f956fa784b7..b10ca9ee3968 100644 --- a/Documentation/media/uapi/v4l/meta-formats.rst +++ b/Documentation/media/uapi/v4l/meta-formats.rst @@ -19,8 +19,8 @@ These formats are used for the :ref:`metadata` interface only. .. toctree:: :maxdepth: 1 - pixfmt-meta-intel-ipu3 pixfmt-meta-d4xx + pixfmt-meta-intel-ipu3 pixfmt-meta-uvc pixfmt-meta-vsp1-hgo pixfmt-meta-vsp1-hgt diff --git a/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst b/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst index 659e58aa9c93..7fb54339f4a7 100644 --- a/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst +++ b/Documentation/media/uapi/v4l/pixfmt-meta-intel-ipu3.rst @@ -30,21 +30,22 @@ V4L2_META_FMT_IPU3_PARAMS ('ip3p'), V4L2_META_FMT_IPU3_3A ('ip3s') ****************************************************************** -.. c:type:: ipu3_uapi_stats_3a +.. ipu3_uapi_stats_3a 3A statistics ============= -For IPU3 ImgU, the 3A statistics accelerators collect different statistics over -an input bayer frame. Those statistics, defined in data struct :c:type:`ipu3_uapi_stats_3a`, -are obtained from "ipu3-imgu 3a stat" metadata capture video node, which are then -passed to user space for statistics analysis using :c:type:`v4l2_meta_format` interface. +The IPU3 ImgU 3A statistics accelerators collect different statistics over +an input Bayer frame. Those statistics are obtained from the "ipu3-imgu [01] 3a +stat" metadata capture video nodes, using the :c:type:`v4l2_meta_format` +interface. They are formatted as described by the :c:type:`ipu3_uapi_stats_3a` +structure. The statistics collected are AWB (Auto-white balance) RGBS (Red, Green, Blue and Saturation measure) cells, AWB filter response, AF (Auto-focus) filter response, and AE (Auto-exposure) histogram. -struct :c:type:`ipu3_uapi_4a_config` saves configurable parameters for all above. +The struct :c:type:`ipu3_uapi_4a_config` saves all configurable parameters. .. code-block:: c @@ -60,105 +61,14 @@ struct :c:type:`ipu3_uapi_4a_config` saves configurable parameters for all above struct ipu3_uapi_ff_status stats_3a_status; }; -.. c:type:: ipu3_uapi_params +.. ipu3_uapi_params Pipeline parameters =================== -IPU3 pipeline has a number of image processing stages, each of which takes a -set of parameters as input. The major stages of pipelines are shown here: - -Raw pixels -> Bayer Downscaling -> Optical Black Correction -> - -Linearization -> Lens Shading Correction -> White Balance / Exposure / - -Focus Apply -> Bayer Noise Reduction -> ANR -> Demosaicing -> Color - -Correction Matrix -> Gamma correction -> Color Space Conversion -> - -Chroma Down Scaling -> Chromatic Noise Reduction -> Total Color - -Correction -> XNR3 -> TNR -> DDR - -The table below presents a description of the above algorithms. - -======================== ======================================================= -Name Description -======================== ======================================================= -Optical Black Correction Optical Black Correction block subtracts a pre-defined - value from the respective pixel values to obtain better - image quality. - Defined in :c:type:`ipu3_uapi_obgrid_param`. -Linearization This algo block uses linearization parameters to - address non-linearity sensor effects. The Lookup table - table is defined in - :c:type:`ipu3_uapi_isp_lin_vmem_params`. -SHD Lens shading correction is used to correct spatial - non-uniformity of the pixel response due to optical - lens shading. This is done by applying a different gain - for each pixel. The gain, black level etc are - configured in :c:type:`ipu3_uapi_shd_config_static`. -BNR Bayer noise reduction block removes image noise by - applying a bilateral filter. - See :c:type:`ipu3_uapi_bnr_static_config` for details. -ANR Advanced Noise Reduction is a block based algorithm - that performs noise reduction in the Bayer domain. The - convolution matrix etc can be found in - :c:type:`ipu3_uapi_anr_config`. -Demosaicing Demosaicing converts raw sensor data in Bayer format - into RGB (Red, Green, Blue) presentation. Then add - outputs of estimation of Y channel for following stream - processing by Firmware. The struct is defined as - :c:type:`ipu3_uapi_dm_config`. (TODO) -Color Correction Color Correction algo transforms sensor specific color - space to the standard "sRGB" color space. This is done - by applying 3x3 matrix defined in - :c:type:`ipu3_uapi_ccm_mat_config`. -Gamma correction Gamma correction :c:type:`ipu3_uapi_gamma_config` is a - basic non-linear tone mapping correction that is - applied per pixel for each pixel component. -CSC Color space conversion transforms each pixel from the - RGB primary presentation to YUV (Y: brightness, - UV: Luminance) presentation. This is done by applying - a 3x3 matrix defined in - :c:type:`ipu3_uapi_csc_mat_config` -CDS Chroma down sampling - After the CSC is performed, the Chroma Down Sampling - is applied for a UV plane down sampling by a factor - of 2 in each direction for YUV 4:2:0 using a 4x2 - configurable filter :c:type:`ipu3_uapi_cds_params`. -CHNR Chroma noise reduction - This block processes only the chrominance pixels and - performs noise reduction by cleaning the high - frequency noise. - See struct :c:type:`ipu3_uapi_yuvp1_chnr_config`. -TCC Total color correction as defined in struct - :c:type:`ipu3_uapi_yuvp2_tcc_static_config`. -XNR3 eXtreme Noise Reduction V3 is the third revision of - noise reduction algorithm used to improve image - quality. This removes the low frequency noise in the - captured image. Two related structs are being defined, - :c:type:`ipu3_uapi_isp_xnr3_params` for ISP data memory - and :c:type:`ipu3_uapi_isp_xnr3_vmem_params` for vector - memory. -TNR Temporal Noise Reduction block compares successive - frames in time to remove anomalies / noise in pixel - values. :c:type:`ipu3_uapi_isp_tnr3_vmem_params` and - :c:type:`ipu3_uapi_isp_tnr3_params` are defined for ISP - vector and data memory respectively. -======================== ======================================================= - -A few stages of the pipeline will be executed by firmware running on the ISP -processor, while many others will use a set of fixed hardware blocks also -called accelerator cluster (ACC) to crunch pixel data and produce statistics. - -ACC parameters of individual algorithms, as defined by -:c:type:`ipu3_uapi_acc_param`, can be chosen to be applied by the user -space through struct :c:type:`ipu3_uapi_flags` embedded in -:c:type:`ipu3_uapi_params` structure. For parameters that are configured as -not enabled by the user space, the corresponding structs are ignored by the -driver, in which case the existing configuration of the algorithm will be -preserved. +The pipeline parameters are passed to the "ipu3-imgu [01] parameters" metadata +output video nodes, using the :c:type:`v4l2_meta_format` interface. They are +formatted as described by the :c:type:`ipu3_uapi_params` structure. Both 3A statistics and pipeline parameters described here are closely tied to the underlying camera sub-system (CSS) APIs. They are usually consumed and @@ -166,13 +76,6 @@ produced by dedicated user space libraries that comprise the important tuning tools, thus freeing the developers from being bothered with the low level hardware and algorithm details. -It should be noted that IPU3 DMA operations require the addresses of all data -structures (that includes both input and output) to be aligned on 32 byte -boundaries. - -The meta data :c:type:`ipu3_uapi_params` will be sent to "ipu3-imgu parameters" -video node in ``V4L2_BUF_TYPE_META_CAPTURE`` format. - .. code-block:: c struct ipu3_uapi_params { diff --git a/Documentation/media/v4l-drivers/ipu3.rst b/Documentation/media/v4l-drivers/ipu3.rst index 804f37300623..c9f780404eee 100644 --- a/Documentation/media/v4l-drivers/ipu3.rst +++ b/Documentation/media/v4l-drivers/ipu3.rst @@ -357,6 +357,153 @@ https://chromium.googlesource.com/chromiumos/platform/arc-camera/+/master/ The source can be located under hal/intel directory. +Overview of IPU3 pipeline +========================= + +IPU3 pipeline has a number of image processing stages, each of which takes a +set of parameters as input. The major stages of pipelines are shown here: + +.. kernel-render:: DOT + :alt: IPU3 ImgU Pipeline + :caption: IPU3 ImgU Pipeline Diagram + + digraph "IPU3 ImgU" { + node [shape=box] + splines="ortho" + rankdir="LR" + + a [label="Raw pixels"] + b [label="Bayer Downscaling"] + c [label="Optical Black Correction"] + d [label="Linearization"] + e [label="Lens Shading Correction"] + f [label="White Balance / Exposure / Focus Apply"] + g [label="Bayer Noise Reduction"] + h [label="ANR"] + i [label="Demosaicing"] + j [label="Color Correction Matrix"] + k [label="Gamma correction"] + l [label="Color Space Conversion"] + m [label="Chroma Down Scaling"] + n [label="Chromatic Noise Reduction"] + o [label="Total Color Correction"] + p [label="XNR3"] + q [label="TNR"] + r [label="DDR"] + + { rank=same; a -> b -> c -> d -> e -> f } + { rank=same; g -> h -> i -> j -> k -> l } + { rank=same; m -> n -> o -> p -> q -> r } + + a -> g -> m [style=invis, weight=10] + + f -> g + l -> m + } + +The table below presents a description of the above algorithms. + +======================== ======================================================= +Name Description +======================== ======================================================= +Optical Black Correction Optical Black Correction block subtracts a pre-defined + value from the respective pixel values to obtain better + image quality. + Defined in :c:type:`ipu3_uapi_obgrid_param`. +Linearization This algo block uses linearization parameters to + address non-linearity sensor effects. The Lookup table + table is defined in + :c:type:`ipu3_uapi_isp_lin_vmem_params`. +SHD Lens shading correction is used to correct spatial + non-uniformity of the pixel response due to optical + lens shading. This is done by applying a different gain + for each pixel. The gain, black level etc are + configured in :c:type:`ipu3_uapi_shd_config_static`. +BNR Bayer noise reduction block removes image noise by + applying a bilateral filter. + See :c:type:`ipu3_uapi_bnr_static_config` for details. +ANR Advanced Noise Reduction is a block based algorithm + that performs noise reduction in the Bayer domain. The + convolution matrix etc can be found in + :c:type:`ipu3_uapi_anr_config`. +DM Demosaicing converts raw sensor data in Bayer format + into RGB (Red, Green, Blue) presentation. Then add + outputs of estimation of Y channel for following stream + processing by Firmware. The struct is defined as + :c:type:`ipu3_uapi_dm_config`. +Color Correction Color Correction algo transforms sensor specific color + space to the standard "sRGB" color space. This is done + by applying 3x3 matrix defined in + :c:type:`ipu3_uapi_ccm_mat_config`. +Gamma correction Gamma correction :c:type:`ipu3_uapi_gamma_config` is a + basic non-linear tone mapping correction that is + applied per pixel for each pixel component. +CSC Color space conversion transforms each pixel from the + RGB primary presentation to YUV (Y: brightness, + UV: Luminance) presentation. This is done by applying + a 3x3 matrix defined in + :c:type:`ipu3_uapi_csc_mat_config` +CDS Chroma down sampling + After the CSC is performed, the Chroma Down Sampling + is applied for a UV plane down sampling by a factor + of 2 in each direction for YUV 4:2:0 using a 4x2 + configurable filter :c:type:`ipu3_uapi_cds_params`. +CHNR Chroma noise reduction + This block processes only the chrominance pixels and + performs noise reduction by cleaning the high + frequency noise. + See struct :c:type:`ipu3_uapi_yuvp1_chnr_config`. +TCC Total color correction as defined in struct + :c:type:`ipu3_uapi_yuvp2_tcc_static_config`. +XNR3 eXtreme Noise Reduction V3 is the third revision of + noise reduction algorithm used to improve image + quality. This removes the low frequency noise in the + captured image. Two related structs are being defined, + :c:type:`ipu3_uapi_isp_xnr3_params` for ISP data memory + and :c:type:`ipu3_uapi_isp_xnr3_vmem_params` for vector + memory. +TNR Temporal Noise Reduction block compares successive + frames in time to remove anomalies / noise in pixel + values. :c:type:`ipu3_uapi_isp_tnr3_vmem_params` and + :c:type:`ipu3_uapi_isp_tnr3_params` are defined for ISP + vector and data memory respectively. +======================== ======================================================= + +Other often encountered acronyms not listed in above table: + + ACC + Accelerator cluster + AWB_FR + Auto white balance filter response statistics + BDS + Bayer downscaler parameters + CCM + Color correction matrix coefficients + IEFd + Image enhancement filter directed + Obgrid + Optical black level compensation + OSYS + Output system configuration + ROI + Region of interest + YDS + Y down sampling + YTM + Y-tone mapping + +A few stages of the pipeline will be executed by firmware running on the ISP +processor, while many others will use a set of fixed hardware blocks also +called accelerator cluster (ACC) to crunch pixel data and produce statistics. + +ACC parameters of individual algorithms, as defined by +:c:type:`ipu3_uapi_acc_param`, can be chosen to be applied by the user +space through struct :c:type:`ipu3_uapi_flags` embedded in +:c:type:`ipu3_uapi_params` structure. For parameters that are configured as +not enabled by the user space, the corresponding structs are ignored by the +driver, in which case the existing configuration of the algorithm will be +preserved. + References ========== -- cgit v1.2.3-59-g8ed1b From 45602f7110fe8fa2d109641d6f80fb7d6ccf9b5a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 04:13:35 -0500 Subject: media: uvcvideo: Fix smatch warning drivers/media/usb/uvc/uvc_video.c: drivers/media/usb/uvc/uvc_video.c:1893 uvc_video_start_transfer() warn: argument 2 to %u specifier is cast from pointer Signed-off-by: Hans Verkuil Reviewed-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvcvideo.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 9b41b14ce076..c7c1baa90dea 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -620,8 +620,10 @@ struct uvc_streaming { (uvc_urb) < &(uvc_streaming)->uvc_urb[UVC_URBS]; \ ++(uvc_urb)) -#define uvc_urb_index(uvc_urb) \ - (unsigned int)((uvc_urb) - (&(uvc_urb)->stream->uvc_urb[0])) +static inline u32 uvc_urb_index(const struct uvc_urb *uvc_urb) +{ + return uvc_urb - &uvc_urb->stream->uvc_urb[0]; +} struct uvc_device_info { u32 quirks; -- cgit v1.2.3-59-g8ed1b From 0654cbcc8dfbf3bc5e6f93916b67fa57ed826a4b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 1 Feb 2019 04:57:31 -0500 Subject: media: uvcvideo: Use usb_make_path to fill in usb_info The uvc driver uses this function to fill in bus_info for VIDIOC_QUERYCAP, so use the same function when filling in the bus_info for the media device. The current implementation only fills in part of the info. E.g. if the full bus_info is usb-0000:01:00.0-1.4.2, then the media bus_info only has 1.4.2. Signed-off-by: Hans Verkuil Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 33a22c016456..10cfe8e51626 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2183,7 +2183,7 @@ static int uvc_probe(struct usb_interface *intf, if (udev->serial) strscpy(dev->mdev.serial, udev->serial, sizeof(dev->mdev.serial)); - strscpy(dev->mdev.bus_info, udev->devpath, sizeof(dev->mdev.bus_info)); + usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info)); dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); media_device_init(&dev->mdev); -- cgit v1.2.3-59-g8ed1b From 5b6326b7aedd08a4d8108f44af9b7af77a0dfd4e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2019 04:13:36 -0500 Subject: media: vsp1: Fix smatch warning drivers/media/platform/vsp1/vsp1_drm.c: drivers/media/platform/vsp1/vsp1_drm.c:336 vsp1_du_pipeline_setup_brx() error: we previously assumed 'pipe->brx' could be null (see line 244) smatch missed that if pipe->brx was NULL, then later on it will be set with a non-NULL value. But it is easier to just use the brx pointer so smatch doesn't get confused. Tested-on: Salvator-XS-ES2.0, Salvator-XS-M3N Signed-off-by: Hans Verkuil Reviewed-by: Kieran Bingham Tested-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_drm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 8d86f618ec77..84895385d2e5 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -333,19 +333,19 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, * on the BRx sink pad 0 and propagated inside the entity, not on the * source pad. */ - format.pad = pipe->brx->source_pad; + format.pad = brx->source_pad; format.format.width = drm_pipe->width; format.format.height = drm_pipe->height; format.format.field = V4L2_FIELD_NONE; - ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL, + ret = v4l2_subdev_call(&brx->subdev, pad, set_fmt, NULL, &format); if (ret < 0) return ret; dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", __func__, format.format.width, format.format.height, - format.format.code, BRX_NAME(pipe->brx), pipe->brx->source_pad); + format.format.code, BRX_NAME(brx), brx->source_pad); if (format.format.width != drm_pipe->width || format.format.height != drm_pipe->height) { -- cgit v1.2.3-59-g8ed1b From e260d78736db0c74240df2243c299be7b0d0b182 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:20:24 -0500 Subject: media: vsp1: Add RZ/G support Document RZ/G1 and RZ/G2 support. Signed-off-by: Fabrizio Castro Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/renesas,vsp1.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.txt b/Documentation/devicetree/bindings/media/renesas,vsp1.txt index 16427017cb45..cd5a955b2ea0 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.txt +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.txt @@ -2,13 +2,13 @@ The VSP is a video processing engine that supports up-/down-scaling, alpha blending, color space conversion and various other image processing features. -It can be found in the Renesas R-Car second generation SoCs. +It can be found in the Renesas R-Car Gen2, R-Car Gen3, RZ/G1, and RZ/G2 SoCs. Required properties: - compatible: Must contain one of the following values - - "renesas,vsp1" for the R-Car Gen2 VSP1 - - "renesas,vsp2" for the R-Car Gen3 VSP2 + - "renesas,vsp1" for the R-Car Gen2 and RZ/G1 VSP1 + - "renesas,vsp2" for the R-Car Gen3 and RZ/G2 VSP2 - reg: Base address and length of the registers block for the VSP. - interrupts: VSP interrupt specifier. -- cgit v1.2.3-59-g8ed1b From b8eb83457e79fc745abea3abcd6c95b1428a05ee Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 15:20:33 -0500 Subject: media: dt-bindings: media: renesas-fcp: Add RZ/G2 support Document RZ/G2 support. Signed-off-by: Fabrizio Castro Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/renesas,fcp.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.txt b/Documentation/devicetree/bindings/media/renesas,fcp.txt index 3ec91803ba58..79c37395b396 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.txt +++ b/Documentation/devicetree/bindings/media/renesas,fcp.txt @@ -2,8 +2,9 @@ Renesas R-Car Frame Compression Processor (FCP) ----------------------------------------------- The FCP is a companion module of video processing modules in the Renesas R-Car -Gen3 SoCs. It provides data compression and decompression, data caching, and -conversion of AXI transactions in order to reduce the memory bandwidth. +Gen3 and RZ/G2 SoCs. It provides data compression and decompression, data +caching, and conversion of AXI transactions in order to reduce the memory +bandwidth. There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and FCP for FDP (FCPF). Their configuration and behaviour depend on the module they -- cgit v1.2.3-59-g8ed1b From d31b282e2c0de9c7fb113516820340251f03a625 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 3 Feb 2019 11:03:56 -0500 Subject: media: sun6i: Fix CSI regmap's max_register max_register is currently set to 0x1000. This is beyond the mapped address range of the hardware, so attempts to dump the regmap from debugfs would trigger a kernel exception. Furthermore, the useful registers only occupy a small section at the beginning of the full range. Change the value to 0x9c, the last known register on the V3s and H3. On the A31, the register range is extended to support additional capture channels. Since this is not yet supported, ignore it for now. Fixes: 5cc7522d8965 ("media: sun6i: Add support for Allwinner CSI V3s") Cc: Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 5ecdfbf9f6ae..457131c7f74a 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -803,7 +803,7 @@ static const struct regmap_config sun6i_csi_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = 0x1000, + .max_register = 0x9c, }; static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev, -- cgit v1.2.3-59-g8ed1b From 19b18e78b327c8e0a5964ea7d08de5cb718f472d Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 3 Feb 2019 11:03:57 -0500 Subject: media: sun6i: Add support for RGB565 formats The CSI controller can take raw data from the data bus and output RGB565 format. The controller does not distinguish between RGB565 LE and BE. Instead this is determined by the media bus format, i.e. the format or order the sensor is sending data in. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 19 +++++++++++++++++-- drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 2 ++ drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c | 2 ++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 457131c7f74a..ff739466f189 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -144,6 +144,12 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi, break; } break; + + case V4L2_PIX_FMT_RGB565: + return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_LE); + case V4L2_PIX_FMT_RGB565X: + return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_BE); + default: dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat); break; @@ -208,8 +214,8 @@ clk_mod_disable: static enum csi_input_fmt get_csi_input_format(struct sun6i_csi_dev *sdev, u32 mbus_code, u32 pixformat) { - /* bayer */ - if ((mbus_code & 0xF000) == 0x3000) + /* non-YUV */ + if ((mbus_code & 0xF000) != 0x2000) return CSI_INPUT_FORMAT_RAW; switch (pixformat) { @@ -278,6 +284,11 @@ static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev, case V4L2_PIX_FMT_YUV422P: return buf_interlaced ? CSI_FRAME_PLANAR_YUV422 : CSI_FIELD_PLANAR_YUV422; + + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + return buf_interlaced ? CSI_FRAME_RGB565 : CSI_FIELD_RGB565; + default: dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat); break; @@ -289,6 +300,10 @@ static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev, static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev, u32 mbus_code, u32 pixformat) { + /* Input sequence does not apply to non-YUV formats */ + if ((mbus_code & 0xF000) != 0x2000) + return 0; + switch (pixformat) { case V4L2_PIX_FMT_HM12: case V4L2_PIX_FMT_NV12: diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h index 0bb000712c33..585cd9669417 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h @@ -117,6 +117,8 @@ static inline int sun6i_csi_get_bpp(unsigned int pixformat) case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: return 16; case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index b04300c3811f..bff6fe832803 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -56,6 +56,8 @@ static const u32 supported_pixformats[] = { V4L2_PIX_FMT_NV16, V4L2_PIX_FMT_NV61, V4L2_PIX_FMT_YUV422P, + V4L2_PIX_FMT_RGB565, + V4L2_PIX_FMT_RGB565X, }; static bool is_pixformat_valid(unsigned int pixformat) -- cgit v1.2.3-59-g8ed1b From 35deee14183457754f77e34dc92c588d93d40052 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 3 Feb 2019 11:03:58 -0500 Subject: media: sun6i: Add support for JPEG media bus format The CSI controller can take raw data from the data bus and output it directly to capture buffers. This can be used to support the JPEG media bus format. While the controller can report minimum and maximum bytes per line, it has no way to report how many lines were captured in the last frame. Thus, even when the on-bus data is framed correctly, we have no way to accertain the actual amount of data captured, unless we scan the buffer for JPEG EOI markers, or sequential zeros. For now we leave bytesused alone, and leave it up to userspace applications to parse the data. Signed-off-by: Chen-Yu Tsai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 6 ++++++ drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 1 + drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c | 1 + 3 files changed, 8 insertions(+) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index ff739466f189..4c79eb64a7a7 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -150,6 +150,9 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi, case V4L2_PIX_FMT_RGB565X: return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_BE); + case V4L2_PIX_FMT_JPEG: + return (mbus_code == MEDIA_BUS_FMT_JPEG_1X8); + default: dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat); break; @@ -289,6 +292,9 @@ static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev, case V4L2_PIX_FMT_RGB565X: return buf_interlaced ? CSI_FRAME_RGB565 : CSI_FIELD_RGB565; + case V4L2_PIX_FMT_JPEG: + return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8; + default: dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat); break; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h index 585cd9669417..f30a210e9caa 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h @@ -94,6 +94,7 @@ static inline int sun6i_csi_get_bpp(unsigned int pixformat) case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_JPEG: return 8; case V4L2_PIX_FMT_SBGGR10: case V4L2_PIX_FMT_SGBRG10: diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index bff6fe832803..1fd16861f111 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -58,6 +58,7 @@ static const u32 supported_pixformats[] = { V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_RGB565X, + V4L2_PIX_FMT_JPEG, }; static bool is_pixformat_valid(unsigned int pixformat) -- cgit v1.2.3-59-g8ed1b From 0806bc0afbb4157b6e1c8d91cbe8e536b292996c Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 5 Feb 2019 11:23:36 -0500 Subject: media: tda1997x: fix get_edid set_edid never wrote the new EDID to state->edid.edid, it was only written to the hardware. Since get_edid returned state->edid.edid, it was never returning the right EDID. Signed-off-by: Tim Harvey Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: added missing commit log] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tda1997x.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index e8613e364403..a62ede096636 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -1884,6 +1884,10 @@ static int tda1997x_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) for (i = 0; i < 128; i++) io_write(sd, REG_EDID_IN_BYTE128 + i, edid->edid[i+128]); + /* store state */ + memcpy(state->edid.edid, edid->edid, 256); + state->edid.blocks = edid->blocks; + tda1997x_enable_edid(sd); return 0; -- cgit v1.2.3-59-g8ed1b From 5c88ee02932a964096cbbcc7c9f38b78d230bacb Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 5 Feb 2019 14:37:42 -0500 Subject: media: sh: migor: Include missing dma-mapping header Since the removal of the stale soc_camera headers, Migo-R board fails to build due to missing dma-mapping include directive. Include missing dma-mapping.h header in Migo-R board file to fix the build error. Fixes: a50c7738e8ae ("media: sh: migor: Remove stale soc_camera include") Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- arch/sh/boards/mach-migor/setup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index f4ad33c6d2aa..1d2993bdd231 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -5,6 +5,7 @@ * Copyright (C) 2008 Magnus Damm */ #include +#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 1b275e4e8b70dbff9850874b30831c1bd8d3c504 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:39 -0500 Subject: media: mtk-jpeg: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index 2a5d5002c27e..f761e4d8bf2a 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -702,7 +702,7 @@ end: v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb)); } -static void *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx, +static struct vb2_v4l2_buffer *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx, enum v4l2_buf_type type) { if (V4L2_TYPE_IS_OUTPUT(type)) @@ -714,7 +714,7 @@ static void *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx, static int mtk_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) { struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q); - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; int ret = 0; ret = pm_runtime_get_sync(ctx->jpeg->dev); @@ -724,14 +724,14 @@ static int mtk_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) return 0; err: while ((vb = mtk_jpeg_buf_remove(ctx, q->type))) - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_QUEUED); + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_QUEUED); return ret; } static void mtk_jpeg_stop_streaming(struct vb2_queue *q) { struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q); - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; /* * STREAMOFF is an acknowledgment for source change event. @@ -743,7 +743,7 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q) struct mtk_jpeg_src_buf *src_buf; vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - src_buf = mtk_jpeg_vb2_to_srcbuf(vb); + src_buf = mtk_jpeg_vb2_to_srcbuf(&vb->vb2_buf); mtk_jpeg_set_queue_data(ctx, &src_buf->dec_param); ctx->state = MTK_JPEG_RUNNING; } else if (V4L2_TYPE_IS_OUTPUT(q->type)) { @@ -751,7 +751,7 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q) } while ((vb = mtk_jpeg_buf_remove(ctx, q->type))) - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); pm_runtime_put_sync(ctx->jpeg->dev); } @@ -807,7 +807,7 @@ static void mtk_jpeg_device_run(void *priv) { struct mtk_jpeg_ctx *ctx = priv; struct mtk_jpeg_dev *jpeg = ctx->jpeg; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR; unsigned long flags; struct mtk_jpeg_src_buf *jpeg_src_buf; @@ -817,11 +817,11 @@ static void mtk_jpeg_device_run(void *priv) src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(src_buf); + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf); if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) { - for (i = 0; i < dst_buf->num_planes; i++) - vb2_set_plane_payload(dst_buf, i, 0); + for (i = 0; i < dst_buf->vb2_buf.num_planes; i++) + vb2_set_plane_payload(&dst_buf->vb2_buf, i, 0); buf_state = VB2_BUF_STATE_DONE; goto dec_end; } @@ -833,8 +833,8 @@ static void mtk_jpeg_device_run(void *priv) return; } - mtk_jpeg_set_dec_src(ctx, src_buf, &bs); - if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, dst_buf, &fb)) + mtk_jpeg_set_dec_src(ctx, &src_buf->vb2_buf, &bs); + if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, &dst_buf->vb2_buf, &fb)) goto dec_end; spin_lock_irqsave(&jpeg->hw_lock, flags); @@ -849,8 +849,8 @@ static void mtk_jpeg_device_run(void *priv) dec_end: v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), buf_state); - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), buf_state); + v4l2_m2m_buf_done(src_buf, buf_state); + v4l2_m2m_buf_done(dst_buf, buf_state); v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); } @@ -921,7 +921,7 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv) { struct mtk_jpeg_dev *jpeg = priv; struct mtk_jpeg_ctx *ctx; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct mtk_jpeg_src_buf *jpeg_src_buf; enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR; u32 dec_irq_ret; @@ -938,7 +938,7 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv) src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(src_buf); + jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf); if (dec_irq_ret >= MTK_JPEG_DEC_RESULT_UNDERFLOW) mtk_jpeg_dec_reset(jpeg->dec_reg_base); @@ -948,15 +948,15 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv) goto dec_end; } - for (i = 0; i < dst_buf->num_planes; i++) - vb2_set_plane_payload(dst_buf, i, + for (i = 0; i < dst_buf->vb2_buf.num_planes; i++) + vb2_set_plane_payload(&dst_buf->vb2_buf, i, jpeg_src_buf->dec_param.comp_size[i]); buf_state = VB2_BUF_STATE_DONE; dec_end: - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), buf_state); - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), buf_state); + v4l2_m2m_buf_done(src_buf, buf_state); + v4l2_m2m_buf_done(dst_buf, buf_state); v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); return IRQ_HANDLED; } -- cgit v1.2.3-59-g8ed1b From 9af469c5be3b031e651ca90ea193f09bae0792d3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:40 -0500 Subject: media: mtk-mdp: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index 51a13466261e..7d15c06e9db9 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -473,20 +473,17 @@ static void mtk_mdp_prepare_addr(struct mtk_mdp_ctx *ctx, static void mtk_mdp_m2m_get_bufs(struct mtk_mdp_ctx *ctx) { struct mtk_mdp_frame *s_frame, *d_frame; - struct vb2_buffer *src_vb, *dst_vb; struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; s_frame = &ctx->s_frame; d_frame = &ctx->d_frame; - src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - mtk_mdp_prepare_addr(ctx, src_vb, s_frame, &s_frame->addr); + src_vbuf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + mtk_mdp_prepare_addr(ctx, &src_vbuf->vb2_buf, s_frame, &s_frame->addr); - dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - mtk_mdp_prepare_addr(ctx, dst_vb, d_frame, &d_frame->addr); + dst_vbuf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + mtk_mdp_prepare_addr(ctx, &dst_vbuf->vb2_buf, d_frame, &d_frame->addr); - src_vbuf = to_vb2_v4l2_buffer(src_vb); - dst_vbuf = to_vb2_v4l2_buffer(dst_vb); dst_vbuf->vb2_buf.timestamp = src_vbuf->vb2_buf.timestamp; } @@ -494,17 +491,14 @@ static void mtk_mdp_process_done(void *priv, int vb_state) { struct mtk_mdp_dev *mdp = priv; struct mtk_mdp_ctx *ctx; - struct vb2_buffer *src_vb, *dst_vb; - struct vb2_v4l2_buffer *src_vbuf = NULL, *dst_vbuf = NULL; + struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; ctx = v4l2_m2m_get_curr_priv(mdp->m2m_dev); if (!ctx) return; - src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - src_vbuf = to_vb2_v4l2_buffer(src_vb); - dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - dst_vbuf = to_vb2_v4l2_buffer(dst_vb); + src_vbuf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); dst_vbuf->vb2_buf.timestamp = src_vbuf->vb2_buf.timestamp; dst_vbuf->timecode = src_vbuf->timecode; -- cgit v1.2.3-59-g8ed1b From 0650a91499e0102ff658c48895bcc881cbfdf3b5 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:41 -0500 Subject: media: mtk-vcodec: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c | 62 ++++++++----------- drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c | 72 +++++++++------------- 2 files changed, 54 insertions(+), 80 deletions(-) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index ba619647bc10..2a36f0ce6974 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -325,13 +325,12 @@ static void mtk_vdec_worker(struct work_struct *work) struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx, decode_work); struct mtk_vcodec_dev *dev = ctx->dev; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct mtk_vcodec_mem buf; struct vdec_fb *pfb; bool res_chg = false; int ret; struct mtk_video_dec_buf *dst_buf_info, *src_buf_info; - struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2; src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); if (src_buf == NULL) { @@ -347,26 +346,23 @@ static void mtk_vdec_worker(struct work_struct *work) return; } - src_vb2_v4l2 = container_of(src_buf, struct vb2_v4l2_buffer, vb2_buf); - src_buf_info = container_of(src_vb2_v4l2, struct mtk_video_dec_buf, vb); - - dst_vb2_v4l2 = container_of(dst_buf, struct vb2_v4l2_buffer, vb2_buf); - dst_buf_info = container_of(dst_vb2_v4l2, struct mtk_video_dec_buf, vb); + src_buf_info = container_of(src_buf, struct mtk_video_dec_buf, vb); + dst_buf_info = container_of(dst_buf, struct mtk_video_dec_buf, vb); pfb = &dst_buf_info->frame_buffer; - pfb->base_y.va = vb2_plane_vaddr(dst_buf, 0); - pfb->base_y.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + pfb->base_y.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + pfb->base_y.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); pfb->base_y.size = ctx->picinfo.y_bs_sz + ctx->picinfo.y_len_sz; - pfb->base_c.va = vb2_plane_vaddr(dst_buf, 1); - pfb->base_c.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 1); + pfb->base_c.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 1); + pfb->base_c.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 1); pfb->base_c.size = ctx->picinfo.c_bs_sz + ctx->picinfo.c_len_sz; pfb->status = 0; mtk_v4l2_debug(3, "===>[%d] vdec_if_decode() ===>", ctx->id); mtk_v4l2_debug(3, "id=%d Framebuf pfb=%p VA=%p Y_DMA=%pad C_DMA=%pad Size=%zx", - dst_buf->index, pfb, + dst_buf->vb2_buf.index, pfb, pfb->base_y.va, &pfb->base_y.dma_addr, &pfb->base_c.dma_addr, pfb->base_y.size); @@ -384,19 +380,19 @@ static void mtk_vdec_worker(struct work_struct *work) clean_display_buffer(ctx); vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 0, 0); vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 1, 0); - dst_vb2_v4l2->flags |= V4L2_BUF_FLAG_LAST; + dst_buf->flags |= V4L2_BUF_FLAG_LAST; v4l2_m2m_buf_done(&dst_buf_info->vb, VB2_BUF_STATE_DONE); clean_free_buffer(ctx); v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); return; } - buf.va = vb2_plane_vaddr(src_buf, 0); - buf.dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + buf.va = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + buf.dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); buf.size = (size_t)src_buf->planes[0].bytesused; if (!buf.va) { v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); mtk_v4l2_err("[%d] id=%d src_addr is NULL!!", - ctx->id, src_buf->index); + ctx->id, src_buf->vb2_buf.index); return; } mtk_v4l2_debug(3, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", @@ -416,10 +412,10 @@ static void mtk_vdec_worker(struct work_struct *work) mtk_v4l2_err( " <===[%d], src_buf[%d] sz=0x%zx pts=%llu dst_buf[%d] vdec_if_decode() ret=%d res_chg=%d===>", ctx->id, - src_buf->index, + src_buf->vb2_buf.index, buf.size, src_buf_info->vb.vb2_buf.timestamp, - dst_buf->index, + dst_buf->vb2_buf.index, ret, res_chg); src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); if (ret == -EIO) { @@ -1103,7 +1099,7 @@ static int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb) static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb) { - struct vb2_buffer *src_buf; + struct vb2_v4l2_buffer *src_buf; struct mtk_vcodec_mem src_mem; bool res_chg = false; int ret = 0; @@ -1149,8 +1145,7 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb) mtk_v4l2_err("No src buffer"); return; } - vb2_v4l2 = to_vb2_v4l2_buffer(src_buf); - buf = container_of(vb2_v4l2, struct mtk_video_dec_buf, vb); + buf = container_of(src_buf, struct mtk_video_dec_buf, vb); if (buf->lastframe) { /* This shouldn't happen. Just in case. */ mtk_v4l2_err("Invalid flush buffer."); @@ -1158,8 +1153,8 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb) return; } - src_mem.va = vb2_plane_vaddr(src_buf, 0); - src_mem.dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + src_mem.va = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + src_mem.dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); src_mem.size = (size_t)src_buf->planes[0].bytesused; mtk_v4l2_debug(2, "[%d] buf id=%d va=%p dma=%pad size=%zx", @@ -1181,11 +1176,9 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb) mtk_v4l2_err("[%d] Unrecoverable error in vdec_if_decode.", ctx->id); ctx->state = MTK_STATE_ABORT; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); } else { - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), - VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); } mtk_v4l2_debug(ret ? 0 : 1, "[%d] vdec_if_decode() src_buf=%d, size=%zu, fail=%d, res_chg=%d", @@ -1281,7 +1274,7 @@ static int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count) static void vb2ops_vdec_stop_streaming(struct vb2_queue *q) { - struct vb2_buffer *src_buf = NULL, *dst_buf = NULL; + struct vb2_v4l2_buffer *src_buf = NULL, *dst_buf = NULL; struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); mtk_v4l2_debug(3, "[%d] (%d) state=(%x) ctx->decoded_frame_cnt=%d", @@ -1289,12 +1282,10 @@ static void vb2ops_vdec_stop_streaming(struct vb2_queue *q) if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) { - struct vb2_v4l2_buffer *vb2_v4l2 = - to_vb2_v4l2_buffer(src_buf); struct mtk_video_dec_buf *buf_info = container_of( - vb2_v4l2, struct mtk_video_dec_buf, vb); + src_buf, struct mtk_video_dec_buf, vb); if (!buf_info->lastframe) - v4l2_m2m_buf_done(vb2_v4l2, + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); } return; @@ -1323,10 +1314,9 @@ static void vb2ops_vdec_stop_streaming(struct vb2_queue *q) ctx->state = MTK_STATE_FLUSH; while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { - vb2_set_plane_payload(dst_buf, 0, 0); - vb2_set_plane_payload(dst_buf, 1, 0); - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), - VB2_BUF_STATE_ERROR); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); } } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index d1f12257bf66..c627202fcffd 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -887,7 +887,7 @@ err_set_param: static void vb2ops_venc_stop_streaming(struct vb2_queue *q) { struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; int ret; mtk_v4l2_debug(2, "[%d]-> type=%d", ctx->id, q->type); @@ -895,13 +895,11 @@ static void vb2ops_venc_stop_streaming(struct vb2_queue *q) if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { dst_buf->planes[0].bytesused = 0; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); } } else { while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); } if ((q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && @@ -937,8 +935,7 @@ static int mtk_venc_encode_header(void *priv) { struct mtk_vcodec_ctx *ctx = priv; int ret; - struct vb2_buffer *src_buf, *dst_buf; - struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct mtk_vcodec_mem bs_buf; struct venc_done_result enc_result; @@ -948,14 +945,14 @@ static int mtk_venc_encode_header(void *priv) return -EINVAL; } - bs_buf.va = vb2_plane_vaddr(dst_buf, 0); - bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); bs_buf.size = (size_t)dst_buf->planes[0].length; mtk_v4l2_debug(1, "[%d] buf id=%d va=0x%p dma_addr=0x%llx size=%zu", ctx->id, - dst_buf->index, bs_buf.va, + dst_buf->vb2_buf.index, bs_buf.va, (u64)bs_buf.dma_addr, bs_buf.size); @@ -964,26 +961,23 @@ static int mtk_venc_encode_header(void *priv) NULL, &bs_buf, &enc_result); if (ret) { - dst_buf->planes[0].bytesused = 0; + dst_buf->vb2_buf.planes[0].bytesused = 0; ctx->state = MTK_STATE_ABORT; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); mtk_v4l2_err("venc_if_encode failed=%d", ret); return -EINVAL; } src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); if (src_buf) { - src_vb2_v4l2 = to_vb2_v4l2_buffer(src_buf); - dst_vb2_v4l2 = to_vb2_v4l2_buffer(dst_buf); - dst_buf->timestamp = src_buf->timestamp; - dst_vb2_v4l2->timecode = src_vb2_v4l2->timecode; + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + dst_buf->timecode = src_buf->timecode; } else { mtk_v4l2_err("No timestamp for the header buffer."); } ctx->state = MTK_STATE_HEADER; dst_buf->planes[0].bytesused = enc_result.bs_size; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); return 0; } @@ -991,9 +985,7 @@ static int mtk_venc_encode_header(void *priv) static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx) { struct venc_enc_param enc_prm; - struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - struct vb2_v4l2_buffer *vb2_v4l2 = - container_of(vb, struct vb2_v4l2_buffer, vb2_buf); + struct vb2_v4l2_buffer *vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx); struct mtk_video_enc_buf *mtk_buf = container_of(vb2_v4l2, struct mtk_video_enc_buf, vb); @@ -1067,12 +1059,11 @@ static void mtk_venc_worker(struct work_struct *work) { struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx, encode_work); - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct venc_frm_buf frm_buf; struct mtk_vcodec_mem bs_buf; struct venc_done_result enc_result; int ret, i; - struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2; /* check dst_buf, dst_buf may be removed in device_run * to stored encdoe header so we need check dst_buf and @@ -1086,15 +1077,15 @@ static void mtk_venc_worker(struct work_struct *work) src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); memset(&frm_buf, 0, sizeof(frm_buf)); - for (i = 0; i < src_buf->num_planes ; i++) { + for (i = 0; i < src_buf->vb2_buf.num_planes ; i++) { frm_buf.fb_addr[i].dma_addr = - vb2_dma_contig_plane_dma_addr(src_buf, i); + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, i); frm_buf.fb_addr[i].size = - (size_t)src_buf->planes[i].length; + (size_t)src_buf->vb2_buf.planes[i].length; } - bs_buf.va = vb2_plane_vaddr(dst_buf, 0); - bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); - bs_buf.size = (size_t)dst_buf->planes[0].length; + bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + bs_buf.size = (size_t)dst_buf->vb2_buf.planes[0].length; mtk_v4l2_debug(2, "Framebuf PA=%llx Size=0x%zx;PA=0x%llx Size=0x%zx;PA=0x%llx Size=%zu", @@ -1108,28 +1099,21 @@ static void mtk_venc_worker(struct work_struct *work) ret = venc_if_encode(ctx, VENC_START_OPT_ENCODE_FRAME, &frm_buf, &bs_buf, &enc_result); - src_vb2_v4l2 = to_vb2_v4l2_buffer(src_buf); - dst_vb2_v4l2 = to_vb2_v4l2_buffer(dst_buf); - - dst_buf->timestamp = src_buf->timestamp; - dst_vb2_v4l2->timecode = src_vb2_v4l2->timecode; + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + dst_buf->timecode = src_buf->timecode; if (enc_result.is_key_frm) - dst_vb2_v4l2->flags |= V4L2_BUF_FLAG_KEYFRAME; + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; if (ret) { - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); dst_buf->planes[0].bytesused = 0; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), - VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); mtk_v4l2_err("venc_if_encode failed=%d", ret); } else { - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), - VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); dst_buf->planes[0].bytesused = enc_result.bs_size; - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), - VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); mtk_v4l2_debug(2, "venc_if_encode bs size=%d", enc_result.bs_size); } @@ -1137,7 +1121,7 @@ static void mtk_venc_worker(struct work_struct *work) v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); mtk_v4l2_debug(1, "<=== src_buf[%d] dst_buf[%d] venc_if_encode ret=%d Size=%u===>", - src_buf->index, dst_buf->index, ret, + src_buf->vb2_buf.index, dst_buf->vb2_buf.index, ret, enc_result.bs_size); } -- cgit v1.2.3-59-g8ed1b From 8d20dcefe471763f23ad538369ec65b51993ffff Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:42 -0500 Subject: media: mx2_emmaprp: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mx2_emmaprp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 27b078cf98e3..f60f499c596b 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -274,7 +274,7 @@ static void emmaprp_device_run(void *priv) { struct emmaprp_ctx *ctx = priv; struct emmaprp_q_data *s_q_data, *d_q_data; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct emmaprp_dev *pcdev = ctx->dev; unsigned int s_width, s_height; unsigned int d_width, d_height; @@ -294,8 +294,8 @@ static void emmaprp_device_run(void *priv) d_height = d_q_data->height; d_size = d_width * d_height; - p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0); - p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + p_in = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + p_out = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); if (!p_in || !p_out) { v4l2_err(&pcdev->v4l2_dev, "Acquiring kernel pointers to buffers failed\n"); -- cgit v1.2.3-59-g8ed1b From da2d3a4e4adabc6ccfb100bc9abd58ee9cd6c4b7 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:43 -0500 Subject: media: rockchip/rga: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rga/rga.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 5c653287185f..b096227a9722 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -43,7 +43,7 @@ static void device_run(void *prv) { struct rga_ctx *ctx = prv; struct rockchip_rga *rga = ctx->rga; - struct vb2_buffer *src, *dst; + struct vb2_v4l2_buffer *src, *dst; unsigned long flags; spin_lock_irqsave(&rga->ctrl_lock, flags); @@ -53,8 +53,8 @@ static void device_run(void *prv) src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - rga_buf_map(src); - rga_buf_map(dst); + rga_buf_map(&src->vb2_buf); + rga_buf_map(&dst->vb2_buf); rga_hw_start(rga); -- cgit v1.2.3-59-g8ed1b From 30fa627b32230737bc3f678067e2adfecf956987 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:44 -0500 Subject: media: s5p-g2d: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 57ab1d1085d1..971c47165010 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -513,7 +513,7 @@ static void device_run(void *prv) { struct g2d_ctx *ctx = prv; struct g2d_dev *dev = ctx->dev; - struct vb2_buffer *src, *dst; + struct vb2_v4l2_buffer *src, *dst; unsigned long flags; u32 cmd = 0; @@ -528,10 +528,10 @@ static void device_run(void *prv) spin_lock_irqsave(&dev->ctrl_lock, flags); g2d_set_src_size(dev, &ctx->in); - g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0)); + g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0)); g2d_set_dst_size(dev, &ctx->out); - g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0)); + g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0)); g2d_set_rop4(dev, ctx->rop); g2d_set_flip(dev, ctx->flip); -- cgit v1.2.3-59-g8ed1b From 4a88f89885c7cf65c62793f385261a6e3315178a Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:45 -0500 Subject: media: s5p-jpeg: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.c | 38 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 0a23b2d19e14..8cc730eccb6c 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -793,14 +793,14 @@ static void skip(struct s5p_jpeg_buffer *buf, long len); static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); struct s5p_jpeg_buffer jpeg_buffer; unsigned int word; int c, x, components; jpeg_buffer.size = 2; /* Ls */ jpeg_buffer.data = - (unsigned long)vb2_plane_vaddr(vb, 0) + ctx->out_q.sos + 2; + (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sos + 2; jpeg_buffer.curr = 0; word = 0; @@ -830,14 +830,14 @@ static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx) static void exynos4_jpeg_parse_huff_tbl(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); struct s5p_jpeg_buffer jpeg_buffer; unsigned int word; int c, i, n, j; for (j = 0; j < ctx->out_q.dht.n; ++j) { jpeg_buffer.size = ctx->out_q.dht.len[j]; - jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(vb, 0) + + jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.dht.marker[j]; jpeg_buffer.curr = 0; @@ -889,13 +889,13 @@ static void exynos4_jpeg_parse_huff_tbl(struct s5p_jpeg_ctx *ctx) static void exynos4_jpeg_parse_decode_q_tbl(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); struct s5p_jpeg_buffer jpeg_buffer; int c, x, components; jpeg_buffer.size = ctx->out_q.sof_len; jpeg_buffer.data = - (unsigned long)vb2_plane_vaddr(vb, 0) + ctx->out_q.sof; + (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sof; jpeg_buffer.curr = 0; skip(&jpeg_buffer, 5); /* P, Y, X */ @@ -920,14 +920,14 @@ static void exynos4_jpeg_parse_decode_q_tbl(struct s5p_jpeg_ctx *ctx) static void exynos4_jpeg_parse_q_tbl(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct vb2_v4l2_buffer *vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); struct s5p_jpeg_buffer jpeg_buffer; unsigned int word; int c, i, j; for (j = 0; j < ctx->out_q.dqt.n; ++j) { jpeg_buffer.size = ctx->out_q.dqt.len[j]; - jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(vb, 0) + + jpeg_buffer.data = (unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.dqt.marker[j]; jpeg_buffer.curr = 0; @@ -2075,15 +2075,15 @@ static void s5p_jpeg_device_run(void *priv) { struct s5p_jpeg_ctx *ctx = priv; struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; unsigned long src_addr, dst_addr, flags; spin_lock_irqsave(&ctx->jpeg->slock, flags); src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - src_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); - dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + src_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); s5p_jpeg_reset(jpeg->regs); s5p_jpeg_poweron(jpeg->regs); @@ -2156,7 +2156,7 @@ static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; struct s5p_jpeg_fmt *fmt; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; struct s5p_jpeg_addr jpeg_addr = {}; u32 pix_size, padding_bytes = 0; @@ -2175,7 +2175,7 @@ static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); } - jpeg_addr.y = vb2_dma_contig_plane_dma_addr(vb, 0); + jpeg_addr.y = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); if (fmt->colplanes == 2) { jpeg_addr.cb = jpeg_addr.y + pix_size - padding_bytes; @@ -2193,7 +2193,7 @@ static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; unsigned int jpeg_addr = 0; if (ctx->mode == S5P_JPEG_ENCODE) @@ -2201,7 +2201,7 @@ static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) else vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + jpeg_addr = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); if (jpeg->variant->version == SJPEG_EXYNOS5433 && ctx->mode == S5P_JPEG_DECODE) jpeg_addr += ctx->out_q.sos; @@ -2317,7 +2317,7 @@ static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; struct s5p_jpeg_fmt *fmt; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; struct s5p_jpeg_addr jpeg_addr = {}; u32 pix_size; @@ -2331,7 +2331,7 @@ static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) fmt = ctx->cap_q.fmt; } - jpeg_addr.y = vb2_dma_contig_plane_dma_addr(vb, 0); + jpeg_addr.y = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); if (fmt->colplanes == 2) { jpeg_addr.cb = jpeg_addr.y + pix_size; @@ -2349,7 +2349,7 @@ static void exynos3250_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) static void exynos3250_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) { struct s5p_jpeg *jpeg = ctx->jpeg; - struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb; unsigned int jpeg_addr = 0; if (ctx->mode == S5P_JPEG_ENCODE) @@ -2357,7 +2357,7 @@ static void exynos3250_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) else vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + jpeg_addr = vb2_dma_contig_plane_dma_addr(&vb->vb2_buf, 0); exynos3250_jpeg_jpgadr(jpeg->regs, jpeg_addr); } -- cgit v1.2.3-59-g8ed1b From 43c145195c7fc3025ee7ecfc67112ac1c82af7c2 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:46 -0500 Subject: media: sh_veu: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_veu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 09ae64a0004c..d277cc674349 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -273,13 +273,13 @@ static void sh_veu_process(struct sh_veu_dev *veu, static void sh_veu_device_run(void *priv) { struct sh_veu_dev *veu = priv; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; src_buf = v4l2_m2m_next_src_buf(veu->m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(veu->m2m_ctx); if (src_buf && dst_buf) - sh_veu_process(veu, src_buf, dst_buf); + sh_veu_process(veu, &src_buf->vb2_buf, &dst_buf->vb2_buf); } /* ========== video ioctls ========== */ -- cgit v1.2.3-59-g8ed1b From 29701c3612fa025d5e8dc64c7a4ae8dc4763912e Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:47 -0500 Subject: media: rockchip/vpu: Correct return type for mem2mem buffer helpers Fix the assigned type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove return a struct vb2_v4l2_buffer, and not a struct vb2_buffer. Fixing this is necessary to fix the mem2mem buffer handling API, changing the return to the correct struct vb2_v4l2_buffer instead of a void pointer. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c | 6 +++--- drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c index 5282236d1bb1..06daea66fb49 100644 --- a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c @@ -80,7 +80,7 @@ rk3288_vpu_jpeg_enc_set_qtable(struct rockchip_vpu_dev *vpu, void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) { struct rockchip_vpu_dev *vpu = ctx->dev; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct rockchip_vpu_jpeg_ctx jpeg_ctx; u32 reg; @@ -88,7 +88,7 @@ void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); - jpeg_ctx.buffer = vb2_plane_vaddr(dst_buf, 0); + jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); jpeg_ctx.width = ctx->dst_fmt.width; jpeg_ctx.height = ctx->dst_fmt.height; jpeg_ctx.quality = ctx->jpeg_quality; @@ -99,7 +99,7 @@ void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) VEPU_REG_ENC_CTRL); rk3288_vpu_set_src_img_ctrl(vpu, ctx); - rk3288_vpu_jpeg_enc_set_buffers(vpu, ctx, src_buf); + rk3288_vpu_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); rk3288_vpu_jpeg_enc_set_qtable(vpu, rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 0), rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 1)); diff --git a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c index dbc86d95fe3b..3d438797692e 100644 --- a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c @@ -111,7 +111,7 @@ rk3399_vpu_jpeg_enc_set_qtable(struct rockchip_vpu_dev *vpu, void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) { struct rockchip_vpu_dev *vpu = ctx->dev; - struct vb2_buffer *src_buf, *dst_buf; + struct vb2_v4l2_buffer *src_buf, *dst_buf; struct rockchip_vpu_jpeg_ctx jpeg_ctx; u32 reg; @@ -119,7 +119,7 @@ void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); - jpeg_ctx.buffer = vb2_plane_vaddr(dst_buf, 0); + jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); jpeg_ctx.width = ctx->dst_fmt.width; jpeg_ctx.height = ctx->dst_fmt.height; jpeg_ctx.quality = ctx->jpeg_quality; @@ -130,7 +130,7 @@ void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) VEPU_REG_ENCODE_START); rk3399_vpu_set_src_img_ctrl(vpu, ctx); - rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, src_buf); + rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); rk3399_vpu_jpeg_enc_set_qtable(vpu, rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 0), rockchip_vpu_jpeg_get_qtable(&jpeg_ctx, 1)); -- cgit v1.2.3-59-g8ed1b From 8dd22b289c8fab1dcb13ab038c0228aa74f134ca Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 8 Feb 2019 11:17:48 -0500 Subject: media: v4l2-mem2mem: Correct return type for mem2mem buffer helpers This commit changes the return type of mem2mem buffer handling API. Namely, these functions: v4l2_m2m_next_buf v4l2_m2m_last_buf v4l2_m2m_buf_remove v4l2_m2m_next_src_buf v4l2_m2m_next_dst_buf v4l2_m2m_last_src_buf v4l2_m2m_last_dst_buf v4l2_m2m_src_buf_remove v4l2_m2m_dst_buf_remove which currently return void pointer. In every case, the actual return type is a struct vb2_v4l2_buffer pointer. Change the return type of the listed functions, so type checking can be properly used. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: clean up line-too-long checkpatch warnings] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 6 +++--- include/media/v4l2-mem2mem.h | 24 +++++++++++++++--------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 57eccbaa52de..3392833d9541 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -131,7 +131,7 @@ struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL(v4l2_m2m_get_vq); -void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) +struct vb2_v4l2_buffer *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) { struct v4l2_m2m_buffer *b; unsigned long flags; @@ -149,7 +149,7 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) } EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf); -void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx) +struct vb2_v4l2_buffer *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx) { struct v4l2_m2m_buffer *b; unsigned long flags; @@ -167,7 +167,7 @@ void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx) } EXPORT_SYMBOL_GPL(v4l2_m2m_last_buf); -void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx) +struct vb2_v4l2_buffer *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx) { struct v4l2_m2m_buffer *b; unsigned long flags; diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 47c6d9aa0bf4..bb3e63d6bd1a 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -425,7 +425,7 @@ unsigned int v4l2_m2m_num_dst_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx) * * @q_ctx: pointer to struct @v4l2_m2m_queue_ctx */ -void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx); +struct vb2_v4l2_buffer *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx); /** * v4l2_m2m_next_src_buf() - return next source buffer from the list of ready @@ -433,7 +433,8 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx); * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_next_buf(&m2m_ctx->out_q_ctx); } @@ -444,7 +445,8 @@ static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx) * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_next_buf(&m2m_ctx->cap_q_ctx); } @@ -454,7 +456,7 @@ static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) * * @q_ctx: pointer to struct @v4l2_m2m_queue_ctx */ -void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx); +struct vb2_v4l2_buffer *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx); /** * v4l2_m2m_last_src_buf() - return last destination buffer from the list of @@ -462,7 +464,8 @@ void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx); * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_last_src_buf(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_last_src_buf(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_last_buf(&m2m_ctx->out_q_ctx); } @@ -473,7 +476,8 @@ static inline void *v4l2_m2m_last_src_buf(struct v4l2_m2m_ctx *m2m_ctx) * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_last_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_last_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_last_buf(&m2m_ctx->cap_q_ctx); } @@ -547,7 +551,7 @@ struct vb2_queue *v4l2_m2m_get_dst_vq(struct v4l2_m2m_ctx *m2m_ctx) * * @q_ctx: pointer to struct @v4l2_m2m_queue_ctx */ -void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx); +struct vb2_v4l2_buffer *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx); /** * v4l2_m2m_src_buf_remove() - take off a source buffer from the list of ready @@ -555,7 +559,8 @@ void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx); * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_buf_remove(&m2m_ctx->out_q_ctx); } @@ -566,7 +571,8 @@ static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) * * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx */ -static inline void *v4l2_m2m_dst_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) +static inline struct vb2_v4l2_buffer * +v4l2_m2m_dst_buf_remove(struct v4l2_m2m_ctx *m2m_ctx) { return v4l2_m2m_buf_remove(&m2m_ctx->cap_q_ctx); } -- cgit v1.2.3-59-g8ed1b From ffaec3b21abcce537be166bd023f55986a5b2002 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 13 Feb 2019 08:25:59 -0500 Subject: media: exynos4-is: remove redundant check on type The check to see if type is V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE is redundant as this has been already checked at the start of the function and if it's not that value then -ENOSYS is returned. Hence the sprintf can be replaced by a simpler string copy. Detected by CoverityScan, CID#1309450 ("Logically dead code") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-isp-video.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index de6bd28f7e31..bb35a2017f21 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -606,9 +606,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp, vdev = &iv->ve.vdev; memset(vdev, 0, sizeof(*vdev)); - snprintf(vdev->name, sizeof(vdev->name), "fimc-is-isp.%s", - type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? - "capture" : "output"); + strscpy(vdev->name, "fimc-is-isp.capture", sizeof(vdev->name)); vdev->queue = q; vdev->fops = &isp_video_fops; vdev->ioctl_ops = &isp_video_ioctl_ops; -- cgit v1.2.3-59-g8ed1b From 9dacde5ed0645a8db2dbfd0951c24b9eed87cc60 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 14 Feb 2019 04:25:11 -0500 Subject: media: cedrus: Forbid setting new formats on busy queues Check that our queues are not busy before setting the format or return EBUSY if that's the case. This ensures that our format can't change once buffers are allocated for the queue. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_video.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c index b5cc79389d67..b47854b3bce4 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c @@ -282,8 +282,13 @@ static int cedrus_s_fmt_vid_cap(struct file *file, void *priv, { struct cedrus_ctx *ctx = cedrus_file2ctx(file); struct cedrus_dev *dev = ctx->dev; + struct vb2_queue *vq; int ret; + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + ret = cedrus_try_fmt_vid_cap(file, priv, f); if (ret) return ret; @@ -299,8 +304,13 @@ static int cedrus_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct cedrus_ctx *ctx = cedrus_file2ctx(file); + struct vb2_queue *vq; int ret; + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + ret = cedrus_try_fmt_vid_out(file, priv, f); if (ret) return ret; -- cgit v1.2.3-59-g8ed1b From 73a1d43293069eb19642cf7c110f229846a52213 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 14 Feb 2019 04:44:03 -0500 Subject: media: cedrus: mpeg2: Use v4l2_m2m_get_vq helper for capture queue Since there's a v4l2_m2m_get_vq helper, use it instead of accessing the queue directly, which slightly increases readability. Also reformat the code using the queue a bit while at it. Signed-off-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index cb45fda9aaeb..13c34927bad5 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -82,7 +82,7 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) dma_addr_t fwd_luma_addr, fwd_chroma_addr; dma_addr_t bwd_luma_addr, bwd_chroma_addr; struct cedrus_dev *dev = ctx->dev; - struct vb2_queue *cap_q = &ctx->fh.m2m_ctx->cap_q_ctx.q; + struct vb2_queue *vq; const u8 *matrix; int forward_idx; int backward_idx; @@ -159,17 +159,17 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_PICBOUNDSIZE, reg); /* Forward and backward prediction reference buffers. */ - forward_idx = vb2_find_timestamp(cap_q, - slice_params->forward_ref_ts, 0); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + forward_idx = vb2_find_timestamp(vq, slice_params->forward_ref_ts, 0); fwd_luma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 0); fwd_chroma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 1); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr); cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr); - backward_idx = vb2_find_timestamp(cap_q, - slice_params->backward_ref_ts, 0); + backward_idx = vb2_find_timestamp(vq, slice_params->backward_ref_ts, 0); bwd_luma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 0); bwd_chroma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 1); -- cgit v1.2.3-59-g8ed1b From 75e3e5b85da199e0c9a885c800933d74ee53ce3c Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 15 Feb 2019 15:58:53 -0500 Subject: media: vicodec: Add a flag for I-frames in fwht header Add a flag 'FWHT_FL_I_FRAME' that indicates that this is an I-frame. This requires incrementing to version 3 This flag is needed for the upcoming stateless FWHT decoder since it has to know if an encoded frame is an I or a P frame. Signed-off-by: Dafna Hirschfeld [hverkuil-cisco@xs4all.nl: added the last paragraph of the commit msg] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/codec-fwht.h | 3 ++- drivers/media/platform/vicodec/codec-v4l2-fwht.c | 4 +++- drivers/media/platform/vicodec/vicodec-core.c | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-fwht.h b/drivers/media/platform/vicodec/codec-fwht.h index 60d71d9dacb3..c410512d47c5 100644 --- a/drivers/media/platform/vicodec/codec-fwht.h +++ b/drivers/media/platform/vicodec/codec-fwht.h @@ -56,7 +56,7 @@ #define FWHT_MAGIC1 0x4f4f4f4f #define FWHT_MAGIC2 0xffffffff -#define FWHT_VERSION 2 +#define FWHT_VERSION 3 /* Set if this is an interlaced format */ #define FWHT_FL_IS_INTERLACED BIT(0) @@ -76,6 +76,7 @@ #define FWHT_FL_CHROMA_FULL_HEIGHT BIT(7) #define FWHT_FL_CHROMA_FULL_WIDTH BIT(8) #define FWHT_FL_ALPHA_IS_UNCOMPRESSED BIT(9) +#define FWHT_FL_I_FRAME BIT(10) /* A 4-values flag - the number of components - 1 */ #define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16) diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.c b/drivers/media/platform/vicodec/codec-v4l2-fwht.c index c15034849133..6573a471c5ca 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.c @@ -218,6 +218,8 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) flags |= FWHT_FL_CR_IS_UNCOMPRESSED; if (encoding & FWHT_ALPHA_UNENCODED) flags |= FWHT_FL_ALPHA_IS_UNCOMPRESSED; + if (!(encoding & FWHT_FRAME_PCODED)) + flags |= FWHT_FL_I_FRAME; if (rf.height_div == 1) flags |= FWHT_FL_CHROMA_FULL_HEIGHT; if (rf.width_div == 1) @@ -265,7 +267,7 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) flags = ntohl(state->header.flags); - if (version == FWHT_VERSION) { + if (version >= 2) { if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc) return -EINVAL; components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 9d739ea5542d..d7636fe9e174 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -339,7 +339,7 @@ info_from_header(const struct fwht_cframe_hdr *p_hdr) unsigned int pixenc = 0; unsigned int version = ntohl(p_hdr->version); - if (version == FWHT_VERSION) { + if (version >= 2) { components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> FWHT_FL_COMPONENTS_NUM_OFFSET); pixenc = (flags & FWHT_FL_PIXENC_MSK); @@ -362,7 +362,7 @@ static bool is_header_valid(const struct fwht_cframe_hdr *p_hdr) if (w < MIN_WIDTH || w > MAX_WIDTH || h < MIN_HEIGHT || h > MAX_HEIGHT) return false; - if (version == FWHT_VERSION) { + if (version >= 2) { unsigned int components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> FWHT_FL_COMPONENTS_NUM_OFFSET); -- cgit v1.2.3-59-g8ed1b From 12aceee1f412c3ddc7750155fec06c906f14ab51 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 17 Feb 2019 10:17:47 -0500 Subject: media: ov7740: fix runtime pm initialization The runtime PM of this device is enabled after v4l2_ctrl_handler_setup(), and this makes this device's runtime PM usage count a negative value. The ov7740_set_ctrl() tries to do something only if the device's runtime PM usage counter is nonzero. ov7740_set_ctrl() { if (!pm_runtime_get_if_in_use(&client->dev)) return 0; ; pm_runtime_put(&client->dev); return ret; } However, the ov7740_set_ctrl() is called by v4l2_ctrl_handler_setup() while the runtime PM of this device is not yet enabled. In this case, the pm_runtime_get_if_in_use() returns -EINVAL (!= 0). Therefore we can't bail out of this function and the usage count is decreased by pm_runtime_put() without increment. This fixes this problem by enabling the runtime PM of this device before v4l2_ctrl_handler_setup() so that the ov7740_set_ctrl() is always called when the runtime PM is enabled. Cc: Wenyou Yang Signed-off-by: Akinobu Mita Tested-by: Eugen Hristev Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7740.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 177688afd9a6..8835b831cdc0 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1101,6 +1101,9 @@ static int ov7740_probe(struct i2c_client *client, if (ret) return ret; + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + ret = ov7740_detect(ov7740); if (ret) goto error_detect; @@ -1123,8 +1126,6 @@ static int ov7740_probe(struct i2c_client *client, if (ret) goto error_async_register; - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); pm_runtime_idle(&client->dev); return 0; @@ -1134,6 +1135,8 @@ error_async_register: error_init_controls: ov7740_free_controls(ov7740); error_detect: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); ov7740_set_power(ov7740, 0); media_entity_cleanup(&ov7740->subdev.entity); -- cgit v1.2.3-59-g8ed1b From 721074b03411327e7bf41555d4cc7c18f49313f7 Mon Sep 17 00:00:00 2001 From: Patrick Lerda Date: Thu, 17 Jan 2019 03:50:13 -0500 Subject: media: rc: rcmm decoder and encoder media: add support for RCMM infrared remote controls. Signed-off-by: Patrick Lerda Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/lirc.h.rst.exceptions | 3 + MAINTAINERS | 5 + drivers/media/rc/Kconfig | 13 ++ drivers/media/rc/Makefile | 1 + drivers/media/rc/ir-rcmm-decoder.c | 254 ++++++++++++++++++++++++++++++ drivers/media/rc/rc-core-priv.h | 5 + drivers/media/rc/rc-main.c | 9 ++ include/media/rc-map.h | 14 +- include/uapi/linux/lirc.h | 6 + tools/include/uapi/linux/lirc.h | 12 ++ tools/testing/selftests/ir/ir_loopback.c | 9 ++ 11 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 drivers/media/rc/ir-rcmm-decoder.c diff --git a/Documentation/media/lirc.h.rst.exceptions b/Documentation/media/lirc.h.rst.exceptions index 379b9e7df5d0..7a8b8ff4f076 100644 --- a/Documentation/media/lirc.h.rst.exceptions +++ b/Documentation/media/lirc.h.rst.exceptions @@ -60,6 +60,9 @@ ignore symbol RC_PROTO_SHARP ignore symbol RC_PROTO_XMP ignore symbol RC_PROTO_CEC ignore symbol RC_PROTO_IMON +ignore symbol RC_PROTO_RCMM12 +ignore symbol RC_PROTO_RCMM24 +ignore symbol RC_PROTO_RCMM32 # Undocumented macros diff --git a/MAINTAINERS b/MAINTAINERS index 914d333ee73d..9662ad6c58e4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16536,6 +16536,11 @@ M: David Härdeman S: Maintained F: drivers/media/rc/winbond-cir.c +RCMM REMOTE CONTROLS DECODER +M: Patrick Lerda +S: Maintained +F: drivers/media/rc/ir-rcmm-decoder.c + WINSYSTEMS EBC-C384 WATCHDOG DRIVER M: William Breathitt Gray L: linux-watchdog@vger.kernel.org diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 8a216068a35a..8e95f6eac89d 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -133,6 +133,19 @@ config IR_IMON_DECODER remote control and you would like to use it with a raw IR receiver, or if you wish to use an encoder to transmit this IR. +config IR_RCMM_DECODER + tristate "Enable IR raw decoder for the RC-MM protocol" + depends on RC_CORE + help + Enable this option when you have IR with RC-MM protocol, and + you need the software decoder. The driver supports 12, + 24 and 32 bits RC-MM variants. You can enable or disable the + different modes using the following RC protocol keywords: + 'rc-mm-12', 'rc-mm-24' and 'rc-mm-32'. + + To compile this driver as a module, choose M here: the module + will be called ir-rcmm-decoder. + endif #RC_DECODERS menuconfig RC_DEVICES diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 92c163816849..48d23433b3c0 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o obj-$(CONFIG_IR_IMON_DECODER) += ir-imon-decoder.o +obj-$(CONFIG_IR_RCMM_DECODER) += ir-rcmm-decoder.o # stand-alone IR receivers/transmitters obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o diff --git a/drivers/media/rc/ir-rcmm-decoder.c b/drivers/media/rc/ir-rcmm-decoder.c new file mode 100644 index 000000000000..f1096ac1e5c5 --- /dev/null +++ b/drivers/media/rc/ir-rcmm-decoder.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0+ +// ir-rcmm-decoder.c - A decoder for the RCMM IR protocol +// +// Copyright (C) 2018 by Patrick Lerda + +#include "rc-core-priv.h" +#include +#include + +#define RCMM_UNIT 166667 /* nanosecs */ +#define RCMM_PREFIX_PULSE 416666 /* 166666.666666666*2.5 */ +#define RCMM_PULSE_0 277777 /* 166666.666666666*(1+2/3) */ +#define RCMM_PULSE_1 444444 /* 166666.666666666*(2+2/3) */ +#define RCMM_PULSE_2 611111 /* 166666.666666666*(3+2/3) */ +#define RCMM_PULSE_3 777778 /* 166666.666666666*(4+2/3) */ + +enum rcmm_state { + STATE_INACTIVE, + STATE_LOW, + STATE_BUMP, + STATE_VALUE, + STATE_FINISHED, +}; + +static bool rcmm_mode(const struct rcmm_dec *data) +{ + return !((0x000c0000 & data->bits) == 0x000c0000); +} + +static int rcmm_miscmode(struct rc_dev *dev, struct rcmm_dec *data) +{ + switch (data->count) { + case 24: + if (dev->enabled_protocols & RC_PROTO_BIT_RCMM24) { + rc_keydown(dev, RC_PROTO_RCMM24, data->bits, 0); + data->state = STATE_INACTIVE; + return 0; + } + return -1; + + case 12: + if (dev->enabled_protocols & RC_PROTO_BIT_RCMM12) { + rc_keydown(dev, RC_PROTO_RCMM12, data->bits, 0); + data->state = STATE_INACTIVE; + return 0; + } + return -1; + } + + return -1; +} + +/** + * ir_rcmm_decode() - Decode one RCMM pulse or space + * @dev: the struct rc_dev descriptor of the device + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev) +{ + struct rcmm_dec *data = &dev->raw->rcmm; + u32 scancode; + u8 toggle; + int value; + + if (!(dev->enabled_protocols & (RC_PROTO_BIT_RCMM32 | + RC_PROTO_BIT_RCMM24 | + RC_PROTO_BIT_RCMM12))) + return 0; + + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; + return 0; + } + + switch (data->state) { + case STATE_INACTIVE: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, RCMM_PREFIX_PULSE, RCMM_UNIT / 2)) + break; + + data->state = STATE_LOW; + data->count = 0; + data->bits = 0; + return 0; + + case STATE_LOW: + if (ev.pulse) + break; + + if (!eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2)) + break; + + data->state = STATE_BUMP; + return 0; + + case STATE_BUMP: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2)) + break; + + data->state = STATE_VALUE; + return 0; + + case STATE_VALUE: + if (ev.pulse) + break; + + if (eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2)) + value = 0; + else if (eq_margin(ev.duration, RCMM_PULSE_1, RCMM_UNIT / 2)) + value = 1; + else if (eq_margin(ev.duration, RCMM_PULSE_2, RCMM_UNIT / 2)) + value = 2; + else if (eq_margin(ev.duration, RCMM_PULSE_3, RCMM_UNIT / 2)) + value = 3; + else + value = -1; + + if (value == -1) { + if (!rcmm_miscmode(dev, data)) + return 0; + break; + } + + data->bits <<= 2; + data->bits |= value; + + data->count += 2; + + if (data->count < 32) + data->state = STATE_BUMP; + else + data->state = STATE_FINISHED; + + return 0; + + case STATE_FINISHED: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2)) + break; + + if (rcmm_mode(data)) { + toggle = !!(0x8000 & data->bits); + scancode = data->bits & ~0x8000; + } else { + toggle = 0; + scancode = data->bits; + } + + if (dev->enabled_protocols & RC_PROTO_BIT_RCMM32) { + rc_keydown(dev, RC_PROTO_RCMM32, scancode, toggle); + data->state = STATE_INACTIVE; + return 0; + } + + break; + } + + data->state = STATE_INACTIVE; + return -EINVAL; +} + +static const int rcmmspace[] = { + RCMM_PULSE_0, + RCMM_PULSE_1, + RCMM_PULSE_2, + RCMM_PULSE_3, +}; + +static int ir_rcmm_rawencoder(struct ir_raw_event **ev, unsigned int max, + unsigned int n, u32 data) +{ + int i; + int ret; + + ret = ir_raw_gen_pulse_space(ev, &max, RCMM_PREFIX_PULSE, RCMM_PULSE_0); + if (ret) + return ret; + + for (i = n - 2; i >= 0; i -= 2) { + const unsigned int space = rcmmspace[(data >> i) & 3]; + + ret = ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, space); + if (ret) + return ret; + } + + return ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, RCMM_PULSE_3 * 2); +} + +static int ir_rcmm_encode(enum rc_proto protocol, u32 scancode, + struct ir_raw_event *events, unsigned int max) +{ + struct ir_raw_event *e = events; + int ret; + + switch (protocol) { + case RC_PROTO_RCMM32: + ret = ir_rcmm_rawencoder(&e, max, 32, scancode); + break; + case RC_PROTO_RCMM24: + ret = ir_rcmm_rawencoder(&e, max, 24, scancode); + break; + case RC_PROTO_RCMM12: + ret = ir_rcmm_rawencoder(&e, max, 12, scancode); + break; + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + return e - events; +} + +static struct ir_raw_handler rcmm_handler = { + .protocols = RC_PROTO_BIT_RCMM32 | + RC_PROTO_BIT_RCMM24 | + RC_PROTO_BIT_RCMM12, + .decode = ir_rcmm_decode, + .encode = ir_rcmm_encode, + .carrier = 36000, + .min_timeout = RCMM_PULSE_3 + RCMM_UNIT, +}; + +static int __init ir_rcmm_decode_init(void) +{ + ir_raw_handler_register(&rcmm_handler); + + pr_info("IR RCMM protocol handler initialized\n"); + return 0; +} + +static void __exit ir_rcmm_decode_exit(void) +{ + ir_raw_handler_unregister(&rcmm_handler); +} + +module_init(ir_rcmm_decode_init); +module_exit(ir_rcmm_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick Lerda"); +MODULE_DESCRIPTION("RCMM IR protocol decoder"); diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index c2cbe7f6266c..9f21b3e8b377 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -131,6 +131,11 @@ struct ir_raw_event_ctrl { unsigned int bits; bool stick_keyboard; } imon; + struct rcmm_dec { + int state; + unsigned int count; + u32 bits; + } rcmm; }; /* Mutex for locking raw IR processing and handler change */ diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 66a174979b3c..dc38e9c0a2ff 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -70,6 +70,12 @@ static const struct { [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 0 }, [RC_PROTO_IMON] = { .name = "imon", .scancode_bits = 0x7fffffff, .repeat_period = 114 }, + [RC_PROTO_RCMM12] = { .name = "rc-mm-12", + .scancode_bits = 0x00000fff, .repeat_period = 114 }, + [RC_PROTO_RCMM24] = { .name = "rc-mm-24", + .scancode_bits = 0x00ffffff, .repeat_period = 114 }, + [RC_PROTO_RCMM32] = { .name = "rc-mm-32", + .scancode_bits = 0xffffffff, .repeat_period = 114 }, }; /* Used to keep track of known keymaps */ @@ -1006,6 +1012,9 @@ static const struct { { RC_PROTO_BIT_XMP, "xmp", "ir-xmp-decoder" }, { RC_PROTO_BIT_CEC, "cec", NULL }, { RC_PROTO_BIT_IMON, "imon", "ir-imon-decoder" }, + { RC_PROTO_BIT_RCMM12 | + RC_PROTO_BIT_RCMM24 | + RC_PROTO_BIT_RCMM32, "rc-mm", "ir-rcmm-decoder" }, }; /** diff --git a/include/media/rc-map.h b/include/media/rc-map.h index d621acadfbf3..e5e86d595645 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -37,6 +37,9 @@ #define RC_PROTO_BIT_XMP BIT_ULL(RC_PROTO_XMP) #define RC_PROTO_BIT_CEC BIT_ULL(RC_PROTO_CEC) #define RC_PROTO_BIT_IMON BIT_ULL(RC_PROTO_IMON) +#define RC_PROTO_BIT_RCMM12 BIT_ULL(RC_PROTO_RCMM12) +#define RC_PROTO_BIT_RCMM24 BIT_ULL(RC_PROTO_RCMM24) +#define RC_PROTO_BIT_RCMM32 BIT_ULL(RC_PROTO_RCMM32) #define RC_PROTO_BIT_ALL \ (RC_PROTO_BIT_UNKNOWN | RC_PROTO_BIT_OTHER | \ @@ -51,7 +54,8 @@ RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ RC_PROTO_BIT_XMP | RC_PROTO_BIT_CEC | \ - RC_PROTO_BIT_IMON) + RC_PROTO_BIT_IMON | RC_PROTO_BIT_RCMM12 | \ + RC_PROTO_BIT_RCMM24 | RC_PROTO_BIT_RCMM32) /* All rc protocols for which we have decoders */ #define RC_PROTO_BIT_ALL_IR_DECODER \ (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ @@ -64,7 +68,9 @@ RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ - RC_PROTO_BIT_XMP | RC_PROTO_BIT_IMON) + RC_PROTO_BIT_XMP | RC_PROTO_BIT_IMON | \ + RC_PROTO_BIT_RCMM12 | RC_PROTO_BIT_RCMM24 | \ + RC_PROTO_BIT_RCMM32) #define RC_PROTO_BIT_ALL_IR_ENCODER \ (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ @@ -77,7 +83,9 @@ RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ RC_PROTO_BIT_RC6_6A_24 | \ RC_PROTO_BIT_RC6_6A_32 | RC_PROTO_BIT_RC6_MCE | \ - RC_PROTO_BIT_SHARP | RC_PROTO_BIT_IMON) + RC_PROTO_BIT_SHARP | RC_PROTO_BIT_IMON | \ + RC_PROTO_BIT_RCMM12 | RC_PROTO_BIT_RCMM24 | \ + RC_PROTO_BIT_RCMM32) #define RC_SCANCODE_UNKNOWN(x) (x) #define RC_SCANCODE_OTHER(x) (x) diff --git a/include/uapi/linux/lirc.h b/include/uapi/linux/lirc.h index 6b319581882f..45fcbf99d72e 100644 --- a/include/uapi/linux/lirc.h +++ b/include/uapi/linux/lirc.h @@ -192,6 +192,9 @@ struct lirc_scancode { * @RC_PROTO_XMP: XMP protocol * @RC_PROTO_CEC: CEC protocol * @RC_PROTO_IMON: iMon Pad protocol + * @RC_PROTO_RCMM12: RC-MM protocol 12 bits + * @RC_PROTO_RCMM24: RC-MM protocol 24 bits + * @RC_PROTO_RCMM32: RC-MM protocol 32 bits */ enum rc_proto { RC_PROTO_UNKNOWN = 0, @@ -218,6 +221,9 @@ enum rc_proto { RC_PROTO_XMP = 21, RC_PROTO_CEC = 22, RC_PROTO_IMON = 23, + RC_PROTO_RCMM12 = 24, + RC_PROTO_RCMM24 = 25, + RC_PROTO_RCMM32 = 26, }; #endif diff --git a/tools/include/uapi/linux/lirc.h b/tools/include/uapi/linux/lirc.h index f189931042a7..45fcbf99d72e 100644 --- a/tools/include/uapi/linux/lirc.h +++ b/tools/include/uapi/linux/lirc.h @@ -133,6 +133,12 @@ #define LIRC_SET_WIDEBAND_RECEIVER _IOW('i', 0x00000023, __u32) +/* + * Return the recording timeout, which is either set by + * the ioctl LIRC_SET_REC_TIMEOUT or by the kernel after setting the protocols. + */ +#define LIRC_GET_REC_TIMEOUT _IOR('i', 0x00000024, __u32) + /* * struct lirc_scancode - decoded scancode with protocol for use with * LIRC_MODE_SCANCODE @@ -186,6 +192,9 @@ struct lirc_scancode { * @RC_PROTO_XMP: XMP protocol * @RC_PROTO_CEC: CEC protocol * @RC_PROTO_IMON: iMon Pad protocol + * @RC_PROTO_RCMM12: RC-MM protocol 12 bits + * @RC_PROTO_RCMM24: RC-MM protocol 24 bits + * @RC_PROTO_RCMM32: RC-MM protocol 32 bits */ enum rc_proto { RC_PROTO_UNKNOWN = 0, @@ -212,6 +221,9 @@ enum rc_proto { RC_PROTO_XMP = 21, RC_PROTO_CEC = 22, RC_PROTO_IMON = 23, + RC_PROTO_RCMM12 = 24, + RC_PROTO_RCMM24 = 25, + RC_PROTO_RCMM32 = 26, }; #endif diff --git a/tools/testing/selftests/ir/ir_loopback.c b/tools/testing/selftests/ir/ir_loopback.c index 858c19caf224..570a7358942c 100644 --- a/tools/testing/selftests/ir/ir_loopback.c +++ b/tools/testing/selftests/ir/ir_loopback.c @@ -51,6 +51,10 @@ static const struct { { RC_PROTO_RC6_6A_32, "rc-6-6a-32", 0xffffffff, "rc-6" }, { RC_PROTO_RC6_MCE, "rc-6-mce", 0x00007fff, "rc-6" }, { RC_PROTO_SHARP, "sharp", 0x1fff, "sharp" }, + { RC_PROTO_IMON, "imon", 0x7fffffff, "imon" }, + { RC_PROTO_RCMM12, "rcmm-12", 0x00000fff, "rcmm" }, + { RC_PROTO_RCMM24, "rcmm-24", 0x00ffffff, "rcmm" }, + { RC_PROTO_RCMM32, "rcmm-32", 0xffffffff, "rcmm" }, }; int lirc_open(const char *rc) @@ -139,6 +143,11 @@ int main(int argc, char **argv) (((scancode >> 8) ^ ~scancode) & 0xff) == 0) continue; + if (rc_proto == RC_PROTO_RCMM32 && + (scancode & 0x000c0000) != 0x000c0000 && + scancode & 0x00008000) + continue; + struct lirc_scancode lsc = { .rc_proto = rc_proto, .scancode = scancode -- cgit v1.2.3-59-g8ed1b From b1f37757dc3befef88fba3961410627e43795f13 Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Thu, 7 Feb 2019 04:29:12 -0500 Subject: media: rc: ir-rc6-decoder: enable toggle bit for Zotac remotes The Zotac RC2604323/01G and RC2604329/02BG remotes use the 32-bit rc6 protocol and toggle bit 15 (0x8000) on repeated button presses, like MCE remotes. Add the customer code 0x80340000 to the 32-bit rc6 toggle handling code to get proper scancodes and toggle reports. Signed-off-by: Matthias Reichl Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-rc6-decoder.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index d96aed1343e4..5cc302fa4daa 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -40,6 +40,7 @@ #define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */ #define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */ #define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */ +#define RC6_6A_ZOTAC_CC 0x80340000 /* Zotac customer code */ #define RC6_6A_KATHREIN_CC 0x80460000 /* Kathrein RCU-676 customer code */ #ifndef CHAR_BIT #define CHAR_BIT 8 /* Normally in */ @@ -246,6 +247,7 @@ again: switch (scancode & RC6_6A_LCC_MASK) { case RC6_6A_MCE_CC: case RC6_6A_KATHREIN_CC: + case RC6_6A_ZOTAC_CC: protocol = RC_PROTO_RC6_MCE; toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); scancode &= ~RC6_6A_MCE_TOGGLE_MASK; -- cgit v1.2.3-59-g8ed1b From a49a7a4635dea5b799a8f77e227ef5c648fa29ec Mon Sep 17 00:00:00 2001 From: Patrick Lerda Date: Wed, 23 Jan 2019 19:04:20 -0500 Subject: media: smipcie: add universal ir capability smipcie: switch to RC_DRIVER_IR_RAW. Signed-off-by: Patrick Lerda Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/smipcie/smipcie-ir.c | 132 +++++++++++---------------------- drivers/media/pci/smipcie/smipcie.h | 1 - 2 files changed, 45 insertions(+), 88 deletions(-) diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c index c5595af6b976..d292cdfb3671 100644 --- a/drivers/media/pci/smipcie/smipcie-ir.c +++ b/drivers/media/pci/smipcie/smipcie-ir.c @@ -16,6 +16,9 @@ #include "smipcie.h" +#define SMI_SAMPLE_PERIOD 83 +#define SMI_SAMPLE_IDLEMIN (10000 / SMI_SAMPLE_PERIOD) + static void smi_ir_enableInterrupt(struct smi_rc *ir) { struct smi_dev *dev = ir->dev; @@ -42,114 +45,64 @@ static void smi_ir_stop(struct smi_rc *ir) struct smi_dev *dev = ir->dev; smi_ir_disableInterrupt(ir); - smi_clear(IR_Init_Reg, 0x80); + smi_clear(IR_Init_Reg, rbIRen); } -#define BITS_PER_COMMAND 14 -#define GROUPS_PER_BIT 2 -#define IR_RC5_MIN_BIT 36 -#define IR_RC5_MAX_BIT 52 -static u32 smi_decode_rc5(u8 *pData, u8 size) +static void smi_raw_process(struct rc_dev *rc_dev, const u8 *buffer, + const u8 length) { - u8 index, current_bit, bit_count; - u8 group_array[BITS_PER_COMMAND * GROUPS_PER_BIT + 4]; - u8 group_index = 0; - u32 command = 0xFFFFFFFF; - - group_array[group_index++] = 1; - - for (index = 0; index < size; index++) { - - current_bit = (pData[index] & 0x80) ? 1 : 0; - bit_count = pData[index] & 0x7f; - - if ((current_bit == 1) && (bit_count >= 2*IR_RC5_MAX_BIT + 1)) { - goto process_code; - } else if ((bit_count >= IR_RC5_MIN_BIT) && - (bit_count <= IR_RC5_MAX_BIT)) { - group_array[group_index++] = current_bit; - } else if ((bit_count > IR_RC5_MAX_BIT) && - (bit_count <= 2*IR_RC5_MAX_BIT)) { - group_array[group_index++] = current_bit; - group_array[group_index++] = current_bit; - } else { - goto invalid_timing; - } - if (group_index >= BITS_PER_COMMAND*GROUPS_PER_BIT) - goto process_code; - - if ((group_index == BITS_PER_COMMAND*GROUPS_PER_BIT - 1) - && (group_array[group_index-1] == 0)) { - group_array[group_index++] = 1; - goto process_code; - } - } - -process_code: - if (group_index == (BITS_PER_COMMAND*GROUPS_PER_BIT-1)) - group_array[group_index++] = 1; - - if (group_index == BITS_PER_COMMAND*GROUPS_PER_BIT) { - command = 0; - for (index = 0; index < (BITS_PER_COMMAND*GROUPS_PER_BIT); - index = index + 2) { - if ((group_array[index] == 1) && - (group_array[index+1] == 0)) { - command |= (1 << (BITS_PER_COMMAND - - (index/2) - 1)); - } else if ((group_array[index] == 0) && - (group_array[index+1] == 1)) { - /* */ - } else { - command = 0xFFFFFFFF; - goto invalid_timing; - } + struct ir_raw_event rawir = {}; + int cnt; + + for (cnt = 0; cnt < length; cnt++) { + if (buffer[cnt] & 0x7f) { + rawir.pulse = (buffer[cnt] & 0x80) == 0; + rawir.duration = ((buffer[cnt] & 0x7f) + + (rawir.pulse ? 0 : -1)) * + rc_dev->rx_resolution; + ir_raw_event_store_with_filter(rc_dev, &rawir); } } - -invalid_timing: - return command; } -static void smi_ir_decode(struct work_struct *work) +static void smi_ir_decode(struct smi_rc *ir) { - struct smi_rc *ir = container_of(work, struct smi_rc, work); struct smi_dev *dev = ir->dev; struct rc_dev *rc_dev = ir->rc_dev; - u32 dwIRControl, dwIRData, dwIRCode, scancode; - u8 index, ucIRCount, readLoop, rc5_command, rc5_system, toggle; + u32 dwIRControl, dwIRData; + u8 index, ucIRCount, readLoop; dwIRControl = smi_read(IR_Init_Reg); + if (dwIRControl & rbIRVld) { ucIRCount = (u8) smi_read(IR_Data_Cnt); - if (ucIRCount < 4) - goto end_ir_decode; - readLoop = ucIRCount/4; if (ucIRCount % 4) readLoop += 1; for (index = 0; index < readLoop; index++) { - dwIRData = smi_read(IR_DATA_BUFFER_BASE + (index*4)); + dwIRData = smi_read(IR_DATA_BUFFER_BASE + (index * 4)); ir->irData[index*4 + 0] = (u8)(dwIRData); ir->irData[index*4 + 1] = (u8)(dwIRData >> 8); ir->irData[index*4 + 2] = (u8)(dwIRData >> 16); ir->irData[index*4 + 3] = (u8)(dwIRData >> 24); } - dwIRCode = smi_decode_rc5(ir->irData, ucIRCount); - - if (dwIRCode != 0xFFFFFFFF) { - rc5_command = dwIRCode & 0x3F; - rc5_system = (dwIRCode & 0x7C0) >> 6; - toggle = (dwIRCode & 0x800) ? 1 : 0; - scancode = rc5_system << 8 | rc5_command; - rc_keydown(rc_dev, RC_PROTO_RC5, scancode, toggle); - } + smi_raw_process(rc_dev, ir->irData, ucIRCount); + smi_set(IR_Init_Reg, rbIRVld); } -end_ir_decode: - smi_set(IR_Init_Reg, 0x04); - smi_ir_enableInterrupt(ir); + + if (dwIRControl & rbIRhighidle) { + struct ir_raw_event rawir = {}; + + rawir.pulse = 0; + rawir.duration = US_TO_NS(SMI_SAMPLE_PERIOD * + SMI_SAMPLE_IDLEMIN); + ir_raw_event_store_with_filter(rc_dev, &rawir); + smi_set(IR_Init_Reg, rbIRhighidle); + } + + ir_raw_event_handle(rc_dev); } /* ir functions call by main driver.*/ @@ -160,7 +113,8 @@ int smi_ir_irq(struct smi_rc *ir, u32 int_status) if (int_status & IR_X_INT) { smi_ir_disableInterrupt(ir); smi_ir_clearInterrupt(ir); - schedule_work(&ir->work); + smi_ir_decode(ir); + smi_ir_enableInterrupt(ir); handled = 1; } return handled; @@ -170,9 +124,11 @@ void smi_ir_start(struct smi_rc *ir) { struct smi_dev *dev = ir->dev; - smi_write(IR_Idle_Cnt_Low, 0x00140070); + smi_write(IR_Idle_Cnt_Low, + (((SMI_SAMPLE_PERIOD - 1) & 0xFFFF) << 16) | + (SMI_SAMPLE_IDLEMIN & 0xFFFF)); msleep(20); - smi_set(IR_Init_Reg, 0x90); + smi_set(IR_Init_Reg, rbIRen | rbIRhighidle); smi_ir_enableInterrupt(ir); } @@ -183,7 +139,7 @@ int smi_ir_init(struct smi_dev *dev) struct rc_dev *rc_dev; struct smi_rc *ir = &dev->ir; - rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE); + rc_dev = rc_allocate_device(RC_DRIVER_IR_RAW); if (!rc_dev) return -ENOMEM; @@ -193,6 +149,7 @@ int smi_ir_init(struct smi_dev *dev) snprintf(ir->input_phys, sizeof(ir->input_phys), "pci-%s/ir0", pci_name(dev->pci_dev)); + rc_dev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; rc_dev->driver_name = "SMI_PCIe"; rc_dev->input_phys = ir->input_phys; rc_dev->device_name = ir->device_name; @@ -203,11 +160,12 @@ int smi_ir_init(struct smi_dev *dev) rc_dev->dev.parent = &dev->pci_dev->dev; rc_dev->map_name = dev->info->rc_map; + rc_dev->timeout = MS_TO_NS(100); + rc_dev->rx_resolution = US_TO_NS(SMI_SAMPLE_PERIOD); ir->rc_dev = rc_dev; ir->dev = dev; - INIT_WORK(&ir->work, smi_ir_decode); smi_ir_disableInterrupt(ir); ret = rc_register_device(rc_dev); diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h index a6c5b1bd7edb..e52229a87b84 100644 --- a/drivers/media/pci/smipcie/smipcie.h +++ b/drivers/media/pci/smipcie/smipcie.h @@ -241,7 +241,6 @@ struct smi_rc { struct rc_dev *rc_dev; char input_phys[64]; char device_name[64]; - struct work_struct work; u8 irData[256]; int users; -- cgit v1.2.3-59-g8ed1b From 174bcae1a4e98e1d5c35cd878bdf282e56287e8f Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 6 Feb 2019 11:11:37 -0500 Subject: media: staging/imx: refactor imx media device probe Refactor and move media device initialization code to a new common module, so it can be used by other devices, this will allow for example a near to introduce imx7 CSI driver, to use this media device. Signed-off-by: Rui Miguel Silva Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/Makefile | 1 + drivers/staging/media/imx/imx-media-dev-common.c | 90 ++++++++++++++++++++++++ drivers/staging/media/imx/imx-media-dev.c | 86 +++++----------------- drivers/staging/media/imx/imx-media-of.c | 6 +- drivers/staging/media/imx/imx-media.h | 14 ++++ 5 files changed, 127 insertions(+), 70 deletions(-) create mode 100644 drivers/staging/media/imx/imx-media-dev-common.c diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile index 698a4210316e..a30b3033f9a3 100644 --- a/drivers/staging/media/imx/Makefile +++ b/drivers/staging/media/imx/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 imx-media-objs := imx-media-dev.o imx-media-internal-sd.o imx-media-of.o +imx-media-objs += imx-media-dev-common.o imx-media-common-objs := imx-media-utils.o imx-media-fim.o imx-media-ic-objs := imx-ic-common.o imx-ic-prp.o imx-ic-prpencvf.o diff --git a/drivers/staging/media/imx/imx-media-dev-common.c b/drivers/staging/media/imx/imx-media-dev-common.c new file mode 100644 index 000000000000..910594125889 --- /dev/null +++ b/drivers/staging/media/imx/imx-media-dev-common.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 Media Controller Driver for Freescale common i.MX5/6/7 SOC + * + * Copyright (c) 2019 Linaro Ltd + * Copyright (c) 2016 Mentor Graphics Inc. + */ + +#include +#include +#include "imx-media.h" + +static const struct v4l2_async_notifier_operations imx_media_subdev_ops = { + .bound = imx_media_subdev_bound, + .complete = imx_media_probe_complete, +}; + +static const struct media_device_ops imx_media_md_ops = { + .link_notify = imx_media_link_notify, +}; + +struct imx_media_dev *imx_media_dev_init(struct device *dev) +{ + struct imx_media_dev *imxmd; + int ret; + + imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL); + if (!imxmd) + return ERR_PTR(-ENOMEM); + + dev_set_drvdata(dev, imxmd); + + strlcpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model)); + imxmd->md.ops = &imx_media_md_ops; + imxmd->md.dev = dev; + + mutex_init(&imxmd->mutex); + + imxmd->v4l2_dev.mdev = &imxmd->md; + imxmd->v4l2_dev.notify = imx_media_notify; + strlcpy(imxmd->v4l2_dev.name, "imx-media", + sizeof(imxmd->v4l2_dev.name)); + + media_device_init(&imxmd->md); + + ret = v4l2_device_register(dev, &imxmd->v4l2_dev); + if (ret < 0) { + v4l2_err(&imxmd->v4l2_dev, + "Failed to register v4l2_device: %d\n", ret); + goto cleanup; + } + + dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd); + + INIT_LIST_HEAD(&imxmd->vdev_list); + + v4l2_async_notifier_init(&imxmd->notifier); + + return imxmd; + +cleanup: + media_device_cleanup(&imxmd->md); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(imx_media_dev_init); + +int imx_media_dev_notifier_register(struct imx_media_dev *imxmd) +{ + int ret; + + /* no subdevs? just bail */ + if (list_empty(&imxmd->notifier.asd_list)) { + v4l2_err(&imxmd->v4l2_dev, "no subdevs\n"); + return -ENODEV; + } + + /* prepare the async subdev notifier and register it */ + imxmd->notifier.ops = &imx_media_subdev_ops; + ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, + &imxmd->notifier); + if (ret) { + v4l2_err(&imxmd->v4l2_dev, + "v4l2_async_notifier_register failed with %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(imx_media_dev_notifier_register); diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 25e916562c66..c42bddd78906 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -116,9 +116,9 @@ static int imx_media_get_ipu(struct imx_media_dev *imxmd, } /* async subdev bound notifier */ -static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) +int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) { struct imx_media_dev *imxmd = notifier2dev(notifier); int ret = 0; @@ -302,7 +302,7 @@ static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd) } /* async subdev complete notifier */ -static int imx_media_probe_complete(struct v4l2_async_notifier *notifier) +int imx_media_probe_complete(struct v4l2_async_notifier *notifier) { struct imx_media_dev *imxmd = notifier2dev(notifier); int ret; @@ -326,11 +326,6 @@ unlock: return media_device_register(&imxmd->md); } -static const struct v4l2_async_notifier_operations imx_media_subdev_ops = { - .bound = imx_media_subdev_bound, - .complete = imx_media_probe_complete, -}; - /* * adds controls to a video device from an entity subdevice. * Continues upstream from the entity's sink pads. @@ -374,8 +369,8 @@ static int imx_media_inherit_controls(struct imx_media_dev *imxmd, return ret; } -static int imx_media_link_notify(struct media_link *link, u32 flags, - unsigned int notification) +int imx_media_link_notify(struct media_link *link, u32 flags, + unsigned int notification) { struct media_entity *source = link->source->entity; struct imx_media_pad_vdev *pad_vdev; @@ -438,13 +433,8 @@ static int imx_media_link_notify(struct media_link *link, u32 flags, return ret; } -static const struct media_device_ops imx_media_md_ops = { - .link_notify = imx_media_link_notify, -}; - -static void imx_media_notify(struct v4l2_subdev *sd, - unsigned int notification, - void *arg) +void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification, + void *arg) { struct media_entity *entity = &sd->entity; int i; @@ -472,77 +462,37 @@ static int imx_media_probe(struct platform_device *pdev) struct imx_media_dev *imxmd; int ret; - imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL); - if (!imxmd) - return -ENOMEM; - - dev_set_drvdata(dev, imxmd); - - strscpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model)); - imxmd->md.ops = &imx_media_md_ops; - imxmd->md.dev = dev; - - mutex_init(&imxmd->mutex); - - imxmd->v4l2_dev.mdev = &imxmd->md; - imxmd->v4l2_dev.notify = imx_media_notify; - strscpy(imxmd->v4l2_dev.name, "imx-media", - sizeof(imxmd->v4l2_dev.name)); - - media_device_init(&imxmd->md); - - ret = v4l2_device_register(dev, &imxmd->v4l2_dev); - if (ret < 0) { - v4l2_err(&imxmd->v4l2_dev, - "Failed to register v4l2_device: %d\n", ret); - goto cleanup; - } - - dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd); - - INIT_LIST_HEAD(&imxmd->vdev_list); - - v4l2_async_notifier_init(&imxmd->notifier); + imxmd = imx_media_dev_init(dev); + if (IS_ERR(imxmd)) + return PTR_ERR(imxmd); ret = imx_media_add_of_subdevs(imxmd, node); if (ret) { v4l2_err(&imxmd->v4l2_dev, "add_of_subdevs failed with %d\n", ret); - goto notifier_cleanup; + goto cleanup; } ret = imx_media_add_internal_subdevs(imxmd); if (ret) { v4l2_err(&imxmd->v4l2_dev, "add_internal_subdevs failed with %d\n", ret); - goto notifier_cleanup; - } - - /* no subdevs? just bail */ - if (list_empty(&imxmd->notifier.asd_list)) { - ret = -ENODEV; - goto notifier_cleanup; + goto cleanup; } - /* prepare the async subdev notifier and register it */ - imxmd->notifier.ops = &imx_media_subdev_ops; - ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, - &imxmd->notifier); - if (ret) { - v4l2_err(&imxmd->v4l2_dev, - "v4l2_async_notifier_register failed with %d\n", ret); + ret = imx_media_dev_notifier_register(imxmd); + if (ret) goto del_int; - } return 0; del_int: imx_media_remove_internal_subdevs(imxmd); -notifier_cleanup: +cleanup: v4l2_async_notifier_cleanup(&imxmd->notifier); v4l2_device_unregister(&imxmd->v4l2_dev); -cleanup: media_device_cleanup(&imxmd->md); + return ret; } @@ -556,8 +506,8 @@ static int imx_media_remove(struct platform_device *pdev) v4l2_async_notifier_unregister(&imxmd->notifier); imx_media_remove_internal_subdevs(imxmd); v4l2_async_notifier_cleanup(&imxmd->notifier); - v4l2_device_unregister(&imxmd->v4l2_dev); media_device_unregister(&imxmd->md); + v4l2_device_unregister(&imxmd->v4l2_dev); media_device_cleanup(&imxmd->md); return 0; diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index a01327f6e045..03446335ac03 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -20,7 +20,8 @@ #include